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