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