]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
Add "dist" target that builds the proper packages for each OS we support.
[thirdparty/cups.git] / backend / ipp.c
CommitLineData
c8f9565c 1/*
c9d3f842 2 * "$Id$"
c8f9565c 3 *
4 * IPP backend for the Common UNIX Printing System (CUPS).
5 *
0e66904d 6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
c8f9565c 7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
8784b6a6 17 * 44141 Airport View Drive, Suite 204
c9d3f842 18 * Hollywood, Maryland 20636 USA
c8f9565c 19 *
9639c4de 20 * Voice: (301) 373-9600
c8f9565c 21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
dab1a4d8 24 * This file is subject to the Apple OS-Developed Software exception.
25 *
c8f9565c 26 * Contents:
27 *
d1c2727f 28 * main() - Send a file to the printer or server.
1230a8a0 29 * cancel_job() - Cancel a print job.
6bb5a528 30 * check_printer_state() - Check the printer state...
1230a8a0 31 * compress_files() - Compress print files...
d1c2727f 32 * password_cb() - Disable the password prompt for
33 * cupsDoFileRequest().
34 * report_printer_state() - Report the printer state.
8f4595eb 35 * run_pictwps_filter() - Convert PICT files to PostScript when printing
36 * remotely.
37 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
c8f9565c 38 */
39
40/*
41 * Include necessary headers.
42 */
43
1a59b1c1 44#include <cups/http-private.h>
c8f9565c 45#include <stdio.h>
46#include <stdlib.h>
47#include <errno.h>
48#include <sys/types.h>
49#include <sys/stat.h>
6248387b 50#include <cups/backend.h>
c8f9565c 51#include <cups/cups.h>
52#include <cups/language.h>
53#include <cups/string.h>
4ff40357 54#include <signal.h>
1c85e5c0 55#include <sys/wait.h>
c8f9565c 56
57
8f4595eb 58/*
59 * Globals...
60 */
61
6bb5a528 62static char *password = NULL; /* Password for device URI */
b8f3410f 63#ifdef __APPLE__
64static char pstmpname[1024] = ""; /* Temporary PostScript file name */
65#endif /* __APPLE__ */
6bb5a528 66static char tmpfilename[1024] = ""; /* Temporary spool file name */
072c4872 67static int job_cancelled = 0; /* Job cancelled? */
8f4595eb 68
69
b5cb0608 70/*
71 * Local functions...
72 */
73
072c4872 74static void cancel_job(http_t *http, const char *uri, int id,
75 const char *resource, const char *user, int version);
76static void check_printer_state(http_t *http, const char *uri,
6bb5a528 77 const char *resource, const char *user,
78 int version);
1230a8a0 79#ifdef HAVE_LIBZ
80static void compress_files(int num_files, char **files);
81#endif /* HAVE_LIBZ */
072c4872 82static const char *password_cb(const char *);
83static int report_printer_state(ipp_t *ipp);
b5cb0608 84
a684a3b0 85#ifdef __APPLE__
072c4872 86static int run_pictwps_filter(char **argv, const char *filename);
a684a3b0 87#endif /* __APPLE__ */
8f4595eb 88static void sigterm_handler(int sig);
a684a3b0 89
b5cb0608 90
c8f9565c 91/*
92 * 'main()' - Send a file to the printer or server.
93 *
94 * Usage:
95 *
96 * printer-uri job-id user title copies options [file]
97 */
98
74ef1ffb 99int /* O - Exit status */
072c4872 100main(int argc, /* I - Number of command-line args */
74ef1ffb 101 char *argv[]) /* I - Command-line arguments */
c8f9565c 102{
74ef1ffb 103 int i; /* Looping var */
104 int num_options; /* Number of printer options */
105 cups_option_t *options; /* Printer options */
106 char method[255], /* Method in URI */
107 hostname[1024], /* Hostname */
108 username[255], /* Username info */
109 resource[1024], /* Resource info (printer name) */
110 *optptr, /* Pointer to URI options */
111 name[255], /* Name of option */
112 value[255], /* Value of option */
113 *ptr; /* Pointer into name or value */
072c4872 114 int num_files; /* Number of files to print */
115 char **files, /* Files to print */
116 *filename; /* Pointer to single filename */
74ef1ffb 117 int port; /* Port number (not used) */
118 char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
119 ipp_status_t ipp_status; /* Status of IPP request */
120 http_t *http; /* HTTP connection */
121 ipp_t *request, /* IPP request */
122 *response, /* IPP response */
123 *supported; /* get-printer-attributes response */
1230a8a0 124 int compression, /* Do compression of the job data? */
125 waitjob, /* Wait for job complete? */
74ef1ffb 126 waitprinter; /* Wait for printer ready? */
127 ipp_attribute_t *job_id_attr; /* job-id attribute */
128 int job_id; /* job-id value */
072c4872 129 ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
130 ipp_attribute_t *job_state; /* job-state */
131 ipp_attribute_t *copies_sup; /* copies-supported */
132 ipp_attribute_t *format_sup; /* document-format-supported */
74ef1ffb 133 ipp_attribute_t *printer_state; /* printer-state attribute */
072c4872 134 ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
74ef1ffb 135 int copies; /* Number of copies remaining */
136 const char *content_type; /* CONTENT_TYPE environment variable */
4ff40357 137#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
74ef1ffb 138 struct sigaction action; /* Actions for POSIX signals */
4ff40357 139#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
74ef1ffb 140 int version; /* IPP version */
8f4595eb 141 static const char * const pattrs[] =
74ef1ffb 142 { /* Printer attributes we want */
8f4595eb 143 "copies-supported",
8f4595eb 144 "document-format-supported",
145 "printer-is-accepting-jobs",
146 "printer-state",
147 "printer-state-reasons",
148 };
149 static const char * const jattrs[] =
74ef1ffb 150 { /* Job attributes we want */
8f4595eb 151 "job-media-sheets-completed",
152 "job-state"
153 };
c8f9565c 154
4b23f3b3 155
156 /*
157 * Make sure status messages are not buffered...
158 */
159
2922de55 160 setbuf(stderr, NULL);
c8f9565c 161
98904cd6 162 /*
8f4595eb 163 * Ignore SIGPIPE and catch SIGTERM signals...
98904cd6 164 */
165
166#ifdef HAVE_SIGSET
167 sigset(SIGPIPE, SIG_IGN);
8f4595eb 168 sigset(SIGTERM, sigterm_handler);
98904cd6 169#elif defined(HAVE_SIGACTION)
170 memset(&action, 0, sizeof(action));
171 action.sa_handler = SIG_IGN;
172 sigaction(SIGPIPE, &action, NULL);
8f4595eb 173
174 sigemptyset(&action.sa_mask);
175 sigaddset(&action.sa_mask, SIGTERM);
176 action.sa_handler = sigterm_handler;
177 sigaction(SIGTERM, &action, NULL);
98904cd6 178#else
179 signal(SIGPIPE, SIG_IGN);
8f4595eb 180 signal(SIGTERM, sigterm_handler);
98904cd6 181#endif /* HAVE_SIGSET */
182
4b23f3b3 183 /*
184 * Check command-line...
185 */
186
68edc300 187 if (argc == 1)
188 {
183914a3 189 char *s;
190
d4c438d4 191 if ((s = strrchr(argv[0], '/')) != NULL)
192 s ++;
193 else
194 s = argv[0];
195
072c4872 196 printf("network %s \"Unknown\" \"Internet Printing Protocol (%s)\"\n",
197 s, s);
6248387b 198 return (CUPS_BACKEND_OK);
68edc300 199 }
072c4872 200 else if (argc < 6)
c8f9565c 201 {
072c4872 202 fprintf(stderr,
203 "Usage: %s job-id user title copies options [file ... fileN]\n",
c8f9565c 204 argv[0]);
6248387b 205 return (CUPS_BACKEND_STOP);
c8f9565c 206 }
207
a684a3b0 208 /*
072c4872 209 * Get the (final) content type...
a684a3b0 210 */
211
072c4872 212 if ((content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
65a691a9 213 if ((content_type = getenv("CONTENT_TYPE")) == NULL)
214 content_type = "application/octet-stream";
a684a3b0 215
12d8a513 216 if (!strncmp(content_type, "printer/", 8))
217 content_type = "application/vnd.cups-raw";
218
7abb7137 219 /*
220 * Extract the hostname and printer name from the URI...
221 */
222
00a1fad8 223 if (httpSeparateURI(HTTP_URI_CODING_ALL, cupsBackendDeviceURI(argv),
224 method, sizeof(method), username, sizeof(username),
225 hostname, sizeof(hostname), &port,
226 resource, sizeof(resource)) < HTTP_URI_OK)
7abb7137 227 {
00a1fad8 228 fputs("ERROR: Missing device URI on command-line and no DEVICE_URI "
229 "environment variable!\n", stderr);
6248387b 230 return (CUPS_BACKEND_STOP);
7abb7137 231 }
232
233 if (!strcmp(method, "https"))
234 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
692bbbae 235 else
236 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
7abb7137 237
897922a9 238 /*
239 * See if there are any options...
240 */
241
1230a8a0 242 compression = 0;
a708af2a 243 version = 1;
897922a9 244 waitjob = 1;
245 waitprinter = 1;
246
247 if ((optptr = strchr(resource, '?')) != NULL)
248 {
249 /*
250 * Yup, terminate the device name string and move to the first
251 * character of the optptr...
252 */
253
254 *optptr++ = '\0';
255
256 /*
257 * Then parse the optptr...
258 */
259
260 while (*optptr)
261 {
262 /*
263 * Get the name...
264 */
265
266 for (ptr = name; *optptr && *optptr != '=';)
267 if (ptr < (name + sizeof(name) - 1))
268 *ptr++ = *optptr++;
269 *ptr = '\0';
270
271 if (*optptr == '=')
272 {
273 /*
274 * Get the value...
275 */
276
277 optptr ++;
278
74ef1ffb 279 for (ptr = value; *optptr && *optptr != '+' && *optptr != '&';)
897922a9 280 if (ptr < (value + sizeof(value) - 1))
281 *ptr++ = *optptr++;
282 *ptr = '\0';
283
6e9b3e9f 284 if (*optptr == '+' || *optptr == '&')
897922a9 285 optptr ++;
286 }
287 else
288 value[0] = '\0';
289
290 /*
291 * Process the option...
292 */
293
294 if (!strcasecmp(name, "waitjob"))
295 {
296 /*
297 * Wait for job completion?
298 */
299
300 waitjob = !strcasecmp(value, "on") ||
301 !strcasecmp(value, "yes") ||
302 !strcasecmp(value, "true");
303 }
304 else if (!strcasecmp(name, "waitprinter"))
305 {
306 /*
307 * Wait for printer idle?
308 */
309
310 waitprinter = !strcasecmp(value, "on") ||
311 !strcasecmp(value, "yes") ||
312 !strcasecmp(value, "true");
313 }
314 else if (!strcasecmp(name, "encryption"))
315 {
316 /*
317 * Enable/disable encryption?
318 */
319
320 if (!strcasecmp(value, "always"))
321 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
322 else if (!strcasecmp(value, "required"))
323 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
324 else if (!strcasecmp(value, "never"))
325 cupsSetEncryption(HTTP_ENCRYPT_NEVER);
326 else if (!strcasecmp(value, "ifrequested"))
327 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
328 else
329 {
330 fprintf(stderr, "ERROR: Unknown encryption option value \"%s\"!\n",
331 value);
332 }
333 }
a708af2a 334 else if (!strcasecmp(name, "version"))
335 {
336 if (!strcmp(value, "1.0"))
337 version = 0;
338 else if (!strcmp(value, "1.1"))
339 version = 1;
340 else
341 {
342 fprintf(stderr, "ERROR: Unknown version option value \"%s\"!\n",
343 value);
344 }
345 }
1230a8a0 346#ifdef HAVE_LIBZ
347 else if (!strcasecmp(name, "compression"))
348 {
349 compression = !strcasecmp(value, "true") ||
350 !strcasecmp(value, "yes") ||
351 !strcasecmp(value, "on") ||
352 !strcasecmp(value, "gzip");
353 }
354#endif /* HAVE_LIBZ */
897922a9 355 else
356 {
357 /*
358 * Unknown option...
359 */
360
361 fprintf(stderr, "ERROR: Unknown option \"%s\" with value \"%s\"!\n",
362 name, value);
363 }
364 }
365 }
366
1230a8a0 367 /*
368 * If we have 7 arguments, print the file named on the command-line.
369 * Otherwise, copy stdin to a temporary file and print the temporary
370 * file.
371 */
372
373 if (argc == 6)
374 {
375 /*
376 * Copy stdin to a temporary file...
377 */
378
379 int fd; /* File descriptor */
380 cups_file_t *fp; /* Temporary file */
381 char buffer[8192]; /* Buffer for copying */
382 int bytes; /* Number of bytes read */
383
384
385 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
386 {
387 perror("ERROR: unable to create temporary file");
388 return (CUPS_BACKEND_FAILED);
389 }
390
391 if ((fp = cupsFileOpenFd(fd, compression ? "w9" : "w")) == NULL)
392 {
393 perror("ERROR: unable to open temporary file");
394 close(fd);
395 unlink(tmpfilename);
396 return (CUPS_BACKEND_FAILED);
397 }
398
399 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
400 if (cupsFileWrite(fp, buffer, bytes) < bytes)
401 {
402 perror("ERROR: unable to write to temporary file");
403 cupsFileClose(fp);
404 unlink(tmpfilename);
405 return (CUPS_BACKEND_FAILED);
406 }
407
408 cupsFileClose(fp);
409
410 /*
411 * Point to the single file from stdin...
412 */
413
414 filename = tmpfilename;
415 files = &filename;
416 num_files = 1;
417 }
418 else
419 {
420 /*
421 * Point to the files on the command-line...
422 */
423
424 num_files = argc - 6;
425 files = argv + 6;
426
427#ifdef HAVE_LIBZ
428 if (compression)
429 compress_files(num_files, files);
430#endif /* HAVE_LIBZ */
431 }
432
433 fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
434
c8f9565c 435 /*
b5cb0608 436 * Set the authentication info, if any...
c8f9565c 437 */
438
b5cb0608 439 cupsSetPasswordCB(password_cb);
440
441 if (username[0])
442 {
48e211f3 443 /*
444 * Use authenticaion information in the device URI...
445 */
446
b5cb0608 447 if ((password = strchr(username, ':')) != NULL)
448 *password++ = '\0';
449
450 cupsSetUser(username);
451 }
48e211f3 452 else if (!getuid())
453 {
454 /*
455 * Try loading authentication information from the a##### file.
456 */
457
458 const char *request_root; /* CUPS_REQUESTROOT env var */
459 char afilename[1024], /* a##### filename */
460 aline[1024]; /* Line from file */
461 FILE *fp; /* File pointer */
462
463
464 if ((request_root = getenv("CUPS_REQUESTROOT")) != NULL)
465 {
466 /*
467 * Try opening authentication cache file...
468 */
469
470 snprintf(afilename, sizeof(afilename), "%s/a%05d", request_root,
471 atoi(argv[1]));
472 if ((fp = fopen(afilename, "r")) != NULL)
473 {
474 /*
475 * Read username...
476 */
477
478 if (fgets(aline, sizeof(aline), fp))
479 {
480 /*
481 * Decode username...
482 */
483
484 i = sizeof(username);
485 httpDecode64_2(username, &i, aline);
486
487 /*
488 * Read password...
489 */
490
491 if (fgets(aline, sizeof(aline), fp))
492 {
493 /*
494 * Decode password...
495 */
496
497 i = sizeof(password);
498 httpDecode64_2(password, &i, aline);
499 }
500 }
501
502 /*
503 * Close the file...
504 */
505
506 fclose(fp);
507 }
508 }
509 }
c8f9565c 510
511 /*
512 * Try connecting to the remote server...
513 */
514
22a980cc 515 fputs("STATE: +connecting-to-device\n", stderr);
516
97fcaf92 517 do
c8f9565c 518 {
a4e23897 519 fprintf(stderr, "INFO: Connecting to %s on port %d...\n", hostname, port);
c8f9565c 520
7914a6a1 521 if ((http = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL)
d21a7597 522 {
997cf8b0 523 if (getenv("CLASS") != NULL)
524 {
525 /*
526 * If the CLASS environment variable is set, the job was submitted
527 * to a class and not to a specific queue. In this case, we want
528 * to abort immediately so that the job can be requeued on the next
529 * available printer in the class.
530 */
531
072c4872 532 fprintf(stderr,
533 "INFO: Unable to connect to %s, queuing on next printer in "
534 "class...\n",
997cf8b0 535 hostname);
536
a684a3b0 537 if (argc == 6 || strcmp(filename, argv[6]))
997cf8b0 538 unlink(filename);
539
540 /*
541 * Sleep 5 seconds to keep the job from requeuing too rapidly...
542 */
543
544 sleep(5);
545
6248387b 546 return (CUPS_BACKEND_FAILED);
997cf8b0 547 }
548
4c2096b8 549 if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
550 errno == EHOSTUNREACH)
97fcaf92 551 {
072c4872 552 fprintf(stderr,
553 "INFO: Network host \'%s\' is busy; will retry in 30 "
554 "seconds...\n",
97fcaf92 555 hostname);
556 sleep(30);
557 }
7e3ba0af 558 else if (h_errno)
559 {
560 fprintf(stderr, "INFO: Unable to lookup host \'%s\' - %s\n",
561 hostname, hstrerror(h_errno));
562 sleep(30);
563 }
97fcaf92 564 else
565 {
566 perror("ERROR: Unable to connect to IPP host");
2cc18dd2 567 sleep(30);
97fcaf92 568 }
d21a7597 569 }
c8f9565c 570 }
97fcaf92 571 while (http == NULL);
c8f9565c 572
22a980cc 573 fputs("STATE: -connecting-to-device\n", stderr);
a4e23897 574 fprintf(stderr, "INFO: Connected to %s...\n", hostname);
575
c8f9565c 576 /*
577 * Build a URI for the printer and fill the standard IPP attributes for
8ce7c000 578 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
579 * might contain username:password information...
c8f9565c 580 */
581
3f9cb6c6 582 snprintf(uri, sizeof(uri), "%s://%s:%d%s", method, hostname, port, resource);
c8f9565c 583
584 /*
97fcaf92 585 * First validate the destination and see if the device supports multiple
586 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
587 * don't support the copies attribute...
c8f9565c 588 */
589
e12df069 590 copies_sup = NULL;
591 format_sup = NULL;
592 supported = NULL;
c8f9565c 593
97fcaf92 594 do
c8f9565c 595 {
596 /*
97fcaf92 597 * Build the IPP request...
c8f9565c 598 */
599
e12df069 600 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
601 request->request.op.version[1] = version;
c8f9565c 602
97fcaf92 603 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
604 NULL, uri);
c8f9565c 605
8f4595eb 606 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
607 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
608 NULL, pattrs);
609
97fcaf92 610 /*
611 * Do the request...
612 */
c8f9565c 613
a4e23897 614 fputs("DEBUG: Getting supported attributes...\n", stderr);
615
b5cb0608 616 if ((supported = cupsDoRequest(http, request, resource)) == NULL)
617 ipp_status = cupsLastError();
618 else
0a3ac972 619 ipp_status = supported->request.status.status_code;
b5cb0608 620
621 if (ipp_status > IPP_OK_CONFLICT)
c8f9565c 622 {
b5cb0608 623 if (ipp_status == IPP_PRINTER_BUSY ||
624 ipp_status == IPP_SERVICE_UNAVAILABLE)
c8f9565c 625 {
b5cb0608 626 fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr);
d1c2727f 627 report_printer_state(supported);
97fcaf92 628 sleep(10);
97fcaf92 629 }
b5cb0608 630 else if ((ipp_status == IPP_BAD_REQUEST ||
631 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version == 1)
c8f9565c 632 {
b5cb0608 633 /*
634 * Switch to IPP/1.0...
635 */
97fcaf92 636
072c4872 637 fputs("INFO: Printer does not support IPP/1.1, trying IPP/1.0...\n",
638 stderr);
b5cb0608 639 version = 0;
a4e23897 640 httpReconnect(http);
c8f9565c 641 }
67474245 642 else if (ipp_status == IPP_NOT_FOUND)
643 {
644 fputs("ERROR: Destination printer does not exist!\n", stderr);
645
646 if (supported)
647 ippDelete(supported);
648
6248387b 649 return (CUPS_BACKEND_STOP);
67474245 650 }
97fcaf92 651 else
67474245 652 {
d1c2727f 653 fprintf(stderr, "ERROR: Unable to get printer status (%s)!\n",
b5cb0608 654 ippErrorString(ipp_status));
67474245 655 sleep(10);
656 }
d1c2727f 657
658 if (supported)
659 ippDelete(supported);
660
661 continue;
97fcaf92 662 }
b5cb0608 663 else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
664 IPP_TAG_RANGE)) != NULL)
97fcaf92 665 {
b5cb0608 666 /*
667 * Has the "copies-supported" attribute - does it have an upper
668 * bound > 1?
669 */
97fcaf92 670
b5cb0608 671 if (copies_sup->values[0].range.upper <= 1)
672 copies_sup = NULL; /* No */
673 }
97fcaf92 674
e12df069 675 format_sup = ippFindAttribute(supported, "document-format-supported",
676 IPP_TAG_MIMETYPE);
b5cb0608 677
678 if (format_sup)
679 {
680 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
681 format_sup->num_values);
682 for (i = 0; i < format_sup->num_values; i ++)
683 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
684 format_sup->values[i].string.text);
c8f9565c 685 }
d1c2727f 686
687 report_printer_state(supported);
c8f9565c 688 }
97fcaf92 689 while (ipp_status > IPP_OK_CONFLICT);
c8f9565c 690
997cf8b0 691 /*
692 * See if the printer is accepting jobs and is not stopped; if either
693 * condition is true and we are printing to a class, requeue the job...
694 */
695
696 if (getenv("CLASS") != NULL)
697 {
698 printer_state = ippFindAttribute(supported, "printer-state",
699 IPP_TAG_ENUM);
700 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
701 IPP_TAG_BOOLEAN);
702
703 if (printer_state == NULL ||
072c4872 704 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
705 waitprinter) ||
997cf8b0 706 printer_accepting == NULL ||
707 !printer_accepting->values[0].boolean)
708 {
709 /*
710 * If the CLASS environment variable is set, the job was submitted
711 * to a class and not to a specific queue. In this case, we want
712 * to abort immediately so that the job can be requeued on the next
713 * available printer in the class.
714 */
715
072c4872 716 fprintf(stderr,
717 "INFO: Unable to queue job on %s, queuing on next printer in "
718 "class...\n",
997cf8b0 719 hostname);
720
721 ippDelete(supported);
722 httpClose(http);
723
a684a3b0 724 if (argc == 6 || strcmp(filename, argv[6]))
997cf8b0 725 unlink(filename);
726
727 /*
728 * Sleep 5 seconds to keep the job from requeuing too rapidly...
729 */
730
731 sleep(5);
732
6248387b 733 return (CUPS_BACKEND_FAILED);
997cf8b0 734 }
735 }
736
c8f9565c 737 /*
97fcaf92 738 * See if the printer supports multiple copies...
c8f9565c 739 */
740
d1c2727f 741 if (copies_sup || argc < 7)
97fcaf92 742 copies = 1;
0c5b0932 743 else
97fcaf92 744 copies = atoi(argv[4]);
0c5b0932 745
c8f9565c 746 /*
97fcaf92 747 * Then issue the print-job request...
c8f9565c 748 */
749
072c4872 750 job_id = 0;
d1c2727f 751
97fcaf92 752 while (copies > 0)
c8f9565c 753 {
754 /*
97fcaf92 755 * Build the IPP request...
c8f9565c 756 */
757
072c4872 758 if (job_cancelled)
759 break;
760
761 if (num_files > 1)
762 request = ippNewRequest(IPP_CREATE_JOB);
763 else
764 request = ippNewRequest(IPP_PRINT_JOB);
765
e12df069 766 request->request.op.version[1] = version;
c8f9565c 767
97fcaf92 768 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
769 NULL, uri);
c8f9565c 770
97fcaf92 771 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
772
753453e4 773 if (argv[2][0])
897922a9 774 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
775 "requesting-user-name", NULL, argv[2]);
97fcaf92 776
777 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
778
753453e4 779 if (argv[3][0])
780 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
781 argv[3]);
c8f9565c 782
97fcaf92 783 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
8ce7c000 784
1230a8a0 785#ifdef HAVE_LIBZ
786 if (compression)
787 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
788 "compression", NULL, "gzip");
789#endif /* HAVE_LIBZ */
790
c8f9565c 791 /*
97fcaf92 792 * Handle options on the command-line...
c8f9565c 793 */
794
97fcaf92 795 options = NULL;
796 num_options = cupsParseOptions(argv[5], 0, &options);
797
e7186d00 798#ifdef __APPLE__
072c4872 799 if (content_type != NULL &&
800 !strcasecmp(content_type, "application/pictwps") && num_files == 1)
e7186d00 801 {
802 if (format_sup != NULL)
803 {
804 for (i = 0; i < format_sup->num_values; i ++)
072c4872 805 if (!strcasecmp(content_type, format_sup->values[i].string.text))
e7186d00 806 break;
807 }
808
809 if (format_sup == NULL || i >= format_sup->num_values)
810 {
811 /*
812 * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
813 * so convert the document to PostScript...
814 */
815
b8f3410f 816 if (run_pictwps_filter(argv, filename))
6248387b 817 return (CUPS_BACKEND_FAILED);
e7186d00 818
b8f3410f 819 filename = pstmpname;
820
e7186d00 821 /*
8e1e3983 822 * Change the MIME type to application/postscript and change the
823 * number of copies to 1...
e7186d00 824 */
825
826 content_type = "application/postscript";
8e1e3983 827 copies = 1;
e7186d00 828 }
829 }
830#endif /* __APPLE__ */
831
753453e4 832 if (content_type != NULL && format_sup != NULL)
36aa99bb 833 {
834 for (i = 0; i < format_sup->num_values; i ++)
072c4872 835 if (!strcasecmp(content_type, format_sup->values[i].string.text))
36aa99bb 836 break;
837
838 if (i < format_sup->num_values)
839 num_options = cupsAddOption("document-format", content_type,
840 num_options, &options);
841 }
97fcaf92 842
753453e4 843 if (copies_sup)
844 {
845 /*
846 * Only send options if the destination printer supports the copies
847 * attribute. This is a hack for the HP JetDirect implementation of
848 * IPP, which does not accept extension attributes and incorrectly
849 * reports a client-error-bad-request error instead of the
850 * successful-ok-unsupported-attributes status. In short, at least
851 * some HP implementations of IPP are non-compliant.
852 */
853
854 cupsEncodeOptions(request, num_options, options);
855 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
856 atoi(argv[4]));
857 }
858
183914a3 859 cupsFreeOptions(num_options, options);
c8f9565c 860
a4e23897 861 /*
862 * If copies aren't supported, then we are likely dealing with an HP
863 * JetDirect. The HP IPP implementation seems to close the connection
864 * after every request (that is, it does *not* implement HTTP Keep-
865 * Alive, which is REQUIRED by HTTP/1.1...
866 */
867
868 if (!copies_sup)
869 httpReconnect(http);
870
cb555bcf 871 /*
b5cb0608 872 * Do the request...
cb555bcf 873 */
874
072c4872 875 if (num_files > 1)
876 response = cupsDoRequest(http, request, resource);
b5cb0608 877 else
072c4872 878 response = cupsDoFileRequest(http, request, resource, files[0]);
879
880 ipp_status = cupsLastError();
b5cb0608 881
882 if (ipp_status > IPP_OK_CONFLICT)
883 {
753453e4 884 job_id = 0;
885
072c4872 886 if (job_cancelled)
887 break;
888
b5cb0608 889 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
890 ipp_status == IPP_PRINTER_BUSY)
891 {
892 fputs("INFO: Printer is busy; retrying print job...\n", stderr);
893 sleep(10);
894 }
895 else
896 fprintf(stderr, "ERROR: Print file was not accepted (%s)!\n",
072c4872 897 cupsLastErrorString());
b5cb0608 898 }
899 else if ((job_id_attr = ippFindAttribute(response, "job-id",
900 IPP_TAG_INTEGER)) == NULL)
97fcaf92 901 {
f9aed4f2 902 fputs("NOTICE: Print file accepted - job ID unknown.\n", stderr);
b5cb0608 903 job_id = 0;
904 }
905 else
906 {
907 job_id = job_id_attr->values[0].integer;
f9aed4f2 908 fprintf(stderr, "NOTICE: Print file accepted - job ID %d.\n", job_id);
97fcaf92 909 }
cb555bcf 910
072c4872 911 ippDelete(response);
912
913 if (job_cancelled)
914 break;
915
916 if (job_id && num_files > 1)
917 {
918 for (i = 0; i < num_files; i ++)
919 {
920 request = ippNewRequest(IPP_SEND_DOCUMENT);
921
922 request->request.op.version[1] = version;
923
924 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
925 NULL, uri);
926
927 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
928 job_id);
929
930 if (argv[2][0])
931 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
932 "requesting-user-name", NULL, argv[2]);
933
934 if ((i + 1) == num_files)
935 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
936
937 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
938 "document-format", NULL, content_type);
939
940 ippDelete(cupsDoFileRequest(http, request, resource, files[i]));
941
942 if (cupsLastError() > IPP_OK_CONFLICT)
943 {
944 ipp_status = cupsLastError();
945
946 fprintf(stderr, "ERROR: Unable to add file %d to job: %s\n",
947 job_id, cupsLastErrorString());
948 break;
949 }
950 }
951 }
b5cb0608 952
753453e4 953 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
b5cb0608 954 {
955 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
956 copies --;
957 }
ab827512 958 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
959 ipp_status == IPP_PRINTER_BUSY)
b5cb0608 960 break;
ab827512 961 else
962 copies --;
8ce7c000 963
c8f9565c 964 /*
b5cb0608 965 * Wait for the job to complete...
c8f9565c 966 */
967
897922a9 968 if (!job_id || !waitjob)
b5cb0608 969 continue;
970
971 fputs("INFO: Waiting for job to complete...\n", stderr);
972
072c4872 973 for (; !job_cancelled;)
c8f9565c 974 {
97fcaf92 975 /*
b5cb0608 976 * Build an IPP_GET_JOB_ATTRIBUTES request...
97fcaf92 977 */
978
e12df069 979 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
980 request->request.op.version[1] = version;
97fcaf92 981
b5cb0608 982 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
983 NULL, uri);
3f9cb6c6 984
b5cb0608 985 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
986 job_id);
c8f9565c 987
ee8b7dd3 988 if (argv[2][0])
897922a9 989 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
990 "requesting-user-name", NULL, argv[2]);
ee8b7dd3 991
8f4595eb 992 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
993 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
994 NULL, jattrs);
97fcaf92 995
996 /*
b5cb0608 997 * Do the request...
97fcaf92 998 */
999
a4e23897 1000 if (!copies_sup)
1001 httpReconnect(http);
1002
072c4872 1003 response = cupsDoRequest(http, request, resource);
1004 ipp_status = cupsLastError();
97fcaf92 1005
b5cb0608 1006 if (ipp_status == IPP_NOT_FOUND)
97fcaf92 1007 {
b5cb0608 1008 /*
d1c2727f 1009 * Job has gone away and/or the server has no job history...
b5cb0608 1010 */
97fcaf92 1011
b5cb0608 1012 ippDelete(response);
d1c2727f 1013
1014 ipp_status = IPP_OK;
b5cb0608 1015 break;
97fcaf92 1016 }
1017
b5cb0608 1018 if (ipp_status > IPP_OK_CONFLICT)
6a536282 1019 {
b5cb0608 1020 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1021 ipp_status != IPP_PRINTER_BUSY)
97fcaf92 1022 {
072c4872 1023 ippDelete(response);
b5cb0608 1024
1025 fprintf(stderr, "ERROR: Unable to get job %d attributes (%s)!\n",
1026 job_id, ippErrorString(ipp_status));
1027 break;
97fcaf92 1028 }
6a536282 1029 }
8f4595eb 1030
072c4872 1031 if (response)
97fcaf92 1032 {
8f4595eb 1033 if ((job_state = ippFindAttribute(response, "job-state",
1034 IPP_TAG_ENUM)) != NULL)
97fcaf92 1035 {
8f4595eb 1036 /*
1037 * Stop polling if the job is finished or pending-held...
1038 */
1039
1040 if (job_state->values[0].integer > IPP_JOB_PROCESSING ||
1041 job_state->values[0].integer == IPP_JOB_HELD)
1042 {
e12df069 1043 if ((job_sheets = ippFindAttribute(response,
1044 "job-media-sheets-completed",
1045 IPP_TAG_INTEGER)) != NULL)
072c4872 1046 fprintf(stderr, "PAGE: total %d\n",
1047 job_sheets->values[0].integer);
ab827512 1048
8f4595eb 1049 ippDelete(response);
1050 break;
1051 }
97fcaf92 1052 }
97fcaf92 1053 }
1054
072c4872 1055 ippDelete(response);
d1c2727f 1056
b5cb0608 1057 /*
6bb5a528 1058 * Check the printer state and report it if necessary...
b5cb0608 1059 */
c8f9565c 1060
e12df069 1061 check_printer_state(http, uri, resource, argv[2], version);
d1c2727f 1062
1063 /*
1064 * Wait 10 seconds before polling again...
1065 */
97fcaf92 1066
b5cb0608 1067 sleep(10);
97fcaf92 1068 }
c8f9565c 1069 }
1070
6bb5a528 1071 /*
072c4872 1072 * Cancel the job as needed...
6bb5a528 1073 */
1074
072c4872 1075 if (job_cancelled && job_id)
1076 cancel_job(http, uri, job_id, resource, argv[2], version);
1077
1078 /*
1079 * Check the printer state and report it if necessary...
1080 */
6bb5a528 1081
e12df069 1082 check_printer_state(http, uri, resource, argv[2], version);
6bb5a528 1083
c8f9565c 1084 /*
1085 * Free memory...
1086 */
1087
1088 httpClose(http);
c8f9565c 1089
072c4872 1090 ippDelete(supported);
2922de55 1091
c8f9565c 1092 /*
b8f3410f 1093 * Remove the temporary file(s) if necessary...
c8f9565c 1094 */
1095
d8e1f369 1096 if (tmpfilename[0])
8f4595eb 1097 unlink(tmpfilename);
c8f9565c 1098
1230a8a0 1099#ifdef HAVE_LIBZ
1100 if (compression)
1101 {
1102 for (i = 0; i < num_files; i ++)
1103 unlink(files[i]);
1104 }
1105#endif /* HAVE_LIBZ */
1106
b8f3410f 1107#ifdef __APPLE__
1108 if (pstmpname[0])
1109 unlink(pstmpname);
1110#endif /* __APPLE__ */
1111
c8f9565c 1112 /*
1113 * Return the queue status...
1114 */
1115
6248387b 1116 return (ipp_status > IPP_OK_CONFLICT ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
b5cb0608 1117}
1118
1119
072c4872 1120/*
1121 * 'cancel_job()' - Cancel a print job.
1122 */
1123
1124static void
1125cancel_job(http_t *http, /* I - HTTP connection */
1126 const char *uri, /* I - printer-uri */
1127 int id, /* I - job-id */
1128 const char *resource, /* I - Resource path */
1129 const char *user, /* I - requesting-user-name */
1130 int version) /* I - IPP version */
1131{
1132 ipp_t *request; /* Cancel-Job request */
1133
1134
1135 fputs("INFO: Cancelling print job...\n", stderr);
1136
1137 request = ippNewRequest(IPP_CANCEL_JOB);
1138 request->request.op.version[1] = version;
1139
1140 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1141 NULL, uri);
1142 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1143
1144 if (user && user[0])
1145 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1146 "requesting-user-name", NULL, user);
1147
1148 /*
1149 * Do the request...
1150 */
1151
1152 ippDelete(cupsDoRequest(http, request, resource));
1153
1154 if (cupsLastError() > IPP_OK_CONFLICT)
1155 fprintf(stderr, "ERROR: Unable to cancel job %d: %s\n", id,
1156 cupsLastErrorString());
1157}
1158
1159
6bb5a528 1160/*
1161 * 'check_printer_state()' - Check the printer state...
1162 */
1163
072c4872 1164static void
e12df069 1165check_printer_state(
1166 http_t *http, /* I - HTTP connection */
1167 const char *uri, /* I - Printer URI */
1168 const char *resource, /* I - Resource path */
1169 const char *user, /* I - Username, if any */
1170 int version) /* I - IPP version */
6bb5a528 1171{
1172 ipp_t *request, /* IPP request */
1173 *response; /* IPP response */
1174
1175
1176 /*
1177 * Check on the printer state...
1178 */
1179
e12df069 1180 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1181 request->request.op.version[1] = version;
6bb5a528 1182
1183 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1184 NULL, uri);
1185
1186 if (user && user[0])
1187 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1188 "requesting-user-name", NULL, user);
1189
1190 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1191 "requested-attributes", NULL, "printer-state-reasons");
1192
1193 /*
1194 * Do the request...
1195 */
1196
1197 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1198 {
1199 report_printer_state(response);
1200 ippDelete(response);
1201 }
1202}
1203
1204
1230a8a0 1205#ifdef HAVE_LIBZ
1206/*
1207 * 'compress_files()' - Compress print files...
1208 */
1209
1210static void
1211compress_files(int num_files, /* I - Number of files */
1212 char **files) /* I - Files */
1213{
1214 int i, /* Looping var */
1215 fd; /* Temporary file descriptor */
1216 ssize_t bytes; /* Bytes read/written */
1217 size_t total; /* Total bytes read */
1218 cups_file_t *in, /* Input file */
1219 *out; /* Output file */
1220 struct stat outinfo; /* Output file information */
1221 char filename[1024], /* Temporary filename */
1fb9f3e8 1222 buffer[32768]; /* Copy buffer */
1230a8a0 1223
1224
1225 fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1226 for (i = 0; i < num_files; i ++)
1227 {
1228 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1229 {
1230 perror("ERROR: Unable to create temporary compressed print file");
1231 exit(CUPS_BACKEND_FAILED);
1232 }
1233
1234 if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1235 {
1236 perror("ERROR: Unable to open temporary compressed print file");
1237 exit(CUPS_BACKEND_FAILED);
1238 }
1239
1240 if ((in = cupsFileOpen(files[i], "r")) == NULL)
1241 {
1242 fprintf(stderr, "ERROR: Unable to open print file \"%s\": %s\n",
1243 files[i], strerror(errno));
1244 cupsFileClose(out);
1245 exit(CUPS_BACKEND_FAILED);
1246 }
1247
1248 total = 0;
1249 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1250 if (cupsFileWrite(out, buffer, bytes) < bytes)
1251 {
22a980cc 1252 fprintf(stderr, "ERROR: Unable to write " CUPS_LLFMT " bytes to \"%s\": %s\n",
1253 CUPS_LLCAST bytes, filename, strerror(errno));
1230a8a0 1254 cupsFileClose(in);
1255 cupsFileClose(out);
1256 exit(CUPS_BACKEND_FAILED);
1257 }
1258 else
1259 total += bytes;
1260
1261 cupsFileClose(out);
1262 cupsFileClose(in);
1263
1264 files[i] = strdup(filename);
1265
1266 if (!stat(filename, &outinfo))
1267 fprintf(stderr,
1268 "DEBUG: File %d compressed to %.1f%% of original size, "
1269 CUPS_LLFMT " bytes...\n",
fc22a985 1270 i + 1, 100.0 * outinfo.st_size / total,
1271 CUPS_LLCAST outinfo.st_size);
1230a8a0 1272 }
1273}
1274#endif /* HAVE_LIBZ */
1275
1276
b5cb0608 1277/*
1278 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1279 */
1280
072c4872 1281static const char * /* O - Password */
e12df069 1282password_cb(const char *prompt) /* I - Prompt (not used) */
b5cb0608 1283{
1284 (void)prompt;
1285
6248387b 1286 if (password)
1287 return (password);
1288 else
1289 {
1290 /*
1291 * If there is no password set in the device URI, return the
1292 * "authentication required" exit code...
1293 */
1294
1295 if (tmpfilename[0])
1296 unlink(tmpfilename);
1297
1298#ifdef __APPLE__
1299 if (pstmpname[0])
1300 unlink(pstmpname);
1301#endif /* __APPLE__ */
1302
1303 exit(CUPS_BACKEND_AUTH_REQUIRED);
be5262d8 1304
1305 return (NULL); /* Eliminate compiler warning */
6248387b 1306 }
c8f9565c 1307}
1308
1309
1310/*
d1c2727f 1311 * 'report_printer_state()' - Report the printer state.
1312 */
1313
072c4872 1314static int /* O - Number of reasons shown */
d1c2727f 1315report_printer_state(ipp_t *ipp) /* I - IPP response */
1316{
1317 int i; /* Looping var */
d8076497 1318 int count; /* Count of reasons shown... */
d1c2727f 1319 ipp_attribute_t *reasons; /* printer-state-reasons */
8f4595eb 1320 const char *reason; /* Current reason */
d1c2727f 1321 const char *message; /* Message to show */
d8076497 1322 char unknown[1024]; /* Unknown message string */
8f4595eb 1323 const char *prefix; /* Prefix for STATE: line */
1324 char state[1024]; /* State string */
d1c2727f 1325
1326
1327 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
1328 IPP_TAG_KEYWORD)) == NULL)
1329 return (0);
1330
8f4595eb 1331 state[0] = '\0';
1332 prefix = "STATE: ";
1333
d8076497 1334 for (i = 0, count = 0; i < reasons->num_values; i ++)
d1c2727f 1335 {
8f4595eb 1336 reason = reasons->values[i].string.text;
1337
1338 strlcat(state, prefix, sizeof(state));
1339 strlcat(state, reason, sizeof(state));
1340
1341 prefix = ",";
d1c2727f 1342 message = NULL;
1343
5baa731f 1344 if (!strncmp(reason, "media-needed", 12))
d1c2727f 1345 message = "Media tray needs to be filled.";
5baa731f 1346 else if (!strncmp(reason, "media-jam", 9))
d1c2727f 1347 message = "Media jam!";
5baa731f 1348 else if (!strncmp(reason, "moving-to-paused", 16) ||
1349 !strncmp(reason, "paused", 6) ||
1350 !strncmp(reason, "shutdown", 8))
d1c2727f 1351 message = "Printer off-line.";
5baa731f 1352 else if (!strncmp(reason, "toner-low", 9))
d1c2727f 1353 message = "Toner low.";
5baa731f 1354 else if (!strncmp(reason, "toner-empty", 11))
d1c2727f 1355 message = "Out of toner!";
5baa731f 1356 else if (!strncmp(reason, "cover-open", 10))
d1c2727f 1357 message = "Cover open.";
5baa731f 1358 else if (!strncmp(reason, "interlock-open", 14))
d1c2727f 1359 message = "Interlock open.";
5baa731f 1360 else if (!strncmp(reason, "door-open", 9))
d1c2727f 1361 message = "Door open.";
5baa731f 1362 else if (!strncmp(reason, "input-tray-missing", 18))
d1c2727f 1363 message = "Media tray missing!";
5baa731f 1364 else if (!strncmp(reason, "media-low", 9))
d1c2727f 1365 message = "Media tray almost empty.";
5baa731f 1366 else if (!strncmp(reason, "media-empty", 11))
d1c2727f 1367 message = "Media tray empty!";
5baa731f 1368 else if (!strncmp(reason, "output-tray-missing", 19))
d1c2727f 1369 message = "Output tray missing!";
5baa731f 1370 else if (!strncmp(reason, "output-area-almost-full", 23))
d1c2727f 1371 message = "Output bin almost full.";
5baa731f 1372 else if (!strncmp(reason, "output-area-full", 16))
d1c2727f 1373 message = "Output bin full!";
5baa731f 1374 else if (!strncmp(reason, "marker-supply-low", 17))
d1c2727f 1375 message = "Ink/toner almost empty.";
5baa731f 1376 else if (!strncmp(reason, "marker-supply-empty", 19))
d1c2727f 1377 message = "Ink/toner empty!";
5baa731f 1378 else if (!strncmp(reason, "marker-waste-almost-full", 24))
d1c2727f 1379 message = "Ink/toner waste bin almost full.";
5baa731f 1380 else if (!strncmp(reason, "marker-waste-full", 17))
d1c2727f 1381 message = "Ink/toner waste bin full!";
5baa731f 1382 else if (!strncmp(reason, "fuser-over-temp", 15))
d1c2727f 1383 message = "Fuser temperature high!";
5baa731f 1384 else if (!strncmp(reason, "fuser-under-temp", 16))
d1c2727f 1385 message = "Fuser temperature low!";
5baa731f 1386 else if (!strncmp(reason, "opc-near-eol", 12))
d1c2727f 1387 message = "OPC almost at end-of-life.";
5baa731f 1388 else if (!strncmp(reason, "opc-life-over", 13))
d1c2727f 1389 message = "OPC at end-of-life!";
5baa731f 1390 else if (!strncmp(reason, "developer-low", 13))
d1c2727f 1391 message = "Developer almost empty.";
5baa731f 1392 else if (!strncmp(reason, "developer-empty", 15))
d1c2727f 1393 message = "Developer empty!";
8f4595eb 1394 else if (strstr(reason, "error") != NULL)
d8076497 1395 {
1396 message = unknown;
1397
1398 snprintf(unknown, sizeof(unknown), "Unknown printer error (%s)!",
8f4595eb 1399 reason);
d8076497 1400 }
d1c2727f 1401
1402 if (message)
1403 {
d8076497 1404 count ++;
d1c2727f 1405 if (strstr(reasons->values[i].string.text, "error"))
1406 fprintf(stderr, "ERROR: %s\n", message);
1407 else if (strstr(reasons->values[i].string.text, "warning"))
1408 fprintf(stderr, "WARNING: %s\n", message);
1409 else
1410 fprintf(stderr, "INFO: %s\n", message);
1411 }
1412 }
1413
8f4595eb 1414 fprintf(stderr, "%s\n", state);
1415
d8076497 1416 return (count);
d1c2727f 1417}
1418
1419
a684a3b0 1420#ifdef __APPLE__
1421/*
1422 * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
1423 * remotely.
1424 *
1425 * This step is required because the PICT format is not documented and
1426 * subject to change, so developing a filter for other OS's is infeasible.
1427 * Also, fonts required by the PICT file need to be embedded on the
1428 * client side (which has the fonts), so we run the filter to get a
1429 * PostScript file for printing...
1430 */
1431
072c4872 1432static int /* O - Exit status of filter */
e12df069 1433run_pictwps_filter(char **argv, /* I - Command-line arguments */
1434 const char *filename)/* I - Filename */
a684a3b0 1435{
e12df069 1436 struct stat fileinfo; /* Print file information */
1437 const char *ppdfile; /* PPD file for destination printer */
1438 int pid; /* Child process ID */
1439 int fd; /* Temporary file descriptor */
1440 int status; /* Exit status of filter */
1441 const char *printer; /* PRINTER env var */
1442 static char ppdenv[1024]; /* PPD environment variable */
a684a3b0 1443
1444
1445 /*
1446 * First get the PPD file for the printer...
1447 */
1448
1449 printer = getenv("PRINTER");
1450 if (!printer)
1451 {
1452 fputs("ERROR: PRINTER environment variable not defined!\n", stderr);
1453 return (-1);
1454 }
1455
1456 if ((ppdfile = cupsGetPPD(printer)) == NULL)
1457 {
1458 fprintf(stderr, "ERROR: Unable to get PPD file for printer \"%s\" - %s.\n",
1459 printer, ippErrorString(cupsLastError()));
1c85e5c0 1460 /*return (-1);*/
1461 }
1462 else
1463 {
1464 snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
1465 putenv(ppdenv);
a684a3b0 1466 }
a684a3b0 1467
1468 /*
1469 * Then create a temporary file for printing...
1470 */
1471
b8f3410f 1472 if ((fd = cupsTempFd(pstmpname, sizeof(pstmpname))) < 0)
a684a3b0 1473 {
1474 fprintf(stderr, "ERROR: Unable to create temporary file - %s.\n",
1c85e5c0 1475 strerror(errno));
1476 if (ppdfile)
1477 unlink(ppdfile);
a684a3b0 1478 return (-1);
1479 }
1480
1481 /*
1482 * Get the owner of the spool file - it is owned by the user we want to run
1483 * as...
1484 */
1485
b8f3410f 1486 if (argv[6])
1487 stat(argv[6], &fileinfo);
1488 else
1489 {
1490 /*
1491 * Use the OSX defaults, as an up-stream filter created the PICT
1492 * file...
1493 */
1494
1495 fileinfo.st_uid = 1;
1496 fileinfo.st_gid = 80;
1497 }
a684a3b0 1498
1c85e5c0 1499 if (ppdfile)
1500 chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
1501
1502 fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
1503
a684a3b0 1504 /*
1505 * Finally, run the filter to convert the file...
1506 */
1507
1508 if ((pid = fork()) == 0)
1509 {
1510 /*
1511 * Child process for pictwpstops... Redirect output of pictwpstops to a
1512 * file...
1513 */
1514
1515 close(1);
1516 dup(fd);
1517 close(fd);
1518
1519 if (!getuid())
1520 {
1521 /*
1522 * Change to an unpriviledged user...
1523 */
1524
1525 setgid(fileinfo.st_gid);
1526 setuid(fileinfo.st_uid);
1527 }
1528
1529 execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
b8f3410f 1530 filename, NULL);
a684a3b0 1531 perror("ERROR: Unable to exec pictwpstops");
1532 return (errno);
1533 }
1c85e5c0 1534
1535 close(fd);
1536
1537 if (pid < 0)
a684a3b0 1538 {
1539 /*
1540 * Error!
1541 */
1542
1543 perror("ERROR: Unable to fork pictwpstops");
a684a3b0 1544 unlink(filename);
1c85e5c0 1545 if (ppdfile)
1546 unlink(ppdfile);
a684a3b0 1547 return (-1);
1548 }
1549
1550 /*
1551 * Now wait for the filter to complete...
1552 */
1553
1554 if (wait(&status) < 0)
1555 {
1556 perror("ERROR: Unable to wait for pictwpstops");
1557 close(fd);
1558 unlink(filename);
1c85e5c0 1559 if (ppdfile)
1560 unlink(ppdfile);
a684a3b0 1561 return (-1);
1562 }
1563
1c85e5c0 1564 if (ppdfile)
1565 unlink(ppdfile);
1566
a684a3b0 1567 close(fd);
1568
1569 if (status)
1570 {
1571 if (status >= 256)
1572 fprintf(stderr, "ERROR: pictwpstops exited with status %d!\n",
1573 status / 256);
1574 else
1575 fprintf(stderr, "ERROR: pictwpstops exited on signal %d!\n",
1576 status);
1577
1578 unlink(filename);
1579 return (status);
1580 }
1581
1582 /*
1583 * Return with no errors..
1584 */
1585
1586 return (0);
1587}
1588#endif /* __APPLE__ */
1589
d1c2727f 1590
1591/*
8f4595eb 1592 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1593 */
1594
1595static void
1596sigterm_handler(int sig) /* I - Signal */
1597{
1598 (void)sig; /* remove compiler warnings... */
1599
072c4872 1600 if (!job_cancelled)
1601 {
1602 /*
1603 * Flag that the job should be cancelled...
1604 */
1605
1606 job_cancelled = 1;
1607 return;
1608 }
1609
8f4595eb 1610 /*
072c4872 1611 * The scheduler already tried to cancel us once, now just terminate
1612 * after removing our temp files!
8f4595eb 1613 */
1614
1615 if (tmpfilename[0])
1616 unlink(tmpfilename);
1617
b8f3410f 1618#ifdef __APPLE__
1619 if (pstmpname[0])
1620 unlink(pstmpname);
1621#endif /* __APPLE__ */
1622
8f4595eb 1623 exit(1);
1624}
1625
1626
1627/*
c9d3f842 1628 * End of "$Id$".
c8f9565c 1629 */