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