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