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