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