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