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