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