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