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