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