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