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