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