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