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