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