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