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