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