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