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