Revert manifest to default one
[external/cups.git] / backend / ipp.c
1 /*
2  * "$Id: ipp.c 10452 2012-05-04 23:00:01Z mike $"
3  *
4  *   IPP backend for CUPS.
5  *
6  *   Copyright 2007-2012 by Apple Inc.
7  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
8  *
9  *   These coded instructions, statements, and computer programs are the
10  *   property of Apple Inc. and are protected by Federal copyright
11  *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12  *   "LICENSE" which should have been included with this file.  If this
13  *   file is missing or damaged, see the license at "http://www.cups.org/".
14  *
15  *   This file is subject to the Apple OS-Developed Software exception.
16  *
17  * Contents:
18  *
19  *   main()                 - Send a file to the printer or server.
20  *   cancel_job()           - Cancel a print job.
21  *   check_printer_state()  - Check the printer state.
22  *   compress_files()       - Compress print files.
23  *   monitor_printer()      - Monitor the printer state.
24  *   new_request()          - Create a new print creation or validation request.
25  *   password_cb()          - Disable the password prompt for
26  *                            cupsDoFileRequest().
27  *   report_attr()          - Report an IPP attribute value.
28  *   report_printer_state() - Report the printer state.
29  *   run_as_user()          - Run the IPP backend as the printing user.
30  *   timeout_cb()           - Handle HTTP timeouts.
31  *   sigterm_handler()      - Handle 'terminate' signals that stop the backend.
32  */
33
34 /*
35  * Include necessary headers.
36  */
37
38 #include "backend-private.h"
39 #include <cups/array-private.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/wait.h>
43 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
44 #  include <xpc/xpc.h>
45 #  define kPMPrintUIToolAgent   "com.apple.printuitool.agent"
46 #  define kPMStartJob           100
47 #  define kPMWaitForJob         101
48 extern void     xpc_connection_set_target_uid(xpc_connection_t connection,
49                                               uid_t uid);
50 #endif /* HAVE_GSSAPI && HAVE_XPC */
51
52
53 /*
54  * Types...
55  */
56
57 typedef struct _cups_monitor_s          /**** Monitoring data ****/
58 {
59   const char            *uri,           /* Printer URI */
60                         *hostname,      /* Hostname */
61                         *user,          /* Username */
62                         *resource;      /* Resource path */
63   int                   port,           /* Port number */
64                         version,        /* IPP version */
65                         job_id,         /* Job ID for submitted job */
66                         get_job_attrs;  /* Support Get-Job-Attributes? */
67   const char            *job_name;      /* Job name for submitted job */
68   http_encryption_t     encryption;     /* Use encryption? */
69   ipp_jstate_t          job_state;      /* Current job state */
70   ipp_pstate_t          printer_state;  /* Current printer state */
71 } _cups_monitor_t;
72
73
74 /*
75  * Globals...
76  */
77
78 static const char       *auth_info_required;
79                                         /* New auth-info-required value */
80 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
81 static int              child_pid = 0;  /* Child process ID */
82 #endif /* HAVE_GSSAPI && HAVE_XPC */
83 static const char * const jattrs[] =    /* Job attributes we want */
84 {
85   "job-impressions-completed",
86   "job-media-sheets-completed",
87   "job-name",
88   "job-originating-user-name",
89   "job-state",
90   "job-state-reasons"
91 };
92 static int              job_canceled = 0;
93                                         /* Job cancelled? */
94 static char             username[256] = "",
95                                         /* Username for device URI */
96                         *password = NULL;
97                                         /* Password for device URI */
98 static int              password_tries = 0;
99                                         /* Password tries */
100 static const char * const pattrs[] =    /* Printer attributes we want */
101 {
102   "copies-supported",
103   "cups-version",
104   "document-format-supported",
105   "marker-colors",
106   "marker-high-levels",
107   "marker-levels",
108   "marker-low-levels",
109   "marker-message",
110   "marker-names",
111   "marker-types",
112   "media-col-supported",
113   "multiple-document-handling-supported",
114   "operations-supported",
115   "printer-alert",
116   "printer-alert-description",
117   "printer-is-accepting-jobs",
118   "printer-state",
119   "printer-state-message",
120   "printer-state-reasons"
121 };
122 static const char * const remote_job_states[] =
123 {                                       /* Remote job state keywords */
124   "+cups-remote-pending",
125   "+cups-remote-pending-held",
126   "+cups-remote-processing",
127   "+cups-remote-stopped",
128   "+cups-remote-canceled",
129   "+cups-remote-aborted",
130   "+cups-remote-completed"
131 };
132 static _cups_mutex_t    report_mutex = _CUPS_MUTEX_INITIALIZER;
133                                         /* Mutex to control access */
134 static int              num_attr_cache = 0;
135                                         /* Number of cached attributes */
136 static cups_option_t    *attr_cache = NULL;
137                                         /* Cached attributes */
138 static cups_array_t     *state_reasons; /* Array of printe-state-reasons keywords */
139 static char             tmpfilename[1024] = "";
140                                         /* Temporary spool file name */
141
142
143 /*
144  * Local functions...
145  */
146
147 static void             cancel_job(http_t *http, const char *uri, int id,
148                                    const char *resource, const char *user,
149                                    int version);
150 static ipp_pstate_t     check_printer_state(http_t *http, const char *uri,
151                                             const char *resource,
152                                             const char *user, int version);
153 #ifdef HAVE_LIBZ
154 static void             compress_files(int num_files, char **files);
155 #endif /* HAVE_LIBZ */
156 static void             *monitor_printer(_cups_monitor_t *monitor);
157 static ipp_t            *new_request(ipp_op_t op, int version, const char *uri,
158                                      const char *user, const char *title,
159                                      int num_options, cups_option_t *options,
160                                      const char *compression, int copies,
161                                      const char *format, _ppd_cache_t *pc,
162                                      ipp_attribute_t *media_col_sup,
163                                      ipp_attribute_t *doc_handling_sup);
164 static const char       *password_cb(const char *);
165 static void             report_attr(ipp_attribute_t *attr);
166 static void             report_printer_state(ipp_t *ipp);
167 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
168 static int              run_as_user(int argc, char *argv[], uid_t uid,
169                                     const char *device_uri, int fd);
170 #endif /* HAVE_GSSAPI && HAVE_XPC */
171 static void             sigterm_handler(int sig);
172 static int              timeout_cb(http_t *http, void *user_data);
173 static void             update_reasons(ipp_attribute_t *attr, const char *s);
174
175
176 /*
177  * 'main()' - Send a file to the printer or server.
178  *
179  * Usage:
180  *
181  *    printer-uri job-id user title copies options [file]
182  */
183
184 int                                     /* O - Exit status */
185 main(int  argc,                         /* I - Number of command-line args */
186      char *argv[])                      /* I - Command-line arguments */
187 {
188   int           i;                      /* Looping var */
189   int           send_options;           /* Send job options? */
190   int           num_options;            /* Number of printer options */
191   cups_option_t *options;               /* Printer options */
192   const char    *device_uri;            /* Device URI */
193   char          scheme[255],            /* Scheme in URI */
194                 hostname[1024],         /* Hostname */
195                 resource[1024],         /* Resource info (printer name) */
196                 addrname[256],          /* Address name */
197                 *optptr,                /* Pointer to URI options */
198                 *name,                  /* Name of option */
199                 *value,                 /* Value of option */
200                 sep;                    /* Separator character */
201   http_addrlist_t *addrlist;            /* Address of printer */
202   int           snmp_fd,                /* SNMP socket */
203                 start_count,            /* Page count via SNMP at start */
204                 page_count,             /* Page count via SNMP */
205                 have_supplies;          /* Printer supports supply levels? */
206   int           num_files;              /* Number of files to print */
207   char          **files,                /* Files to print */
208                 *compatfile = NULL;     /* Compatibility filename */
209   off_t         compatsize = 0;         /* Size of compatibility file */
210   int           port;                   /* Port number (not used) */
211   char          portname[255];          /* Port name */
212   char          uri[HTTP_MAX_URI];      /* Updated URI without user/pass */
213   char          print_job_name[1024];   /* Update job-name for Print-Job */
214   http_status_t http_status;            /* Status of HTTP request */
215   ipp_status_t  ipp_status;             /* Status of IPP request */
216   http_t        *http;                  /* HTTP connection */
217   ipp_t         *request,               /* IPP request */
218                 *response,              /* IPP response */
219                 *supported;             /* get-printer-attributes response */
220   time_t        start_time;             /* Time of first connect */
221   int           contimeout;             /* Connection timeout */
222   int           delay,                  /* Delay for retries */
223                 prev_delay;             /* Previous delay */
224   const char    *compression;           /* Compression mode */
225   int           waitjob,                /* Wait for job complete? */
226                 waitprinter;            /* Wait for printer ready? */
227   _cups_monitor_t monitor;              /* Monitoring data */
228   ipp_attribute_t *job_id_attr;         /* job-id attribute */
229   int           job_id;                 /* job-id value */
230   ipp_attribute_t *job_sheets;          /* job-media-sheets-completed */
231   ipp_attribute_t *job_state;           /* job-state */
232   ipp_attribute_t *copies_sup;          /* copies-supported */
233   ipp_attribute_t *cups_version;        /* cups-version */
234   ipp_attribute_t *format_sup;          /* document-format-supported */
235   ipp_attribute_t *media_col_sup;       /* media-col-supported */
236   ipp_attribute_t *operations_sup;      /* operations-supported */
237   ipp_attribute_t *doc_handling_sup;    /* multiple-document-handling-supported */
238   ipp_attribute_t *printer_state;       /* printer-state attribute */
239   ipp_attribute_t *printer_accepting;   /* printer-is-accepting-jobs */
240   int           create_job = 0,         /* Does printer support Create-Job? */
241                 get_job_attrs = 0,      /* Does printer support Get-Job-Attributes? */
242                 send_document = 0,      /* Does printer support Send-Document? */
243                 validate_job = 0;       /* Does printer support Validate-Job? */
244   int           copies,                 /* Number of copies for job */
245                 copies_remaining;       /* Number of copies remaining */
246   const char    *content_type,          /* CONTENT_TYPE environment variable */
247                 *final_content_type,    /* FINAL_CONTENT_TYPE environment var */
248                 *document_format;       /* document-format value */
249   int           fd;                     /* File descriptor */
250   off_t         bytes = 0;              /* Bytes copied */
251   char          buffer[16384];          /* Copy buffer */
252 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
253   struct sigaction action;              /* Actions for POSIX signals */
254 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
255   int           version;                /* IPP version */
256   ppd_file_t    *ppd;                   /* PPD file */
257   _ppd_cache_t  *pc;                    /* PPD cache and mapping data */
258   fd_set        input;                  /* Input set for select() */
259
260
261  /*
262   * Make sure status messages are not buffered...
263   */
264
265   setbuf(stderr, NULL);
266
267  /*
268   * Ignore SIGPIPE and catch SIGTERM signals...
269   */
270
271 #ifdef HAVE_SIGSET
272   sigset(SIGPIPE, SIG_IGN);
273   sigset(SIGTERM, sigterm_handler);
274 #elif defined(HAVE_SIGACTION)
275   memset(&action, 0, sizeof(action));
276   action.sa_handler = SIG_IGN;
277   sigaction(SIGPIPE, &action, NULL);
278
279   sigemptyset(&action.sa_mask);
280   sigaddset(&action.sa_mask, SIGTERM);
281   action.sa_handler = sigterm_handler;
282   sigaction(SIGTERM, &action, NULL);
283 #else
284   signal(SIGPIPE, SIG_IGN);
285   signal(SIGTERM, sigterm_handler);
286 #endif /* HAVE_SIGSET */
287
288  /*
289   * Check command-line...
290   */
291
292   if (argc == 1)
293   {
294     char *s;
295
296     if ((s = strrchr(argv[0], '/')) != NULL)
297       s ++;
298     else
299       s = argv[0];
300
301     printf("network %s \"Unknown\" \"%s (%s)\"\n",
302            s, _cupsLangString(cupsLangDefault(),
303                               _("Internet Printing Protocol")), s);
304     return (CUPS_BACKEND_OK);
305   }
306   else if (argc < 6)
307   {
308     _cupsLangPrintf(stderr,
309                     _("Usage: %s job-id user title copies options [file]"),
310                     argv[0]);
311     return (CUPS_BACKEND_STOP);
312   }
313
314  /*
315   * Get the device URI...
316   */
317
318   while ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
319   {
320     _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
321     sleep(10);
322
323     if (getenv("CLASS") != NULL)
324       return (CUPS_BACKEND_FAILED);
325   }
326
327   if ((auth_info_required = getenv("AUTH_INFO_REQUIRED")) == NULL)
328     auth_info_required = "none";
329
330   state_reasons = _cupsArrayNewStrings(getenv("PRINTER_STATE_REASONS"));
331
332 #ifdef HAVE_GSSAPI
333  /*
334   * For Kerberos, become the printing user (if we can) to get the credentials
335   * that way.
336   */
337
338   if (!getuid() && (value = getenv("AUTH_UID")) != NULL)
339   {
340     uid_t       uid = (uid_t)atoi(value);
341                                         /* User ID */
342
343 #  ifdef HAVE_XPC
344     if (uid > 0)
345     {
346       if (argc == 6)
347         return (run_as_user(argc, argv, uid, device_uri, 0));
348       else
349       {
350         int status = 0;                 /* Exit status */
351
352         for (i = 6; i < argc && !status && !job_canceled; i ++)
353         {
354           if ((fd = open(argv[i], O_RDONLY)) >= 0)
355           {
356             status = run_as_user(argc, argv, uid, device_uri, fd);
357             close(fd);
358           }
359           else
360           {
361             _cupsLangPrintError("ERROR", _("Unable to open print file"));
362             status = CUPS_BACKEND_FAILED;
363           }
364         }
365
366         return (status);
367       }
368     }
369
370 #  else /* No XPC, just try to run as the user ID */
371     if (uid > 0)
372       seteuid(uid);
373 #  endif /* HAVE_XPC */
374   }
375 #endif /* HAVE_GSSAPI */
376
377  /*
378   * Get the (final) content type...
379   */
380
381   if ((content_type = getenv("CONTENT_TYPE")) == NULL)
382     content_type = "application/octet-stream";
383
384   if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
385   {
386     final_content_type = content_type;
387
388     if (!strncmp(final_content_type, "printer/", 8))
389       final_content_type = "application/vnd.cups-raw";
390   }
391
392  /*
393   * Extract the hostname and printer name from the URI...
394   */
395
396   httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
397                   username, sizeof(username), hostname, sizeof(hostname), &port,
398                   resource, sizeof(resource));
399
400   if (!port)
401     port = IPP_PORT;                    /* Default to port 631 */
402
403   if (!strcmp(scheme, "https"))
404     cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
405   else
406     cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
407
408  /*
409   * See if there are any options...
410   */
411
412   compression = NULL;
413   version     = 20;
414   waitjob     = 1;
415   waitprinter = 1;
416   contimeout  = 7 * 24 * 60 * 60;
417
418   if ((optptr = strchr(resource, '?')) != NULL)
419   {
420    /*
421     * Yup, terminate the device name string and move to the first
422     * character of the optptr...
423     */
424
425     *optptr++ = '\0';
426
427    /*
428     * Then parse the optptr...
429     */
430
431     while (*optptr)
432     {
433      /*
434       * Get the name...
435       */
436
437       name = optptr;
438
439       while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
440         optptr ++;
441
442       if ((sep = *optptr) != '\0')
443         *optptr++ = '\0';
444
445       if (sep == '=')
446       {
447        /*
448         * Get the value...
449         */
450
451         value = optptr;
452
453         while (*optptr && *optptr != '+' && *optptr != '&')
454           optptr ++;
455
456         if (*optptr)
457           *optptr++ = '\0';
458       }
459       else
460         value = (char *)"";
461
462      /*
463       * Process the option...
464       */
465
466       if (!_cups_strcasecmp(name, "waitjob"))
467       {
468        /*
469         * Wait for job completion?
470         */
471
472         waitjob = !_cups_strcasecmp(value, "on") ||
473                   !_cups_strcasecmp(value, "yes") ||
474                   !_cups_strcasecmp(value, "true");
475       }
476       else if (!_cups_strcasecmp(name, "waitprinter"))
477       {
478        /*
479         * Wait for printer idle?
480         */
481
482         waitprinter = !_cups_strcasecmp(value, "on") ||
483                       !_cups_strcasecmp(value, "yes") ||
484                       !_cups_strcasecmp(value, "true");
485       }
486       else if (!_cups_strcasecmp(name, "encryption"))
487       {
488        /*
489         * Enable/disable encryption?
490         */
491
492         if (!_cups_strcasecmp(value, "always"))
493           cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
494         else if (!_cups_strcasecmp(value, "required"))
495           cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
496         else if (!_cups_strcasecmp(value, "never"))
497           cupsSetEncryption(HTTP_ENCRYPT_NEVER);
498         else if (!_cups_strcasecmp(value, "ifrequested"))
499           cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
500         else
501         {
502           _cupsLangPrintFilter(stderr, "ERROR",
503                                _("Unknown encryption option value: \"%s\"."),
504                                value);
505         }
506       }
507       else if (!_cups_strcasecmp(name, "version"))
508       {
509         if (!strcmp(value, "1.0"))
510           version = 10;
511         else if (!strcmp(value, "1.1"))
512           version = 11;
513         else if (!strcmp(value, "2.0"))
514           version = 20;
515         else if (!strcmp(value, "2.1"))
516           version = 21;
517         else if (!strcmp(value, "2.2"))
518           version = 22;
519         else
520         {
521           _cupsLangPrintFilter(stderr, "ERROR",
522                                _("Unknown version option value: \"%s\"."),
523                                value);
524         }
525       }
526 #ifdef HAVE_LIBZ
527       else if (!_cups_strcasecmp(name, "compression"))
528       {
529         if (!_cups_strcasecmp(value, "true") || !_cups_strcasecmp(value, "yes") ||
530             !_cups_strcasecmp(value, "on") || !_cups_strcasecmp(value, "gzip"))
531           compression = "gzip";
532       }
533 #endif /* HAVE_LIBZ */
534       else if (!_cups_strcasecmp(name, "contimeout"))
535       {
536        /*
537         * Set the connection timeout...
538         */
539
540         if (atoi(value) > 0)
541           contimeout = atoi(value);
542       }
543       else
544       {
545        /*
546         * Unknown option...
547         */
548
549         _cupsLangPrintFilter(stderr, "ERROR",
550                              _("Unknown option \"%s\" with value \"%s\"."),
551                              name, value);
552       }
553     }
554   }
555
556  /*
557   * If we have 7 arguments, print the file named on the command-line.
558   * Otherwise, copy stdin to a temporary file and print the temporary
559   * file.
560   */
561
562   if (argc == 6)
563   {
564     num_files    = 0;
565     files        = NULL;
566     send_options = !_cups_strcasecmp(final_content_type, "application/pdf") ||
567                    !_cups_strcasecmp(final_content_type, "application/vnd.cups-pdf") ||
568                    !_cups_strncasecmp(final_content_type, "image/", 6);
569
570     fputs("DEBUG: Sending stdin for job...\n", stderr);
571   }
572   else
573   {
574    /*
575     * Point to the files on the command-line...
576     */
577
578     num_files    = argc - 6;
579     files        = argv + 6;
580     send_options = 1;
581
582 #ifdef HAVE_LIBZ
583     if (compression)
584       compress_files(num_files, files);
585 #endif /* HAVE_LIBZ */
586
587     fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
588   }
589
590  /*
591   * Set the authentication info, if any...
592   */
593
594   cupsSetPasswordCB(password_cb);
595
596   if (username[0])
597   {
598    /*
599     * Use authenticaion information in the device URI...
600     */
601
602     if ((password = strchr(username, ':')) != NULL)
603       *password++ = '\0';
604
605     cupsSetUser(username);
606   }
607   else
608   {
609    /*
610     * Try loading authentication information from the environment.
611     */
612
613     const char *ptr = getenv("AUTH_USERNAME");
614
615     if (ptr)
616     {
617       strlcpy(username, ptr, sizeof(username));
618       cupsSetUser(ptr);
619     }
620
621     password = getenv("AUTH_PASSWORD");
622   }
623
624  /*
625   * Try finding the remote server...
626   */
627
628   start_time = time(NULL);
629
630   sprintf(portname, "%d", port);
631
632   update_reasons(NULL, "+connecting-to-device");
633   fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
634
635   while ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
636   {
637     _cupsLangPrintFilter(stderr, "INFO",
638                          _("Unable to locate printer \"%s\"."), hostname);
639     sleep(10);
640
641     if (getenv("CLASS") != NULL)
642     {
643       update_reasons(NULL, "-connecting-to-device");
644       return (CUPS_BACKEND_STOP);
645     }
646   }
647
648   http = _httpCreate(hostname, port, addrlist, cupsEncryption(), AF_UNSPEC);
649   httpSetTimeout(http, 30.0, timeout_cb, NULL);
650
651  /*
652   * See if the printer supports SNMP...
653   */
654
655   if ((snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family)) >= 0)
656   {
657     have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr),
658                                          &start_count, NULL);
659   }
660   else
661     have_supplies = start_count = 0;
662
663  /*
664   * Wait for data from the filter...
665   */
666
667   if (num_files == 0)
668   {
669     if (!backendWaitLoop(snmp_fd, &(addrlist->addr), 0, backendNetworkSideCB))
670       return (CUPS_BACKEND_OK);
671     else if ((bytes = read(0, buffer, sizeof(buffer))) <= 0)
672       return (CUPS_BACKEND_OK);
673   }
674
675  /*
676   * Try connecting to the remote server...
677   */
678
679   delay = _cupsNextDelay(0, &prev_delay);
680
681   do
682   {
683     fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
684     _cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer."));
685
686     if (httpReconnect(http))
687     {
688       int error = errno;                /* Connection error */
689
690       if (http->status == HTTP_PKI_ERROR)
691         update_reasons(NULL, "+cups-certificate-error");
692
693       if (job_canceled)
694         break;
695
696       if (getenv("CLASS") != NULL)
697       {
698        /*
699         * If the CLASS environment variable is set, the job was submitted
700         * to a class and not to a specific queue.  In this case, we want
701         * to abort immediately so that the job can be requeued on the next
702         * available printer in the class.
703         */
704
705         _cupsLangPrintFilter(stderr, "INFO",
706                              _("Unable to contact printer, queuing on next "
707                                "printer in class."));
708
709        /*
710         * Sleep 5 seconds to keep the job from requeuing too rapidly...
711         */
712
713         sleep(5);
714
715         update_reasons(NULL, "-connecting-to-device");
716
717         return (CUPS_BACKEND_FAILED);
718       }
719
720       fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
721
722       if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
723           errno == EHOSTUNREACH)
724       {
725         if (contimeout && (time(NULL) - start_time) > contimeout)
726         {
727           _cupsLangPrintFilter(stderr, "ERROR",
728                                _("The printer is not responding."));
729           update_reasons(NULL, "-connecting-to-device");
730           return (CUPS_BACKEND_FAILED);
731         }
732
733         switch (error)
734         {
735           case EHOSTDOWN :
736               _cupsLangPrintFilter(stderr, "WARNING",
737                                    _("The printer may not exist or "
738                                      "is unavailable at this time."));
739               break;
740
741           case EHOSTUNREACH :
742               _cupsLangPrintFilter(stderr, "WARNING",
743                                    _("The printer is unreachable at this "
744                                      "time."));
745               break;
746
747           case ECONNREFUSED :
748           default :
749               _cupsLangPrintFilter(stderr, "WARNING",
750                                    _("The printer is busy."));
751               break;
752         }
753
754         sleep(delay);
755
756         delay = _cupsNextDelay(delay, &prev_delay);
757       }
758       else
759       {
760         _cupsLangPrintFilter(stderr, "ERROR",
761                              _("The printer is not responding."));
762         sleep(30);
763       }
764
765       if (job_canceled)
766         break;
767     }
768     else
769       update_reasons(NULL, "-cups-certificate-error");
770   }
771   while (http->fd < 0);
772
773   if (job_canceled || !http)
774     return (CUPS_BACKEND_FAILED);
775
776   update_reasons(NULL, "-connecting-to-device");
777   _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer."));
778
779   fprintf(stderr, "DEBUG: Connected to %s:%d...\n",
780           httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
781           _httpAddrPort(http->hostaddr));
782
783  /*
784   * Build a URI for the printer and fill the standard IPP attributes for
785   * an IPP_PRINT_FILE request.  We can't use the URI in argv[0] because it
786   * might contain username:password information...
787   */
788
789   httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname,
790                   port, resource);
791
792  /*
793   * First validate the destination and see if the device supports multiple
794   * copies...
795   */
796
797   copies_sup       = NULL;
798   cups_version     = NULL;
799   format_sup       = NULL;
800   media_col_sup    = NULL;
801   supported        = NULL;
802   operations_sup   = NULL;
803   doc_handling_sup = NULL;
804
805   do
806   {
807    /*
808     * Check for side-channel requests...
809     */
810
811     backendCheckSideChannel(snmp_fd, http->hostaddr);
812
813    /*
814     * Build the IPP request...
815     */
816
817     request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
818     request->request.op.version[0] = version / 10;
819     request->request.op.version[1] = version % 10;
820
821     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
822                  NULL, uri);
823
824     ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
825                   "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
826                   NULL, pattrs);
827
828    /*
829     * Do the request...
830     */
831
832     fputs("DEBUG: Getting supported attributes...\n", stderr);
833
834     if (http->version < HTTP_1_1)
835     {
836       fprintf(stderr, "DEBUG: Printer responded with HTTP version %d.%d.\n",
837               http->version / 100, http->version % 100);
838       update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
839                            "cups-ipp-wrong-http-version");
840     }
841
842     supported  = cupsDoRequest(http, request, resource);
843     ipp_status = cupsLastError();
844
845     fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
846             ippErrorString(ipp_status), cupsLastErrorString());
847
848     if (ipp_status <= IPP_OK_CONFLICT)
849       password_tries = 0;
850     else
851     {
852       fprintf(stderr, "DEBUG: Get-Printer-Attributes returned %s.\n",
853               ippErrorString(ipp_status));
854
855       if (ipp_status == IPP_PRINTER_BUSY ||
856           ipp_status == IPP_SERVICE_UNAVAILABLE)
857       {
858         if (contimeout && (time(NULL) - start_time) > contimeout)
859         {
860           _cupsLangPrintFilter(stderr, "ERROR",
861                                _("The printer is not responding."));
862           return (CUPS_BACKEND_FAILED);
863         }
864
865         _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
866
867         report_printer_state(supported);
868
869         sleep(delay);
870
871         delay = _cupsNextDelay(delay, &prev_delay);
872       }
873       else if ((ipp_status == IPP_BAD_REQUEST ||
874                 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
875       {
876        /*
877         * Switch to IPP/1.1 or IPP/1.0...
878         */
879
880         if (version >= 20)
881         {
882           _cupsLangPrintFilter(stderr, "INFO",
883                                _("Printer does not support IPP/%d.%d, trying "
884                                  "IPP/%s."), version / 10, version % 10, "1.1");
885           version = 11;
886         }
887         else
888         {
889           _cupsLangPrintFilter(stderr, "INFO",
890                                _("Printer does not support IPP/%d.%d, trying "
891                                  "IPP/%s."), version / 10, version % 10, "1.0");
892           version = 10;
893         }
894
895         httpReconnect(http);
896       }
897       else if (ipp_status == IPP_NOT_FOUND)
898       {
899         _cupsLangPrintFilter(stderr, "ERROR",
900                              _("The printer URI is incorrect or no longer "
901                                "exists."));
902
903         ippDelete(supported);
904
905         return (CUPS_BACKEND_STOP);
906       }
907       else if (ipp_status == IPP_FORBIDDEN ||
908                ipp_status == IPP_AUTHENTICATION_CANCELED)
909       {
910         const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
911                                         /* WWW-Authenticate field value */
912
913         if (!strncmp(www_auth, "Negotiate", 9))
914           auth_info_required = "negotiate";
915         else if (www_auth[0])
916           auth_info_required = "username,password";
917
918         fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
919         return (CUPS_BACKEND_AUTH_REQUIRED);
920       }
921       else if (ipp_status != IPP_NOT_AUTHORIZED)
922       {
923         _cupsLangPrintFilter(stderr, "ERROR",
924                              _("Unable to get printer status."));
925         sleep(10);
926       }
927
928       ippDelete(supported);
929       supported = NULL;
930       continue;
931     }
932
933     if (!getenv("CLASS"))
934     {
935      /*
936       * Check printer-is-accepting-jobs = false and printer-state-reasons for the
937       * "spool-area-full" keyword...
938       */
939
940       int busy = 0;
941
942       if ((printer_accepting = ippFindAttribute(supported,
943                                                 "printer-is-accepting-jobs",
944                                                 IPP_TAG_BOOLEAN)) != NULL &&
945           !printer_accepting->values[0].boolean)
946         busy = 1;
947       else if (!printer_accepting)
948         update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
949                              "cups-ipp-missing-printer-is-accepting-jobs");
950
951       if ((printer_state = ippFindAttribute(supported,
952                                             "printer-state-reasons",
953                                             IPP_TAG_KEYWORD)) != NULL && !busy)
954       {
955         for (i = 0; i < printer_state->num_values; i ++)
956           if (!strcmp(printer_state->values[0].string.text,
957                       "spool-area-full") ||
958               !strncmp(printer_state->values[0].string.text, "spool-area-full-",
959                        16))
960           {
961             busy = 1;
962             break;
963           }
964       }
965       else
966         update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
967                              "cups-ipp-missing-printer-state-reasons");
968
969       if (busy)
970       {
971         _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
972
973         report_printer_state(supported);
974
975         sleep(delay);
976
977         delay = _cupsNextDelay(delay, &prev_delay);
978
979         ippDelete(supported);
980         supported = NULL;
981         continue;
982       }
983     }
984
985    /*
986     * Check for supported attributes...
987     */
988
989     if ((copies_sup = ippFindAttribute(supported, "copies-supported",
990                                        IPP_TAG_RANGE)) != NULL)
991     {
992      /*
993       * Has the "copies-supported" attribute - does it have an upper
994       * bound > 1?
995       */
996
997       fprintf(stderr, "DEBUG: copies-supported=%d-%d\n",
998               copies_sup->values[0].range.lower,
999               copies_sup->values[0].range.upper);
1000
1001       if (copies_sup->values[0].range.upper <= 1)
1002         copies_sup = NULL; /* No */
1003     }
1004
1005     cups_version = ippFindAttribute(supported, "cups-version", IPP_TAG_TEXT);
1006
1007     if ((format_sup = ippFindAttribute(supported, "document-format-supported",
1008                                        IPP_TAG_MIMETYPE)) != NULL)
1009     {
1010       fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
1011               format_sup->num_values);
1012       for (i = 0; i < format_sup->num_values; i ++)
1013         fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
1014                 format_sup->values[i].string.text);
1015     }
1016
1017     if ((media_col_sup = ippFindAttribute(supported, "media-col-supported",
1018                                           IPP_TAG_KEYWORD)) != NULL)
1019     {
1020       fprintf(stderr, "DEBUG: media-col-supported (%d values)\n",
1021               media_col_sup->num_values);
1022       for (i = 0; i < media_col_sup->num_values; i ++)
1023         fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
1024                 media_col_sup->values[i].string.text);
1025     }
1026
1027     if ((operations_sup = ippFindAttribute(supported, "operations-supported",
1028                                            IPP_TAG_ENUM)) != NULL)
1029     {
1030       for (i = 0; i < operations_sup->num_values; i ++)
1031         if (operations_sup->values[i].integer == IPP_PRINT_JOB)
1032           break;
1033
1034       if (i >= operations_sup->num_values)
1035         update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1036                              "cups-ipp-missing-print-job");
1037
1038       for (i = 0; i < operations_sup->num_values; i ++)
1039         if (operations_sup->values[i].integer == IPP_CANCEL_JOB)
1040           break;
1041
1042       if (i >= operations_sup->num_values)
1043         update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1044                              "cups-ipp-missing-cancel-job");
1045
1046       for (i = 0; i < operations_sup->num_values; i ++)
1047         if (operations_sup->values[i].integer == IPP_GET_JOB_ATTRIBUTES)
1048           break;
1049
1050       if (i >= operations_sup->num_values)
1051         update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1052                              "cups-ipp-missing-get-job-attributes");
1053
1054       for (i = 0; i < operations_sup->num_values; i ++)
1055         if (operations_sup->values[i].integer == IPP_GET_PRINTER_ATTRIBUTES)
1056           break;
1057
1058       if (i >= operations_sup->num_values)
1059         update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1060                              "cups-ipp-missing-get-printer-attributes");
1061
1062       for (i = 0; i < operations_sup->num_values; i ++)
1063       {
1064         if (operations_sup->values[i].integer == IPP_VALIDATE_JOB)
1065           validate_job = 1;
1066         else if (operations_sup->values[i].integer == IPP_CREATE_JOB)
1067           create_job = 1;
1068         else if (operations_sup->values[i].integer == IPP_SEND_DOCUMENT)
1069           send_document = 1;
1070         else if (operations_sup->values[i].integer == IPP_GET_JOB_ATTRIBUTES)
1071           get_job_attrs = 1;
1072       }
1073
1074       if (create_job && !send_document)
1075       {
1076         fputs("DEBUG: Printer supports Create-Job but not Send-Document.\n",
1077               stderr);
1078         create_job = 0;
1079
1080         update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1081                              "cups-ipp-missing-send-document");
1082       }
1083
1084       if (!validate_job)
1085         update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1086                              "cups-ipp-missing-validate-job");
1087     }
1088     else
1089       update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1090                            "cups-ipp-missing-operations-supported");
1091
1092     doc_handling_sup = ippFindAttribute(supported,
1093                                         "multiple-document-handling-supported",
1094                                         IPP_TAG_KEYWORD);
1095
1096     report_printer_state(supported);
1097   }
1098   while (ipp_status > IPP_OK_CONFLICT);
1099
1100  /*
1101   * See if the printer is accepting jobs and is not stopped; if either
1102   * condition is true and we are printing to a class, requeue the job...
1103   */
1104
1105   if (getenv("CLASS") != NULL)
1106   {
1107     printer_state     = ippFindAttribute(supported, "printer-state",
1108                                          IPP_TAG_ENUM);
1109     printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
1110                                          IPP_TAG_BOOLEAN);
1111
1112     if (printer_state == NULL ||
1113         (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
1114          waitprinter) ||
1115         printer_accepting == NULL ||
1116         !printer_accepting->values[0].boolean)
1117     {
1118      /*
1119       * If the CLASS environment variable is set, the job was submitted
1120       * to a class and not to a specific queue.  In this case, we want
1121       * to abort immediately so that the job can be requeued on the next
1122       * available printer in the class.
1123       */
1124
1125       _cupsLangPrintFilter(stderr, "INFO",
1126                            _("Unable to contact printer, queuing on next "
1127                              "printer in class."));
1128
1129       ippDelete(supported);
1130       httpClose(http);
1131
1132      /*
1133       * Sleep 5 seconds to keep the job from requeuing too rapidly...
1134       */
1135
1136       sleep(5);
1137
1138       return (CUPS_BACKEND_FAILED);
1139     }
1140   }
1141
1142  /*
1143   * See if the printer supports multiple copies...
1144   */
1145
1146   copies = atoi(argv[4]);
1147
1148   if (copies_sup || argc < 7)
1149   {
1150     copies_remaining = 1;
1151
1152     if (argc < 7 && !_cups_strncasecmp(final_content_type, "image/", 6))
1153       copies = 1;
1154   }
1155   else
1156     copies_remaining = copies;
1157
1158  /*
1159   * Prepare remaining printing options...
1160   */
1161
1162   options = NULL;
1163   pc      = NULL;
1164
1165   if (send_options)
1166   {
1167     num_options = cupsParseOptions(argv[5], 0, &options);
1168
1169     if (!cups_version && media_col_sup)
1170     {
1171      /*
1172       * Load the PPD file and generate PWG attribute mapping information...
1173       */
1174
1175       ppd = ppdOpenFile(getenv("PPD"));
1176       pc  = _ppdCacheCreateWithPPD(ppd);
1177
1178       ppdClose(ppd);
1179     }
1180   }
1181   else
1182     num_options = 0;
1183
1184   document_format = NULL;
1185
1186   if (format_sup != NULL)
1187   {
1188     for (i = 0; i < format_sup->num_values; i ++)
1189       if (!_cups_strcasecmp(final_content_type,
1190                             format_sup->values[i].string.text))
1191       {
1192         document_format = final_content_type;
1193         break;
1194       }
1195
1196     if (!document_format)
1197     {
1198       for (i = 0; i < format_sup->num_values; i ++)
1199         if (!_cups_strcasecmp("application/octet-stream",
1200                               format_sup->values[i].string.text))
1201         {
1202           document_format = "application/octet-stream";
1203           break;
1204         }
1205     }
1206   }
1207
1208   fprintf(stderr, "DEBUG: final_content_type=\"%s\", document_format=\"%s\"\n",
1209           final_content_type, document_format ? document_format : "(null)");
1210
1211  /*
1212   * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin
1213   * to a temporary file so that we can do a HTTP/1.0 submission...
1214   *
1215   * (I hate compatibility hacks!)
1216   */
1217
1218   if (http->version < HTTP_1_1 && num_files == 0)
1219   {
1220     if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
1221     {
1222       perror("DEBUG: Unable to create temporary file");
1223       return (CUPS_BACKEND_FAILED);
1224     }
1225
1226     _cupsLangPrintFilter(stderr, "INFO", _("Copying print data."));
1227
1228     if ((compatsize = write(fd, buffer, bytes)) < 0)
1229     {
1230       perror("DEBUG: Unable to write temporary file");
1231       return (CUPS_BACKEND_FAILED);
1232     }
1233
1234     if ((bytes = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
1235                                 backendNetworkSideCB)) < 0)
1236       return (CUPS_BACKEND_FAILED);
1237
1238     compatsize += bytes;
1239
1240     close(fd);
1241
1242     compatfile = tmpfilename;
1243     files      = &compatfile;
1244     num_files  = 1;
1245   }
1246   else if (http->version < HTTP_1_1 && num_files == 1)
1247   {
1248     struct stat fileinfo;               /* File information */
1249
1250     if (!stat(files[0], &fileinfo))
1251       compatsize = fileinfo.st_size;
1252   }
1253
1254  /*
1255   * Start monitoring the printer in the background...
1256   */
1257
1258   monitor.uri           = uri;
1259   monitor.hostname      = hostname;
1260   monitor.user          = argv[2];
1261   monitor.resource      = resource;
1262   monitor.port          = port;
1263   monitor.version       = version;
1264   monitor.job_id        = 0;
1265   monitor.get_job_attrs = get_job_attrs;
1266   monitor.encryption    = cupsEncryption();
1267   monitor.job_state     = IPP_JOB_PENDING;
1268   monitor.printer_state = IPP_PRINTER_IDLE;
1269
1270   if (create_job)
1271   {
1272     monitor.job_name = argv[3];
1273   }
1274   else
1275   {
1276     snprintf(print_job_name, sizeof(print_job_name), "%s - %s", argv[1],
1277              argv[3]);
1278     monitor.job_name = print_job_name;
1279   }
1280
1281   _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
1282
1283  /*
1284   * Validate access to the printer...
1285   */
1286
1287   while (!job_canceled && validate_job)
1288   {
1289     request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2],
1290                           monitor.job_name, num_options, options, compression,
1291                           copies_sup ? copies : 1, document_format, pc,
1292                           media_col_sup, doc_handling_sup);
1293
1294     ippDelete(cupsDoRequest(http, request, resource));
1295
1296     ipp_status = cupsLastError();
1297
1298     fprintf(stderr, "DEBUG: Validate-Job: %s (%s)\n",
1299             ippErrorString(ipp_status), cupsLastErrorString());
1300
1301     if (job_canceled)
1302       break;
1303
1304     if (ipp_status == IPP_SERVICE_UNAVAILABLE || ipp_status == IPP_PRINTER_BUSY)
1305     {
1306       _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
1307       sleep(10);
1308     }
1309     else if (ipp_status == IPP_DOCUMENT_FORMAT)
1310       goto cleanup;
1311     else if (ipp_status == IPP_FORBIDDEN ||
1312              ipp_status == IPP_AUTHENTICATION_CANCELED)
1313     {
1314       const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
1315                                         /* WWW-Authenticate field value */
1316
1317       if (!strncmp(www_auth, "Negotiate", 9))
1318         auth_info_required = "negotiate";
1319       else if (www_auth[0])
1320         auth_info_required = "username,password";
1321
1322       goto cleanup;
1323     }
1324     else if (ipp_status == IPP_OPERATION_NOT_SUPPORTED)
1325     {
1326      /*
1327       * This is all too common...
1328       */
1329
1330       update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1331                            "cups-ipp-missing-validate-job");
1332       break;
1333     }
1334     else if (ipp_status < IPP_REDIRECTION_OTHER_SITE ||
1335              ipp_status == IPP_BAD_REQUEST)
1336       break;
1337   }
1338
1339  /*
1340   * Then issue the print-job request...
1341   */
1342
1343   job_id = 0;
1344
1345   while (!job_canceled && copies_remaining > 0)
1346   {
1347    /*
1348     * Check for side-channel requests...
1349     */
1350
1351     backendCheckSideChannel(snmp_fd, http->hostaddr);
1352
1353    /*
1354     * Build the IPP job creation request...
1355     */
1356
1357     if (job_canceled)
1358       break;
1359
1360     request = new_request((num_files > 1 || create_job) ? IPP_CREATE_JOB :
1361                                                           IPP_PRINT_JOB,
1362                           version, uri, argv[2], monitor.job_name, num_options,
1363                           options, compression, copies_sup ? copies : 1,
1364                           document_format, pc, media_col_sup, doc_handling_sup);
1365
1366    /*
1367     * Do the request...
1368     */
1369
1370     if (num_files > 1 || create_job)
1371       response = cupsDoRequest(http, request, resource);
1372     else
1373     {
1374       size_t    length = 0;             /* Length of request */
1375
1376       if (compatsize > 0)
1377       {
1378         fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr);
1379         length = ippLength(request) + (size_t)compatsize;
1380       }
1381       else
1382         fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr);
1383
1384       http_status = cupsSendRequest(http, request, resource, length);
1385       if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1386       {
1387         if (num_files == 1)
1388         {
1389           if ((fd = open(files[0], O_RDONLY)) < 0)
1390           {
1391             _cupsLangPrintError("ERROR", _("Unable to open print file"));
1392             return (CUPS_BACKEND_FAILED);
1393           }
1394         }
1395         else
1396         {
1397           fd          = 0;
1398           http_status = cupsWriteRequestData(http, buffer, bytes);
1399         }
1400
1401         while (http_status == HTTP_CONTINUE &&
1402                (!job_canceled || compatsize > 0))
1403         {
1404          /*
1405           * Check for side-channel requests and more print data...
1406           */
1407
1408           FD_ZERO(&input);
1409           FD_SET(fd, &input);
1410           FD_SET(snmp_fd, &input);
1411
1412           while (select(fd > snmp_fd ? fd + 1 : snmp_fd + 1, &input, NULL, NULL,
1413                         NULL) <= 0 && !job_canceled);
1414
1415           if (FD_ISSET(snmp_fd, &input))
1416             backendCheckSideChannel(snmp_fd, http->hostaddr);
1417
1418           if (FD_ISSET(fd, &input))
1419           {
1420             if ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1421             {
1422               fprintf(stderr, "DEBUG: Read %d bytes...\n", (int)bytes);
1423
1424               if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
1425                 break;
1426             }
1427             else if (bytes == 0 || (errno != EINTR && errno != EAGAIN))
1428               break;
1429           }
1430         }
1431
1432         if (num_files == 1)
1433           close(fd);
1434       }
1435
1436       response = cupsGetResponse(http, resource);
1437       ippDelete(request);
1438     }
1439
1440     ipp_status = cupsLastError();
1441
1442     fprintf(stderr, "DEBUG: %s: %s (%s)\n",
1443             (num_files > 1 || create_job) ? "Create-Job" : "Print-Job",
1444             ippErrorString(ipp_status), cupsLastErrorString());
1445
1446     if (ipp_status > IPP_OK_CONFLICT)
1447     {
1448       job_id = 0;
1449
1450       if (job_canceled)
1451         break;
1452
1453       if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1454           ipp_status == IPP_NOT_POSSIBLE ||
1455           ipp_status == IPP_PRINTER_BUSY)
1456       {
1457         _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
1458         sleep(10);
1459
1460         if (num_files == 0)
1461         {
1462          /*
1463           * We can't re-submit when we have no files to print, so exit
1464           * immediately with the right status code...
1465           */
1466
1467           goto cleanup;
1468         }
1469       }
1470       else if (ipp_status == IPP_ERROR_JOB_CANCELED)
1471         goto cleanup;
1472       else if (ipp_status == IPP_NOT_AUTHORIZED)
1473         continue;
1474       else
1475       {
1476        /*
1477         * Update auth-info-required as needed...
1478         */
1479
1480         _cupsLangPrintFilter(stderr, "ERROR",
1481                              _("Print file was not accepted."));
1482
1483         if (ipp_status == IPP_FORBIDDEN ||
1484             ipp_status == IPP_AUTHENTICATION_CANCELED)
1485         {
1486           const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
1487                                         /* WWW-Authenticate field value */
1488
1489           if (!strncmp(www_auth, "Negotiate", 9))
1490             auth_info_required = "negotiate";
1491           else if (www_auth[0])
1492             auth_info_required = "username,password";
1493         }
1494         else if (ipp_status == IPP_REQUEST_VALUE)
1495         {
1496          /*
1497           * Print file is too large, abort this job...
1498           */
1499
1500           goto cleanup;
1501         }
1502         else
1503           sleep(10);
1504
1505         if (num_files == 0)
1506         {
1507          /*
1508           * We can't re-submit when we have no files to print, so exit
1509           * immediately with the right status code...
1510           */
1511
1512           goto cleanup;
1513         }
1514       }
1515     }
1516     else if ((job_id_attr = ippFindAttribute(response, "job-id",
1517                                              IPP_TAG_INTEGER)) == NULL)
1518     {
1519       _cupsLangPrintFilter(stderr, "INFO",
1520                            _("Print file accepted - job ID unknown."));
1521       update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1522                            "cups-ipp-missing-job-id");
1523       job_id = 0;
1524     }
1525     else
1526     {
1527       password_tries = 0;
1528       monitor.job_id = job_id = job_id_attr->values[0].integer;
1529       _cupsLangPrintFilter(stderr, "INFO",
1530                            _("Print file accepted - job ID %d."), job_id);
1531     }
1532
1533     fprintf(stderr, "DEBUG: job-id=%d\n", job_id);
1534     ippDelete(response);
1535
1536     if (job_canceled)
1537       break;
1538
1539     if (job_id && (num_files > 1 || create_job))
1540     {
1541       for (i = 0; num_files == 0 || i < num_files; i ++)
1542       {
1543        /*
1544         * Check for side-channel requests...
1545         */
1546
1547         backendCheckSideChannel(snmp_fd, http->hostaddr);
1548
1549        /*
1550         * Send the next file in the job...
1551         */
1552
1553         request = ippNewRequest(IPP_SEND_DOCUMENT);
1554         request->request.op.version[0] = version / 10;
1555         request->request.op.version[1] = version % 10;
1556
1557         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1558                      NULL, uri);
1559
1560         ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1561                       job_id);
1562
1563         if (argv[2][0])
1564           ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1565                        "requesting-user-name", NULL, argv[2]);
1566
1567         if ((i + 1) >= num_files)
1568           ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1569
1570         if (document_format)
1571           ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1572                        "document-format", NULL, document_format);
1573
1574         fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1);
1575         http_status = cupsSendRequest(http, request, resource, 0);
1576         if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1577         {
1578           if (num_files == 0)
1579           {
1580             fd          = 0;
1581             http_status = cupsWriteRequestData(http, buffer, bytes);
1582           }
1583           else
1584           {
1585             if ((fd = open(files[i], O_RDONLY)) < 0)
1586             {
1587               _cupsLangPrintError("ERROR", _("Unable to open print file"));
1588               return (CUPS_BACKEND_FAILED);
1589             }
1590           }
1591         }
1592         else
1593           fd = -1;
1594
1595         if (fd >= 0)
1596         {
1597           while (!job_canceled && http_status == HTTP_CONTINUE &&
1598                  (bytes = read(fd, buffer, sizeof(buffer))) > 0)
1599           {
1600             if ((http_status = cupsWriteRequestData(http, buffer, bytes))
1601                     != HTTP_CONTINUE)
1602               break;
1603             else
1604             {
1605              /*
1606               * Check for side-channel requests...
1607               */
1608
1609               backendCheckSideChannel(snmp_fd, http->hostaddr);
1610             }
1611           }
1612
1613           if (fd > 0)
1614             close(fd);
1615         }
1616
1617         ippDelete(cupsGetResponse(http, resource));
1618         ippDelete(request);
1619
1620         fprintf(stderr, "DEBUG: Send-Document: %s (%s)\n",
1621                 ippErrorString(cupsLastError()), cupsLastErrorString());
1622
1623         if (cupsLastError() > IPP_OK_CONFLICT)
1624         {
1625           ipp_status = cupsLastError();
1626
1627           _cupsLangPrintFilter(stderr, "ERROR",
1628                                _("Unable to add document to print job."));
1629           break;
1630         }
1631         else
1632         {
1633           password_tries = 0;
1634
1635           if (num_files == 0 || fd < 0)
1636             break;
1637         }
1638       }
1639     }
1640
1641     if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
1642     {
1643       fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
1644       copies_remaining --;
1645     }
1646     else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1647              ipp_status == IPP_NOT_POSSIBLE ||
1648              ipp_status == IPP_PRINTER_BUSY)
1649       continue;
1650     else if (ipp_status == IPP_REQUEST_VALUE)
1651     {
1652      /*
1653       * Print file is too large, abort this job...
1654       */
1655
1656       goto cleanup;
1657     }
1658     else
1659       copies_remaining --;
1660
1661    /*
1662     * Wait for the job to complete...
1663     */
1664
1665     if (!job_id || !waitjob || !get_job_attrs)
1666       continue;
1667
1668     _cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
1669
1670     for (delay = _cupsNextDelay(0, &prev_delay); !job_canceled;)
1671     {
1672      /*
1673       * Check for side-channel requests...
1674       */
1675
1676       backendCheckSideChannel(snmp_fd, http->hostaddr);
1677
1678      /*
1679       * Build an IPP_GET_JOB_ATTRIBUTES request...
1680       */
1681
1682       request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
1683       request->request.op.version[0] = version / 10;
1684       request->request.op.version[1] = version % 10;
1685
1686       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1687                    NULL, uri);
1688
1689       ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1690                     job_id);
1691
1692       if (argv[2][0])
1693         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1694                      "requesting-user-name", NULL, argv[2]);
1695
1696       ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1697                     "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1698                     NULL, jattrs);
1699
1700      /*
1701       * Do the request...
1702       */
1703
1704       httpReconnect(http);
1705       response   = cupsDoRequest(http, request, resource);
1706       ipp_status = cupsLastError();
1707
1708       if (ipp_status == IPP_NOT_FOUND || ipp_status == IPP_NOT_POSSIBLE)
1709       {
1710        /*
1711         * Job has gone away and/or the server has no job history...
1712         */
1713
1714         update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1715                              "cups-ipp-missing-job-history");
1716         ippDelete(response);
1717
1718         ipp_status = IPP_OK;
1719         break;
1720       }
1721
1722       fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
1723               ippErrorString(ipp_status), cupsLastErrorString());
1724
1725       if (ipp_status <= IPP_OK_CONFLICT)
1726         password_tries = 0;
1727       else
1728       {
1729         if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1730             ipp_status != IPP_PRINTER_BUSY)
1731         {
1732           ippDelete(response);
1733           ipp_status = IPP_OK;
1734           break;
1735         }
1736       }
1737
1738       if (response)
1739       {
1740         if ((job_state = ippFindAttribute(response, "job-state",
1741                                           IPP_TAG_ENUM)) != NULL)
1742         {
1743          /*
1744           * Reflect the remote job state in the local queue...
1745           */
1746
1747           if (cups_version &&
1748               job_state->values[0].integer >= IPP_JOB_PENDING &&
1749               job_state->values[0].integer <= IPP_JOB_COMPLETED)
1750             update_reasons(NULL,
1751                            remote_job_states[job_state->values[0].integer -
1752                                              IPP_JOB_PENDING]);
1753
1754           if ((job_sheets = ippFindAttribute(response,
1755                                              "job-media-sheets-completed",
1756                                              IPP_TAG_INTEGER)) == NULL)
1757             job_sheets = ippFindAttribute(response,
1758                                           "job-impressions-completed",
1759                                           IPP_TAG_INTEGER);
1760
1761           if (job_sheets)
1762             fprintf(stderr, "PAGE: total %d\n",
1763                     job_sheets->values[0].integer);
1764
1765          /*
1766           * Stop polling if the job is finished or pending-held...
1767           */
1768
1769           if (job_state->values[0].integer > IPP_JOB_STOPPED)
1770           {
1771             ippDelete(response);
1772             break;
1773           }
1774         }
1775         else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1776                  ipp_status != IPP_NOT_POSSIBLE &&
1777                  ipp_status != IPP_PRINTER_BUSY)
1778         {
1779          /*
1780           * If the printer does not return a job-state attribute, it does not
1781           * conform to the IPP specification - break out immediately and fail
1782           * the job...
1783           */
1784
1785           update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1786                                "cups-ipp-missing-job-state");
1787           ipp_status = IPP_INTERNAL_ERROR;
1788           break;
1789         }
1790       }
1791
1792       ippDelete(response);
1793
1794      /*
1795       * Wait before polling again...
1796       */
1797
1798       sleep(delay);
1799
1800       delay = _cupsNextDelay(delay, &prev_delay);
1801     }
1802   }
1803
1804  /*
1805   * Cancel the job as needed...
1806   */
1807
1808   if (job_canceled && job_id)
1809     cancel_job(http, uri, job_id, resource, argv[2], version);
1810
1811  /*
1812   * Check the printer state and report it if necessary...
1813   */
1814
1815   check_printer_state(http, uri, resource, argv[2], version);
1816
1817  /*
1818   * Collect the final page count as needed...
1819   */
1820
1821   if (have_supplies &&
1822       !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1823       page_count > start_count)
1824     fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1825
1826 #ifdef HAVE_GSSAPI
1827  /*
1828   * See if we used Kerberos at all...
1829   */
1830
1831   if (http->gssctx)
1832     auth_info_required = "negotiate";
1833 #endif /* HAVE_GSSAPI */
1834
1835  /*
1836   * Free memory...
1837   */
1838
1839   cleanup:
1840
1841   cupsFreeOptions(num_options, options);
1842   _ppdCacheDestroy(pc);
1843
1844   httpClose(http);
1845
1846   ippDelete(supported);
1847
1848  /*
1849   * Remove the temporary file(s) if necessary...
1850   */
1851
1852   if (tmpfilename[0])
1853     unlink(tmpfilename);
1854
1855 #ifdef HAVE_LIBZ
1856   if (compression)
1857   {
1858     for (i = 0; i < num_files; i ++)
1859       unlink(files[i]);
1860   }
1861 #endif /* HAVE_LIBZ */
1862
1863  /*
1864   * Return the queue status...
1865   */
1866
1867   if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1868       ipp_status == IPP_AUTHENTICATION_CANCELED ||
1869       ipp_status <= IPP_OK_CONFLICT)
1870     fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
1871
1872   if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1873       ipp_status == IPP_AUTHENTICATION_CANCELED)
1874     return (CUPS_BACKEND_AUTH_REQUIRED);
1875   else if (ipp_status == IPP_INTERNAL_ERROR)
1876     return (CUPS_BACKEND_STOP);
1877   else if (ipp_status == IPP_CONFLICT)
1878     return (CUPS_BACKEND_FAILED);
1879   else if (ipp_status == IPP_REQUEST_VALUE ||
1880            ipp_status == IPP_DOCUMENT_FORMAT || job_canceled < 0)
1881   {
1882     if (ipp_status == IPP_REQUEST_VALUE)
1883       _cupsLangPrintFilter(stderr, "ERROR", _("Print job too large."));
1884     else if (ipp_status == IPP_DOCUMENT_FORMAT)
1885       _cupsLangPrintFilter(stderr, "ERROR",
1886                            _("Printer cannot print supplied content."));
1887     else
1888       _cupsLangPrintFilter(stderr, "ERROR", _("Print job canceled at printer."));
1889     return (CUPS_BACKEND_CANCEL);
1890   }
1891   else if (ipp_status > IPP_OK_CONFLICT && ipp_status != IPP_ERROR_JOB_CANCELED)
1892     return (CUPS_BACKEND_RETRY_CURRENT);
1893   else
1894   {
1895     _cupsLangPrintFilter(stderr, "INFO", _("Ready to print."));
1896     return (CUPS_BACKEND_OK);
1897   }
1898 }
1899
1900
1901 /*
1902  * 'cancel_job()' - Cancel a print job.
1903  */
1904
1905 static void
1906 cancel_job(http_t     *http,            /* I - HTTP connection */
1907            const char *uri,             /* I - printer-uri */
1908            int        id,               /* I - job-id */
1909            const char *resource,        /* I - Resource path */
1910            const char *user,            /* I - requesting-user-name */
1911            int        version)          /* I - IPP version */
1912 {
1913   ipp_t *request;                       /* Cancel-Job request */
1914
1915
1916   _cupsLangPrintFilter(stderr, "INFO", _("Canceling print job."));
1917
1918   request = ippNewRequest(IPP_CANCEL_JOB);
1919   request->request.op.version[0] = version / 10;
1920   request->request.op.version[1] = version % 10;
1921
1922   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1923                NULL, uri);
1924   ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1925
1926   if (user && user[0])
1927     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1928                  "requesting-user-name", NULL, user);
1929
1930  /*
1931   * Do the request...
1932   */
1933
1934   ippDelete(cupsDoRequest(http, request, resource));
1935
1936   if (cupsLastError() > IPP_OK_CONFLICT)
1937     _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
1938 }
1939
1940
1941 /*
1942  * 'check_printer_state()' - Check the printer state.
1943  */
1944
1945 static ipp_pstate_t                     /* O - Current printer-state */
1946 check_printer_state(
1947     http_t      *http,                  /* I - HTTP connection */
1948     const char  *uri,                   /* I - Printer URI */
1949     const char  *resource,              /* I - Resource path */
1950     const char  *user,                  /* I - Username, if any */
1951     int         version)                /* I - IPP version */
1952  {
1953   ipp_t         *request,               /* IPP request */
1954                 *response;              /* IPP response */
1955   ipp_attribute_t *attr;                /* Attribute in response */
1956   ipp_pstate_t  printer_state = IPP_PRINTER_STOPPED;
1957                                         /* Current printer-state */
1958
1959
1960  /*
1961   * Send a Get-Printer-Attributes request and log the results...
1962   */
1963
1964   request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1965   request->request.op.version[0] = version / 10;
1966   request->request.op.version[1] = version % 10;
1967
1968   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1969                NULL, uri);
1970
1971   if (user && user[0])
1972     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1973                  "requesting-user-name", NULL, user);
1974
1975   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1976                 "requested-attributes",
1977                 (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
1978
1979   if ((response = cupsDoRequest(http, request, resource)) != NULL)
1980   {
1981     report_printer_state(response);
1982
1983     if ((attr = ippFindAttribute(response, "printer-state",
1984                                  IPP_TAG_ENUM)) != NULL)
1985       printer_state = (ipp_pstate_t)attr->values[0].integer;
1986
1987     ippDelete(response);
1988   }
1989
1990   fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
1991           ippErrorString(cupsLastError()), cupsLastErrorString());
1992
1993   if (cupsLastError() <= IPP_OK_CONFLICT)
1994     password_tries = 0;
1995
1996  /*
1997   * Return the printer-state value...
1998   */
1999
2000   return (printer_state);
2001 }
2002
2003
2004 #ifdef HAVE_LIBZ
2005 /*
2006  * 'compress_files()' - Compress print files.
2007  */
2008
2009 static void
2010 compress_files(int  num_files,          /* I - Number of files */
2011                char **files)            /* I - Files */
2012 {
2013   int           i,                      /* Looping var */
2014                 fd;                     /* Temporary file descriptor */
2015   ssize_t       bytes;                  /* Bytes read/written */
2016   size_t        total;                  /* Total bytes read */
2017   cups_file_t   *in,                    /* Input file */
2018                 *out;                   /* Output file */
2019   struct stat   outinfo;                /* Output file information */
2020   char          filename[1024],         /* Temporary filename */
2021                 buffer[32768];          /* Copy buffer */
2022
2023
2024   fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
2025   for (i = 0; i < num_files; i ++)
2026   {
2027     if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
2028     {
2029       _cupsLangPrintError("ERROR", _("Unable to create compressed print file"));
2030       exit(CUPS_BACKEND_FAILED);
2031     }
2032
2033     if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
2034     {
2035       _cupsLangPrintError("ERROR", _("Unable to open compressed print file"));
2036       exit(CUPS_BACKEND_FAILED);
2037     }
2038
2039     if ((in = cupsFileOpen(files[i], "r")) == NULL)
2040     {
2041       _cupsLangPrintError("ERROR", _("Unable to open print file"));
2042       cupsFileClose(out);
2043       exit(CUPS_BACKEND_FAILED);
2044     }
2045
2046     total = 0;
2047     while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
2048       if (cupsFileWrite(out, buffer, bytes) < bytes)
2049       {
2050         _cupsLangPrintError("ERROR",
2051                             _("Unable to generate compressed print file"));
2052         cupsFileClose(in);
2053         cupsFileClose(out);
2054         exit(CUPS_BACKEND_FAILED);
2055       }
2056       else
2057         total += bytes;
2058
2059     cupsFileClose(out);
2060     cupsFileClose(in);
2061
2062     files[i] = strdup(filename);
2063
2064     if (!stat(filename, &outinfo))
2065       fprintf(stderr,
2066               "DEBUG: File %d compressed to %.1f%% of original size, "
2067               CUPS_LLFMT " bytes...\n",
2068               i + 1, 100.0 * outinfo.st_size / total,
2069               CUPS_LLCAST outinfo.st_size);
2070   }
2071 }
2072 #endif /* HAVE_LIBZ */
2073
2074
2075 /*
2076  * 'monitor_printer()' - Monitor the printer state.
2077  */
2078
2079 static void *                           /* O - Thread exit code */
2080 monitor_printer(
2081     _cups_monitor_t *monitor)           /* I - Monitoring data */
2082 {
2083   http_t        *http;                  /* Connection to printer */
2084   ipp_t         *request,               /* IPP request */
2085                 *response;              /* IPP response */
2086   ipp_attribute_t *attr;                /* Attribute in response */
2087   int           delay,                  /* Current delay */
2088                 prev_delay;             /* Previous delay */
2089   ipp_op_t      job_op;                 /* Operation to use */
2090   int           job_id;                 /* Job ID */
2091   const char    *job_name;              /* Job name */
2092   ipp_jstate_t  job_state;              /* Job state */
2093   const char    *job_user;              /* Job originating user name */
2094
2095
2096  /*
2097   * Make a copy of the printer connection...
2098   */
2099
2100   http = _httpCreate(monitor->hostname, monitor->port, NULL, monitor->encryption,
2101                      AF_UNSPEC);
2102   httpSetTimeout(http, 30.0, timeout_cb, NULL);
2103   if (username[0])
2104     cupsSetUser(username);
2105   cupsSetPasswordCB(password_cb);
2106
2107  /*
2108   * Loop until the job is canceled, aborted, or completed.
2109   */
2110
2111   delay = _cupsNextDelay(0, &prev_delay);
2112
2113   while (monitor->job_state < IPP_JOB_CANCELED && !job_canceled)
2114   {
2115    /*
2116     * Reconnect to the printer...
2117     */
2118
2119     if (!httpReconnect(http))
2120     {
2121      /*
2122       * Connected, so check on the printer state...
2123       */
2124
2125       monitor->printer_state = check_printer_state(http, monitor->uri,
2126                                                    monitor->resource,
2127                                                    monitor->user,
2128                                                    monitor->version);
2129
2130      /*
2131       * Check the status of the job itself...
2132       */
2133
2134       job_op  = (monitor->job_id > 0 && monitor->get_job_attrs) ?
2135                     IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
2136       request = ippNewRequest(job_op);
2137       request->request.op.version[0] = monitor->version / 10;
2138       request->request.op.version[1] = monitor->version % 10;
2139
2140       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2141                    NULL, monitor->uri);
2142       if (job_op == IPP_GET_JOB_ATTRIBUTES)
2143         ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
2144                       monitor->job_id);
2145
2146       if (monitor->user && monitor->user[0])
2147         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2148                      "requesting-user-name", NULL, monitor->user);
2149
2150       ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2151                     "requested-attributes",
2152                     (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
2153
2154      /*
2155       * Do the request...
2156       */
2157
2158       response = cupsDoRequest(http, request, monitor->resource);
2159
2160       fprintf(stderr, "DEBUG: %s: %s (%s)\n", ippOpString(job_op),
2161               ippErrorString(cupsLastError()), cupsLastErrorString());
2162
2163       if (cupsLastError() <= IPP_OK_CONFLICT)
2164         password_tries = 0;
2165
2166       if (job_op == IPP_GET_JOB_ATTRIBUTES)
2167       {
2168         if ((attr = ippFindAttribute(response, "job-state",
2169                                      IPP_TAG_ENUM)) != NULL)
2170           monitor->job_state = (ipp_jstate_t)attr->values[0].integer;
2171         else
2172           monitor->job_state = IPP_JOB_COMPLETED;
2173       }
2174       else if (response)
2175       {
2176         for (attr = response->attrs; attr; attr = attr->next)
2177         {
2178           job_id    = 0;
2179           job_name  = NULL;
2180           job_state = IPP_JOB_PENDING;
2181           job_user  = NULL;
2182
2183           while (attr && attr->group_tag != IPP_TAG_JOB)
2184             attr = attr->next;
2185
2186           if (!attr)
2187             break;
2188
2189           while (attr && attr->group_tag == IPP_TAG_JOB)
2190           {
2191             if (!strcmp(attr->name, "job-id") &&
2192                 attr->value_tag == IPP_TAG_INTEGER)
2193               job_id = attr->values[0].integer;
2194             else if (!strcmp(attr->name, "job-name") &&
2195                      (attr->value_tag == IPP_TAG_NAME ||
2196                       attr->value_tag == IPP_TAG_NAMELANG))
2197               job_name = attr->values[0].string.text;
2198             else if (!strcmp(attr->name, "job-state") &&
2199                      attr->value_tag == IPP_TAG_ENUM)
2200               job_state = attr->values[0].integer;
2201             else if (!strcmp(attr->name, "job-originating-user-name") &&
2202                      (attr->value_tag == IPP_TAG_NAME ||
2203                       attr->value_tag == IPP_TAG_NAMELANG))
2204               job_user = attr->values[0].string.text;
2205
2206             attr = attr->next;
2207           }
2208
2209           if (job_id > 0 && job_name && !strcmp(job_name, monitor->job_name) &&
2210               job_user && monitor->user && !strcmp(job_user, monitor->user))
2211           {
2212             monitor->job_id    = job_id;
2213             monitor->job_state = job_state;
2214             break;
2215           }
2216
2217           if (!attr)
2218             break;
2219         }
2220       }
2221
2222       ippDelete(response);
2223
2224      /*
2225       * Disconnect from the printer - we'll reconnect on the next poll...
2226       */
2227
2228       _httpDisconnect(http);
2229     }
2230
2231    /*
2232     * Sleep for N seconds...
2233     */
2234
2235     sleep(delay);
2236
2237     delay = _cupsNextDelay(delay, &prev_delay);
2238   }
2239
2240  /*
2241   * Cancel the job if necessary...
2242   */
2243
2244   if (job_canceled && monitor->job_id > 0)
2245     if (!httpReconnect(http))
2246       cancel_job(http, monitor->uri, monitor->job_id, monitor->resource,
2247                  monitor->user, monitor->version);
2248
2249  /*
2250   * Cleanup and return...
2251   */
2252
2253   httpClose(http);
2254
2255   return (NULL);
2256 }
2257
2258
2259 /*
2260  * 'new_request()' - Create a new print creation or validation request.
2261  */
2262
2263 static ipp_t *                          /* O - Request data */
2264 new_request(
2265     ipp_op_t        op,                 /* I - IPP operation code */
2266     int             version,            /* I - IPP version number */
2267     const char      *uri,               /* I - printer-uri value */
2268     const char      *user,              /* I - requesting-user-name value */
2269     const char      *title,             /* I - job-name value */
2270     int             num_options,        /* I - Number of options to send */
2271     cups_option_t   *options,           /* I - Options to send */
2272     const char      *compression,       /* I - compression value or NULL */
2273     int             copies,             /* I - copies value or 0 */
2274     const char      *format,            /* I - document-format value or NULL */
2275     _ppd_cache_t    *pc,                /* I - PPD cache and mapping data */
2276     ipp_attribute_t *media_col_sup,     /* I - media-col-supported values */
2277     ipp_attribute_t *doc_handling_sup)  /* I - multiple-document-handling-supported values */
2278 {
2279   int           i;                      /* Looping var */
2280   ipp_t         *request;               /* Request data */
2281   const char    *keyword;               /* PWG keyword */
2282   _pwg_size_t   *size;                  /* PWG media size */
2283   ipp_t         *media_col,             /* media-col value */
2284                 *media_size;            /* media-size value */
2285   const char    *media_source,          /* media-source value */
2286                 *media_type,            /* media-type value */
2287                 *collate_str;           /* multiple-document-handling value */
2288
2289
2290  /*
2291   * Create the IPP request...
2292   */
2293
2294   request                        = ippNewRequest(op);
2295   request->request.op.version[0] = version / 10;
2296   request->request.op.version[1] = version % 10;
2297
2298   fprintf(stderr, "DEBUG: %s IPP/%d.%d\n",
2299           ippOpString(request->request.op.operation_id),
2300           request->request.op.version[0],
2301           request->request.op.version[1]);
2302
2303  /*
2304   * Add standard attributes...
2305   */
2306
2307   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2308                NULL, uri);
2309   fprintf(stderr, "DEBUG: printer-uri=\"%s\"\n", uri);
2310
2311   if (user && *user)
2312   {
2313     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2314                  "requesting-user-name", NULL, user);
2315     fprintf(stderr, "DEBUG: requesting-user-name=\"%s\"\n", user);
2316   }
2317
2318   if (title && *title)
2319   {
2320     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
2321                  title);
2322     fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
2323   }
2324
2325   if (format && op != IPP_CREATE_JOB)
2326   {
2327     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
2328                  "document-format", NULL, format);
2329     fprintf(stderr, "DEBUG: document-format=\"%s\"\n", format);
2330   }
2331
2332 #ifdef HAVE_LIBZ
2333   if (compression && op != IPP_CREATE_JOB)
2334   {
2335     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2336                  "compression", NULL, compression);
2337     fprintf(stderr, "DEBUG: compression=\"%s\"\n", compression);
2338   }
2339 #endif /* HAVE_LIBZ */
2340
2341  /*
2342   * Handle options on the command-line...
2343   */
2344
2345   if (num_options > 0)
2346   {
2347     if (pc)
2348     {
2349       int       num_finishings = 0,     /* Number of finishing values */
2350                 finishings[10];         /* Finishing enum values */
2351
2352      /*
2353       * Send standard IPP attributes...
2354       */
2355
2356       if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
2357         keyword = cupsGetOption("media", num_options, options);
2358
2359       if ((size = _ppdCacheGetSize(pc, keyword)) != NULL)
2360       {
2361        /*
2362         * Add a media-col value...
2363         */
2364
2365         media_size = ippNew();
2366         ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2367                       "x-dimension", size->width);
2368         ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2369                       "y-dimension", size->length);
2370
2371         media_col = ippNew();
2372         ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
2373
2374         media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot",
2375                                                             num_options,
2376                                                             options));
2377         media_type   = _ppdCacheGetType(pc, cupsGetOption("MediaType",
2378                                                           num_options,
2379                                                           options));
2380
2381         for (i = 0; i < media_col_sup->num_values; i ++)
2382         {
2383           if (!strcmp(media_col_sup->values[i].string.text,
2384                       "media-left-margin"))
2385             ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2386                           "media-left-margin", size->left);
2387           else if (!strcmp(media_col_sup->values[i].string.text,
2388                            "media-bottom-margin"))
2389             ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2390                           "media-bottom-margin", size->bottom);
2391           else if (!strcmp(media_col_sup->values[i].string.text,
2392                            "media-right-margin"))
2393             ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2394                           "media-right-margin", size->right);
2395           else if (!strcmp(media_col_sup->values[i].string.text,
2396                            "media-top-margin"))
2397             ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2398                           "media-top-margin", size->top);
2399           else if (!strcmp(media_col_sup->values[i].string.text,
2400                            "media-source") && media_source)
2401             ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2402                          "media-source", NULL, media_source);
2403           else if (!strcmp(media_col_sup->values[i].string.text,
2404                            "media-type") && media_type)
2405             ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2406                          "media-type", NULL, media_type);
2407         }
2408
2409         ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
2410       }
2411
2412       if ((keyword = cupsGetOption("output-bin", num_options,
2413                                    options)) == NULL)
2414         keyword = _ppdCacheGetBin(pc, cupsGetOption("OutputBin", num_options,
2415                                                     options));
2416
2417       if (keyword)
2418         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin",
2419                      NULL, keyword);
2420
2421       if ((keyword = cupsGetOption("output-mode", num_options,
2422                                    options)) != NULL)
2423         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2424                      NULL, keyword);
2425       else if ((keyword = cupsGetOption("ColorModel", num_options,
2426                                         options)) != NULL)
2427       {
2428         if (!_cups_strcasecmp(keyword, "Gray"))
2429           ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2430                                NULL, "monochrome");
2431         else
2432           ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2433                            NULL, "color");
2434       }
2435
2436       if ((keyword = cupsGetOption("print-quality", num_options,
2437                                    options)) != NULL)
2438         ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2439                       atoi(keyword));
2440       else if ((keyword = cupsGetOption("cupsPrintQuality", num_options,
2441                                         options)) != NULL)
2442       {
2443         if (!_cups_strcasecmp(keyword, "draft"))
2444           ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2445                         IPP_QUALITY_DRAFT);
2446         else if (!_cups_strcasecmp(keyword, "normal"))
2447           ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2448                         IPP_QUALITY_NORMAL);
2449         else if (!_cups_strcasecmp(keyword, "high"))
2450           ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2451                         IPP_QUALITY_HIGH);
2452       }
2453
2454       if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
2455         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2456                      NULL, keyword);
2457       else if (pc->sides_option &&
2458                (keyword = cupsGetOption(pc->sides_option, num_options,
2459                                         options)) != NULL)
2460       {
2461         if (!_cups_strcasecmp(keyword, pc->sides_1sided))
2462           ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2463                        NULL, "one-sided");
2464         else if (!_cups_strcasecmp(keyword, pc->sides_2sided_long))
2465           ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2466                        NULL, "two-sided-long-edge");
2467         if (!_cups_strcasecmp(keyword, pc->sides_2sided_short))
2468           ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2469                        NULL, "two-sided-short-edge");
2470       }
2471
2472       if (doc_handling_sup &&
2473           (!format || _cups_strncasecmp(format, "image/", 6)) &&
2474           (keyword = cupsGetOption("collate", num_options, options)) != NULL)
2475       {
2476         if (!_cups_strcasecmp(keyword, "true"))
2477           collate_str = "separate-documents-collated-copies";
2478         else
2479           collate_str = "separate-documents-uncollated-copies";
2480
2481         for (i = 0; i < doc_handling_sup->num_values; i ++)
2482           if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
2483           {
2484             ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2485                          "multiple-document-handling", NULL, collate_str);
2486             break;
2487           }
2488       }
2489
2490      /*
2491       * Map finishing options...
2492       */
2493
2494       num_finishings = _ppdCacheGetFinishingValues(pc, num_options, options,
2495                                                    (int)(sizeof(finishings) /
2496                                                          sizeof(finishings[0])),
2497                                                    finishings);
2498       if (num_finishings > 0)
2499         ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings",
2500                        num_finishings, finishings);
2501
2502      /*
2503       * Map FaxOut options...
2504       */
2505
2506       if ((keyword = cupsGetOption("phone", num_options, options)) != NULL)
2507       {
2508         ipp_t   *destination;           /* destination collection */
2509         char    tel_uri[1024];          /* tel: URI */
2510
2511         destination = ippNew();
2512
2513         httpAssembleURI(HTTP_URI_CODING_ALL, tel_uri, sizeof(tel_uri), "tel",
2514                         NULL, NULL, 0, keyword);
2515         ippAddString(destination, IPP_TAG_JOB, IPP_TAG_URI, "destination-uri",
2516                      NULL, tel_uri);
2517
2518         if ((keyword = cupsGetOption("faxPrefix", num_options,
2519                                      options)) != NULL && *keyword)
2520           ippAddString(destination, IPP_TAG_JOB, IPP_TAG_TEXT,
2521                        "pre-dial-string", NULL, keyword);
2522
2523         ippAddCollection(request, IPP_TAG_JOB, "destination-uris", destination);
2524         ippDelete(destination);
2525       }
2526     }
2527     else
2528     {
2529      /*
2530       * When talking to another CUPS server, send all options...
2531       */
2532
2533       cupsEncodeOptions(request, num_options, options);
2534     }
2535
2536     if (copies > 1)
2537       ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
2538   }
2539
2540   return (request);
2541 }
2542
2543
2544 /*
2545  * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
2546  */
2547
2548 static const char *                     /* O - Password  */
2549 password_cb(const char *prompt)         /* I - Prompt (not used) */
2550 {
2551   fprintf(stderr, "DEBUG: password_cb(prompt=\"%s\"), password=%p, "
2552           "password_tries=%d\n", prompt, password, password_tries);
2553
2554   (void)prompt;
2555
2556  /*
2557   * Remember that we need to authenticate...
2558   */
2559
2560   auth_info_required = "username,password";
2561
2562   if (password && *password && password_tries < 3)
2563   {
2564     password_tries ++;
2565
2566     return (password);
2567   }
2568   else
2569   {
2570    /*
2571     * Give up after 3 tries or if we don't have a password to begin with...
2572     */
2573
2574     return (NULL);
2575   }
2576 }
2577
2578
2579 /*
2580  * 'report_attr()' - Report an IPP attribute value.
2581  */
2582
2583 static void
2584 report_attr(ipp_attribute_t *attr)      /* I - Attribute */
2585 {
2586   int           i;                      /* Looping var */
2587   char          value[1024],            /* Value string */
2588                 *valptr,                /* Pointer into value string */
2589                 *attrptr;               /* Pointer into attribute value */
2590   const char    *cached;                /* Cached attribute */
2591
2592
2593  /*
2594   * Convert the attribute values into quoted strings...
2595   */
2596
2597   for (i = 0, valptr = value;
2598        i < attr->num_values && valptr < (value + sizeof(value) - 10);
2599        i ++)
2600   {
2601     if (i > 0)
2602       *valptr++ = ',';
2603
2604     switch (attr->value_tag)
2605     {
2606       case IPP_TAG_INTEGER :
2607       case IPP_TAG_ENUM :
2608           snprintf(valptr, sizeof(value) - (valptr - value), "%d",
2609                    attr->values[i].integer);
2610           valptr += strlen(valptr);
2611           break;
2612
2613       case IPP_TAG_TEXT :
2614       case IPP_TAG_NAME :
2615       case IPP_TAG_KEYWORD :
2616           *valptr++ = '\'';
2617           *valptr++ = '\"';
2618           for (attrptr = attr->values[i].string.text;
2619                *attrptr && valptr < (value + sizeof(value) - 10);
2620                attrptr ++)
2621           {
2622             if (*attrptr == '\\' || *attrptr == '\"' || *attrptr == '\'')
2623             {
2624               *valptr++ = '\\';
2625               *valptr++ = '\\';
2626               *valptr++ = '\\';
2627             }
2628
2629             *valptr++ = *attrptr;
2630           }
2631           *valptr++ = '\"';
2632           *valptr++ = '\'';
2633           break;
2634
2635       default :
2636          /*
2637           * Unsupported value type...
2638           */
2639
2640           return;
2641     }
2642   }
2643
2644   *valptr = '\0';
2645
2646   _cupsMutexLock(&report_mutex);
2647
2648   if ((cached = cupsGetOption(attr->name, num_attr_cache,
2649                               attr_cache)) == NULL || strcmp(cached, value))
2650   {
2651    /*
2652     * Tell the scheduler about the new values...
2653     */
2654
2655     num_attr_cache = cupsAddOption(attr->name, value, num_attr_cache,
2656                                    &attr_cache);
2657     fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
2658   }
2659
2660   _cupsMutexUnlock(&report_mutex);
2661 }
2662
2663
2664 /*
2665  * 'report_printer_state()' - Report the printer state.
2666  */
2667
2668 static void
2669 report_printer_state(ipp_t *ipp)        /* I - IPP response */
2670 {
2671   ipp_attribute_t       *pa,            /* printer-alert */
2672                         *pam,           /* printer-alert-message */
2673                         *psm,           /* printer-state-message */
2674                         *reasons,       /* printer-state-reasons */
2675                         *marker;        /* marker-* attributes */
2676   char                  value[1024],    /* State/message string */
2677                         *valptr;        /* Pointer into string */
2678   static int            ipp_supplies = -1;
2679                                         /* Report supply levels? */
2680
2681
2682  /*
2683   * Report alerts and messages...
2684   */
2685
2686   if ((pa = ippFindAttribute(ipp, "printer-alert", IPP_TAG_TEXT)) != NULL)
2687     report_attr(pa);
2688
2689   if ((pam = ippFindAttribute(ipp, "printer-alert-message",
2690                               IPP_TAG_TEXT)) != NULL)
2691     report_attr(pam);
2692
2693   if ((psm = ippFindAttribute(ipp, "printer-state-message",
2694                               IPP_TAG_TEXT)) != NULL)
2695   {
2696     char        *ptr;                   /* Pointer into message */
2697
2698
2699     strlcpy(value, "INFO: ", sizeof(value));
2700     for (ptr = psm->values[0].string.text, valptr = value + 6;
2701          *ptr && valptr < (value + sizeof(value) - 6);
2702          ptr ++)
2703     {
2704       if (*ptr < ' ' && *ptr > 0 && *ptr != '\t')
2705       {
2706        /*
2707         * Substitute "<XX>" for the control character; sprintf is safe because
2708         * we always leave 6 chars free at the end...
2709         */
2710
2711         sprintf(valptr, "<%02X>", *ptr);
2712         valptr += 4;
2713       }
2714       else
2715         *valptr++ = *ptr;
2716     }
2717
2718     *valptr++ = '\n';
2719     *valptr   = '\0';
2720
2721     fputs(value, stderr);
2722   }
2723
2724  /*
2725   * Now report printer-state-reasons, filtering out some of the reasons we never
2726   * want to set...
2727   */
2728
2729   if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
2730                                   IPP_TAG_KEYWORD)) == NULL)
2731     return;
2732
2733   update_reasons(reasons, NULL);
2734
2735  /*
2736   * Relay the current marker-* attribute values...
2737   */
2738
2739   if (ipp_supplies < 0)
2740   {
2741     ppd_file_t  *ppd;                   /* PPD file */
2742     ppd_attr_t  *ppdattr;               /* Attribute in PPD file */
2743
2744     if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL &&
2745         (ppdattr = ppdFindAttr(ppd, "cupsIPPSupplies", NULL)) != NULL &&
2746         ppdattr->value && _cups_strcasecmp(ppdattr->value, "true"))
2747       ipp_supplies = 0;
2748     else
2749       ipp_supplies = 1;
2750
2751     ppdClose(ppd);
2752   }
2753
2754   if (ipp_supplies > 0)
2755   {
2756     if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
2757       report_attr(marker);
2758     if ((marker = ippFindAttribute(ipp, "marker-high-levels",
2759                                    IPP_TAG_INTEGER)) != NULL)
2760       report_attr(marker);
2761     if ((marker = ippFindAttribute(ipp, "marker-levels",
2762                                    IPP_TAG_INTEGER)) != NULL)
2763       report_attr(marker);
2764     if ((marker = ippFindAttribute(ipp, "marker-low-levels",
2765                                    IPP_TAG_INTEGER)) != NULL)
2766       report_attr(marker);
2767     if ((marker = ippFindAttribute(ipp, "marker-message",
2768                                    IPP_TAG_TEXT)) != NULL)
2769       report_attr(marker);
2770     if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
2771       report_attr(marker);
2772     if ((marker = ippFindAttribute(ipp, "marker-types",
2773                                    IPP_TAG_KEYWORD)) != NULL)
2774       report_attr(marker);
2775   }
2776 }
2777
2778
2779 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
2780 /*
2781  * 'run_as_user()' - Run the IPP backend as the printing user.
2782  *
2783  * This function uses an XPC-based user agent to run the backend as the printing
2784  * user. We need to do this in order to have access to the user's Kerberos
2785  * credentials.
2786  */
2787
2788 static int                              /* O - Exit status */
2789 run_as_user(int        argc,            /* I - Number of command-line args */
2790             char       *argv[],         /* I - Command-line arguments */
2791             uid_t      uid,             /* I - User ID */
2792             const char *device_uri,     /* I - Device URI */
2793             int        fd)              /* I - File to print */
2794 {
2795   const char            *auth_negotiate;/* AUTH_NEGOTIATE env var */
2796   xpc_connection_t      conn;           /* Connection to XPC service */
2797   xpc_object_t          request;        /* Request message dictionary */
2798   __block xpc_object_t  response;       /* Response message dictionary */
2799   dispatch_semaphore_t  sem;            /* Semaphore for waiting for response */
2800   int                   status = CUPS_BACKEND_FAILED;
2801                                         /* Status of request */
2802
2803
2804   fprintf(stderr, "DEBUG: Running IPP backend as UID %d.\n", (int)uid);
2805
2806  /*
2807   * Connect to the user agent for the specified UID...
2808   */
2809
2810   conn = xpc_connection_create_mach_service(kPMPrintUIToolAgent,
2811                                             dispatch_get_global_queue(0, 0), 0);
2812   if (!conn)
2813   {
2814     _cupsLangPrintFilter(stderr, "ERROR",
2815                          _("Unable to start backend process."));
2816     fputs("DEBUG: Unable to create connection to agent.\n", stderr);
2817     goto cleanup;
2818   }
2819
2820   xpc_connection_set_event_handler(conn,
2821                                    ^(xpc_object_t event)
2822                                    {
2823                                      xpc_type_t messageType = xpc_get_type(event);
2824
2825                                      if (messageType == XPC_TYPE_ERROR)
2826                                      {
2827                                        if (event == XPC_ERROR_CONNECTION_INTERRUPTED)
2828                                          fprintf(stderr, "DEBUG: Interrupted connection to service %s.\n",
2829                                                  xpc_connection_get_name(conn));
2830                                        else if (event == XPC_ERROR_CONNECTION_INVALID)
2831                                          fprintf(stderr, "DEBUG: Connection invalid for service %s.\n",
2832                                                  xpc_connection_get_name(conn));
2833                                        else
2834                                          fprintf(stderr, "DEBUG: Unxpected error for service %s: %s\n",
2835                                                  xpc_connection_get_name(conn),
2836                                                  xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
2837                                      }
2838                                    });
2839   xpc_connection_set_target_uid(conn, uid);
2840   xpc_connection_resume(conn);
2841
2842  /*
2843   * Try starting the backend...
2844   */
2845
2846   request = xpc_dictionary_create(NULL, NULL, 0);
2847   xpc_dictionary_set_int64(request, "command", kPMStartJob);
2848   xpc_dictionary_set_string(request, "device-uri", device_uri);
2849   xpc_dictionary_set_string(request, "job-id", argv[1]);
2850   xpc_dictionary_set_string(request, "user", argv[2]);
2851   xpc_dictionary_set_string(request, "title", argv[3]);
2852   xpc_dictionary_set_string(request, "copies", argv[4]);
2853   xpc_dictionary_set_string(request, "options", argv[5]);
2854   xpc_dictionary_set_string(request, "auth-info-required",
2855                             getenv("AUTH_INFO_REQUIRED"));
2856   if ((auth_negotiate = getenv("AUTH_NEGOTIATE")) != NULL)
2857     xpc_dictionary_set_string(request, "auth-negotiate", auth_negotiate);
2858   xpc_dictionary_set_fd(request, "stdin", fd);
2859   xpc_dictionary_set_fd(request, "stderr", 2);
2860   xpc_dictionary_set_fd(request, "side-channel", CUPS_SC_FD);
2861
2862   sem      = dispatch_semaphore_create(0);
2863   response = NULL;
2864
2865   xpc_connection_send_message_with_reply(conn, request,
2866                                          dispatch_get_global_queue(0,0),
2867                                          ^(xpc_object_t reply)
2868                                          {
2869                                            /* Save the response and wake up */
2870                                            if (xpc_get_type(reply)
2871                                                    == XPC_TYPE_DICTIONARY)
2872                                              response = xpc_retain(reply);
2873
2874                                            dispatch_semaphore_signal(sem);
2875                                          });
2876
2877   dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
2878   xpc_release(request);
2879   dispatch_release(sem);
2880
2881   if (response)
2882   {
2883     child_pid = xpc_dictionary_get_int64(response, "child-pid");
2884
2885     xpc_release(response);
2886
2887     if (child_pid)
2888       fprintf(stderr, "DEBUG: Child PID=%d.\n", child_pid);
2889     else
2890     {
2891       _cupsLangPrintFilter(stderr, "ERROR",
2892                            _("Unable to start backend process."));
2893       fputs("DEBUG: No child PID.\n", stderr);
2894       goto cleanup;
2895     }
2896   }
2897   else
2898   {
2899     _cupsLangPrintFilter(stderr, "ERROR",
2900                          _("Unable to start backend process."));
2901     fputs("DEBUG: No reply from agent.\n", stderr);
2902     goto cleanup;
2903   }
2904
2905  /*
2906   * Then wait for the backend to finish...
2907   */
2908
2909   request = xpc_dictionary_create(NULL, NULL, 0);
2910   xpc_dictionary_set_int64(request, "command", kPMWaitForJob);
2911   xpc_dictionary_set_fd(request, "stderr", 2);
2912
2913   sem      = dispatch_semaphore_create(0);
2914   response = NULL;
2915
2916   xpc_connection_send_message_with_reply(conn, request,
2917                                          dispatch_get_global_queue(0,0),
2918                                          ^(xpc_object_t reply)
2919                                          {
2920                                            /* Save the response and wake up */
2921                                            if (xpc_get_type(reply)
2922                                                    == XPC_TYPE_DICTIONARY)
2923                                              response = xpc_retain(reply);
2924
2925                                            dispatch_semaphore_signal(sem);
2926                                          });
2927
2928   dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
2929   xpc_release(request);
2930   dispatch_release(sem);
2931
2932   if (response)
2933   {
2934     status = xpc_dictionary_get_int64(response, "status");
2935
2936     if (status == SIGTERM || status == SIGKILL || status == SIGPIPE)
2937     {
2938       fprintf(stderr, "DEBUG: Child terminated on signal %d.\n", status);
2939       status = CUPS_BACKEND_FAILED;
2940     }
2941     else if (WIFSIGNALED(status))
2942     {
2943       fprintf(stderr, "DEBUG: Child crashed on signal %d.\n", status);
2944       status = CUPS_BACKEND_STOP;
2945     }
2946     else if (WIFEXITED(status))
2947     {
2948       status = WEXITSTATUS(status);
2949       fprintf(stderr, "DEBUG: Child exited with status %d.\n", status);
2950     }
2951
2952     xpc_release(response);
2953   }
2954   else
2955     _cupsLangPrintFilter(stderr, "ERROR",
2956                          _("Unable to get backend exit status."));
2957
2958   cleanup:
2959
2960   if (conn)
2961   {
2962     xpc_connection_suspend(conn);
2963     xpc_connection_cancel(conn);
2964     xpc_release(conn);
2965   }
2966
2967   return (status);
2968 }
2969 #endif /* HAVE_GSSAPI && HAVE_XPC */
2970
2971
2972 /*
2973  * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
2974  */
2975
2976 static void
2977 sigterm_handler(int sig)                /* I - Signal */
2978 {
2979   (void)sig;    /* remove compiler warnings... */
2980
2981 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
2982   if (child_pid)
2983   {
2984     kill(child_pid, sig);
2985     child_pid = 0;
2986   }
2987 #endif /* HAVE_GSSAPI && HAVE_XPC */
2988
2989   if (!job_canceled)
2990   {
2991    /*
2992     * Flag that the job should be canceled...
2993     */
2994
2995     job_canceled = 1;
2996     return;
2997   }
2998
2999  /*
3000   * The scheduler already tried to cancel us once, now just terminate
3001   * after removing our temp file!
3002   */
3003
3004   if (tmpfilename[0])
3005     unlink(tmpfilename);
3006
3007   exit(1);
3008 }
3009
3010
3011 /*
3012  * 'timeout_cb()' - Handle HTTP timeouts.
3013  */
3014
3015 static int                              /* O - 1 to continue, 0 to cancel */
3016 timeout_cb(http_t *http,                /* I - Connection to server (unused) */
3017            void   *user_data)           /* I - User data (unused) */
3018 {
3019   (void)http;
3020   (void)user_data;
3021
3022   return (!job_canceled);
3023 }
3024
3025
3026 /*
3027  * 'update_reasons()' - Update the printer-state-reasons values.
3028  */
3029
3030 static void
3031 update_reasons(ipp_attribute_t *attr,   /* I - printer-state-reasons or NULL */
3032                const char      *s)      /* I - STATE: string or NULL */
3033 {
3034   char          op;                     /* Add (+), remove (-), replace (\0) */
3035   cups_array_t  *new_reasons;           /* New reasons array */
3036   char          *reason,                /* Current reason */
3037                 add[2048],              /* Reasons added string */
3038                 *addptr,                /* Pointer into add string */
3039                 rem[2048],              /* Reasons removed string */
3040                 *remptr;                /* Pointer into remove string */
3041   const char    *addprefix,             /* Current add string prefix */
3042                 *remprefix;             /* Current remove string prefix */
3043
3044
3045   fprintf(stderr, "DEBUG: update_reasons(attr=%d(%s%s), s=\"%s\")\n",
3046           attr ? attr->num_values : 0, attr ? attr->values[0].string.text : "",
3047           attr && attr->num_values > 1 ? ",..." : "", s ? s : "(null)");
3048
3049  /*
3050   * Create an array of new reason keyword strings...
3051   */
3052
3053   if (attr)
3054   {
3055     int i;                              /* Looping var */
3056
3057     new_reasons = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3058     op          = '\0';
3059
3060     for (i = 0; i < attr->num_values; i ++)
3061     {
3062       reason = attr->values[i].string.text;
3063
3064       if (strcmp(reason, "none") &&
3065           strcmp(reason, "none-report") &&
3066           strcmp(reason, "paused") &&
3067           strncmp(reason, "spool-area-full", 15) &&
3068           strcmp(reason, "com.apple.print.recoverable-warning") &&
3069           strncmp(reason, "cups-", 5))
3070         cupsArrayAdd(new_reasons, reason);
3071     }
3072   }
3073   else if (s)
3074   {
3075     if (*s == '+' || *s == '-')
3076       op = *s++;
3077     else
3078       op = '\0';
3079
3080     new_reasons = _cupsArrayNewStrings(s);
3081   }
3082   else
3083     return;
3084
3085  /*
3086   * Compute the changes...
3087   */
3088
3089   add[0]    = '\0';
3090   addprefix = "STATE: +";
3091   addptr    = add;
3092   rem[0]    = '\0';
3093   remprefix = "STATE: -";
3094   remptr    = rem;
3095
3096   fprintf(stderr, "DEBUG2: op='%c', new_reasons=%d, state_reasons=%d\n",
3097           op ? op : ' ', cupsArrayCount(new_reasons),
3098           cupsArrayCount(state_reasons));
3099
3100   _cupsMutexLock(&report_mutex);
3101
3102   if (op == '+')
3103   {
3104    /*
3105     * Add reasons...
3106     */
3107
3108     for (reason = (char *)cupsArrayFirst(new_reasons);
3109          reason;
3110          reason = (char *)cupsArrayNext(new_reasons))
3111     {
3112       if (!cupsArrayFind(state_reasons, reason))
3113       {
3114         if (!strncmp(reason, "cups-remote-", 12))
3115         {
3116          /*
3117           * If we are setting cups-remote-xxx, remove all other cups-remote-xxx
3118           * keywords...
3119           */
3120
3121           char  *temp;          /* Current reason in state_reasons */
3122
3123           cupsArraySave(state_reasons);
3124
3125           for (temp = (char *)cupsArrayFirst(state_reasons);
3126                temp;
3127                temp = (char *)cupsArrayNext(state_reasons))
3128             if (!strncmp(temp, "cups-remote-", 12))
3129             {
3130               snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
3131                        temp);
3132               remptr    += strlen(remptr);
3133               remprefix = ",";
3134
3135               cupsArrayRemove(state_reasons, temp);
3136               break;
3137             }
3138
3139           cupsArrayRestore(state_reasons);
3140         }
3141
3142         cupsArrayAdd(state_reasons, reason);
3143
3144         snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
3145                  reason);
3146         addptr    += strlen(addptr);
3147         addprefix = ",";
3148       }
3149     }
3150   }
3151   else if (op == '-')
3152   {
3153    /*
3154     * Remove reasons...
3155     */
3156
3157     for (reason = (char *)cupsArrayFirst(new_reasons);
3158          reason;
3159          reason = (char *)cupsArrayNext(new_reasons))
3160     {
3161       if (cupsArrayFind(state_reasons, reason))
3162       {
3163         snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
3164                  reason);
3165         remptr    += strlen(remptr);
3166         remprefix = ",";
3167
3168         cupsArrayRemove(state_reasons, reason);
3169       }
3170     }
3171   }
3172   else
3173   {
3174    /*
3175     * Replace reasons...
3176     */
3177
3178     for (reason = (char *)cupsArrayFirst(state_reasons);
3179          reason;
3180          reason = (char *)cupsArrayNext(state_reasons))
3181     {
3182       if (strncmp(reason, "cups-", 5) && !cupsArrayFind(new_reasons, reason))
3183       {
3184         snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
3185                  reason);
3186         remptr    += strlen(remptr);
3187         remprefix = ",";
3188
3189         cupsArrayRemove(state_reasons, reason);
3190       }
3191     }
3192
3193     for (reason = (char *)cupsArrayFirst(new_reasons);
3194          reason;
3195          reason = (char *)cupsArrayNext(new_reasons))
3196     {
3197       if (!cupsArrayFind(state_reasons, reason))
3198       {
3199         cupsArrayAdd(state_reasons, reason);
3200
3201         snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
3202                  reason);
3203         addptr    += strlen(addptr);
3204         addprefix = ",";
3205       }
3206     }
3207   }
3208
3209   _cupsMutexUnlock(&report_mutex);
3210
3211  /*
3212   * Report changes and return...
3213   */
3214
3215   if (add[0] && rem[0])
3216     fprintf(stderr, "%s\n%s\n", add, rem);
3217   else if (add[0])
3218     fprintf(stderr, "%s\n", add);
3219   else if (rem[0])
3220     fprintf(stderr, "%s\n", rem);
3221 }
3222
3223 /*
3224  * End of "$Id: ipp.c 10452 2012-05-04 23:00:01Z mike $".
3225  */