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