]>
Commit | Line | Data |
---|---|---|
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 | 34 | typedef 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 |
60 | static int client_count = 0; |
61 | static _cups_mutex_t client_mutex = _CUPS_MUTEX_INITIALIZER; | |
7e3643bf | 62 | static int verbosity = 0; |
75e1a17c MS |
63 | |
64 | ||
65 | /* | |
66 | * Local functions... | |
67 | */ | |
68 | ||
064e50fb MS |
69 | static const char *make_raster_file(ipp_t *response, int grayscale, char *tempname, size_t tempsize, const char **format); |
70 | static void *monitor_printer(_client_data_t *data); | |
7e3643bf | 71 | static void *run_client(_client_data_t *data); |
064e50fb MS |
72 | static void show_attributes(const char *title, int request, ipp_t *ipp); |
73 | static void show_capabilities(ipp_t *response); | |
74 | static void usage(void); | |
75e1a17c MS |
75 | |
76 | ||
77 | /* | |
78 | * 'main()' - Main entry. | |
79 | */ | |
80 | ||
81 | int /* O - Exit status */ | |
82 | main(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 | ||
279 | static const char * /* O - Print filename */ | |
280 | make_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 | ||
622 | static void * /* O - Thread exit code */ | |
623 | monitor_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 | ||
759 | static void * /* O - Thread exit code */ | |
760 | run_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 | ||
958 | static void | |
959 | show_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 | ||
1000 | static void | |
1001 | show_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 | ||
1046 | static void | |
1047 | usage(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 | } |