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