]> git.ipfire.org Git - thirdparty/cups.git/blame - filter/testclient.c
Fix output to write a full page.
[thirdparty/cups.git] / filter / testclient.c
CommitLineData
75e1a17c
MS
1/*
2 * Simulated client test program for CUPS.
3 *
4 * Copyright 2017 by Apple Inc.
5 *
6 * These coded instructions, statements, and computer programs are the
7 * property of Apple Inc. and are protected by Federal copyright
8 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
9 * which should have been included with this file. If this file is
10 * missing or damaged, see the license at "http://www.cups.org/".
11 *
12 * This file is subject to the Apple OS-Developed Software exception.
13 */
14
15/*
16 * Include necessary headers...
17 */
18
4b16c717 19#include <stdio.h>
75e1a17c 20#include <stdlib.h>
4b16c717
MS
21#include <cups/cups.h>
22#include <cups/raster.h>
23#include <cups/string-private.h>
24#include <cups/thread-private.h>
75e1a17c
MS
25
26
27/*
28 * Local types...
29 */
30
31typedef struct _client_monitor_s
32{
33 const char *uri, /* Printer URI */
34 *hostname, /* Hostname */
35 *user, /* Username */
36 *resource; /* Resource path */
37 int port; /* Port number */
38 http_encryption_t encryption; /* Use encryption? */
39 ipp_pstate_t printer_state; /* Current printer state */
40 char printer_state_reasons[1024];
41 /* Current printer-state-reasons */
42 int job_id; /* Job ID for submitted job */
43 ipp_jstate_t job_state; /* Current job state */
44 char job_state_reasons[1024];
45 /* Current job-state-reasons */
46} _client_monitor_t;
47
48
49/*
50 * Local functions...
51 */
52
7f14a297 53static const char *make_raster_file(ipp_t *response, int grayscale, char *tempname, size_t tempsize, const char **format);
240a27f9 54static void *monitor_printer(_client_monitor_t *monitor);
7f14a297 55static void show_attributes(const char *title, int request, ipp_t *ipp);
240a27f9
MS
56static void show_capabilities(ipp_t *response);
57static void usage(void);
75e1a17c
MS
58
59
60/*
61 * 'main()' - Main entry.
62 */
63
64int /* O - Exit status */
65main(int argc, /* I - Number of command-line arguments */
66 char *argv[]) /* I - Command-line arguments */
67{
240a27f9
MS
68 int i; /* Looping var */
69 const char *opt, /* Current option */
70 *uri = NULL, /* Printer URI */
71 *printfile = NULL,
72 /* Print file */
73 *printformat = NULL;
74 /* Print format */
7f14a297
MS
75 int keepfile = 0, /* Keep temp file? */
76 grayscale = 0; /* Force grayscale? */
240a27f9
MS
77 char tempfile[1024] = "",
78 /* Temporary file (if any) */
79 scheme[32], /* URI scheme */
80 userpass[256], /* Username:password */
81 hostname[256], /* Hostname */
82 resource[256]; /* Resource path */
83 int port; /* Port number */
84 http_encryption_t encryption; /* Encryption mode */
85 _client_monitor_t monitor; /* Monitoring data */
86 http_t *http; /* HTTP connection */
87 ipp_t *request, /* IPP request */
88 *response; /* IPP response */
89 ipp_attribute_t *attr; /* IPP attribute */
90 static const char * const pattrs[] = /* Printer attributes we are interested in */
91 {
92 "job-template",
93 "printer-defaults",
94 "printer-description",
95 "media-col-database",
96 "media-col-ready"
97 };
98
99
100 /*
101 * Parse command-line options...
102 */
103
104 for (i = 1; i < argc; i ++)
105 {
106 if (argv[i][0] == '-')
107 {
108 for (opt = argv[i] + 1; *opt; opt ++)
109 {
110 switch (*opt)
111 {
112 case 'f' : /* -f print-file */
113 if (printfile)
114 {
115 puts("Print file can only be specified once.");
116 usage();
117 return (1);
118 }
119
120 i ++;
121 if (i >= argc)
122 {
123 puts("Expected print file after '-f'.");
124 usage();
125 return (1);
126 }
127
128 printfile = argv[i];
129 break;
130
7f14a297
MS
131 case 'g' :
132 grayscale = 1;
133 break;
134
135 case 'k' :
136 keepfile = 1;
137 break;
138
240a27f9
MS
139 default :
140 printf("Unknown option '-%c'.\n", *opt);
141 usage();
142 return (1);
143 }
144 }
145 }
146 else if (uri || (strncmp(argv[i], "ipp://", 6) && strncmp(argv[i], "ipps://", 7)))
147 {
148 printf("Unknown command-line argument '%s'.\n", argv[i]);
149 usage();
150 return (1);
151 }
152 else
153 uri = argv[i];
154 }
155
156 /*
157 * Make sure we have everything we need.
158 */
159
160 if (!uri)
161 {
162 puts("Expected printer URI.");
163 usage();
164 return (1);
165 }
166
167 /*
168 * Connect to the printer...
169 */
170
4b16c717 171 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
240a27f9
MS
172 {
173 printf("Bad printer URI '%s'.\n", uri);
174 return (1);
175 }
176
177 if (!port)
178 port = IPP_PORT;
179
180 if (!strcmp(scheme, "https") || !strcmp(scheme, "ipps"))
181 encryption = HTTP_ENCRYPTION_ALWAYS;
182 else
183 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
184
185 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 0, NULL)) == NULL)
186 {
187 printf("Unable to connect to '%s' on port %d: %s\n", hostname, port, cupsLastErrorString());
188 return (1);
189 }
190
191 /*
192 * Query printer status and capabilities...
193 */
194
195 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
196 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
197 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
198 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
199
200 response = cupsDoRequest(http, request, resource);
201
202 show_capabilities(response);
203
204 /*
205 * Now figure out what we will be printing...
206 */
207
208 if (printfile)
209 {
210 /*
211 * User specified a print file, figure out the format...
212 */
213
214 if ((opt = strrchr(printfile, '.')) != NULL)
215 {
216 /*
217 * Guess the format from the extension...
218 */
219
220 if (!strcmp(opt, ".jpg"))
221 printformat = "image/jpeg";
222 else if (!strcmp(opt, ".pdf"))
223 printformat = "application/pdf";
224 else if (!strcmp(opt, ".ps"))
225 printformat = "application/postscript";
226 else if (!strcmp(opt, ".pwg"))
227 printformat = "image/pwg-raster";
228 else if (!strcmp(opt, ".urf"))
229 printformat = "image/urf";
230 else
231 printformat = "application/octet-stream";
232 }
233 else
234 {
235 /*
236 * Tell the printer to auto-detect...
237 */
238
239 printformat = "application/octet-stream";
240 }
241 }
242 else
243 {
244 /*
245 * No file specified, make something to test with...
246 */
247
7f14a297 248 if ((printfile = make_raster_file(response, grayscale, tempfile, sizeof(tempfile), &printformat)) == NULL)
240a27f9
MS
249 return (1);
250 }
251
252 ippDelete(response);
253
254 /*
255 * Start monitoring the printer in the background...
256 */
257
258 memset(&monitor, 0, sizeof(monitor));
259
260 monitor.uri = uri;
261 monitor.hostname = hostname;
262 monitor.resource = resource;
263 monitor.port = port;
264 monitor.encryption = encryption;
265
266 _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
267
268 /*
269 * Create the job and wait for completion...
270 */
271
272 request = ippNewRequest(IPP_OP_CREATE_JOB);
273 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
274 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
275
276 if ((opt = strrchr(printfile, '/')) != NULL)
277 opt ++;
278 else
279 opt = printfile;
280
4b16c717 281 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, opt);
240a27f9 282
7f14a297
MS
283 show_attributes("Create-Job request", 1, request);
284
240a27f9
MS
285 response = cupsDoRequest(http, request, resource);
286
7f14a297
MS
287 show_attributes("Create-Job response", 0, response);
288
240a27f9
MS
289 if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
290 {
291 printf("Unable to create print job: %s\n", cupsLastErrorString());
292
293 monitor.job_state = IPP_JSTATE_ABORTED;
294
295 goto cleanup;
296 }
297
298 if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
299 {
300 puts("No job-id returned in Create-Job request.");
301
302 monitor.job_state = IPP_JSTATE_ABORTED;
303
304 goto cleanup;
305 }
306
307 monitor.job_id = ippGetInteger(attr, 0);
308
7f14a297
MS
309 printf("CREATED JOB %d, sending %s of type %s\n", monitor.job_id, printfile, printformat);
310
240a27f9
MS
311 ippDelete(response);
312
4b16c717 313 request = ippNewRequest(IPP_OP_SEND_DOCUMENT);
240a27f9
MS
314 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
315 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor.job_id);
316 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
317 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, printformat);
4b16c717 318 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
240a27f9 319
7f14a297
MS
320 show_attributes("Send-Document request", 1, request);
321
240a27f9
MS
322 response = cupsDoFileRequest(http, request, resource, printfile);
323
7f14a297
MS
324 show_attributes("Send-Document response", 0, response);
325
240a27f9
MS
326 if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
327 {
328 printf("Unable to print file: %s\n", cupsLastErrorString());
329
330 monitor.job_state = IPP_JSTATE_ABORTED;
331
332 goto cleanup;
333 }
334
7f14a297
MS
335 puts("WAITING FOR JOB TO COMPLETE");
336
240a27f9
MS
337 while (monitor.job_state < IPP_JSTATE_CANCELED)
338 sleep(1);
339
340 /*
341 * Cleanup after ourselves...
342 */
343
344 cleanup:
345
346 httpClose(http);
347
7f14a297 348 if (tempfile[0] && !keepfile)
240a27f9
MS
349 unlink(tempfile);
350
351 return (monitor.job_state == IPP_JSTATE_COMPLETED);
352}
353
354
355/*
356 * 'make_raster_file()' - Create a temporary raster file.
357 */
358
359static const char * /* O - Print filename */
360make_raster_file(ipp_t *response, /* I - Printer attributes */
7f14a297 361 int grayscale, /* I - Force grayscale? */
240a27f9
MS
362 char *tempname, /* I - Temporary filename buffer */
363 size_t tempsize, /* I - Size of temp file buffer */
364 const char **format) /* O - Print format */
365{
4b16c717
MS
366 int i, /* Looping var */
367 count; /* Number of values */
368 ipp_attribute_t *attr; /* Printer attribute */
369 const char *type = NULL; /* Raster type (colorspace + bits) */
370 pwg_media_t *media = NULL; /* Media size */
371 int xdpi = 0, /* Horizontal resolution */
372 ydpi = 0; /* Vertical resolution */
373 int fd; /* Temporary file */
374 cups_mode_t mode; /* Raster mode */
375 cups_raster_t *ras; /* Raster stream */
376 cups_page_header2_t header; /* Page header */
377 unsigned char *line, /* Line of raster data */
378 *lineptr; /* Pointer into line */
379 unsigned y, /* Current position on page */
380 xcount, ycount, /* Current count for X and Y */
381 xrep, yrep, /* Repeat count for X and Y */
382 xoff, yoff, /* Offsets for X and Y */
383 yend; /* End Y value */
384 int temprow, /* Row in template */
385 tempcolor; /* Template color */
386 const char *template; /* Pointer into template */
387 const unsigned char *color; /* Current color */
388 static const unsigned char colors[][3] =
389 { /* Colors for test */
390 { 191, 191, 191 },
391 { 127, 127, 127 },
392 { 63, 63, 63 },
393 { 0, 0, 0 },
394 { 255, 0, 0 },
395 { 255, 127, 0 },
396 { 255, 255, 0 },
397 { 127, 255, 0 },
398 { 0, 255, 0 },
399 { 0, 255, 127 },
400 { 0, 255, 255 },
401 { 0, 127, 255 },
402 { 0, 0, 255 },
403 { 127, 0, 255 },
404 { 255, 0, 255 }
405 };
406 static const char * const templates[] =
407 { /* Raster template */
408 " CCC U U PPPP SSS TTTTT EEEEE SSS TTTTT 000 1 222 333 4 55555 66 77777 888 999 ",
409 "C C U U P P S S T E S S T 0 0 11 2 2 3 3 4 4 5 6 7 8 8 9 9 ",
410 "C U U P P S T E S T 0 0 1 2 3 4 4 5 6 7 8 8 9 9 ",
411 "C U U PPPP SSS ----- T EEEE SSS T 0 0 0 1 22 333 44444 555 6666 7 888 9999 ",
412 "C U U P S T E S T 0 0 1 2 3 4 5 6 6 7 8 8 9 ",
413 "C C U U P S S T E S S T 0 0 1 2 3 3 4 5 5 6 6 7 8 8 9 ",
414 " CCC UUU P SSS T EEEEE SSS T 000 111 22222 333 4 555 666 7 888 99 ",
415 " "
416 };
417
418
419 /*
420 * Figure out the output format...
421 */
422
423 if ((attr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) == NULL)
424 {
425 puts("No supported document formats, aborting.");
426 return (NULL);
427 }
428
429 if (ippContainsString(attr, "image/urf"))
430 {
431 /*
432 * Apple Raster format...
433 */
434
435 *format = "image/urf";
436 mode = CUPS_RASTER_WRITE_APPLE;
437 }
438 else if (ippContainsString(attr, "image/pwg-raster"))
439 {
440 /*
441 * PWG Raster format...
442 */
443
444 *format = "image/pwg-raster";
445 mode = CUPS_RASTER_WRITE_PWG;
446 }
447 else
448 {
449 /*
450 * No supported raster format...
451 */
452
453 puts("Printer does not support Apple or PWG raster files, aborting.");
454 return (NULL);
455 }
456
457 /*
458 * Figure out the the media, resolution, and color mode...
459 */
460
461 if ((attr = ippFindAttribute(response, "media-default", IPP_TAG_KEYWORD)) != NULL)
462 {
463 /*
464 * Use default media...
465 */
466
467 media = pwgMediaForPWG(ippGetString(attr, 0, NULL));
468 }
469 else if ((attr = ippFindAttribute(response, "media-ready", IPP_TAG_KEYWORD)) != NULL)
470 {
471 /*
472 * Use ready media...
473 */
474
475 if (ippContainsString(attr, "na_letter_8.5x11in"))
476 media = pwgMediaForPWG("na_letter_8.5x11in");
477 else if (ippContainsString(attr, "iso_a4_210x297mm"))
478 media = pwgMediaForPWG("iso_a4_210x297mm");
479 else
480 media = pwgMediaForPWG(ippGetString(attr, 0, NULL));
481 }
482 else
483 {
484 puts("No default or ready media reported by printer, aborting.");
485 return (NULL);
486 }
487
488 if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
489 {
490 for (i = 0, count = ippGetCount(attr); i < count; i ++)
491 {
492 const char *val = ippGetString(attr, i, NULL);
493
7f14a297
MS
494 if (!strncmp(val, "RS", 2))
495 xdpi = ydpi = atoi(val + 2);
496 else if (!strncmp(val, "W8", 2) && !type)
4b16c717 497 type = "sgray_8";
7f14a297 498 else if (!strncmp(val, "SRGB24", 6) && !grayscale)
4b16c717
MS
499 type = "srgb_8";
500 }
501 }
502 else if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
503 {
504 for (i = 0, count = ippGetCount(attr); i < count; i ++)
505 {
506 int tempxdpi, tempydpi;
507 ipp_res_t tempunits;
508
509 tempxdpi = ippGetResolution(attr, 0, &tempydpi, &tempunits);
510
511 if (i == 0 || tempxdpi < xdpi || tempydpi < ydpi)
512 {
513 xdpi = tempxdpi;
514 ydpi = tempydpi;
515 }
516 }
517
518 if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) != NULL)
519 {
7f14a297 520 if (!grayscale && ippContainsString(attr, "srgb_8"))
4b16c717
MS
521 type = "srgb_8";
522 else if (ippContainsString(attr, "sgray_8"))
523 type = "sgray_8";
524 }
525 }
526
527 if (xdpi < 72 || ydpi < 72)
528 {
529 puts("No supported raster resolutions, aborting.");
530 return (NULL);
531 }
532
533 if (!type)
534 {
535 puts("No supported color spaces or bit depths, aborting.");
536 return (NULL);
537 }
538
539 /*
540 * Make the raster context and details...
541 */
542
543 if (!cupsRasterInitPWGHeader(&header, media, type, xdpi, ydpi, "one-sided", NULL))
544 {
545 printf("Unable to initialize raster context: %s\n", cupsRasterErrorString());
546 return (NULL);
547 }
548
549 header.cupsInteger[CUPS_RASTER_PWG_TotalPageCount] = 1;
550
551 if (header.cupsWidth > (4 * header.HWResolution[0]))
552 {
553 xoff = header.HWResolution[0] / 2;
554 yoff = header.HWResolution[1] / 2;
555 }
556 else
557 {
558 xoff = 0;
559 yoff = 0;
560 }
561
562 xrep = (header.cupsWidth - 2 * xoff) / 140;
563 yrep = xrep * header.HWResolution[1] / header.HWResolution[0];
564 yend = header.cupsHeight - yoff;
565
566 /*
567 * Prepare the raster file...
568 */
569
570 if ((line = malloc(header.cupsBytesPerLine)) == NULL)
571 {
572 printf("Unable to allocate %u bytes for raster output: %s\n", header.cupsBytesPerLine, strerror(errno));
573 return (NULL);
574 }
575
576 if ((fd = cupsTempFd(tempname, (int)tempsize)) < 0)
577 {
578 printf("Unable to create temporary print file: %s\n", strerror(errno));
579 return (NULL);
580 }
581
582 if ((ras = cupsRasterOpen(fd, mode)) == NULL)
583 {
584 printf("Unable to open raster stream: %s\n", cupsRasterErrorString());
585 close(fd);
586 return (NULL);
587 }
588
589 /*
590 * Write a single page consisting of the template dots repeated over the page.
591 */
592
593 cupsRasterWriteHeader2(ras, &header);
594
595 memset(line, 0xff, header.cupsBytesPerLine);
596
597 for (y = 0; y < yoff; y ++)
598 cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
599
7f14a297 600 for (temprow = 0, tempcolor = 0; y < yend;)
4b16c717
MS
601 {
602 template = templates[temprow];
603 color = colors[tempcolor];
604
605 temprow ++;
606 if (temprow >= (int)(sizeof(templates) / sizeof(templates[0])))
607 {
608 temprow = 0;
609 tempcolor ++;
610 if (tempcolor >= (int)(sizeof(colors) / sizeof(colors[0])))
611 tempcolor = 0;
7f14a297
MS
612 else if (tempcolor > 3 && header.cupsColorSpace == CUPS_CSPACE_SW)
613 tempcolor = 0;
4b16c717
MS
614 }
615
616 memset(line, 0xff, header.cupsBytesPerLine);
617
618 if (header.cupsColorSpace == CUPS_CSPACE_SW)
619 {
620 /*
621 * Do grayscale output...
622 */
623
624 for (lineptr = line + xoff; *template; template ++)
625 {
626 if (*template != ' ')
627 {
628 for (xcount = xrep; xcount > 0; xcount --)
629 *lineptr++ = *color;
630 }
631 else
632 {
633 lineptr += xrep;
634 }
635 }
636 }
637 else
638 {
639 /*
640 * Do color output...
641 */
642
643 for (lineptr = line + 3 * xoff; *template; template ++)
644 {
645 if (*template != ' ')
646 {
647 for (xcount = xrep; xcount > 0; xcount --, lineptr += 3)
648 memcpy(lineptr, color, 3);
649 }
650 else
651 {
652 lineptr += 3 * xrep;
653 }
654 }
655 }
656
657 for (ycount = yrep; ycount > 0 && y < yend; ycount --, y ++)
658 cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
659 }
660
661 memset(line, 0xff, header.cupsBytesPerLine);
662
663 for (y = 0; y < header.cupsHeight; y ++)
664 cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
665
666 cupsRasterClose(ras);
667
668 close(fd);
669
670 printf("PRINT FILE: %s\n", tempname);
671
672 return (tempname);
75e1a17c
MS
673}
674
675
676/*
677 * 'monitor_printer()' - Monitor the job and printer states.
678 */
679
680static void * /* O - Thread exit code */
681monitor_printer(
682 _client_monitor_t *monitor) /* I - Monitoring data */
683{
684 http_t *http; /* Connection to printer */
685 ipp_t *request, /* IPP request */
686 *response; /* IPP response */
687 ipp_attribute_t *attr; /* Attribute in response */
688 ipp_pstate_t printer_state; /* Printer state */
689 char printer_state_reasons[1024];
690 /* Printer state reasons */
75e1a17c
MS
691 ipp_jstate_t job_state; /* Job state */
692 char job_state_reasons[1024];/* Printer state reasons */
693 static const char * const jattrs[] = /* Job attributes we want */
694 {
695 "job-state",
696 "job-state-reasons"
697 };
698 static const char * const pattrs[] = /* Printer attributes we want */
699 {
700 "printer-state",
701 "printer-state-reasons"
702 };
703
704
705 /*
706 * Open a connection to the printer...
707 */
708
4b16c717 709 http = httpConnect2(monitor->hostname, monitor->port, NULL, AF_UNSPEC, monitor->encryption, 1, 0, NULL);
75e1a17c
MS
710
711 /*
712 * Loop until the job is canceled, aborted, or completed.
713 */
714
715 printer_state = (ipp_pstate_t)0;
716 printer_state_reasons[0] = '\0';
717
718 job_state = (ipp_jstate_t)0;
719 job_state_reasons[0] = '\0';
720
240a27f9 721 while (monitor->job_state < IPP_JSTATE_CANCELED)
75e1a17c
MS
722 {
723 /*
724 * Reconnect to the printer as needed...
725 */
726
727 if (httpGetFd(http) < 0)
4b16c717 728 httpReconnect2(http, 30000, NULL);
75e1a17c
MS
729
730 if (httpGetFd(http) >= 0)
731 {
732 /*
733 * Connected, so check on the printer state...
734 */
735
736 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
737 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri);
738 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
739 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
740
741 response = cupsDoRequest(http, request, monitor->resource);
742
743 if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
744 printer_state = (ipp_pstate_t)ippGetInteger(attr, 0);
745
746 if ((attr = ippFindAttribute(response, "printer-state-reasons", IPP_TAG_KEYWORD)) != NULL)
747 ippAttributeString(attr, printer_state_reasons, sizeof(printer_state_reasons));
748
749 if (printer_state != monitor->printer_state || strcmp(printer_state_reasons, monitor->printer_state_reasons))
750 {
751 printf("PRINTER: %s (%s)\n", ippEnumString("printer-state", printer_state), printer_state_reasons);
752
753 monitor->printer_state = printer_state;
754 strlcpy(monitor->printer_state_reasons, printer_state_reasons, sizeof(monitor->printer_state_reasons));
755 }
756
757 ippDelete(response);
758
759 if (monitor->job_id > 0)
760 {
761 /*
762 * Check the status of the job itself...
763 */
764
765 request = ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES);
766 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri);
767 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor->job_id);
768 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
769 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
770
771 response = cupsDoRequest(http, request, monitor->resource);
772
773 if ((attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL)
774 job_state = (ipp_jstate_t)ippGetInteger(attr, 0);
775
776 if ((attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD)) != NULL)
777 ippAttributeString(attr, job_state_reasons, sizeof(job_state_reasons));
778
779 if (job_state != monitor->job_state || strcmp(job_state_reasons, monitor->job_state_reasons))
780 {
781 printf("JOB %d: %s (%s)\n", monitor->job_id, ippEnumString("job-state", job_state), job_state_reasons);
782
783 monitor->job_state = job_state;
784 strlcpy(monitor->job_state_reasons, job_state_reasons, sizeof(monitor->job_state_reasons));
785 }
786
787 ippDelete(response);
788 }
789 }
790
4b16c717 791 if (monitor->job_state < IPP_JSTATE_CANCELED)
240a27f9
MS
792 {
793 /*
794 * Sleep for 5 seconds...
795 */
75e1a17c 796
240a27f9
MS
797 sleep(5);
798 }
75e1a17c
MS
799 }
800
801 /*
802 * Cleanup and return...
803 */
804
805 httpClose(http);
806
807 return (NULL);
808}
240a27f9
MS
809
810
7f14a297
MS
811/*
812 * 'show_attributes()' - Show attributes in a request or response.
813 */
814
815static void
816show_attributes(const char *title, /* I - Title */
817 int request, /* I - 1 for request, 0 for response */
818 ipp_t *ipp) /* I - IPP request/response */
819{
820 int minor, major = ippGetVersion(ipp, &minor);
821 /* IPP version number */
822 ipp_tag_t group = IPP_TAG_ZERO;
823 /* Current group tag */
824 ipp_attribute_t *attr; /* Current attribute */
825 const char *name; /* Attribute name */
826 char buffer[1024]; /* Value */
827
828
829 printf("%s:\n", title);
830 printf(" version=%d.%d\n", major, minor);
831 printf(" request-id=%d\n", ippGetRequestId(ipp));
832 if (!request)
833 printf(" status-code=%s\n", ippErrorString(ippGetStatusCode(ipp)));
834
835 for (attr = ippFirstAttribute(ipp); attr; attr = ippNextAttribute(ipp))
836 {
837 if (group != ippGetGroupTag(attr))
838 {
839 group = ippGetGroupTag(attr);
840 if (group)
841 printf(" %s:\n", ippTagString(group));
842 }
843
844 if ((name = ippGetName(attr)) != NULL)
845 {
846 ippAttributeString(attr, buffer, sizeof(buffer));
847 printf(" %s(%s%s)=%s\n", name, ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), buffer);
848 }
849 }
850}
851
852
240a27f9
MS
853/*
854 * 'show_capabilities()' - Show printer capabilities.
855 */
856
857static void
858show_capabilities(ipp_t *response) /* I - Printer attributes */
859{
860 int i; /* Looping var */
861 ipp_attribute_t *attr; /* Attribute */
862 char buffer[1024]; /* Attribute value buffer */
4b16c717 863 static const char * const pattrs[] = /* Attributes we want to show */
240a27f9
MS
864 {
865 "copies-default",
866 "copies-supported",
867 "finishings-default",
868 "finishings-ready",
869 "finishings-supported",
870 "media-default",
871 "media-ready",
872 "media-supported",
873 "output-bin-default",
874 "output-bin-supported",
875 "print-color-mode-default",
876 "print-color-mode-supported",
877 "sides-default",
878 "sides-supported",
879 "document-format-default",
880 "document-format-supported",
881 "pwg-raster-document-resolution-supported",
882 "pwg-raster-document-type-supported",
883 "urf-supported"
4b16c717
MS
884 };
885
240a27f9
MS
886
887 puts("CAPABILITIES:");
888 for (i = 0; i < (int)(sizeof(pattrs) / sizeof(pattrs[0])); i ++)
889 {
890 if ((attr = ippFindAttribute(response, pattrs[i], IPP_TAG_ZERO)) != NULL)
891 {
892 ippAttributeString(attr, buffer, sizeof(buffer));
893 printf(" %s=%s\n", pattrs[i], buffer);
894 }
895 }
896}
897
898
899/*
900 * 'usage()' - Show program usage...
901 */
902
903static void
904usage(void)
905{
906 puts("Usage: ./testclient printer-uri [-f print-file]");
907}