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