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