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