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