]> git.ipfire.org Git - thirdparty/cups.git/blob - tools/ippeveprinter.c
Add authentication support to ippeveprinter (Issue #5665)
[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 color_supported = ippFindAttribute(printer->attrs, "color-supported", IPP_TAG_BOOLEAN);
6747 document_format_supported = ippFindAttribute(printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE);
6748 printer_location = ippFindAttribute(printer->attrs, "printer-location", IPP_TAG_TEXT);
6749 printer_make_and_model = ippFindAttribute(printer->attrs, "printer-make-and-model", IPP_TAG_TEXT);
6750 printer_more_info = ippFindAttribute(printer->attrs, "printer-more-info", IPP_TAG_URI);
6751 printer_uuid = ippFindAttribute(printer->attrs, "printer-uuid", IPP_TAG_URI);
6752 sides_supported = ippFindAttribute(printer->attrs, "sides-supported", IPP_TAG_KEYWORD);
6753 urf_supported = ippFindAttribute(printer->attrs, "urf-supported", IPP_TAG_KEYWORD);
6754
6755 for (i = 0, count = ippGetCount(document_format_supported), ptr = formats; i < count; i ++)
6756 {
6757 value = ippGetString(document_format_supported, i, NULL);
6758
6759 if (!strcasecmp(value, "application/octet-stream"))
6760 continue;
6761
6762 if (ptr > formats && ptr < (formats + sizeof(formats) - 1))
6763 *ptr++ = ',';
6764
6765 strlcpy(ptr, value, sizeof(formats) - (size_t)(ptr - formats));
6766 ptr += strlen(ptr);
6767
6768 if (ptr >= (formats + sizeof(formats) - 1))
6769 break;
6770 }
6771
6772 urf[0] = '\0';
6773 for (i = 0, count = ippGetCount(urf_supported), ptr = urf; i < count; i ++)
6774 {
6775 value = ippGetString(urf_supported, i, NULL);
6776
6777 if (ptr > urf && ptr < (urf + sizeof(urf) - 1))
6778 *ptr++ = ',';
6779
6780 strlcpy(ptr, value, sizeof(urf) - (size_t)(ptr - urf));
6781 ptr += strlen(ptr);
6782
6783 if (ptr >= (urf + sizeof(urf) - 1))
6784 break;
6785 }
6786
6787 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6788 #ifdef HAVE_DNSSD
6789 DNSServiceErrorType error; /* Error from Bonjour */
6790 char regtype[256]; /* Bonjour service type */
6791
6792
6793 /*
6794 * Build the TXT record for IPP...
6795 */
6796
6797 TXTRecordCreate(&ipp_txt, 1024, NULL);
6798 TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print");
6799 if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
6800 TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(value), value);
6801 if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL)
6802 TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(value), value);
6803 if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
6804 TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(value), value);
6805 TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats), formats);
6806 TXTRecordSetValue(&ipp_txt, "Color", 1, ippGetBoolean(color_supported, 0) ? "T" : "F");
6807 TXTRecordSetValue(&ipp_txt, "Duplex", 1, ippGetCount(sides_supported) > 1 ? "T" : "F");
6808 if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
6809 TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(value) - 9, value + 9);
6810 # ifdef HAVE_SSL
6811 TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2");
6812 # endif /* HAVE_SSL */
6813 if (urf[0])
6814 TXTRecordSetValue(&ipp_txt, "URF", (uint8_t)strlen(urf), urf);
6815 TXTRecordSetValue(&ipp_txt, "txtvers", 1, "1");
6816 TXTRecordSetValue(&ipp_txt, "qtotal", 1, "1");
6817
6818 /*
6819 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6820 * defend our service name but not actually support LPD...
6821 */
6822
6823 printer->printer_ref = DNSSDMaster;
6824
6825 if ((error = DNSServiceRegister(&(printer->printer_ref), kDNSServiceFlagsShareConnection, 0 /* interfaceIndex */, printer->dnssd_name, "_printer._tcp", NULL /* domain */, NULL /* host */, 0 /* port */, 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
6826 {
6827 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_printer._tcp", error);
6828 return (0);
6829 }
6830
6831 /*
6832 * Then register the _ipp._tcp (IPP) service type with the real port number to
6833 * advertise our IPP printer...
6834 */
6835
6836 printer->ipp_ref = DNSSDMaster;
6837
6838 if (subtypes && *subtypes)
6839 snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtypes);
6840 else
6841 strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
6842
6843 if ((error = DNSServiceRegister(&(printer->ipp_ref), kDNSServiceFlagsShareConnection, 0 /* interfaceIndex */, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
6844 {
6845 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
6846 return (0);
6847 }
6848
6849 # ifdef HAVE_SSL
6850 /*
6851 * Then register the _ipps._tcp (IPP) service type with the real port number to
6852 * advertise our IPPS printer...
6853 */
6854
6855 printer->ipps_ref = DNSSDMaster;
6856
6857 if (subtypes && *subtypes)
6858 snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtypes);
6859 else
6860 strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
6861
6862 if ((error = DNSServiceRegister(&(printer->ipps_ref), kDNSServiceFlagsShareConnection, 0 /* interfaceIndex */, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
6863 {
6864 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
6865 return (0);
6866 }
6867 # endif /* HAVE_SSL */
6868
6869 /*
6870 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6871 * real port number to advertise our IPP printer...
6872 */
6873
6874 printer->http_ref = DNSSDMaster;
6875
6876 if ((error = DNSServiceRegister(&(printer->http_ref), kDNSServiceFlagsShareConnection, 0 /* interfaceIndex */, printer->dnssd_name, "_http._tcp,_printer", NULL /* domain */, NULL /* host */, htons(printer->port), 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
6877 {
6878 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_http._tcp,_printer", error);
6879 return (0);
6880 }
6881
6882 TXTRecordDeallocate(&ipp_txt);
6883
6884 #elif defined(HAVE_AVAHI)
6885 char temp[256]; /* Subtype service string */
6886
6887 /*
6888 * Create the TXT record...
6889 */
6890
6891 ipp_txt = NULL;
6892 ipp_txt = avahi_string_list_add_printf(ipp_txt, "rp=ipp/print");
6893 if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
6894 ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s", value);
6895 if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL)
6896 ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", value);
6897 if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
6898 ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", value);
6899 ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
6900 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", ippGetBoolean(color_supported, 0) ? "T" : "F");
6901 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", ippGetCount(sides_supported) > 1 ? "T" : "F");
6902 if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
6903 ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", value + 9);
6904 # ifdef HAVE_SSL
6905 ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2");
6906 # endif /* HAVE_SSL */
6907 if (urf[0])
6908 ipp_txt = avahi_string_list_add_printf(ipp_txt, "URF=%s", urf);
6909 ipp_txt = avahi_string_list_add_printf(ipp_txt, "txtvers=1");
6910 ipp_txt = avahi_string_list_add_printf(ipp_txt, "qtotal=1");
6911
6912 /*
6913 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6914 */
6915
6916 avahi_threaded_poll_lock(DNSSDMaster);
6917
6918 printer->ipp_ref = avahi_entry_group_new(DNSSDClient, dnssd_callback, NULL);
6919
6920 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);
6921
6922 /*
6923 * Then register the _ipp._tcp (IPP)...
6924 */
6925
6926 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);
6927 if (subtypes && *subtypes)
6928 {
6929 char *temptypes = strdup(subtypes), *start, *end;
6930
6931 for (start = temptypes; *start; start = end)
6932 {
6933 if ((end = strchr(start, ',')) != NULL)
6934 *end++ = '\0';
6935 else
6936 end = start + strlen(start);
6937
6938 snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", start);
6939 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp);
6940 }
6941
6942 free(temptypes);
6943 }
6944
6945 #ifdef HAVE_SSL
6946 /*
6947 * _ipps._tcp (IPPS) for secure printing...
6948 */
6949
6950 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);
6951 if (subtypes && *subtypes)
6952 {
6953 char *temptypes = strdup(subtypes), *start, *end;
6954
6955 for (start = temptypes; *start; start = end)
6956 {
6957 if ((end = strchr(start, ',')) != NULL)
6958 *end++ = '\0';
6959 else
6960 end = start + strlen(start);
6961
6962 snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", start);
6963 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp);
6964 }
6965
6966 free(temptypes);
6967 }
6968 #endif /* HAVE_SSL */
6969
6970 /*
6971 * Finally _http.tcp (HTTP) for the web interface...
6972 */
6973
6974 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);
6975 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");
6976
6977 /*
6978 * Commit it...
6979 */
6980
6981 avahi_entry_group_commit(printer->ipp_ref);
6982 avahi_threaded_poll_unlock(DNSSDMaster);
6983
6984 avahi_string_list_free(ipp_txt);
6985 #endif /* HAVE_DNSSD */
6986
6987 return (1);
6988 }
6989
6990
6991 /*
6992 * 'respond_http()' - Send a HTTP response.
6993 */
6994
6995 int /* O - 1 on success, 0 on failure */
6996 respond_http(
6997 ippeve_client_t *client, /* I - Client */
6998 http_status_t code, /* I - HTTP status of response */
6999 const char *content_encoding, /* I - Content-Encoding of response */
7000 const char *type, /* I - MIME media type of response */
7001 size_t length) /* I - Length of response */
7002 {
7003 char message[1024]; /* Text message */
7004
7005
7006 fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
7007
7008 if (code == HTTP_STATUS_CONTINUE)
7009 {
7010 /*
7011 * 100-continue doesn't send any headers...
7012 */
7013
7014 return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
7015 }
7016
7017 /*
7018 * Format an error message...
7019 */
7020
7021 if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
7022 {
7023 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
7024
7025 type = "text/plain";
7026 length = strlen(message);
7027 }
7028 else
7029 message[0] = '\0';
7030
7031 /*
7032 * Send the HTTP response header...
7033 */
7034
7035 httpClearFields(client->http);
7036
7037 if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
7038 client->operation == HTTP_STATE_OPTIONS)
7039 httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
7040
7041 if (code == HTTP_STATUS_UNAUTHORIZED)
7042 {
7043 char value[256]; /* WWW-Authenticate value */
7044
7045 snprintf(value, sizeof(value), "Basic realm=\"%s\"", PAMService);
7046 httpSetField(client->http, HTTP_FIELD_WWW_AUTHENTICATE, value);
7047 }
7048
7049 if (type)
7050 {
7051 if (!strcmp(type, "text/html"))
7052 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
7053 "text/html; charset=utf-8");
7054 else
7055 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
7056
7057 if (content_encoding)
7058 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
7059 }
7060
7061 httpSetLength(client->http, length);
7062
7063 if (httpWriteResponse(client->http, code) < 0)
7064 return (0);
7065
7066 /*
7067 * Send the response data...
7068 */
7069
7070 if (message[0])
7071 {
7072 /*
7073 * Send a plain text message.
7074 */
7075
7076 if (httpPrintf(client->http, "%s", message) < 0)
7077 return (0);
7078
7079 if (httpWrite2(client->http, "", 0) < 0)
7080 return (0);
7081 }
7082 else if (client->response)
7083 {
7084 /*
7085 * Send an IPP response...
7086 */
7087
7088 debug_attributes("Response", client->response, 2);
7089
7090 ippSetState(client->response, IPP_STATE_IDLE);
7091
7092 if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
7093 return (0);
7094 }
7095
7096 return (1);
7097 }
7098
7099
7100 /*
7101 * 'respond_ipp()' - Send an IPP response.
7102 */
7103
7104 static void
7105 respond_ipp(ippeve_client_t *client, /* I - Client */
7106 ipp_status_t status, /* I - status-code */
7107 const char *message, /* I - printf-style status-message */
7108 ...) /* I - Additional args as needed */
7109 {
7110 const char *formatted = NULL; /* Formatted message */
7111
7112
7113 ippSetStatusCode(client->response, status);
7114
7115 if (message)
7116 {
7117 va_list ap; /* Pointer to additional args */
7118 ipp_attribute_t *attr; /* New status-message attribute */
7119
7120 va_start(ap, message);
7121 if ((attr = ippFindAttribute(client->response, "status-message", IPP_TAG_TEXT)) != NULL)
7122 ippSetStringfv(client->response, &attr, 0, message, ap);
7123 else
7124 attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, "status-message", NULL, message, ap);
7125 va_end(ap);
7126
7127 formatted = ippGetString(attr, 0, NULL);
7128 }
7129
7130 if (formatted)
7131 fprintf(stderr, "%s %s %s (%s)\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status), formatted);
7132 else
7133 fprintf(stderr, "%s %s %s\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status));
7134 }
7135
7136
7137 /*
7138 * 'respond_unsupported()' - Respond with an unsupported attribute.
7139 */
7140
7141 static void
7142 respond_unsupported(
7143 ippeve_client_t *client, /* I - Client */
7144 ipp_attribute_t *attr) /* I - Atribute */
7145 {
7146 ipp_attribute_t *temp; /* Copy of attribute */
7147
7148
7149 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Unsupported %s %s%s value.", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)));
7150
7151 temp = ippCopyAttribute(client->response, attr, 0);
7152 ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
7153 }
7154
7155
7156 /*
7157 * 'run_printer()' - Run the printer service.
7158 */
7159
7160 static void
7161 run_printer(ippeve_printer_t *printer) /* I - Printer */
7162 {
7163 int num_fds; /* Number of file descriptors */
7164 struct pollfd polldata[3]; /* poll() data */
7165 int timeout; /* Timeout for poll() */
7166 ippeve_client_t *client; /* New client */
7167
7168
7169 /*
7170 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
7171 */
7172
7173 polldata[0].fd = printer->ipv4;
7174 polldata[0].events = POLLIN;
7175
7176 polldata[1].fd = printer->ipv6;
7177 polldata[1].events = POLLIN;
7178
7179 num_fds = 2;
7180
7181 #ifdef HAVE_DNSSD
7182 polldata[num_fds ].fd = DNSServiceRefSockFD(DNSSDMaster);
7183 polldata[num_fds ++].events = POLLIN;
7184 #endif /* HAVE_DNSSD */
7185
7186 /*
7187 * Loop until we are killed or have a hard error...
7188 */
7189
7190 for (;;)
7191 {
7192 if (cupsArrayCount(printer->jobs))
7193 timeout = 10;
7194 else
7195 timeout = -1;
7196
7197 if (poll(polldata, (nfds_t)num_fds, timeout) < 0 && errno != EINTR)
7198 {
7199 perror("poll() failed");
7200 break;
7201 }
7202
7203 if (polldata[0].revents & POLLIN)
7204 {
7205 if ((client = create_client(printer, printer->ipv4)) != NULL)
7206 {
7207 _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
7208
7209 if (t)
7210 {
7211 _cupsThreadDetach(t);
7212 }
7213 else
7214 {
7215 perror("Unable to create client thread");
7216 delete_client(client);
7217 }
7218 }
7219 }
7220
7221 if (polldata[1].revents & POLLIN)
7222 {
7223 if ((client = create_client(printer, printer->ipv6)) != NULL)
7224 {
7225 _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
7226
7227 if (t)
7228 {
7229 _cupsThreadDetach(t);
7230 }
7231 else
7232 {
7233 perror("Unable to create client thread");
7234 delete_client(client);
7235 }
7236 }
7237 }
7238
7239 #ifdef HAVE_DNSSD
7240 if (polldata[2].revents & POLLIN)
7241 DNSServiceProcessResult(DNSSDMaster);
7242 #endif /* HAVE_DNSSD */
7243
7244 /*
7245 * Clean out old jobs...
7246 */
7247
7248 clean_jobs(printer);
7249 }
7250 }
7251
7252
7253 /*
7254 * 'show_media()' - Show media load state.
7255 */
7256
7257 static int /* O - 1 on success, 0 on failure */
7258 show_media(ippeve_client_t *client) /* I - Client connection */
7259 {
7260 ippeve_printer_t *printer = client->printer;
7261 /* Printer */
7262 int i, j, /* Looping vars */
7263 num_ready, /* Number of ready media */
7264 num_sizes, /* Number of media sizes */
7265 num_sources, /* Number of media sources */
7266 num_types; /* Number of media types */
7267 ipp_attribute_t *media_col_ready,/* media-col-ready attribute */
7268 *media_ready, /* media-ready attribute */
7269 *media_sizes, /* media-supported attribute */
7270 *media_sources, /* media-source-supported attribute */
7271 *media_types, /* media-type-supported attribute */
7272 *input_tray; /* printer-input-tray attribute */
7273 ipp_t *media_col; /* media-col value */
7274 const char *media_size, /* media value */
7275 *media_source, /* media-source value */
7276 *media_type, /* media-type value */
7277 *ready_size, /* media-col-ready media-size[-name] value */
7278 *ready_source, /* media-col-ready media-source value */
7279 *ready_tray, /* printer-input-tray value */
7280 *ready_type; /* media-col-ready media-type value */
7281 char tray_str[1024], /* printer-input-tray string value */
7282 *tray_ptr; /* Pointer into value */
7283 int tray_len; /* Length of printer-input-tray value */
7284 int ready_sheets; /* printer-input-tray sheets value */
7285 int num_options = 0;/* Number of form options */
7286 cups_option_t *options = NULL;/* Form options */
7287 static const int sheets[] = /* Number of sheets */
7288 {
7289 250,
7290 125,
7291 50,
7292 25,
7293 5,
7294 0,
7295 -2
7296 };
7297
7298
7299 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7300 return (0);
7301
7302 html_header(client, printer->name, 0);
7303
7304 if ((media_col_ready = ippFindAttribute(printer->attrs, "media-col-ready", IPP_TAG_BEGIN_COLLECTION)) == NULL)
7305 {
7306 html_printf(client, "<p>Error: No media-col-ready defined for printer.</p>\n");
7307 html_footer(client);
7308 return (1);
7309 }
7310
7311 media_ready = ippFindAttribute(printer->attrs, "media-ready", IPP_TAG_ZERO);
7312
7313 if ((media_sizes = ippFindAttribute(printer->attrs, "media-supported", IPP_TAG_ZERO)) == NULL)
7314 {
7315 html_printf(client, "<p>Error: No media-supported defined for printer.</p>\n");
7316 html_footer(client);
7317 return (1);
7318 }
7319
7320 if ((media_sources = ippFindAttribute(printer->attrs, "media-source-supported", IPP_TAG_ZERO)) == NULL)
7321 {
7322 html_printf(client, "<p>Error: No media-source-supported defined for printer.</p>\n");
7323 html_footer(client);
7324 return (1);
7325 }
7326
7327 if ((media_types = ippFindAttribute(printer->attrs, "media-type-supported", IPP_TAG_ZERO)) == NULL)
7328 {
7329 html_printf(client, "<p>Error: No media-type-supported defined for printer.</p>\n");
7330 html_footer(client);
7331 return (1);
7332 }
7333
7334 if ((input_tray = ippFindAttribute(printer->attrs, "printer-input-tray", IPP_TAG_STRING)) == NULL)
7335 {
7336 html_printf(client, "<p>Error: No printer-input-tray defined for printer.</p>\n");
7337 html_footer(client);
7338 return (1);
7339 }
7340
7341 num_ready = ippGetCount(media_col_ready);
7342 num_sizes = ippGetCount(media_sizes);
7343 num_sources = ippGetCount(media_sources);
7344 num_types = ippGetCount(media_types);
7345
7346 if (num_sources != ippGetCount(input_tray))
7347 {
7348 html_printf(client, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n");
7349 html_footer(client);
7350 return (1);
7351 }
7352
7353 /*
7354 * Process form data if present...
7355 */
7356
7357 if (printer->web_forms)
7358 num_options = parse_options(client, &options);
7359
7360 if (num_options > 0)
7361 {
7362 /*
7363 * WARNING: A real printer/server implementation MUST NOT implement
7364 * media updates via a GET request - GET requests are supposed to be
7365 * idempotent (without side-effects) and we obviously are not
7366 * authenticating access here. This form is provided solely to
7367 * enable testing and development!
7368 */
7369
7370 char name[255]; /* Form name */
7371 const char *val; /* Form value */
7372 pwg_media_t *media; /* Media info */
7373
7374 _cupsRWLockWrite(&printer->rwlock);
7375
7376 ippDeleteAttribute(printer->attrs, media_col_ready);
7377 media_col_ready = NULL;
7378
7379 if (media_ready)
7380 {
7381 ippDeleteAttribute(printer->attrs, media_ready);
7382 media_ready = NULL;
7383 }
7384
7385 printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MEDIA_LOW | IPPEVE_PREASON_MEDIA_EMPTY | IPPEVE_PREASON_MEDIA_NEEDED);
7386
7387 for (i = 0; i < num_sources; i ++)
7388 {
7389 media_source = ippGetString(media_sources, i, NULL);
7390
7391 if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
7392 continue;
7393
7394 snprintf(name, sizeof(name), "size%d", i);
7395 if ((media_size = cupsGetOption(name, num_options, options)) != NULL && (media = pwgMediaForPWG(media_size)) != NULL)
7396 {
7397 snprintf(name, sizeof(name), "type%d", i);
7398 if ((media_type = cupsGetOption(name, num_options, options)) != NULL && !*media_type)
7399 media_type = NULL;
7400
7401 if (media_ready)
7402 ippSetString(printer->attrs, &media_ready, ippGetCount(media_ready), media_size);
7403 else
7404 media_ready = ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, media_size);
7405
7406 media_col = create_media_col(media_size, media_source, media_type, media->width, media->length, -1, -1, -1, -1);
7407
7408 if (media_col_ready)
7409 ippSetCollection(printer->attrs, &media_col_ready, ippGetCount(media_col_ready), media_col);
7410 else
7411 media_col_ready = ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-ready", media_col);
7412 ippDelete(media_col);
7413 }
7414 else
7415 media = NULL;
7416
7417 snprintf(name, sizeof(name), "level%d", i);
7418 if ((val = cupsGetOption(name, num_options, options)) != NULL)
7419 ready_sheets = atoi(val);
7420 else
7421 ready_sheets = 0;
7422
7423 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);
7424
7425 ippSetOctetString(printer->attrs, &input_tray, i, tray_str, (int)strlen(tray_str));
7426
7427 if (ready_sheets == 0)
7428 {
7429 printer->state_reasons |= IPPEVE_PREASON_MEDIA_EMPTY;
7430 if (printer->active_job)
7431 printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
7432 }
7433 else if (ready_sheets < 25 && ready_sheets > 0)
7434 printer->state_reasons |= IPPEVE_PREASON_MEDIA_LOW;
7435 }
7436
7437 if (!media_col_ready)
7438 media_col_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-col-ready");
7439
7440 if (!media_ready)
7441 media_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-ready");
7442
7443 _cupsRWUnlock(&printer->rwlock);
7444 }
7445
7446 if (printer->web_forms)
7447 html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
7448
7449 html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
7450 for (i = 0; i < num_sources; i ++)
7451 {
7452 media_source = ippGetString(media_sources, i, NULL);
7453
7454 if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
7455 continue;
7456
7457 for (j = 0, ready_size = NULL, ready_type = NULL; j < num_ready; j ++)
7458 {
7459 media_col = ippGetCollection(media_col_ready, j);
7460 ready_size = ippGetString(ippFindAttribute(media_col, "media-size-name", IPP_TAG_ZERO), 0, NULL);
7461 ready_source = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL);
7462 ready_type = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL);
7463
7464 if (ready_source && !strcmp(ready_source, media_source))
7465 break;
7466
7467 ready_source = NULL;
7468 ready_size = NULL;
7469 ready_type = NULL;
7470 }
7471
7472 html_printf(client, "<tr><th>%s:</th>", media_source);
7473
7474 /*
7475 * Media size...
7476 */
7477
7478 if (printer->web_forms)
7479 {
7480 html_printf(client, "<td><select name=\"size%d\"><option value=\"\">None</option>", i);
7481 for (j = 0; j < num_sizes; j ++)
7482 {
7483 media_size = ippGetString(media_sizes, j, NULL);
7484
7485 html_printf(client, "<option%s>%s</option>", (ready_size && !strcmp(ready_size, media_size)) ? " selected" : "", media_size);
7486 }
7487 html_printf(client, "</select>");
7488 }
7489 else
7490 html_printf(client, "<td>%s", ready_size);
7491
7492 /*
7493 * Media type...
7494 */
7495
7496 if (printer->web_forms)
7497 {
7498 html_printf(client, " <select name=\"type%d\"><option value=\"\">None</option>", i);
7499 for (j = 0; j < num_types; j ++)
7500 {
7501 media_type = ippGetString(media_types, j, NULL);
7502
7503 html_printf(client, "<option%s>%s</option>", (ready_type && !strcmp(ready_type, media_type)) ? " selected" : "", media_type);
7504 }
7505 html_printf(client, "</select>");
7506 }
7507 else if (ready_type)
7508 html_printf(client, ", %s", ready_type);
7509
7510 /*
7511 * Level/sheets loaded...
7512 */
7513
7514 if ((ready_tray = ippGetOctetString(input_tray, i, &tray_len)) != NULL)
7515 {
7516 if (tray_len > (int)(sizeof(tray_str) - 1))
7517 tray_len = (int)sizeof(tray_str) - 1;
7518 memcpy(tray_str, ready_tray, (size_t)tray_len);
7519 tray_str[tray_len] = '\0';
7520
7521 if ((tray_ptr = strstr(tray_str, "level=")) != NULL)
7522 ready_sheets = atoi(tray_ptr + 6);
7523 else
7524 ready_sheets = 0;
7525 }
7526 else
7527 ready_sheets = 0;
7528
7529 if (printer->web_forms)
7530 {
7531 html_printf(client, " <select name=\"level%d\">", i);
7532 for (j = 0; j < (int)(sizeof(sheets) / sizeof(sheets[0])); j ++)
7533 {
7534 if (!strcmp(media_source, "by-pass-tray") && sheets[j] > 25)
7535 continue;
7536
7537 if (sheets[j] < 0)
7538 html_printf(client, "<option value=\"%d\"%s>Unknown</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "");
7539 else
7540 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "", sheets[j]);
7541 }
7542 html_printf(client, "</select></td></tr>\n");
7543 }
7544 else if (ready_sheets == 1)
7545 html_printf(client, ", 1 sheet</td></tr>\n");
7546 else if (ready_sheets > 0)
7547 html_printf(client, ", %d sheets</td></tr>\n", ready_sheets);
7548 else
7549 html_printf(client, "</td></tr>\n");
7550 }
7551
7552 if (printer->web_forms)
7553 {
7554 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\">");
7555 if (num_options > 0)
7556 html_printf(client, " <span class=\"badge\" id=\"status\">Media updated.</span>\n");
7557 html_printf(client, "</td></tr></table></form>\n");
7558
7559 if (num_options > 0)
7560 html_printf(client, "<script>\n"
7561 "setTimeout(hide_status, 3000);\n"
7562 "function hide_status() {\n"
7563 " var status = document.getElementById('status');\n"
7564 " status.style.display = 'none';\n"
7565 "}\n"
7566 "</script>\n");
7567 }
7568 else
7569 html_printf(client, "</table>\n");
7570
7571 html_footer(client);
7572
7573 return (1);
7574 }
7575
7576
7577 /*
7578 * 'show_status()' - Show printer/system state.
7579 */
7580
7581 static int /* O - 1 on success, 0 on failure */
7582 show_status(ippeve_client_t *client) /* I - Client connection */
7583 {
7584 ippeve_printer_t *printer = client->printer;
7585 /* Printer */
7586 ippeve_job_t *job; /* Current job */
7587 int i; /* Looping var */
7588 ippeve_preason_t reason; /* Current reason */
7589 static const char * const reasons[] = /* Reason strings */
7590 {
7591 "Other",
7592 "Cover Open",
7593 "Input Tray Missing",
7594 "Marker Supply Empty",
7595 "Marker Supply Low",
7596 "Marker Waste Almost Full",
7597 "Marker Waste Full",
7598 "Media Empty",
7599 "Media Jam",
7600 "Media Low",
7601 "Media Needed",
7602 "Moving to Paused",
7603 "Paused",
7604 "Spool Area Full",
7605 "Toner Empty",
7606 "Toner Low"
7607 };
7608 static const char * const state_colors[] =
7609 { /* State colors */
7610 "#0C0", /* Idle */
7611 "#EE0", /* Processing */
7612 "#C00" /* Stopped */
7613 };
7614
7615
7616 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7617 return (0);
7618
7619 html_header(client, printer->name, printer->state == IPP_PSTATE_PROCESSING ? 5 : 15);
7620 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);
7621 html_printf(client, "<p>%s, %d job(s).", printer->state == IPP_PSTATE_IDLE ? "Idle" : printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(printer->jobs));
7622 for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
7623 if (printer->state_reasons & reason)
7624 html_printf(client, "\n<br>&nbsp;&nbsp;&nbsp;&nbsp;%s", reasons[i]);
7625 html_printf(client, "</p>\n");
7626
7627 if (cupsArrayCount(printer->jobs) > 0)
7628 {
7629 _cupsRWLockRead(&(printer->rwlock));
7630
7631 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");
7632 for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs); job; job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
7633 {
7634 char when[256], /* When job queued/started/finished */
7635 hhmmss[64]; /* Time HH:MM:SS */
7636
7637 switch (job->state)
7638 {
7639 case IPP_JSTATE_PENDING :
7640 case IPP_JSTATE_HELD :
7641 snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss)));
7642 break;
7643 case IPP_JSTATE_PROCESSING :
7644 case IPP_JSTATE_STOPPED :
7645 snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss)));
7646 break;
7647 case IPP_JSTATE_ABORTED :
7648 snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7649 break;
7650 case IPP_JSTATE_CANCELED :
7651 snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7652 break;
7653 case IPP_JSTATE_COMPLETED :
7654 snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7655 break;
7656 }
7657
7658 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);
7659 }
7660 html_printf(client, "</tbody></table>\n");
7661
7662 _cupsRWUnlock(&(printer->rwlock));
7663 }
7664
7665 html_footer(client);
7666
7667 return (1);
7668 }
7669
7670
7671 /*
7672 * 'show_supplies()' - Show printer supplies.
7673 */
7674
7675 static int /* O - 1 on success, 0 on failure */
7676 show_supplies(
7677 ippeve_client_t *client) /* I - Client connection */
7678 {
7679 ippeve_printer_t *printer = client->printer;
7680 /* Printer */
7681 int i, /* Looping var */
7682 num_supply; /* Number of supplies */
7683 ipp_attribute_t *supply, /* printer-supply attribute */
7684 *supply_desc; /* printer-supply-description attribute */
7685 int num_options = 0; /* Number of form options */
7686 cups_option_t *options = NULL; /* Form options */
7687 int supply_len, /* Length of supply value */
7688 level; /* Supply level */
7689 const char *supply_value; /* Supply value */
7690 char supply_text[1024], /* Supply string */
7691 *supply_ptr; /* Pointer into supply string */
7692 static const char * const printer_supply[] =
7693 { /* printer-supply values */
7694 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
7695 "maxcapacity=100;level=%d;colorantname=unknown;",
7696 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
7697 "maxcapacity=100;level=%d;colorantname=black;",
7698 "index=3;class=supplyThatIsConsumed;type=toner;unit=percent;"
7699 "maxcapacity=100;level=%d;colorantname=cyan;",
7700 "index=4;class=supplyThatIsConsumed;type=toner;unit=percent;"
7701 "maxcapacity=100;level=%d;colorantname=magenta;",
7702 "index=5;class=supplyThatIsConsumed;type=toner;unit=percent;"
7703 "maxcapacity=100;level=%d;colorantname=yellow;"
7704 };
7705 static const char * const backgrounds[] =
7706 { /* Background colors for the supply-level bars */
7707 "#777 linear-gradient(#333,#777)",
7708 "#000 linear-gradient(#666,#000)",
7709 "#0FF linear-gradient(#6FF,#0FF)",
7710 "#F0F linear-gradient(#F6F,#F0F)",
7711 "#CC0 linear-gradient(#EE6,#EE0)"
7712 };
7713 static const char * const colors[] = /* Text colors for the supply-level bars */
7714 {
7715 "#fff",
7716 "#fff",
7717 "#000",
7718 "#000",
7719 "#000"
7720 };
7721
7722
7723 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7724 return (0);
7725
7726 html_header(client, printer->name, 0);
7727
7728 if ((supply = ippFindAttribute(printer->attrs, "printer-supply", IPP_TAG_STRING)) == NULL)
7729 {
7730 html_printf(client, "<p>Error: No printer-supply defined for printer.</p>\n");
7731 html_footer(client);
7732 return (1);
7733 }
7734
7735 num_supply = ippGetCount(supply);
7736
7737 if ((supply_desc = ippFindAttribute(printer->attrs, "printer-supply-description", IPP_TAG_TEXT)) == NULL)
7738 {
7739 html_printf(client, "<p>Error: No printer-supply-description defined for printer.</p>\n");
7740 html_footer(client);
7741 return (1);
7742 }
7743
7744 if (num_supply != ippGetCount(supply_desc))
7745 {
7746 html_printf(client, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n");
7747 html_footer(client);
7748 return (1);
7749 }
7750
7751 if (printer->web_forms)
7752 num_options = parse_options(client, &options);
7753
7754 if (num_options > 0)
7755 {
7756 /*
7757 * WARNING: A real printer/server implementation MUST NOT implement
7758 * supply updates via a GET request - GET requests are supposed to be
7759 * idempotent (without side-effects) and we obviously are not
7760 * authenticating access here. This form is provided solely to
7761 * enable testing and development!
7762 */
7763
7764 char name[64]; /* Form field */
7765 const char *val; /* Form value */
7766
7767 _cupsRWLockWrite(&printer->rwlock);
7768
7769 ippDeleteAttribute(printer->attrs, supply);
7770 supply = NULL;
7771
7772 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);
7773
7774 for (i = 0; i < num_supply; i ++)
7775 {
7776 snprintf(name, sizeof(name), "supply%d", i);
7777 if ((val = cupsGetOption(name, num_options, options)) != NULL)
7778 {
7779 level = atoi(val); /* New level */
7780
7781 snprintf(supply_text, sizeof(supply_text), printer_supply[i], level);
7782 if (supply)
7783 ippSetOctetString(printer->attrs, &supply, ippGetCount(supply), supply_text, (int)strlen(supply_text));
7784 else
7785 supply = ippAddOctetString(printer->attrs, IPP_TAG_PRINTER, "printer-supply", supply_text, (int)strlen(supply_text));
7786
7787 if (i == 0)
7788 {
7789 if (level == 100)
7790 printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_FULL;
7791 else if (level > 90)
7792 printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL;
7793 }
7794 else
7795 {
7796 if (level == 0)
7797 printer->state_reasons |= IPPEVE_PREASON_TONER_EMPTY;
7798 else if (level < 10)
7799 printer->state_reasons |= IPPEVE_PREASON_TONER_LOW;
7800 }
7801 }
7802 }
7803
7804 _cupsRWUnlock(&printer->rwlock);
7805 }
7806
7807 if (printer->web_forms)
7808 html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
7809
7810 html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
7811 for (i = 0; i < num_supply; i ++)
7812 {
7813 supply_value = ippGetOctetString(supply, i, &supply_len);
7814 if (supply_len > (int)(sizeof(supply_text) - 1))
7815 supply_len = (int)sizeof(supply_text) - 1;
7816
7817 memcpy(supply_text, supply_value, (size_t)supply_len);
7818 supply_text[supply_len] = '\0';
7819
7820 if ((supply_ptr = strstr(supply_text, "level=")) != NULL)
7821 level = atoi(supply_ptr + 6);
7822 else
7823 level = 50;
7824
7825 if (printer->web_forms)
7826 html_printf(client, "<tr><th>%s:</th><td><input name=\"supply%d\" size=\"3\" value=\"%d\"></td>", ippGetString(supply_desc, i, NULL), i, level);
7827 else
7828 html_printf(client, "<tr><th>%s:</th>", ippGetString(supply_desc, i, NULL));
7829
7830 if (level < 10)
7831 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);
7832 else
7833 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);
7834 }
7835
7836 if (printer->web_forms)
7837 {
7838 html_printf(client, "<tr><td></td><td colspan=\"2\"><input type=\"submit\" value=\"Update Supplies\">");
7839 if (num_options > 0)
7840 html_printf(client, " <span class=\"badge\" id=\"status\">Supplies updated.</span>\n");
7841 html_printf(client, "</td></tr>\n</table>\n</form>\n");
7842
7843 if (num_options > 0)
7844 html_printf(client, "<script>\n"
7845 "setTimeout(hide_status, 3000);\n"
7846 "function hide_status() {\n"
7847 " var status = document.getElementById('status');\n"
7848 " status.style.display = 'none';\n"
7849 "}\n"
7850 "</script>\n");
7851 }
7852 else
7853 html_printf(client, "</table>\n");
7854
7855 html_footer(client);
7856
7857 return (1);
7858 }
7859
7860
7861 /*
7862 * 'time_string()' - Return the local time in hours, minutes, and seconds.
7863 */
7864
7865 static char *
7866 time_string(time_t tv, /* I - Time value */
7867 char *buffer, /* I - Buffer */
7868 size_t bufsize) /* I - Size of buffer */
7869 {
7870 struct tm date; /* Local time and date */
7871
7872 localtime_r(&tv, &date);
7873
7874 strftime(buffer, bufsize, "%X", &date);
7875
7876 return (buffer);
7877 }
7878
7879
7880 /*
7881 * 'usage()' - Show program usage.
7882 */
7883
7884 static void
7885 usage(int status) /* O - Exit status */
7886 {
7887 _cupsLangPuts(stdout, _("Usage: ippeveprinter [options] \"name\""));
7888 _cupsLangPuts(stdout, _("Options:"));
7889 _cupsLangPuts(stdout, _("--help Show program help"));
7890 _cupsLangPuts(stdout, _("--no-web-forms Disable web forms for media and supplies"));
7891 _cupsLangPuts(stdout, _("--pam-service service Use the named PAM service"));
7892 _cupsLangPuts(stdout, _("--version Show program version"));
7893 _cupsLangPuts(stdout, _("-2 Set 2-sided printing support (default=1-sided)"));
7894 _cupsLangPuts(stdout, _("-A Enable authentication"));
7895 _cupsLangPuts(stdout, _("-D device-uri Set the device URI for the printer"));
7896 _cupsLangPuts(stdout, _("-F output-type/subtype Set the output format for the printer"));
7897 #ifdef HAVE_SSL
7898 _cupsLangPuts(stdout, _("-K keypath Set location of server X.509 certificates and keys."));
7899 #endif /* HAVE_SSL */
7900 _cupsLangPuts(stdout, _("-M manufacturer Set manufacturer name (default=Test)"));
7901 _cupsLangPuts(stdout, _("-P filename.ppd Load printer attributes from PPD file"));
7902 _cupsLangPuts(stdout, _("-V version Set default IPP version"));
7903 _cupsLangPuts(stdout, _("-a filename.conf Load printer attributes from conf file"));
7904 _cupsLangPuts(stdout, _("-c command Set print command"));
7905 _cupsLangPuts(stdout, _("-d spool-directory Set spool directory"));
7906 _cupsLangPuts(stdout, _("-f type/subtype[,...] Set supported file types"));
7907 _cupsLangPuts(stdout, _("-i iconfile.png Set icon file"));
7908 _cupsLangPuts(stdout, _("-k Keep job spool files"));
7909 _cupsLangPuts(stdout, _("-l location Set location of printer"));
7910 _cupsLangPuts(stdout, _("-m model Set model name (default=Printer)"));
7911 _cupsLangPuts(stdout, _("-n hostname Set hostname for printer"));
7912 _cupsLangPuts(stdout, _("-p port Set port number for printer"));
7913 _cupsLangPuts(stdout, _("-r subtype,[subtype] Set DNS-SD service subtype"));
7914 _cupsLangPuts(stdout, _("-s speed[,color-speed] Set speed in pages per minute"));
7915 _cupsLangPuts(stdout, _("-v Be verbose"));
7916
7917 exit(status);
7918 }
7919
7920
7921 /*
7922 * 'valid_doc_attributes()' - Determine whether the document attributes are
7923 * valid.
7924 *
7925 * When one or more document attributes are invalid, this function adds a
7926 * suitable response and attributes to the unsupported group.
7927 */
7928
7929 static int /* O - 1 if valid, 0 if not */
7930 valid_doc_attributes(
7931 ippeve_client_t *client) /* I - Client */
7932 {
7933 int valid = 1; /* Valid attributes? */
7934 ipp_op_t op = ippGetOperation(client->request);
7935 /* IPP operation */
7936 const char *op_name = ippOpString(op);
7937 /* IPP operation name */
7938 ipp_attribute_t *attr, /* Current attribute */
7939 *supported; /* xxx-supported attribute */
7940 const char *compression = NULL,
7941 /* compression value */
7942 *format = NULL; /* document-format value */
7943
7944
7945 /*
7946 * Check operation attributes...
7947 */
7948
7949 if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
7950 {
7951 /*
7952 * If compression is specified, only accept a supported value in a Print-Job
7953 * or Send-Document request...
7954 */
7955
7956 compression = ippGetString(attr, 0, NULL);
7957 supported = ippFindAttribute(client->printer->attrs,
7958 "compression-supported", IPP_TAG_KEYWORD);
7959
7960 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
7961 ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
7962 (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
7963 op != IPP_OP_VALIDATE_JOB) ||
7964 !ippContainsString(supported, compression))
7965 {
7966 respond_unsupported(client, attr);
7967 valid = 0;
7968 }
7969 else
7970 {
7971 fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression);
7972
7973 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression);
7974
7975 if (strcmp(compression, "none"))
7976 {
7977 if (Verbosity)
7978 fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression);
7979 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
7980 }
7981 }
7982 }
7983
7984 /*
7985 * Is it a format we support?
7986 */
7987
7988 if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
7989 {
7990 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
7991 ippGetGroupTag(attr) != IPP_TAG_OPERATION)
7992 {
7993 respond_unsupported(client, attr);
7994 valid = 0;
7995 }
7996 else
7997 {
7998 format = ippGetString(attr, 0, NULL);
7999
8000 fprintf(stderr, "%s %s document-format=\"%s\"\n", client->hostname, op_name, format);
8001
8002 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format);
8003 }
8004 }
8005 else
8006 {
8007 format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL);
8008 if (!format)
8009 format = "application/octet-stream"; /* Should never happen */
8010
8011 attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
8012 }
8013
8014 if (format && !strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
8015 {
8016 /*
8017 * Auto-type the file using the first 8 bytes of the file...
8018 */
8019
8020 unsigned char header[8]; /* First 8 bytes of file */
8021
8022 memset(header, 0, sizeof(header));
8023 httpPeek(client->http, (char *)header, sizeof(header));
8024
8025 if (!memcmp(header, "%PDF", 4))
8026 format = "application/pdf";
8027 else if (!memcmp(header, "%!", 2))
8028 format = "application/postscript";
8029 else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
8030 format = "image/jpeg";
8031 else if (!memcmp(header, "\211PNG", 4))
8032 format = "image/png";
8033 else if (!memcmp(header, "RAS2", 4))
8034 format = "image/pwg-raster";
8035 else if (!memcmp(header, "UNIRAST", 8))
8036 format = "image/urf";
8037 else
8038 format = NULL;
8039
8040 if (format)
8041 {
8042 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n", client->hostname, op_name, format);
8043
8044 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
8045 }
8046 }
8047
8048 if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
8049 {
8050 respond_unsupported(client, attr);
8051 valid = 0;
8052 }
8053
8054 /*
8055 * document-name
8056 */
8057
8058 if ((attr = ippFindAttribute(client->request, "document-name", IPP_TAG_NAME)) != NULL)
8059 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
8060
8061 return (valid);
8062 }
8063
8064
8065 /*
8066 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
8067 *
8068 * When one or more job attributes are invalid, this function adds a suitable
8069 * response and attributes to the unsupported group.
8070 */
8071
8072 static int /* O - 1 if valid, 0 if not */
8073 valid_job_attributes(
8074 ippeve_client_t *client) /* I - Client */
8075 {
8076 int i, /* Looping var */
8077 count, /* Number of values */
8078 valid = 1; /* Valid attributes? */
8079 ipp_attribute_t *attr, /* Current attribute */
8080 *supported; /* xxx-supported attribute */
8081
8082
8083 /*
8084 * Check operation attributes...
8085 */
8086
8087 valid = valid_doc_attributes(client);
8088
8089 /*
8090 * Check the various job template attributes...
8091 */
8092
8093 if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL)
8094 {
8095 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
8096 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
8097 {
8098 respond_unsupported(client, attr);
8099 valid = 0;
8100 }
8101 }
8102
8103 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
8104 {
8105 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
8106 {
8107 respond_unsupported(client, attr);
8108 valid = 0;
8109 }
8110 }
8111
8112 if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
8113 {
8114 if (ippGetCount(attr) != 1 ||
8115 (ippGetValueTag(attr) != IPP_TAG_NAME &&
8116 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8117 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
8118 strcmp(ippGetString(attr, 0, NULL), "no-hold"))
8119 {
8120 respond_unsupported(client, attr);
8121 valid = 0;
8122 }
8123 }
8124
8125 if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL)
8126 {
8127 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0)
8128 {
8129 respond_unsupported(client, attr);
8130 valid = 0;
8131 }
8132 }
8133
8134 if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL)
8135 {
8136 if (ippGetCount(attr) != 1 ||
8137 (ippGetValueTag(attr) != IPP_TAG_NAME &&
8138 ippGetValueTag(attr) != IPP_TAG_NAMELANG))
8139 {
8140 respond_unsupported(client, attr);
8141 valid = 0;
8142 }
8143
8144 ippSetGroupTag(client->request, &attr, IPP_TAG_JOB);
8145 }
8146 else
8147 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
8148
8149 if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL)
8150 {
8151 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
8152 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
8153 {
8154 respond_unsupported(client, attr);
8155 valid = 0;
8156 }
8157 }
8158
8159 if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
8160 {
8161 if (ippGetCount(attr) != 1 ||
8162 (ippGetValueTag(attr) != IPP_TAG_NAME &&
8163 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8164 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
8165 strcmp(ippGetString(attr, 0, NULL), "none"))
8166 {
8167 respond_unsupported(client, attr);
8168 valid = 0;
8169 }
8170 }
8171
8172 if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
8173 {
8174 if (ippGetCount(attr) != 1 ||
8175 (ippGetValueTag(attr) != IPP_TAG_NAME &&
8176 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8177 ippGetValueTag(attr) != IPP_TAG_KEYWORD))
8178 {
8179 respond_unsupported(client, attr);
8180 valid = 0;
8181 }
8182 else
8183 {
8184 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
8185
8186 if (!ippContainsString(supported, ippGetString(attr, 0, NULL)))
8187 {
8188 respond_unsupported(client, attr);
8189 valid = 0;
8190 }
8191 }
8192 }
8193
8194 if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL)
8195 {
8196 ipp_t *col, /* media-col collection */
8197 *size; /* media-size collection */
8198 ipp_attribute_t *member, /* Member attribute */
8199 *x_dim, /* x-dimension */
8200 *y_dim; /* y-dimension */
8201 int x_value, /* y-dimension value */
8202 y_value; /* x-dimension value */
8203
8204 if (ippGetCount(attr) != 1 ||
8205 ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
8206 {
8207 respond_unsupported(client, attr);
8208 valid = 0;
8209 }
8210
8211 col = ippGetCollection(attr, 0);
8212
8213 if ((member = ippFindAttribute(col, "media-size-name", IPP_TAG_ZERO)) != NULL)
8214 {
8215 if (ippGetCount(member) != 1 ||
8216 (ippGetValueTag(member) != IPP_TAG_NAME &&
8217 ippGetValueTag(member) != IPP_TAG_NAMELANG &&
8218 ippGetValueTag(member) != IPP_TAG_KEYWORD))
8219 {
8220 respond_unsupported(client, attr);
8221 valid = 0;
8222 }
8223 else
8224 {
8225 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
8226
8227 if (!ippContainsString(supported, ippGetString(member, 0, NULL)))
8228 {
8229 respond_unsupported(client, attr);
8230 valid = 0;
8231 }
8232 }
8233 }
8234 else if ((member = ippFindAttribute(col, "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
8235 {
8236 if (ippGetCount(member) != 1)
8237 {
8238 respond_unsupported(client, attr);
8239 valid = 0;
8240 }
8241 else
8242 {
8243 size = ippGetCollection(member, 0);
8244
8245 if ((x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(x_dim) != 1 ||
8246 (y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(y_dim) != 1)
8247 {
8248 respond_unsupported(client, attr);
8249 valid = 0;
8250 }
8251 else
8252 {
8253 x_value = ippGetInteger(x_dim, 0);
8254 y_value = ippGetInteger(y_dim, 0);
8255 supported = ippFindAttribute(client->printer->attrs, "media-size-supported", IPP_TAG_BEGIN_COLLECTION);
8256 count = ippGetCount(supported);
8257
8258 for (i = 0; i < count ; i ++)
8259 {
8260 size = ippGetCollection(supported, i);
8261 x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_ZERO);
8262 y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_ZERO);
8263
8264 if (ippContainsInteger(x_dim, x_value) && ippContainsInteger(y_dim, y_value))
8265 break;
8266 }
8267
8268 if (i >= count)
8269 {
8270 respond_unsupported(client, attr);
8271 valid = 0;
8272 }
8273 }
8274 }
8275 }
8276 }
8277
8278 if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL)
8279 {
8280 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
8281 (strcmp(ippGetString(attr, 0, NULL),
8282 "separate-documents-uncollated-copies") &&
8283 strcmp(ippGetString(attr, 0, NULL),
8284 "separate-documents-collated-copies")))
8285 {
8286 respond_unsupported(client, attr);
8287 valid = 0;
8288 }
8289 }
8290
8291 if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL)
8292 {
8293 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
8294 ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
8295 ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
8296 {
8297 respond_unsupported(client, attr);
8298 valid = 0;
8299 }
8300 }
8301
8302 if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
8303 {
8304 if (ippGetValueTag(attr) != IPP_TAG_RANGE)
8305 {
8306 respond_unsupported(client, attr);
8307 valid = 0;
8308 }
8309 }
8310
8311 if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL)
8312 {
8313 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
8314 ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
8315 ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
8316 {
8317 respond_unsupported(client, attr);
8318 valid = 0;
8319 }
8320 }
8321
8322 if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
8323 {
8324 supported = ippFindAttribute(client->printer->attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION);
8325
8326 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION ||
8327 !supported)
8328 {
8329 respond_unsupported(client, attr);
8330 valid = 0;
8331 }
8332 else
8333 {
8334 int xdpi, /* Horizontal resolution for job template attribute */
8335 ydpi, /* Vertical resolution for job template attribute */
8336 sydpi; /* Vertical resolution for supported value */
8337 ipp_res_t units, /* Units for job template attribute */
8338 sunits; /* Units for supported value */
8339
8340 xdpi = ippGetResolution(attr, 0, &ydpi, &units);
8341 count = ippGetCount(supported);
8342
8343 for (i = 0; i < count; i ++)
8344 {
8345 if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits)
8346 break;
8347 }
8348
8349 if (i >= count)
8350 {
8351 respond_unsupported(client, attr);
8352 valid = 0;
8353 }
8354 }
8355 }
8356
8357 if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
8358 {
8359 const char *sides = ippGetString(attr, 0, NULL);
8360 /* "sides" value... */
8361
8362 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
8363 {
8364 respond_unsupported(client, attr);
8365 valid = 0;
8366 }
8367 else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
8368 {
8369 if (!ippContainsString(supported, sides))
8370 {
8371 respond_unsupported(client, attr);
8372 valid = 0;
8373 }
8374 }
8375 else if (strcmp(sides, "one-sided"))
8376 {
8377 respond_unsupported(client, attr);
8378 valid = 0;
8379 }
8380 }
8381
8382 return (valid);
8383 }