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