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