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