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