]> git.ipfire.org Git - thirdparty/cups.git/blob - test/ippserver.c
Fix source file header text duplication text duplication.
[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 _cupsRWLockWrite(&(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 lastcol = attrptr;
5011 }
5012 while (!strcmp(token, "{"));
5013 break;
5014
5015 case IPP_TAG_STRING :
5016 attrptr = ippAddOctetString(attrs, IPP_TAG_PRINTER, attr, token, (int)strlen(token));
5017 break;
5018
5019 default :
5020 fprintf(stderr, "ippserver: Unsupported ATTR value tag %s on line %d of \"%s\".\n", ippTagString(value), linenum, filename);
5021 exit(1);
5022
5023 case IPP_TAG_TEXTLANG :
5024 case IPP_TAG_NAMELANG :
5025 case IPP_TAG_TEXT :
5026 case IPP_TAG_NAME :
5027 case IPP_TAG_KEYWORD :
5028 case IPP_TAG_URI :
5029 case IPP_TAG_URISCHEME :
5030 case IPP_TAG_CHARSET :
5031 case IPP_TAG_LANGUAGE :
5032 case IPP_TAG_MIMETYPE :
5033 if (!strchr(token, ','))
5034 attrptr = ippAddString(attrs, IPP_TAG_PRINTER, value, attr, NULL, token);
5035 else
5036 {
5037 /*
5038 * Multiple string values...
5039 */
5040
5041 int num_values; /* Number of values */
5042 char *values[100], /* Values */
5043 *ptr; /* Pointer to next value */
5044
5045
5046 values[0] = token;
5047 num_values = 1;
5048
5049 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
5050 {
5051 if (ptr > token && ptr[-1] == '\\')
5052 _cups_strcpy(ptr - 1, ptr);
5053 else
5054 {
5055 *ptr++ = '\0';
5056 values[num_values] = ptr;
5057 num_values ++;
5058 if (num_values >= (int)(sizeof(values) / sizeof(values[0])))
5059 break;
5060 }
5061 }
5062
5063 attrptr = ippAddStrings(attrs, IPP_TAG_PRINTER, value, attr, num_values, NULL, (const char **)values);
5064 }
5065 break;
5066 }
5067
5068 if (!attrptr)
5069 {
5070 fprintf(stderr, "ippserver: Unable to add attribute on line %d of \"%s\": %s\n", linenum, filename, cupsLastErrorString());
5071 exit(1);
5072 }
5073 }
5074 else
5075 {
5076 fprintf(stderr, "ippserver: Unknown directive \"%s\" on line %d of \"%s\".\n", token, linenum, filename);
5077 exit(1);
5078 }
5079 }
5080
5081 fclose(fp);
5082 }
5083
5084
5085 /*
5086 * 'parse_options()' - Parse URL options into CUPS options.
5087 *
5088 * The client->options string is destroyed by this function.
5089 */
5090
5091 static int /* O - Number of options */
5092 parse_options(_ipp_client_t *client, /* I - Client */
5093 cups_option_t **options) /* O - Options */
5094 {
5095 char *name, /* Name */
5096 *value, /* Value */
5097 *next; /* Next name=value pair */
5098 int num_options = 0; /* Number of options */
5099
5100
5101 *options = NULL;
5102
5103 for (name = client->options; name && *name; name = next)
5104 {
5105 if ((value = strchr(name, '=')) == NULL)
5106 break;
5107
5108 *value++ = '\0';
5109 if ((next = strchr(value, '&')) != NULL)
5110 *next++ = '\0';
5111
5112 num_options = cupsAddOption(name, value, num_options, options);
5113 }
5114
5115 return (num_options);
5116 }
5117
5118
5119 /*
5120 * 'process_attr_message()' - Process an ATTR: message from a command.
5121 */
5122
5123 static void
5124 process_attr_message(
5125 _ipp_job_t *job, /* I - Job */
5126 char *message) /* I - Message */
5127 {
5128 (void)job;
5129 (void)message;
5130 }
5131
5132
5133 /*
5134 * 'process_client()' - Process client requests on a thread.
5135 */
5136
5137 static void * /* O - Exit status */
5138 process_client(_ipp_client_t *client) /* I - Client */
5139 {
5140 /*
5141 * Loop until we are out of requests or timeout (30 seconds)...
5142 */
5143
5144 #ifdef HAVE_SSL
5145 int first_time = 1; /* First time request? */
5146 #endif /* HAVE_SSL */
5147
5148 while (httpWait(client->http, 30000))
5149 {
5150 #ifdef HAVE_SSL
5151 if (first_time)
5152 {
5153 /*
5154 * See if we need to negotiate a TLS connection...
5155 */
5156
5157 char buf[1]; /* First byte from client */
5158
5159 if (recv(httpGetFd(client->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0])))
5160 {
5161 fprintf(stderr, "%s Starting HTTPS session.\n", client->hostname);
5162
5163 if (httpEncryption(client->http, HTTP_ENCRYPTION_ALWAYS))
5164 {
5165 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
5166 break;
5167 }
5168
5169 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
5170 }
5171
5172 first_time = 0;
5173 }
5174 #endif /* HAVE_SSL */
5175
5176 if (!process_http(client))
5177 break;
5178 }
5179
5180 /*
5181 * Close the conection to the client and return...
5182 */
5183
5184 delete_client(client);
5185
5186 return (NULL);
5187 }
5188
5189
5190 /*
5191 * 'process_http()' - Process a HTTP request.
5192 */
5193
5194 int /* O - 1 on success, 0 on failure */
5195 process_http(_ipp_client_t *client) /* I - Client connection */
5196 {
5197 char uri[1024]; /* URI */
5198 http_state_t http_state; /* HTTP state */
5199 http_status_t http_status; /* HTTP status */
5200 ipp_state_t ipp_state; /* State of IPP transfer */
5201 char scheme[32], /* Method/scheme */
5202 userpass[128], /* Username:password */
5203 hostname[HTTP_MAX_HOST];
5204 /* Hostname */
5205 int port; /* Port number */
5206 const char *encoding; /* Content-Encoding value */
5207 static const char * const http_states[] =
5208 { /* Strings for logging HTTP method */
5209 "WAITING",
5210 "OPTIONS",
5211 "GET",
5212 "GET_SEND",
5213 "HEAD",
5214 "POST",
5215 "POST_RECV",
5216 "POST_SEND",
5217 "PUT",
5218 "PUT_RECV",
5219 "DELETE",
5220 "TRACE",
5221 "CONNECT",
5222 "STATUS",
5223 "UNKNOWN_METHOD",
5224 "UNKNOWN_VERSION"
5225 };
5226
5227
5228 /*
5229 * Clear state variables...
5230 */
5231
5232 ippDelete(client->request);
5233 ippDelete(client->response);
5234
5235 client->request = NULL;
5236 client->response = NULL;
5237 client->operation = HTTP_STATE_WAITING;
5238
5239 /*
5240 * Read a request from the connection...
5241 */
5242
5243 while ((http_state = httpReadRequest(client->http, uri,
5244 sizeof(uri))) == HTTP_STATE_WAITING)
5245 usleep(1);
5246
5247 /*
5248 * Parse the request line...
5249 */
5250
5251 if (http_state == HTTP_STATE_ERROR)
5252 {
5253 if (httpError(client->http) == EPIPE)
5254 fprintf(stderr, "%s Client closed connection.\n", client->hostname);
5255 else
5256 fprintf(stderr, "%s Bad request line (%s).\n", client->hostname,
5257 strerror(httpError(client->http)));
5258
5259 return (0);
5260 }
5261 else if (http_state == HTTP_STATE_UNKNOWN_METHOD)
5262 {
5263 fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname);
5264 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5265 return (0);
5266 }
5267 else if (http_state == HTTP_STATE_UNKNOWN_VERSION)
5268 {
5269 fprintf(stderr, "%s Bad HTTP version.\n", client->hostname);
5270 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5271 return (0);
5272 }
5273
5274 fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state],
5275 uri);
5276
5277 /*
5278 * Separate the URI into its components...
5279 */
5280
5281 if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
5282 userpass, sizeof(userpass),
5283 hostname, sizeof(hostname), &port,
5284 client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK &&
5285 (http_state != HTTP_STATE_OPTIONS || strcmp(uri, "*")))
5286 {
5287 fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri);
5288 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5289 return (0);
5290 }
5291
5292 if ((client->options = strchr(client->uri, '?')) != NULL)
5293 *(client->options)++ = '\0';
5294
5295 /*
5296 * Process the request...
5297 */
5298
5299 client->start = time(NULL);
5300 client->operation = httpGetState(client->http);
5301
5302 /*
5303 * Parse incoming parameters until the status changes...
5304 */
5305
5306 while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE);
5307
5308 if (http_status != HTTP_STATUS_OK)
5309 {
5310 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5311 return (0);
5312 }
5313
5314 if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] &&
5315 httpGetVersion(client->http) >= HTTP_VERSION_1_1)
5316 {
5317 /*
5318 * HTTP/1.1 and higher require the "Host:" field...
5319 */
5320
5321 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5322 return (0);
5323 }
5324
5325 /*
5326 * Handle HTTP Upgrade...
5327 */
5328
5329 if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION),
5330 "Upgrade"))
5331 {
5332 #ifdef HAVE_SSL
5333 if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http))
5334 {
5335 if (!respond_http(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0))
5336 return (0);
5337
5338 fprintf(stderr, "%s Upgrading to encrypted connection.\n", client->hostname);
5339
5340 if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED))
5341 {
5342 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
5343 return (0);
5344 }
5345
5346 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
5347 }
5348 else
5349 #endif /* HAVE_SSL */
5350
5351 if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
5352 return (0);
5353 }
5354
5355 /*
5356 * Handle HTTP Expect...
5357 */
5358
5359 if (httpGetExpect(client->http) &&
5360 (client->operation == HTTP_STATE_POST ||
5361 client->operation == HTTP_STATE_PUT))
5362 {
5363 if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
5364 {
5365 /*
5366 * Send 100-continue header...
5367 */
5368
5369 if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
5370 return (0);
5371 }
5372 else
5373 {
5374 /*
5375 * Send 417-expectation-failed header...
5376 */
5377
5378 if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
5379 return (0);
5380 }
5381 }
5382
5383 /*
5384 * Handle new transfers...
5385 */
5386
5387 encoding = httpGetContentEncoding(client->http);
5388
5389 switch (client->operation)
5390 {
5391 case HTTP_STATE_OPTIONS :
5392 /*
5393 * Do OPTIONS command...
5394 */
5395
5396 return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
5397
5398 case HTTP_STATE_HEAD :
5399 if (!strcmp(client->uri, "/icon.png"))
5400 return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
5401 else if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies"))
5402 return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
5403 else
5404 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
5405
5406 case HTTP_STATE_GET :
5407 if (!strcmp(client->uri, "/icon.png"))
5408 {
5409 /*
5410 * Send PNG icon file.
5411 */
5412
5413 int fd; /* Icon file */
5414 struct stat fileinfo; /* Icon file information */
5415 char buffer[4096]; /* Copy buffer */
5416 ssize_t bytes; /* Bytes */
5417
5418 fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon);
5419
5420 if (!stat(client->printer->icon, &fileinfo) &&
5421 (fd = open(client->printer->icon, O_RDONLY)) >= 0)
5422 {
5423 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png",
5424 (size_t)fileinfo.st_size))
5425 {
5426 close(fd);
5427 return (0);
5428 }
5429
5430 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
5431 httpWrite2(client->http, buffer, (size_t)bytes);
5432
5433 httpFlushWrite(client->http);
5434
5435 close(fd);
5436 }
5437 else
5438 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
5439 }
5440 else if (!strcmp(client->uri, "/"))
5441 {
5442 /*
5443 * Show web status page...
5444 */
5445
5446 _ipp_job_t *job; /* Current job */
5447 int i; /* Looping var */
5448 _ipp_preason_t reason; /* Current reason */
5449 static const char * const reasons[] =
5450 { /* Reason strings */
5451 "Other",
5452 "Cover Open",
5453 "Input Tray Missing",
5454 "Marker Supply Empty",
5455 "Marker Supply Low",
5456 "Marker Waste Almost Full",
5457 "Marker Waste Full",
5458 "Media Empty",
5459 "Media Jam",
5460 "Media Low",
5461 "Media Needed",
5462 "Moving to Paused",
5463 "Paused",
5464 "Spool Area Full",
5465 "Toner Empty",
5466 "Toner Low"
5467 };
5468
5469 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
5470 return (0);
5471
5472 html_header(client, client->printer->name);
5473 html_printf(client,
5474 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION ")</b></p>\n"
5475 "<p>%s, %d job(s).", client->printer->state == IPP_PSTATE_IDLE ? "Idle" : client->printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(client->printer->jobs));
5476 for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
5477 if (client->printer->state_reasons & reason)
5478 html_printf(client, "\n<br>&nbsp;&nbsp;&nbsp;&nbsp;%s", reasons[i]);
5479 html_printf(client, "</p>\n");
5480
5481 if (cupsArrayCount(client->printer->jobs) > 0)
5482 {
5483 _cupsRWLockRead(&(client->printer->rwlock));
5484
5485 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");
5486 for (job = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs); job; job = (_ipp_job_t *)cupsArrayNext(client->printer->jobs))
5487 {
5488 char when[256], /* When job queued/started/finished */
5489 hhmmss[64]; /* Time HH:MM:SS */
5490
5491 switch (job->state)
5492 {
5493 case IPP_JSTATE_PENDING :
5494 case IPP_JSTATE_HELD :
5495 snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss)));
5496 break;
5497 case IPP_JSTATE_PROCESSING :
5498 case IPP_JSTATE_STOPPED :
5499 snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss)));
5500 break;
5501 case IPP_JSTATE_ABORTED :
5502 snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
5503 break;
5504 case IPP_JSTATE_CANCELED :
5505 snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
5506 break;
5507 case IPP_JSTATE_COMPLETED :
5508 snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
5509 break;
5510 }
5511
5512 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);
5513 }
5514 html_printf(client, "</tbody></table>\n");
5515
5516 _cupsRWUnlock(&(client->printer->rwlock));
5517 }
5518 html_footer(client);
5519
5520 return (1);
5521 }
5522 else if (!strcmp(client->uri, "/media"))
5523 {
5524 /*
5525 * Show web media page...
5526 */
5527
5528 int i, /* Looping var */
5529 num_options; /* Number of form options */
5530 cups_option_t *options; /* Form options */
5531 static const char * const sizes[] =
5532 { /* Size strings */
5533 "ISO A4",
5534 "ISO A5",
5535 "ISO A6",
5536 "DL Envelope",
5537 "US Legal",
5538 "US Letter",
5539 "#10 Envelope",
5540 "3x5 Photo",
5541 "3.5x5 Photo",
5542 "4x6 Photo",
5543 "5x7 Photo"
5544 };
5545 static const char * const types[] =
5546 /* Type strings */
5547 {
5548 "Auto",
5549 "Cardstock",
5550 "Envelope",
5551 "Labels",
5552 "Other",
5553 "Glossy Photo",
5554 "High-Gloss Photo",
5555 "Matte Photo",
5556 "Satin Photo",
5557 "Semi-Gloss Photo",
5558 "Plain",
5559 "Letterhead",
5560 "Transparency"
5561 };
5562 static const int sheets[] = /* Number of sheets */
5563 {
5564 250,
5565 100,
5566 25,
5567 5,
5568 0
5569 };
5570
5571 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
5572 return (0);
5573
5574 html_header(client, client->printer->name);
5575
5576 if ((num_options = parse_options(client, &options)) > 0)
5577 {
5578 /*
5579 * WARNING: A real printer/server implementation MUST NOT implement
5580 * media updates via a GET request - GET requests are supposed to be
5581 * idempotent (without side-effects) and we obviously are not
5582 * authenticating access here. This form is provided solely to
5583 * enable testing and development!
5584 */
5585
5586 const char *val; /* Form value */
5587
5588 if ((val = cupsGetOption("main_size", num_options, options)) != NULL)
5589 client->printer->main_size = atoi(val);
5590 if ((val = cupsGetOption("main_type", num_options, options)) != NULL)
5591 client->printer->main_type = atoi(val);
5592 if ((val = cupsGetOption("main_level", num_options, options)) != NULL)
5593 client->printer->main_level = atoi(val);
5594
5595 if ((val = cupsGetOption("envelope_size", num_options, options)) != NULL)
5596 client->printer->envelope_size = atoi(val);
5597 if ((val = cupsGetOption("envelope_level", num_options, options)) != NULL)
5598 client->printer->envelope_level = atoi(val);
5599
5600 if ((val = cupsGetOption("photo_size", num_options, options)) != NULL)
5601 client->printer->photo_size = atoi(val);
5602 if ((val = cupsGetOption("photo_type", num_options, options)) != NULL)
5603 client->printer->photo_type = atoi(val);
5604 if ((val = cupsGetOption("photo_level", num_options, options)) != NULL)
5605 client->printer->photo_level = atoi(val);
5606
5607 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))
5608 client->printer->state_reasons |= _IPP_PREASON_MEDIA_LOW;
5609 else
5610 client->printer->state_reasons &= (_ipp_preason_t)~_IPP_PREASON_MEDIA_LOW;
5611
5612 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))
5613 {
5614 client->printer->state_reasons |= _IPP_PREASON_MEDIA_EMPTY;
5615 if (client->printer->active_job)
5616 client->printer->state_reasons |= _IPP_PREASON_MEDIA_NEEDED;
5617 }
5618 else
5619 client->printer->state_reasons &= (_ipp_preason_t)~(_IPP_PREASON_MEDIA_EMPTY | _IPP_PREASON_MEDIA_NEEDED);
5620
5621 html_printf(client, "<blockquote>Media updated.</blockquote>\n");
5622 }
5623
5624 html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
5625
5626 html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
5627 html_printf(client, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
5628 for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
5629 if (!strstr(sizes[i], "Envelope") && !strstr(sizes[i], "Photo"))
5630 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->main_size ? " selected" : "", sizes[i]);
5631 html_printf(client, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
5632 for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i ++)
5633 if (!strstr(types[i], "Photo"))
5634 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->main_type ? " selected" : "", types[i]);
5635 html_printf(client, "</select> <select name=\"main_level\">");
5636 for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
5637 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->main_level ? " selected" : "", sheets[i]);
5638 html_printf(client, "</select></td></tr>\n");
5639
5640 html_printf(client,
5641 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
5642 for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
5643 if (strstr(sizes[i], "Envelope"))
5644 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->envelope_size ? " selected" : "", sizes[i]);
5645 html_printf(client, "</select> <select name=\"envelope_level\">");
5646 for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
5647 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->envelope_level ? " selected" : "", sheets[i]);
5648 html_printf(client, "</select></td></tr>\n");
5649
5650 html_printf(client,
5651 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
5652 for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
5653 if (strstr(sizes[i], "Photo"))
5654 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->photo_size ? " selected" : "", sizes[i]);
5655 html_printf(client, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
5656 for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i ++)
5657 if (strstr(types[i], "Photo"))
5658 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->photo_type ? " selected" : "", types[i]);
5659 html_printf(client, "</select> <select name=\"photo_level\">");
5660 for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
5661 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->photo_level ? " selected" : "", sheets[i]);
5662 html_printf(client, "</select></td></tr>\n");
5663
5664 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5665 html_footer(client);
5666
5667 return (1);
5668 }
5669 else if (!strcmp(client->uri, "/supplies"))
5670 {
5671 /*
5672 * Show web supplies page...
5673 */
5674
5675 int i, j, /* Looping vars */
5676 num_options; /* Number of form options */
5677 cups_option_t *options; /* Form options */
5678 static const int levels[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5679
5680 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
5681 return (0);
5682
5683 html_header(client, client->printer->name);
5684
5685 if ((num_options = parse_options(client, &options)) > 0)
5686 {
5687 /*
5688 * WARNING: A real printer/server implementation MUST NOT implement
5689 * supply updates via a GET request - GET requests are supposed to be
5690 * idempotent (without side-effects) and we obviously are not
5691 * authenticating access here. This form is provided solely to
5692 * enable testing and development!
5693 */
5694
5695 char name[64]; /* Form field */
5696 const char *val; /* Form value */
5697
5698 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);
5699
5700 for (i = 0; i < (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])); i ++)
5701 {
5702 snprintf(name, sizeof(name), "supply_%d", i);
5703 if ((val = cupsGetOption(name, num_options, options)) != NULL)
5704 {
5705 int level = client->printer->supplies[i] = atoi(val);
5706 /* New level */
5707
5708 if (i < 4)
5709 {
5710 if (level == 0)
5711 client->printer->state_reasons |= _IPP_PREASON_TONER_EMPTY;
5712 else if (level < 10)
5713 client->printer->state_reasons |= _IPP_PREASON_TONER_LOW;
5714 }
5715 else
5716 {
5717 if (level == 100)
5718 client->printer->state_reasons |= _IPP_PREASON_MARKER_WASTE_FULL;
5719 else if (level > 90)
5720 client->printer->state_reasons |= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL;
5721 }
5722 }
5723 }
5724
5725 html_printf(client, "<blockquote>Supplies updated.</blockquote>\n");
5726 }
5727
5728 html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
5729
5730 html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
5731 for (i = 0; i < (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])); i ++)
5732 {
5733 html_printf(client, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies[i], i);
5734 for (j = 0; j < (int)(sizeof(levels) / sizeof(levels[0])); j ++)
5735 html_printf(client, "<option value=\"%d\"%s>%d%%</option>", levels[j], levels[j] == client->printer->supplies[i] ? " selected" : "", levels[j]);
5736 html_printf(client, "</select></td></tr>\n");
5737 }
5738 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5739 html_footer(client);
5740
5741 return (1);
5742 }
5743 else
5744 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
5745 break;
5746
5747 case HTTP_STATE_POST :
5748 if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
5749 "application/ipp"))
5750 {
5751 /*
5752 * Not an IPP request...
5753 */
5754
5755 return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
5756 }
5757
5758 /*
5759 * Read the IPP request...
5760 */
5761
5762 client->request = ippNew();
5763
5764 while ((ipp_state = ippRead(client->http,
5765 client->request)) != IPP_STATE_DATA)
5766 {
5767 if (ipp_state == IPP_STATE_ERROR)
5768 {
5769 fprintf(stderr, "%s IPP read error (%s).\n", client->hostname,
5770 cupsLastErrorString());
5771 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5772 return (0);
5773 }
5774 }
5775
5776 /*
5777 * Now that we have the IPP request, process the request...
5778 */
5779
5780 return (process_ipp(client));
5781
5782 default :
5783 break; /* Anti-compiler-warning-code */
5784 }
5785
5786 return (1);
5787 }
5788
5789
5790 /*
5791 * 'process_ipp()' - Process an IPP request.
5792 */
5793
5794 static int /* O - 1 on success, 0 on error */
5795 process_ipp(_ipp_client_t *client) /* I - Client */
5796 {
5797 ipp_tag_t group; /* Current group tag */
5798 ipp_attribute_t *attr; /* Current attribute */
5799 ipp_attribute_t *charset; /* Character set attribute */
5800 ipp_attribute_t *language; /* Language attribute */
5801 ipp_attribute_t *uri; /* Printer URI attribute */
5802 int major, minor; /* Version number */
5803 const char *name; /* Name of attribute */
5804
5805
5806 debug_attributes("Request", client->request, 1);
5807
5808 /*
5809 * First build an empty response message for this request...
5810 */
5811
5812 client->operation_id = ippGetOperation(client->request);
5813 client->response = ippNewResponse(client->request);
5814
5815 /*
5816 * Then validate the request header and required attributes...
5817 */
5818
5819 major = ippGetVersion(client->request, &minor);
5820
5821 if (major < 1 || major > 2)
5822 {
5823 /*
5824 * Return an error, since we only support IPP 1.x and 2.x.
5825 */
5826
5827 respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED,
5828 "Bad request version number %d.%d.", major, minor);
5829 }
5830 else if (ippGetRequestId(client->request) <= 0)
5831 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.",
5832 ippGetRequestId(client->request));
5833 else if (!ippFirstAttribute(client->request))
5834 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5835 "No attributes in request.");
5836 else
5837 {
5838 /*
5839 * Make sure that the attributes are provided in the correct order and
5840 * don't repeat groups...
5841 */
5842
5843 for (attr = ippFirstAttribute(client->request),
5844 group = ippGetGroupTag(attr);
5845 attr;
5846 attr = ippNextAttribute(client->request))
5847 {
5848 if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
5849 {
5850 /*
5851 * Out of order; return an error...
5852 */
5853
5854 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5855 "Attribute groups are out of order (%x < %x).",
5856 ippGetGroupTag(attr), group);
5857 break;
5858 }
5859 else
5860 group = ippGetGroupTag(attr);
5861 }
5862
5863 if (!attr)
5864 {
5865 /*
5866 * Then make sure that the first three attributes are:
5867 *
5868 * attributes-charset
5869 * attributes-natural-language
5870 * printer-uri/job-uri
5871 */
5872
5873 attr = ippFirstAttribute(client->request);
5874 name = ippGetName(attr);
5875 if (attr && name && !strcmp(name, "attributes-charset") &&
5876 ippGetValueTag(attr) == IPP_TAG_CHARSET)
5877 charset = attr;
5878 else
5879 charset = NULL;
5880
5881 attr = ippNextAttribute(client->request);
5882 name = ippGetName(attr);
5883
5884 if (attr && name && !strcmp(name, "attributes-natural-language") &&
5885 ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
5886 language = attr;
5887 else
5888 language = NULL;
5889
5890 if ((attr = ippFindAttribute(client->request, "printer-uri",
5891 IPP_TAG_URI)) != NULL)
5892 uri = attr;
5893 else if ((attr = ippFindAttribute(client->request, "job-uri",
5894 IPP_TAG_URI)) != NULL)
5895 uri = attr;
5896 else
5897 uri = NULL;
5898
5899 if (charset &&
5900 strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
5901 strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
5902 {
5903 /*
5904 * Bad character set...
5905 */
5906
5907 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5908 "Unsupported character set \"%s\".",
5909 ippGetString(charset, 0, NULL));
5910 }
5911 else if (!charset || !language || !uri)
5912 {
5913 /*
5914 * Return an error, since attributes-charset,
5915 * attributes-natural-language, and printer-uri/job-uri are required
5916 * for all operations.
5917 */
5918
5919 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5920 "Missing required attributes.");
5921 }
5922 else
5923 {
5924 char scheme[32], /* URI scheme */
5925 userpass[32], /* Username/password in URI */
5926 host[256], /* Host name in URI */
5927 resource[256]; /* Resource path in URI */
5928 int port; /* Port number in URI */
5929
5930 name = ippGetName(uri);
5931
5932 if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
5933 scheme, sizeof(scheme),
5934 userpass, sizeof(userpass),
5935 host, sizeof(host), &port,
5936 resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
5937 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
5938 "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
5939 else if ((!strcmp(name, "job-uri") &&
5940 strncmp(resource, "/ipp/print/", 11)) ||
5941 (!strcmp(name, "printer-uri") &&
5942 strcmp(resource, "/ipp/print")))
5943 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
5944 name, ippGetString(uri, 0, NULL));
5945 else
5946 {
5947 /*
5948 * Try processing the operation...
5949 */
5950
5951 switch (ippGetOperation(client->request))
5952 {
5953 case IPP_OP_PRINT_JOB :
5954 ipp_print_job(client);
5955 break;
5956
5957 case IPP_OP_PRINT_URI :
5958 ipp_print_uri(client);
5959 break;
5960
5961 case IPP_OP_VALIDATE_JOB :
5962 ipp_validate_job(client);
5963 break;
5964
5965 case IPP_OP_CREATE_JOB :
5966 ipp_create_job(client);
5967 break;
5968
5969 case IPP_OP_SEND_DOCUMENT :
5970 ipp_send_document(client);
5971 break;
5972
5973 case IPP_OP_SEND_URI :
5974 ipp_send_uri(client);
5975 break;
5976
5977 case IPP_OP_CANCEL_JOB :
5978 ipp_cancel_job(client);
5979 break;
5980
5981 case IPP_OP_GET_JOB_ATTRIBUTES :
5982 ipp_get_job_attributes(client);
5983 break;
5984
5985 case IPP_OP_GET_JOBS :
5986 ipp_get_jobs(client);
5987 break;
5988
5989 case IPP_OP_GET_PRINTER_ATTRIBUTES :
5990 ipp_get_printer_attributes(client);
5991 break;
5992
5993 case IPP_OP_CLOSE_JOB :
5994 ipp_close_job(client);
5995 break;
5996
5997 case IPP_OP_IDENTIFY_PRINTER :
5998 ipp_identify_printer(client);
5999 break;
6000
6001 default :
6002 respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
6003 "Operation not supported.");
6004 break;
6005 }
6006 }
6007 }
6008 }
6009 }
6010
6011 /*
6012 * Send the HTTP header and return...
6013 */
6014
6015 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
6016 httpFlush(client->http); /* Flush trailing (junk) data */
6017
6018 return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
6019 ippLength(client->response)));
6020 }
6021
6022
6023 /*
6024 * 'process_job()' - Process a print job.
6025 */
6026
6027 static void * /* O - Thread exit status */
6028 process_job(_ipp_job_t *job) /* I - Job */
6029 {
6030 job->state = IPP_JSTATE_PROCESSING;
6031 job->printer->state = IPP_PSTATE_PROCESSING;
6032 job->processing = time(NULL);
6033
6034 while (job->printer->state_reasons & _IPP_PREASON_MEDIA_EMPTY)
6035 {
6036 job->printer->state_reasons |= _IPP_PREASON_MEDIA_NEEDED;
6037
6038 sleep(1);
6039 }
6040
6041 job->printer->state_reasons &= (_ipp_preason_t)~_IPP_PREASON_MEDIA_NEEDED;
6042
6043 if (job->printer->command)
6044 {
6045 /*
6046 * Execute a command with the job spool file and wait for it to complete...
6047 */
6048
6049 int pid, /* Process ID */
6050 status; /* Exit status */
6051 time_t start, /* Start time */
6052 end; /* End time */
6053 char *myargv[3], /* Command-line arguments */
6054 *myenvp[200]; /* Environment variables */
6055 int myenvc; /* Number of environment variables */
6056 ipp_attribute_t *attr; /* Job attribute */
6057 char val[1280], /* IPP_NAME=value */
6058 *valptr; /* Pointer into string */
6059 #ifndef WIN32
6060 int mypipe[2]; /* Pipe for stderr */
6061 char line[2048], /* Line from stderr */
6062 *ptr, /* Pointer into line */
6063 *endptr; /* End of line */
6064 ssize_t bytes; /* Bytes read */
6065 #endif /* !WIN32 */
6066
6067 fprintf(stderr, "Running command \"%s %s\".\n", job->printer->command,
6068 job->filename);
6069 time(&start);
6070
6071 /*
6072 * Setup the command-line arguments...
6073 */
6074
6075 myargv[0] = job->printer->command;
6076 myargv[1] = job->filename;
6077 myargv[2] = NULL;
6078
6079 /*
6080 * Copy the current environment, then add ENV variables for every Job
6081 * attribute...
6082 */
6083
6084 for (myenvc = 0; environ[myenvc] && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); myenvc ++)
6085 myenvp[myenvc] = strdup(environ[myenvc]);
6086
6087 for (attr = ippFirstAttribute(job->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->attrs))
6088 {
6089 /*
6090 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6091 * value(s) from the attribute.
6092 */
6093
6094 const char *name = ippGetName(attr);
6095 if (!name)
6096 continue;
6097
6098 valptr = val;
6099 *valptr++ = 'I';
6100 *valptr++ = 'P';
6101 *valptr++ = 'P';
6102 *valptr++ = '_';
6103 while (*name && valptr < (val + sizeof(val) - 2))
6104 {
6105 if (*name == '-')
6106 *valptr++ = '_';
6107 else
6108 *valptr++ = (char)toupper(*name & 255);
6109
6110 name ++;
6111 }
6112 *valptr++ = '=';
6113 ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
6114
6115 myenvp[myenvc++] = strdup(val);
6116 }
6117 myenvp[myenvc] = NULL;
6118
6119 /*
6120 * Now run the program...
6121 */
6122
6123 #ifdef WIN32
6124 status = _spawnvpe(_P_WAIT, job->printer->command, myargv, myenvp);
6125
6126 #else
6127 if (pipe(mypipe))
6128 {
6129 perror("Unable to create pipe for stderr");
6130 mypipe[0] = mypipe[1] = -1;
6131 }
6132
6133 if ((pid = fork()) == 0)
6134 {
6135 /*
6136 * Child comes here...
6137 */
6138
6139 close(2);
6140 dup2(mypipe[1], 2);
6141 close(mypipe[0]);
6142 close(mypipe[1]);
6143
6144 execve(job->printer->command, myargv, myenvp);
6145 exit(errno);
6146 }
6147 else if (pid < 0)
6148 {
6149 /*
6150 * Unable to fork process...
6151 */
6152
6153 perror("Unable to start job processing command");
6154 status = -1;
6155
6156 close(mypipe[0]);
6157 close(mypipe[1]);
6158
6159 /*
6160 * Free memory used for environment...
6161 */
6162
6163 while (myenvc > 0)
6164 free(myenvp[-- myenvc]);
6165 }
6166 else
6167 {
6168 /*
6169 * Free memory used for environment...
6170 */
6171
6172 while (myenvc > 0)
6173 free(myenvp[-- myenvc]);
6174
6175 /*
6176 * If the pipe exists, read from it until EOF...
6177 */
6178
6179 if (mypipe[0] >= 0)
6180 {
6181 close(mypipe[1]);
6182
6183 endptr = line;
6184 while ((bytes = read(mypipe[0], endptr, sizeof(line) - (size_t)(endptr - line) - 1)) > 0)
6185 {
6186 endptr += bytes;
6187 *endptr = '\0';
6188
6189 while ((ptr = strchr(line, '\n')) != NULL)
6190 {
6191 *ptr++ = '\0';
6192
6193 if (!strncmp(line, "STATE:", 6))
6194 {
6195 /*
6196 * Process printer-state-reasons keywords.
6197 */
6198
6199 process_state_message(job, line);
6200 }
6201 else if (!strncmp(line, "ATTR:", 5))
6202 {
6203 /*
6204 * Process printer attribute update.
6205 */
6206
6207 process_attr_message(job, line);
6208 }
6209 else if (Verbosity > 1)
6210 fprintf(stderr, "%s: %s\n", job->printer->command, line);
6211
6212 bytes = ptr - line;
6213 if (ptr < endptr)
6214 memmove(line, ptr, (size_t)(endptr - ptr));
6215 endptr -= bytes;
6216 *endptr = '\0';
6217 }
6218 }
6219
6220 close(mypipe[0]);
6221 }
6222
6223 /*
6224 * Wait for child to complete...
6225 */
6226
6227 # ifdef HAVE_WAITPID
6228 while (waitpid(pid, &status, 0) < 0);
6229 # else
6230 while (wait(&status) < 0);
6231 # endif /* HAVE_WAITPID */
6232 }
6233 #endif /* WIN32 */
6234
6235 if (status)
6236 {
6237 #ifndef WIN32
6238 if (WIFEXITED(status))
6239 #endif /* !WIN32 */
6240 fprintf(stderr, "Command \"%s\" exited with status %d.\n",
6241 job->printer->command, WEXITSTATUS(status));
6242 #ifndef WIN32
6243 else
6244 fprintf(stderr, "Command \"%s\" terminated with signal %d.\n",
6245 job->printer->command, WTERMSIG(status));
6246 #endif /* !WIN32 */
6247 job->state = IPP_JSTATE_ABORTED;
6248 }
6249 else if (status < 0)
6250 job->state = IPP_JSTATE_ABORTED;
6251 else
6252 fprintf(stderr, "Command \"%s\" completed successfully.\n",
6253 job->printer->command);
6254
6255 /*
6256 * Make sure processing takes at least 5 seconds...
6257 */
6258
6259 time(&end);
6260 if ((end - start) < 5)
6261 sleep(5);
6262 }
6263 else
6264 {
6265 /*
6266 * Sleep for a random amount of time to simulate job processing.
6267 */
6268
6269 sleep((unsigned)(5 + (rand() % 11)));
6270 }
6271
6272 if (job->cancel)
6273 job->state = IPP_JSTATE_CANCELED;
6274 else if (job->state == IPP_JSTATE_PROCESSING)
6275 job->state = IPP_JSTATE_COMPLETED;
6276
6277 job->completed = time(NULL);
6278 job->printer->state = IPP_PSTATE_IDLE;
6279 job->printer->active_job = NULL;
6280
6281 return (NULL);
6282 }
6283
6284
6285 /*
6286 * 'process_state_message()' - Process a STATE: message from a command.
6287 */
6288
6289 static void
6290 process_state_message(
6291 _ipp_job_t *job, /* I - Job */
6292 char *message) /* I - Message */
6293 {
6294 int i; /* Looping var */
6295 _ipp_preason_t state_reasons, /* printer-state-reasons values */
6296 bit; /* Current reason bit */
6297 char *ptr, /* Pointer into message */
6298 *next; /* Next keyword in message */
6299 int remove; /* Non-zero if we are removing keywords */
6300
6301
6302 /*
6303 * Skip leading "STATE:" and any whitespace...
6304 */
6305
6306 for (message += 6; *message; message ++)
6307 if (*message != ' ' && *message != '\t')
6308 break;
6309
6310 /*
6311 * Support the following forms of message:
6312 *
6313 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6314 *
6315 * "-keyword[,keyword,...]" to remove keywords.
6316 *
6317 * "+keyword[,keyword,...]" to add keywords.
6318 *
6319 * Keywords may or may not have a suffix (-report, -warning, -error) per
6320 * RFC 2911.
6321 */
6322
6323 if (*message == '-')
6324 {
6325 remove = 1;
6326 state_reasons = job->printer->state_reasons;
6327 message ++;
6328 }
6329 else if (*message == '+')
6330 {
6331 remove = 0;
6332 state_reasons = job->printer->state_reasons;
6333 message ++;
6334 }
6335 else
6336 {
6337 remove = 0;
6338 state_reasons = _IPP_PREASON_NONE;
6339 }
6340
6341 while (*message)
6342 {
6343 if ((next = strchr(message, ',')) != NULL)
6344 *next++ = '\0';
6345
6346 if ((ptr = strstr(message, "-error")) != NULL)
6347 *ptr = '\0';
6348 else if ((ptr = strstr(message, "-report")) != NULL)
6349 *ptr = '\0';
6350 else if ((ptr = strstr(message, "-warning")) != NULL)
6351 *ptr = '\0';
6352
6353 for (i = 0, bit = 1; i < (int)(sizeof(_ipp_preason_strings) / sizeof(_ipp_preason_strings[0])); i ++, bit *= 2)
6354 {
6355 if (!strcmp(message, _ipp_preason_strings[i]))
6356 {
6357 if (remove)
6358 state_reasons &= ~bit;
6359 else
6360 state_reasons |= bit;
6361 }
6362 }
6363
6364 if (next)
6365 message = next;
6366 else
6367 break;
6368 }
6369
6370 job->printer->state_reasons = state_reasons;
6371 }
6372
6373
6374 /*
6375 * 'register_printer()' - Register a printer object via Bonjour.
6376 */
6377
6378 static int /* O - 1 on success, 0 on error */
6379 register_printer(
6380 _ipp_printer_t *printer, /* I - Printer */
6381 const char *location, /* I - Location */
6382 const char *make, /* I - Manufacturer */
6383 const char *model, /* I - Model name */
6384 const char *formats, /* I - Supported formats */
6385 const char *adminurl, /* I - Web interface URL */
6386 const char *uuid, /* I - Printer UUID */
6387 int color, /* I - 1 = color, 0 = monochrome */
6388 int duplex, /* I - 1 = duplex, 0 = simplex */
6389 const char *subtype) /* I - Service subtype */
6390 {
6391 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6392 _ipp_txt_t ipp_txt; /* Bonjour IPP TXT record */
6393 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6394 #ifdef HAVE_DNSSD
6395 DNSServiceErrorType error; /* Error from Bonjour */
6396 char make_model[256],/* Make and model together */
6397 product[256], /* Product string */
6398 regtype[256]; /* Bonjour service type */
6399
6400
6401 /*
6402 * Build the TXT record for IPP...
6403 */
6404
6405 snprintf(make_model, sizeof(make_model), "%s %s", make, model);
6406 snprintf(product, sizeof(product), "(%s)", model);
6407
6408 TXTRecordCreate(&ipp_txt, 1024, NULL);
6409 TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print");
6410 TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(make_model),
6411 make_model);
6412 TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(adminurl),
6413 adminurl);
6414 if (*location)
6415 TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(location),
6416 location);
6417 TXTRecordSetValue(&ipp_txt, "product", (uint8_t)strlen(product),
6418 product);
6419 TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats),
6420 formats);
6421 TXTRecordSetValue(&ipp_txt, "Color", 1, color ? "T" : "F");
6422 TXTRecordSetValue(&ipp_txt, "Duplex", 1, duplex ? "T" : "F");
6423 TXTRecordSetValue(&ipp_txt, "usb_MFG", (uint8_t)strlen(make),
6424 make);
6425 TXTRecordSetValue(&ipp_txt, "usb_MDL", (uint8_t)strlen(model),
6426 model);
6427 TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(uuid), uuid);
6428 # ifdef HAVE_SSL
6429 TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2");
6430 # endif /* HAVE_SSL */
6431 if (strstr(formats, "image/urf"))
6432 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");
6433
6434 TXTRecordSetValue(&ipp_txt, "txtvers", 1, "1");
6435 TXTRecordSetValue(&ipp_txt, "qtotal", 1, "1");
6436
6437 /*
6438 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6439 * defend our service name but not actually support LPD...
6440 */
6441
6442 printer->printer_ref = DNSSDMaster;
6443
6444 if ((error = DNSServiceRegister(&(printer->printer_ref),
6445 kDNSServiceFlagsShareConnection,
6446 0 /* interfaceIndex */, printer->dnssd_name,
6447 "_printer._tcp", NULL /* domain */,
6448 NULL /* host */, 0 /* port */, 0 /* txtLen */,
6449 NULL /* txtRecord */,
6450 (DNSServiceRegisterReply)dnssd_callback,
6451 printer)) != kDNSServiceErr_NoError)
6452 {
6453 fprintf(stderr, "Unable to register \"%s._printer._tcp\": %d\n",
6454 printer->dnssd_name, error);
6455 return (0);
6456 }
6457
6458 /*
6459 * Then register the _ipp._tcp (IPP) service type with the real port number to
6460 * advertise our IPP printer...
6461 */
6462
6463 printer->ipp_ref = DNSSDMaster;
6464
6465 if (subtype && *subtype)
6466 snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtype);
6467 else
6468 strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
6469
6470 if ((error = DNSServiceRegister(&(printer->ipp_ref),
6471 kDNSServiceFlagsShareConnection,
6472 0 /* interfaceIndex */, printer->dnssd_name,
6473 regtype, NULL /* domain */,
6474 NULL /* host */, htons(printer->port),
6475 TXTRecordGetLength(&ipp_txt),
6476 TXTRecordGetBytesPtr(&ipp_txt),
6477 (DNSServiceRegisterReply)dnssd_callback,
6478 printer)) != kDNSServiceErr_NoError)
6479 {
6480 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
6481 printer->dnssd_name, regtype, error);
6482 return (0);
6483 }
6484
6485 # ifdef HAVE_SSL
6486 /*
6487 * Then register the _ipps._tcp (IPP) service type with the real port number to
6488 * advertise our IPPS printer...
6489 */
6490
6491 printer->ipps_ref = DNSSDMaster;
6492
6493 if (subtype && *subtype)
6494 snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtype);
6495 else
6496 strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
6497
6498 if ((error = DNSServiceRegister(&(printer->ipps_ref),
6499 kDNSServiceFlagsShareConnection,
6500 0 /* interfaceIndex */, printer->dnssd_name,
6501 regtype, NULL /* domain */,
6502 NULL /* host */, htons(printer->port),
6503 TXTRecordGetLength(&ipp_txt),
6504 TXTRecordGetBytesPtr(&ipp_txt),
6505 (DNSServiceRegisterReply)dnssd_callback,
6506 printer)) != kDNSServiceErr_NoError)
6507 {
6508 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
6509 printer->dnssd_name, regtype, error);
6510 return (0);
6511 }
6512 # endif /* HAVE_SSL */
6513
6514 /*
6515 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6516 * real port number to advertise our IPP printer...
6517 */
6518
6519 printer->http_ref = DNSSDMaster;
6520
6521 if ((error = DNSServiceRegister(&(printer->http_ref),
6522 kDNSServiceFlagsShareConnection,
6523 0 /* interfaceIndex */, printer->dnssd_name,
6524 "_http._tcp,_printer", NULL /* domain */,
6525 NULL /* host */, htons(printer->port),
6526 0 /* txtLen */, NULL, /* txtRecord */
6527 (DNSServiceRegisterReply)dnssd_callback,
6528 printer)) != kDNSServiceErr_NoError)
6529 {
6530 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
6531 printer->dnssd_name, regtype, error);
6532 return (0);
6533 }
6534
6535 TXTRecordDeallocate(&ipp_txt);
6536
6537 #elif defined(HAVE_AVAHI)
6538 char temp[256]; /* Subtype service string */
6539
6540 /*
6541 * Create the TXT record...
6542 */
6543
6544 ipp_txt = NULL;
6545 ipp_txt = avahi_string_list_add_printf(ipp_txt, "rp=ipp/print");
6546 ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s %s", make, model);
6547 ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", adminurl);
6548 if (*location)
6549 ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", location);
6550 ipp_txt = avahi_string_list_add_printf(ipp_txt, "product=(%s)", model);
6551 ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
6552 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", color ? "T" : "F");
6553 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", duplex ? "T" : "F");
6554 ipp_txt = avahi_string_list_add_printf(ipp_txt, "usb_MFG=%s", make);
6555 ipp_txt = avahi_string_list_add_printf(ipp_txt, "usb_MDL=%s", model);
6556 ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", uuid);
6557 # ifdef HAVE_SSL
6558 ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2");
6559 # endif /* HAVE_SSL */
6560
6561 /*
6562 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6563 */
6564
6565 avahi_threaded_poll_lock(DNSSDMaster);
6566
6567 printer->ipp_ref = avahi_entry_group_new(DNSSDClient, dnssd_callback, NULL);
6568
6569 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);
6570
6571 /*
6572 * Then register the _ipp._tcp (IPP)...
6573 */
6574
6575 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);
6576 if (subtype && *subtype)
6577 {
6578 snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", subtype);
6579 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp);
6580 }
6581
6582 #ifdef HAVE_SSL
6583 /*
6584 * _ipps._tcp (IPPS) for secure printing...
6585 */
6586
6587 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);
6588 if (subtype && *subtype)
6589 {
6590 snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", subtype);
6591 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp);
6592 }
6593 #endif /* HAVE_SSL */
6594
6595 /*
6596 * Finally _http.tcp (HTTP) for the web interface...
6597 */
6598
6599 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);
6600 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");
6601
6602 /*
6603 * Commit it...
6604 */
6605
6606 avahi_entry_group_commit(printer->ipp_ref);
6607 avahi_threaded_poll_unlock(DNSSDMaster);
6608
6609 avahi_string_list_free(ipp_txt);
6610 #endif /* HAVE_DNSSD */
6611
6612 return (1);
6613 }
6614
6615
6616 /*
6617 * 'respond_http()' - Send a HTTP response.
6618 */
6619
6620 int /* O - 1 on success, 0 on failure */
6621 respond_http(
6622 _ipp_client_t *client, /* I - Client */
6623 http_status_t code, /* I - HTTP status of response */
6624 const char *content_encoding, /* I - Content-Encoding of response */
6625 const char *type, /* I - MIME media type of response */
6626 size_t length) /* I - Length of response */
6627 {
6628 char message[1024]; /* Text message */
6629
6630
6631 fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
6632
6633 if (code == HTTP_STATUS_CONTINUE)
6634 {
6635 /*
6636 * 100-continue doesn't send any headers...
6637 */
6638
6639 return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
6640 }
6641
6642 /*
6643 * Format an error message...
6644 */
6645
6646 if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
6647 {
6648 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
6649
6650 type = "text/plain";
6651 length = strlen(message);
6652 }
6653 else
6654 message[0] = '\0';
6655
6656 /*
6657 * Send the HTTP response header...
6658 */
6659
6660 httpClearFields(client->http);
6661
6662 if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
6663 client->operation == HTTP_STATE_OPTIONS)
6664 httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
6665
6666 if (type)
6667 {
6668 if (!strcmp(type, "text/html"))
6669 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
6670 "text/html; charset=utf-8");
6671 else
6672 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
6673
6674 if (content_encoding)
6675 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
6676 }
6677
6678 httpSetLength(client->http, length);
6679
6680 if (httpWriteResponse(client->http, code) < 0)
6681 return (0);
6682
6683 /*
6684 * Send the response data...
6685 */
6686
6687 if (message[0])
6688 {
6689 /*
6690 * Send a plain text message.
6691 */
6692
6693 if (httpPrintf(client->http, "%s", message) < 0)
6694 return (0);
6695
6696 if (httpWrite2(client->http, "", 0) < 0)
6697 return (0);
6698 }
6699 else if (client->response)
6700 {
6701 /*
6702 * Send an IPP response...
6703 */
6704
6705 debug_attributes("Response", client->response, 2);
6706
6707 ippSetState(client->response, IPP_STATE_IDLE);
6708
6709 if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
6710 return (0);
6711 }
6712
6713 return (1);
6714 }
6715
6716
6717 /*
6718 * 'respond_ipp()' - Send an IPP response.
6719 */
6720
6721 static void
6722 respond_ipp(_ipp_client_t *client, /* I - Client */
6723 ipp_status_t status, /* I - status-code */
6724 const char *message, /* I - printf-style status-message */
6725 ...) /* I - Additional args as needed */
6726 {
6727 const char *formatted = NULL; /* Formatted message */
6728
6729
6730 ippSetStatusCode(client->response, status);
6731
6732 if (message)
6733 {
6734 va_list ap; /* Pointer to additional args */
6735 ipp_attribute_t *attr; /* New status-message attribute */
6736
6737 va_start(ap, message);
6738 if ((attr = ippFindAttribute(client->response, "status-message",
6739 IPP_TAG_TEXT)) != NULL)
6740 ippSetStringfv(client->response, &attr, 0, message, ap);
6741 else
6742 attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
6743 "status-message", NULL, message, ap);
6744 va_end(ap);
6745
6746 formatted = ippGetString(attr, 0, NULL);
6747 }
6748
6749 if (formatted)
6750 fprintf(stderr, "%s %s %s (%s)\n", client->hostname,
6751 ippOpString(client->operation_id), ippErrorString(status),
6752 formatted);
6753 else
6754 fprintf(stderr, "%s %s %s\n", client->hostname,
6755 ippOpString(client->operation_id), ippErrorString(status));
6756 }
6757
6758
6759 /*
6760 * 'respond_unsupported()' - Respond with an unsupported attribute.
6761 */
6762
6763 static void
6764 respond_unsupported(
6765 _ipp_client_t *client, /* I - Client */
6766 ipp_attribute_t *attr) /* I - Atribute */
6767 {
6768 ipp_attribute_t *temp; /* Copy of attribute */
6769
6770
6771 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
6772 "Unsupported %s %s%s value.", ippGetName(attr),
6773 ippGetCount(attr) > 1 ? "1setOf " : "",
6774 ippTagString(ippGetValueTag(attr)));
6775
6776 temp = ippCopyAttribute(client->response, attr, 0);
6777 ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
6778 }
6779
6780
6781 /*
6782 * 'run_printer()' - Run the printer service.
6783 */
6784
6785 static void
6786 run_printer(_ipp_printer_t *printer) /* I - Printer */
6787 {
6788 int num_fds; /* Number of file descriptors */
6789 struct pollfd polldata[3]; /* poll() data */
6790 int timeout; /* Timeout for poll() */
6791 _ipp_client_t *client; /* New client */
6792
6793
6794 /*
6795 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6796 */
6797
6798 polldata[0].fd = printer->ipv4;
6799 polldata[0].events = POLLIN;
6800
6801 polldata[1].fd = printer->ipv6;
6802 polldata[1].events = POLLIN;
6803
6804 num_fds = 2;
6805
6806 #ifdef HAVE_DNSSD
6807 polldata[num_fds ].fd = DNSServiceRefSockFD(DNSSDMaster);
6808 polldata[num_fds ++].events = POLLIN;
6809 #endif /* HAVE_DNSSD */
6810
6811 /*
6812 * Loop until we are killed or have a hard error...
6813 */
6814
6815 for (;;)
6816 {
6817 if (cupsArrayCount(printer->jobs))
6818 timeout = 10;
6819 else
6820 timeout = -1;
6821
6822 if (poll(polldata, (nfds_t)num_fds, timeout) < 0 && errno != EINTR)
6823 {
6824 perror("poll() failed");
6825 break;
6826 }
6827
6828 if (polldata[0].revents & POLLIN)
6829 {
6830 if ((client = create_client(printer, printer->ipv4)) != NULL)
6831 {
6832 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
6833 {
6834 perror("Unable to create client thread");
6835 delete_client(client);
6836 }
6837 }
6838 }
6839
6840 if (polldata[1].revents & POLLIN)
6841 {
6842 if ((client = create_client(printer, printer->ipv6)) != NULL)
6843 {
6844 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
6845 {
6846 perror("Unable to create client thread");
6847 delete_client(client);
6848 }
6849 }
6850 }
6851
6852 #ifdef HAVE_DNSSD
6853 if (polldata[2].revents & POLLIN)
6854 DNSServiceProcessResult(DNSSDMaster);
6855 #endif /* HAVE_DNSSD */
6856
6857 /*
6858 * Clean out old jobs...
6859 */
6860
6861 clean_jobs(printer);
6862 }
6863 }
6864
6865
6866 /*
6867 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6868 */
6869
6870 static char *
6871 time_string(time_t tv, /* I - Time value */
6872 char *buffer, /* I - Buffer */
6873 size_t bufsize) /* I - Size of buffer */
6874 {
6875 struct tm *curtime = localtime(&tv);
6876 /* Local time */
6877
6878 strftime(buffer, bufsize, "%X", curtime);
6879 return (buffer);
6880 }
6881
6882
6883 /*
6884 * 'usage()' - Show program usage.
6885 */
6886
6887 static void
6888 usage(int status) /* O - Exit status */
6889 {
6890 if (!status)
6891 {
6892 puts(CUPS_SVERSION " - Copyright 2010-2015 by Apple Inc. All rights "
6893 "reserved.");
6894 puts("");
6895 }
6896
6897 puts("Usage: ippserver [options] \"name\"");
6898 puts("");
6899 puts("Options:");
6900 puts("-2 Supports 2-sided printing (default=1-sided)");
6901 puts("-M manufacturer Manufacturer name (default=Test)");
6902 puts("-P PIN printing mode");
6903 puts("-a attributes-file Load printer attributes from file");
6904 puts("-c command Run command for every print job");
6905 printf("-d spool-directory Spool directory "
6906 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6907 puts("-f type/subtype[,...] List of supported types "
6908 "(default=application/pdf,image/jpeg)");
6909 puts("-h Show program help");
6910 puts("-i iconfile.png PNG icon file (default=printer.png)");
6911 puts("-k Keep job spool files");
6912 puts("-l location Location of printer (default=empty string)");
6913 puts("-m model Model name (default=Printer)");
6914 puts("-n hostname Hostname for printer");
6915 puts("-p port Port number (default=auto)");
6916 puts("-r subtype Bonjour service subtype (default=_print)");
6917 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6918 puts("-v[vvv] Be (very) verbose");
6919
6920 exit(status);
6921 }
6922
6923
6924 /*
6925 * 'valid_doc_attributes()' - Determine whether the document attributes are
6926 * valid.
6927 *
6928 * When one or more document attributes are invalid, this function adds a
6929 * suitable response and attributes to the unsupported group.
6930 */
6931
6932 static int /* O - 1 if valid, 0 if not */
6933 valid_doc_attributes(
6934 _ipp_client_t *client) /* I - Client */
6935 {
6936 int valid = 1; /* Valid attributes? */
6937 ipp_op_t op = ippGetOperation(client->request);
6938 /* IPP operation */
6939 const char *op_name = ippOpString(op);
6940 /* IPP operation name */
6941 ipp_attribute_t *attr, /* Current attribute */
6942 *supported; /* xxx-supported attribute */
6943 const char *compression = NULL,
6944 /* compression value */
6945 *format = NULL; /* document-format value */
6946
6947
6948 /*
6949 * Check operation attributes...
6950 */
6951
6952 if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
6953 {
6954 /*
6955 * If compression is specified, only accept a supported value in a Print-Job
6956 * or Send-Document request...
6957 */
6958
6959 compression = ippGetString(attr, 0, NULL);
6960 supported = ippFindAttribute(client->printer->attrs,
6961 "compression-supported", IPP_TAG_KEYWORD);
6962
6963 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
6964 ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
6965 (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
6966 op != IPP_OP_VALIDATE_JOB) ||
6967 !ippContainsString(supported, compression))
6968 {
6969 respond_unsupported(client, attr);
6970 valid = 0;
6971 }
6972 else
6973 {
6974 fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression);
6975
6976 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression);
6977
6978 if (strcmp(compression, "none"))
6979 {
6980 if (Verbosity)
6981 fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression);
6982 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
6983 }
6984 }
6985 }
6986
6987 /*
6988 * Is it a format we support?
6989 */
6990
6991 if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
6992 {
6993 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
6994 ippGetGroupTag(attr) != IPP_TAG_OPERATION)
6995 {
6996 respond_unsupported(client, attr);
6997 valid = 0;
6998 }
6999 else
7000 {
7001 format = ippGetString(attr, 0, NULL);
7002
7003 fprintf(stderr, "%s %s document-format=\"%s\"\n",
7004 client->hostname, op_name, format);
7005
7006 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format);
7007 }
7008 }
7009 else
7010 {
7011 format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL);
7012 if (!format)
7013 format = "application/octet-stream"; /* Should never happen */
7014
7015 attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
7016 }
7017
7018 if (!strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
7019 {
7020 /*
7021 * Auto-type the file using the first 8 bytes of the file...
7022 */
7023
7024 unsigned char header[8]; /* First 8 bytes of file */
7025
7026 memset(header, 0, sizeof(header));
7027 httpPeek(client->http, (char *)header, sizeof(header));
7028
7029 if (!memcmp(header, "%PDF", 4))
7030 format = "application/pdf";
7031 else if (!memcmp(header, "%!", 2))
7032 format = "application/postscript";
7033 else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
7034 format = "image/jpeg";
7035 else if (!memcmp(header, "\211PNG", 4))
7036 format = "image/png";
7037 else if (!memcmp(header, "RAS2", 4))
7038 format = "image/pwg-raster";
7039 else if (!memcmp(header, "UNIRAST", 8))
7040 format = "image/urf";
7041 else
7042 format = NULL;
7043
7044 if (format)
7045 {
7046 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n",
7047 client->hostname, op_name, format);
7048
7049 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
7050 }
7051 }
7052
7053 if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
7054 {
7055 respond_unsupported(client, attr);
7056 valid = 0;
7057 }
7058
7059 /*
7060 * document-name
7061 */
7062
7063 if ((attr = ippFindAttribute(client->request, "document-name", IPP_TAG_NAME)) != NULL)
7064 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
7065
7066 return (valid);
7067 }
7068
7069
7070 /*
7071 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
7072 *
7073 * When one or more job attributes are invalid, this function adds a suitable
7074 * response and attributes to the unsupported group.
7075 */
7076
7077 static int /* O - 1 if valid, 0 if not */
7078 valid_job_attributes(
7079 _ipp_client_t *client) /* I - Client */
7080 {
7081 int i, /* Looping var */
7082 count, /* Number of values */
7083 valid = 1; /* Valid attributes? */
7084 ipp_attribute_t *attr, /* Current attribute */
7085 *supported; /* xxx-supported attribute */
7086
7087
7088 /*
7089 * Check operation attributes...
7090 */
7091
7092 valid = valid_doc_attributes(client);
7093
7094 /*
7095 * Check the various job template attributes...
7096 */
7097
7098 if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL)
7099 {
7100 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
7101 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
7102 {
7103 respond_unsupported(client, attr);
7104 valid = 0;
7105 }
7106 }
7107
7108 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
7109 {
7110 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
7111 {
7112 respond_unsupported(client, attr);
7113 valid = 0;
7114 }
7115 }
7116
7117 if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
7118 {
7119 if (ippGetCount(attr) != 1 ||
7120 (ippGetValueTag(attr) != IPP_TAG_NAME &&
7121 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
7122 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
7123 strcmp(ippGetString(attr, 0, NULL), "no-hold"))
7124 {
7125 respond_unsupported(client, attr);
7126 valid = 0;
7127 }
7128 }
7129
7130 if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL)
7131 {
7132 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0)
7133 {
7134 respond_unsupported(client, attr);
7135 valid = 0;
7136 }
7137 }
7138
7139 if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL)
7140 {
7141 if (ippGetCount(attr) != 1 ||
7142 (ippGetValueTag(attr) != IPP_TAG_NAME &&
7143 ippGetValueTag(attr) != IPP_TAG_NAMELANG))
7144 {
7145 respond_unsupported(client, attr);
7146 valid = 0;
7147 }
7148
7149 ippSetGroupTag(client->request, &attr, IPP_TAG_JOB);
7150 }
7151 else
7152 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
7153
7154 if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL)
7155 {
7156 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
7157 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
7158 {
7159 respond_unsupported(client, attr);
7160 valid = 0;
7161 }
7162 }
7163
7164 if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
7165 {
7166 if (ippGetCount(attr) != 1 ||
7167 (ippGetValueTag(attr) != IPP_TAG_NAME &&
7168 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
7169 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
7170 strcmp(ippGetString(attr, 0, NULL), "none"))
7171 {
7172 respond_unsupported(client, attr);
7173 valid = 0;
7174 }
7175 }
7176
7177 if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
7178 {
7179 if (ippGetCount(attr) != 1 ||
7180 (ippGetValueTag(attr) != IPP_TAG_NAME &&
7181 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
7182 ippGetValueTag(attr) != IPP_TAG_KEYWORD))
7183 {
7184 respond_unsupported(client, attr);
7185 valid = 0;
7186 }
7187 else
7188 {
7189 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
7190
7191 if (!ippContainsString(supported, ippGetString(attr, 0, NULL)))
7192 {
7193 respond_unsupported(client, attr);
7194 valid = 0;
7195 }
7196 }
7197 }
7198
7199 if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL)
7200 {
7201 ipp_t *col, /* media-col collection */
7202 *size; /* media-size collection */
7203 ipp_attribute_t *member, /* Member attribute */
7204 *x_dim, /* x-dimension */
7205 *y_dim; /* y-dimension */
7206 int x_value, /* y-dimension value */
7207 y_value; /* x-dimension value */
7208
7209 if (ippGetCount(attr) != 1 ||
7210 ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
7211 {
7212 respond_unsupported(client, attr);
7213 valid = 0;
7214 }
7215
7216 col = ippGetCollection(attr, 0);
7217
7218 if ((member = ippFindAttribute(col, "media-size-name", IPP_TAG_ZERO)) != NULL)
7219 {
7220 if (ippGetCount(member) != 1 ||
7221 (ippGetValueTag(member) != IPP_TAG_NAME &&
7222 ippGetValueTag(member) != IPP_TAG_NAMELANG &&
7223 ippGetValueTag(member) != IPP_TAG_KEYWORD))
7224 {
7225 respond_unsupported(client, attr);
7226 valid = 0;
7227 }
7228 else
7229 {
7230 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
7231
7232 if (!ippContainsString(supported, ippGetString(member, 0, NULL)))
7233 {
7234 respond_unsupported(client, attr);
7235 valid = 0;
7236 }
7237 }
7238 }
7239 else if ((member = ippFindAttribute(col, "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
7240 {
7241 if (ippGetCount(member) != 1)
7242 {
7243 respond_unsupported(client, attr);
7244 valid = 0;
7245 }
7246 else
7247 {
7248 size = ippGetCollection(member, 0);
7249
7250 if ((x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(x_dim) != 1 ||
7251 (y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(y_dim) != 1)
7252 {
7253 respond_unsupported(client, attr);
7254 valid = 0;
7255 }
7256 else
7257 {
7258 x_value = ippGetInteger(x_dim, 0);
7259 y_value = ippGetInteger(y_dim, 0);
7260 supported = ippFindAttribute(client->printer->attrs, "media-size-supported", IPP_TAG_BEGIN_COLLECTION);
7261 count = ippGetCount(supported);
7262
7263 for (i = 0; i < count ; i ++)
7264 {
7265 size = ippGetCollection(supported, i);
7266 x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_ZERO);
7267 y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_ZERO);
7268
7269 if (ippContainsInteger(x_dim, x_value) && ippContainsInteger(y_dim, y_value))
7270 break;
7271 }
7272
7273 if (i >= count)
7274 {
7275 respond_unsupported(client, attr);
7276 valid = 0;
7277 }
7278 }
7279 }
7280 }
7281 }
7282
7283 if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL)
7284 {
7285 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
7286 (strcmp(ippGetString(attr, 0, NULL),
7287 "separate-documents-uncollated-copies") &&
7288 strcmp(ippGetString(attr, 0, NULL),
7289 "separate-documents-collated-copies")))
7290 {
7291 respond_unsupported(client, attr);
7292 valid = 0;
7293 }
7294 }
7295
7296 if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL)
7297 {
7298 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
7299 ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
7300 ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
7301 {
7302 respond_unsupported(client, attr);
7303 valid = 0;
7304 }
7305 }
7306
7307 if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
7308 {
7309 if (ippGetValueTag(attr) != IPP_TAG_RANGE)
7310 {
7311 respond_unsupported(client, attr);
7312 valid = 0;
7313 }
7314 }
7315
7316 if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL)
7317 {
7318 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
7319 ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
7320 ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
7321 {
7322 respond_unsupported(client, attr);
7323 valid = 0;
7324 }
7325 }
7326
7327 if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
7328 {
7329 supported = ippFindAttribute(client->printer->attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION);
7330
7331 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION ||
7332 !supported)
7333 {
7334 respond_unsupported(client, attr);
7335 valid = 0;
7336 }
7337 else
7338 {
7339 int xdpi, /* Horizontal resolution for job template attribute */
7340 ydpi, /* Vertical resolution for job template attribute */
7341 sydpi; /* Vertical resolution for supported value */
7342 ipp_res_t units, /* Units for job template attribute */
7343 sunits; /* Units for supported value */
7344
7345 xdpi = ippGetResolution(attr, 0, &ydpi, &units);
7346 count = ippGetCount(supported);
7347
7348 for (i = 0; i < count; i ++)
7349 {
7350 if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits)
7351 break;
7352 }
7353
7354 if (i >= count)
7355 {
7356 respond_unsupported(client, attr);
7357 valid = 0;
7358 }
7359 }
7360 }
7361
7362 if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
7363 {
7364 const char *sides = ippGetString(attr, 0, NULL);
7365 /* "sides" value... */
7366
7367 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
7368 {
7369 respond_unsupported(client, attr);
7370 valid = 0;
7371 }
7372 else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
7373 {
7374 if (!ippContainsString(supported, sides))
7375 {
7376 respond_unsupported(client, attr);
7377 valid = 0;
7378 }
7379 }
7380 else if (strcmp(sides, "one-sided"))
7381 {
7382 respond_unsupported(client, attr);
7383 valid = 0;
7384 }
7385 }
7386
7387 return (valid);
7388 }