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