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