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