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