• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * IPP Everywhere printer application for CUPS.
3  *
4  * Copyright © 2020-2024 by OpenPrinting.
5  * Copyright © 2020 by the IEEE-ISTO Printer Working Group.
6  * Copyright © 2010-2021 by Apple Inc.
7  *
8  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
9  * information.
10  *
11  * Note: This program began life as the "ippserver" sample code that first
12  * appeared in CUPS 1.4.  The name has been changed in order to distinguish it
13  * from the PWG's much more ambitious "ippserver" program, which supports
14  * different kinds of IPP services and multiple services per instance - the
15  * "ippeveprinter" program exposes a single print service conforming to the
16  * current IPP Everywhere specification, thus the new name.
17  */
18 
19 /*
20  * Include necessary headers...
21  */
22 
23 #include <cups/cups-private.h>
24 #include <cups/debug-private.h>
25 #if !CUPS_LITE
26 #  include <cups/ppd-private.h>
27 #endif /* !CUPS_LITE */
28 
29 #include <limits.h>
30 #include <fcntl.h>
31 #include <sys/stat.h>
32 
33 #ifdef _WIN32
34 #  include <io.h>
35 #  include <process.h>
36 #  define WEXITSTATUS(s) (s)
37 #  include <winsock2.h>
38 typedef ULONG nfds_t;
39 #  define poll WSAPoll
40 #else
41 extern char **environ;
42 
43 #  include <spawn.h>
44 #  include <sys/wait.h>
45 #  include <poll.h>
46 #endif /* _WIN32 */
47 
48 #ifndef O_BINARY
49 #  define O_BINARY 0			/* Windows "binary file" nonsense */
50 #endif /* !O_BINARY */
51 
52 #ifdef HAVE_MDNSRESPONDER
53 #  include <dns_sd.h>
54 #elif defined(HAVE_AVAHI)
55 #  include <avahi-client/client.h>
56 #  include <avahi-client/publish.h>
57 #  include <avahi-common/alternative.h>
58 #  include <avahi-common/error.h>
59 #  include <avahi-common/malloc.h>
60 #  include <avahi-common/thread-watch.h>
61 #endif /* HAVE_MDNSRESPONDER */
62 
63 #ifdef HAVE_SYS_MOUNT_H
64 #  include <sys/mount.h>
65 #endif /* HAVE_SYS_MOUNT_H */
66 #ifdef HAVE_SYS_STATFS_H
67 #  include <sys/statfs.h>
68 #endif /* HAVE_SYS_STATFS_H */
69 #ifdef HAVE_SYS_STATVFS_H
70 #  include <sys/statvfs.h>
71 #endif /* HAVE_SYS_STATVFS_H */
72 #ifdef HAVE_SYS_VFS_H
73 #  include <sys/vfs.h>
74 #endif /* HAVE_SYS_VFS_H */
75 
76 #if HAVE_LIBPAM
77 #  ifdef HAVE_PAM_PAM_APPL_H
78 #    include <pam/pam_appl.h>
79 #  else
80 #    include <security/pam_appl.h>
81 #  endif /* HAVE_PAM_PAM_APPL_H */
82 #endif /* HAVE_LIBPAM */
83 
84 #include "printer-png.h"
85 #include "printer-lg-png.h"
86 #include "printer-sm-png.h"
87 
88 
89 /*
90  * Constants...
91  */
92 
93 enum ippeve_preason_e			/* printer-state-reasons bit values */
94 {
95   IPPEVE_PREASON_NONE = 0x0000,		/* none */
96   IPPEVE_PREASON_OTHER = 0x0001,	/* other */
97   IPPEVE_PREASON_COVER_OPEN = 0x0002,	/* cover-open */
98   IPPEVE_PREASON_INPUT_TRAY_MISSING = 0x0004,
99 					/* input-tray-missing */
100   IPPEVE_PREASON_MARKER_SUPPLY_EMPTY = 0x0008,
101 					/* marker-supply-empty */
102   IPPEVE_PREASON_MARKER_SUPPLY_LOW = 0x0010,
103 					/* marker-supply-low */
104   IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL = 0x0020,
105 					/* marker-waste-almost-full */
106   IPPEVE_PREASON_MARKER_WASTE_FULL = 0x0040,
107 					/* marker-waste-full */
108   IPPEVE_PREASON_MEDIA_EMPTY = 0x0080,	/* media-empty */
109   IPPEVE_PREASON_MEDIA_JAM = 0x0100,	/* media-jam */
110   IPPEVE_PREASON_MEDIA_LOW = 0x0200,	/* media-low */
111   IPPEVE_PREASON_MEDIA_NEEDED = 0x0400,	/* media-needed */
112   IPPEVE_PREASON_MOVING_TO_PAUSED = 0x0800,
113 					/* moving-to-paused */
114   IPPEVE_PREASON_PAUSED = 0x1000,	/* paused */
115   IPPEVE_PREASON_SPOOL_AREA_FULL = 0x2000,/* spool-area-full */
116   IPPEVE_PREASON_TONER_EMPTY = 0x4000,	/* toner-empty */
117   IPPEVE_PREASON_TONER_LOW = 0x8000	/* toner-low */
118 };
119 typedef unsigned int ippeve_preason_t;	/* Bitfield for printer-state-reasons */
120 static const char * const ippeve_preason_strings[] =
121 {					/* Strings for each bit */
122   /* "none" is implied for no bits set */
123   "other",
124   "cover-open",
125   "input-tray-missing",
126   "marker-supply-empty",
127   "marker-supply-low",
128   "marker-waste-almost-full",
129   "marker-waste-full",
130   "media-empty",
131   "media-jam",
132   "media-low",
133   "media-needed",
134   "moving-to-paused",
135   "paused",
136   "spool-area-full",
137   "toner-empty",
138   "toner-low"
139 };
140 
141 
142 /*
143  * URL scheme for web resources...
144  */
145 
146 #ifdef HAVE_TLS
147 #  define WEB_SCHEME "https"
148 #else
149 #  define WEB_SCHEME "http"
150 #endif /* HAVE_TLS */
151 
152 
153 /*
154  * Structures...
155  */
156 
157 #ifdef HAVE_MDNSRESPONDER
158 typedef DNSServiceRef ippeve_srv_t;	/* Service reference */
159 typedef TXTRecordRef ippeve_txt_t;	/* TXT record */
160 
161 #elif defined(HAVE_AVAHI)
162 typedef AvahiEntryGroup *ippeve_srv_t;	/* Service reference */
163 typedef AvahiStringList *ippeve_txt_t;	/* TXT record */
164 
165 #else
166 typedef void *ippeve_srv_t;		/* Service reference */
167 typedef void *ippeve_txt_t;		/* TXT record */
168 #endif /* HAVE_MDNSRESPONDER */
169 
170 #if HAVE_LIBPAM
171 typedef struct ippeve_authdata_s	/* Authentication data */
172 {
173   char	username[HTTP_MAX_VALUE],	/* Username string */
174 	*password;			/* Password string */
175 } ippeve_authdata_t;
176 #endif /* HAVE_LIBPAM */
177 
178 typedef struct ippeve_filter_s		/**** Attribute filter ****/
179 {
180   cups_array_t		*ra;		/* Requested attributes */
181   ipp_tag_t		group_tag;	/* Group to copy */
182 } ippeve_filter_t;
183 
184 typedef struct ippeve_job_s ippeve_job_t;
185 
186 typedef struct ippeve_printer_s		/**** Printer data ****/
187 {
188   /* TODO: One IPv4 and one IPv6 listener are really not sufficient */
189   int			ipv4,		/* IPv4 listener */
190 			ipv6;		/* IPv6 listener */
191 #ifdef HAVE_MDNSRESPONDER
192   ippeve_srv_t		ipp_ref,	/* DNS-SD IPP service */
193 			ipps_ref,	/* DNS-SD IPPS service */
194 			http_ref,	/* DNS-SD HTTP service */
195 			printer_ref;	/* DNS-SD LPD service */
196 #elif defined(HAVE_AVAHI)
197   ippeve_srv_t		dnssd_ref;	/* DNS-SD services */
198 #endif /* HAVE_MDNSRESPONDER */
199   char			*dnssd_subtypes;/* DNS-SD subtypes */
200   int			dnssd_collision;/* Name collision? */
201   char			*dnssd_name,	/* printer-dns-sd-name */
202 			*name,		/* printer-name */
203 			*icons[3],	/* Icon filenames */
204 			*strings,	/* Strings filename */
205 			*directory,	/* Spool directory */
206 			*hostname,	/* Hostname */
207 			*device_uri,	/* Device URI (if any) */
208 			*output_format,	/* Output format */
209 #if !CUPS_LITE
210 			*ppdfile,	/* PPD file (if any) */
211 #endif /* !CUPS_LITE */
212 			*command;	/* Command to run with job file */
213   int			port;		/* Port */
214   int			web_forms;	/* Enable web interface forms? */
215   size_t		urilen;		/* Length of printer URI */
216   ipp_t			*attrs;		/* Static attributes */
217   time_t		start_time;	/* Startup time */
218   time_t		config_time;	/* printer-config-change-time */
219   ipp_pstate_t		state;		/* printer-state value */
220   ippeve_preason_t	state_reasons;	/* printer-state-reasons values */
221   time_t		state_time;	/* printer-state-change-time */
222   cups_array_t		*jobs;		/* Jobs */
223   ippeve_job_t		*active_job;	/* Current active/pending job */
224   int			next_job_id;	/* Next job-id value */
225   _cups_rwlock_t	rwlock;		/* Printer lock */
226 } ippeve_printer_t;
227 
228 struct ippeve_job_s			/**** Job data ****/
229 {
230   int			id;		/* Job ID */
231   const char		*name,		/* job-name */
232 			*username,	/* job-originating-user-name */
233 			*format;	/* document-format */
234   ipp_jstate_t		state;		/* job-state value */
235   char			*message;	/* job-state-message value */
236   int			msglevel;	/* job-state-message log level (0=error, 1=info) */
237   time_t		created,	/* time-at-creation value */
238 			processing,	/* time-at-processing value */
239 			completed;	/* time-at-completed value */
240   int			impressions,	/* job-impressions value */
241 			impcompleted;	/* job-impressions-completed value */
242   ipp_t			*attrs;		/* Static attributes */
243   int			cancel;		/* Non-zero when job canceled */
244   char			*filename;	/* Print file name */
245   int			fd;		/* Print file descriptor */
246   ippeve_printer_t	*printer;	/* Printer */
247 };
248 
249 typedef struct ippeve_client_s		/**** Client data ****/
250 {
251   http_t		*http;		/* HTTP connection */
252   ipp_t			*request,	/* IPP request */
253 			*response;	/* IPP response */
254   time_t		start;		/* Request start time */
255   http_state_t		operation;	/* Request operation */
256   ipp_op_t		operation_id;	/* IPP operation-id */
257   char			uri[1024],	/* Request URI */
258 			*options,	/* URI options */
259 			host_field[HTTP_MAX_VALUE];
260 					/* Host: header */
261   int			host_port;	/* Port number from Host: header */
262   http_addr_t		addr;		/* Client address */
263   char			hostname[256],	/* Client hostname */
264 			username[HTTP_MAX_VALUE];
265 					/* Authenticated username, if any */
266   ippeve_printer_t	*printer;	/* Printer */
267   ippeve_job_t		*job;		/* Current job, if any */
268 } ippeve_client_t;
269 
270 
271 /*
272  * Local functions...
273  */
274 
275 static http_status_t	authenticate_request(ippeve_client_t *client);
276 static void		clean_jobs(ippeve_printer_t *printer);
277 static int		compare_jobs(ippeve_job_t *a, ippeve_job_t *b);
278 static void		copy_attributes(ipp_t *to, ipp_t *from, cups_array_t *ra, ipp_tag_t group_tag, int quickcopy);
279 static void		copy_job_attributes(ippeve_client_t *client, ippeve_job_t *job, cups_array_t *ra);
280 static ippeve_client_t	*create_client(ippeve_printer_t *printer, int sock);
281 static ippeve_job_t	*create_job(ippeve_client_t *client);
282 static int		create_job_file(ippeve_job_t *job, char *fname, size_t fnamesize, const char *dir, const char *ext);
283 static int		create_listener(const char *name, int port, int family);
284 static ipp_t		*create_media_col(const char *media, const char *source, const char *type, int width, int length, int bottom, int left, int right, int top);
285 static ipp_t		*create_media_size(int width, int length);
286 static ippeve_printer_t	*create_printer(const char *servername, int serverport, const char *name, const char *location, const char *icons, const char *strings, cups_array_t *docformats, const char *subtypes, const char *directory, const char *command, const char *device_uri, const char *output_format, ipp_t *attrs);
287 static void		debug_attributes(const char *title, ipp_t *ipp, int response);
288 static void		delete_client(ippeve_client_t *client);
289 static void		delete_job(ippeve_job_t *job);
290 static void		delete_printer(ippeve_printer_t *printer);
291 #ifdef HAVE_MDNSRESPONDER
292 static void DNSSD_API	dnssd_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, ippeve_printer_t *printer);
293 #elif defined(HAVE_AVAHI)
294 static void		dnssd_callback(AvahiEntryGroup *p, AvahiEntryGroupState state, void *context);
295 static void		dnssd_client_cb(AvahiClient *c, AvahiClientState state, void *userdata);
296 #endif /* HAVE_MDNSRESPONDER */
297 static void		dnssd_init(void);
298 static int		filter_cb(ippeve_filter_t *filter, ipp_t *dst, ipp_attribute_t *attr);
299 static ippeve_job_t	*find_job(ippeve_client_t *client);
300 static void		finish_document_data(ippeve_client_t *client, ippeve_job_t *job);
301 static void		finish_document_uri(ippeve_client_t *client, ippeve_job_t *job);
302 static void		flush_document_data(ippeve_client_t *client);
303 static int		have_document_data(ippeve_client_t *client);
304 static void		html_escape(ippeve_client_t *client, const char *s, size_t slen);
305 static void		html_footer(ippeve_client_t *client);
306 static void		html_header(ippeve_client_t *client, const char *title, int refresh);
307 static void		html_printf(ippeve_client_t *client, const char *format, ...) _CUPS_FORMAT(2, 3);
308 static void		ipp_cancel_job(ippeve_client_t *client);
309 static void		ipp_cancel_my_jobs(ippeve_client_t *client);
310 static void		ipp_close_job(ippeve_client_t *client);
311 static void		ipp_create_job(ippeve_client_t *client);
312 static void		ipp_get_job_attributes(ippeve_client_t *client);
313 static void		ipp_get_jobs(ippeve_client_t *client);
314 static void		ipp_get_printer_attributes(ippeve_client_t *client);
315 static void		ipp_identify_printer(ippeve_client_t *client);
316 static void		ipp_print_job(ippeve_client_t *client);
317 static void		ipp_print_uri(ippeve_client_t *client);
318 static void		ipp_send_document(ippeve_client_t *client);
319 static void		ipp_send_uri(ippeve_client_t *client);
320 static void		ipp_validate_job(ippeve_client_t *client);
321 static ipp_t		*load_ippserver_attributes(const char *servername, int serverport, const char *filename, cups_array_t *docformats);
322 static ipp_t		*load_legacy_attributes(const char *make, const char *model, int ppm, int ppm_color, int duplex, cups_array_t *docformats);
323 #if !CUPS_LITE
324 static ipp_t		*load_ppd_attributes(const char *ppdfile, cups_array_t *docformats);
325 #endif /* !CUPS_LITE */
326 #if HAVE_LIBPAM
327 static int		pam_func(int, const struct pam_message **, struct pam_response **, void *);
328 #endif /* HAVE_LIBPAM */
329 static int		parse_options(ippeve_client_t *client, cups_option_t **options);
330 static void		process_attr_message(ippeve_job_t *job, char *message);
331 static void		*process_client(ippeve_client_t *client);
332 static int		process_http(ippeve_client_t *client);
333 static int		process_ipp(ippeve_client_t *client);
334 static void		*process_job(ippeve_job_t *job);
335 static void		process_state_message(ippeve_job_t *job, char *message);
336 static int		register_printer(ippeve_printer_t *printer);
337 static int		respond_http(ippeve_client_t *client, http_status_t code, const char *content_coding, const char *type, size_t length);
338 static void		respond_ignored(ippeve_client_t *client, ipp_attribute_t *attr);
339 static void		respond_ipp(ippeve_client_t *client, ipp_status_t status, const char *message, ...) _CUPS_FORMAT(3, 4);
340 static void		respond_unsupported(ippeve_client_t *client, ipp_attribute_t *attr);
341 static void		run_printer(ippeve_printer_t *printer);
342 static int		show_media(ippeve_client_t *client);
343 static int		show_status(ippeve_client_t *client);
344 static int		show_supplies(ippeve_client_t *client);
345 #ifndef _WIN32
346 static void		signal_handler(int signum);
347 #endif // !_WIN32
348 static char		*time_string(time_t tv, char *buffer, size_t bufsize);
349 static void		usage(int status) _CUPS_NORETURN;
350 static int		valid_doc_attributes(ippeve_client_t *client);
351 static int		valid_job_attributes(ippeve_client_t *client);
352 
353 
354 /*
355  * Globals...
356  */
357 
358 #ifdef HAVE_MDNSRESPONDER
359 static DNSServiceRef	DNSSDMaster = NULL;
360 #elif defined(HAVE_AVAHI)
361 static AvahiThreadedPoll *DNSSDMaster = NULL;
362 static AvahiClient	*DNSSDClient = NULL;
363 #endif /* HAVE_MDNSRESPONDER */
364 
365 static int		KeepFiles = 0,	/* Keep spooled job files? */
366 			MaxVersion = 20,/* Maximum IPP version (20 = 2.0, 11 = 1.1, etc.) */
367 			Verbosity = 0;	/* Verbosity level */
368 static const char	*PAMService = NULL;
369 					/* PAM service */
370 #ifndef _WIN32
371 static int		StopPrinter = 0;/* Stop the printer server? */
372 #endif // !_WIN32
373 
374 
375 /*
376  * 'main()' - Main entry to the sample server.
377  */
378 
379 int					/* O - Exit status */
main(int argc,char * argv[])380 main(int  argc,				/* I - Number of command-line args */
381      char *argv[])			/* I - Command-line arguments */
382 {
383   int		i;			/* Looping var */
384   const char	*opt,			/* Current option character */
385 		*attrfile = NULL,	/* ippserver attributes file */
386 		*command = NULL,	/* Command to run with job files */
387 		*device_uri = NULL,	/* Device URI */
388 		*output_format = NULL,	/* Output format */
389 		*icon = NULL,		/* Icon file */
390 #ifdef HAVE_TLS
391 		*keypath = NULL,	/* Keychain path */
392 #endif /* HAVE_TLS */
393 		*location = "",		/* Location of printer */
394 		*make = "Example",	/* Manufacturer */
395 		*model = "Printer",	/* Model */
396 		*name = NULL,		/* Printer name */
397 #if !CUPS_LITE
398 		*ppdfile = NULL,	/* PPD file */
399 #endif /* !CUPS_LITE */
400 		*strings = NULL,	/* Strings file */
401 		*subtypes = "_print";	/* DNS-SD service subtype */
402   int		legacy = 0,		/* Legacy mode? */
403 		duplex = 0,		/* Duplex mode */
404 		ppm = 10,		/* Pages per minute for mono */
405 		ppm_color = 0,		/* Pages per minute for color */
406 		web_forms = 1;		/* Enable website forms? */
407   ipp_t		*attrs = NULL;		/* Printer attributes */
408   char		directory[1024] = "";	/* Spool directory */
409   cups_array_t	*docformats = NULL;	/* Supported formats */
410   const char	*servername = NULL;	/* Server host name */
411   int		serverport = 0;		/* Server port number (0 = auto) */
412   ippeve_printer_t *printer;		/* Printer object */
413 
414 
415  /*
416   * Parse command-line arguments...
417   */
418 
419   for (i = 1; i < argc; i ++)
420   {
421     if (!strcmp(argv[i], "--help"))
422     {
423       usage(0);
424     }
425     else if (!strcmp(argv[i], "--no-web-forms"))
426     {
427       web_forms = 0;
428     }
429     else if (!strcmp(argv[i], "--pam-service"))
430     {
431       i ++;
432       if (i >= argc)
433         usage(1);
434 
435       PAMService = argv[i];
436     }
437     else if (!strcmp(argv[i], "--version"))
438     {
439       puts(CUPS_SVERSION);
440       return (0);
441     }
442     else if (!strncmp(argv[i], "--", 2))
443     {
444       _cupsLangPrintf(stderr, _("%s: Unknown option \"%s\"."), argv[0], argv[i]);
445       usage(1);
446     }
447     else if (argv[i][0] == '-')
448     {
449       for (opt = argv[i] + 1; *opt; opt ++)
450       {
451         switch (*opt)
452 	{
453 	  case '2' : /* -2 (enable 2-sided printing) */
454 	      duplex = 1;
455 	      legacy = 1;
456 	      break;
457 
458           case 'A' : /* -A (enable authentication) */
459               if (!PAMService)
460                 PAMService = "cups";
461 	      break;
462 
463           case 'D' : /* -D device-uri */
464 	      i ++;
465 	      if (i >= argc)
466 	        usage(1);
467 
468 	      device_uri = argv[i];
469 	      break;
470 
471           case 'F' : /* -F output/format */
472 	      i ++;
473 	      if (i >= argc)
474 	        usage(1);
475 
476 	      output_format = argv[i];
477 	      break;
478 
479 #ifdef HAVE_TLS
480 	  case 'K' : /* -K keypath */
481 	      i ++;
482 	      if (i >= argc)
483 	        usage(1);
484 
485 	      keypath = argv[i];
486 	      break;
487 #endif /* HAVE_TLS */
488 
489 	  case 'M' : /* -M manufacturer */
490 	      i ++;
491 	      if (i >= argc)
492 	        usage(1);
493 
494 	      make   = argv[i];
495 	      legacy = 1;
496 	      break;
497 
498 #if !CUPS_LITE
499           case 'P' : /* -P filename.ppd */
500 	      i ++;
501 	      if (i >= argc)
502 	        usage(1);
503 
504               ppdfile = argv[i];
505               break;
506 #endif /* !CUPS_LITE */
507 
508 	  case 'S' : /* -S filename.strings */
509 	      i ++;
510 	      if (i >= argc)
511 	        usage(1);
512 
513 	      strings = argv[i];
514 	      break;
515 
516           case 'V' : /* -V max-version */
517 	      i ++;
518 	      if (i >= argc)
519 	        usage(1);
520 
521 	      if (!strcmp(argv[i], "2.0"))
522                 MaxVersion = 20;
523 	      else if (!strcmp(argv[i], "1.1"))
524                 MaxVersion = 11;
525 	      else
526 	        usage(1);
527               break;
528 
529 	  case 'a' : /* -a attributes-file */
530 	      i ++;
531 	      if (i >= argc)
532 	        usage(1);
533 
534 	      attrfile = argv[i];
535 	      break;
536 
537           case 'c' : /* -c command */
538               i ++;
539 	      if (i >= argc)
540 	        usage(1);
541 
542 	      command = argv[i];
543 	      break;
544 
545 	  case 'd' : /* -d spool-directory */
546 	      i ++;
547 	      if (i >= argc)
548 	        usage(1);
549 
550 	      strlcpy(directory, argv[i], sizeof(directory));
551 	      break;
552 
553 	  case 'f' : /* -f type/subtype[,...] */
554 	      i ++;
555 	      if (i >= argc)
556 	        usage(1);
557 
558 	      docformats = _cupsArrayNewStrings(argv[i], ',');
559 	      legacy     = 1;
560 	      break;
561 
562 	  case 'i' : /* -i icon.png */
563 	      i ++;
564 	      if (i >= argc)
565 	        usage(1);
566 
567 	      icon = argv[i];
568 	      break;
569 
570 	  case 'k' : /* -k (keep files) */
571 	      KeepFiles = 1;
572 	      break;
573 
574 	  case 'l' : /* -l location */
575 	      i ++;
576 	      if (i >= argc)
577 	        usage(1);
578 
579 	      location = argv[i];
580 	      break;
581 
582 	  case 'm' : /* -m model */
583 	      i ++;
584 	      if (i >= argc)
585 	        usage(1);
586 
587 	      model  = argv[i];
588 	      legacy = 1;
589 	      break;
590 
591 	  case 'n' : /* -n hostname */
592 	      i ++;
593 	      if (i >= argc)
594 	        usage(1);
595 
596 	      servername = argv[i];
597 	      break;
598 
599 	  case 'p' : /* -p port */
600 	      i ++;
601 	      if (i >= argc || !isdigit(argv[i][0] & 255))
602 	        usage(1);
603 
604 	      serverport = atoi(argv[i]);
605 	      break;
606 
607 	  case 'r' : /* -r subtype */
608 	      i ++;
609 	      if (i >= argc)
610 	        usage(1);
611 
612 	      subtypes = argv[i];
613 	      break;
614 
615 	  case 's' : /* -s speed[,color-speed] */
616 	      i ++;
617 	      if (i >= argc)
618 	        usage(1);
619 
620 	      if (sscanf(argv[i], "%d,%d", &ppm, &ppm_color) < 1)
621 	        usage(1);
622 
623 	      legacy = 1;
624 	      break;
625 
626 	  case 'v' : /* -v (be verbose) */
627 	      Verbosity ++;
628 	      break;
629 
630           default : /* Unknown */
631 	      _cupsLangPrintf(stderr, _("%s: Unknown option \"-%c\"."), argv[0], *opt);
632 	      usage(1);
633 	}
634       }
635     }
636     else if (!name)
637     {
638       name = argv[i];
639     }
640     else
641     {
642       _cupsLangPrintf(stderr, _("%s: Unknown option \"%s\"."), argv[0], argv[i]);
643       usage(1);
644     }
645   }
646 
647   if (!name)
648     usage(1);
649 
650 #if CUPS_LITE
651   if (attrfile != NULL && legacy)
652     usage(1);
653 #else
654   if (((ppdfile != NULL) + (attrfile != NULL) + legacy) > 1)
655     usage(1);
656 #endif /* CUPS_LITE */
657 
658  /*
659   * Apply defaults as needed...
660   */
661 
662   if (!directory[0])
663   {
664     const char *tmpdir;			/* Temporary directory */
665 
666 #ifdef _WIN32
667     if ((tmpdir = getenv("TEMP")) == NULL)
668       tmpdir = "C:/TEMP";
669 #elif defined(__APPLE__) && TARGET_OS_OSX
670     if ((tmpdir = getenv("TMPDIR")) == NULL)
671       tmpdir = "/private/tmp";
672 #else
673     if ((tmpdir = getenv("TMPDIR")) == NULL)
674       tmpdir = "/tmp";
675 #endif /* _WIN32 */
676 
677     snprintf(directory, sizeof(directory), "%s/ippeveprinter.%d", tmpdir, (int)getpid());
678 
679     if (mkdir(directory, 0755) && errno != EEXIST)
680     {
681       _cupsLangPrintf(stderr, _("Unable to create spool directory \"%s\": %s"), directory, strerror(errno));
682       usage(1);
683     }
684 
685     if (Verbosity)
686       _cupsLangPrintf(stderr, _("Using spool directory \"%s\"."), directory);
687   }
688 
689  /*
690   * Initialize DNS-SD...
691   */
692 
693   dnssd_init();
694 
695  /*
696   * Create the printer...
697   */
698 
699   if (!docformats)
700     docformats = _cupsArrayNewStrings(ppm_color > 0 ? "image/jpeg,image/pwg-raster,image/urf": "image/pwg-raster,image/urf", ',');
701 
702   if (attrfile)
703     attrs = load_ippserver_attributes(servername, serverport, attrfile, docformats);
704 #if !CUPS_LITE
705   else if (ppdfile)
706   {
707     attrs = load_ppd_attributes(ppdfile, docformats);
708 
709     if (!command)
710       command = "ippeveps";
711 
712     if (!output_format)
713       output_format = "application/postscript";
714   }
715 #endif /* !CUPS_LITE */
716   else
717     attrs = load_legacy_attributes(make, model, ppm, ppm_color, duplex, docformats);
718 
719   if ((printer = create_printer(servername, serverport, name, location, icon, strings, docformats, subtypes, directory, command, device_uri, output_format, attrs)) == NULL)
720     return (1);
721 
722   printer->web_forms = web_forms;
723 
724 #if !CUPS_LITE
725   if (ppdfile)
726     printer->ppdfile = strdup(ppdfile);
727 #endif /* !CUPS_LITE */
728 
729 #ifdef HAVE_TLS
730   cupsSetServerCredentials(keypath, printer->hostname, 1);
731 #endif /* HAVE_TLS */
732 
733  /*
734   * Run the print service...
735   */
736 
737   run_printer(printer);
738 
739  /*
740   * Destroy the printer and exit...
741   */
742 
743   delete_printer(printer);
744 
745   return (0);
746 }
747 
748 
749 /*
750  * 'authenticate_request()' - Try to authenticate the request.
751  */
752 
753 static http_status_t			/* O - HTTP_STATUS_CONTINUE to keep going, otherwise status to return */
authenticate_request(ippeve_client_t * client)754 authenticate_request(
755     ippeve_client_t *client)		/* I - Client */
756 {
757 #if HAVE_LIBPAM
758  /*
759   * If PAM isn't enabled, return 'continue' now...
760   */
761 
762   const char		*authorization;	/* Pointer into Authorization string */
763   int			userlen;	/* Username:password length */
764   pam_handle_t		*pamh;		/* PAM authentication handle */
765   int			pamerr;		/* PAM error code */
766   struct pam_conv	pamdata;	/* PAM conversation data */
767   ippeve_authdata_t	data;		/* Authentication data */
768 
769 
770   if (!PAMService)
771     return (HTTP_STATUS_CONTINUE);
772 
773  /*
774   * Try authenticating using PAM...
775   */
776 
777   authorization = httpGetField(client->http, HTTP_FIELD_AUTHORIZATION);
778 
779   if (!*authorization)
780     return (HTTP_STATUS_UNAUTHORIZED);
781 
782   if (strncmp(authorization, "Basic ", 6))
783   {
784     fputs("Unsupported scheme in Authorization header.\n", stderr);
785     return (HTTP_STATUS_BAD_REQUEST);
786   }
787 
788   authorization += 5;
789   while (isspace(*authorization & 255))
790     authorization ++;
791 
792   userlen = sizeof(data.username);
793   httpDecode64_2(data.username, &userlen, authorization);
794 
795   if ((data.password = strchr(data.username, ':')) == NULL)
796   {
797     fputs("No password in Authorization header.\n", stderr);
798     return (HTTP_STATUS_BAD_REQUEST);
799   }
800 
801   *(data.password)++ = '\0';
802 
803   if (!data.username[0])
804   {
805     fputs("No username in Authorization header.\n", stderr);
806     return (HTTP_STATUS_BAD_REQUEST);
807   }
808 
809   pamdata.conv        = pam_func;
810   pamdata.appdata_ptr = &data;
811 
812   if ((pamerr = pam_start(PAMService, data.username, &pamdata, &pamh)) != PAM_SUCCESS)
813   {
814     fprintf(stderr, "pam_start() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
815     return (HTTP_STATUS_SERVER_ERROR);
816   }
817 
818   if ((pamerr = pam_authenticate(pamh, PAM_SILENT)) != PAM_SUCCESS)
819   {
820     fprintf(stderr, "pam_authenticate() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
821     pam_end(pamh, 0);
822     return (HTTP_STATUS_UNAUTHORIZED);
823   }
824 
825   if ((pamerr = pam_acct_mgmt(pamh, PAM_SILENT)) != PAM_SUCCESS)
826   {
827     fprintf(stderr, "pam_acct_mgmt() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
828     pam_end(pamh, 0);
829     return (HTTP_STATUS_SERVER_ERROR);
830   }
831 
832   strlcpy(client->username, data.username, sizeof(client->username));
833 
834   pam_end(pamh, PAM_SUCCESS);
835 
836   return (HTTP_STATUS_CONTINUE);
837 
838 #else
839  /*
840   * No authentication support built-in, return 'continue'...
841   */
842 
843   return (HTTP_STATUS_CONTINUE);
844 #endif /* HAVE_LIBPAM */
845 }
846 
847 
848 /*
849  * 'clean_jobs()' - Clean out old (completed) jobs.
850  */
851 
852 static void
clean_jobs(ippeve_printer_t * printer)853 clean_jobs(ippeve_printer_t *printer)	/* I - Printer */
854 {
855   ippeve_job_t	*job;			/* Current job */
856   time_t	cleantime;		/* Clean time */
857 
858 
859   if (cupsArrayCount(printer->jobs) == 0)
860     return;
861 
862   cleantime = time(NULL) - 60;
863 
864   _cupsRWLockWrite(&(printer->rwlock));
865   for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs);
866        job;
867        job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
868     if (job->completed && job->completed < cleantime)
869     {
870       cupsArrayRemove(printer->jobs, job);
871       delete_job(job);
872     }
873     else
874       break;
875   _cupsRWUnlock(&(printer->rwlock));
876 }
877 
878 
879 /*
880  * 'compare_jobs()' - Compare two jobs.
881  */
882 
883 static int				/* O - Result of comparison */
compare_jobs(ippeve_job_t * a,ippeve_job_t * b)884 compare_jobs(ippeve_job_t *a,		/* I - First job */
885              ippeve_job_t *b)		/* I - Second job */
886 {
887   return (b->id - a->id);
888 }
889 
890 
891 /*
892  * 'copy_attributes()' - Copy attributes from one request to another.
893  */
894 
895 static void
copy_attributes(ipp_t * to,ipp_t * from,cups_array_t * ra,ipp_tag_t group_tag,int quickcopy)896 copy_attributes(ipp_t        *to,	/* I - Destination request */
897 	        ipp_t        *from,	/* I - Source request */
898 	        cups_array_t *ra,	/* I - Requested attributes */
899 	        ipp_tag_t    group_tag,	/* I - Group to copy */
900 	        int          quickcopy)	/* I - Do a quick copy? */
901 {
902   ippeve_filter_t	filter;			/* Filter data */
903 
904 
905   filter.ra        = ra;
906   filter.group_tag = group_tag;
907 
908   ippCopyAttributes(to, from, quickcopy, (ipp_copycb_t)filter_cb, &filter);
909 }
910 
911 
912 /*
913  * 'copy_job_attrs()' - Copy job attributes to the response.
914  */
915 
916 static void
copy_job_attributes(ippeve_client_t * client,ippeve_job_t * job,cups_array_t * ra)917 copy_job_attributes(
918     ippeve_client_t *client,		/* I - Client */
919     ippeve_job_t    *job,			/* I - Job */
920     cups_array_t  *ra)			/* I - requested-attributes */
921 {
922   copy_attributes(client->response, job->attrs, ra, IPP_TAG_JOB, 0);
923 
924   if (!ra || cupsArrayFind(ra, "date-time-at-completed"))
925   {
926     if (job->completed)
927       ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-completed", ippTimeToDate(job->completed));
928     else
929       ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-completed");
930   }
931 
932   if (!ra || cupsArrayFind(ra, "date-time-at-processing"))
933   {
934     if (job->processing)
935       ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-processing", ippTimeToDate(job->processing));
936     else
937       ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-processing");
938   }
939 
940   if (!ra || cupsArrayFind(ra, "job-impressions"))
941     ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions", job->impressions);
942 
943   if (!ra || cupsArrayFind(ra, "job-impressions-completed"))
944     ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions-completed", job->impcompleted);
945 
946   if (!ra || cupsArrayFind(ra, "job-printer-up-time"))
947     ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-printer-up-time", (int)(time(NULL) - client->printer->start_time));
948 
949   if (!ra || cupsArrayFind(ra, "job-state"))
950     ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state);
951 
952   if (!ra || cupsArrayFind(ra, "job-state-message"))
953   {
954     if (job->message)
955     {
956       ippAddString(client->response, IPP_TAG_JOB, IPP_TAG_TEXT, "job-state-message", NULL, job->message);
957     }
958     else
959     {
960       switch (job->state)
961       {
962 	case IPP_JSTATE_PENDING :
963 	    ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job pending.");
964 	    break;
965 
966 	case IPP_JSTATE_HELD :
967 	    if (job->fd >= 0)
968 	      ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job incoming.");
969 	    else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
970 	      ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job held.");
971 	    else
972 	      ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job created.");
973 	    break;
974 
975 	case IPP_JSTATE_PROCESSING :
976 	    if (job->cancel)
977 	      ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceling.");
978 	    else
979 	      ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job printing.");
980 	    break;
981 
982 	case IPP_JSTATE_STOPPED :
983 	    ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job stopped.");
984 	    break;
985 
986 	case IPP_JSTATE_CANCELED :
987 	    ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceled.");
988 	    break;
989 
990 	case IPP_JSTATE_ABORTED :
991 	    ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job aborted.");
992 	    break;
993 
994 	case IPP_JSTATE_COMPLETED :
995 	    ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job completed.");
996 	    break;
997       }
998     }
999   }
1000 
1001   if (!ra || cupsArrayFind(ra, "job-state-reasons"))
1002   {
1003     switch (job->state)
1004     {
1005       case IPP_JSTATE_PENDING :
1006 	  ippAddString(client->response, IPP_TAG_JOB,
1007 	               IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1008 		       NULL, "none");
1009 	  break;
1010 
1011       case IPP_JSTATE_HELD :
1012           if (job->fd >= 0)
1013 	    ippAddString(client->response, IPP_TAG_JOB,
1014 	                 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1015 	                 "job-state-reasons", NULL, "job-incoming");
1016 	  else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
1017 	    ippAddString(client->response, IPP_TAG_JOB,
1018 	                 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1019 	                 "job-state-reasons", NULL, "job-hold-until-specified");
1020           else
1021 	    ippAddString(client->response, IPP_TAG_JOB,
1022 	                 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1023 	                 "job-state-reasons", NULL, "job-data-insufficient");
1024 	  break;
1025 
1026       case IPP_JSTATE_PROCESSING :
1027 	  if (job->cancel)
1028 	    ippAddString(client->response, IPP_TAG_JOB,
1029 	                 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1030 	                 "job-state-reasons", NULL, "processing-to-stop-point");
1031 	  else
1032 	    ippAddString(client->response, IPP_TAG_JOB,
1033 	                 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1034 	                 "job-state-reasons", NULL, "job-printing");
1035 	  break;
1036 
1037       case IPP_JSTATE_STOPPED :
1038 	  ippAddString(client->response, IPP_TAG_JOB,
1039 	               IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1040 		       NULL, "job-stopped");
1041 	  break;
1042 
1043       case IPP_JSTATE_CANCELED :
1044 	  ippAddString(client->response, IPP_TAG_JOB,
1045 	               IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1046 		       NULL, "job-canceled-by-user");
1047 	  break;
1048 
1049       case IPP_JSTATE_ABORTED :
1050 	  ippAddString(client->response, IPP_TAG_JOB,
1051 	               IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1052 		       NULL, "aborted-by-system");
1053 	  break;
1054 
1055       case IPP_JSTATE_COMPLETED :
1056 	  ippAddString(client->response, IPP_TAG_JOB,
1057 	               IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1058 		       NULL, "job-completed-successfully");
1059 	  break;
1060     }
1061   }
1062 
1063   if (!ra || cupsArrayFind(ra, "time-at-completed"))
1064     ippAddInteger(client->response, IPP_TAG_JOB,
1065                   job->completed ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
1066                   "time-at-completed", (int)(job->completed - client->printer->start_time));
1067 
1068   if (!ra || cupsArrayFind(ra, "time-at-processing"))
1069     ippAddInteger(client->response, IPP_TAG_JOB,
1070                   job->processing ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
1071                   "time-at-processing", (int)(job->processing - client->printer->start_time));
1072 }
1073 
1074 
1075 /*
1076  * 'create_client()' - Accept a new network connection and create a client
1077  *                     object.
1078  */
1079 
1080 static ippeve_client_t *			/* O - Client */
create_client(ippeve_printer_t * printer,int sock)1081 create_client(ippeve_printer_t *printer,	/* I - Printer */
1082               int            sock)	/* I - Listen socket */
1083 {
1084   ippeve_client_t	*client;		/* Client */
1085 
1086 
1087   if ((client = calloc(1, sizeof(ippeve_client_t))) == NULL)
1088   {
1089     perror("Unable to allocate memory for client");
1090     return (NULL);
1091   }
1092 
1093   client->printer = printer;
1094 
1095  /*
1096   * Accept the client and get the remote address...
1097   */
1098 
1099   if ((client->http = httpAcceptConnection(sock, 1)) == NULL)
1100   {
1101     perror("Unable to accept client connection");
1102 
1103     free(client);
1104 
1105     return (NULL);
1106   }
1107 
1108   httpGetHostname(client->http, client->hostname, sizeof(client->hostname));
1109 
1110   if (Verbosity)
1111     fprintf(stderr, "Accepted connection from %s\n", client->hostname);
1112 
1113   return (client);
1114 }
1115 
1116 
1117 /*
1118  * 'create_job()' - Create a new job object from a Print-Job or Create-Job
1119  *                  request.
1120  */
1121 
1122 static ippeve_job_t *			/* O - Job */
create_job(ippeve_client_t * client)1123 create_job(ippeve_client_t *client)	/* I - Client */
1124 {
1125   ippeve_job_t		*job;		/* Job */
1126   ipp_attribute_t	*attr;		/* Job attribute */
1127   char			uri[1024],	/* job-uri value */
1128 			uuid[64];	/* job-uuid value */
1129 
1130 
1131   _cupsRWLockWrite(&(client->printer->rwlock));
1132   if (client->printer->active_job &&
1133       client->printer->active_job->state < IPP_JSTATE_CANCELED)
1134   {
1135    /*
1136     * Only accept a single job at a time...
1137     */
1138 
1139     _cupsRWUnlock(&(client->printer->rwlock));
1140     return (NULL);
1141   }
1142 
1143  /*
1144   * Allocate and initialize the job object...
1145   */
1146 
1147   if ((job = calloc(1, sizeof(ippeve_job_t))) == NULL)
1148   {
1149     perror("Unable to allocate memory for job");
1150     _cupsRWUnlock(&(client->printer->rwlock));
1151     return (NULL);
1152   }
1153 
1154   job->printer    = client->printer;
1155   job->attrs      = ippNew();
1156   job->state      = IPP_JSTATE_HELD;
1157   job->fd         = -1;
1158 
1159  /*
1160   * Copy all of the job attributes...
1161   */
1162 
1163   copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
1164 
1165  /*
1166   * Get the requesting-user-name, document format, and priority...
1167   */
1168 
1169   if ((attr = ippFindAttribute(client->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
1170     job->username = ippGetString(attr, 0, NULL);
1171   else
1172     job->username = "anonymous";
1173 
1174   ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name", NULL, job->username);
1175 
1176   if (ippGetOperation(client->request) != IPP_OP_CREATE_JOB)
1177   {
1178     if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
1179       job->format = ippGetString(attr, 0, NULL);
1180     else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
1181       job->format = ippGetString(attr, 0, NULL);
1182     else
1183       job->format = "application/octet-stream";
1184   }
1185 
1186   if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_INTEGER)) != NULL)
1187     job->impressions = ippGetInteger(attr, 0);
1188 
1189   if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_NAME)) != NULL)
1190     job->name = ippGetString(attr, 0, NULL);
1191 
1192  /*
1193   * Add job description attributes and add to the jobs array...
1194   */
1195 
1196   job->id = client->printer->next_job_id ++;
1197 
1198   if ((attr = ippFindAttribute(client->request, "printer-uri", IPP_TAG_URI)) != NULL)
1199     snprintf(uri, sizeof(uri), "%s/%d", ippGetString(attr, 0, NULL), job->id);
1200   else
1201     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, client->printer->hostname, client->printer->port, "/ipp/print/%d", job->id);
1202 
1203   httpAssembleUUID(client->printer->hostname, client->printer->port, client->printer->name, job->id, uuid, sizeof(uuid));
1204 
1205   ippAddDate(job->attrs, IPP_TAG_JOB, "date-time-at-creation", ippTimeToDate(time(&job->created)));
1206   ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1207   ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, uri);
1208   ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL, uuid);
1209   if ((attr = ippFindAttribute(client->request, "printer-uri", IPP_TAG_URI)) != NULL)
1210   {
1211     ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, ippGetString(attr, 0, NULL));
1212   }
1213   else
1214   {
1215     char printer_uri[1024];		/* job-printer-uri value */
1216 
1217     httpAssembleURI(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp", NULL, client->printer->hostname, client->printer->port, "/ipp/print");
1218     ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, printer_uri);
1219   }
1220 
1221   ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", (int)(job->created - client->printer->start_time));
1222 
1223   cupsArrayAdd(client->printer->jobs, job);
1224   client->printer->active_job = job;
1225 
1226   _cupsRWUnlock(&(client->printer->rwlock));
1227 
1228   return (job);
1229 }
1230 
1231 
1232 /*
1233  * 'create_job_file()' - Create a file for the document in a job.
1234  */
1235 
1236 static int				/* O - File descriptor or -1 on error */
create_job_file(ippeve_job_t * job,char * fname,size_t fnamesize,const char * directory,const char * ext)1237 create_job_file(
1238     ippeve_job_t     *job,		/* I - Job */
1239     char             *fname,		/* I - Filename buffer */
1240     size_t           fnamesize,		/* I - Size of filename buffer */
1241     const char       *directory,	/* I - Directory to store in */
1242     const char       *ext)		/* I - Extension (`NULL` for default) */
1243 {
1244   char			name[256],	/* "Safe" filename */
1245 			*nameptr;	/* Pointer into filename */
1246   const char		*job_name;	/* job-name value */
1247 
1248 
1249  /*
1250   * Make a name from the job-name attribute...
1251   */
1252 
1253   if ((job_name = ippGetString(ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME), 0, NULL)) == NULL)
1254     job_name = "untitled";
1255 
1256   for (nameptr = name; *job_name && nameptr < (name + sizeof(name) - 1); job_name ++)
1257   {
1258     if (isalnum(*job_name & 255) || *job_name == '-')
1259     {
1260       *nameptr++ = (char)tolower(*job_name & 255);
1261     }
1262     else
1263     {
1264       *nameptr++ = '_';
1265 
1266       while (job_name[1] && !isalnum(job_name[1] & 255) && job_name[1] != '-')
1267         job_name ++;
1268     }
1269   }
1270 
1271   *nameptr = '\0';
1272 
1273  /*
1274   * Figure out the extension...
1275   */
1276 
1277   if (!ext)
1278   {
1279     if (!strcasecmp(job->format, "image/jpeg"))
1280       ext = "jpg";
1281     else if (!strcasecmp(job->format, "image/png"))
1282       ext = "png";
1283     else if (!strcasecmp(job->format, "image/pwg-raster"))
1284       ext = "pwg";
1285     else if (!strcasecmp(job->format, "image/urf"))
1286       ext = "urf";
1287     else if (!strcasecmp(job->format, "application/pdf"))
1288       ext = "pdf";
1289     else if (!strcasecmp(job->format, "application/postscript"))
1290       ext = "ps";
1291     else if (!strcasecmp(job->format, "application/vnd.hp-pcl"))
1292       ext = "pcl";
1293     else
1294       ext = "dat";
1295   }
1296 
1297  /*
1298   * Create a filename with the job-id, job-name, and document-format (extension)...
1299   */
1300 
1301   snprintf(fname, fnamesize, "%s/%d-%s.%s", directory, job->id, name, ext);
1302 
1303   return (open(fname, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666));
1304 }
1305 
1306 
1307 /*
1308  * 'create_listener()' - Create a listener socket.
1309  */
1310 
1311 static int				/* O - Listener socket or -1 on error */
create_listener(const char * name,int port,int family)1312 create_listener(const char *name,	/* I - Host name (`NULL` for any address) */
1313                 int        port,	/* I - Port number */
1314                 int        family)	/* I - Address family */
1315 {
1316   int			sock;		/* Listener socket */
1317   http_addrlist_t	*addrlist;	/* Listen address */
1318   char			service[255];	/* Service port */
1319 
1320 
1321   snprintf(service, sizeof(service), "%d", port);
1322   if ((addrlist = httpAddrGetList(name, family, service)) == NULL)
1323     return (-1);
1324 
1325   sock = httpAddrListen(&(addrlist->addr), port);
1326 
1327   httpAddrFreeList(addrlist);
1328 
1329   return (sock);
1330 }
1331 
1332 
1333 /*
1334  * 'create_media_col()' - Create a media-col value.
1335  */
1336 
1337 static ipp_t *				/* O - media-col collection */
create_media_col(const char * media,const char * source,const char * type,int width,int length,int bottom,int left,int right,int top)1338 create_media_col(const char *media,	/* I - Media name */
1339 		 const char *source,	/* I - Media source, if any */
1340 		 const char *type,	/* I - Media type, if any */
1341 		 int        width,	/* I - x-dimension in 2540ths */
1342 		 int        length,	/* I - y-dimension in 2540ths */
1343 		 int        bottom,	/* I - Bottom margin in 2540ths */
1344 		 int        left,	/* I - Left margin in 2540ths */
1345 		 int        right,	/* I - Right margin in 2540ths */
1346 		 int        top)	/* I - Top margin in 2540ths */
1347 {
1348   ipp_t		*media_col = ippNew(),	/* media-col value */
1349 		*media_size = create_media_size(width, length);
1350 					/* media-size value */
1351   char		media_key[256];		/* media-key value */
1352   const char	*media_key_suffix = "";	/* media-key suffix */
1353 
1354 
1355   if (bottom == 0 && left == 0 && right == 0 && top == 0)
1356     media_key_suffix = "_borderless";
1357 
1358   if (type && source)
1359     snprintf(media_key, sizeof(media_key), "%s_%s_%s%s", media, source, type, media_key_suffix);
1360   else if (type)
1361     snprintf(media_key, sizeof(media_key), "%s__%s%s", media, type, media_key_suffix);
1362   else if (source)
1363     snprintf(media_key, sizeof(media_key), "%s_%s%s", media, source, media_key_suffix);
1364   else
1365     snprintf(media_key, sizeof(media_key), "%s%s", media, media_key_suffix);
1366 
1367   ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-key", NULL, media_key);
1368   ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
1369   ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-size-name", NULL, media);
1370   if (bottom >= 0)
1371     ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin", bottom);
1372   if (left >= 0)
1373     ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin", left);
1374   if (right >= 0)
1375     ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin", right);
1376   if (top >= 0)
1377     ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin", top);
1378   if (source)
1379     ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source", NULL, source);
1380   if (type)
1381     ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type", NULL, type);
1382 
1383   ippDelete(media_size);
1384 
1385   return (media_col);
1386 }
1387 
1388 
1389 /*
1390  * 'create_media_size()' - Create a media-size value.
1391  */
1392 
1393 static ipp_t *				/* O - media-col collection */
create_media_size(int width,int length)1394 create_media_size(int width,		/* I - x-dimension in 2540ths */
1395 		  int length)		/* I - y-dimension in 2540ths */
1396 {
1397   ipp_t	*media_size = ippNew();		/* media-size value */
1398 
1399 
1400   ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension", width);
1401   ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension", length);
1402 
1403   return (media_size);
1404 }
1405 
1406 
1407 /*
1408  * 'create_printer()' - Create, register, and listen for connections to a
1409  *                      printer object.
1410  */
1411 
1412 static ippeve_printer_t *		/* O - Printer */
create_printer(const char * servername,int serverport,const char * name,const char * location,const char * icons,const char * strings,cups_array_t * docformats,const char * subtypes,const char * directory,const char * command,const char * device_uri,const char * output_format,ipp_t * attrs)1413 create_printer(
1414     const char   *servername,		/* I - Server hostname (NULL for default) */
1415     int          serverport,		/* I - Server port */
1416     const char   *name,			/* I - printer-name */
1417     const char   *location,		/* I - printer-location */
1418     const char   *icons,		/* I - printer-icons */
1419     const char   *strings,		/* I - printer-strings-uri */
1420     cups_array_t *docformats,		/* I - document-format-supported */
1421     const char   *subtypes,		/* I - DNS-SD service subtype(s) */
1422     const char   *directory,		/* I - Spool directory */
1423     const char   *command,		/* I - Command to run on job files, if any */
1424     const char   *device_uri,		/* I - Output device, if any */
1425     const char   *output_format,	/* I - Output format, if any */
1426     ipp_t        *attrs)		/* I - Capability attributes */
1427 {
1428   ippeve_printer_t	*printer;	/* Printer */
1429   int			i;		/* Looping var */
1430 #ifndef _WIN32
1431   char			path[1024];	/* Full path to command */
1432 #endif /* !_WIN32 */
1433   unsigned char		sha256[32];	/* SHA-256 digest/sum */
1434   char			uuid_data[1024],/* Data to hash for printer-uuid */
1435 			uuid[128],	/* printer-uuid */
1436 			*iconsptr;	/* Pointer into icons string */
1437   int			k_supported;	/* Maximum file size supported */
1438   int			num_formats;	/* Number of supported document formats */
1439   const char		*formats[100],	/* Supported document formats */
1440 			*format;	/* Current format */
1441   int			num_sup_attrs;	/* Number of supported attributes */
1442   const char		*sup_attrs[100];/* Supported attributes */
1443   char			xxx_supported[256];
1444 					/* Name of -supported attribute */
1445   _cups_globals_t	*cg = _cupsGlobals();
1446 					/* Global path values */
1447 #ifdef HAVE_STATVFS
1448   struct statvfs	spoolinfo;	/* FS info for spool directory */
1449   double		spoolsize;	/* FS size */
1450 #elif defined(HAVE_STATFS)
1451   struct statfs		spoolinfo;	/* FS info for spool directory */
1452   double		spoolsize;	/* FS size */
1453 #endif /* HAVE_STATVFS */
1454   static const char * const versions[] =/* ipp-versions-supported values */
1455   {
1456     "1.1",
1457     "2.0"
1458   };
1459   static const char * const features[] =/* ipp-features-supported values */
1460   {
1461     "ipp-everywhere"
1462   };
1463   static const int	ops[] =		/* operations-supported values */
1464   {
1465     IPP_OP_PRINT_JOB,
1466     IPP_OP_PRINT_URI,
1467     IPP_OP_VALIDATE_JOB,
1468     IPP_OP_CREATE_JOB,
1469     IPP_OP_SEND_DOCUMENT,
1470     IPP_OP_SEND_URI,
1471     IPP_OP_CANCEL_JOB,
1472     IPP_OP_GET_JOB_ATTRIBUTES,
1473     IPP_OP_GET_JOBS,
1474     IPP_OP_GET_PRINTER_ATTRIBUTES,
1475     IPP_OP_CANCEL_MY_JOBS,
1476     IPP_OP_CLOSE_JOB,
1477     IPP_OP_IDENTIFY_PRINTER
1478   };
1479   static const char * const charsets[] =/* charset-supported values */
1480   {
1481     "us-ascii",
1482     "utf-8"
1483   };
1484   static const char * const compressions[] =/* compression-supported values */
1485   {
1486 #ifdef HAVE_LIBZ
1487     "deflate",
1488     "gzip",
1489 #endif /* HAVE_LIBZ */
1490     "none"
1491   };
1492   static const char * const identify_actions[] =
1493   {
1494     "display",
1495     "sound"
1496   };
1497   static const char * const job_creation[] =
1498   {					/* job-creation-attributes-supported values */
1499     "copies",
1500     "document-access",
1501     "document-charset",
1502     "document-format",
1503     "document-message",
1504     "document-metadata",
1505     "document-name",
1506     "document-natural-language",
1507     "document-password",
1508     "finishings",
1509     "finishings-col",
1510     "ipp-attribute-fidelity",
1511     "job-account-id",
1512     "job-account-type",
1513     "job-accouunting-sheets",
1514     "job-accounting-user-id",
1515     "job-authorization-uri",
1516     "job-error-action",
1517     "job-error-sheet",
1518     "job-hold-until",
1519     "job-hold-until-time",
1520     "job-mandatory-attributes",
1521     "job-message-to-operator",
1522     "job-name",
1523     "job-pages-per-set",
1524     "job-password",
1525     "job-password-encryption",
1526     "job-phone-number",
1527     "job-priority",
1528     "job-recipient-name",
1529     "job-resource-ids",
1530     "job-sheet-message",
1531     "job-sheets",
1532     "job-sheets-col",
1533     "media",
1534     "media-col",
1535     "multiple-document-handling",
1536     "number-up",
1537     "orientation-requested",
1538     "output-bin",
1539     "output-device",
1540     "overrides",
1541     "page-delivery",
1542     "page-ranges",
1543     "presentation-direction-number-up",
1544     "print-color-mode",
1545     "print-content-optimize",
1546     "print-quality",
1547     "print-rendering-intent",
1548     "print-scaling",
1549     "printer-resolution",
1550     "proof-print",
1551     "separator-sheets",
1552     "sides",
1553     "x-image-position",
1554     "x-image-shift",
1555     "x-side1-image-shift",
1556     "x-side2-image-shift",
1557     "y-image-position",
1558     "y-image-shift",
1559     "y-side1-image-shift",
1560     "y-side2-image-shift"
1561   };
1562   static const char * const media_col_supported[] =
1563   {					/* media-col-supported values */
1564     "media-bottom-margin",
1565     "media-left-margin",
1566     "media-right-margin",
1567     "media-size",
1568     "media-size-name",
1569     "media-source",
1570     "media-top-margin",
1571     "media-type"
1572   };
1573   static const char * const multiple_document_handling[] =
1574   {					/* multiple-document-handling-supported values */
1575     "separate-documents-uncollated-copies",
1576     "separate-documents-collated-copies"
1577   };
1578   static const char * const reference_uri_schemes_supported[] =
1579   {					/* reference-uri-schemes-supported */
1580     "file",
1581     "ftp",
1582     "http"
1583 #ifdef HAVE_TLS
1584     , "https"
1585 #endif /* HAVE_TLS */
1586   };
1587 #ifdef HAVE_TLS
1588   static const char * const uri_authentication_supported[] =
1589   {					/* uri-authentication-supported values */
1590     "none",
1591     "none"
1592   };
1593   static const char * const uri_authentication_basic[] =
1594   {					/* uri-authentication-supported values with authentication */
1595     "basic",
1596     "basic"
1597   };
1598   static const char * const uri_security_supported[] =
1599   {					/* uri-security-supported values */
1600     "none",
1601     "tls"
1602   };
1603 #endif /* HAVE_TLS */
1604   static const char * const which_jobs[] =
1605   {					/* which-jobs-supported values */
1606     "completed",
1607     "not-completed",
1608     "aborted",
1609     "all",
1610     "canceled",
1611     "pending",
1612     "pending-held",
1613     "processing",
1614     "processing-stopped"
1615   };
1616 
1617 
1618 #ifndef _WIN32
1619  /*
1620   * If a command was specified, make sure it exists and is executable...
1621   */
1622 
1623   if (command)
1624   {
1625     if (*command == '/' || !strncmp(command, "./", 2))
1626     {
1627       if (access(command, X_OK))
1628       {
1629         _cupsLangPrintf(stderr, _("Unable to execute command \"%s\": %s"), command, strerror(errno));
1630 	return (NULL);
1631       }
1632     }
1633     else
1634     {
1635       snprintf(path, sizeof(path), "%s/command/%s", cg->cups_serverbin, command);
1636 
1637       if (access(path, X_OK))
1638       {
1639         _cupsLangPrintf(stderr, _("Unable to execute command \"%s\": %s"), path, strerror(errno));
1640 	return (NULL);
1641       }
1642 
1643       command = path;
1644     }
1645   }
1646 #endif /* !_WIN32 */
1647 
1648  /*
1649   * Allocate memory for the printer...
1650   */
1651 
1652   if ((printer = calloc(1, sizeof(ippeve_printer_t))) == NULL)
1653   {
1654     _cupsLangPrintError(NULL, _("Unable to allocate memory for printer"));
1655     return (NULL);
1656   }
1657 
1658   printer->ipv4           = -1;
1659   printer->ipv6           = -1;
1660   printer->name           = strdup(name);
1661   printer->dnssd_name     = strdup(name);
1662   printer->dnssd_subtypes = subtypes ? strdup(subtypes) : NULL;
1663   printer->command        = command ? strdup(command) : NULL;
1664   printer->device_uri     = device_uri ? strdup(device_uri) : NULL;
1665   printer->output_format  = output_format ? strdup(output_format) : NULL;
1666   printer->directory      = strdup(directory);
1667   printer->icons[0]       = icons ? strdup(icons) : NULL;
1668   printer->strings        = strings ? strdup(strings) : NULL;
1669   printer->port           = serverport;
1670   printer->start_time     = time(NULL);
1671   printer->config_time    = printer->start_time;
1672   printer->state          = IPP_PSTATE_IDLE;
1673   printer->state_reasons  = IPPEVE_PREASON_NONE;
1674   printer->state_time     = printer->start_time;
1675   printer->jobs           = cupsArrayNew((cups_array_func_t)compare_jobs, NULL);
1676   printer->next_job_id    = 1;
1677 
1678   if (printer->icons[0])
1679   {
1680    /*
1681     * Extract up to 3 icons...
1682     */
1683 
1684     for (i = 1, iconsptr = strchr(printer->icons[0], ','); iconsptr && i < 3; i ++, iconsptr = strchr(iconsptr, ','))
1685     {
1686       *iconsptr++ = '\0';
1687       printer->icons[i] = iconsptr;
1688     }
1689 
1690     if (iconsptr)
1691       *iconsptr = '\0';			/* Strip any icons after the third... */
1692 
1693     while (i < 3)
1694     {
1695       printer->icons[i] = printer->icons[i - 1];
1696       i ++;
1697     }
1698   }
1699 
1700   if (servername)
1701   {
1702     printer->hostname = strdup(servername);
1703   }
1704   else
1705   {
1706     char	temp[1024],		/* Temporary string */
1707 		*tempptr;		/* Pointer into temporary string */
1708 
1709 #ifdef HAVE_AVAHI
1710     const char *avahi_name = avahi_client_get_host_name_fqdn(DNSSDClient);
1711 
1712     if (avahi_name)
1713       strlcpy(temp, avahi_name, sizeof(temp));
1714     else
1715 #endif /* HAVE_AVAHI */
1716 
1717     if ((tempptr = strstr(httpGetHostname(NULL, temp, sizeof(temp)), ".lan")) != NULL && !tempptr[5])
1718       strlcpy(tempptr, ".local", sizeof(temp) - (size_t)(tempptr - temp));
1719 
1720     printer->hostname = strdup(temp);
1721   }
1722 
1723   _cupsRWInit(&(printer->rwlock));
1724 
1725  /*
1726   * Create the listener sockets...
1727   */
1728 
1729   if (printer->port)
1730   {
1731     if ((printer->ipv4 = create_listener(servername, printer->port, AF_INET)) < 0)
1732     {
1733       perror("Unable to create IPv4 listener");
1734       goto bad_printer;
1735     }
1736   }
1737   else
1738   {
1739 #ifdef _WIN32
1740    /*
1741     * Windows is almost always used as a single user system, so use a default
1742     * port number of 8631.
1743     */
1744 
1745     serverport = 8631;
1746 
1747 #else
1748    /*
1749     * Use 8000 + UID mod 1000 for the default port number...
1750     */
1751 
1752     serverport = 8000 + ((int)getuid() % 1000);
1753 #endif /* _WIN32 */
1754 
1755     while (serverport < 10000)
1756     {
1757       if ((printer->ipv4 = create_listener(servername, serverport, AF_INET)) >= 0)
1758         break;
1759 
1760       serverport ++;
1761     }
1762 
1763     if (serverport < 10000)
1764     {
1765       _cupsLangPrintf(stderr, _("Listening on port %d."), serverport);
1766       printer->port = serverport;
1767     }
1768     else
1769     {
1770       perror("Unable to create IPv4 listener");
1771       goto bad_printer;
1772     }
1773   }
1774 
1775   if ((printer->ipv6 = create_listener(servername, printer->port, AF_INET6)) < 0)
1776   {
1777     perror("Unable to create IPv6 listener");
1778     goto bad_printer;
1779   }
1780 
1781  /*
1782   * Prepare values for the printer attributes...
1783   */
1784 
1785   snprintf(uuid_data, sizeof(uuid_data), "_IPPEVEPRINTER_:%s:%d:%s", printer->hostname, printer->port, printer->name);
1786   cupsHashData("sha2-256", (unsigned char *)uuid_data, strlen(uuid_data), sha256, sizeof(sha256));
1787   snprintf(uuid, sizeof(uuid), "urn:uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", sha256[0], sha256[1], sha256[3], sha256[4], sha256[5], sha256[6], (sha256[10] & 15) | 0x30, sha256[11], (sha256[15] & 0x3f) | 0x40, sha256[16], sha256[20], sha256[21], sha256[25], sha256[26], sha256[30], sha256[31]);
1788 
1789   if (Verbosity)
1790   {
1791 #ifdef HAVE_TLS
1792     fprintf(stderr, "printer-uri-supported=\"ipp://%s:%d/ipp/print\",\"ipps://%s:%d/ipp/print\"\n", printer->hostname, printer->port, printer->hostname, printer->port);
1793 #else
1794     fprintf(stderr, "printer-uri-supported=\"ipp://%s:%d/ipp/print\"\n", printer->hostname, printer->port);
1795 #endif /* HAVE_TLS */
1796     fprintf(stderr, "printer-uuid=\"%s\"\n", uuid);
1797   }
1798 
1799  /*
1800   * Get the maximum spool size based on the size of the filesystem used for
1801   * the spool directory.  If the host OS doesn't support the statfs call
1802   * or the filesystem is larger than 2TiB, always report INT_MAX.
1803   */
1804 
1805 #ifdef HAVE_STATVFS
1806   if (statvfs(printer->directory, &spoolinfo))
1807     k_supported = INT_MAX;
1808   else if ((spoolsize = (double)spoolinfo.f_frsize *
1809                         spoolinfo.f_blocks / 1024) > INT_MAX)
1810     k_supported = INT_MAX;
1811   else
1812     k_supported = (int)spoolsize;
1813 
1814 #elif defined(HAVE_STATFS)
1815   if (statfs(printer->directory, &spoolinfo))
1816     k_supported = INT_MAX;
1817   else if ((spoolsize = (double)spoolinfo.f_bsize *
1818                         spoolinfo.f_blocks / 1024) > INT_MAX)
1819     k_supported = INT_MAX;
1820   else
1821     k_supported = (int)spoolsize;
1822 
1823 #else
1824   k_supported = INT_MAX;
1825 #endif /* HAVE_STATVFS */
1826 
1827  /*
1828   * Assemble the final list of document formats...
1829   */
1830 
1831   if (!cupsArrayFind(docformats, (void *)"application/octet-stream"))
1832     cupsArrayAdd(docformats, (void *)"application/octet-stream");
1833 
1834   for (num_formats = 0, format = (const char *)cupsArrayFirst(docformats); format && num_formats < (int)(sizeof(formats) / sizeof(formats[0])); format = (const char *)cupsArrayNext(docformats))
1835     formats[num_formats ++] = format;
1836 
1837  /*
1838   * Get the list of attributes that can be used when creating a job...
1839   */
1840 
1841   num_sup_attrs = 0;
1842   sup_attrs[num_sup_attrs ++] = "document-access";
1843   sup_attrs[num_sup_attrs ++] = "document-charset";
1844   sup_attrs[num_sup_attrs ++] = "document-format";
1845   sup_attrs[num_sup_attrs ++] = "document-message";
1846   sup_attrs[num_sup_attrs ++] = "document-metadata";
1847   sup_attrs[num_sup_attrs ++] = "document-name";
1848   sup_attrs[num_sup_attrs ++] = "document-natural-language";
1849   sup_attrs[num_sup_attrs ++] = "ipp-attribute-fidelity";
1850   sup_attrs[num_sup_attrs ++] = "job-name";
1851   sup_attrs[num_sup_attrs ++] = "job-priority";
1852 
1853   for (i = 0; i < (int)(sizeof(job_creation) / sizeof(job_creation[0])) && num_sup_attrs < (int)(sizeof(sup_attrs) / sizeof(sup_attrs[0])); i ++)
1854   {
1855     snprintf(xxx_supported, sizeof(xxx_supported), "%s-supported", job_creation[i]);
1856     if (ippFindAttribute(attrs, xxx_supported, IPP_TAG_ZERO))
1857       sup_attrs[num_sup_attrs ++] = job_creation[i];
1858   }
1859 
1860  /*
1861   * Fill out the rest of the printer attributes.
1862   */
1863 
1864   printer->attrs = attrs;
1865 
1866   /* charset-configured */
1867   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-configured", NULL, "utf-8");
1868 
1869   /* charset-supported */
1870   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-supported", sizeof(charsets) / sizeof(charsets[0]), NULL, charsets);
1871 
1872   /* compression-supported */
1873   if (!ippFindAttribute(printer->attrs, "compression-supported", IPP_TAG_ZERO))
1874     ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "compression-supported", (int)(sizeof(compressions) / sizeof(compressions[0])), NULL, compressions);
1875 
1876   /* document-format-default */
1877   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_MIMETYPE), "document-format-default", NULL, "application/octet-stream");
1878 
1879   /* document-format-supported */
1880   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE, "document-format-supported", num_formats, NULL, formats);
1881 
1882   /* generated-natural-language-supported */
1883   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "generated-natural-language-supported", NULL, "en");
1884 
1885   /* identify-actions-default */
1886   ippAddString (printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-default", NULL, "sound");
1887 
1888   /* identify-actions-supported */
1889   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-supported", sizeof(identify_actions) / sizeof(identify_actions[0]), NULL, identify_actions);
1890 
1891   /* ipp-features-supported */
1892   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-features-supported", sizeof(features) / sizeof(features[0]), NULL, features);
1893 
1894   /* ipp-versions-supported */
1895   if (MaxVersion == 11)
1896     ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", NULL, "1.1");
1897   else
1898     ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", (int)(sizeof(versions) / sizeof(versions[0])), NULL, versions);
1899 
1900   /* job-creation-attributes-supported */
1901   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-creation-attributes-supported", num_sup_attrs, NULL, sup_attrs);
1902 
1903   /* job-ids-supported */
1904   ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-ids-supported", 1);
1905 
1906   /* job-k-octets-supported */
1907   ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0, k_supported);
1908 
1909   /* job-priority-default */
1910   ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-default", 50);
1911 
1912   /* job-priority-supported */
1913   ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-supported", 1);
1914 
1915   /* job-sheets-default */
1916   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-default", NULL, "none");
1917 
1918   /* job-sheets-supported */
1919   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-supported", NULL, "none");
1920 
1921   /* media-col-supported */
1922   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-col-supported", (int)(sizeof(media_col_supported) / sizeof(media_col_supported[0])), NULL, media_col_supported);
1923 
1924   /* multiple-document-handling-supported */
1925   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "multiple-document-handling-supported", sizeof(multiple_document_handling) / sizeof(multiple_document_handling[0]), NULL, multiple_document_handling);
1926 
1927   /* multiple-document-jobs-supported */
1928   ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "multiple-document-jobs-supported", 0);
1929 
1930   /* multiple-operation-time-out */
1931   ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "multiple-operation-time-out", 60);
1932 
1933   /* multiple-operation-time-out-action */
1934   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "multiple-operation-time-out-action", NULL, "abort-job");
1935 
1936   /* natural-language-configured */
1937   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "natural-language-configured", NULL, "en");
1938 
1939   /* operations-supported */
1940   ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
1941 
1942   /* pdl-override-supported */
1943   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pdl-override-supported", NULL, "attempted");
1944 
1945   /* preferred-attributes-supported */
1946   ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "preferred-attributes-supported", 0);
1947 
1948   /* printer-get-attributes-supported */
1949   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-get-attributes-supported", NULL, "document-format");
1950 
1951   /* printer-geo-location */
1952   ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_UNKNOWN, "printer-geo-location");
1953 
1954   /* printer-is-accepting-jobs */
1955   ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
1956 
1957   /* printer-info */
1958   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL, name);
1959 
1960   /* printer-location */
1961   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", NULL, location);
1962 
1963   /* printer-name */
1964   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL, name);
1965 
1966   /* printer-organization */
1967   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organization", NULL, "");
1968 
1969   /* printer-organizational-unit */
1970   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organizational-unit", NULL, "");
1971 
1972   /* printer-strings-languages-supported */
1973   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE, "printer-strings-languages-supported", NULL, "en");
1974 
1975   /* printer-uuid */
1976   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uuid", NULL, uuid);
1977 
1978   /* reference-uri-scheme-supported */
1979   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_URISCHEME), "reference-uri-schemes-supported", (int)(sizeof(reference_uri_schemes_supported) / sizeof(reference_uri_schemes_supported[0])), NULL, reference_uri_schemes_supported);
1980 
1981   /* uri-authentication-supported */
1982 #ifdef HAVE_TLS
1983   if (PAMService)
1984     ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", 2, NULL, uri_authentication_basic);
1985   else
1986     ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", 2, NULL, uri_authentication_supported);
1987 #else
1988   if (PAMService)
1989     ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", NULL, "basic");
1990   else
1991     ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", NULL, "none");
1992 #endif /* HAVE_TLS */
1993 
1994   /* uri-security-supported */
1995 #ifdef HAVE_TLS
1996   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", 2, NULL, uri_security_supported);
1997 #else
1998   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", NULL, "none");
1999 #endif /* HAVE_TLS */
2000 
2001   /* which-jobs-supported */
2002   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "which-jobs-supported", sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs);
2003 
2004   debug_attributes("Printer", printer->attrs, 0);
2005 
2006  /*
2007   * Register the printer with DNS-SD...
2008   */
2009 
2010   if (!register_printer(printer))
2011     goto bad_printer;
2012 
2013  /*
2014   * Return it!
2015   */
2016 
2017   return (printer);
2018 
2019 
2020  /*
2021   * If we get here we were unable to create the printer...
2022   */
2023 
2024   bad_printer:
2025 
2026   delete_printer(printer);
2027 
2028   return (NULL);
2029 }
2030 
2031 
2032 /*
2033  * 'debug_attributes()' - Print attributes in a request or response.
2034  */
2035 
2036 static void
debug_attributes(const char * title,ipp_t * ipp,int type)2037 debug_attributes(const char *title,	/* I - Title */
2038                  ipp_t      *ipp,	/* I - Request/response */
2039                  int        type)	/* I - 0 = object, 1 = request, 2 = response */
2040 {
2041   ipp_tag_t		group_tag;	/* Current group */
2042   ipp_attribute_t	*attr;		/* Current attribute */
2043   char			buffer[2048];	/* String buffer for value */
2044   int			major, minor;	/* Version */
2045 
2046 
2047   if (Verbosity <= 1)
2048     return;
2049 
2050   fprintf(stderr, "%s:\n", title);
2051   major = ippGetVersion(ipp, &minor);
2052   fprintf(stderr, "  version=%d.%d\n", major, minor);
2053   if (type == 1)
2054     fprintf(stderr, "  operation-id=%s(%04x)\n",
2055             ippOpString(ippGetOperation(ipp)), ippGetOperation(ipp));
2056   else if (type == 2)
2057     fprintf(stderr, "  status-code=%s(%04x)\n",
2058             ippErrorString(ippGetStatusCode(ipp)), ippGetStatusCode(ipp));
2059   fprintf(stderr, "  request-id=%d\n\n", ippGetRequestId(ipp));
2060 
2061   for (attr = ippFirstAttribute(ipp), group_tag = IPP_TAG_ZERO;
2062        attr;
2063        attr = ippNextAttribute(ipp))
2064   {
2065     if (ippGetGroupTag(attr) != group_tag)
2066     {
2067       group_tag = ippGetGroupTag(attr);
2068       fprintf(stderr, "  %s\n", ippTagString(group_tag));
2069     }
2070 
2071     if (ippGetName(attr))
2072     {
2073       ippAttributeString(attr, buffer, sizeof(buffer));
2074       fprintf(stderr, "    %s (%s%s) %s\n", ippGetName(attr),
2075 	      ippGetCount(attr) > 1 ? "1setOf " : "",
2076 	      ippTagString(ippGetValueTag(attr)), buffer);
2077     }
2078   }
2079 }
2080 
2081 
2082 /*
2083  * 'delete_client()' - Close the socket and free all memory used by a client
2084  *                     object.
2085  */
2086 
2087 static void
delete_client(ippeve_client_t * client)2088 delete_client(ippeve_client_t *client)	/* I - Client */
2089 {
2090   if (Verbosity)
2091     fprintf(stderr, "Closing connection from %s\n", client->hostname);
2092 
2093  /*
2094   * Flush pending writes before closing...
2095   */
2096 
2097   httpFlushWrite(client->http);
2098 
2099  /*
2100   * Free memory...
2101   */
2102 
2103   httpClose(client->http);
2104 
2105   ippDelete(client->request);
2106   ippDelete(client->response);
2107 
2108   free(client);
2109 }
2110 
2111 
2112 /*
2113  * 'delete_job()' - Remove from the printer and free all memory used by a job
2114  *                  object.
2115  */
2116 
2117 static void
delete_job(ippeve_job_t * job)2118 delete_job(ippeve_job_t *job)		/* I - Job */
2119 {
2120   if (Verbosity)
2121     fprintf(stderr, "[Job %d] Removing job from history.\n", job->id);
2122 
2123   ippDelete(job->attrs);
2124 
2125   if (job->message)
2126     free(job->message);
2127 
2128   if (job->filename)
2129   {
2130     if (!KeepFiles)
2131       unlink(job->filename);
2132 
2133     free(job->filename);
2134   }
2135 
2136   free(job);
2137 }
2138 
2139 
2140 /*
2141  * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2142  *                      used by a printer object.
2143  */
2144 
2145 static void
delete_printer(ippeve_printer_t * printer)2146 delete_printer(ippeve_printer_t *printer)	/* I - Printer */
2147 {
2148   if (printer->ipv4 >= 0)
2149     close(printer->ipv4);
2150 
2151   if (printer->ipv6 >= 0)
2152     close(printer->ipv6);
2153 
2154 #if HAVE_MDNSRESPONDER
2155   if (printer->printer_ref)
2156     DNSServiceRefDeallocate(printer->printer_ref);
2157   if (printer->ipp_ref)
2158     DNSServiceRefDeallocate(printer->ipp_ref);
2159   if (printer->ipps_ref)
2160     DNSServiceRefDeallocate(printer->ipps_ref);
2161   if (printer->http_ref)
2162     DNSServiceRefDeallocate(printer->http_ref);
2163 #elif defined(HAVE_AVAHI)
2164   avahi_threaded_poll_lock(DNSSDMaster);
2165 
2166   if (printer->dnssd_ref)
2167     avahi_entry_group_free(printer->dnssd_ref);
2168 
2169   avahi_threaded_poll_unlock(DNSSDMaster);
2170 #endif /* HAVE_MDNSRESPONDER */
2171 
2172   if (printer->dnssd_name)
2173     free(printer->dnssd_name);
2174   if (printer->name)
2175     free(printer->name);
2176   if (printer->icons[0])
2177     free(printer->icons[0]);
2178   if (printer->strings)
2179     free(printer->strings);
2180   if (printer->command)
2181     free(printer->command);
2182   if (printer->device_uri)
2183     free(printer->device_uri);
2184 #if !CUPS_LITE
2185   if (printer->ppdfile)
2186     free(printer->ppdfile);
2187 #endif /* !CUPS_LITE */
2188   if (printer->directory)
2189     free(printer->directory);
2190   if (printer->hostname)
2191     free(printer->hostname);
2192 
2193   ippDelete(printer->attrs);
2194   cupsArrayDelete(printer->jobs);
2195 
2196   free(printer);
2197 }
2198 
2199 
2200 #ifdef HAVE_MDNSRESPONDER
2201 /*
2202  * 'dnssd_callback()' - Handle DNS-SD registration events.
2203  */
2204 
2205 static void DNSSD_API
dnssd_callback(DNSServiceRef sdRef,DNSServiceFlags flags,DNSServiceErrorType errorCode,const char * name,const char * regtype,const char * domain,ippeve_printer_t * printer)2206 dnssd_callback(
2207     DNSServiceRef       sdRef,		/* I - Service reference */
2208     DNSServiceFlags     flags,		/* I - Status flags */
2209     DNSServiceErrorType errorCode,	/* I - Error, if any */
2210     const char          *name,		/* I - Service name */
2211     const char          *regtype,	/* I - Service type */
2212     const char          *domain,	/* I - Domain for service */
2213     ippeve_printer_t    *printer)	/* I - Printer */
2214 {
2215   (void)sdRef;
2216   (void)flags;
2217   (void)domain;
2218   (void)name;
2219 
2220   if (errorCode == kDNSServiceErr_NameConflict)
2221   {
2222     fputs("DNS-SD service name collision detected.\n", stderr);
2223     printer->dnssd_collision = 1;
2224   }
2225   else if (errorCode)
2226   {
2227     fprintf(stderr, "DNSServiceRegister for %s failed with error %d.\n", regtype, (int)errorCode);
2228     return;
2229   }
2230 }
2231 
2232 
2233 #elif defined(HAVE_AVAHI)
2234 /*
2235  * 'dnssd_callback()' - Handle DNS-SD registration events.
2236  */
2237 
2238 static void
dnssd_callback(AvahiEntryGroup * srv,AvahiEntryGroupState state,void * context)2239 dnssd_callback(
2240     AvahiEntryGroup      *srv,		/* I - Service */
2241     AvahiEntryGroupState state,		/* I - Registration state */
2242     void                 *context)	/* I - Printer */
2243 {
2244   ippeve_printer_t *printer = (ippeve_printer_t *)context;
2245  					/* Printer */
2246 
2247 
2248   (void)srv;
2249 
2250   if (state == AVAHI_ENTRY_GROUP_COLLISION)
2251   {
2252     fputs("DNS-SD service name collision detected.\n", stderr);
2253     printer->dnssd_collision = 1;
2254   }
2255 }
2256 
2257 
2258 /*
2259  * 'dnssd_client_cb()' - Client callback for Avahi.
2260  *
2261  * Called whenever the client or server state changes...
2262  */
2263 
2264 static void
dnssd_client_cb(AvahiClient * c,AvahiClientState state,void * userdata)2265 dnssd_client_cb(
2266     AvahiClient      *c,		/* I - Client */
2267     AvahiClientState state,		/* I - Current state */
2268     void             *userdata)		/* I - User data (printer) */
2269 {
2270   if (!c)
2271     return;
2272 
2273   if (state == AVAHI_CLIENT_FAILURE)
2274   {
2275     if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
2276     {
2277       fputs("Avahi server crashed, exiting.\n", stderr);
2278       exit(1);
2279     }
2280   }
2281   else
2282   {
2283     fprintf(stderr, "Ignored Avahi state %d.\n", state);
2284   }
2285 }
2286 #endif /* HAVE_MDNSRESPONDER */
2287 
2288 
2289 /*
2290  * 'dnssd_init()' - Initialize the DNS-SD service connections...
2291  */
2292 
2293 static void
dnssd_init(void)2294 dnssd_init(void)
2295 {
2296 #ifdef HAVE_MDNSRESPONDER
2297   if (DNSServiceCreateConnection(&DNSSDMaster) != kDNSServiceErr_NoError)
2298   {
2299     fputs("Error: Unable to initialize DNS-SD.\n", stderr);
2300     exit(1);
2301   }
2302 
2303 #elif defined(HAVE_AVAHI)
2304   int error;			/* Error code, if any */
2305 
2306   if ((DNSSDMaster = avahi_threaded_poll_new()) == NULL)
2307   {
2308     fputs("Error: Unable to initialize DNS-SD.\n", stderr);
2309     exit(1);
2310   }
2311 
2312   if ((DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssd_client_cb, NULL, &error)) == NULL)
2313   {
2314     fputs("Error: Unable to initialize DNS-SD.\n", stderr);
2315     exit(1);
2316   }
2317 
2318   avahi_threaded_poll_start(DNSSDMaster);
2319 #endif /* HAVE_MDNSRESPONDER */
2320 }
2321 
2322 
2323 /*
2324  * 'filter_cb()' - Filter printer attributes based on the requested array.
2325  */
2326 
2327 static int				/* O - 1 to copy, 0 to ignore */
filter_cb(ippeve_filter_t * filter,ipp_t * dst,ipp_attribute_t * attr)2328 filter_cb(ippeve_filter_t   *filter,	/* I - Filter parameters */
2329           ipp_t           *dst,		/* I - Destination (unused) */
2330 	  ipp_attribute_t *attr)	/* I - Source attribute */
2331 {
2332  /*
2333   * Filter attributes as needed...
2334   */
2335 
2336 #ifndef _WIN32 /* Avoid MS compiler bug */
2337   (void)dst;
2338 #endif /* !_WIN32 */
2339 
2340   ipp_tag_t group = ippGetGroupTag(attr);
2341   const char *name = ippGetName(attr);
2342 
2343   if ((filter->group_tag != IPP_TAG_ZERO && group != filter->group_tag && group != IPP_TAG_ZERO) || !name || (!strcmp(name, "media-col-database") && !cupsArrayFind(filter->ra, (void *)name)))
2344     return (0);
2345 
2346   return (!filter->ra || cupsArrayFind(filter->ra, (void *)name) != NULL);
2347 }
2348 
2349 
2350 /*
2351  * 'find_job()' - Find a job specified in a request.
2352  */
2353 
2354 static ippeve_job_t *			/* O - Job or NULL */
find_job(ippeve_client_t * client)2355 find_job(ippeve_client_t *client)		/* I - Client */
2356 {
2357   ipp_attribute_t	*attr;		/* job-id or job-uri attribute */
2358   ippeve_job_t		key,		/* Job search key */
2359 			*job;		/* Matching job, if any */
2360 
2361 
2362   if ((attr = ippFindAttribute(client->request, "job-uri", IPP_TAG_URI)) != NULL)
2363   {
2364     const char *uri = ippGetString(attr, 0, NULL);
2365 					/* URI value */
2366     const char *uriptr = strrchr(uri, '/');
2367 					/* Pointer to the last slash in the URI */
2368 
2369     if (uriptr && isdigit(uriptr[1] & 255))
2370       key.id = atoi(uriptr + 1);
2371     else
2372       return (NULL);
2373   }
2374   else if ((attr = ippFindAttribute(client->request, "job-id", IPP_TAG_INTEGER)) != NULL)
2375     key.id = ippGetInteger(attr, 0);
2376 
2377   _cupsRWLockRead(&(client->printer->rwlock));
2378   job = (ippeve_job_t *)cupsArrayFind(client->printer->jobs, &key);
2379   _cupsRWUnlock(&(client->printer->rwlock));
2380 
2381   return (job);
2382 }
2383 
2384 
2385 /*
2386  * 'finish_document()' - Finish receiving a document file and start processing.
2387  */
2388 
2389 static void
finish_document_data(ippeve_client_t * client,ippeve_job_t * job)2390 finish_document_data(
2391     ippeve_client_t *client,		/* I - Client */
2392     ippeve_job_t    *job)		/* I - Job */
2393 {
2394   char			filename[1024],	/* Filename buffer */
2395 			buffer[4096];	/* Copy buffer */
2396   ssize_t		bytes;		/* Bytes read */
2397   cups_array_t		*ra;		/* Attributes to send in response */
2398   _cups_thread_t        t;              /* Thread */
2399 
2400 
2401  /*
2402   * Create a file for the request data...
2403   *
2404   * TODO: Update code to support piping large raster data to the print command.
2405   */
2406 
2407   if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0)
2408   {
2409     respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno));
2410 
2411     goto abort_job;
2412   }
2413 
2414   if (Verbosity)
2415     fprintf(stderr, "Created job file \"%s\", format \"%s\".\n", filename, job->format);
2416 
2417   while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
2418   {
2419     if (write(job->fd, buffer, (size_t)bytes) < bytes)
2420     {
2421       int error = errno;		/* Write error */
2422 
2423       close(job->fd);
2424       job->fd = -1;
2425 
2426       unlink(filename);
2427 
2428       respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2429 
2430       goto abort_job;
2431     }
2432   }
2433 
2434   if (bytes < 0)
2435   {
2436    /*
2437     * Got an error while reading the print data, so abort this job.
2438     */
2439 
2440     close(job->fd);
2441     job->fd = -1;
2442 
2443     unlink(filename);
2444 
2445     respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to read print file.");
2446 
2447     goto abort_job;
2448   }
2449 
2450   if (close(job->fd))
2451   {
2452     int error = errno;			/* Write error */
2453 
2454     job->fd = -1;
2455 
2456     unlink(filename);
2457 
2458     respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2459 
2460     goto abort_job;
2461   }
2462 
2463   job->fd       = -1;
2464   job->filename = strdup(filename);
2465   job->state    = IPP_JSTATE_PENDING;
2466 
2467  /*
2468   * Process the job...
2469   */
2470 
2471   t = _cupsThreadCreate((_cups_thread_func_t)process_job, job);
2472 
2473   if (t)
2474   {
2475     _cupsThreadDetach(t);
2476   }
2477   else
2478   {
2479     respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
2480     goto abort_job;
2481   }
2482 
2483  /*
2484   * Return the job info...
2485   */
2486 
2487   respond_ipp(client, IPP_STATUS_OK, NULL);
2488 
2489   ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2490   cupsArrayAdd(ra, "job-id");
2491   cupsArrayAdd(ra, "job-state");
2492   cupsArrayAdd(ra, "job-state-message");
2493   cupsArrayAdd(ra, "job-state-reasons");
2494   cupsArrayAdd(ra, "job-uri");
2495 
2496   copy_job_attributes(client, job, ra);
2497   cupsArrayDelete(ra);
2498   return;
2499 
2500  /*
2501   * If we get here we had to abort the job...
2502   */
2503 
2504   abort_job:
2505 
2506   job->state     = IPP_JSTATE_ABORTED;
2507   job->completed = time(NULL);
2508 
2509   ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2510   cupsArrayAdd(ra, "job-id");
2511   cupsArrayAdd(ra, "job-state");
2512   cupsArrayAdd(ra, "job-state-reasons");
2513   cupsArrayAdd(ra, "job-uri");
2514 
2515   copy_job_attributes(client, job, ra);
2516   cupsArrayDelete(ra);
2517 }
2518 
2519 
2520 /*
2521  * 'finish_uri()' - Finish fetching a document URI and start processing.
2522  */
2523 
2524 static void
finish_document_uri(ippeve_client_t * client,ippeve_job_t * job)2525 finish_document_uri(
2526     ippeve_client_t *client,		/* I - Client */
2527     ippeve_job_t    *job)		/* I - Job */
2528 {
2529   ipp_attribute_t	*uri;		/* document-uri */
2530   char			scheme[256],	/* URI scheme */
2531 			userpass[256],	/* Username and password info */
2532 			hostname[256],	/* Hostname */
2533 			resource[1024];	/* Resource path */
2534   int			port;		/* Port number */
2535   http_uri_status_t	uri_status;	/* URI decode status */
2536   http_encryption_t	encryption;	/* Encryption to use, if any */
2537   http_t		*http;		/* Connection for http/https URIs */
2538   http_status_t		status;		/* Access status for http/https URIs */
2539   int			infile;		/* Input file for local file URIs */
2540   char			filename[1024],	/* Filename buffer */
2541 			buffer[4096];	/* Copy buffer */
2542   ssize_t		bytes;		/* Bytes read */
2543   ipp_attribute_t	*attr;		/* Current attribute */
2544   cups_array_t		*ra;		/* Attributes to send in response */
2545 
2546 
2547  /*
2548   * Do we have a file to print?
2549   */
2550 
2551   if (have_document_data(client))
2552   {
2553     flush_document_data(client);
2554     respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Unexpected document data following request.");
2555 
2556     goto abort_job;
2557   }
2558 
2559  /*
2560   * Do we have a document URI?
2561   */
2562 
2563   if ((uri = ippFindAttribute(client->request, "document-uri", IPP_TAG_URI)) == NULL)
2564   {
2565     respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
2566 
2567     goto abort_job;
2568   }
2569 
2570   if (ippGetCount(uri) != 1)
2571   {
2572     respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Too many document-uri values.");
2573 
2574     goto abort_job;
2575   }
2576 
2577   uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
2578                                scheme, sizeof(scheme), userpass,
2579                                sizeof(userpass), hostname, sizeof(hostname),
2580                                &port, resource, sizeof(resource));
2581   if (uri_status < HTTP_URI_STATUS_OK)
2582   {
2583     respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s", httpURIStatusString(uri_status));
2584 
2585     goto abort_job;
2586   }
2587 
2588   if (strcmp(scheme, "file") &&
2589 #ifdef HAVE_TLS
2590       strcmp(scheme, "https") &&
2591 #endif /* HAVE_TLS */
2592       strcmp(scheme, "http"))
2593   {
2594     respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME, "URI scheme \"%s\" not supported.", scheme);
2595 
2596     goto abort_job;
2597   }
2598 
2599   if (!strcmp(scheme, "file") && access(resource, R_OK))
2600   {
2601     respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno));
2602 
2603     goto abort_job;
2604   }
2605 
2606  /*
2607   * Get the document format for the job...
2608   */
2609 
2610   _cupsRWLockWrite(&(client->printer->rwlock));
2611 
2612   if ((attr = ippFindAttribute(job->attrs, "document-format", IPP_TAG_MIMETYPE)) != NULL)
2613     job->format = ippGetString(attr, 0, NULL);
2614   else
2615     job->format = "application/octet-stream";
2616 
2617  /*
2618   * Create a file for the request data...
2619   */
2620 
2621   if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0)
2622   {
2623     _cupsRWUnlock(&(client->printer->rwlock));
2624 
2625     respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno));
2626 
2627     goto abort_job;
2628   }
2629 
2630   _cupsRWUnlock(&(client->printer->rwlock));
2631 
2632   if (!strcmp(scheme, "file"))
2633   {
2634     if ((infile = open(resource, O_RDONLY | O_BINARY)) < 0)
2635     {
2636       respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno));
2637 
2638       goto abort_job;
2639     }
2640 
2641     do
2642     {
2643       if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
2644           (errno == EAGAIN || errno == EINTR))
2645       {
2646         bytes = 1;
2647       }
2648       else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes)
2649       {
2650 	int error = errno;		/* Write error */
2651 
2652 	close(job->fd);
2653 	job->fd = -1;
2654 
2655 	unlink(filename);
2656 	close(infile);
2657 
2658 	respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2659 
2660         goto abort_job;
2661       }
2662     }
2663     while (bytes > 0);
2664 
2665     close(infile);
2666   }
2667   else
2668   {
2669 #ifdef HAVE_TLS
2670     if (port == 443 || !strcmp(scheme, "https"))
2671       encryption = HTTP_ENCRYPTION_ALWAYS;
2672     else
2673 #endif /* HAVE_TLS */
2674     encryption = HTTP_ENCRYPTION_IF_REQUESTED;
2675 
2676     if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL)
2677     {
2678       respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to connect to %s: %s", hostname, cupsLastErrorString());
2679 
2680       close(job->fd);
2681       job->fd = -1;
2682 
2683       unlink(filename);
2684 
2685       goto abort_job;
2686     }
2687 
2688     httpClearFields(http);
2689     httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
2690     if (httpGet(http, resource))
2691     {
2692       respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", strerror(errno));
2693 
2694       close(job->fd);
2695       job->fd = -1;
2696 
2697       unlink(filename);
2698       httpClose(http);
2699 
2700       goto abort_job;
2701     }
2702 
2703     while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
2704 
2705     if (status != HTTP_STATUS_OK)
2706     {
2707       respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", httpStatus(status));
2708 
2709       close(job->fd);
2710       job->fd = -1;
2711 
2712       unlink(filename);
2713       httpClose(http);
2714 
2715       goto abort_job;
2716     }
2717 
2718     while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
2719     {
2720       if (write(job->fd, buffer, (size_t)bytes) < bytes)
2721       {
2722 	int error = errno;		/* Write error */
2723 
2724 	close(job->fd);
2725 	job->fd = -1;
2726 
2727 	unlink(filename);
2728 	httpClose(http);
2729 
2730 	respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
2731 		    "Unable to write print file: %s", strerror(error));
2732 
2733         goto abort_job;
2734       }
2735     }
2736 
2737     httpClose(http);
2738   }
2739 
2740   if (close(job->fd))
2741   {
2742     int error = errno;		/* Write error */
2743 
2744     job->fd = -1;
2745 
2746     unlink(filename);
2747 
2748     respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2749 
2750     goto abort_job;
2751   }
2752 
2753   _cupsRWLockWrite(&(client->printer->rwlock));
2754 
2755   job->fd       = -1;
2756   job->filename = strdup(filename);
2757   job->state    = IPP_JSTATE_PENDING;
2758 
2759   _cupsRWUnlock(&(client->printer->rwlock));
2760 
2761  /*
2762   * Process the job...
2763   */
2764 
2765   process_job(job);
2766 
2767  /*
2768   * Return the job info...
2769   */
2770 
2771   respond_ipp(client, IPP_STATUS_OK, NULL);
2772 
2773   ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2774   cupsArrayAdd(ra, "job-id");
2775   cupsArrayAdd(ra, "job-state");
2776   cupsArrayAdd(ra, "job-state-reasons");
2777   cupsArrayAdd(ra, "job-uri");
2778 
2779   copy_job_attributes(client, job, ra);
2780   cupsArrayDelete(ra);
2781   return;
2782 
2783  /*
2784   * If we get here we had to abort the job...
2785   */
2786 
2787   abort_job:
2788 
2789   job->state     = IPP_JSTATE_ABORTED;
2790   job->completed = time(NULL);
2791 
2792   ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2793   cupsArrayAdd(ra, "job-id");
2794   cupsArrayAdd(ra, "job-state");
2795   cupsArrayAdd(ra, "job-state-reasons");
2796   cupsArrayAdd(ra, "job-uri");
2797 
2798   copy_job_attributes(client, job, ra);
2799   cupsArrayDelete(ra);
2800 }
2801 
2802 
2803 /*
2804  * 'flush_document_data()' - Safely flush remaining document data.
2805  */
2806 
2807 static void
flush_document_data(ippeve_client_t * client)2808 flush_document_data(
2809     ippeve_client_t *client)		/* I - Client */
2810 {
2811   char	buffer[8192];			/* Read buffer */
2812 
2813 
2814   if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
2815   {
2816     while (httpRead2(client->http, buffer, sizeof(buffer)) > 0);
2817   }
2818 }
2819 
2820 
2821 /*
2822  * 'have_document_data()' - Determine whether we have more document data.
2823  */
2824 
2825 static int				/* O - 1 if data is present, 0 otherwise */
have_document_data(ippeve_client_t * client)2826 have_document_data(
2827     ippeve_client_t *client)		/* I - Client */
2828 {
2829   char temp;				/* Data */
2830 
2831 
2832   if (httpGetState(client->http) != HTTP_STATE_POST_RECV)
2833     return (0);
2834   else
2835     return (httpPeek(client->http, &temp, 1) > 0);
2836 }
2837 
2838 
2839 /*
2840  * 'html_escape()' - Write a HTML-safe string.
2841  */
2842 
2843 static void
html_escape(ippeve_client_t * client,const char * s,size_t slen)2844 html_escape(ippeve_client_t *client,	/* I - Client */
2845 	    const char    *s,		/* I - String to write */
2846 	    size_t        slen)		/* I - Number of characters to write */
2847 {
2848   const char	*start,			/* Start of segment */
2849 		*end;			/* End of string */
2850 
2851 
2852   start = s;
2853   end   = s + (slen > 0 ? slen : strlen(s));
2854 
2855   while (*s && s < end)
2856   {
2857     if (*s == '&' || *s == '<')
2858     {
2859       if (s > start)
2860         httpWrite2(client->http, start, (size_t)(s - start));
2861 
2862       if (*s == '&')
2863         httpWrite2(client->http, "&amp;", 5);
2864       else
2865         httpWrite2(client->http, "&lt;", 4);
2866 
2867       start = s + 1;
2868     }
2869 
2870     s ++;
2871   }
2872 
2873   if (s > start)
2874     httpWrite2(client->http, start, (size_t)(s - start));
2875 }
2876 
2877 
2878 /*
2879  * 'html_footer()' - Show the web interface footer.
2880  *
2881  * This function also writes the trailing 0-length chunk.
2882  */
2883 
2884 static void
html_footer(ippeve_client_t * client)2885 html_footer(ippeve_client_t *client)	/* I - Client */
2886 {
2887   html_printf(client,
2888 	      "</div>\n"
2889 	      "</body>\n"
2890 	      "</html>\n");
2891   httpWrite2(client->http, "", 0);
2892 }
2893 
2894 
2895 /*
2896  * 'html_header()' - Show the web interface header and title.
2897  */
2898 
2899 static void
html_header(ippeve_client_t * client,const char * title,int refresh)2900 html_header(ippeve_client_t *client,	/* I - Client */
2901             const char    *title,	/* I - Title */
2902             int           refresh)	/* I - Refresh timer, if any */
2903 {
2904   html_printf(client,
2905 	      "<!doctype html>\n"
2906 	      "<html>\n"
2907 	      "<head>\n"
2908 	      "<title>%s</title>\n"
2909 	      "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2910 	      "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2911 	      "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n", title);
2912   if (refresh > 0)
2913     html_printf(client, "<meta http-equiv=\"refresh\" content=\"%d\">\n", refresh);
2914   html_printf(client,
2915 	      "<meta name=\"viewport\" content=\"width=device-width\">\n"
2916 	      "<style>\n"
2917 	      "body { font-family: sans-serif; margin: 0; }\n"
2918 	      "div.body { padding: 0px 10px 10px; }\n"
2919 	      "span.badge { background: #090; border-radius: 5px; color: #fff; padding: 5px 10px; }\n"
2920 	      "span.bar { box-shadow: 0px 1px 5px #333; font-size: 75%%; }\n"
2921 	      "table.form { border-collapse: collapse; margin-left: auto; margin-right: auto; margin-top: 10px; width: auto; }\n"
2922 	      "table.form td, table.form th { padding: 5px 2px; }\n"
2923 	      "table.form td.meter { border-right: solid 1px #ccc; padding: 0px; width: 400px; }\n"
2924 	      "table.form th { text-align: right; }\n"
2925 	      "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2926 	      "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2927 	      "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2928 	      "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2929 	      "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2930 	      "table.nav { border-collapse: collapse; width: 100%%; }\n"
2931 	      "table.nav td { margin: 0; text-align: center; }\n"
2932 	      "td.nav a, td.nav a:active, td.nav a:hover, td.nav a:hover:link, td.nav a:hover:link:visited, td.nav a:link, td.nav a:link:visited, td.nav a:visited { background: inherit; color: inherit; font-size: 80%%; text-decoration: none; }\n"
2933 	      "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2934 	      "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2935 	      "td.nav:hover { background: #666; color: #fff; }\n"
2936 	      "td.nav:active { background: #000; color: #ff0; }\n"
2937 	      "</style>\n"
2938 	      "</head>\n"
2939 	      "<body>\n"
2940 	      "<table class=\"nav\"><tr>"
2941 	      "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2942 	      "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2943 	      "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2944 	      "</tr></table>\n"
2945 	      "<div class=\"body\">\n", !strcmp(client->uri, "/") ? " sel" : "", !strcmp(client->uri, "/supplies") ? " sel" : "", !strcmp(client->uri, "/media") ? " sel" : "");
2946 }
2947 
2948 
2949 /*
2950  * 'html_printf()' - Send formatted text to the client, quoting as needed.
2951  */
2952 
2953 static void
html_printf(ippeve_client_t * client,const char * format,...)2954 html_printf(ippeve_client_t *client,	/* I - Client */
2955 	    const char    *format,	/* I - Printf-style format string */
2956 	    ...)			/* I - Additional arguments as needed */
2957 {
2958   va_list	ap;			/* Pointer to arguments */
2959   const char	*start;			/* Start of string */
2960   char		size,			/* Size character (h, l, L) */
2961 		type;			/* Format type character */
2962   int		width,			/* Width of field */
2963 		prec;			/* Number of characters of precision */
2964   char		tformat[100],		/* Temporary format string for sprintf() */
2965 		*tptr,			/* Pointer into temporary format */
2966 		temp[1024];		/* Buffer for formatted numbers */
2967   char		*s;			/* Pointer to string */
2968 
2969 
2970  /*
2971   * Loop through the format string, formatting as needed...
2972   */
2973 
2974   va_start(ap, format);
2975   start = format;
2976 
2977   while (*format)
2978   {
2979     if (*format == '%')
2980     {
2981       if (format > start)
2982         httpWrite2(client->http, start, (size_t)(format - start));
2983 
2984       tptr    = tformat;
2985       *tptr++ = *format++;
2986 
2987       if (*format == '%')
2988       {
2989         httpWrite2(client->http, "%", 1);
2990         format ++;
2991 	start = format;
2992 	continue;
2993       }
2994       else if (strchr(" -+#\'", *format))
2995         *tptr++ = *format++;
2996 
2997       if (*format == '*')
2998       {
2999        /*
3000         * Get width from argument...
3001 	*/
3002 
3003 	format ++;
3004 	width = va_arg(ap, int);
3005 
3006 	snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
3007 	tptr += strlen(tptr);
3008       }
3009       else
3010       {
3011 	width = 0;
3012 
3013 	while (isdigit(*format & 255))
3014 	{
3015 	  if (tptr < (tformat + sizeof(tformat) - 1))
3016 	    *tptr++ = *format;
3017 
3018 	  width = width * 10 + *format++ - '0';
3019 	}
3020       }
3021 
3022       if (*format == '.')
3023       {
3024 	if (tptr < (tformat + sizeof(tformat) - 1))
3025 	  *tptr++ = *format;
3026 
3027         format ++;
3028 
3029         if (*format == '*')
3030 	{
3031          /*
3032 	  * Get precision from argument...
3033 	  */
3034 
3035 	  format ++;
3036 	  prec = va_arg(ap, int);
3037 
3038 	  snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
3039 	  tptr += strlen(tptr);
3040 	}
3041 	else
3042 	{
3043 	  prec = 0;
3044 
3045 	  while (isdigit(*format & 255))
3046 	  {
3047 	    if (tptr < (tformat + sizeof(tformat) - 1))
3048 	      *tptr++ = *format;
3049 
3050 	    prec = prec * 10 + *format++ - '0';
3051 	  }
3052 	}
3053       }
3054 
3055       if (*format == 'l' && format[1] == 'l')
3056       {
3057         size = 'L';
3058 
3059 	if (tptr < (tformat + sizeof(tformat) - 2))
3060 	{
3061 	  *tptr++ = 'l';
3062 	  *tptr++ = 'l';
3063 	}
3064 
3065 	format += 2;
3066       }
3067       else if (*format == 'h' || *format == 'l' || *format == 'L')
3068       {
3069 	if (tptr < (tformat + sizeof(tformat) - 1))
3070 	  *tptr++ = *format;
3071 
3072         size = *format++;
3073       }
3074       else
3075         size = 0;
3076 
3077 
3078       if (!*format)
3079       {
3080         start = format;
3081         break;
3082       }
3083 
3084       if (tptr < (tformat + sizeof(tformat) - 1))
3085         *tptr++ = *format;
3086 
3087       type  = *format++;
3088       *tptr = '\0';
3089       start = format;
3090 
3091       switch (type)
3092       {
3093 	case 'E' : /* Floating point formats */
3094 	case 'G' :
3095 	case 'e' :
3096 	case 'f' :
3097 	case 'g' :
3098 	    if ((size_t)(width + 2) > sizeof(temp))
3099 	      break;
3100 
3101 	    snprintf(temp, sizeof(temp), tformat, va_arg(ap, double));
3102 
3103             httpWrite2(client->http, temp, strlen(temp));
3104 	    break;
3105 
3106         case 'B' : /* Integer formats */
3107 	case 'X' :
3108 	case 'b' :
3109         case 'd' :
3110 	case 'i' :
3111 	case 'o' :
3112 	case 'u' :
3113 	case 'x' :
3114 	    if ((size_t)(width + 2) > sizeof(temp))
3115 	      break;
3116 
3117 #  ifdef HAVE_LONG_LONG
3118             if (size == 'L')
3119 	      snprintf(temp, sizeof(temp), tformat, va_arg(ap, long long));
3120 	    else
3121 #  endif /* HAVE_LONG_LONG */
3122             if (size == 'l')
3123 	      snprintf(temp, sizeof(temp), tformat, va_arg(ap, long));
3124 	    else
3125 	      snprintf(temp, sizeof(temp), tformat, va_arg(ap, int));
3126 
3127             httpWrite2(client->http, temp, strlen(temp));
3128 	    break;
3129 
3130 	case 'p' : /* Pointer value */
3131 	    if ((size_t)(width + 2) > sizeof(temp))
3132 	      break;
3133 
3134 	    snprintf(temp, sizeof(temp), tformat, va_arg(ap, void *));
3135 
3136             httpWrite2(client->http, temp, strlen(temp));
3137 	    break;
3138 
3139         case 'c' : /* Character or character array */
3140             if (width <= 1)
3141             {
3142               temp[0] = (char)va_arg(ap, int);
3143               temp[1] = '\0';
3144               html_escape(client, temp, 1);
3145             }
3146             else
3147               html_escape(client, va_arg(ap, char *), (size_t)width);
3148 	    break;
3149 
3150 	case 's' : /* String */
3151 	    if ((s = va_arg(ap, char *)) == NULL)
3152 	      s = "(null)";
3153 
3154             html_escape(client, s, strlen(s));
3155 	    break;
3156       }
3157     }
3158     else
3159       format ++;
3160   }
3161 
3162   if (format > start)
3163     httpWrite2(client->http, start, (size_t)(format - start));
3164 
3165   va_end(ap);
3166 }
3167 
3168 
3169 /*
3170  * 'ipp_cancel_job()' - Cancel a job.
3171  */
3172 
3173 static void
ipp_cancel_job(ippeve_client_t * client)3174 ipp_cancel_job(ippeve_client_t *client)	/* I - Client */
3175 {
3176   ippeve_job_t		*job;		/* Job information */
3177 
3178 
3179  /*
3180   * Get the job...
3181   */
3182 
3183   if ((job = find_job(client)) == NULL)
3184   {
3185     respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3186     return;
3187   }
3188 
3189  /*
3190   * See if the job is already completed, canceled, or aborted; if so,
3191   * we can't cancel...
3192   */
3193 
3194   switch (job->state)
3195   {
3196     case IPP_JSTATE_CANCELED :
3197 	respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3198 		    "Job #%d is already canceled - can\'t cancel.", job->id);
3199         break;
3200 
3201     case IPP_JSTATE_ABORTED :
3202 	respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3203 		    "Job #%d is already aborted - can\'t cancel.", job->id);
3204         break;
3205 
3206     case IPP_JSTATE_COMPLETED :
3207 	respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3208 		    "Job #%d is already completed - can\'t cancel.", job->id);
3209         break;
3210 
3211     default :
3212        /*
3213         * Cancel the job...
3214 	*/
3215 
3216 	_cupsRWLockWrite(&(client->printer->rwlock));
3217 
3218 	if (job->state == IPP_JSTATE_PROCESSING ||
3219 	    (job->state == IPP_JSTATE_HELD && job->fd >= 0))
3220           job->cancel = 1;
3221 	else
3222 	{
3223 	  job->state     = IPP_JSTATE_CANCELED;
3224 	  job->completed = time(NULL);
3225 	}
3226 
3227 	_cupsRWUnlock(&(client->printer->rwlock));
3228 
3229 	respond_ipp(client, IPP_STATUS_OK, NULL);
3230         break;
3231   }
3232 }
3233 
3234 
3235 /*
3236  * 'ipp_cancel_my_jobs()' - Cancel all jobs.
3237  *
3238  * Note: Since ippeveprinter doesn't do spooling, this really just cancels the
3239  * current job.
3240  */
3241 
3242 static void
ipp_cancel_my_jobs(ippeve_client_t * client)3243 ipp_cancel_my_jobs(
3244     ippeve_client_t *client)		/* I - Client */
3245 {
3246   ippeve_job_t		*job;		/* Job information */
3247 
3248 
3249   _cupsRWLockWrite(&client->printer->rwlock);
3250 
3251   if ((job = client->printer->active_job) != NULL)
3252   {
3253    /*
3254     * See if the job is already completed, canceled, or aborted; if so,
3255     * we can't cancel...
3256     */
3257 
3258     if (job->state < IPP_JSTATE_CANCELED)
3259     {
3260      /*
3261       * Cancel the job...
3262       */
3263 
3264       if (job->state == IPP_JSTATE_PROCESSING || (job->state == IPP_JSTATE_HELD && job->fd >= 0))
3265       {
3266 	job->cancel = 1;
3267       }
3268       else
3269       {
3270 	job->state     = IPP_JSTATE_CANCELED;
3271 	job->completed = time(NULL);
3272       }
3273     }
3274   }
3275 
3276   respond_ipp(client, IPP_STATUS_OK, NULL);
3277 
3278   _cupsRWUnlock(&client->printer->rwlock);
3279 }
3280 
3281 
3282 /*
3283  * 'ipp_close_job()' - Close an open job.
3284  */
3285 
3286 static void
ipp_close_job(ippeve_client_t * client)3287 ipp_close_job(ippeve_client_t *client)	/* I - Client */
3288 {
3289   ippeve_job_t		*job;		/* Job information */
3290 
3291 
3292  /*
3293   * Get the job...
3294   */
3295 
3296   if ((job = find_job(client)) == NULL)
3297   {
3298     respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3299     return;
3300   }
3301 
3302  /*
3303   * See if the job is already completed, canceled, or aborted; if so,
3304   * we can't cancel...
3305   */
3306 
3307   switch (job->state)
3308   {
3309     case IPP_JSTATE_CANCELED :
3310 	respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3311 		    "Job #%d is canceled - can\'t close.", job->id);
3312         break;
3313 
3314     case IPP_JSTATE_ABORTED :
3315 	respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3316 		    "Job #%d is aborted - can\'t close.", job->id);
3317         break;
3318 
3319     case IPP_JSTATE_COMPLETED :
3320 	respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3321 		    "Job #%d is completed - can\'t close.", job->id);
3322         break;
3323 
3324     case IPP_JSTATE_PROCESSING :
3325     case IPP_JSTATE_STOPPED :
3326 	respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3327 		    "Job #%d is already closed.", job->id);
3328         break;
3329 
3330     default :
3331 	respond_ipp(client, IPP_STATUS_OK, NULL);
3332         break;
3333   }
3334 }
3335 
3336 
3337 /*
3338  * 'ipp_create_job()' - Create a job object.
3339  */
3340 
3341 static void
ipp_create_job(ippeve_client_t * client)3342 ipp_create_job(ippeve_client_t *client)	/* I - Client */
3343 {
3344   ippeve_job_t		*job;		/* New job */
3345   cups_array_t		*ra;		/* Attributes to send in response */
3346 
3347 
3348  /*
3349   * Do we have a file to print?
3350   */
3351 
3352   if (have_document_data(client))
3353   {
3354     flush_document_data(client);
3355     respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3356                 "Unexpected document data following request.");
3357     return;
3358   }
3359 
3360  /*
3361   * Validate print job attributes...
3362   */
3363 
3364   if (!valid_job_attributes(client))
3365     return;
3366 
3367  /*
3368   * Create the job...
3369   */
3370 
3371   if ((job = create_job(client)) == NULL)
3372   {
3373     respond_ipp(client, IPP_STATUS_ERROR_BUSY,
3374                 "Currently printing another job.");
3375     return;
3376   }
3377 
3378  /*
3379   * Return the job info...
3380   */
3381 
3382   respond_ipp(client, IPP_STATUS_OK, NULL);
3383 
3384   ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3385   cupsArrayAdd(ra, "job-id");
3386   cupsArrayAdd(ra, "job-state");
3387   cupsArrayAdd(ra, "job-state-message");
3388   cupsArrayAdd(ra, "job-state-reasons");
3389   cupsArrayAdd(ra, "job-uri");
3390 
3391   copy_job_attributes(client, job, ra);
3392   cupsArrayDelete(ra);
3393 }
3394 
3395 
3396 /*
3397  * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3398  */
3399 
3400 static void
ipp_get_job_attributes(ippeve_client_t * client)3401 ipp_get_job_attributes(
3402     ippeve_client_t *client)		/* I - Client */
3403 {
3404   ippeve_job_t	*job;			/* Job */
3405   cups_array_t	*ra;			/* requested-attributes */
3406 
3407 
3408   if ((job = find_job(client)) == NULL)
3409   {
3410     respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job not found.");
3411     return;
3412   }
3413 
3414   respond_ipp(client, IPP_STATUS_OK, NULL);
3415 
3416   ra = ippCreateRequestedArray(client->request);
3417   copy_job_attributes(client, job, ra);
3418   cupsArrayDelete(ra);
3419 }
3420 
3421 
3422 /*
3423  * 'ipp_get_jobs()' - Get a list of job objects.
3424  */
3425 
3426 static void
ipp_get_jobs(ippeve_client_t * client)3427 ipp_get_jobs(ippeve_client_t *client)	/* I - Client */
3428 {
3429   ipp_attribute_t	*attr;		/* Current attribute */
3430   const char		*which_jobs = NULL;
3431 					/* which-jobs values */
3432   int			job_comparison;	/* Job comparison */
3433   ipp_jstate_t		job_state;	/* job-state value */
3434   int			first_job_id,	/* First job ID */
3435 			limit,		/* Maximum number of jobs to return */
3436 			count;		/* Number of jobs that match */
3437   const char		*username;	/* Username */
3438   ippeve_job_t		*job;		/* Current job pointer */
3439   cups_array_t		*ra;		/* Requested attributes array */
3440 
3441 
3442  /*
3443   * See if the "which-jobs" attribute have been specified...
3444   */
3445 
3446   if ((attr = ippFindAttribute(client->request, "which-jobs",
3447                                IPP_TAG_KEYWORD)) != NULL)
3448   {
3449     which_jobs = ippGetString(attr, 0, NULL);
3450     fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->hostname, which_jobs);
3451   }
3452 
3453   if (!which_jobs || !strcmp(which_jobs, "not-completed"))
3454   {
3455     job_comparison = -1;
3456     job_state      = IPP_JSTATE_STOPPED;
3457   }
3458   else if (!strcmp(which_jobs, "completed"))
3459   {
3460     job_comparison = 1;
3461     job_state      = IPP_JSTATE_CANCELED;
3462   }
3463   else if (!strcmp(which_jobs, "aborted"))
3464   {
3465     job_comparison = 0;
3466     job_state      = IPP_JSTATE_ABORTED;
3467   }
3468   else if (!strcmp(which_jobs, "all"))
3469   {
3470     job_comparison = 1;
3471     job_state      = IPP_JSTATE_PENDING;
3472   }
3473   else if (!strcmp(which_jobs, "canceled"))
3474   {
3475     job_comparison = 0;
3476     job_state      = IPP_JSTATE_CANCELED;
3477   }
3478   else if (!strcmp(which_jobs, "pending"))
3479   {
3480     job_comparison = 0;
3481     job_state      = IPP_JSTATE_PENDING;
3482   }
3483   else if (!strcmp(which_jobs, "pending-held"))
3484   {
3485     job_comparison = 0;
3486     job_state      = IPP_JSTATE_HELD;
3487   }
3488   else if (!strcmp(which_jobs, "processing"))
3489   {
3490     job_comparison = 0;
3491     job_state      = IPP_JSTATE_PROCESSING;
3492   }
3493   else if (!strcmp(which_jobs, "processing-stopped"))
3494   {
3495     job_comparison = 0;
3496     job_state      = IPP_JSTATE_STOPPED;
3497   }
3498   else
3499   {
3500     respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
3501                 "The which-jobs value \"%s\" is not supported.", which_jobs);
3502     ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
3503                  "which-jobs", NULL, which_jobs);
3504     return;
3505   }
3506 
3507  /*
3508   * See if they want to limit the number of jobs reported...
3509   */
3510 
3511   if ((attr = ippFindAttribute(client->request, "limit",
3512                                IPP_TAG_INTEGER)) != NULL)
3513   {
3514     limit = ippGetInteger(attr, 0);
3515 
3516     fprintf(stderr, "%s Get-Jobs limit=%d", client->hostname, limit);
3517   }
3518   else
3519     limit = 0;
3520 
3521   if ((attr = ippFindAttribute(client->request, "first-job-id",
3522                                IPP_TAG_INTEGER)) != NULL)
3523   {
3524     first_job_id = ippGetInteger(attr, 0);
3525 
3526     fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->hostname, first_job_id);
3527   }
3528   else
3529     first_job_id = 1;
3530 
3531  /*
3532   * See if we only want to see jobs for a specific user...
3533   */
3534 
3535   username = NULL;
3536 
3537   if ((attr = ippFindAttribute(client->request, "my-jobs",
3538                                IPP_TAG_BOOLEAN)) != NULL)
3539   {
3540     int my_jobs = ippGetBoolean(attr, 0);
3541 
3542     fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->hostname, my_jobs ? "true" : "false");
3543 
3544     if (my_jobs)
3545     {
3546       if ((attr = ippFindAttribute(client->request, "requesting-user-name",
3547 					IPP_TAG_NAME)) == NULL)
3548       {
3549 	respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3550 	            "Need requesting-user-name with my-jobs.");
3551 	return;
3552       }
3553 
3554       username = ippGetString(attr, 0, NULL);
3555 
3556       fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n", client->hostname, username);
3557     }
3558   }
3559 
3560  /*
3561   * OK, build a list of jobs for this printer...
3562   */
3563 
3564   ra = ippCreateRequestedArray(client->request);
3565 
3566   respond_ipp(client, IPP_STATUS_OK, NULL);
3567 
3568   _cupsRWLockRead(&(client->printer->rwlock));
3569 
3570   for (count = 0, job = (ippeve_job_t *)cupsArrayFirst(client->printer->jobs);
3571        (limit <= 0 || count < limit) && job;
3572        job = (ippeve_job_t *)cupsArrayNext(client->printer->jobs))
3573   {
3574    /*
3575     * Filter out jobs that don't match...
3576     */
3577 
3578     if ((job_comparison < 0 && job->state > job_state) ||
3579 	(job_comparison == 0 && job->state != job_state) ||
3580 	(job_comparison > 0 && job->state < job_state) ||
3581 	job->id < first_job_id ||
3582 	(username && job->username &&
3583 	 strcasecmp(username, job->username)))
3584       continue;
3585 
3586     if (count > 0)
3587       ippAddSeparator(client->response);
3588 
3589     count ++;
3590     copy_job_attributes(client, job, ra);
3591   }
3592 
3593   cupsArrayDelete(ra);
3594 
3595   _cupsRWUnlock(&(client->printer->rwlock));
3596 }
3597 
3598 
3599 /*
3600  * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3601  */
3602 
3603 static void
ipp_get_printer_attributes(ippeve_client_t * client)3604 ipp_get_printer_attributes(
3605     ippeve_client_t *client)		/* I - Client */
3606 {
3607   cups_array_t		*ra;		/* Requested attributes array */
3608   ippeve_printer_t	*printer;	/* Printer */
3609 
3610 
3611  /*
3612   * Send the attributes...
3613   */
3614 
3615   ra      = ippCreateRequestedArray(client->request);
3616   printer = client->printer;
3617 
3618   respond_ipp(client, IPP_STATUS_OK, NULL);
3619 
3620   _cupsRWLockRead(&(printer->rwlock));
3621 
3622   copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
3623 		  IPP_TAG_CUPS_CONST);
3624 
3625   if (!ra || cupsArrayFind(ra, "printer-config-change-date-time"))
3626     ippAddDate(client->response, IPP_TAG_PRINTER, "printer-config-change-date-time", ippTimeToDate(printer->config_time));
3627 
3628   if (!ra || cupsArrayFind(ra, "printer-config-change-time"))
3629     ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-config-change-time", (int)(printer->config_time - printer->start_time));
3630 
3631   if (!ra || cupsArrayFind(ra, "printer-current-time"))
3632     ippAddDate(client->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(time(NULL)));
3633 
3634   if (!ra || cupsArrayFind(ra, "printer-icons"))
3635   {
3636     char	uris[3][1024];		/* Buffers for URIs */
3637     const char	*values[3];		/* Values for attribute */
3638 
3639     httpAssembleURI(HTTP_URI_CODING_ALL, uris[0], sizeof(uris[0]), WEB_SCHEME, NULL, client->host_field, client->host_port, "/icon-sm.png");
3640     httpAssembleURI(HTTP_URI_CODING_ALL, uris[1], sizeof(uris[1]), WEB_SCHEME, NULL, client->host_field, client->host_port, "/icon.png");
3641     httpAssembleURI(HTTP_URI_CODING_ALL, uris[2], sizeof(uris[2]), WEB_SCHEME, NULL, client->host_field, client->host_port, "/icon-lg.png");
3642 
3643     values[0] = uris[0];
3644     values[1] = uris[1];
3645     values[2] = uris[2];
3646 
3647     ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons", 3, NULL, values);
3648   }
3649 
3650   if (!ra || cupsArrayFind(ra, "printer-more-info"))
3651   {
3652     char	uri[1024];		/* URI value */
3653 
3654     httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), WEB_SCHEME, NULL, client->host_field, client->host_port, "/");
3655     ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info", NULL, uri);
3656   }
3657 
3658   if (!ra || cupsArrayFind(ra, "printer-state"))
3659     ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state);
3660 
3661   if (!ra || cupsArrayFind(ra, "printer-state-change-date-time"))
3662     ippAddDate(client->response, IPP_TAG_PRINTER, "printer-state-change-date-time", ippTimeToDate(printer->state_time));
3663 
3664   if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
3665     ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-state-change-time", (int)(printer->state_time - printer->start_time));
3666 
3667   if (!ra || cupsArrayFind(ra, "printer-state-message"))
3668   {
3669     static const char * const messages[] = { "Idle.", "Printing.", "Stopped." };
3670 
3671     ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-state-message", NULL, messages[printer->state - IPP_PSTATE_IDLE]);
3672   }
3673 
3674   if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
3675   {
3676     if (printer->state_reasons == IPPEVE_PREASON_NONE)
3677     {
3678       ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-state-reasons", NULL, "none");
3679     }
3680     else
3681     {
3682       ipp_attribute_t	*attr = NULL;		/* printer-state-reasons */
3683       ippeve_preason_t	bit;			/* Reason bit */
3684       int		i;			/* Looping var */
3685       char		reason[32];		/* Reason string */
3686 
3687       for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
3688       {
3689         if (printer->state_reasons & bit)
3690 	{
3691 	  snprintf(reason, sizeof(reason), "%s-%s", ippeve_preason_strings[i], printer->state == IPP_PSTATE_IDLE ? "report" : printer->state == IPP_PSTATE_PROCESSING ? "warning" : "error");
3692 	  if (attr)
3693 	    ippSetString(client->response, &attr, ippGetCount(attr), reason);
3694 	  else
3695 	    attr = ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "printer-state-reasons", NULL, reason);
3696 	}
3697       }
3698     }
3699   }
3700 
3701   if (!ra || cupsArrayFind(ra, "printer-strings-uri"))
3702   {
3703     char	uri[1024];		/* URI value */
3704 
3705     httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), WEB_SCHEME, NULL, client->host_field, client->host_port, "/en.strings");
3706     ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-strings-uri", NULL, uri);
3707   }
3708 
3709   if (!ra || cupsArrayFind(ra, "printer-supply-info-uri"))
3710   {
3711     char	uri[1024];		/* URI value */
3712 
3713     httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), WEB_SCHEME, NULL, client->host_field, client->host_port, "/supplies");
3714     ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-supply-info-uri", NULL, uri);
3715   }
3716 
3717   if (!ra || cupsArrayFind(ra, "printer-up-time"))
3718     ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-up-time", (int)(time(NULL) - printer->start_time));
3719 
3720   if (!ra || cupsArrayFind(ra, "printer-uri-supported"))
3721   {
3722     char	uris[2][1024];		/* Buffers for URIs */
3723     const char	*values[2];		/* Values for attribute */
3724     int		num_values = 0;		/* Number of values */
3725 
3726     httpAssembleURI(HTTP_URI_CODING_ALL, uris[0], sizeof(uris[0]), "ipp", NULL, client->host_field, client->host_port, "/ipp/print");
3727     values[num_values ++] = uris[0];
3728 
3729 #ifdef HAVE_TLS
3730     httpAssembleURI(HTTP_URI_CODING_ALL, uris[1], sizeof(uris[1]), "ipps", NULL, client->host_field, client->host_port, "/ipp/print");
3731     values[num_values ++] = uris[1];
3732 #endif /* HAVE_TLS */
3733 
3734     ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", num_values, NULL, values);
3735   }
3736 
3737   if (!ra || cupsArrayFind(ra, "queued-job-count"))
3738     ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "queued-job-count", printer->active_job && printer->active_job->state < IPP_JSTATE_CANCELED);
3739 
3740   _cupsRWUnlock(&(printer->rwlock));
3741 
3742   cupsArrayDelete(ra);
3743 }
3744 
3745 
3746 /*
3747  * 'ipp_identify_printer()' - Beep or display a message.
3748  */
3749 
3750 static void
ipp_identify_printer(ippeve_client_t * client)3751 ipp_identify_printer(
3752     ippeve_client_t *client)		/* I - Client */
3753 {
3754   ipp_attribute_t	*actions,	/* identify-actions */
3755 			*message;	/* message */
3756 
3757 
3758   actions = ippFindAttribute(client->request, "identify-actions", IPP_TAG_KEYWORD);
3759   message = ippFindAttribute(client->request, "message", IPP_TAG_TEXT);
3760 
3761   if (!actions || ippContainsString(actions, "sound"))
3762   {
3763 #ifdef __APPLE__
3764     pid_t	pid;			/* Process ID for "afplay" utility */
3765     static const char * const afplay[3] =
3766     {					/* Arguments for "afplay" utility */
3767       "/usr/bin/afplay",
3768       "/System/Library/Sounds/Ping.aiff",
3769       NULL
3770     };
3771 
3772     posix_spawn(&pid, afplay[0], NULL, NULL, (char **)afplay, NULL);
3773 
3774 #else
3775     putchar(0x07);
3776     fflush(stdout);
3777 #endif /* __APPLE__ */
3778   }
3779 
3780   if (ippContainsString(actions, "display"))
3781     printf("IDENTIFY from %s: %s\n", client->hostname, message ? ippGetString(message, 0, NULL) : "No message supplied");
3782 
3783   respond_ipp(client, IPP_STATUS_OK, NULL);
3784 }
3785 
3786 
3787 /*
3788  * 'ipp_print_job()' - Create a job object with an attached document.
3789  */
3790 
3791 static void
ipp_print_job(ippeve_client_t * client)3792 ipp_print_job(ippeve_client_t *client)	/* I - Client */
3793 {
3794   ippeve_job_t		*job;		/* New job */
3795 
3796 
3797  /*
3798   * Validate print job attributes...
3799   */
3800 
3801   if (!valid_job_attributes(client))
3802   {
3803     flush_document_data(client);
3804     return;
3805   }
3806 
3807  /*
3808   * Do we have a file to print?
3809   */
3810 
3811   if (!have_document_data(client))
3812   {
3813     respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No file in request.");
3814     return;
3815   }
3816 
3817  /*
3818   * Create the job...
3819   */
3820 
3821   if ((job = create_job(client)) == NULL)
3822   {
3823     respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job.");
3824     return;
3825   }
3826 
3827  /*
3828   * Then finish getting the document data and process things...
3829   */
3830 
3831   finish_document_data(client, job);
3832 }
3833 
3834 
3835 /*
3836  * 'ipp_print_uri()' - Create a job object with a referenced document.
3837  */
3838 
3839 static void
ipp_print_uri(ippeve_client_t * client)3840 ipp_print_uri(ippeve_client_t *client)	/* I - Client */
3841 {
3842   ippeve_job_t		*job;		/* New job */
3843 
3844 
3845  /*
3846   * Validate print job attributes...
3847   */
3848 
3849   if (!valid_job_attributes(client))
3850   {
3851     flush_document_data(client);
3852     return;
3853   }
3854 
3855  /*
3856   * Create the job...
3857   */
3858 
3859   if ((job = create_job(client)) == NULL)
3860   {
3861     respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job.");
3862     return;
3863   }
3864 
3865  /*
3866   * Then finish getting the document data and process things...
3867   */
3868 
3869   finish_document_uri(client, job);
3870 }
3871 
3872 
3873 /*
3874  * 'ipp_send_document()' - Add an attached document to a job object created with
3875  *                         Create-Job.
3876  */
3877 
3878 static void
ipp_send_document(ippeve_client_t * client)3879 ipp_send_document(
3880     ippeve_client_t *client)		/* I - Client */
3881 {
3882   ippeve_job_t		*job;		/* Job information */
3883   ipp_attribute_t	*attr;		/* Current attribute */
3884   int			have_data;	/* Have document data? */
3885 
3886 
3887  /*
3888   * Get the job...
3889   */
3890 
3891   if ((job = find_job(client)) == NULL)
3892   {
3893     flush_document_data(client);
3894     respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3895     return;
3896   }
3897 
3898  /*
3899   * See if we already have a document for this job or the job has already
3900   * in a terminating state...
3901   */
3902 
3903   have_data = have_document_data(client);
3904 
3905   if ((job->filename || job->fd >= 0) && have_data)
3906   {
3907     flush_document_data(client);
3908     respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported.");
3909     return;
3910   }
3911   else if (job->state > IPP_JSTATE_HELD && have_data)
3912   {
3913     flush_document_data(client);
3914     respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state.");
3915     return;
3916   }
3917 
3918  /*
3919   * Make sure we have the "last-document" operation attribute...
3920   */
3921 
3922   if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL)
3923   {
3924     flush_document_data(client);
3925     respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute.");
3926     return;
3927   }
3928   else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION)
3929   {
3930     flush_document_data(client);
3931     respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group.");
3932     return;
3933   }
3934   else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1)
3935   {
3936     flush_document_data(client);
3937     respond_unsupported(client, attr);
3938     return;
3939   }
3940 
3941  /*
3942   * Validate document attributes...
3943   */
3944 
3945   if (have_data && !valid_doc_attributes(client))
3946   {
3947     flush_document_data(client);
3948     return;
3949   }
3950 
3951   if (!have_data && !job->filename)
3952     job->state = IPP_JSTATE_ABORTED;
3953 
3954  /*
3955   * Then finish getting the document data and process things...
3956   */
3957 
3958   _cupsRWLockWrite(&(client->printer->rwlock));
3959 
3960   copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
3961 
3962   if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
3963     job->format = ippGetString(attr, 0, NULL);
3964   else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
3965     job->format = ippGetString(attr, 0, NULL);
3966   else
3967     job->format = "application/octet-stream";
3968 
3969   _cupsRWUnlock(&(client->printer->rwlock));
3970 
3971   if (have_data)
3972     finish_document_data(client, job);
3973 }
3974 
3975 
3976 /*
3977  * 'ipp_send_uri()' - Add a referenced document to a job object created with
3978  *                    Create-Job.
3979  */
3980 
3981 static void
ipp_send_uri(ippeve_client_t * client)3982 ipp_send_uri(ippeve_client_t *client)	/* I - Client */
3983 {
3984   ippeve_job_t		*job;		/* Job information */
3985   ipp_attribute_t	*attr;		/* Current attribute */
3986 
3987 
3988  /*
3989   * Get the job...
3990   */
3991 
3992   if ((job = find_job(client)) == NULL)
3993   {
3994     respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3995     return;
3996   }
3997 
3998  /*
3999   * See if we already have a document for this job or the job has already
4000   * in a non-terminating state...
4001   */
4002 
4003   if (job->filename || job->fd >= 0)
4004   {
4005     respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported.");
4006     return;
4007   }
4008   else if (job->state > IPP_JSTATE_HELD)
4009   {
4010     flush_document_data(client);
4011     respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state.");
4012     return;
4013   }
4014 
4015   if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL)
4016   {
4017     respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute.");
4018     return;
4019   }
4020   else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION)
4021   {
4022     respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group.");
4023     return;
4024   }
4025   else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1)
4026   {
4027     respond_unsupported(client, attr);
4028     return;
4029   }
4030 
4031  /*
4032   * Validate document attributes...
4033   */
4034 
4035   if (!valid_doc_attributes(client))
4036   {
4037     flush_document_data(client);
4038     return;
4039   }
4040 
4041  /*
4042   * Then finish getting the document data and process things...
4043   */
4044 
4045   _cupsRWLockWrite(&(client->printer->rwlock));
4046 
4047   copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
4048 
4049   if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
4050     job->format = ippGetString(attr, 0, NULL);
4051   else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
4052     job->format = ippGetString(attr, 0, NULL);
4053   else
4054     job->format = "application/octet-stream";
4055 
4056   _cupsRWUnlock(&(client->printer->rwlock));
4057 
4058   finish_document_uri(client, job);
4059 }
4060 
4061 
4062 /*
4063  * 'ipp_validate_job()' - Validate job creation attributes.
4064  */
4065 
4066 static void
ipp_validate_job(ippeve_client_t * client)4067 ipp_validate_job(ippeve_client_t *client)	/* I - Client */
4068 {
4069   if (valid_job_attributes(client))
4070     respond_ipp(client, IPP_STATUS_OK, NULL);
4071 }
4072 
4073 
4074 /*
4075  * 'ippserver_attr_cb()' - Determine whether an attribute should be loaded.
4076  */
4077 
4078 static int				/* O - 1 to use, 0 to ignore */
ippserver_attr_cb(_ipp_file_t * f,void * user_data,const char * attr)4079 ippserver_attr_cb(
4080     _ipp_file_t    *f,			/* I - IPP file */
4081     void           *user_data,		/* I - User data pointer (unused) */
4082     const char     *attr)		/* I - Attribute name */
4083 {
4084   int		i,			/* Current element */
4085 		result;			/* Result of comparison */
4086   static const char * const ignored[] =
4087   {					/* Ignored attributes */
4088     "attributes-charset",
4089     "attributes-natural-language",
4090     "charset-configured",
4091     "charset-supported",
4092     "device-service-count",
4093     "device-uuid",
4094     "document-format-varying-attributes",
4095     "generated-natural-language-supported",
4096     "identify-actions-default",
4097     "identify-actions-supported",
4098     "ipp-features-supported",
4099     "ipp-versions-supported",
4100     "ippget-event-life",
4101     "job-creation-attributes-supported",
4102     "job-hold-until-supported",
4103     "job-hold-until-time-supported",
4104     "job-ids-supported",
4105     "job-k-octets-supported",
4106     "job-priority-default",
4107     "job-priority-supported",
4108     "job-settable-attributes-supported",
4109     "job-sheets-default",
4110     "job-sheets-supported",
4111     "multiple-document-jobs-supported",
4112     "multiple-document-handling-supported",
4113     "multiple-operation-time-out",
4114     "multiple-operation-time-out-action",
4115     "natural-language-configured",
4116     "notify-attributes-supported",
4117     "notify-events-default",
4118     "notify-events-supported",
4119     "notify-lease-duration-default",
4120     "notify-lease-duration-supported",
4121     "notify-max-events-supported",
4122     "notify-pull-method-supported",
4123     "operations-supported",
4124     "pdl-override-supported",
4125     "printer-alert",
4126     "printer-alert-description",
4127     "printer-camera-image-uri",
4128     "printer-charge-info",
4129     "printer-charge-info-uri",
4130     "printer-config-change-date-time",
4131     "printer-config-change-time",
4132     "printer-current-time",
4133     "printer-detailed-status-messages",
4134     "printer-dns-sd-name",
4135     "printer-fax-log-uri",
4136     "printer-geo-location",
4137     "printer-get-attributes-supported",
4138     "printer-icons",
4139     "printer-id",
4140     "printer-info",
4141     "printer-is-accepting-jobs",
4142     "printer-location",
4143     "printer-message-date-time",
4144     "printer-message-from-operator",
4145     "printer-message-time",
4146     "printer-more-info",
4147     "printer-name",
4148     "printer-organization",
4149     "printer-organizational-unit",
4150     "printer-service-type",
4151     "printer-settable-attributes-supported",
4152     "printer-state",
4153     "printer-state-change-date-time",
4154     "printer-state-change-time",
4155     "printer-state-message",
4156     "printer-state-reasons",
4157     "printer-static-resource-directory-uri",
4158     "printer-static-resource-k-octets-free",
4159     "printer-static-resource-k-octets-supported",
4160     "printer-strings-languages-supported",
4161     "printer-strings-uri",
4162     "printer-supply-info-uri",
4163     "printer-up-time",
4164     "printer-uri-supported",
4165     "printer-uuid",
4166     "printer-xri-supported",
4167     "queued-job-count",
4168     "reference-uri-scheme-supported",
4169     "uri-authentication-supported",
4170     "uri-security-supported",
4171     "which-jobs-supported",
4172     "xri-authentication-supported",
4173     "xri-security-supported",
4174     "xri-uri-scheme-supported"
4175   };
4176 
4177 
4178   (void)f;
4179   (void)user_data;
4180 
4181   for (i = 0, result = 1; i < (int)(sizeof(ignored) / sizeof(ignored[0])); i ++)
4182   {
4183     if ((result = strcmp(attr, ignored[i])) <= 0)
4184       break;
4185   }
4186 
4187   return (result != 0);
4188 }
4189 
4190 
4191 /*
4192  * 'ippserver_error_cb()' - Log an error message.
4193  */
4194 
4195 static int				/* O - 1 to continue, 0 to stop */
ippserver_error_cb(_ipp_file_t * f,void * user_data,const char * error)4196 ippserver_error_cb(
4197     _ipp_file_t    *f,			/* I - IPP file data */
4198     void           *user_data,		/* I - User data pointer (unused) */
4199     const char     *error)		/* I - Error message */
4200 {
4201   (void)f;
4202   (void)user_data;
4203 
4204   _cupsLangPrintf(stderr, "%s\n", error);
4205 
4206   return (1);
4207 }
4208 
4209 
4210 /*
4211  * 'ippserver_token_cb()' - Process ippserver-specific config file tokens.
4212  */
4213 
4214 static int				/* O - 1 to continue, 0 to stop */
ippserver_token_cb(_ipp_file_t * f,_ipp_vars_t * vars,void * user_data,const char * token)4215 ippserver_token_cb(
4216     _ipp_file_t    *f,			/* I - IPP file data */
4217     _ipp_vars_t    *vars,		/* I - IPP variables */
4218     void           *user_data,		/* I - User data pointer (unused) */
4219     const char     *token)		/* I - Current token */
4220 {
4221   (void)vars;
4222   (void)user_data;
4223 
4224   if (!token)
4225   {
4226    /*
4227     * NULL token means do the initial setup - create an empty IPP message and
4228     * return...
4229     */
4230 
4231     f->attrs     = ippNew();
4232     f->group_tag = IPP_TAG_PRINTER;
4233   }
4234   else
4235   {
4236     _cupsLangPrintf(stderr, _("Unknown directive \"%s\" on line %d of \"%s\" ignored."), token, f->linenum, f->filename);
4237   }
4238 
4239   return (1);
4240 }
4241 
4242 
4243 /*
4244  * 'load_ippserver_attributes()' - Load IPP attributes from an ippserver file.
4245  */
4246 
4247 static ipp_t *				/* O - IPP attributes or `NULL` on error */
load_ippserver_attributes(const char * servername,int serverport,const char * filename,cups_array_t * docformats)4248 load_ippserver_attributes(
4249     const char   *servername,		/* I - Server name or `NULL` for default */
4250     int          serverport,		/* I - Server port number */
4251     const char   *filename,		/* I - ippserver attribute filename */
4252     cups_array_t *docformats)		/* I - document-format-supported values */
4253 {
4254   ipp_t		*attrs;			/* IPP attributes */
4255   _ipp_vars_t	vars;			/* IPP variables */
4256   char		temp[256];		/* Temporary string */
4257 
4258 
4259   (void)docformats; /* for now */
4260 
4261  /*
4262   * Setup callbacks and variables for the printer configuration file...
4263   *
4264   * The following additional variables are supported:
4265   *
4266   * - SERVERNAME: The host name of the server.
4267   * - SERVERPORT: The default port of the server.
4268   */
4269 
4270   _ippVarsInit(&vars, (_ipp_fattr_cb_t)ippserver_attr_cb, (_ipp_ferror_cb_t)ippserver_error_cb, (_ipp_ftoken_cb_t)ippserver_token_cb);
4271 
4272   if (servername)
4273   {
4274     _ippVarsSet(&vars, "SERVERNAME", servername);
4275   }
4276   else
4277   {
4278     httpGetHostname(NULL, temp, sizeof(temp));
4279     _ippVarsSet(&vars, "SERVERNAME", temp);
4280   }
4281 
4282   snprintf(temp, sizeof(temp), "%d", serverport);
4283   _ippVarsSet(&vars, "SERVERPORT", temp);
4284 
4285  /*
4286   * Load attributes and values for the printer...
4287   */
4288 
4289   attrs = _ippFileParse(&vars, filename, NULL);
4290 
4291  /*
4292   * Free memory and return...
4293   */
4294 
4295   _ippVarsDeinit(&vars);
4296 
4297   return (attrs);
4298 }
4299 
4300 
4301 /*
4302  * 'load_legacy_attributes()' - Load IPP attributes using the old ippserver
4303  *                              options.
4304  */
4305 
4306 static ipp_t *				/* O - IPP attributes or `NULL` on error */
load_legacy_attributes(const char * make,const char * model,int ppm,int ppm_color,int duplex,cups_array_t * docformats)4307 load_legacy_attributes(
4308     const char   *make,			/* I - Manufacturer name */
4309     const char   *model,		/* I - Model name */
4310     int          ppm,			/* I - pages-per-minute */
4311     int          ppm_color,		/* I - pages-per-minute-color */
4312     int          duplex,		/* I - Duplex support? */
4313     cups_array_t *docformats)		/* I - document-format-supported values */
4314 {
4315   int			i;		/* Looping var */
4316   ipp_t			*attrs,		/* IPP attributes */
4317 			*col;		/* Collection value */
4318   ipp_attribute_t	*attr;		/* Current attribute */
4319   char			device_id[1024],/* printer-device-id */
4320 			*ptr,		/* Pointer into device ID */
4321 			make_model[128];/* printer-make-and-model */
4322   const char		*format,	/* Current document format */
4323 			*prefix;	/* Prefix for device ID */
4324   int			num_media;	/* Number of media */
4325   const char * const	*media;		/* List of media */
4326   int			num_ready;	/* Number of loaded media */
4327   const char * const	*ready;		/* List of loaded media */
4328   pwg_media_t		*pwg;		/* PWG media size information */
4329   static const char * const media_supported[] =
4330   {					/* media-supported values */
4331     "na_letter_8.5x11in",		/* Letter */
4332     "na_legal_8.5x14in",		/* Legal */
4333     "iso_a4_210x297mm",			/* A4 */
4334     "na_number-10_4.125x9.5in",		/* #10 Envelope */
4335     "iso_dl_110x220mm"			/* DL Envelope */
4336   };
4337   static const char * const media_supported_color[] =
4338   {					/* media-supported values */
4339     "na_letter_8.5x11in",		/* Letter */
4340     "na_legal_8.5x14in",		/* Legal */
4341     "iso_a4_210x297mm",			/* A4 */
4342     "na_number-10_4.125x9.5in",		/* #10 Envelope */
4343     "iso_dl_110x220mm",			/* DL Envelope */
4344     "na_index-3x5_3x5in",		/* Photo 3x5 */
4345     "oe_photo-l_3.5x5in",		/* Photo L */
4346     "na_index-4x6_4x6in",		/* Photo 4x6 */
4347     "iso_a6_105x148mm",			/* A6 */
4348     "na_5x7_5x7in",			/* Photo 5x7 aka 2L */
4349     "iso_a5_148x210mm",			/* A5 */
4350   };
4351   static const char * const media_ready[] =
4352   {					/* media-ready values */
4353     "na_letter_8.5x11in",		/* Letter */
4354     "na_number-10_4.125x9.5in"		/* #10 */
4355   };
4356   static const char * const media_ready_color[] =
4357   {					/* media-ready values */
4358     "na_letter_8.5x11in",		/* Letter */
4359     "na_index-4x6_4x6in"		/* Photo 4x6 */
4360   };
4361   static const char * const media_source_supported[] =
4362   {					/* media-source-supported values */
4363     "auto",
4364     "main",
4365     "manual",
4366     "by-pass-tray"			/* AKA multi-purpose tray */
4367   };
4368   static const char * const media_source_supported_color[] =
4369   {					/* media-source-supported values */
4370     "auto",
4371     "main",
4372     "photo"
4373   };
4374   static const char * const media_type_supported[] =
4375   {					/* media-type-supported values */
4376     "auto",
4377     "cardstock",
4378     "envelope",
4379     "labels",
4380     "other",
4381     "stationery",
4382     "stationery-letterhead",
4383     "transparency"
4384   };
4385   static const char * const media_type_supported_color[] =
4386   {					/* media-type-supported values */
4387     "auto",
4388     "cardstock",
4389     "envelope",
4390     "labels",
4391     "other",
4392     "stationery",
4393     "stationery-letterhead",
4394     "transparency",
4395     "photographic-glossy",
4396     "photographic-high-gloss",
4397     "photographic-matte",
4398     "photographic-satin",
4399     "photographic-semi-gloss"
4400   };
4401   static const int	media_bottom_margin_supported[] =
4402   {					/* media-bottom-margin-supported values */
4403     635					/* 1/4" */
4404   };
4405   static const int	media_bottom_margin_supported_color[] =
4406   {					/* media-bottom/top-margin-supported values */
4407     0,					/* Borderless */
4408     1168				/* 0.46" (common HP inkjet bottom margin) */
4409   };
4410   static const int	media_lr_margin_supported[] =
4411   {					/* media-left/right-margin-supported values */
4412     340,				/* 3.4mm (historical HP PCL A4 margin) */
4413     635					/* 1/4" */
4414   };
4415   static const int	media_lr_margin_supported_color[] =
4416   {					/* media-left/right-margin-supported values */
4417     0,					/* Borderless */
4418     340,				/* 3.4mm (historical HP PCL A4 margin) */
4419     635					/* 1/4" */
4420   };
4421   static const int	media_top_margin_supported[] =
4422   {					/* media-top-margin-supported values */
4423     635					/* 1/4" */
4424   };
4425   static const int	media_top_margin_supported_color[] =
4426   {					/* media-top/top-margin-supported values */
4427     0,					/* Borderless */
4428     102					/* 0.04" (common HP inkjet top margin */
4429   };
4430   static const int	orientation_requested_supported[4] =
4431   {					/* orientation-requested-supported values */
4432     IPP_ORIENT_PORTRAIT,
4433     IPP_ORIENT_LANDSCAPE,
4434     IPP_ORIENT_REVERSE_LANDSCAPE,
4435     IPP_ORIENT_REVERSE_PORTRAIT
4436   };
4437   static const char * const overrides_supported[] =
4438   {					/* overrides-supported values */
4439     "document-numbers",
4440     "media",
4441     "media-col",
4442     "orientation-requested",
4443     "pages"
4444   };
4445   static const char * const print_color_mode_supported[] =
4446   {					/* print-color-mode-supported values */
4447     "monochrome"
4448   };
4449   static const char * const print_color_mode_supported_color[] =
4450   {					/* print-color-mode-supported values */
4451     "auto",
4452     "color",
4453     "monochrome"
4454   };
4455   static const int	print_quality_supported[] =
4456   {					/* print-quality-supported values */
4457     IPP_QUALITY_DRAFT,
4458     IPP_QUALITY_NORMAL,
4459     IPP_QUALITY_HIGH
4460   };
4461   static const char * const printer_input_tray[] =
4462   {					/* printer-input-tray values */
4463     "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4464     "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=100;status=0;name=main",
4465     "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=manual",
4466     "type=sheetFeedAutoNonRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=by-pass-tray"
4467   };
4468   static const char * const printer_input_tray_color[] =
4469   {					/* printer-input-tray values */
4470     "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4471     "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=-2;status=0;name=main",
4472     "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=photo"
4473   };
4474   static const char * const printer_supply[] =
4475   {					/* printer-supply values */
4476     "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4477         "maxcapacity=100;level=25;colorantname=unknown;",
4478     "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4479         "maxcapacity=100;level=75;colorantname=black;"
4480   };
4481   static const char * const printer_supply_color[] =
4482   {					/* printer-supply values */
4483     "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4484         "maxcapacity=100;level=25;colorantname=unknown;",
4485     "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4486         "maxcapacity=100;level=75;colorantname=black;",
4487     "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4488         "maxcapacity=100;level=50;colorantname=cyan;",
4489     "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4490         "maxcapacity=100;level=33;colorantname=magenta;",
4491     "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4492         "maxcapacity=100;level=67;colorantname=yellow;"
4493   };
4494   static const char * const printer_supply_description[] =
4495   {					/* printer-supply-description values */
4496     "Toner Waste Tank",
4497     "Black Toner"
4498   };
4499   static const char * const printer_supply_description_color[] =
4500   {					/* printer-supply-description values */
4501     "Ink Waste Tank",
4502     "Black Ink",
4503     "Cyan Ink",
4504     "Magenta Ink",
4505     "Yellow Ink"
4506   };
4507   static const int	pwg_raster_document_resolution_supported[] =
4508   {
4509     300,
4510     600
4511   };
4512   static const char * const pwg_raster_document_type_supported[] =
4513   {
4514     "black_1",
4515     "sgray_8"
4516   };
4517   static const char * const pwg_raster_document_type_supported_color[] =
4518   {
4519     "black_1",
4520     "sgray_8",
4521     "srgb_8",
4522     "srgb_16"
4523   };
4524   static const char * const sides_supported[] =
4525   {					/* sides-supported values */
4526     "one-sided",
4527     "two-sided-long-edge",
4528     "two-sided-short-edge"
4529   };
4530   static const char * const urf_supported[] =
4531   {					/* urf-supported values */
4532     "CP1",
4533     "IS1-4-5-19",
4534     "MT1-2-3-4-5-6",
4535     "RS600",
4536     "V1.4",
4537     "W8"
4538   };
4539   static const char * const urf_supported_color[] =
4540   {					/* urf-supported values */
4541     "CP1",
4542     "IS1-4-5-7-19",
4543     "MT1-2-3-4-5-6-8-9-10-11-12-13",
4544     "RS600",
4545     "SRGB24",
4546     "V1.4",
4547     "W8"
4548   };
4549   static const char * const urf_supported_color_duplex[] =
4550   {					/* urf-supported values */
4551     "CP1",
4552     "IS1-4-5-7-19",
4553     "MT1-2-3-4-5-6-8-9-10-11-12-13",
4554     "RS600",
4555     "SRGB24",
4556     "V1.4",
4557     "W8",
4558     "DM3"
4559   };
4560   static const char * const urf_supported_duplex[] =
4561   {					/* urf-supported values */
4562     "CP1",
4563     "IS1-4-5-19",
4564     "MT1-2-3-4-5-6",
4565     "RS600",
4566     "V1.4",
4567     "W8",
4568     "DM1"
4569   };
4570 
4571 
4572   attrs = ippNew();
4573 
4574   if (ppm_color > 0)
4575   {
4576     num_media = (int)(sizeof(media_supported_color) / sizeof(media_supported_color[0]));
4577     media     = media_supported_color;
4578     num_ready = (int)(sizeof(media_ready_color) / sizeof(media_ready_color[0]));
4579     ready     = media_ready_color;
4580   }
4581   else
4582   {
4583     num_media = (int)(sizeof(media_supported) / sizeof(media_supported[0]));
4584     media     = media_supported;
4585     num_ready = (int)(sizeof(media_ready) / sizeof(media_ready[0]));
4586     ready     = media_ready;
4587   }
4588 
4589   /* color-supported */
4590   ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", ppm_color > 0);
4591 
4592   /* copies-default */
4593   ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
4594 
4595   /* copies-supported */
4596   ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, (cupsArrayFind(docformats, (void *)"application/pdf") != NULL || cupsArrayFind(docformats, (void *)"image/jpeg") != NULL) ? 999 : 1);
4597 
4598   /* document-password-supported */
4599   if (cupsArrayFind(docformats, (void *)"application/pdf"))
4600     ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 1023);
4601 
4602   /* finishing-template-supported */
4603   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template-supported", NULL, "none");
4604 
4605   /* finishings-col-database */
4606   col = ippNew();
4607   ippAddString(col, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template", NULL, "none");
4608   ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-database", col);
4609   ippDelete(col);
4610 
4611   /* finishings-col-default */
4612   col = ippNew();
4613   ippAddString(col, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template", NULL, "none");
4614   ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-default", col);
4615   ippDelete(col);
4616 
4617   /* finishings-col-ready */
4618   col = ippNew();
4619   ippAddString(col, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template", NULL, "none");
4620   ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-ready", col);
4621   ippDelete(col);
4622 
4623   /* finishings-col-supported */
4624   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishings-col-supported", NULL, "finishing-template");
4625 
4626   /* finishings-default */
4627   ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
4628 
4629   /* finishings-ready */
4630   ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-ready", IPP_FINISHINGS_NONE);
4631 
4632   /* finishings-supported */
4633   ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", IPP_FINISHINGS_NONE);
4634 
4635   /* media-bottom-margin-supported */
4636   if (ppm_color > 0)
4637     ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", (int)(sizeof(media_bottom_margin_supported) / sizeof(media_bottom_margin_supported[0])), media_bottom_margin_supported);
4638   else
4639     ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", (int)(sizeof(media_bottom_margin_supported_color) / sizeof(media_bottom_margin_supported_color[0])), media_bottom_margin_supported_color);
4640 
4641   /* media-col-database and media-col-default */
4642   attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", num_media, NULL);
4643   for (i = 0; i < num_media; i ++)
4644   {
4645     int		bottom, left,		/* media-xxx-margins */
4646 		right, top;
4647     const char	*source;		/* media-source, if any */
4648 
4649     pwg = pwgMediaForPWG(media[i]);
4650 
4651     if (pwg->width < 21000 && pwg->length < 21000)
4652     {
4653       source = "photo";			/* Photo size media from photo tray */
4654       bottom =				/* Borderless margins */
4655       left   =
4656       right  =
4657       top    = 0;
4658     }
4659     else if (pwg->width < 21000)
4660     {
4661       source = "by-pass-tray";		/* Envelopes from multi-purpose tray */
4662       bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4663       left   =				/* Left/right margins are standard */
4664       right  = media_lr_margin_supported[1];
4665       top    = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4666     }
4667     else if (pwg->width == 21000)
4668     {
4669       source = NULL;			/* A4 from any tray */
4670       bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4671       left   =				/* Left/right margins are reduced */
4672       right  = media_lr_margin_supported[0];
4673       top    = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4674     }
4675     else
4676     {
4677       source = NULL;			/* Other size media from any tray */
4678       bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4679       left   =				/* Left/right margins are standard */
4680       right  = media_lr_margin_supported[1];
4681       top    = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4682     }
4683 
4684     col = create_media_col(media[i], source, NULL, pwg->width, pwg->length, bottom, left, right, top);
4685     ippSetCollection(attrs, &attr, i, col);
4686 
4687     ippDelete(col);
4688   }
4689 
4690   /* media-col-default */
4691   pwg = pwgMediaForPWG(ready[0]);
4692 
4693   if (pwg->width == 21000)
4694     col = create_media_col(ready[0], "main", "stationery", pwg->width, pwg->length, ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0], media_lr_margin_supported[0], media_lr_margin_supported[0], ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]);
4695   else
4696     col = create_media_col(ready[0], "main", "stationery", pwg->width, pwg->length, ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0], media_lr_margin_supported[1], media_lr_margin_supported[1], ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]);
4697 
4698   ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col);
4699 
4700   ippDelete(col);
4701 
4702   /* media-col-ready */
4703   attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-ready", num_ready, NULL);
4704   for (i = 0; i < num_ready; i ++)
4705   {
4706     int		bottom, left,		/* media-xxx-margins */
4707 		right, top;
4708     const char	*source,		/* media-source */
4709 		*type;			/* media-type */
4710 
4711     pwg = pwgMediaForPWG(ready[i]);
4712 
4713     if (pwg->width < 21000 && pwg->length < 21000)
4714     {
4715       source = "photo";			/* Photo size media from photo tray */
4716       type   = "photographic-glossy";	/* Glossy photo paper */
4717       bottom =				/* Borderless margins */
4718       left   =
4719       right  =
4720       top    = 0;
4721     }
4722     else if (pwg->width < 21000)
4723     {
4724       source = "by-pass-tray";		/* Envelopes from multi-purpose tray */
4725       type   = "envelope";		/* Envelope */
4726       bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4727       left   =				/* Left/right margins are standard */
4728       right  = media_lr_margin_supported[1];
4729       top    = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4730     }
4731     else if (pwg->width == 21000)
4732     {
4733       source = "main";			/* A4 from main tray */
4734       type   = "stationery";		/* Plain paper */
4735       bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4736       left   =				/* Left/right margins are reduced */
4737       right  = media_lr_margin_supported[0];
4738       top    = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4739     }
4740     else
4741     {
4742       source = "main";			/* A4 from main tray */
4743       type   = "stationery";		/* Plain paper */
4744       bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4745       left   =				/* Left/right margins are standard */
4746       right  = media_lr_margin_supported[1];
4747       top    = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4748     }
4749 
4750     col = create_media_col(ready[i], source, type, pwg->width, pwg->length, bottom, left, right, top);
4751     ippSetCollection(attrs, &attr, i, col);
4752     ippDelete(col);
4753   }
4754 
4755   /* media-default */
4756   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-default", NULL, media[0]);
4757 
4758   /* media-left/right-margin-supported */
4759   if (ppm_color > 0)
4760   {
4761     ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", (int)(sizeof(media_lr_margin_supported_color) / sizeof(media_lr_margin_supported_color[0])), media_lr_margin_supported_color);
4762     ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", (int)(sizeof(media_lr_margin_supported_color) / sizeof(media_lr_margin_supported_color[0])), media_lr_margin_supported_color);
4763   }
4764   else
4765   {
4766     ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", (int)(sizeof(media_lr_margin_supported) / sizeof(media_lr_margin_supported[0])), media_lr_margin_supported);
4767     ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", (int)(sizeof(media_lr_margin_supported) / sizeof(media_lr_margin_supported[0])), media_lr_margin_supported);
4768   }
4769 
4770   /* media-ready */
4771   ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", num_ready, NULL, ready);
4772 
4773   /* media-supported */
4774   ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-supported", num_media, NULL, media);
4775 
4776   /* media-size-supported */
4777   attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", num_media, NULL);
4778   for (i = 0; i < num_media; i ++)
4779   {
4780     pwg = pwgMediaForPWG(media[i]);
4781     col = create_media_size(pwg->width, pwg->length);
4782 
4783     ippSetCollection(attrs, &attr, i, col);
4784     ippDelete(col);
4785   }
4786 
4787   /* media-source-supported */
4788   if (ppm_color > 0)
4789     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", (int)(sizeof(media_source_supported_color) / sizeof(media_source_supported_color[0])), NULL, media_source_supported_color);
4790   else
4791     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", (int)(sizeof(media_source_supported) / sizeof(media_source_supported[0])), NULL, media_source_supported);
4792 
4793   /* media-top-margin-supported */
4794   if (ppm_color > 0)
4795     ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", (int)(sizeof(media_top_margin_supported) / sizeof(media_top_margin_supported[0])), media_top_margin_supported);
4796   else
4797     ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", (int)(sizeof(media_top_margin_supported_color) / sizeof(media_top_margin_supported_color[0])), media_top_margin_supported_color);
4798 
4799   /* media-type-supported */
4800   if (ppm_color > 0)
4801     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", (int)(sizeof(media_type_supported_color) / sizeof(media_type_supported_color[0])), NULL, media_type_supported_color);
4802   else
4803     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", (int)(sizeof(media_type_supported) / sizeof(media_type_supported[0])), NULL, media_type_supported);
4804 
4805   /* orientation-requested-default */
4806   ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", IPP_ORIENT_PORTRAIT);
4807 
4808   /* orientation-requested-supported */
4809   if (cupsArrayFind(docformats, (void *)"application/pdf") || cupsArrayFind(docformats, (void *)"image/jpeg"))
4810     ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported);
4811   else
4812     ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", IPP_ORIENT_PORTRAIT);
4813 
4814   /* output-bin-default */
4815   if (ppm_color > 0)
4816     ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-up");
4817   else
4818     ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
4819 
4820   /* output-bin-supported */
4821   if (ppm_color > 0)
4822     ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-up");
4823   else
4824     ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
4825 
4826   /* overrides-supported */
4827   if (cupsArrayFind(docformats, (void *)"application/pdf"))
4828     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "overrides-supported", (int)(sizeof(overrides_supported) / sizeof(overrides_supported[0])), NULL, overrides_supported);
4829 
4830   /* page-ranges-supported */
4831   ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", cupsArrayFind(docformats, (void *)"application/pdf") != NULL);
4832 
4833   /* pages-per-minute */
4834   ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppm);
4835 
4836   /* pages-per-minute-color */
4837   if (ppm_color > 0)
4838     ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppm_color);
4839 
4840   /* print-color-mode-default */
4841   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, ppm_color > 0 ? "auto" : "monochrome");
4842 
4843   /* print-color-mode-supported */
4844   if (ppm_color > 0)
4845     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported_color) / sizeof(print_color_mode_supported_color[0])), NULL, print_color_mode_supported_color);
4846   else
4847     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported) / sizeof(print_color_mode_supported[0])), NULL, print_color_mode_supported);
4848 
4849   /* print-content-optimize-default */
4850   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
4851 
4852   /* print-content-optimize-supported */
4853   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
4854 
4855   /* print-quality-default */
4856   ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
4857 
4858   /* print-quality-supported */
4859   ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
4860 
4861   /* print-rendering-intent-default */
4862   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
4863 
4864   /* print-rendering-intent-supported */
4865   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
4866 
4867   /* printer-device-id */
4868   snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make, model);
4869   ptr    = device_id + strlen(device_id);
4870   prefix = "CMD:";
4871   for (format = (const char *)cupsArrayFirst(docformats); format; format = (const char *)cupsArrayNext(docformats))
4872   {
4873     if (!strcasecmp(format, "application/pdf"))
4874       snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPDF", prefix);
4875     else if (!strcasecmp(format, "application/postscript"))
4876       snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPS", prefix);
4877     else if (!strcasecmp(format, "application/vnd.hp-PCL"))
4878       snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPCL", prefix);
4879     else if (!strcasecmp(format, "image/jpeg"))
4880       snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sJPEG", prefix);
4881     else if (!strcasecmp(format, "image/png"))
4882       snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPNG", prefix);
4883     else if (!strcasecmp(format, "image/pwg-raster"))
4884       snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPWG", prefix);
4885     else if (!strcasecmp(format, "image/urf"))
4886       snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sURF", prefix);
4887     else
4888       continue;
4889 
4890     ptr += strlen(ptr);
4891     prefix = ",";
4892   }
4893   if (ptr < (device_id + sizeof(device_id) - 1))
4894   {
4895     *ptr++ = ';';
4896     *ptr = '\0';
4897   }
4898   ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id);
4899 
4900   /* printer-input-tray */
4901   if (ppm_color > 0)
4902   {
4903     attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray_color[0], (int)strlen(printer_input_tray_color[0]));
4904     for (i = 1; i < (int)(sizeof(printer_input_tray_color) / sizeof(printer_input_tray_color[0])); i ++)
4905       ippSetOctetString(attrs, &attr, i, printer_input_tray_color[i], (int)strlen(printer_input_tray_color[i]));
4906   }
4907   else
4908   {
4909     attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray[0], (int)strlen(printer_input_tray[0]));
4910     for (i = 1; i < (int)(sizeof(printer_input_tray) / sizeof(printer_input_tray[0])); i ++)
4911       ippSetOctetString(attrs, &attr, i, printer_input_tray[i], (int)strlen(printer_input_tray[i]));
4912   }
4913 
4914   /* printer-make-and-model */
4915   snprintf(make_model, sizeof(make_model), "%s %s", make, model);
4916   ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, make_model);
4917 
4918   /* printer-resolution-default */
4919   ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, 600, 600);
4920 
4921   /* printer-resolution-supported */
4922   ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600);
4923 
4924   /* printer-supply and printer-supply-description */
4925   if (ppm_color > 0)
4926   {
4927     attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], (int)strlen(printer_supply_color[0]));
4928     for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++)
4929       ippSetOctetString(attrs, &attr, i, printer_supply_color[i], (int)strlen(printer_supply_color[i]));
4930 
4931     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description_color) / sizeof(printer_supply_description_color[0])), NULL, printer_supply_description_color);
4932   }
4933   else
4934   {
4935     attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], (int)strlen(printer_supply[0]));
4936     for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++)
4937       ippSetOctetString(attrs, &attr, i, printer_supply[i], (int)strlen(printer_supply[i]));
4938 
4939     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description) / sizeof(printer_supply_description[0])), NULL, printer_supply_description);
4940   }
4941 
4942   /* pwg-raster-document-xxx-supported */
4943   if (cupsArrayFind(docformats, (void *)"image/pwg-raster"))
4944   {
4945     ippAddResolutions(attrs, IPP_TAG_PRINTER, "pwg-raster-document-resolution-supported", (int)(sizeof(pwg_raster_document_resolution_supported) / sizeof(pwg_raster_document_resolution_supported[0])), IPP_RES_PER_INCH, pwg_raster_document_resolution_supported, pwg_raster_document_resolution_supported);
4946 
4947     if (ppm_color > 0 && duplex)
4948       ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "rotated");
4949     else if (duplex)
4950       ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal");
4951 
4952     if (ppm_color > 0)
4953       ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported_color) / sizeof(pwg_raster_document_type_supported_color[0])), NULL, pwg_raster_document_type_supported_color);
4954     else
4955       ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported) / sizeof(pwg_raster_document_type_supported[0])), NULL, pwg_raster_document_type_supported);
4956   }
4957 
4958   /* sides-default */
4959   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
4960 
4961   /* sides-supported */
4962   if (duplex)
4963     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported);
4964   else
4965     ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided");
4966 
4967   /* urf-supported */
4968   if (cupsArrayFind(docformats, (void *)"image/urf"))
4969   {
4970     if (ppm_color > 0)
4971     {
4972       if (duplex)
4973 	ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_color_duplex) / sizeof(urf_supported_color_duplex[0])), NULL, urf_supported_color_duplex);
4974       else
4975 	ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_color) / sizeof(urf_supported_color[0])), NULL, urf_supported_color);
4976     }
4977     else if (duplex)
4978     {
4979       ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_duplex) / sizeof(urf_supported_duplex[0])), NULL, urf_supported_duplex);
4980     }
4981     else
4982     {
4983       ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported) / sizeof(urf_supported[0])), NULL, urf_supported);
4984     }
4985   }
4986 
4987   return (attrs);
4988 }
4989 
4990 
4991 #if !CUPS_LITE
4992 /*
4993  * 'load_ppd_attributes()' - Load IPP attributes from a PPD file.
4994  */
4995 
4996 static ipp_t *				/* O - IPP attributes or `NULL` on error */
load_ppd_attributes(const char * ppdfile,cups_array_t * docformats)4997 load_ppd_attributes(
4998     const char   *ppdfile,		/* I - PPD filename */
4999     cups_array_t *docformats)		/* I - document-format-supported values */
5000 {
5001   int		i, j;			/* Looping vars */
5002   ipp_t		*attrs;			/* Attributes */
5003   ipp_attribute_t *attr;		/* Current attribute */
5004   ipp_t		*col;			/* Current collection value */
5005   ppd_file_t	*ppd;			/* PPD data */
5006   ppd_attr_t	*ppd_attr;		/* PPD attribute */
5007   ppd_choice_t	*ppd_choice;		/* PPD choice */
5008   ppd_size_t	*ppd_size;		/* Default PPD size */
5009   pwg_size_t	*pwg_size,		/* Current PWG size */
5010 		*default_size = NULL;	/* Default PWG size */
5011   const char	*default_source = NULL,	/* Default media source */
5012 		*default_type = NULL;	/* Default media type */
5013   pwg_map_t	*pwg_map;		/* Mapping from PWG to PPD keywords */
5014   _ppd_cache_t	*pc;			/* PPD cache */
5015   _pwg_finishings_t *finishings;	/* Current finishings value */
5016   const char	*template;		/* Current finishings-template value */
5017   int		num_margins;		/* Number of media-xxx-margin-supported values */
5018   int		margins[10];		/* media-xxx-margin-supported values */
5019   int		xres,			/* Default horizontal resolution */
5020 		yres;			/* Default vertical resolution */
5021   int		num_urf;		/* Number of urf-supported values */
5022   const char	*urf[10];		/* urf-supported values */
5023   char		urf_rs[32];		/* RS value */
5024   static const int	orientation_requested_supported[4] =
5025   {					/* orientation-requested-supported values */
5026     IPP_ORIENT_PORTRAIT,
5027     IPP_ORIENT_LANDSCAPE,
5028     IPP_ORIENT_REVERSE_LANDSCAPE,
5029     IPP_ORIENT_REVERSE_PORTRAIT
5030   };
5031   static const char * const overrides_supported[] =
5032   {					/* overrides-supported */
5033     "document-numbers",
5034     "media",
5035     "media-col",
5036     "orientation-requested",
5037     "pages"
5038   };
5039   static const char * const print_color_mode_supported[] =
5040   {					/* print-color-mode-supported values */
5041     "monochrome"
5042   };
5043   static const char * const print_color_mode_supported_color[] =
5044   {					/* print-color-mode-supported values */
5045     "auto",
5046     "color",
5047     "monochrome"
5048   };
5049   static const int	print_quality_supported[] =
5050   {					/* print-quality-supported values */
5051     IPP_QUALITY_DRAFT,
5052     IPP_QUALITY_NORMAL,
5053     IPP_QUALITY_HIGH
5054   };
5055   static const char * const printer_supply[] =
5056   {					/* printer-supply values */
5057     "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
5058         "maxcapacity=100;level=25;colorantname=unknown;",
5059     "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
5060         "maxcapacity=100;level=75;colorantname=black;"
5061   };
5062   static const char * const printer_supply_color[] =
5063   {					/* printer-supply values */
5064     "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
5065         "maxcapacity=100;level=25;colorantname=unknown;",
5066     "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
5067         "maxcapacity=100;level=75;colorantname=black;",
5068     "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
5069         "maxcapacity=100;level=50;colorantname=cyan;",
5070     "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
5071         "maxcapacity=100;level=33;colorantname=magenta;",
5072     "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
5073         "maxcapacity=100;level=67;colorantname=yellow;"
5074   };
5075   static const char * const printer_supply_description[] =
5076   {					/* printer-supply-description values */
5077     "Toner Waste Tank",
5078     "Black Toner"
5079   };
5080   static const char * const printer_supply_description_color[] =
5081   {					/* printer-supply-description values */
5082     "Ink Waste Tank",
5083     "Black Ink",
5084     "Cyan Ink",
5085     "Magenta Ink",
5086     "Yellow Ink"
5087   };
5088   static const char * const pwg_raster_document_type_supported[] =
5089   {
5090     "black_1",
5091     "sgray_8"
5092   };
5093   static const char * const pwg_raster_document_type_supported_color[] =
5094   {
5095     "black_1",
5096     "sgray_8",
5097     "srgb_8",
5098     "srgb_16"
5099   };
5100   static const char * const sides_supported[] =
5101   {					/* sides-supported values */
5102     "one-sided",
5103     "two-sided-long-edge",
5104     "two-sided-short-edge"
5105   };
5106 
5107 
5108  /*
5109   * Open the PPD file...
5110   */
5111 
5112   if ((ppd = ppdOpenFile(ppdfile)) == NULL)
5113   {
5114     ppd_status_t	status;		/* Load error */
5115 
5116     status = ppdLastError(&i);
5117     _cupsLangPrintf(stderr, _("ippeveprinter: Unable to open \"%s\": %s on line %d."), ppdfile, ppdErrorString(status), i);
5118     return (NULL);
5119   }
5120 
5121   ppdMarkDefaults(ppd);
5122 
5123   pc = _ppdCacheCreateWithPPD(ppd);
5124 
5125   if ((ppd_size = ppdPageSize(ppd, NULL)) != NULL)
5126   {
5127    /*
5128     * Look up default size...
5129     */
5130 
5131     for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
5132     {
5133       if (!strcmp(pwg_size->map.ppd, ppd_size->name))
5134       {
5135         default_size = pwg_size;
5136         break;
5137       }
5138     }
5139   }
5140 
5141   if (!default_size)
5142   {
5143    /*
5144     * Default to A4 or Letter...
5145     */
5146 
5147     for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
5148     {
5149       if (!strcmp(pwg_size->map.ppd, "Letter") || !strcmp(pwg_size->map.ppd, "A4"))
5150       {
5151         default_size = pwg_size;
5152         break;
5153       }
5154     }
5155 
5156     if (!default_size)
5157       default_size = pc->sizes;		/* Last resort: first size */
5158   }
5159 
5160   if ((ppd_choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
5161     default_source = _ppdCacheGetSource(pc, ppd_choice->choice);
5162 
5163   if ((ppd_choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL)
5164     default_source = _ppdCacheGetType(pc, ppd_choice->choice);
5165 
5166   if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL)
5167   {
5168    /*
5169     * Use the PPD-defined default resolution...
5170     */
5171 
5172     if ((i = sscanf(ppd_attr->value, "%dx%d", &xres, &yres)) == 1)
5173       yres = xres;
5174     else if (i < 0)
5175       xres = yres = 300;
5176   }
5177   else
5178   {
5179    /*
5180     * Use default of 300dpi...
5181     */
5182 
5183     xres = yres = 300;
5184   }
5185 
5186   snprintf(urf_rs, sizeof(urf_rs), "RS%d", yres < xres ? yres : xres);
5187 
5188   num_urf = 0;
5189   urf[num_urf ++] = "V1.4";
5190   urf[num_urf ++] = "CP1";
5191   urf[num_urf ++] = urf_rs;
5192   urf[num_urf ++] = "W8";
5193   if (pc->sides_2sided_long)
5194     urf[num_urf ++] = "DM1";
5195   if (ppd->color_device)
5196     urf[num_urf ++] = "SRGB24";
5197 
5198  /*
5199   * PostScript printers accept PDF via one of the CUPS PDF to PostScript
5200   * filters, along with PostScript (of course) and JPEG...
5201   */
5202 
5203   cupsArrayAdd(docformats, "application/pdf");
5204   cupsArrayAdd(docformats, "application/postscript");
5205   cupsArrayAdd(docformats, "image/jpeg");
5206 
5207  /*
5208   * Create the attributes...
5209   */
5210 
5211   attrs = ippNew();
5212 
5213   /* color-supported */
5214   ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", (char)ppd->color_device);
5215 
5216   /* copies-default */
5217   ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
5218 
5219   /* copies-supported */
5220   ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999);
5221 
5222   /* document-password-supported */
5223   ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 127);
5224 
5225   /* finishing-template-supported */
5226   attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template-supported", cupsArrayCount(pc->templates) + 1, NULL, NULL);
5227   ippSetString(attrs, &attr, 0, "none");
5228   for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
5229     ippSetString(attrs, &attr, i, template);
5230 
5231   /* finishings-col-database */
5232   attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-database", cupsArrayCount(pc->templates) + 1, NULL);
5233 
5234   col = ippNew();
5235   ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
5236   ippSetCollection(attrs, &attr, 0, col);
5237   ippDelete(col);
5238 
5239   for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
5240   {
5241     col = ippNew();
5242     ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template);
5243     ippSetCollection(attrs, &attr, i, col);
5244     ippDelete(col);
5245   }
5246 
5247   /* finishings-col-default */
5248   col = ippNew();
5249   ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
5250   ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-default", col);
5251   ippDelete(col);
5252 
5253   /* finishings-col-ready */
5254   attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-ready", cupsArrayCount(pc->templates) + 1, NULL);
5255 
5256   col = ippNew();
5257   ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
5258   ippSetCollection(attrs, &attr, 0, col);
5259   ippDelete(col);
5260 
5261   for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
5262   {
5263     col = ippNew();
5264     ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template);
5265     ippSetCollection(attrs, &attr, i, col);
5266     ippDelete(col);
5267   }
5268 
5269   /* finishings-col-supported */
5270   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishings-col-supported", NULL, "finishing-template");
5271 
5272   /* finishings-default */
5273   ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
5274 
5275   /* finishings-ready */
5276   attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-ready", cupsArrayCount(pc->finishings) + 1, NULL);
5277   ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
5278   for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
5279     ippSetInteger(attrs, &attr, i, (int)finishings->value);
5280 
5281   /* finishings-supported */
5282   attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", cupsArrayCount(pc->finishings) + 1, NULL);
5283   ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
5284   for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
5285     ippSetInteger(attrs, &attr, i, (int)finishings->value);
5286 
5287   /* media-bottom-margin-supported */
5288   for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
5289   {
5290     for (j = 0; j < num_margins; j ++)
5291     {
5292       if (margins[j] == pwg_size->bottom)
5293         break;
5294     }
5295 
5296     if (j >= num_margins)
5297       margins[num_margins ++] = pwg_size->bottom;
5298   }
5299 
5300   for (i = 0; i < (num_margins - 1); i ++)
5301   {
5302     for (j = i + 1; j < num_margins; j ++)
5303     {
5304       if (margins[i] > margins[j])
5305       {
5306         int mtemp = margins[i];
5307 
5308         margins[i] = margins[j];
5309         margins[j] = mtemp;
5310       }
5311     }
5312   }
5313 
5314   ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", num_margins, margins);
5315 
5316   /* media-col-database */
5317   attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", pc->num_sizes, NULL);
5318   for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
5319   {
5320     col = create_media_col(pwg_size->map.pwg, NULL, NULL, pwg_size->width, pwg_size->length, pwg_size->bottom, pwg_size->left, pwg_size->right, pwg_size->top);
5321     ippSetCollection(attrs, &attr, i, col);
5322     ippDelete(col);
5323   }
5324 
5325   /* media-col-default */
5326   col = create_media_col(default_size->map.pwg, default_source, default_type, default_size->width, default_size->length, default_size->bottom, default_size->left, default_size->right, default_size->top);
5327   ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col);
5328   ippDelete(col);
5329 
5330   /* media-col-ready */
5331   col = create_media_col(default_size->map.pwg, default_source, default_type, default_size->width, default_size->length, default_size->bottom, default_size->left, default_size->right, default_size->top);
5332   ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-ready", col);
5333   ippDelete(col);
5334 
5335   /* media-default */
5336   ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default", NULL, default_size->map.pwg);
5337 
5338   /* media-left-margin-supported */
5339   for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
5340   {
5341     for (j = 0; j < num_margins; j ++)
5342     {
5343       if (margins[j] == pwg_size->left)
5344         break;
5345     }
5346 
5347     if (j >= num_margins)
5348       margins[num_margins ++] = pwg_size->left;
5349   }
5350 
5351   for (i = 0; i < (num_margins - 1); i ++)
5352   {
5353     for (j = i + 1; j < num_margins; j ++)
5354     {
5355       if (margins[i] > margins[j])
5356       {
5357         int mtemp = margins[i];
5358 
5359         margins[i] = margins[j];
5360         margins[j] = mtemp;
5361       }
5362     }
5363   }
5364 
5365   ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", num_margins, margins);
5366 
5367   /* media-ready */
5368   ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, default_size->map.pwg);
5369 
5370   /* media-right-margin-supported */
5371   for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
5372   {
5373     for (j = 0; j < num_margins; j ++)
5374     {
5375       if (margins[j] == pwg_size->right)
5376         break;
5377     }
5378 
5379     if (j >= num_margins)
5380       margins[num_margins ++] = pwg_size->right;
5381   }
5382 
5383   for (i = 0; i < (num_margins - 1); i ++)
5384   {
5385     for (j = i + 1; j < num_margins; j ++)
5386     {
5387       if (margins[i] > margins[j])
5388       {
5389         int mtemp = margins[i];
5390 
5391         margins[i] = margins[j];
5392         margins[j] = mtemp;
5393       }
5394     }
5395   }
5396 
5397   ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", num_margins, margins);
5398 
5399   /* media-supported */
5400   attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-supported", pc->num_sizes, NULL, NULL);
5401   for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
5402     ippSetString(attrs, &attr, i, pwg_size->map.pwg);
5403 
5404   /* media-size-supported */
5405   attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", pc->num_sizes, NULL);
5406   for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
5407   {
5408     col = create_media_size(pwg_size->width, pwg_size->length);
5409     ippSetCollection(attrs, &attr, i, col);
5410     ippDelete(col);
5411   }
5412 
5413   /* media-source-supported */
5414   if (pc->num_sources > 0)
5415   {
5416     attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source-supported", pc->num_sources, NULL,  NULL);
5417     for (i = 0, pwg_map = pc->sources; i < pc->num_sources; i ++, pwg_map ++)
5418       ippSetString(attrs, &attr, i, pwg_map->pwg);
5419   }
5420   else
5421   {
5422     ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", NULL, "auto");
5423   }
5424 
5425   /* media-top-margin-supported */
5426   for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
5427   {
5428     for (j = 0; j < num_margins; j ++)
5429     {
5430       if (margins[j] == pwg_size->top)
5431         break;
5432     }
5433 
5434     if (j >= num_margins)
5435       margins[num_margins ++] = pwg_size->top;
5436   }
5437 
5438   for (i = 0; i < (num_margins - 1); i ++)
5439   {
5440     for (j = i + 1; j < num_margins; j ++)
5441     {
5442       if (margins[i] > margins[j])
5443       {
5444         int mtemp = margins[i];
5445 
5446         margins[i] = margins[j];
5447         margins[j] = mtemp;
5448       }
5449     }
5450   }
5451 
5452   ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", num_margins, margins);
5453 
5454   /* media-type-supported */
5455   if (pc->num_types > 0)
5456   {
5457     attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type-supported", pc->num_types, NULL,  NULL);
5458     for (i = 0, pwg_map = pc->types; i < pc->num_types; i ++, pwg_map ++)
5459       ippSetString(attrs, &attr, i, pwg_map->pwg);
5460   }
5461   else
5462   {
5463     ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", NULL, "auto");
5464   }
5465 
5466   /* orientation-requested-default */
5467   ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", IPP_ORIENT_PORTRAIT);
5468 
5469   /* orientation-requested-supported */
5470   ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported);
5471 
5472   /* output-bin-default */
5473   if (pc->num_bins > 0)
5474     ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-default", NULL, pc->bins->pwg);
5475   else
5476     ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
5477 
5478   /* output-bin-supported */
5479   if (pc->num_bins > 0)
5480   {
5481     attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-supported", pc->num_bins, NULL,  NULL);
5482     for (i = 0, pwg_map = pc->bins; i < pc->num_bins; i ++, pwg_map ++)
5483       ippSetString(attrs, &attr, i, pwg_map->pwg);
5484   }
5485   else
5486   {
5487     ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
5488   }
5489 
5490   /* overrides-supported */
5491   ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "overrides-supported", (int)(sizeof(overrides_supported) / sizeof(overrides_supported[0])), NULL, overrides_supported);
5492 
5493   /* page-ranges-supported */
5494   ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1);
5495 
5496   /* pages-per-minute */
5497   ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppd->throughput);
5498 
5499   /* pages-per-minute-color */
5500   if (ppd->color_device)
5501     ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppd->throughput);
5502 
5503   /* print-color-mode-default */
5504   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, ppd->color_device ? "auto" : "monochrome");
5505 
5506   /* print-color-mode-supported */
5507   if (ppd->color_device)
5508     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported_color) / sizeof(print_color_mode_supported_color[0])), NULL, print_color_mode_supported_color);
5509   else
5510     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported) / sizeof(print_color_mode_supported[0])), NULL, print_color_mode_supported);
5511 
5512   /* print-content-optimize-default */
5513   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
5514 
5515   /* print-content-optimize-supported */
5516   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
5517 
5518   /* print-quality-default */
5519   ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
5520 
5521   /* print-quality-supported */
5522   ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
5523 
5524   /* print-rendering-intent-default */
5525   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
5526 
5527   /* print-rendering-intent-supported */
5528   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
5529 
5530   /* printer-device-id */
5531   if ((ppd_attr = ppdFindAttr(ppd, "1284DeviceId", NULL)) != NULL)
5532   {
5533    /*
5534     * Use the device ID string from the PPD...
5535     */
5536 
5537     ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, ppd_attr->value);
5538   }
5539   else
5540   {
5541    /*
5542     * Synthesize a device ID string...
5543     */
5544 
5545     char	device_id[1024];		/* Device ID string */
5546 
5547     snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;CMD:PS;", ppd->manufacturer, ppd->modelname);
5548 
5549     ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id);
5550   }
5551 
5552   /* printer-input-tray */
5553   if (pc->num_sources > 0)
5554   {
5555     for (i = 0, attr = NULL; i < pc->num_sources; i ++)
5556     {
5557       char	input_tray[1024];	/* printer-input-tray value */
5558 
5559       if (!strcmp(pc->sources[i].pwg, "manual") || strstr(pc->sources[i].pwg, "-man") != NULL)
5560         snprintf(input_tray, sizeof(input_tray), "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=%s", pc->sources[i].pwg);
5561       else
5562         snprintf(input_tray, sizeof(input_tray), "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=125;status=0;name=%s", pc->sources[i].pwg);
5563 
5564       if (attr)
5565         ippSetOctetString(attrs, &attr, i, input_tray, (int)strlen(input_tray));
5566       else
5567         attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", input_tray, (int)strlen(input_tray));
5568     }
5569   }
5570   else
5571   {
5572     static const char *printer_input_tray = "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto";
5573 
5574     ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray, (int)strlen(printer_input_tray));
5575   }
5576 
5577   /* printer-make-and-model */
5578   ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, ppd->nickname);
5579 
5580   /* printer-resolution-default */
5581   ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, xres, yres);
5582 
5583   /* printer-resolution-supported */
5584   ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, xres, yres);
5585 
5586   /* printer-supply and printer-supply-description */
5587   if (ppd->color_device)
5588   {
5589     attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], (int)strlen(printer_supply_color[0]));
5590     for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++)
5591       ippSetOctetString(attrs, &attr, i, printer_supply_color[i], (int)strlen(printer_supply_color[i]));
5592 
5593     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description_color) / sizeof(printer_supply_description_color[0])), NULL, printer_supply_description_color);
5594   }
5595   else
5596   {
5597     attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], (int)strlen(printer_supply[0]));
5598     for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++)
5599       ippSetOctetString(attrs, &attr, i, printer_supply[i], (int)strlen(printer_supply[i]));
5600 
5601     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description) / sizeof(printer_supply_description[0])), NULL, printer_supply_description);
5602   }
5603 
5604   /* pwg-raster-document-xxx-supported */
5605   if (cupsArrayFind(docformats, (void *)"image/pwg-raster"))
5606   {
5607     ippAddResolution(attrs, IPP_TAG_PRINTER, "pwg-raster-document-resolution-supported", IPP_RES_PER_INCH, xres, yres);
5608 
5609     if (pc->sides_2sided_long)
5610       ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal");
5611 
5612     if (ppd->color_device)
5613       ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported_color) / sizeof(pwg_raster_document_type_supported_color[0])), NULL, pwg_raster_document_type_supported_color);
5614     else
5615       ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported) / sizeof(pwg_raster_document_type_supported[0])), NULL, pwg_raster_document_type_supported);
5616   }
5617 
5618   /* sides-default */
5619   ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
5620 
5621   /* sides-supported */
5622   if (pc->sides_2sided_long)
5623     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported);
5624   else
5625     ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided");
5626 
5627   /* urf-supported */
5628   if (cupsArrayFind(docformats, (void *)"image/urf"))
5629     ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", num_urf, NULL, urf);
5630 
5631  /*
5632   * Free the PPD file and return the attributes...
5633   */
5634 
5635   _ppdCacheDestroy(pc);
5636 
5637   ppdClose(ppd);
5638 
5639   return (attrs);
5640 }
5641 #endif /* !CUPS_LITE */
5642 
5643 
5644 #if HAVE_LIBPAM
5645 /*
5646  * 'pam_func()' - PAM conversation function.
5647  */
5648 
5649 static int				/* O - Success or failure */
pam_func(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)5650 pam_func(
5651     int                      num_msg,	/* I - Number of messages */
5652     const struct pam_message **msg,	/* I - Messages */
5653     struct pam_response      **resp,	/* O - Responses */
5654     void                     *appdata_ptr)
5655 					/* I - Pointer to connection */
5656 {
5657   int			i;		/* Looping var */
5658   struct pam_response	*replies;	/* Replies */
5659   ippeve_authdata_t	*data;		/* Pointer to auth data */
5660 
5661 
5662  /*
5663   * Allocate memory for the responses...
5664   */
5665 
5666   if ((replies = malloc(sizeof(struct pam_response) * (size_t)num_msg)) == NULL)
5667     return (PAM_CONV_ERR);
5668 
5669  /*
5670   * Answer all of the messages...
5671   */
5672 
5673   data = (ippeve_authdata_t *)appdata_ptr;
5674 
5675   for (i = 0; i < num_msg; i ++)
5676   {
5677     switch (msg[i]->msg_style)
5678     {
5679       case PAM_PROMPT_ECHO_ON:
5680           replies[i].resp_retcode = PAM_SUCCESS;
5681           replies[i].resp         = strdup(data->username);
5682           break;
5683 
5684       case PAM_PROMPT_ECHO_OFF:
5685           replies[i].resp_retcode = PAM_SUCCESS;
5686           replies[i].resp         = strdup(data->password);
5687           break;
5688 
5689       case PAM_TEXT_INFO:
5690           replies[i].resp_retcode = PAM_SUCCESS;
5691           replies[i].resp         = NULL;
5692           break;
5693 
5694       case PAM_ERROR_MSG:
5695           replies[i].resp_retcode = PAM_SUCCESS;
5696           replies[i].resp         = NULL;
5697           break;
5698 
5699       default:
5700           free(replies);
5701           return (PAM_CONV_ERR);
5702     }
5703   }
5704 
5705  /*
5706   * Return the responses back to PAM...
5707   */
5708 
5709   *resp = replies;
5710 
5711   return (PAM_SUCCESS);
5712 }
5713 #endif /* HAVE_LIBPAM */
5714 
5715 
5716 /*
5717  * 'parse_options()' - Parse URL options into CUPS options.
5718  *
5719  * The client->options string is destroyed by this function.
5720  */
5721 
5722 static int				/* O - Number of options */
parse_options(ippeve_client_t * client,cups_option_t ** options)5723 parse_options(ippeve_client_t *client,	/* I - Client */
5724               cups_option_t   **options)/* O - Options */
5725 {
5726   char	*name,				/* Name */
5727 	*value,				/* Value */
5728 	*next;				/* Next name=value pair */
5729   int	num_options = 0;		/* Number of options */
5730 
5731 
5732   *options = NULL;
5733 
5734   for (name = client->options; name && *name; name = next)
5735   {
5736     if ((value = strchr(name, '=')) == NULL)
5737       break;
5738 
5739     *value++ = '\0';
5740     if ((next = strchr(value, '&')) != NULL)
5741       *next++ = '\0';
5742 
5743     num_options = cupsAddOption(name, value, num_options, options);
5744   }
5745 
5746   return (num_options);
5747 }
5748 
5749 
5750 /*
5751  * 'process_attr_message()' - Process an ATTR: message from a command.
5752  */
5753 
5754 static void
process_attr_message(ippeve_job_t * job,char * message)5755 process_attr_message(
5756     ippeve_job_t *job,			/* I - Job */
5757     char       *message)		/* I - Message */
5758 {
5759   int		i,			/* Looping var */
5760 		num_options = 0;	/* Number of name=value pairs */
5761   cups_option_t	*options = NULL,	/* name=value pairs from message */
5762 		*option;		/* Current option */
5763   ipp_attribute_t *attr;		/* Current attribute */
5764 
5765 
5766  /*
5767   * Grab attributes from the message line...
5768   */
5769 
5770   num_options = cupsParseOptions(message + 5, num_options, &options);
5771 
5772  /*
5773   * Loop through the options and record them in the printer or job objects...
5774   */
5775 
5776   for (i = num_options, option = options; i > 0; i --, option ++)
5777   {
5778     if (!strcmp(option->name, "job-impressions"))
5779     {
5780      /*
5781       * Update job-impressions attribute...
5782       */
5783 
5784       job->impressions = atoi(option->value);
5785     }
5786     else if (!strcmp(option->name, "job-impressions-completed"))
5787     {
5788      /*
5789       * Update job-impressions-completed attribute...
5790       */
5791 
5792       job->impcompleted = atoi(option->value);
5793     }
5794     else if (!strncmp(option->name, "marker-", 7) || !strcmp(option->name, "printer-alert") || !strcmp(option->name, "printer-alert-description") || !strcmp(option->name, "printer-supply") || !strcmp(option->name, "printer-supply-description"))
5795     {
5796      /*
5797       * Update Printer Status attribute...
5798       */
5799 
5800       _cupsRWLockWrite(&job->printer->rwlock);
5801 
5802       if ((attr = ippFindAttribute(job->printer->attrs, option->name, IPP_TAG_ZERO)) != NULL)
5803         ippDeleteAttribute(job->printer->attrs, attr);
5804 
5805       cupsEncodeOption(job->printer->attrs, IPP_TAG_PRINTER, option->name, option->value);
5806 
5807       _cupsRWUnlock(&job->printer->rwlock);
5808     }
5809     else
5810     {
5811      /*
5812       * Something else that isn't currently supported...
5813       */
5814 
5815       fprintf(stderr, "[Job %d] Ignoring update of attribute \"%s\" with value \"%s\".\n", job->id, option->name, option->value);
5816     }
5817   }
5818 
5819   cupsFreeOptions(num_options, options);
5820 }
5821 
5822 
5823 /*
5824  * 'process_client()' - Process client requests on a thread.
5825  */
5826 
5827 static void *				/* O - Exit status */
process_client(ippeve_client_t * client)5828 process_client(ippeve_client_t *client)	/* I - Client */
5829 {
5830  /*
5831   * Loop until we are out of requests or timeout (30 seconds)...
5832   */
5833 
5834 #ifdef HAVE_TLS
5835   int first_time = 1;			/* First time request? */
5836 #endif /* HAVE_TLS */
5837 
5838   while (httpWait(client->http, 30000))
5839   {
5840 #ifdef HAVE_TLS
5841     if (first_time)
5842     {
5843      /*
5844       * See if we need to negotiate a TLS connection...
5845       */
5846 
5847       char buf[1];			/* First byte from client */
5848 
5849       if (recv(httpGetFd(client->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0])))
5850       {
5851         fprintf(stderr, "%s Starting HTTPS session.\n", client->hostname);
5852 
5853 	if (httpEncryption(client->http, HTTP_ENCRYPTION_ALWAYS))
5854 	{
5855 	  fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
5856 	  break;
5857         }
5858 
5859         fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
5860       }
5861 
5862       first_time = 0;
5863     }
5864 #endif /* HAVE_TLS */
5865 
5866     if (!process_http(client))
5867       break;
5868   }
5869 
5870  /*
5871   * Close the connection to the client and return...
5872   */
5873 
5874   delete_client(client);
5875 
5876   return (NULL);
5877 }
5878 
5879 
5880 /*
5881  * 'process_http()' - Process a HTTP request.
5882  */
5883 
5884 int					/* O - 1 on success, 0 on failure */
process_http(ippeve_client_t * client)5885 process_http(ippeve_client_t *client)	/* I - Client connection */
5886 {
5887   char			uri[1024];	/* URI */
5888   http_state_t		http_state;	/* HTTP state */
5889   http_status_t		http_status;	/* HTTP status */
5890   ipp_state_t		ipp_state;	/* State of IPP transfer */
5891   char			scheme[32],	/* Method/scheme */
5892 			userpass[128],	/* Username:password */
5893 			hostname[HTTP_MAX_HOST],
5894 					/* Hostname */
5895 			*ptr;		/* Pointer into value */
5896   int			port;		/* Port number */
5897   static const char * const http_states[] =
5898   {					/* Strings for logging HTTP method */
5899     "WAITING",
5900     "OPTIONS",
5901     "GET",
5902     "GET_SEND",
5903     "HEAD",
5904     "POST",
5905     "POST_RECV",
5906     "POST_SEND",
5907     "PUT",
5908     "PUT_RECV",
5909     "DELETE",
5910     "TRACE",
5911     "CONNECT",
5912     "STATUS",
5913     "UNKNOWN_METHOD",
5914     "UNKNOWN_VERSION"
5915   };
5916 
5917 
5918  /*
5919   * Clear state variables...
5920   */
5921 
5922   client->username[0] = '\0';
5923 
5924   ippDelete(client->request);
5925   ippDelete(client->response);
5926 
5927   client->request   = NULL;
5928   client->response  = NULL;
5929   client->operation = HTTP_STATE_WAITING;
5930 
5931  /*
5932   * Read a request from the connection...
5933   */
5934 
5935   while ((http_state = httpReadRequest(client->http, uri,
5936                                        sizeof(uri))) == HTTP_STATE_WAITING)
5937     usleep(1);
5938 
5939  /*
5940   * Parse the request line...
5941   */
5942 
5943   if (http_state == HTTP_STATE_ERROR)
5944   {
5945     if (httpError(client->http) == EPIPE)
5946       fprintf(stderr, "%s Client closed connection.\n", client->hostname);
5947     else
5948       fprintf(stderr, "%s Bad request line (%s).\n", client->hostname, strerror(httpError(client->http)));
5949 
5950     return (0);
5951   }
5952   else if (http_state == HTTP_STATE_UNKNOWN_METHOD)
5953   {
5954     fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname);
5955     respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5956     return (0);
5957   }
5958   else if (http_state == HTTP_STATE_UNKNOWN_VERSION)
5959   {
5960     fprintf(stderr, "%s Bad HTTP version.\n", client->hostname);
5961     respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5962     return (0);
5963   }
5964 
5965   fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state], uri);
5966 
5967  /*
5968   * Separate the URI into its components...
5969   */
5970 
5971   if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
5972 		      userpass, sizeof(userpass),
5973 		      hostname, sizeof(hostname), &port,
5974 		      client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK &&
5975       (http_state != HTTP_STATE_OPTIONS || strcmp(uri, "*")))
5976   {
5977     fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri);
5978     respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5979     return (0);
5980   }
5981 
5982   if ((client->options = strchr(client->uri, '?')) != NULL)
5983     *(client->options)++ = '\0';
5984 
5985  /*
5986   * Process the request...
5987   */
5988 
5989   client->start     = time(NULL);
5990   client->operation = httpGetState(client->http);
5991 
5992  /*
5993   * Parse incoming parameters until the status changes...
5994   */
5995 
5996   while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE);
5997 
5998   if (http_status != HTTP_STATUS_OK)
5999   {
6000     respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
6001     return (0);
6002   }
6003 
6004  /*
6005   * Validate the host header...
6006   */
6007 
6008   if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] &&
6009       httpGetVersion(client->http) >= HTTP_VERSION_1_1)
6010   {
6011    /*
6012     * HTTP/1.1 and higher require the "Host:" field...
6013     */
6014 
6015     fprintf(stderr, "%s Missing Host: header.\n", client->hostname);
6016     respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
6017     return (0);
6018   }
6019 
6020   strlcpy(client->host_field, httpGetField(client->http, HTTP_FIELD_HOST), sizeof(client->host_field));
6021   if ((ptr = strrchr(client->host_field, ':')) != NULL)
6022   {
6023    /*
6024     * Grab port number from Host: header...
6025     */
6026 
6027     *ptr++ = '\0';
6028     client->host_port = atoi(ptr);
6029   }
6030   else
6031   {
6032    /*
6033     * Use the default port number...
6034     */
6035 
6036     client->host_port = client->printer->port;
6037   }
6038 
6039   if ((ptr = strstr(client->host_field, ".local")) == NULL)
6040     ptr = strrchr(client->host_field, '.');
6041 
6042   if (!isdigit(client->host_field[0] & 255) && client->host_field[0] != '[' && strcmp(client->host_field, client->printer->hostname) && strcmp(client->host_field, "localhost") &&
6043       (!ptr || (strcmp(ptr, ".local") && strcmp(ptr, ".local."))))
6044   {
6045     fprintf(stderr, "%s Bad Host: header '%s'.\n", client->hostname, client->host_field);
6046     respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
6047     return (0);
6048   }
6049 
6050  /*
6051   * Handle HTTP Upgrade...
6052   */
6053 
6054   if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION), "Upgrade"))
6055   {
6056 #ifdef HAVE_TLS
6057     if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http))
6058     {
6059       if (!respond_http(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0))
6060         return (0);
6061 
6062       fprintf(stderr, "%s Upgrading to encrypted connection.\n", client->hostname);
6063 
6064       if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED))
6065       {
6066         fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
6067 	return (0);
6068       }
6069 
6070       fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
6071     }
6072     else
6073 #endif /* HAVE_TLS */
6074 
6075     if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
6076       return (0);
6077   }
6078 
6079  /*
6080   * Handle new transfers...
6081   */
6082 
6083   switch (client->operation)
6084   {
6085     case HTTP_STATE_OPTIONS :
6086        /*
6087 	* Do OPTIONS command...
6088 	*/
6089 
6090 	return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
6091 
6092     case HTTP_STATE_HEAD :
6093         if (!strcmp(client->uri, "/en.strings"))
6094 	  return (respond_http(client, HTTP_STATUS_OK, NULL, "text/strings", 0));
6095         else if (!strcmp(client->uri, "/icon.png") || !strcmp(client->uri, "/icon-lg.png") || !strcmp(client->uri, "/icon-sm.png"))
6096 	  return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
6097 	else if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies"))
6098 	  return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
6099 	else
6100 	  return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6101 
6102     case HTTP_STATE_GET :
6103         if (!strcmp(client->uri, "/en.strings"))
6104 	{
6105 	 /*
6106 	  * Send strings file.
6107 	  */
6108 
6109           if (client->printer->strings)
6110           {
6111 	    int		fd;		/* Icon file */
6112 	    struct stat	fileinfo;	/* Icon file information */
6113 	    char	buffer[4096];	/* Copy buffer */
6114 	    ssize_t	bytes;		/* Bytes */
6115 
6116 	    if (!stat(client->printer->strings, &fileinfo) && (fd = open(client->printer->strings, O_RDONLY | O_BINARY)) >= 0)
6117 	    {
6118 	      if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/strings", (size_t)fileinfo.st_size))
6119 	      {
6120 		close(fd);
6121 		return (0);
6122 	      }
6123 
6124 	      while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
6125 		httpWrite2(client->http, buffer, (size_t)bytes);
6126 
6127 	      httpFlushWrite(client->http);
6128 
6129 	      close(fd);
6130 	    }
6131 	    else
6132 	      return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6133 	  }
6134 	  else
6135 	    return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6136         }
6137         else if (!strcmp(client->uri, "/icon.png"))
6138 	{
6139 	 /*
6140 	  * Send medium PNG icon file.
6141 	  */
6142 
6143           if (client->printer->icons[1])
6144           {
6145 	    int		fd;		/* Icon file */
6146 	    struct stat	fileinfo;	/* Icon file information */
6147 	    char	buffer[4096];	/* Copy buffer */
6148 	    ssize_t	bytes;		/* Bytes */
6149 
6150 	    if (!stat(client->printer->icons[1], &fileinfo) && (fd = open(client->printer->icons[1], O_RDONLY | O_BINARY)) >= 0)
6151 	    {
6152 	      if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
6153 	      {
6154 		close(fd);
6155 		return (0);
6156 	      }
6157 
6158 	      while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
6159 		httpWrite2(client->http, buffer, (size_t)bytes);
6160 
6161 	      httpFlushWrite(client->http);
6162 
6163 	      close(fd);
6164 	    }
6165 	    else
6166 	      return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6167 	  }
6168 	  else
6169 	  {
6170 	    fputs("Icon file is internal printer.png.\n", stderr);
6171 
6172 	    if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_png)))
6173 	      return (0);
6174 
6175             httpWrite2(client->http, (const char *)printer_png, sizeof(printer_png));
6176 	    httpFlushWrite(client->http);
6177 	  }
6178 	}
6179         else if (!strcmp(client->uri, "/icon-lg.png"))
6180 	{
6181 	 /*
6182 	  * Send large PNG icon file.
6183 	  */
6184 
6185           if (client->printer->icons[2])
6186           {
6187 	    int		fd;		/* Icon file */
6188 	    struct stat	fileinfo;	/* Icon file information */
6189 	    char	buffer[4096];	/* Copy buffer */
6190 	    ssize_t	bytes;		/* Bytes */
6191 
6192 	    if (!stat(client->printer->icons[2], &fileinfo) && (fd = open(client->printer->icons[2], O_RDONLY | O_BINARY)) >= 0)
6193 	    {
6194 	      if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
6195 	      {
6196 		close(fd);
6197 		return (0);
6198 	      }
6199 
6200 	      while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
6201 		httpWrite2(client->http, buffer, (size_t)bytes);
6202 
6203 	      httpFlushWrite(client->http);
6204 
6205 	      close(fd);
6206 	    }
6207 	    else
6208 	      return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6209 	  }
6210 	  else
6211 	  {
6212 	    fputs("Icon file is internal printer-lg.png.\n", stderr);
6213 
6214 	    if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_lg_png)))
6215 	      return (0);
6216 
6217             httpWrite2(client->http, (const char *)printer_lg_png, sizeof(printer_lg_png));
6218 	    httpFlushWrite(client->http);
6219 	  }
6220 	}
6221         else if (!strcmp(client->uri, "/icon-sm.png"))
6222 	{
6223 	 /*
6224 	  * Send small PNG icon file.
6225 	  */
6226 
6227           if (client->printer->icons[0])
6228           {
6229 	    int		fd;		/* Icon file */
6230 	    struct stat	fileinfo;	/* Icon file information */
6231 	    char	buffer[4096];	/* Copy buffer */
6232 	    ssize_t	bytes;		/* Bytes */
6233 
6234 	    if (!stat(client->printer->icons[0], &fileinfo) && (fd = open(client->printer->icons[0], O_RDONLY | O_BINARY)) >= 0)
6235 	    {
6236 	      if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
6237 	      {
6238 		close(fd);
6239 		return (0);
6240 	      }
6241 
6242 	      while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
6243 		httpWrite2(client->http, buffer, (size_t)bytes);
6244 
6245 	      httpFlushWrite(client->http);
6246 
6247 	      close(fd);
6248 	    }
6249 	    else
6250 	      return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6251 	  }
6252 	  else
6253 	  {
6254 	    fputs("Icon file is internal printer-sm.png.\n", stderr);
6255 
6256 	    if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_sm_png)))
6257 	      return (0);
6258 
6259             httpWrite2(client->http, (const char *)printer_sm_png, sizeof(printer_sm_png));
6260 	    httpFlushWrite(client->http);
6261 	  }
6262 	}
6263 	else
6264 	{
6265 	 /*
6266 	  * Authenticate if needed...
6267 	  */
6268 
6269 	  if ((http_status = authenticate_request(client)) != HTTP_STATUS_CONTINUE)
6270 	  {
6271 	    return (respond_http(client, http_status, NULL, NULL, 0));
6272 	  }
6273 
6274 	  if (!strcmp(client->uri, "/"))
6275 	  {
6276 	   /*
6277 	    * Show web status page...
6278 	    */
6279 
6280 	    return (show_status(client));
6281 	  }
6282 	  else if (!strcmp(client->uri, "/media"))
6283 	  {
6284 	   /*
6285 	    * Show web media page...
6286 	    */
6287 
6288 	    return (show_media(client));
6289 	  }
6290 	  else if (!strcmp(client->uri, "/supplies"))
6291 	  {
6292 	   /*
6293 	    * Show web supplies page...
6294 	    */
6295 
6296 	    return (show_supplies(client));
6297 	  }
6298 	  else
6299 	    return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6300 	}
6301 	break;
6302 
6303     case HTTP_STATE_POST :
6304 	if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
6305 	           "application/ipp"))
6306         {
6307 	 /*
6308 	  * Not an IPP request...
6309 	  */
6310 
6311 	  return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
6312 	}
6313 
6314        /*
6315         * Read the IPP request...
6316 	*/
6317 
6318 	client->request = ippNew();
6319 
6320         while ((ipp_state = ippRead(client->http,
6321                                     client->request)) != IPP_STATE_DATA)
6322 	{
6323 	  if (ipp_state == IPP_STATE_ERROR)
6324 	  {
6325             fprintf(stderr, "%s IPP read error (%s).\n", client->hostname, cupsLastErrorString());
6326 	    respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
6327 	    return (0);
6328 	  }
6329 	}
6330 
6331        /*
6332         * Now that we have the IPP request, process the request...
6333 	*/
6334 
6335         return (process_ipp(client));
6336 
6337     default :
6338         break; /* Anti-compiler-warning-code */
6339   }
6340 
6341   return (1);
6342 }
6343 
6344 
6345 /*
6346  * 'process_ipp()' - Process an IPP request.
6347  */
6348 
6349 static int				/* O - 1 on success, 0 on error */
process_ipp(ippeve_client_t * client)6350 process_ipp(ippeve_client_t *client)	/* I - Client */
6351 {
6352   ipp_tag_t		group;		/* Current group tag */
6353   ipp_attribute_t	*attr;		/* Current attribute */
6354   ipp_attribute_t	*charset;	/* Character set attribute */
6355   ipp_attribute_t	*language;	/* Language attribute */
6356   ipp_attribute_t	*uri;		/* Printer URI attribute */
6357   int			major, minor;	/* Version number */
6358   const char		*name;		/* Name of attribute */
6359   http_status_t		status;		/* Authentication status */
6360 
6361 
6362   debug_attributes("Request", client->request, 1);
6363 
6364  /*
6365   * First build an empty response message for this request...
6366   */
6367 
6368   client->operation_id = ippGetOperation(client->request);
6369   client->response     = ippNewResponse(client->request);
6370 
6371  /*
6372   * Then validate the request header and required attributes...
6373   */
6374 
6375   major = ippGetVersion(client->request, &minor);
6376 
6377   if (major < 1 || major > 2)
6378   {
6379    /*
6380     * Return an error, since we only support IPP 1.x and 2.x.
6381     */
6382 
6383     respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, "Bad request version number %d.%d.", major, minor);
6384   }
6385   else if ((major * 10 + minor) > MaxVersion)
6386   {
6387     if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
6388       httpFlush(client->http);		/* Flush trailing (junk) data */
6389 
6390     respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
6391     return (0);
6392   }
6393   else if (ippGetRequestId(client->request) <= 0)
6394   {
6395     respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.", ippGetRequestId(client->request));
6396   }
6397   else if (!ippFirstAttribute(client->request))
6398   {
6399     respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No attributes in request.");
6400   }
6401   else
6402   {
6403    /*
6404     * Make sure that the attributes are provided in the correct order and
6405     * don't repeat groups...
6406     */
6407 
6408     for (attr = ippFirstAttribute(client->request),
6409              group = ippGetGroupTag(attr);
6410 	 attr;
6411 	 attr = ippNextAttribute(client->request))
6412     {
6413       if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
6414       {
6415        /*
6416 	* Out of order; return an error...
6417 	*/
6418 
6419 	respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6420 		    "Attribute groups are out of order (%x < %x).",
6421 		    ippGetGroupTag(attr), group);
6422 	break;
6423       }
6424       else
6425 	group = ippGetGroupTag(attr);
6426     }
6427 
6428     if (!attr)
6429     {
6430      /*
6431       * Then make sure that the first three attributes are:
6432       *
6433       *     attributes-charset
6434       *     attributes-natural-language
6435       *     printer-uri/job-uri
6436       */
6437 
6438       attr = ippFirstAttribute(client->request);
6439       name = ippGetName(attr);
6440       if (attr && name && !strcmp(name, "attributes-charset") &&
6441 	  ippGetValueTag(attr) == IPP_TAG_CHARSET)
6442 	charset = attr;
6443       else
6444 	charset = NULL;
6445 
6446       attr = ippNextAttribute(client->request);
6447       name = ippGetName(attr);
6448 
6449       if (attr && name && !strcmp(name, "attributes-natural-language") &&
6450 	  ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
6451 	language = attr;
6452       else
6453 	language = NULL;
6454 
6455       if ((attr = ippFindAttribute(client->request, "printer-uri",
6456                                    IPP_TAG_URI)) != NULL)
6457 	uri = attr;
6458       else if ((attr = ippFindAttribute(client->request, "job-uri",
6459                                         IPP_TAG_URI)) != NULL)
6460 	uri = attr;
6461       else
6462 	uri = NULL;
6463 
6464       if (charset &&
6465           strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
6466           strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
6467       {
6468        /*
6469         * Bad character set...
6470 	*/
6471 
6472 	respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6473 	            "Unsupported character set \"%s\".",
6474 	            ippGetString(charset, 0, NULL));
6475       }
6476       else if (!charset || !language || !uri)
6477       {
6478        /*
6479 	* Return an error, since attributes-charset,
6480 	* attributes-natural-language, and printer-uri/job-uri are required
6481 	* for all operations.
6482 	*/
6483 
6484 	respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6485 	            "Missing required attributes.");
6486       }
6487       else
6488       {
6489         char		scheme[32],	/* URI scheme */
6490 			userpass[32],	/* Username/password in URI */
6491 			host[256],	/* Host name in URI */
6492 			resource[256];	/* Resource path in URI */
6493 	int		port;		/* Port number in URI */
6494 
6495         name = ippGetName(uri);
6496 
6497         if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
6498                             scheme, sizeof(scheme),
6499                             userpass, sizeof(userpass),
6500                             host, sizeof(host), &port,
6501                             resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
6502 	  respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
6503 	              "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
6504         else if ((!strcmp(name, "job-uri") &&
6505                   strncmp(resource, "/ipp/print/", 11)) ||
6506                  (!strcmp(name, "printer-uri") &&
6507                   strcmp(resource, "/ipp/print") &&
6508                   (strcmp(resource, "/") || ippGetOperation(client->request) != IPP_OP_GET_PRINTER_ATTRIBUTES)))
6509 	  respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
6510 		      name, ippGetString(uri, 0, NULL));
6511 	else if (client->operation_id != IPP_OP_GET_PRINTER_ATTRIBUTES && (status = authenticate_request(client)) != HTTP_STATUS_CONTINUE)
6512         {
6513           flush_document_data(client);
6514 
6515           return (respond_http(client, status, NULL, NULL, 0));
6516         }
6517         else
6518 	{
6519 	 /*
6520 	  * Handle HTTP Expect...
6521 	  */
6522 
6523 	  if (httpGetExpect(client->http))
6524 	  {
6525 	    if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
6526 	    {
6527 	     /*
6528 	      * Send 100-continue header...
6529 	      */
6530 
6531 	      if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
6532 		return (0);
6533 	    }
6534 	    else
6535 	    {
6536 	     /*
6537 	      * Send 417-expectation-failed header...
6538 	      */
6539 
6540 	      if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
6541 		return (0);
6542 
6543 	      flush_document_data(client);
6544 	      return (1);
6545 	    }
6546 	  }
6547 
6548 	 /*
6549 	  * Try processing the operation...
6550 	  */
6551 
6552 	  switch (client->operation_id)
6553 	  {
6554 	    case IPP_OP_PRINT_JOB :
6555 		ipp_print_job(client);
6556 		break;
6557 
6558 	    case IPP_OP_PRINT_URI :
6559 		ipp_print_uri(client);
6560 		break;
6561 
6562 	    case IPP_OP_VALIDATE_JOB :
6563 		ipp_validate_job(client);
6564 		break;
6565 
6566 	    case IPP_OP_CREATE_JOB :
6567 		ipp_create_job(client);
6568 		break;
6569 
6570 	    case IPP_OP_SEND_DOCUMENT :
6571 		ipp_send_document(client);
6572 		break;
6573 
6574 	    case IPP_OP_SEND_URI :
6575 		ipp_send_uri(client);
6576 		break;
6577 
6578 	    case IPP_OP_CANCEL_JOB :
6579 		ipp_cancel_job(client);
6580 		break;
6581 
6582 	    case IPP_OP_CANCEL_MY_JOBS :
6583 		ipp_cancel_my_jobs(client);
6584 		break;
6585 
6586 	    case IPP_OP_GET_JOB_ATTRIBUTES :
6587 		ipp_get_job_attributes(client);
6588 		break;
6589 
6590 	    case IPP_OP_GET_JOBS :
6591 		ipp_get_jobs(client);
6592 		break;
6593 
6594 	    case IPP_OP_GET_PRINTER_ATTRIBUTES :
6595 		ipp_get_printer_attributes(client);
6596 		break;
6597 
6598 	    case IPP_OP_CLOSE_JOB :
6599 	        ipp_close_job(client);
6600 		break;
6601 
6602 	    case IPP_OP_IDENTIFY_PRINTER :
6603 	        ipp_identify_printer(client);
6604 		break;
6605 
6606 	    default :
6607 		respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
6608 			    "Operation not supported.");
6609 		break;
6610 	  }
6611 	}
6612       }
6613     }
6614   }
6615 
6616  /*
6617   * Send the HTTP header and return...
6618   */
6619 
6620   if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
6621     httpFlush(client->http);		/* Flush trailing (junk) data */
6622 
6623   return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
6624                        ippLength(client->response)));
6625 }
6626 
6627 
6628 /*
6629  * 'process_job()' - Process a print job.
6630  */
6631 
6632 static void *				/* O - Thread exit status */
process_job(ippeve_job_t * job)6633 process_job(ippeve_job_t *job)		/* I - Job */
6634 {
6635   job->state          = IPP_JSTATE_PROCESSING;
6636   job->printer->state = IPP_PSTATE_PROCESSING;
6637   job->processing     = time(NULL);
6638 
6639   while (job->printer->state_reasons & IPPEVE_PREASON_MEDIA_EMPTY)
6640   {
6641     job->printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
6642 
6643     sleep(1);
6644   }
6645 
6646   job->printer->state_reasons &= (ippeve_preason_t)~IPPEVE_PREASON_MEDIA_NEEDED;
6647 
6648   if (job->printer->command)
6649   {
6650    /*
6651     * Execute a command with the job spool file and wait for it to complete...
6652     */
6653 
6654     int 		pid,		/* Process ID */
6655 			status;		/* Exit status */
6656     struct timeval	start,		/* Start time */
6657 			end;		/* End time */
6658     char		*myargv[3],	/* Command-line arguments */
6659 			*myenvp[400];	/* Environment variables */
6660     int			myenvc;		/* Number of environment variables */
6661     ipp_attribute_t	*attr;		/* Job attribute */
6662     char		val[1280],	/* IPP_NAME=value */
6663 			*valptr;	/* Pointer into string */
6664 #ifndef _WIN32
6665     int			mystdout = -1;	/* File for stdout */
6666     int			mypipe[2];	/* Pipe for stderr */
6667     char		line[2048],	/* Line from stderr */
6668 			*ptr,		/* Pointer into line */
6669 			*endptr;	/* End of line */
6670     ssize_t		bytes;		/* Bytes read */
6671 #endif /* !_WIN32 */
6672 
6673     fprintf(stderr, "[Job %d] Running command \"%s %s\".\n", job->id, job->printer->command, job->filename);
6674     gettimeofday(&start, NULL);
6675 
6676    /*
6677     * Setup the command-line arguments...
6678     */
6679 
6680     myargv[0] = job->printer->command;
6681     myargv[1] = job->filename;
6682     myargv[2] = NULL;
6683 
6684    /*
6685     * Copy the current environment, then add environment variables for every
6686     * Job attribute and Printer -default attributes...
6687     */
6688 
6689     for (myenvc = 0; environ[myenvc] && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); myenvc ++)
6690       myenvp[myenvc] = strdup(environ[myenvc]);
6691 
6692     if (myenvc > (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 32))
6693     {
6694       fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id);
6695       job->state = IPP_JSTATE_ABORTED;
6696       goto error;
6697     }
6698 
6699     snprintf(val, sizeof(val), "CONTENT_TYPE=%s", job->format);
6700     myenvp[myenvc ++] = strdup(val);
6701 
6702     if (job->printer->device_uri)
6703     {
6704       snprintf(val, sizeof(val), "DEVICE_URI=%s", job->printer->device_uri);
6705       myenvp[myenvc ++] = strdup(val);
6706     }
6707 
6708     if (job->printer->output_format)
6709     {
6710       snprintf(val, sizeof(val), "OUTPUT_TYPE=%s", job->printer->output_format);
6711       myenvp[myenvc ++] = strdup(val);
6712     }
6713 
6714 #if !CUPS_LITE
6715     if (job->printer->ppdfile)
6716     {
6717       snprintf(val, sizeof(val), "PPD=%s", job->printer->ppdfile);
6718       myenvp[myenvc++] = strdup(val);
6719     }
6720 #endif /* !CUPS_LITE */
6721 
6722     for (attr = ippFirstAttribute(job->printer->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->printer->attrs))
6723     {
6724      /*
6725       * Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and
6726       * "pwg-xxx" to "IPP_PWG_XXX", then add the value(s) from the attribute.
6727       */
6728 
6729       const char	*name = ippGetName(attr),
6730 					/* Attribute name */
6731 			*suffix = strstr(name, "-default");
6732 					/* Suffix on attribute name */
6733 
6734       if (strncmp(name, "pwg-", 4) && (!suffix || suffix[8]))
6735         continue;
6736 
6737       valptr = val;
6738       *valptr++ = 'I';
6739       *valptr++ = 'P';
6740       *valptr++ = 'P';
6741       *valptr++ = '_';
6742       while (*name && valptr < (val + sizeof(val) - 2))
6743       {
6744         if (*name == '-')
6745 	  *valptr++ = '_';
6746 	else
6747 	  *valptr++ = (char)toupper(*name & 255);
6748 
6749 	name ++;
6750       }
6751       *valptr++ = '=';
6752       ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
6753 
6754       myenvp[myenvc++] = strdup(val);
6755     }
6756 
6757     for (attr = ippFirstAttribute(job->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->attrs))
6758     {
6759      /*
6760       * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6761       * value(s) from the attribute.
6762       */
6763 
6764       const char *name = ippGetName(attr);
6765 					/* Attribute name */
6766 
6767       if (!name)
6768         continue;
6769 
6770       valptr = val;
6771       *valptr++ = 'I';
6772       *valptr++ = 'P';
6773       *valptr++ = 'P';
6774       *valptr++ = '_';
6775       while (*name && valptr < (val + sizeof(val) - 2))
6776       {
6777         if (*name == '-')
6778 	  *valptr++ = '_';
6779 	else
6780 	  *valptr++ = (char)toupper(*name & 255);
6781 
6782 	name ++;
6783       }
6784       *valptr++ = '=';
6785       ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
6786 
6787       myenvp[myenvc++] = strdup(val);
6788     }
6789 
6790     if (attr)
6791     {
6792       fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id);
6793       job->state = IPP_JSTATE_ABORTED;
6794       goto error;
6795     }
6796 
6797     myenvp[myenvc] = NULL;
6798 
6799    /*
6800     * Now run the program...
6801     */
6802 
6803 #ifdef _WIN32
6804     status = _spawnvpe(_P_WAIT, job->printer->command, myargv, myenvp);
6805 
6806 #else
6807     if (job->printer->device_uri)
6808     {
6809       char	scheme[32],		/* URI scheme */
6810 		userpass[256],		/* username:password (unused) */
6811 		host[256],		/* Hostname or IP address */
6812 		resource[256];		/* Resource path */
6813       int	port;			/* Port number */
6814 
6815 
6816       if (httpSeparateURI(HTTP_URI_CODING_ALL, job->printer->device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
6817       {
6818         fprintf(stderr, "[Job %d] Bad device URI \"%s\".\n", job->id, job->printer->device_uri);
6819       }
6820       else if (!strcmp(scheme, "file"))
6821       {
6822         struct stat	fileinfo;	/* See if this is a file or directory... */
6823 
6824         if (stat(resource, &fileinfo))
6825         {
6826           if (errno == ENOENT)
6827           {
6828             if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) >= 0)
6829 	      fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
6830 	    else
6831 	      fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno));
6832           }
6833           else
6834             fprintf(stderr, "[Job %d] Unable to access \"%s\": %s\n", job->id, resource, strerror(errno));
6835         }
6836         else if (S_ISDIR(fileinfo.st_mode))
6837         {
6838           if ((mystdout = create_job_file(job, line, sizeof(line), resource, "prn")) >= 0)
6839 	    fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line);
6840           else
6841             fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, line, strerror(errno));
6842         }
6843 	else if (!S_ISREG(fileinfo.st_mode))
6844 	{
6845 	  if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) >= 0)
6846 	    fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
6847 	  else
6848             fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno));
6849 	}
6850         else if ((mystdout = open(resource, O_WRONLY | O_BINARY)) >= 0)
6851 	  fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
6852 	else
6853 	  fprintf(stderr, "[Job %d] Unable to open \"%s\": %s\n", job->id, resource, strerror(errno));
6854       }
6855       else if (!strcmp(scheme, "socket"))
6856       {
6857         http_addrlist_t	*addrlist;	/* List of addresses */
6858         char		service[32];	/* Service number */
6859 
6860         snprintf(service, sizeof(service), "%d", port);
6861 
6862         if ((addrlist = httpAddrGetList(host, AF_UNSPEC, service)) == NULL)
6863           fprintf(stderr, "[Job %d] Unable to find \"%s\": %s\n", job->id, host, cupsLastErrorString());
6864         else if (!httpAddrConnect2(addrlist, &mystdout, 30000, &(job->cancel)))
6865           fprintf(stderr, "[Job %d] Unable to connect to \"%s\": %s\n", job->id, host, cupsLastErrorString());
6866 
6867         httpAddrFreeList(addrlist);
6868       }
6869       else
6870       {
6871         fprintf(stderr, "[Job %d] Unsupported device URI scheme \"%s\".\n", job->id, scheme);
6872       }
6873     }
6874     else if ((mystdout = create_job_file(job, line, sizeof(line), job->printer->directory, "prn")) >= 0)
6875     {
6876       fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line);
6877     }
6878 
6879     if (mystdout < 0)
6880     {
6881       if ((mystdout = open("/dev/null", O_WRONLY | O_BINARY)) < 0)
6882         fprintf(stderr, "[Job %d] Unable to redirect command output to /dev/null: %s", job->id, strerror(errno));
6883     }
6884 
6885     if (pipe(mypipe))
6886     {
6887       fprintf(stderr, "[Job %d] Unable to create pipe for stderr: %s\n", job->id, strerror(errno));
6888       mypipe[0] = mypipe[1] = -1;
6889     }
6890 
6891     if ((pid = fork()) == 0)
6892     {
6893      /*
6894       * Child comes here...
6895       */
6896 
6897       if (mystdout >= 0)
6898       {
6899 	close(1);
6900         dup2(mystdout, 1);
6901         close(mystdout);
6902       }
6903 
6904       if (mypipe[1] >= 0)
6905       {
6906 	close(2);
6907 	dup2(mypipe[1], 2);
6908 	close(mypipe[0]);
6909 	close(mypipe[1]);
6910       }
6911 
6912       execve(job->printer->command, myargv, myenvp);
6913       exit(errno);
6914     }
6915     else if (pid < 0)
6916     {
6917      /*
6918       * Unable to fork process...
6919       */
6920 
6921       fprintf(stderr, "[Job %d] Unable to start job processing command: %s\n", job->id, strerror(errno));
6922       status = -1;
6923 
6924       close(mystdout);
6925       close(mypipe[0]);
6926       close(mypipe[1]);
6927 
6928      /*
6929       * Free memory used for environment...
6930       */
6931 
6932       while (myenvc > 0)
6933 	free(myenvp[-- myenvc]);
6934     }
6935     else
6936     {
6937      /*
6938       * Free memory used for environment...
6939       */
6940 
6941       while (myenvc > 0)
6942 	free(myenvp[-- myenvc]);
6943 
6944      /*
6945       * Close the output file in the parent process...
6946       */
6947 
6948       close(mystdout);
6949 
6950      /*
6951       * If the pipe exists, read from it until EOF...
6952       */
6953 
6954       if (mypipe[0] >= 0)
6955       {
6956 	close(mypipe[1]);
6957 
6958 	endptr = line;
6959 	while ((bytes = read(mypipe[0], endptr, sizeof(line) - (size_t)(endptr - line) - 1)) > 0)
6960 	{
6961 	  endptr += bytes;
6962 	  *endptr = '\0';
6963 
6964           while ((ptr = strchr(line, '\n')) != NULL)
6965 	  {
6966 	    int level = 3;		/* Message log level */
6967 
6968 	    *ptr++ = '\0';
6969 
6970 	    if (!strncmp(line, "ATTR:", 5))
6971 	    {
6972 	     /*
6973 	      * Process job/printer attribute updates.
6974 	      */
6975 
6976 	      process_attr_message(job, line);
6977 	    }
6978 	    else if (!strncmp(line, "DEBUG:", 6))
6979 	    {
6980 	     /*
6981 	      * Debug message...
6982 	      */
6983 
6984               level = 2;
6985 	    }
6986 	    else if (!strncmp(line, "ERROR:", 6))
6987 	    {
6988 	     /*
6989 	      * Error message...
6990 	      */
6991 
6992               level         = 0;
6993               job->message  = strdup(line + 6);
6994               job->msglevel = 0;
6995 	    }
6996 	    else if (!strncmp(line, "INFO:", 5))
6997 	    {
6998 	     /*
6999 	      * Informational/progress message...
7000 	      */
7001 
7002               level = 1;
7003               if (job->msglevel)
7004               {
7005                 job->message  = strdup(line + 5);
7006                 job->msglevel = 1;
7007 	      }
7008 	    }
7009 	    else if (!strncmp(line, "STATE:", 6))
7010 	    {
7011 	     /*
7012 	      * Process printer-state-reasons keywords.
7013 	      */
7014 
7015 	      process_state_message(job, line);
7016 	    }
7017 
7018 	    if (Verbosity >= level)
7019 	      fprintf(stderr, "[Job %d] Command - %s\n", job->id, line);
7020 
7021 	    bytes = ptr - line;
7022             if (ptr < endptr)
7023 	      memmove(line, ptr, (size_t)(endptr - ptr));
7024 	    endptr -= bytes;
7025 	    *endptr = '\0';
7026 	  }
7027 	}
7028 
7029 	close(mypipe[0]);
7030       }
7031 
7032      /*
7033       * Wait for child to complete...
7034       */
7035 
7036 #  ifdef HAVE_WAITPID
7037       while (waitpid(pid, &status, 0) < 0);
7038 #  else
7039       while (wait(&status) < 0);
7040 #  endif /* HAVE_WAITPID */
7041     }
7042 #endif /* _WIN32 */
7043 
7044     if (status)
7045     {
7046 #ifndef _WIN32
7047       if (WIFEXITED(status))
7048 #endif /* !_WIN32 */
7049 	fprintf(stderr, "[Job %d] Command \"%s\" exited with status %d.\n", job->id,  job->printer->command, WEXITSTATUS(status));
7050 #ifndef _WIN32
7051       else
7052 	fprintf(stderr, "[Job %d] Command \"%s\" terminated with signal %d.\n", job->id, job->printer->command, WTERMSIG(status));
7053 #endif /* !_WIN32 */
7054       job->state = IPP_JSTATE_ABORTED;
7055     }
7056     else
7057       fprintf(stderr, "[Job %d] Command \"%s\" completed successfully.\n", job->id, job->printer->command);
7058 
7059    /*
7060     * Report the total processing time...
7061     */
7062 
7063     gettimeofday(&end, NULL);
7064 
7065     fprintf(stderr, "[Job %d] Processing time was %.3f seconds.\n", job->id, end.tv_sec - start.tv_sec + 0.000001 * (end.tv_usec - start.tv_usec));
7066   }
7067   else
7068   {
7069    /*
7070     * Sleep for a random amount of time to simulate job processing.
7071     */
7072 
7073     sleep((unsigned)(5 + (CUPS_RAND() % 11)));
7074   }
7075 
7076   if (job->cancel)
7077     job->state = IPP_JSTATE_CANCELED;
7078   else if (job->state == IPP_JSTATE_PROCESSING)
7079     job->state = IPP_JSTATE_COMPLETED;
7080 
7081   error:
7082 
7083   job->completed           = time(NULL);
7084   job->printer->state      = IPP_PSTATE_IDLE;
7085   job->printer->active_job = NULL;
7086 
7087   return (NULL);
7088 }
7089 
7090 
7091 /*
7092  * 'process_state_message()' - Process a STATE: message from a command.
7093  */
7094 
7095 static void
process_state_message(ippeve_job_t * job,char * message)7096 process_state_message(
7097     ippeve_job_t *job,			/* I - Job */
7098     char       *message)		/* I - Message */
7099 {
7100   int		i;			/* Looping var */
7101   ippeve_preason_t state_reasons,		/* printer-state-reasons values */
7102 		bit;			/* Current reason bit */
7103   char		*ptr,			/* Pointer into message */
7104 		*next;			/* Next keyword in message */
7105   int		remove;			/* Non-zero if we are removing keywords */
7106 
7107 
7108  /*
7109   * Skip leading "STATE:" and any whitespace...
7110   */
7111 
7112   for (message += 6; *message; message ++)
7113     if (*message != ' ' && *message != '\t')
7114       break;
7115 
7116  /*
7117   * Support the following forms of message:
7118   *
7119   * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
7120   *
7121   * "-keyword[,keyword,...]" to remove keywords.
7122   *
7123   * "+keyword[,keyword,...]" to add keywords.
7124   *
7125   * Keywords may or may not have a suffix (-report, -warning, -error) per
7126   * RFC 8011.
7127   */
7128 
7129   if (*message == '-')
7130   {
7131     remove        = 1;
7132     state_reasons = job->printer->state_reasons;
7133     message ++;
7134   }
7135   else if (*message == '+')
7136   {
7137     remove        = 0;
7138     state_reasons = job->printer->state_reasons;
7139     message ++;
7140   }
7141   else
7142   {
7143     remove        = 0;
7144     state_reasons = IPPEVE_PREASON_NONE;
7145   }
7146 
7147   while (*message)
7148   {
7149     if ((next = strchr(message, ',')) != NULL)
7150       *next++ = '\0';
7151 
7152     if ((ptr = strstr(message, "-error")) != NULL)
7153       *ptr = '\0';
7154     else if ((ptr = strstr(message, "-report")) != NULL)
7155       *ptr = '\0';
7156     else if ((ptr = strstr(message, "-warning")) != NULL)
7157       *ptr = '\0';
7158 
7159     for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
7160     {
7161       if (!strcmp(message, ippeve_preason_strings[i]))
7162       {
7163         if (remove)
7164 	  state_reasons &= ~bit;
7165 	else
7166 	  state_reasons |= bit;
7167       }
7168     }
7169 
7170     if (next)
7171       message = next;
7172     else
7173       break;
7174   }
7175 
7176   job->printer->state_reasons = state_reasons;
7177 }
7178 
7179 
7180 /*
7181  * 'register_printer()' - Register a printer object via DNS-SD.
7182  */
7183 
7184 static int				/* O - 1 on success, 0 on error */
register_printer(ippeve_printer_t * printer)7185 register_printer(
7186     ippeve_printer_t *printer)		/* I - Printer */
7187 {
7188 #ifdef HAVE_DNSSD
7189   ippeve_txt_t		ipp_txt;	/* DNS-SD IPP TXT record */
7190   int			i,		/* Looping var */
7191 			count;		/* Number of values */
7192   ipp_attribute_t	*color_supported,
7193 			*document_format_supported,
7194 			*printer_location,
7195 			*printer_make_and_model,
7196 			*printer_uuid,
7197 			*sides_supported,
7198 			*urf_supported;	/* Printer attributes */
7199   const char		*value;		/* Value string */
7200   char			adminurl[247],	/* adminurl value */
7201 			formats[252],	/* List of supported formats */
7202 			urf[252],	/* List of supported URF values */
7203 			*ptr;		/* Pointer into string */
7204 
7205 
7206   if (printer->dnssd_subtypes && !strcmp(printer->dnssd_subtypes, "off"))
7207     return (1);
7208 
7209   color_supported           = ippFindAttribute(printer->attrs, "color-supported", IPP_TAG_BOOLEAN);
7210   document_format_supported = ippFindAttribute(printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE);
7211   printer_location          = ippFindAttribute(printer->attrs, "printer-location", IPP_TAG_TEXT);
7212   printer_make_and_model    = ippFindAttribute(printer->attrs, "printer-make-and-model", IPP_TAG_TEXT);
7213   printer_uuid              = ippFindAttribute(printer->attrs, "printer-uuid", IPP_TAG_URI);
7214   sides_supported           = ippFindAttribute(printer->attrs, "sides-supported", IPP_TAG_KEYWORD);
7215   urf_supported             = ippFindAttribute(printer->attrs, "urf-supported", IPP_TAG_KEYWORD);
7216 
7217   httpAssembleURI(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), WEB_SCHEME, NULL, printer->hostname, printer->port, "/");
7218 
7219   for (i = 0, count = ippGetCount(document_format_supported), ptr = formats; i < count; i ++)
7220   {
7221     value = ippGetString(document_format_supported, i, NULL);
7222 
7223     if (!strcasecmp(value, "application/octet-stream"))
7224       continue;
7225 
7226     if (ptr > formats && ptr < (formats + sizeof(formats) - 1))
7227       *ptr++ = ',';
7228 
7229     strlcpy(ptr, value, sizeof(formats) - (size_t)(ptr - formats));
7230     ptr += strlen(ptr);
7231 
7232     if (ptr >= (formats + sizeof(formats) - 1))
7233       break;
7234   }
7235 
7236   urf[0] = '\0';
7237   for (i = 0, count = ippGetCount(urf_supported), ptr = urf; i < count; i ++)
7238   {
7239     value = ippGetString(urf_supported, i, NULL);
7240 
7241     if (ptr > urf && ptr < (urf + sizeof(urf) - 1))
7242       *ptr++ = ',';
7243 
7244     strlcpy(ptr, value, sizeof(urf) - (size_t)(ptr - urf));
7245     ptr += strlen(ptr);
7246 
7247     if (ptr >= (urf + sizeof(urf) - 1))
7248       break;
7249   }
7250 
7251  /*
7252   * Rename the service as needed...
7253   */
7254 
7255   if (printer->dnssd_collision)
7256   {
7257     char	new_dnssd_name[256];	/* New DNS-SD name */
7258     const char	*uuid = ippGetString(printer_uuid, 0, NULL);
7259 					/* "printer-uuid" value */
7260 
7261     _cupsRWLockWrite(&printer->rwlock);
7262 
7263     snprintf(new_dnssd_name, sizeof(new_dnssd_name), "%s (%c%c%c%c%c%c)", printer->dnssd_name, toupper(uuid[39]), toupper(uuid[40]), toupper(uuid[41]), toupper(uuid[42]), toupper(uuid[43]), toupper(uuid[44]));
7264 
7265     free(printer->dnssd_name);
7266     printer->dnssd_name = strdup(new_dnssd_name);
7267 
7268     fprintf(stderr, "DNS-SD name collision, trying new DNS-SD service name '%s'.\n", printer->dnssd_name);
7269 
7270     _cupsRWUnlock(&printer->rwlock);
7271 
7272     printer->dnssd_collision = 0;
7273   }
7274 #endif /* HAVE_DNSSD */
7275 
7276 #ifdef HAVE_MDNSRESPONDER
7277   DNSServiceErrorType	error;		/* Error from DNS-SD */
7278   char			regtype[256];	/* DNS-SD service type */
7279   uint32_t		ifindex;	/* Interface index */
7280 
7281 
7282  /*
7283   * Build the TXT record for IPP...
7284   */
7285 
7286   TXTRecordCreate(&ipp_txt, 1024, NULL);
7287   TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print");
7288   if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
7289     TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(value), value);
7290   TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(adminurl), adminurl);
7291   if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
7292     TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(value), value);
7293   TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats), formats);
7294   TXTRecordSetValue(&ipp_txt, "Color", 1, ippGetBoolean(color_supported, 0) ? "T" : "F");
7295   TXTRecordSetValue(&ipp_txt, "Duplex", 1, ippGetCount(sides_supported) > 1 ? "T" : "F");
7296   if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
7297     TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(value) - 9, value + 9);
7298 #  ifdef HAVE_TLS
7299   TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2");
7300 #  endif /* HAVE_TLS */
7301   if (urf[0])
7302     TXTRecordSetValue(&ipp_txt, "URF", (uint8_t)strlen(urf), urf);
7303   TXTRecordSetValue(&ipp_txt, "txtvers", 1, "1");
7304   TXTRecordSetValue(&ipp_txt, "qtotal", 1, "1");
7305 
7306  /*
7307   * Register the _printer._tcp (LPD) service type with a port number of 0 to
7308   * defend our service name but not actually support LPD...
7309   */
7310 
7311   ifindex = !strcmp(printer->hostname, "localhost") ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
7312 
7313   if (printer->printer_ref)
7314     DNSServiceRefDeallocate(printer->printer_ref);
7315 
7316   printer->printer_ref = DNSSDMaster;
7317 
7318   if ((error = DNSServiceRegister(&(printer->printer_ref), kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, ifindex, printer->dnssd_name, "_printer._tcp", NULL /* domain */, NULL /* host */, 0 /* port */, 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
7319   {
7320     _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_printer._tcp", error);
7321     return (0);
7322   }
7323 
7324  /*
7325   * Then register the _ipp._tcp (IPP) service type with the real port number to
7326   * advertise our IPP printer...
7327   */
7328 
7329   if (printer->ipp_ref)
7330     DNSServiceRefDeallocate(printer->ipp_ref);
7331 
7332   printer->ipp_ref = DNSSDMaster;
7333 
7334   if (printer->dnssd_subtypes && *(printer->dnssd_subtypes))
7335     snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", printer->dnssd_subtypes);
7336   else
7337     strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
7338 
7339   if ((error = DNSServiceRegister(&(printer->ipp_ref), kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, ifindex, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
7340   {
7341     _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
7342     return (0);
7343   }
7344 
7345 #  ifdef HAVE_TLS
7346  /*
7347   * Then register the _ipps._tcp (IPP) service type with the real port number to
7348   * advertise our IPPS printer...
7349   */
7350 
7351   if (printer->ipps_ref)
7352     DNSServiceRefDeallocate(printer->ipps_ref);
7353 
7354   printer->ipps_ref = DNSSDMaster;
7355 
7356   if (printer->dnssd_subtypes && *(printer->dnssd_subtypes))
7357     snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", printer->dnssd_subtypes);
7358   else
7359     strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
7360 
7361   if ((error = DNSServiceRegister(&(printer->ipps_ref), kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, ifindex, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
7362   {
7363     _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
7364     return (0);
7365   }
7366 #  endif /* HAVE_TLS */
7367 
7368  /*
7369   * Similarly, register the _http._tcp,_printer (HTTP) service type with the
7370   * real port number to advertise our IPP printer...
7371   */
7372 
7373   if (printer->http_ref)
7374     DNSServiceRefDeallocate(printer->http_ref);
7375 
7376   printer->http_ref = DNSSDMaster;
7377 
7378   if ((error = DNSServiceRegister(&(printer->http_ref), kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, ifindex, printer->dnssd_name, "_http._tcp,_printer", NULL /* domain */, NULL /* host */, htons(printer->port), 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
7379   {
7380     _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_http._tcp,_printer", error);
7381     return (0);
7382   }
7383 
7384   TXTRecordDeallocate(&ipp_txt);
7385 
7386 #elif defined(HAVE_AVAHI)
7387   char		temp[256];		/* Subtype service string */
7388 
7389  /*
7390   * Create the TXT record...
7391   */
7392 
7393   ipp_txt = NULL;
7394   ipp_txt = avahi_string_list_add_printf(ipp_txt, "rp=ipp/print");
7395   if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
7396     ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s", value);
7397   ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", adminurl);
7398   if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
7399     ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", value);
7400   ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
7401   ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", ippGetBoolean(color_supported, 0) ? "T" : "F");
7402   ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", ippGetCount(sides_supported) > 1 ? "T" : "F");
7403   if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
7404     ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", value + 9);
7405 #  ifdef HAVE_TLS
7406   ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2");
7407 #  endif /* HAVE_TLS */
7408   if (urf[0])
7409     ipp_txt = avahi_string_list_add_printf(ipp_txt, "URF=%s", urf);
7410   ipp_txt = avahi_string_list_add_printf(ipp_txt, "txtvers=1");
7411   ipp_txt = avahi_string_list_add_printf(ipp_txt, "qtotal=1");
7412 
7413  /*
7414   * Register _printer._tcp (LPD) with port 0 to reserve the service name...
7415   */
7416 
7417   avahi_threaded_poll_lock(DNSSDMaster);
7418 
7419   if (printer->dnssd_ref)
7420     avahi_entry_group_free(printer->dnssd_ref);
7421 
7422   printer->dnssd_ref = avahi_entry_group_new(DNSSDClient, dnssd_callback, printer);
7423 
7424   avahi_entry_group_add_service_strlst(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_printer._tcp", NULL, NULL, 0, NULL);
7425 
7426  /*
7427   * Then register the _ipp._tcp (IPP)...
7428   */
7429 
7430   avahi_entry_group_add_service_strlst(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, NULL, printer->port, ipp_txt);
7431   if (printer->dnssd_subtypes && *(printer->dnssd_subtypes))
7432   {
7433     char *temptypes = strdup(printer->dnssd_subtypes), *start, *end;
7434 
7435     for (start = temptypes; *start; start = end)
7436     {
7437       if ((end = strchr(start, ',')) != NULL)
7438         *end++ = '\0';
7439       else
7440         end = start + strlen(start);
7441 
7442       snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", start);
7443       avahi_entry_group_add_service_subtype(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp);
7444     }
7445 
7446     free(temptypes);
7447   }
7448 
7449 #ifdef HAVE_TLS
7450  /*
7451   * _ipps._tcp (IPPS) for secure printing...
7452   */
7453 
7454   avahi_entry_group_add_service_strlst(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, NULL, printer->port, ipp_txt);
7455   if (printer->dnssd_subtypes && *(printer->dnssd_subtypes))
7456   {
7457     char *temptypes = strdup(printer->dnssd_subtypes), *start, *end;
7458 
7459     for (start = temptypes; *start; start = end)
7460     {
7461       if ((end = strchr(start, ',')) != NULL)
7462         *end++ = '\0';
7463       else
7464         end = start + strlen(start);
7465 
7466       snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", start);
7467       avahi_entry_group_add_service_subtype(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp);
7468     }
7469 
7470     free(temptypes);
7471   }
7472 #endif /* HAVE_TLS */
7473 
7474  /*
7475   * Finally _http.tcp (HTTP) for the web interface...
7476   */
7477 
7478   avahi_entry_group_add_service_strlst(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_http._tcp", NULL, NULL, printer->port, NULL);
7479   avahi_entry_group_add_service_subtype(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_http._tcp", NULL, "_printer._sub._http._tcp");
7480 
7481  /*
7482   * Commit it...
7483   */
7484 
7485   avahi_entry_group_commit(printer->dnssd_ref);
7486   avahi_threaded_poll_unlock(DNSSDMaster);
7487 
7488   avahi_string_list_free(ipp_txt);
7489 #endif /* HAVE_MDNSRESPONDER */
7490 
7491   return (1);
7492 }
7493 
7494 
7495 /*
7496  * 'respond_http()' - Send a HTTP response.
7497  */
7498 
7499 int					/* O - 1 on success, 0 on failure */
respond_http(ippeve_client_t * client,http_status_t code,const char * content_encoding,const char * type,size_t length)7500 respond_http(
7501     ippeve_client_t *client,		/* I - Client */
7502     http_status_t code,			/* I - HTTP status of response */
7503     const char    *content_encoding,	/* I - Content-Encoding of response */
7504     const char    *type,		/* I - MIME media type of response */
7505     size_t        length)		/* I - Length of response */
7506 {
7507   char	message[1024];			/* Text message */
7508 
7509 
7510   fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
7511 
7512   if (code == HTTP_STATUS_CONTINUE)
7513   {
7514    /*
7515     * 100-continue doesn't send any headers...
7516     */
7517 
7518     return (!httpWriteResponse(client->http, HTTP_STATUS_CONTINUE));
7519   }
7520 
7521  /*
7522   * Format an error message...
7523   */
7524 
7525   if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
7526   {
7527     snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
7528 
7529     type   = "text/plain";
7530     length = strlen(message);
7531   }
7532   else
7533     message[0] = '\0';
7534 
7535  /*
7536   * Send the HTTP response header...
7537   */
7538 
7539   httpClearFields(client->http);
7540 
7541   if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
7542       client->operation == HTTP_STATE_OPTIONS)
7543     httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
7544 
7545   if (code == HTTP_STATUS_UNAUTHORIZED)
7546   {
7547     char value[256];			/* WWW-Authenticate value */
7548 
7549     snprintf(value, sizeof(value), "Basic realm=\"%s\"", PAMService);
7550     httpSetField(client->http, HTTP_FIELD_WWW_AUTHENTICATE, value);
7551   }
7552 
7553   if (type)
7554   {
7555     if (!strcmp(type, "text/html"))
7556       httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
7557                    "text/html; charset=utf-8");
7558     else
7559       httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
7560 
7561     if (content_encoding)
7562       httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
7563   }
7564 
7565   httpSetLength(client->http, length);
7566 
7567   if (httpWriteResponse(client->http, code))
7568     return (0);
7569 
7570  /*
7571   * Send the response data...
7572   */
7573 
7574   if (message[0])
7575   {
7576    /*
7577     * Send a plain text message.
7578     */
7579 
7580     if (httpPrintf(client->http, "%s", message) < 0)
7581       return (0);
7582 
7583     if (httpWrite2(client->http, "", 0) < 0)
7584       return (0);
7585   }
7586   else if (client->response)
7587   {
7588    /*
7589     * Send an IPP response...
7590     */
7591 
7592     debug_attributes("Response", client->response, 2);
7593 
7594     ippSetState(client->response, IPP_STATE_IDLE);
7595 
7596     if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
7597       return (0);
7598   }
7599 
7600   return (1);
7601 }
7602 
7603 
7604 /*
7605  * 'respond_ignored()' - Respond with an ignored attribute.
7606  */
7607 
7608 static void
respond_ignored(ippeve_client_t * client,ipp_attribute_t * attr)7609 respond_ignored(
7610     ippeve_client_t *client,		/* I - Client */
7611     ipp_attribute_t *attr)		/* I - Attribute */
7612 {
7613   ipp_attribute_t	*temp;		/* Copy of attribute */
7614 
7615 
7616   if (!ippGetStatusCode(client->response))
7617     respond_ipp(client, IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED, "Unsupported %s %s%s value.", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)));
7618 
7619   temp = ippCopyAttribute(client->response, attr, 0);
7620   ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
7621 }
7622 
7623 
7624 /*
7625  * 'respond_ipp()' - Send an IPP response.
7626  */
7627 
7628 static void
respond_ipp(ippeve_client_t * client,ipp_status_t status,const char * message,...)7629 respond_ipp(ippeve_client_t *client,	/* I - Client */
7630             ipp_status_t  status,	/* I - status-code */
7631 	    const char    *message,	/* I - printf-style status-message */
7632 	    ...)			/* I - Additional args as needed */
7633 {
7634   const char	*formatted = NULL;	/* Formatted message */
7635 
7636 
7637   ippSetStatusCode(client->response, status);
7638 
7639   if (message)
7640   {
7641     va_list		ap;		/* Pointer to additional args */
7642     ipp_attribute_t	*attr;		/* New status-message attribute */
7643 
7644     va_start(ap, message);
7645     if ((attr = ippFindAttribute(client->response, "status-message", IPP_TAG_TEXT)) != NULL)
7646       ippSetStringfv(client->response, &attr, 0, message, ap);
7647     else
7648       attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, "status-message", NULL, message, ap);
7649     va_end(ap);
7650 
7651     formatted = ippGetString(attr, 0, NULL);
7652   }
7653 
7654   if (formatted)
7655     fprintf(stderr, "%s %s %s (%s)\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status), formatted);
7656   else
7657     fprintf(stderr, "%s %s %s\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status));
7658 }
7659 
7660 
7661 /*
7662  * 'respond_unsupported()' - Respond with an unsupported attribute.
7663  */
7664 
7665 static void
respond_unsupported(ippeve_client_t * client,ipp_attribute_t * attr)7666 respond_unsupported(
7667     ippeve_client_t *client,		/* I - Client */
7668     ipp_attribute_t *attr)		/* I - Attribute */
7669 {
7670   ipp_attribute_t	*temp;		/* Copy of attribute */
7671 
7672 
7673   respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Unsupported %s %s%s value.", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)));
7674 
7675   temp = ippCopyAttribute(client->response, attr, 0);
7676   ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
7677 }
7678 
7679 
7680 /*
7681  * 'run_printer()' - Run the printer service.
7682  */
7683 
7684 static void
run_printer(ippeve_printer_t * printer)7685 run_printer(ippeve_printer_t *printer)	/* I - Printer */
7686 {
7687   int			num_fds;	/* Number of file descriptors */
7688   struct pollfd		polldata[3];	/* poll() data */
7689   ippeve_client_t	*client;	/* New client */
7690 
7691 
7692 #ifndef _WIN32
7693  /*
7694   * Set signal handlers for SIGINT and SIGTERM...
7695   */
7696 
7697   signal(SIGINT, signal_handler);
7698   signal(SIGTERM, signal_handler);
7699 #endif // !_WIN32
7700 
7701  /*
7702   * Setup poll() data for the DNS-SD service socket and IPv4/6 listeners...
7703   */
7704 
7705   polldata[0].fd     = printer->ipv4;
7706   polldata[0].events = POLLIN;
7707 
7708   polldata[1].fd     = printer->ipv6;
7709   polldata[1].events = POLLIN;
7710 
7711   num_fds = 2;
7712 
7713 #ifdef HAVE_MDNSRESPONDER
7714   polldata[num_fds   ].fd     = DNSServiceRefSockFD(DNSSDMaster);
7715   polldata[num_fds ++].events = POLLIN;
7716 #endif /* HAVE_MDNSRESPONDER */
7717 
7718  /*
7719   * Loop until we are killed or have a hard error...
7720   */
7721 
7722   for (;;)
7723   {
7724     if (poll(polldata, (nfds_t)num_fds, 1000) < 0 && errno != EINTR)
7725     {
7726       perror("poll() failed");
7727       break;
7728     }
7729 
7730 #ifndef _WIN32
7731     if (StopPrinter)
7732       break;
7733 #endif // !_WIN32
7734 
7735     if (polldata[0].revents & POLLIN)
7736     {
7737       if ((client = create_client(printer, printer->ipv4)) != NULL)
7738       {
7739         _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
7740 
7741         if (t)
7742         {
7743           _cupsThreadDetach(t);
7744         }
7745         else
7746 	{
7747 	  perror("Unable to create client thread");
7748 	  delete_client(client);
7749 	}
7750       }
7751     }
7752 
7753     if (polldata[1].revents & POLLIN)
7754     {
7755       if ((client = create_client(printer, printer->ipv6)) != NULL)
7756       {
7757         _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
7758 
7759         if (t)
7760         {
7761           _cupsThreadDetach(t);
7762         }
7763         else
7764 	{
7765 	  perror("Unable to create client thread");
7766 	  delete_client(client);
7767 	}
7768       }
7769     }
7770 
7771    /*
7772     * Process DNS-SD messages...
7773     */
7774 
7775 #ifdef HAVE_MDNSRESPONDER
7776     if (polldata[2].revents & POLLIN)
7777       DNSServiceProcessResult(DNSSDMaster);
7778 #endif /* HAVE_MDNSRESPONDER */
7779 
7780 #ifdef HAVE_DNSSD
7781     if (printer->dnssd_collision)
7782       register_printer(printer);
7783 #endif /* HAVE_DNSSD */
7784 
7785    /*
7786     * Clean out old jobs...
7787     */
7788 
7789     clean_jobs(printer);
7790   }
7791 }
7792 
7793 
7794 /*
7795  * 'show_media()' - Show media load state.
7796  */
7797 
7798 static int				/* O - 1 on success, 0 on failure */
show_media(ippeve_client_t * client)7799 show_media(ippeve_client_t  *client)	/* I - Client connection */
7800 {
7801   ippeve_printer_t *printer = client->printer;
7802 					/* Printer */
7803   int			i, j,		/* Looping vars */
7804                         num_ready,	/* Number of ready media */
7805                         num_sizes,	/* Number of media sizes */
7806 			num_sources,	/* Number of media sources */
7807                         num_types;	/* Number of media types */
7808   ipp_attribute_t	*media_col_ready,/* media-col-ready attribute */
7809                         *media_ready,	/* media-ready attribute */
7810                         *media_sizes,	/* media-supported attribute */
7811                         *media_sources,	/* media-source-supported attribute */
7812                         *media_types,	/* media-type-supported attribute */
7813                         *input_tray;	/* printer-input-tray attribute */
7814   ipp_t			*media_col;	/* media-col value */
7815   const char            *media_size,	/* media value */
7816                         *media_source,	/* media-source value */
7817                         *media_type,	/* media-type value */
7818                         *ready_size,	/* media-col-ready media-size[-name] value */
7819                         *ready_source,	/* media-col-ready media-source value */
7820                         *ready_tray,	/* printer-input-tray value */
7821                         *ready_type;	/* media-col-ready media-type value */
7822   char			tray_str[1024],	/* printer-input-tray string value */
7823 			*tray_ptr;	/* Pointer into value */
7824   int			tray_len;	/* Length of printer-input-tray value */
7825   int			ready_sheets;	/* printer-input-tray sheets value */
7826   int			num_options = 0;/* Number of form options */
7827   cups_option_t		*options = NULL;/* Form options */
7828   static const int	sheets[] =	/* Number of sheets */
7829   {
7830     250,
7831     125,
7832     50,
7833     25,
7834     5,
7835     0,
7836     -2
7837   };
7838 
7839 
7840   if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7841     return (0);
7842 
7843   html_header(client, printer->name, 0);
7844 
7845   if ((media_col_ready = ippFindAttribute(printer->attrs, "media-col-ready", IPP_TAG_BEGIN_COLLECTION)) == NULL)
7846   {
7847     html_printf(client, "<p>Error: No media-col-ready defined for printer.</p>\n");
7848     html_footer(client);
7849     return (1);
7850   }
7851 
7852   media_ready = ippFindAttribute(printer->attrs, "media-ready", IPP_TAG_ZERO);
7853 
7854   if ((media_sizes = ippFindAttribute(printer->attrs, "media-supported", IPP_TAG_ZERO)) == NULL)
7855   {
7856     html_printf(client, "<p>Error: No media-supported defined for printer.</p>\n");
7857     html_footer(client);
7858     return (1);
7859   }
7860 
7861   if ((media_sources = ippFindAttribute(printer->attrs, "media-source-supported", IPP_TAG_ZERO)) == NULL)
7862   {
7863     html_printf(client, "<p>Error: No media-source-supported defined for printer.</p>\n");
7864     html_footer(client);
7865     return (1);
7866   }
7867 
7868   if ((media_types = ippFindAttribute(printer->attrs, "media-type-supported", IPP_TAG_ZERO)) == NULL)
7869   {
7870     html_printf(client, "<p>Error: No media-type-supported defined for printer.</p>\n");
7871     html_footer(client);
7872     return (1);
7873   }
7874 
7875   if ((input_tray = ippFindAttribute(printer->attrs, "printer-input-tray", IPP_TAG_STRING)) == NULL)
7876   {
7877     html_printf(client, "<p>Error: No printer-input-tray defined for printer.</p>\n");
7878     html_footer(client);
7879     return (1);
7880   }
7881 
7882   num_sources = ippGetCount(media_sources);
7883 
7884  /*
7885   * Make sure the number of trays is consistent.
7886   */
7887 
7888   if (num_sources != ippGetCount(input_tray))
7889   {
7890     html_printf(client, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n");
7891     html_footer(client);
7892     return (1);
7893   }
7894 
7895   num_ready   = ippGetCount(media_col_ready);
7896   num_sizes   = ippGetCount(media_sizes);
7897   num_types   = ippGetCount(media_types);
7898 
7899  /*
7900   * Process form data if present...
7901   */
7902 
7903   if (printer->web_forms)
7904     num_options = parse_options(client, &options);
7905 
7906   if (num_options > 0)
7907   {
7908    /*
7909     * WARNING: A real printer/server implementation MUST NOT implement
7910     * media updates via a GET request - GET requests are supposed to be
7911     * idempotent (without side-effects) and we obviously are not
7912     * authenticating access here.  This form is provided solely to
7913     * enable testing and development!
7914     */
7915 
7916     char	name[255];		/* Form name */
7917     const char	*val;			/* Form value */
7918     pwg_media_t	*media;			/* Media info */
7919 
7920     _cupsRWLockWrite(&printer->rwlock);
7921 
7922     ippDeleteAttribute(printer->attrs, media_col_ready);
7923     media_col_ready = NULL;
7924 
7925     if (media_ready)
7926     {
7927       ippDeleteAttribute(printer->attrs, media_ready);
7928       media_ready = NULL;
7929     }
7930 
7931     printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MEDIA_LOW | IPPEVE_PREASON_MEDIA_EMPTY | IPPEVE_PREASON_MEDIA_NEEDED);
7932 
7933     for (i = 0; i < num_sources; i ++)
7934     {
7935       media_source = ippGetString(media_sources, i, NULL);
7936 
7937       if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
7938 	continue;
7939 
7940       snprintf(name, sizeof(name), "size%d", i);
7941       if ((media_size = cupsGetOption(name, num_options, options)) != NULL && (media = pwgMediaForPWG(media_size)) != NULL)
7942       {
7943         snprintf(name, sizeof(name), "type%d", i);
7944         if ((media_type = cupsGetOption(name, num_options, options)) != NULL && !*media_type)
7945           media_type = NULL;
7946 
7947         if (media_ready)
7948           ippSetString(printer->attrs, &media_ready, ippGetCount(media_ready), media_size);
7949         else
7950           media_ready = ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, media_size);
7951 
7952         media_col = create_media_col(media_size, media_source, media_type, media->width, media->length, -1, -1, -1, -1);
7953 
7954         if (media_col_ready)
7955           ippSetCollection(printer->attrs, &media_col_ready, ippGetCount(media_col_ready), media_col);
7956         else
7957           media_col_ready = ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-ready", media_col);
7958         ippDelete(media_col);
7959       }
7960       else
7961         media = NULL;
7962 
7963       snprintf(name, sizeof(name), "level%d", i);
7964       if ((val = cupsGetOption(name, num_options, options)) != NULL)
7965         ready_sheets = atoi(val);
7966       else
7967         ready_sheets = 0;
7968 
7969       snprintf(tray_str, sizeof(tray_str), "type=sheetFeedAuto%sRemovableTray;mediafeed=%d;mediaxfeed=%d;maxcapacity=%d;level=%d;status=0;name=%s;", !strcmp(media_source, "by-pass-tray") ? "Non" : "", media ? media->length : 0, media ? media->width : 0, strcmp(media_source, "by-pass-tray") ? 250 : 25, ready_sheets, media_source);
7970 
7971       ippSetOctetString(printer->attrs, &input_tray, i, tray_str, (int)strlen(tray_str));
7972 
7973       if (ready_sheets == 0)
7974       {
7975         printer->state_reasons |= IPPEVE_PREASON_MEDIA_EMPTY;
7976         if (printer->active_job)
7977           printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
7978       }
7979       else if (ready_sheets < 25 && ready_sheets > 0)
7980         printer->state_reasons |= IPPEVE_PREASON_MEDIA_LOW;
7981     }
7982 
7983     if (!media_col_ready)
7984       media_col_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-col-ready");
7985 
7986     if (!media_ready)
7987       media_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-ready");
7988 
7989     _cupsRWUnlock(&printer->rwlock);
7990   }
7991 
7992   if (printer->web_forms)
7993     html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
7994 
7995   html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
7996   for (i = 0; i < num_sources; i ++)
7997   {
7998     media_source = ippGetString(media_sources, i, NULL);
7999 
8000     if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
8001       continue;
8002 
8003     for (j = 0, ready_size = NULL, ready_type = NULL; j < num_ready; j ++)
8004     {
8005       media_col    = ippGetCollection(media_col_ready, j);
8006       ready_size   = ippGetString(ippFindAttribute(media_col, "media-size-name", IPP_TAG_ZERO), 0, NULL);
8007       ready_source = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL);
8008       ready_type   = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL);
8009 
8010       if (ready_source && !strcmp(ready_source, media_source))
8011         break;
8012 
8013       ready_source = NULL;
8014       ready_size   = NULL;
8015       ready_type   = NULL;
8016     }
8017 
8018     html_printf(client, "<tr><th>%s:</th>", media_source);
8019 
8020    /*
8021     * Media size...
8022     */
8023 
8024     if (printer->web_forms)
8025     {
8026       html_printf(client, "<td><select name=\"size%d\"><option value=\"\">None</option>", i);
8027       for (j = 0; j < num_sizes; j ++)
8028       {
8029 	media_size = ippGetString(media_sizes, j, NULL);
8030 
8031 	html_printf(client, "<option%s>%s</option>", (ready_size && !strcmp(ready_size, media_size)) ? " selected" : "", media_size);
8032       }
8033       html_printf(client, "</select>");
8034     }
8035     else
8036       html_printf(client, "<td>%s", ready_size);
8037 
8038    /*
8039     * Media type...
8040     */
8041 
8042     if (printer->web_forms)
8043     {
8044       html_printf(client, " <select name=\"type%d\"><option value=\"\">None</option>", i);
8045       for (j = 0; j < num_types; j ++)
8046       {
8047 	media_type = ippGetString(media_types, j, NULL);
8048 
8049 	html_printf(client, "<option%s>%s</option>", (ready_type && !strcmp(ready_type, media_type)) ? " selected" : "", media_type);
8050       }
8051       html_printf(client, "</select>");
8052     }
8053     else if (ready_type)
8054       html_printf(client, ", %s", ready_type);
8055 
8056    /*
8057     * Level/sheets loaded...
8058     */
8059 
8060     if ((ready_tray = ippGetOctetString(input_tray, i, &tray_len)) != NULL)
8061     {
8062       if (tray_len > (int)(sizeof(tray_str) - 1))
8063         tray_len = (int)sizeof(tray_str) - 1;
8064       memcpy(tray_str, ready_tray, (size_t)tray_len);
8065       tray_str[tray_len] = '\0';
8066 
8067       if ((tray_ptr = strstr(tray_str, "level=")) != NULL)
8068         ready_sheets = atoi(tray_ptr + 6);
8069       else
8070         ready_sheets = 0;
8071     }
8072     else
8073       ready_sheets = 0;
8074 
8075     if (printer->web_forms)
8076     {
8077       html_printf(client, " <select name=\"level%d\">", i);
8078       for (j = 0; j < (int)(sizeof(sheets) / sizeof(sheets[0])); j ++)
8079       {
8080 	if (!strcmp(media_source, "by-pass-tray") && sheets[j] > 25)
8081 	  continue;
8082 
8083 	if (sheets[j] < 0)
8084 	  html_printf(client, "<option value=\"%d\"%s>Unknown</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "");
8085 	else
8086 	  html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "", sheets[j]);
8087       }
8088       html_printf(client, "</select></td></tr>\n");
8089     }
8090     else if (ready_sheets == 1)
8091       html_printf(client, ", 1 sheet</td></tr>\n");
8092     else if (ready_sheets > 0)
8093       html_printf(client, ", %d sheets</td></tr>\n", ready_sheets);
8094     else
8095       html_printf(client, "</td></tr>\n");
8096   }
8097 
8098   if (printer->web_forms)
8099   {
8100     html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\">");
8101     if (num_options > 0)
8102       html_printf(client, " <span class=\"badge\" id=\"status\">Media updated.</span>\n");
8103     html_printf(client, "</td></tr></table></form>\n");
8104 
8105     if (num_options > 0)
8106       html_printf(client, "<script>\n"
8107 			  "setTimeout(hide_status, 3000);\n"
8108 			  "function hide_status() {\n"
8109 			  "  var status = document.getElementById('status');\n"
8110 			  "  status.style.display = 'none';\n"
8111 			  "}\n"
8112 			  "</script>\n");
8113   }
8114   else
8115     html_printf(client, "</table>\n");
8116 
8117   cupsFreeOptions(num_options, options);
8118 
8119   html_footer(client);
8120 
8121   return (1);
8122 }
8123 
8124 
8125 /*
8126  * 'show_status()' - Show printer/system state.
8127  */
8128 
8129 static int				/* O - 1 on success, 0 on failure */
show_status(ippeve_client_t * client)8130 show_status(ippeve_client_t  *client)	/* I - Client connection */
8131 {
8132   ippeve_printer_t *printer = client->printer;
8133 					/* Printer */
8134   ippeve_job_t		*job;		/* Current job */
8135   int			i;		/* Looping var */
8136   ippeve_preason_t	reason;		/* Current reason */
8137   static const char * const reasons[] =	/* Reason strings */
8138   {
8139     "Other",
8140     "Cover Open",
8141     "Input Tray Missing",
8142     "Marker Supply Empty",
8143     "Marker Supply Low",
8144     "Marker Waste Almost Full",
8145     "Marker Waste Full",
8146     "Media Empty",
8147     "Media Jam",
8148     "Media Low",
8149     "Media Needed",
8150     "Moving to Paused",
8151     "Paused",
8152     "Spool Area Full",
8153     "Toner Empty",
8154     "Toner Low"
8155   };
8156   static const char * const state_colors[] =
8157   {					/* State colors */
8158     "#0C0",				/* Idle */
8159     "#EE0",				/* Processing */
8160     "#C00"				/* Stopped */
8161   };
8162 
8163 
8164   if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
8165     return (0);
8166 
8167   html_header(client, printer->name, printer->state == IPP_PSTATE_PROCESSING ? 5 : 15);
8168   html_printf(client, "<h1><img style=\"background: %s; border-radius: 10px; float: left; margin-right: 10px; padding: 10px;\" src=\"/icon.png\" width=\"64\" height=\"64\">%s Jobs</h1>\n", state_colors[printer->state - IPP_PSTATE_IDLE], printer->name);
8169   html_printf(client, "<p>%s, %d job(s).", printer->state == IPP_PSTATE_IDLE ? "Idle" : printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(printer->jobs));
8170   for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
8171     if (printer->state_reasons & reason)
8172       html_printf(client, "\n<br>&nbsp;&nbsp;&nbsp;&nbsp;%s", reasons[i]);
8173   html_printf(client, "</p>\n");
8174 
8175   if (cupsArrayCount(printer->jobs) > 0)
8176   {
8177     _cupsRWLockRead(&(printer->rwlock));
8178 
8179     html_printf(client, "<table class=\"striped\" summary=\"Jobs\"><thead><tr><th>Job #</th><th>Name</th><th>Owner</th><th>Status</th></tr></thead><tbody>\n");
8180     for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs); job; job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
8181     {
8182       char	when[256],		/* When job queued/started/finished */
8183 	      hhmmss[64];		/* Time HH:MM:SS */
8184 
8185       switch (job->state)
8186       {
8187 	case IPP_JSTATE_PENDING :
8188 	case IPP_JSTATE_HELD :
8189 	    snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss)));
8190 	    break;
8191 	case IPP_JSTATE_PROCESSING :
8192 	case IPP_JSTATE_STOPPED :
8193 	    snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss)));
8194 	    break;
8195 	case IPP_JSTATE_ABORTED :
8196 	    snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
8197 	    break;
8198 	case IPP_JSTATE_CANCELED :
8199 	    snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
8200 	    break;
8201 	case IPP_JSTATE_COMPLETED :
8202 	    snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
8203 	    break;
8204       }
8205 
8206       html_printf(client, "<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", job->id, job->name, job->username, when);
8207     }
8208     html_printf(client, "</tbody></table>\n");
8209 
8210     _cupsRWUnlock(&(printer->rwlock));
8211   }
8212 
8213   html_footer(client);
8214 
8215   return (1);
8216 }
8217 
8218 
8219 /*
8220  * 'show_supplies()' - Show printer supplies.
8221  */
8222 
8223 static int				/* O - 1 on success, 0 on failure */
show_supplies(ippeve_client_t * client)8224 show_supplies(
8225     ippeve_client_t  *client)		/* I - Client connection */
8226 {
8227   ippeve_printer_t *printer = client->printer;
8228 					/* Printer */
8229   int		i,			/* Looping var */
8230 		num_supply;		/* Number of supplies */
8231   ipp_attribute_t *supply,		/* printer-supply attribute */
8232 		*supply_desc;		/* printer-supply-description attribute */
8233   int		num_options = 0;	/* Number of form options */
8234   cups_option_t	*options = NULL;	/* Form options */
8235   int		supply_len,		/* Length of supply value */
8236 		level;			/* Supply level */
8237   const char	*supply_value;		/* Supply value */
8238   char		supply_text[1024],	/* Supply string */
8239 		*supply_ptr;		/* Pointer into supply string */
8240   static const char * const printer_supply[] =
8241   {					/* printer-supply values */
8242     "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
8243         "maxcapacity=100;level=%d;colorantname=unknown;",
8244     "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
8245         "maxcapacity=100;level=%d;colorantname=black;",
8246     "index=3;class=supplyThatIsConsumed;type=toner;unit=percent;"
8247         "maxcapacity=100;level=%d;colorantname=cyan;",
8248     "index=4;class=supplyThatIsConsumed;type=toner;unit=percent;"
8249         "maxcapacity=100;level=%d;colorantname=magenta;",
8250     "index=5;class=supplyThatIsConsumed;type=toner;unit=percent;"
8251         "maxcapacity=100;level=%d;colorantname=yellow;"
8252   };
8253   static const char * const backgrounds[] =
8254   {					/* Background colors for the supply-level bars */
8255     "#777 linear-gradient(#333,#777)",
8256     "#000 linear-gradient(#666,#000)",
8257     "#0FF linear-gradient(#6FF,#0FF)",
8258     "#F0F linear-gradient(#F6F,#F0F)",
8259     "#CC0 linear-gradient(#EE6,#EE0)"
8260   };
8261   static const char * const colors[] =	/* Text colors for the supply-level bars */
8262   {
8263     "#fff",
8264     "#fff",
8265     "#000",
8266     "#000",
8267     "#000"
8268   };
8269 
8270 
8271   if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
8272     return (0);
8273 
8274   html_header(client, printer->name, 0);
8275 
8276   if ((supply = ippFindAttribute(printer->attrs, "printer-supply", IPP_TAG_STRING)) == NULL)
8277   {
8278     html_printf(client, "<p>Error: No printer-supply defined for printer.</p>\n");
8279     html_footer(client);
8280     return (1);
8281   }
8282 
8283   num_supply = ippGetCount(supply);
8284 
8285   if ((supply_desc = ippFindAttribute(printer->attrs, "printer-supply-description", IPP_TAG_TEXT)) == NULL)
8286   {
8287     html_printf(client, "<p>Error: No printer-supply-description defined for printer.</p>\n");
8288     html_footer(client);
8289     return (1);
8290   }
8291 
8292   if (num_supply != ippGetCount(supply_desc))
8293   {
8294     html_printf(client, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n");
8295     html_footer(client);
8296     return (1);
8297   }
8298 
8299   if (printer->web_forms)
8300     num_options = parse_options(client, &options);
8301 
8302   if (num_options > 0)
8303   {
8304    /*
8305     * WARNING: A real printer/server implementation MUST NOT implement
8306     * supply updates via a GET request - GET requests are supposed to be
8307     * idempotent (without side-effects) and we obviously are not
8308     * authenticating access here.  This form is provided solely to
8309     * enable testing and development!
8310     */
8311 
8312     char	name[64];		/* Form field */
8313     const char	*val;			/* Form value */
8314 
8315     _cupsRWLockWrite(&printer->rwlock);
8316 
8317     ippDeleteAttribute(printer->attrs, supply);
8318     supply = NULL;
8319 
8320     printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MARKER_SUPPLY_EMPTY | IPPEVE_PREASON_MARKER_SUPPLY_LOW | IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL | IPPEVE_PREASON_MARKER_WASTE_FULL | IPPEVE_PREASON_TONER_EMPTY | IPPEVE_PREASON_TONER_LOW);
8321 
8322     for (i = 0; i < num_supply; i ++)
8323     {
8324       snprintf(name, sizeof(name), "supply%d", i);
8325       if ((val = cupsGetOption(name, num_options, options)) != NULL)
8326       {
8327         level = atoi(val);      /* New level */
8328 
8329         snprintf(supply_text, sizeof(supply_text), printer_supply[i], level);
8330         if (supply)
8331           ippSetOctetString(printer->attrs, &supply, ippGetCount(supply), supply_text, (int)strlen(supply_text));
8332         else
8333           supply = ippAddOctetString(printer->attrs, IPP_TAG_PRINTER, "printer-supply", supply_text, (int)strlen(supply_text));
8334 
8335         if (i == 0)
8336         {
8337           if (level == 100)
8338             printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_FULL;
8339           else if (level > 90)
8340             printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL;
8341         }
8342         else
8343         {
8344           if (level == 0)
8345             printer->state_reasons |= IPPEVE_PREASON_TONER_EMPTY;
8346           else if (level < 10)
8347             printer->state_reasons |= IPPEVE_PREASON_TONER_LOW;
8348         }
8349       }
8350     }
8351 
8352     _cupsRWUnlock(&printer->rwlock);
8353   }
8354 
8355   if (printer->web_forms)
8356     html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
8357 
8358   html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
8359   for (i = 0; i < num_supply; i ++)
8360   {
8361     supply_value = ippGetOctetString(supply, i, &supply_len);
8362     if (supply_len > (int)(sizeof(supply_text) - 1))
8363       supply_len = (int)sizeof(supply_text) - 1;
8364 
8365     memcpy(supply_text, supply_value, (size_t)supply_len);
8366     supply_text[supply_len] = '\0';
8367 
8368     if ((supply_ptr = strstr(supply_text, "level=")) != NULL)
8369       level = atoi(supply_ptr + 6);
8370     else
8371       level = 50;
8372 
8373     if (printer->web_forms)
8374       html_printf(client, "<tr><th>%s:</th><td><input name=\"supply%d\" size=\"3\" value=\"%d\"></td>", ippGetString(supply_desc, i, NULL), i, level);
8375     else
8376       html_printf(client, "<tr><th>%s:</th>", ippGetString(supply_desc, i, NULL));
8377 
8378     if (level < 10)
8379       html_printf(client, "<td class=\"meter\"><span class=\"bar\" style=\"background: %s; padding: 5px %dpx;\"></span>&nbsp;%d%%</td></tr>\n", backgrounds[i], level * 2, level);
8380     else
8381       html_printf(client, "<td class=\"meter\"><span class=\"bar\" style=\"background: %s; color: %s; padding: 5px %dpx;\">%d%%</span></td></tr>\n", backgrounds[i], colors[i], level * 2, level);
8382   }
8383 
8384   if (printer->web_forms)
8385   {
8386     html_printf(client, "<tr><td></td><td colspan=\"2\"><input type=\"submit\" value=\"Update Supplies\">");
8387     if (num_options > 0)
8388       html_printf(client, " <span class=\"badge\" id=\"status\">Supplies updated.</span>\n");
8389     html_printf(client, "</td></tr>\n</table>\n</form>\n");
8390 
8391     if (num_options > 0)
8392       html_printf(client, "<script>\n"
8393 			  "setTimeout(hide_status, 3000);\n"
8394 			  "function hide_status() {\n"
8395 			  "  var status = document.getElementById('status');\n"
8396 			  "  status.style.display = 'none';\n"
8397 			  "}\n"
8398 			  "</script>\n");
8399   }
8400   else
8401     html_printf(client, "</table>\n");
8402 
8403   cupsFreeOptions(num_options, options);
8404 
8405   html_footer(client);
8406 
8407   return (1);
8408 }
8409 
8410 
8411 #ifndef _WIN32
8412 /*
8413  * 'signal_handler()' - Handle termination signals.
8414  */
8415 
8416 static void
signal_handler(int signum)8417 signal_handler(int signum)		/* I - Signal number (not used) */
8418 {
8419   (void)signum;
8420 
8421   StopPrinter = 1;
8422 }
8423 #endif // !_WIN32
8424 
8425 
8426 /*
8427  * 'time_string()' - Return the local time in hours, minutes, and seconds.
8428  */
8429 
8430 static char *
time_string(time_t tv,char * buffer,size_t bufsize)8431 time_string(time_t tv,			/* I - Time value */
8432             char   *buffer,		/* I - Buffer */
8433 	    size_t bufsize)		/* I - Size of buffer */
8434 {
8435   struct tm	date;			/* Local time and date */
8436 
8437   localtime_r(&tv, &date);
8438 
8439   strftime(buffer, bufsize, "%X", &date);
8440 
8441   return (buffer);
8442 }
8443 
8444 
8445 /*
8446  * 'usage()' - Show program usage.
8447  */
8448 
8449 static void
usage(int status)8450 usage(int status)			/* O - Exit status */
8451 {
8452   _cupsLangPuts(stdout, _("Usage: ippeveprinter [options] \"name\""));
8453   _cupsLangPuts(stdout, _("Options:"));
8454   _cupsLangPuts(stdout, _("--help                  Show program help"));
8455   _cupsLangPuts(stdout, _("--no-web-forms          Disable web forms for media and supplies"));
8456   _cupsLangPuts(stdout, _("--pam-service service   Use the named PAM service"));
8457   _cupsLangPuts(stdout, _("--version               Show program version"));
8458   _cupsLangPuts(stdout, _("-2                      Set 2-sided printing support (default=1-sided)"));
8459   _cupsLangPuts(stdout, _("-A                      Enable authentication"));
8460   _cupsLangPuts(stdout, _("-D device-uri           Set the device URI for the printer"));
8461   _cupsLangPuts(stdout, _("-F output-type/subtype  Set the output format for the printer"));
8462 #ifdef HAVE_TLS
8463   _cupsLangPuts(stdout, _("-K keypath              Set location of server X.509 certificates and keys."));
8464 #endif /* HAVE_TLS */
8465   _cupsLangPuts(stdout, _("-M manufacturer         Set manufacturer name (default=Test)"));
8466 #if !CUPS_LITE
8467   _cupsLangPuts(stdout, _("-P filename.ppd         Load printer attributes from PPD file"));
8468 #endif /* !CUPS_LITE */
8469   _cupsLangPuts(stdout, _("-S filename.strings     Set strings file"));
8470   _cupsLangPuts(stdout, _("-V version              Set default IPP version"));
8471   _cupsLangPuts(stdout, _("-a filename.conf        Load printer attributes from conf file"));
8472   _cupsLangPuts(stdout, _("-c command              Set print command"));
8473   _cupsLangPuts(stdout, _("-d spool-directory      Set spool directory"));
8474   _cupsLangPuts(stdout, _("-f type/subtype[,...]   Set supported file types"));
8475   _cupsLangPuts(stdout, _("-i iconfile.png[,...]   Set icon file(s)"));
8476   _cupsLangPuts(stdout, _("-k                      Keep job spool files"));
8477   _cupsLangPuts(stdout, _("-l location             Set location of printer"));
8478   _cupsLangPuts(stdout, _("-m model                Set model name (default=Printer)"));
8479   _cupsLangPuts(stdout, _("-n hostname             Set hostname for printer"));
8480   _cupsLangPuts(stdout, _("-p port                 Set port number for printer"));
8481   _cupsLangPuts(stdout, _("-r subtype,[subtype]    Set DNS-SD service subtype"));
8482   _cupsLangPuts(stdout, _("-s speed[,color-speed]  Set speed in pages per minute"));
8483   _cupsLangPuts(stdout, _("-v                      Be verbose"));
8484 
8485   exit(status);
8486 }
8487 
8488 
8489 /*
8490  * 'valid_doc_attributes()' - Determine whether the document attributes are
8491  *                            valid.
8492  *
8493  * When one or more document attributes are invalid, this function adds a
8494  * suitable response and attributes to the unsupported group.
8495  */
8496 
8497 static int				/* O - 1 if valid, 0 if not */
valid_doc_attributes(ippeve_client_t * client)8498 valid_doc_attributes(
8499     ippeve_client_t *client)		/* I - Client */
8500 {
8501   int			valid = 1;	/* Valid attributes? */
8502   ipp_op_t		op = ippGetOperation(client->request);
8503 					/* IPP operation */
8504   const char		*op_name = ippOpString(op);
8505 					/* IPP operation name */
8506   ipp_attribute_t	*attr,		/* Current attribute */
8507 			*supported;	/* xxx-supported attribute */
8508   const char		*compression = NULL,
8509 					/* compression value */
8510 			*format = NULL;	/* document-format value */
8511 
8512 
8513  /*
8514   * Check operation attributes...
8515   */
8516 
8517   if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
8518   {
8519    /*
8520     * If compression is specified, only accept a supported value in a Print-Job
8521     * or Send-Document request...
8522     */
8523 
8524     compression = ippGetString(attr, 0, NULL);
8525     supported   = ippFindAttribute(client->printer->attrs,
8526                                    "compression-supported", IPP_TAG_KEYWORD);
8527 
8528     if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
8529         ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
8530         (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
8531          op != IPP_OP_VALIDATE_JOB) ||
8532         !ippContainsString(supported, compression))
8533     {
8534       respond_unsupported(client, attr);
8535       valid = 0;
8536     }
8537     else
8538     {
8539       fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression);
8540 
8541       ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression);
8542 
8543       if (strcmp(compression, "none"))
8544       {
8545 	if (Verbosity)
8546 	  fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression);
8547         httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
8548       }
8549     }
8550   }
8551 
8552  /*
8553   * Is it a format we support?
8554   */
8555 
8556   if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
8557   {
8558     if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
8559         ippGetGroupTag(attr) != IPP_TAG_OPERATION)
8560     {
8561       respond_unsupported(client, attr);
8562       valid = 0;
8563     }
8564     else
8565     {
8566       format = ippGetString(attr, 0, NULL);
8567 
8568       fprintf(stderr, "%s %s document-format=\"%s\"\n", client->hostname, op_name, format);
8569 
8570       ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format);
8571     }
8572   }
8573   else
8574   {
8575     format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL);
8576     if (!format)
8577       format = "application/octet-stream"; /* Should never happen */
8578 
8579     attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
8580   }
8581 
8582   if (format && !strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
8583   {
8584    /*
8585     * Auto-type the file using the first 8 bytes of the file...
8586     */
8587 
8588     unsigned char	header[8];	/* First 8 bytes of file */
8589 
8590     memset(header, 0, sizeof(header));
8591     httpPeek(client->http, (char *)header, sizeof(header));
8592 
8593     fprintf(stderr, "%s %s Auto-type header: %02X%02X%02X%02X%02X%02X%02X%02X\n", client->hostname, op_name, header[0], header[1], header[2], header[3], header[4], header[5], header[6], header[7]);
8594     if (!memcmp(header, "%PDF", 4))
8595       format = "application/pdf";
8596     else if (!memcmp(header, "%!", 2))
8597       format = "application/postscript";
8598     else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
8599       format = "image/jpeg";
8600     else if (!memcmp(header, "\211PNG", 4))
8601       format = "image/png";
8602     else if (!memcmp(header, "RaS2PwgR", 8))
8603       format = "image/pwg-raster";
8604     else if (!memcmp(header, "UNIRAST", 8))
8605       format = "image/urf";
8606     else
8607       format = NULL;
8608 
8609     if (format)
8610     {
8611       fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n", client->hostname, op_name, format);
8612 
8613       ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
8614     }
8615   }
8616 
8617   if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
8618   {
8619     respond_unsupported(client, attr);
8620     valid = 0;
8621   }
8622 
8623  /*
8624   * document-name
8625   */
8626 
8627   if ((attr = ippFindAttribute(client->request, "document-name", IPP_TAG_NAME)) != NULL)
8628     ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
8629 
8630   return (valid);
8631 }
8632 
8633 
8634 /*
8635  * 'valid_job_attributes()' - Determine whether the job attributes are valid.
8636  *
8637  * When one or more job attributes are invalid, this function adds a suitable
8638  * response and attributes to the unsupported group.
8639  */
8640 
8641 static int				/* O - 1 if valid, 0 if not */
valid_job_attributes(ippeve_client_t * client)8642 valid_job_attributes(
8643     ippeve_client_t *client)		/* I - Client */
8644 {
8645   int			i,		/* Looping var */
8646 			count,		/* Number of values */
8647 			fidelity,	/* "ipp-attribute-fidelity" value */
8648 			valid = 1;	/* Valid attributes? */
8649   ipp_attribute_t	*attr,		/* Current attribute */
8650 			*supported;	/* xxx-supported attribute */
8651 
8652 
8653  /*
8654   * Check operation attributes...
8655   */
8656 
8657   valid = valid_doc_attributes(client);
8658 
8659  /*
8660   * Check the various job template attributes...
8661   */
8662 
8663   if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
8664   {
8665     if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
8666     {
8667       respond_unsupported(client, attr);
8668       valid = 0;
8669     }
8670   }
8671 
8672   fidelity = ippGetBoolean(attr, 0);
8673 
8674   if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL)
8675   {
8676     if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
8677         ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
8678     {
8679       if (fidelity)
8680       {
8681         respond_unsupported(client, attr);
8682         valid = 0;
8683       }
8684       else
8685       {
8686         respond_ignored(client, attr);
8687         ippDeleteAttribute(client->request, attr);
8688       }
8689     }
8690   }
8691 
8692   if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
8693   {
8694     if (ippGetCount(attr) != 1 ||
8695         (ippGetValueTag(attr) != IPP_TAG_NAME &&
8696 	 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8697 	 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
8698 	strcmp(ippGetString(attr, 0, NULL), "no-hold"))
8699     {
8700       respond_unsupported(client, attr);
8701       valid = 0;
8702     }
8703   }
8704 
8705   if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL)
8706   {
8707     if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0)
8708     {
8709       respond_unsupported(client, attr);
8710       valid = 0;
8711     }
8712   }
8713 
8714   if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL)
8715   {
8716     if (ippGetCount(attr) != 1 ||
8717         (ippGetValueTag(attr) != IPP_TAG_NAME &&
8718 	 ippGetValueTag(attr) != IPP_TAG_NAMELANG))
8719     {
8720       respond_unsupported(client, attr);
8721       valid = 0;
8722     }
8723 
8724     ippSetGroupTag(client->request, &attr, IPP_TAG_JOB);
8725   }
8726   else
8727   {
8728     ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
8729   }
8730 
8731   if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL)
8732   {
8733     if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
8734         ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
8735     {
8736       respond_unsupported(client, attr);
8737       valid = 0;
8738     }
8739   }
8740 
8741   if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
8742   {
8743     if (ippGetCount(attr) != 1 ||
8744         (ippGetValueTag(attr) != IPP_TAG_NAME &&
8745 	 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8746 	 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
8747 	strcmp(ippGetString(attr, 0, NULL), "none"))
8748     {
8749       respond_unsupported(client, attr);
8750       valid = 0;
8751     }
8752   }
8753 
8754   if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
8755   {
8756     if (ippGetCount(attr) != 1 ||
8757         (ippGetValueTag(attr) != IPP_TAG_NAME &&
8758 	 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8759 	 ippGetValueTag(attr) != IPP_TAG_KEYWORD))
8760     {
8761       respond_unsupported(client, attr);
8762       valid = 0;
8763     }
8764     else
8765     {
8766       supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
8767 
8768       if (!ippContainsString(supported, ippGetString(attr, 0, NULL)))
8769       {
8770         if (fidelity)
8771         {
8772 	  respond_unsupported(client, attr);
8773 	  valid = 0;
8774 	}
8775 	else
8776 	{
8777 	  respond_ignored(client, attr);
8778 	  ippDeleteAttribute(client->request, attr);
8779 	}
8780       }
8781     }
8782   }
8783 
8784   if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL)
8785   {
8786     ipp_t		*col,		/* media-col collection */
8787 			*size;		/* media-size collection */
8788     ipp_attribute_t	*member,	/* Member attribute */
8789 			*x_dim,		/* x-dimension */
8790 			*y_dim;		/* y-dimension */
8791     int			x_value,	/* y-dimension value */
8792 			y_value;	/* x-dimension value */
8793 
8794     if (ippGetCount(attr) != 1 ||
8795         ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
8796     {
8797       respond_unsupported(client, attr);
8798       valid = 0;
8799     }
8800 
8801     col = ippGetCollection(attr, 0);
8802 
8803     if ((member = ippFindAttribute(col, "media-size-name", IPP_TAG_ZERO)) != NULL)
8804     {
8805       if (ippGetCount(member) != 1 ||
8806 	  (ippGetValueTag(member) != IPP_TAG_NAME &&
8807 	   ippGetValueTag(member) != IPP_TAG_NAMELANG &&
8808 	   ippGetValueTag(member) != IPP_TAG_KEYWORD))
8809       {
8810 	respond_unsupported(client, attr);
8811 	valid = 0;
8812       }
8813       else
8814       {
8815 	supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
8816 
8817 	if (!ippContainsString(supported, ippGetString(member, 0, NULL)))
8818 	{
8819 	  if (fidelity)
8820 	  {
8821 	    respond_unsupported(client, attr);
8822 	    valid = 0;
8823 	  }
8824 	  else
8825 	  {
8826 	    respond_ignored(client, attr);
8827 	    ippDeleteAttribute(client->request, attr);
8828 	  }
8829 	}
8830       }
8831     }
8832     else if ((member = ippFindAttribute(col, "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
8833     {
8834       if (ippGetCount(member) != 1)
8835       {
8836 	respond_unsupported(client, attr);
8837 	valid = 0;
8838       }
8839       else
8840       {
8841 	size = ippGetCollection(member, 0);
8842 
8843 	if ((x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(x_dim) != 1 ||
8844 	    (y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(y_dim) != 1)
8845 	{
8846 	  respond_unsupported(client, attr);
8847 	  valid = 0;
8848 	}
8849 	else
8850 	{
8851 	  x_value   = ippGetInteger(x_dim, 0);
8852 	  y_value   = ippGetInteger(y_dim, 0);
8853 	  supported = ippFindAttribute(client->printer->attrs, "media-size-supported", IPP_TAG_BEGIN_COLLECTION);
8854 	  count     = ippGetCount(supported);
8855 
8856 	  for (i = 0; i < count ; i ++)
8857 	  {
8858 	    int	x_min, x_max;		// Min/max width
8859 	    int y_min, y_max;		// Min/max length
8860 
8861 	    size  = ippGetCollection(supported, i);
8862 	    x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_ZERO);
8863 	    y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_ZERO);
8864 
8865             if (ippGetValueTag(x_dim) == IPP_TAG_INTEGER)
8866             {
8867               x_min = ippGetInteger(x_dim, 0) - 100;
8868               x_max = ippGetInteger(x_dim, 0) + 100;
8869             }
8870             else
8871             {
8872               x_min = ippGetRange(x_dim, 0, &x_max);
8873             }
8874 
8875             if (ippGetValueTag(y_dim) == IPP_TAG_INTEGER)
8876             {
8877               y_min = ippGetInteger(y_dim, 0) - 100;
8878               y_max = ippGetInteger(y_dim, 0) + 100;
8879             }
8880             else
8881             {
8882               y_min = ippGetRange(y_dim, 0, &y_max);
8883             }
8884 
8885 	    if ((x_value < x_min || x_value > x_max) && (y_value < y_min || y_value > y_max))
8886 	      break;
8887 	  }
8888 
8889 	  if (i >= count)
8890 	  {
8891 	    if (fidelity)
8892 	    {
8893 	      respond_unsupported(client, attr);
8894 	      valid = 0;
8895 	    }
8896 	    else
8897 	    {
8898 	      respond_ignored(client, attr);
8899 	      ippDeleteAttribute(client->request, attr);
8900 	    }
8901 	  }
8902 	}
8903       }
8904     }
8905   }
8906 
8907   if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL)
8908   {
8909     if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
8910         (strcmp(ippGetString(attr, 0, NULL),
8911 		"separate-documents-uncollated-copies") &&
8912 	 strcmp(ippGetString(attr, 0, NULL),
8913 		"separate-documents-collated-copies")))
8914     {
8915       respond_unsupported(client, attr);
8916       valid = 0;
8917     }
8918   }
8919 
8920   if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL)
8921   {
8922     if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
8923         ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
8924         ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
8925     {
8926       respond_unsupported(client, attr);
8927       valid = 0;
8928     }
8929   }
8930 
8931   if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
8932   {
8933     if (ippGetValueTag(attr) != IPP_TAG_RANGE)
8934     {
8935       respond_unsupported(client, attr);
8936       valid = 0;
8937     }
8938   }
8939 
8940   if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL)
8941   {
8942     if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
8943         ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
8944         ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
8945     {
8946       respond_unsupported(client, attr);
8947       valid = 0;
8948     }
8949   }
8950 
8951   if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
8952   {
8953     supported = ippFindAttribute(client->printer->attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION);
8954 
8955     if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION ||
8956         !supported)
8957     {
8958       respond_unsupported(client, attr);
8959       valid = 0;
8960     }
8961     else
8962     {
8963       int	xdpi,			/* Horizontal resolution for job template attribute */
8964 		ydpi,			/* Vertical resolution for job template attribute */
8965 		sydpi;			/* Vertical resolution for supported value */
8966       ipp_res_t	units,			/* Units for job template attribute */
8967 		sunits;			/* Units for supported value */
8968 
8969       xdpi  = ippGetResolution(attr, 0, &ydpi, &units);
8970       count = ippGetCount(supported);
8971 
8972       for (i = 0; i < count; i ++)
8973       {
8974         if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits)
8975           break;
8976       }
8977 
8978       if (i >= count)
8979       {
8980         if (fidelity)
8981         {
8982 	  respond_unsupported(client, attr);
8983 	  valid = 0;
8984 	}
8985 	else
8986 	{
8987 	  respond_ignored(client, attr);
8988 	  ippDeleteAttribute(client->request, attr);
8989 	}
8990       }
8991     }
8992   }
8993 
8994   if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
8995   {
8996     const char *sides = ippGetString(attr, 0, NULL);
8997 					/* "sides" value... */
8998 
8999     if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
9000     {
9001       respond_unsupported(client, attr);
9002       valid = 0;
9003     }
9004     else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
9005     {
9006       if (!ippContainsString(supported, sides))
9007       {
9008         if (fidelity)
9009         {
9010 	  respond_unsupported(client, attr);
9011 	  valid = 0;
9012 	}
9013 	else
9014 	{
9015 	  respond_ignored(client, attr);
9016 	  ippDeleteAttribute(client->request, attr);
9017 	}
9018       }
9019     }
9020     else if (strcmp(sides, "one-sided"))
9021     {
9022       if (fidelity)
9023       {
9024 	respond_unsupported(client, attr);
9025 	valid = 0;
9026       }
9027       else
9028       {
9029         respond_ignored(client, attr);
9030         ippDeleteAttribute(client->request, attr);
9031       }
9032     }
9033   }
9034 
9035   return (valid);
9036 }
9037