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