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