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