]> git.ipfire.org Git - thirdparty/cups.git/blob - test/ippserver.c
Fix builds with VC++ 2008
[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 #ifndef WIN32 /* Avoid MS compiler bug */
2421 (void)dst;
2422 #endif /* !WIN32 */
2423
2424 ipp_tag_t group = ippGetGroupTag(attr);
2425 const char *name = ippGetName(attr);
2426
2427 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)))
2428 return (0);
2429
2430 return (!filter->ra || cupsArrayFind(filter->ra, (void *)name) != NULL);
2431 }
2432
2433
2434 /*
2435 * 'find_job()' - Find a job specified in a request.
2436 */
2437
2438 static _ipp_job_t * /* O - Job or NULL */
2439 find_job(_ipp_client_t *client) /* I - Client */
2440 {
2441 ipp_attribute_t *attr; /* job-id or job-uri attribute */
2442 _ipp_job_t key, /* Job search key */
2443 *job; /* Matching job, if any */
2444
2445
2446 if ((attr = ippFindAttribute(client->request, "job-uri", IPP_TAG_URI)) != NULL)
2447 {
2448 const char *uri = ippGetString(attr, 0, NULL);
2449
2450 if (!strncmp(uri, client->printer->uri, client->printer->urilen) &&
2451 uri[client->printer->urilen] == '/')
2452 key.id = atoi(uri + client->printer->urilen + 1);
2453 else
2454 return (NULL);
2455 }
2456 else if ((attr = ippFindAttribute(client->request, "job-id", IPP_TAG_INTEGER)) != NULL)
2457 key.id = ippGetInteger(attr, 0);
2458
2459 _cupsRWLockRead(&(client->printer->rwlock));
2460 job = (_ipp_job_t *)cupsArrayFind(client->printer->jobs, &key);
2461 _cupsRWUnlock(&(client->printer->rwlock));
2462
2463 return (job);
2464 }
2465
2466
2467 /*
2468 * 'html_escape()' - Write a HTML-safe string.
2469 */
2470
2471 static void
2472 html_escape(_ipp_client_t *client, /* I - Client */
2473 const char *s, /* I - String to write */
2474 size_t slen) /* I - Number of characters to write */
2475 {
2476 const char *start, /* Start of segment */
2477 *end; /* End of string */
2478
2479
2480 start = s;
2481 end = s + (slen > 0 ? slen : strlen(s));
2482
2483 while (*s && s < end)
2484 {
2485 if (*s == '&' || *s == '<')
2486 {
2487 if (s > start)
2488 httpWrite2(client->http, start, (size_t)(s - start));
2489
2490 if (*s == '&')
2491 httpWrite2(client->http, "&amp;", 5);
2492 else
2493 httpWrite2(client->http, "&lt;", 4);
2494
2495 start = s + 1;
2496 }
2497
2498 s ++;
2499 }
2500
2501 if (s > start)
2502 httpWrite2(client->http, start, (size_t)(s - start));
2503 }
2504
2505
2506 /*
2507 * 'html_footer()' - Show the web interface footer.
2508 *
2509 * This function also writes the trailing 0-length chunk.
2510 */
2511
2512 static void
2513 html_footer(_ipp_client_t *client) /* I - Client */
2514 {
2515 html_printf(client,
2516 "</div>\n"
2517 "</body>\n"
2518 "</html>\n");
2519 httpWrite2(client->http, "", 0);
2520 }
2521
2522
2523 /*
2524 * 'html_header()' - Show the web interface header and title.
2525 */
2526
2527 static void
2528 html_header(_ipp_client_t *client, /* I - Client */
2529 const char *title) /* I - Title */
2530 {
2531 html_printf(client,
2532 "<!doctype html>\n"
2533 "<html>\n"
2534 "<head>\n"
2535 "<title>%s</title>\n"
2536 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2537 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2538 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2539 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2540 "<style>\n"
2541 "body { font-family: sans-serif; margin: 0; }\n"
2542 "div.body { padding: 0px 10px 10px; }\n"
2543 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2544 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2545 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2546 "table.form th { text-align: right; }\n"
2547 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2548 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2549 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2550 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2551 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2552 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2553 "table.nav td { margin: 0; text-align: center; }\n"
2554 "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"
2555 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2556 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2557 "td.nav:hover { background: #666; color: #fff; }\n"
2558 "td.nav:active { background: #000; color: #ff0; }\n"
2559 "</style>\n"
2560 "</head>\n"
2561 "<body>\n"
2562 "<table class=\"nav\"><tr>"
2563 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2564 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2565 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2566 "</tr></table>\n"
2567 "<div class=\"body\">\n", title, !strcmp(client->uri, "/") ? " sel" : "", !strcmp(client->uri, "/supplies") ? " sel" : "", !strcmp(client->uri, "/media") ? " sel" : "");
2568 }
2569
2570
2571 /*
2572 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2573 */
2574
2575 static void
2576 html_printf(_ipp_client_t *client, /* I - Client */
2577 const char *format, /* I - Printf-style format string */
2578 ...) /* I - Additional arguments as needed */
2579 {
2580 va_list ap; /* Pointer to arguments */
2581 const char *start; /* Start of string */
2582 char size, /* Size character (h, l, L) */
2583 type; /* Format type character */
2584 int width, /* Width of field */
2585 prec; /* Number of characters of precision */
2586 char tformat[100], /* Temporary format string for sprintf() */
2587 *tptr, /* Pointer into temporary format */
2588 temp[1024]; /* Buffer for formatted numbers */
2589 char *s; /* Pointer to string */
2590
2591
2592 /*
2593 * Loop through the format string, formatting as needed...
2594 */
2595
2596 va_start(ap, format);
2597 start = format;
2598
2599 while (*format)
2600 {
2601 if (*format == '%')
2602 {
2603 if (format > start)
2604 httpWrite2(client->http, start, (size_t)(format - start));
2605
2606 tptr = tformat;
2607 *tptr++ = *format++;
2608
2609 if (*format == '%')
2610 {
2611 httpWrite2(client->http, "%", 1);
2612 format ++;
2613 start = format;
2614 continue;
2615 }
2616 else if (strchr(" -+#\'", *format))
2617 *tptr++ = *format++;
2618
2619 if (*format == '*')
2620 {
2621 /*
2622 * Get width from argument...
2623 */
2624
2625 format ++;
2626 width = va_arg(ap, int);
2627
2628 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
2629 tptr += strlen(tptr);
2630 }
2631 else
2632 {
2633 width = 0;
2634
2635 while (isdigit(*format & 255))
2636 {
2637 if (tptr < (tformat + sizeof(tformat) - 1))
2638 *tptr++ = *format;
2639
2640 width = width * 10 + *format++ - '0';
2641 }
2642 }
2643
2644 if (*format == '.')
2645 {
2646 if (tptr < (tformat + sizeof(tformat) - 1))
2647 *tptr++ = *format;
2648
2649 format ++;
2650
2651 if (*format == '*')
2652 {
2653 /*
2654 * Get precision from argument...
2655 */
2656
2657 format ++;
2658 prec = va_arg(ap, int);
2659
2660 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
2661 tptr += strlen(tptr);
2662 }
2663 else
2664 {
2665 prec = 0;
2666
2667 while (isdigit(*format & 255))
2668 {
2669 if (tptr < (tformat + sizeof(tformat) - 1))
2670 *tptr++ = *format;
2671
2672 prec = prec * 10 + *format++ - '0';
2673 }
2674 }
2675 }
2676
2677 if (*format == 'l' && format[1] == 'l')
2678 {
2679 size = 'L';
2680
2681 if (tptr < (tformat + sizeof(tformat) - 2))
2682 {
2683 *tptr++ = 'l';
2684 *tptr++ = 'l';
2685 }
2686
2687 format += 2;
2688 }
2689 else if (*format == 'h' || *format == 'l' || *format == 'L')
2690 {
2691 if (tptr < (tformat + sizeof(tformat) - 1))
2692 *tptr++ = *format;
2693
2694 size = *format++;
2695 }
2696 else
2697 size = 0;
2698
2699
2700 if (!*format)
2701 {
2702 start = format;
2703 break;
2704 }
2705
2706 if (tptr < (tformat + sizeof(tformat) - 1))
2707 *tptr++ = *format;
2708
2709 type = *format++;
2710 *tptr = '\0';
2711 start = format;
2712
2713 switch (type)
2714 {
2715 case 'E' : /* Floating point formats */
2716 case 'G' :
2717 case 'e' :
2718 case 'f' :
2719 case 'g' :
2720 if ((size_t)(width + 2) > sizeof(temp))
2721 break;
2722
2723 sprintf(temp, tformat, va_arg(ap, double));
2724
2725 httpWrite2(client->http, temp, strlen(temp));
2726 break;
2727
2728 case 'B' : /* Integer formats */
2729 case 'X' :
2730 case 'b' :
2731 case 'd' :
2732 case 'i' :
2733 case 'o' :
2734 case 'u' :
2735 case 'x' :
2736 if ((size_t)(width + 2) > sizeof(temp))
2737 break;
2738
2739 # ifdef HAVE_LONG_LONG
2740 if (size == 'L')
2741 sprintf(temp, tformat, va_arg(ap, long long));
2742 else
2743 # endif /* HAVE_LONG_LONG */
2744 if (size == 'l')
2745 sprintf(temp, tformat, va_arg(ap, long));
2746 else
2747 sprintf(temp, tformat, va_arg(ap, int));
2748
2749 httpWrite2(client->http, temp, strlen(temp));
2750 break;
2751
2752 case 'p' : /* Pointer value */
2753 if ((size_t)(width + 2) > sizeof(temp))
2754 break;
2755
2756 sprintf(temp, tformat, va_arg(ap, void *));
2757
2758 httpWrite2(client->http, temp, strlen(temp));
2759 break;
2760
2761 case 'c' : /* Character or character array */
2762 if (width <= 1)
2763 {
2764 temp[0] = (char)va_arg(ap, int);
2765 temp[1] = '\0';
2766 html_escape(client, temp, 1);
2767 }
2768 else
2769 html_escape(client, va_arg(ap, char *), (size_t)width);
2770 break;
2771
2772 case 's' : /* String */
2773 if ((s = va_arg(ap, char *)) == NULL)
2774 s = "(null)";
2775
2776 html_escape(client, s, strlen(s));
2777 break;
2778 }
2779 }
2780 else
2781 format ++;
2782 }
2783
2784 if (format > start)
2785 httpWrite2(client->http, start, (size_t)(format - start));
2786
2787 va_end(ap);
2788 }
2789
2790
2791 /*
2792 * 'ipp_cancel_job()' - Cancel a job.
2793 */
2794
2795 static void
2796 ipp_cancel_job(_ipp_client_t *client) /* I - Client */
2797 {
2798 _ipp_job_t *job; /* Job information */
2799
2800
2801 /*
2802 * Get the job...
2803 */
2804
2805 if ((job = find_job(client)) == NULL)
2806 {
2807 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
2808 return;
2809 }
2810
2811 /*
2812 * See if the job is already completed, canceled, or aborted; if so,
2813 * we can't cancel...
2814 */
2815
2816 switch (job->state)
2817 {
2818 case IPP_JSTATE_CANCELED :
2819 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2820 "Job #%d is already canceled - can\'t cancel.", job->id);
2821 break;
2822
2823 case IPP_JSTATE_ABORTED :
2824 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2825 "Job #%d is already aborted - can\'t cancel.", job->id);
2826 break;
2827
2828 case IPP_JSTATE_COMPLETED :
2829 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2830 "Job #%d is already completed - can\'t cancel.", job->id);
2831 break;
2832
2833 default :
2834 /*
2835 * Cancel the job...
2836 */
2837
2838 _cupsRWLockWrite(&(client->printer->rwlock));
2839
2840 if (job->state == IPP_JSTATE_PROCESSING ||
2841 (job->state == IPP_JSTATE_HELD && job->fd >= 0))
2842 job->cancel = 1;
2843 else
2844 {
2845 job->state = IPP_JSTATE_CANCELED;
2846 job->completed = time(NULL);
2847 }
2848
2849 _cupsRWUnlock(&(client->printer->rwlock));
2850
2851 respond_ipp(client, IPP_STATUS_OK, NULL);
2852 break;
2853 }
2854 }
2855
2856
2857 /*
2858 * 'ipp_close_job()' - Close an open job.
2859 */
2860
2861 static void
2862 ipp_close_job(_ipp_client_t *client) /* I - Client */
2863 {
2864 _ipp_job_t *job; /* Job information */
2865
2866
2867 /*
2868 * Get the job...
2869 */
2870
2871 if ((job = find_job(client)) == NULL)
2872 {
2873 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
2874 return;
2875 }
2876
2877 /*
2878 * See if the job is already completed, canceled, or aborted; if so,
2879 * we can't cancel...
2880 */
2881
2882 switch (job->state)
2883 {
2884 case IPP_JSTATE_CANCELED :
2885 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2886 "Job #%d is canceled - can\'t close.", job->id);
2887 break;
2888
2889 case IPP_JSTATE_ABORTED :
2890 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2891 "Job #%d is aborted - can\'t close.", job->id);
2892 break;
2893
2894 case IPP_JSTATE_COMPLETED :
2895 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2896 "Job #%d is completed - can\'t close.", job->id);
2897 break;
2898
2899 case IPP_JSTATE_PROCESSING :
2900 case IPP_JSTATE_STOPPED :
2901 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2902 "Job #%d is already closed.", job->id);
2903 break;
2904
2905 default :
2906 respond_ipp(client, IPP_STATUS_OK, NULL);
2907 break;
2908 }
2909 }
2910
2911
2912 /*
2913 * 'ipp_create_job()' - Create a job object.
2914 */
2915
2916 static void
2917 ipp_create_job(_ipp_client_t *client) /* I - Client */
2918 {
2919 _ipp_job_t *job; /* New job */
2920 cups_array_t *ra; /* Attributes to send in response */
2921
2922
2923 /*
2924 * Validate print job attributes...
2925 */
2926
2927 if (!valid_job_attributes(client))
2928 {
2929 httpFlush(client->http);
2930 return;
2931 }
2932
2933 /*
2934 * Do we have a file to print?
2935 */
2936
2937 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
2938 {
2939 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
2940 "Unexpected document data following request.");
2941 return;
2942 }
2943
2944 /*
2945 * Create the job...
2946 */
2947
2948 if ((job = create_job(client)) == NULL)
2949 {
2950 respond_ipp(client, IPP_STATUS_ERROR_BUSY,
2951 "Currently printing another job.");
2952 return;
2953 }
2954
2955 /*
2956 * Return the job info...
2957 */
2958
2959 respond_ipp(client, IPP_STATUS_OK, NULL);
2960
2961 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2962 cupsArrayAdd(ra, "job-id");
2963 cupsArrayAdd(ra, "job-state");
2964 cupsArrayAdd(ra, "job-state-message");
2965 cupsArrayAdd(ra, "job-state-reasons");
2966 cupsArrayAdd(ra, "job-uri");
2967
2968 copy_job_attributes(client, job, ra);
2969 cupsArrayDelete(ra);
2970 }
2971
2972
2973 /*
2974 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2975 */
2976
2977 static void
2978 ipp_get_job_attributes(
2979 _ipp_client_t *client) /* I - Client */
2980 {
2981 _ipp_job_t *job; /* Job */
2982 cups_array_t *ra; /* requested-attributes */
2983
2984
2985 if ((job = find_job(client)) == NULL)
2986 {
2987 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job not found.");
2988 return;
2989 }
2990
2991 respond_ipp(client, IPP_STATUS_OK, NULL);
2992
2993 ra = ippCreateRequestedArray(client->request);
2994 copy_job_attributes(client, job, ra);
2995 cupsArrayDelete(ra);
2996 }
2997
2998
2999 /*
3000 * 'ipp_get_jobs()' - Get a list of job objects.
3001 */
3002
3003 static void
3004 ipp_get_jobs(_ipp_client_t *client) /* I - Client */
3005 {
3006 ipp_attribute_t *attr; /* Current attribute */
3007 const char *which_jobs = NULL;
3008 /* which-jobs values */
3009 int job_comparison; /* Job comparison */
3010 ipp_jstate_t job_state; /* job-state value */
3011 int first_job_id, /* First job ID */
3012 limit, /* Maximum number of jobs to return */
3013 count; /* Number of jobs that match */
3014 const char *username; /* Username */
3015 _ipp_job_t *job; /* Current job pointer */
3016 cups_array_t *ra; /* Requested attributes array */
3017
3018
3019 /*
3020 * See if the "which-jobs" attribute have been specified...
3021 */
3022
3023 if ((attr = ippFindAttribute(client->request, "which-jobs",
3024 IPP_TAG_KEYWORD)) != NULL)
3025 {
3026 which_jobs = ippGetString(attr, 0, NULL);
3027 fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->hostname, which_jobs);
3028 }
3029
3030 if (!which_jobs || !strcmp(which_jobs, "not-completed"))
3031 {
3032 job_comparison = -1;
3033 job_state = IPP_JSTATE_STOPPED;
3034 }
3035 else if (!strcmp(which_jobs, "completed"))
3036 {
3037 job_comparison = 1;
3038 job_state = IPP_JSTATE_CANCELED;
3039 }
3040 else if (!strcmp(which_jobs, "aborted"))
3041 {
3042 job_comparison = 0;
3043 job_state = IPP_JSTATE_ABORTED;
3044 }
3045 else if (!strcmp(which_jobs, "all"))
3046 {
3047 job_comparison = 1;
3048 job_state = IPP_JSTATE_PENDING;
3049 }
3050 else if (!strcmp(which_jobs, "canceled"))
3051 {
3052 job_comparison = 0;
3053 job_state = IPP_JSTATE_CANCELED;
3054 }
3055 else if (!strcmp(which_jobs, "pending"))
3056 {
3057 job_comparison = 0;
3058 job_state = IPP_JSTATE_PENDING;
3059 }
3060 else if (!strcmp(which_jobs, "pending-held"))
3061 {
3062 job_comparison = 0;
3063 job_state = IPP_JSTATE_HELD;
3064 }
3065 else if (!strcmp(which_jobs, "processing"))
3066 {
3067 job_comparison = 0;
3068 job_state = IPP_JSTATE_PROCESSING;
3069 }
3070 else if (!strcmp(which_jobs, "processing-stopped"))
3071 {
3072 job_comparison = 0;
3073 job_state = IPP_JSTATE_STOPPED;
3074 }
3075 else
3076 {
3077 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
3078 "The which-jobs value \"%s\" is not supported.", which_jobs);
3079 ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
3080 "which-jobs", NULL, which_jobs);
3081 return;
3082 }
3083
3084 /*
3085 * See if they want to limit the number of jobs reported...
3086 */
3087
3088 if ((attr = ippFindAttribute(client->request, "limit",
3089 IPP_TAG_INTEGER)) != NULL)
3090 {
3091 limit = ippGetInteger(attr, 0);
3092
3093 fprintf(stderr, "%s Get-Jobs limit=%d", client->hostname, limit);
3094 }
3095 else
3096 limit = 0;
3097
3098 if ((attr = ippFindAttribute(client->request, "first-job-id",
3099 IPP_TAG_INTEGER)) != NULL)
3100 {
3101 first_job_id = ippGetInteger(attr, 0);
3102
3103 fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->hostname,
3104 first_job_id);
3105 }
3106 else
3107 first_job_id = 1;
3108
3109 /*
3110 * See if we only want to see jobs for a specific user...
3111 */
3112
3113 username = NULL;
3114
3115 if ((attr = ippFindAttribute(client->request, "my-jobs",
3116 IPP_TAG_BOOLEAN)) != NULL)
3117 {
3118 int my_jobs = ippGetBoolean(attr, 0);
3119
3120 fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->hostname,
3121 my_jobs ? "true" : "false");
3122
3123 if (my_jobs)
3124 {
3125 if ((attr = ippFindAttribute(client->request, "requesting-user-name",
3126 IPP_TAG_NAME)) == NULL)
3127 {
3128 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3129 "Need requesting-user-name with my-jobs.");
3130 return;
3131 }
3132
3133 username = ippGetString(attr, 0, NULL);
3134
3135 fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3136 client->hostname, username);
3137 }
3138 }
3139
3140 /*
3141 * OK, build a list of jobs for this printer...
3142 */
3143
3144 ra = ippCreateRequestedArray(client->request);
3145
3146 respond_ipp(client, IPP_STATUS_OK, NULL);
3147
3148 _cupsRWLockRead(&(client->printer->rwlock));
3149
3150 for (count = 0, job = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs);
3151 (limit <= 0 || count < limit) && job;
3152 job = (_ipp_job_t *)cupsArrayNext(client->printer->jobs))
3153 {
3154 /*
3155 * Filter out jobs that don't match...
3156 */
3157
3158 if ((job_comparison < 0 && job->state > job_state) ||
3159 (job_comparison == 0 && job->state != job_state) ||
3160 (job_comparison > 0 && job->state < job_state) ||
3161 job->id < first_job_id ||
3162 (username && job->username &&
3163 strcasecmp(username, job->username)))
3164 continue;
3165
3166 if (count > 0)
3167 ippAddSeparator(client->response);
3168
3169 count ++;
3170 copy_job_attributes(client, job, ra);
3171 }
3172
3173 cupsArrayDelete(ra);
3174
3175 _cupsRWUnlock(&(client->printer->rwlock));
3176 }
3177
3178
3179 /*
3180 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3181 */
3182
3183 static void
3184 ipp_get_printer_attributes(
3185 _ipp_client_t *client) /* I - Client */
3186 {
3187 cups_array_t *ra; /* Requested attributes array */
3188 _ipp_printer_t *printer; /* Printer */
3189
3190
3191 /*
3192 * Send the attributes...
3193 */
3194
3195 ra = ippCreateRequestedArray(client->request);
3196 printer = client->printer;
3197
3198 respond_ipp(client, IPP_STATUS_OK, NULL);
3199
3200 _cupsRWLockRead(&(printer->rwlock));
3201
3202 copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
3203 IPP_TAG_CUPS_CONST);
3204
3205 if (!ra || cupsArrayFind(ra, "media-col-ready"))
3206 {
3207 int i, /* Looping var */
3208 num_ready = 0; /* Number of ready media */
3209 ipp_t *ready[3]; /* Ready media */
3210
3211 if (printer->main_size != _IPP_MEDIA_SIZE_NONE)
3212 {
3213 if (printer->main_type != _IPP_MEDIA_TYPE_NONE)
3214 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);
3215 else
3216 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);
3217 }
3218 if (printer->envelope_size != _IPP_MEDIA_SIZE_NONE)
3219 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);
3220 if (printer->photo_size != _IPP_MEDIA_SIZE_NONE)
3221 {
3222 if (printer->photo_type != _IPP_MEDIA_TYPE_NONE)
3223 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);
3224 else
3225 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);
3226 }
3227
3228 if (num_ready)
3229 {
3230 ippAddCollections(client->response, IPP_TAG_PRINTER, "media-col-ready", num_ready, (const ipp_t **)ready);
3231 for (i = 0; i < num_ready; i ++)
3232 ippDelete(ready[i]);
3233 }
3234 else
3235 ippAddOutOfBand(client->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-col-ready");
3236 }
3237
3238 if (!ra || cupsArrayFind(ra, "media-ready"))
3239 {
3240 int num_ready = 0; /* Number of ready media */
3241 const char *ready[3]; /* Ready media */
3242
3243 if (printer->main_size != _IPP_MEDIA_SIZE_NONE)
3244 ready[num_ready ++] = media_supported[printer->main_size];
3245
3246 if (printer->envelope_size != _IPP_MEDIA_SIZE_NONE)
3247 ready[num_ready ++] = media_supported[printer->envelope_size];
3248
3249 if (printer->photo_size != _IPP_MEDIA_SIZE_NONE)
3250 ready[num_ready ++] = media_supported[printer->photo_size];
3251
3252 if (num_ready)
3253 ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-ready", num_ready, NULL, ready);
3254 else
3255 ippAddOutOfBand(client->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-ready");
3256 }
3257
3258 if (!ra || cupsArrayFind(ra, "printer-config-change-date-time"))
3259 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-config-change-date-time", ippTimeToDate(printer->config_time));
3260
3261 if (!ra || cupsArrayFind(ra, "printer-config-change-time"))
3262 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-config-change-time", (int)(printer->config_time - printer->start_time));
3263
3264 if (!ra || cupsArrayFind(ra, "printer-current-time"))
3265 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(time(NULL)));
3266
3267
3268 if (!ra || cupsArrayFind(ra, "printer-state"))
3269 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
3270 "printer-state", printer->state);
3271
3272 if (!ra || cupsArrayFind(ra, "printer-state-change-date-time"))
3273 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-state-change-date-time", ippTimeToDate(printer->state_time));
3274
3275 if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
3276 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-state-change-time", (int)(printer->state_time - printer->start_time));
3277
3278 if (!ra || cupsArrayFind(ra, "printer-state-message"))
3279 {
3280 static const char * const messages[] = { "Idle.", "Printing.", "Stopped." };
3281
3282 ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-state-message", NULL, messages[printer->state - IPP_PSTATE_IDLE]);
3283 }
3284
3285 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
3286 {
3287 if (printer->state_reasons == _IPP_PREASON_NONE)
3288 ippAddString(client->response, IPP_TAG_PRINTER,
3289 IPP_CONST_TAG(IPP_TAG_KEYWORD),
3290 "printer-state-reasons", NULL, "none");
3291 else
3292 {
3293 int num_reasons = 0;/* Number of reasons */
3294 const char *reasons[32]; /* Reason strings */
3295
3296 if (printer->state_reasons & _IPP_PREASON_OTHER)
3297 reasons[num_reasons ++] = "other";
3298 if (printer->state_reasons & _IPP_PREASON_COVER_OPEN)
3299 reasons[num_reasons ++] = "cover-open";
3300 if (printer->state_reasons & _IPP_PREASON_INPUT_TRAY_MISSING)
3301 reasons[num_reasons ++] = "input-tray-missing";
3302 if (printer->state_reasons & _IPP_PREASON_MARKER_SUPPLY_EMPTY)
3303 reasons[num_reasons ++] = "marker-supply-empty-warning";
3304 if (printer->state_reasons & _IPP_PREASON_MARKER_SUPPLY_LOW)
3305 reasons[num_reasons ++] = "marker-supply-low-report";
3306 if (printer->state_reasons & _IPP_PREASON_MARKER_WASTE_ALMOST_FULL)
3307 reasons[num_reasons ++] = "marker-waste-almost-full-report";
3308 if (printer->state_reasons & _IPP_PREASON_MARKER_WASTE_FULL)
3309 reasons[num_reasons ++] = "marker-waste-full-warning";
3310 if (printer->state_reasons & _IPP_PREASON_MEDIA_EMPTY)
3311 reasons[num_reasons ++] = "media-empty-warning";
3312 if (printer->state_reasons & _IPP_PREASON_MEDIA_JAM)
3313 reasons[num_reasons ++] = "media-jam-warning";
3314 if (printer->state_reasons & _IPP_PREASON_MEDIA_LOW)
3315 reasons[num_reasons ++] = "media-low-report";
3316 if (printer->state_reasons & _IPP_PREASON_MEDIA_NEEDED)
3317 reasons[num_reasons ++] = "media-needed-report";
3318 if (printer->state_reasons & _IPP_PREASON_MOVING_TO_PAUSED)
3319 reasons[num_reasons ++] = "moving-to-paused";
3320 if (printer->state_reasons & _IPP_PREASON_PAUSED)
3321 reasons[num_reasons ++] = "paused";
3322 if (printer->state_reasons & _IPP_PREASON_SPOOL_AREA_FULL)
3323 reasons[num_reasons ++] = "spool-area-full";
3324 if (printer->state_reasons & _IPP_PREASON_TONER_EMPTY)
3325 reasons[num_reasons ++] = "toner-empty-warning";
3326 if (printer->state_reasons & _IPP_PREASON_TONER_LOW)
3327 reasons[num_reasons ++] = "toner-low-report";
3328
3329 ippAddStrings(client->response, IPP_TAG_PRINTER,
3330 IPP_CONST_TAG(IPP_TAG_KEYWORD),
3331 "printer-state-reasons", num_reasons, NULL, reasons);
3332 }
3333 }
3334
3335 if (!ra || cupsArrayFind(ra, "printer-supply"))
3336 {
3337 int i; /* Looping var */
3338 char buffer[256]; /* Supply value buffer */
3339 ipp_attribute_t *attr = NULL; /* Attribute */
3340 static const char * const colorants[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3341
3342 for (i = 0; i < 5; i ++)
3343 {
3344 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]);
3345
3346 if (!attr)
3347 attr = ippAddOctetString(client->response, IPP_TAG_PRINTER, "printer-supply", buffer, (int)strlen(buffer));
3348 else
3349 ippSetOctetString(client->response, &attr, i, buffer, (int)strlen(buffer));
3350 }
3351 }
3352
3353 if (!ra || cupsArrayFind(ra, "printer-up-time"))
3354 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-up-time", (int)(time(NULL) - printer->start_time));
3355
3356 if (!ra || cupsArrayFind(ra, "queued-job-count"))
3357 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3358 "queued-job-count", printer->active_job && printer->active_job->state < IPP_JSTATE_CANCELED);
3359
3360 _cupsRWUnlock(&(printer->rwlock));
3361
3362 cupsArrayDelete(ra);
3363 }
3364
3365
3366 /*
3367 * 'ipp_identify_printer()' - Beep or display a message.
3368 */
3369
3370 static void
3371 ipp_identify_printer(
3372 _ipp_client_t *client) /* I - Client */
3373 {
3374 /* TODO: Do something */
3375
3376 respond_ipp(client, IPP_STATUS_OK, NULL);
3377 }
3378
3379
3380 /*
3381 * 'ipp_print_job()' - Create a job object with an attached document.
3382 */
3383
3384 static void
3385 ipp_print_job(_ipp_client_t *client) /* I - Client */
3386 {
3387 _ipp_job_t *job; /* New job */
3388 char filename[1024], /* Filename buffer */
3389 buffer[4096]; /* Copy buffer */
3390 ssize_t bytes; /* Bytes read */
3391 cups_array_t *ra; /* Attributes to send in response */
3392
3393
3394 /*
3395 * Validate print job attributes...
3396 */
3397
3398 if (!valid_job_attributes(client))
3399 {
3400 httpFlush(client->http);
3401 return;
3402 }
3403
3404 /*
3405 * Do we have a file to print?
3406 */
3407
3408 if (httpGetState(client->http) == HTTP_STATE_POST_SEND)
3409 {
3410 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No file in request.");
3411 return;
3412 }
3413
3414 /*
3415 * Print the job...
3416 */
3417
3418 if ((job = create_job(client)) == NULL)
3419 {
3420 respond_ipp(client, IPP_STATUS_ERROR_BUSY,
3421 "Currently printing another job.");
3422 return;
3423 }
3424
3425 /*
3426 * Create a file for the request data...
3427 */
3428
3429 create_job_filename(client->printer, job, filename, sizeof(filename));
3430
3431 if (Verbosity)
3432 fprintf(stderr, "Creating job file \"%s\", format \"%s\".\n", filename, job->format);
3433
3434 if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
3435 {
3436 job->state = IPP_JSTATE_ABORTED;
3437
3438 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3439 "Unable to create print file: %s", strerror(errno));
3440 return;
3441 }
3442
3443 while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
3444 {
3445 if (write(job->fd, buffer, (size_t)bytes) < bytes)
3446 {
3447 int error = errno; /* Write error */
3448
3449 job->state = IPP_JSTATE_ABORTED;
3450
3451 close(job->fd);
3452 job->fd = -1;
3453
3454 unlink(filename);
3455
3456 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3457 "Unable to write print file: %s", strerror(error));
3458 return;
3459 }
3460 }
3461
3462 if (bytes < 0)
3463 {
3464 /*
3465 * Got an error while reading the print data, so abort this job.
3466 */
3467
3468 job->state = IPP_JSTATE_ABORTED;
3469
3470 close(job->fd);
3471 job->fd = -1;
3472
3473 unlink(filename);
3474
3475 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3476 "Unable to read print file.");
3477 return;
3478 }
3479
3480 if (close(job->fd))
3481 {
3482 int error = errno; /* Write error */
3483
3484 job->state = IPP_JSTATE_ABORTED;
3485 job->fd = -1;
3486
3487 unlink(filename);
3488
3489 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3490 "Unable to write print file: %s", strerror(error));
3491 return;
3492 }
3493
3494 job->fd = -1;
3495 job->filename = strdup(filename);
3496 job->state = IPP_JSTATE_PENDING;
3497
3498 /*
3499 * Process the job...
3500 */
3501
3502 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3503 {
3504 job->state = IPP_JSTATE_ABORTED;
3505 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
3506 return;
3507 }
3508
3509 /*
3510 * Return the job info...
3511 */
3512
3513 respond_ipp(client, IPP_STATUS_OK, NULL);
3514
3515 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3516 cupsArrayAdd(ra, "job-id");
3517 cupsArrayAdd(ra, "job-state");
3518 cupsArrayAdd(ra, "job-state-message");
3519 cupsArrayAdd(ra, "job-state-reasons");
3520 cupsArrayAdd(ra, "job-uri");
3521
3522 copy_job_attributes(client, job, ra);
3523 cupsArrayDelete(ra);
3524 }
3525
3526
3527 /*
3528 * 'ipp_print_uri()' - Create a job object with a referenced document.
3529 */
3530
3531 static void
3532 ipp_print_uri(_ipp_client_t *client) /* I - Client */
3533 {
3534 _ipp_job_t *job; /* New job */
3535 ipp_attribute_t *uri; /* document-uri */
3536 char scheme[256], /* URI scheme */
3537 userpass[256], /* Username and password info */
3538 hostname[256], /* Hostname */
3539 resource[1024]; /* Resource path */
3540 int port; /* Port number */
3541 http_uri_status_t uri_status; /* URI decode status */
3542 http_encryption_t encryption; /* Encryption to use, if any */
3543 http_t *http; /* Connection for http/https URIs */
3544 http_status_t status; /* Access status for http/https URIs */
3545 int infile; /* Input file for local file URIs */
3546 char filename[1024], /* Filename buffer */
3547 buffer[4096]; /* Copy buffer */
3548 ssize_t bytes; /* Bytes read */
3549 cups_array_t *ra; /* Attributes to send in response */
3550 static const char * const uri_status_strings[] =
3551 { /* URI decode errors */
3552 "URI too large.",
3553 "Bad arguments to function.",
3554 "Bad resource in URI.",
3555 "Bad port number in URI.",
3556 "Bad hostname in URI.",
3557 "Bad username in URI.",
3558 "Bad scheme in URI.",
3559 "Bad/empty URI."
3560 };
3561
3562
3563 /*
3564 * Validate print job attributes...
3565 */
3566
3567 if (!valid_job_attributes(client))
3568 {
3569 httpFlush(client->http);
3570 return;
3571 }
3572
3573 /*
3574 * Do we have a file to print?
3575 */
3576
3577 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
3578 {
3579 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3580 "Unexpected document data following request.");
3581 return;
3582 }
3583
3584 /*
3585 * Do we have a document URI?
3586 */
3587
3588 if ((uri = ippFindAttribute(client->request, "document-uri",
3589 IPP_TAG_URI)) == NULL)
3590 {
3591 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
3592 return;
3593 }
3594
3595 if (ippGetCount(uri) != 1)
3596 {
3597 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3598 "Too many document-uri values.");
3599 return;
3600 }
3601
3602 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
3603 scheme, sizeof(scheme), userpass,
3604 sizeof(userpass), hostname, sizeof(hostname),
3605 &port, resource, sizeof(resource));
3606 if (uri_status < HTTP_URI_STATUS_OK)
3607 {
3608 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s",
3609 uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]);
3610 return;
3611 }
3612
3613 if (strcmp(scheme, "file") &&
3614 #ifdef HAVE_SSL
3615 strcmp(scheme, "https") &&
3616 #endif /* HAVE_SSL */
3617 strcmp(scheme, "http"))
3618 {
3619 respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME,
3620 "URI scheme \"%s\" not supported.", scheme);
3621 return;
3622 }
3623
3624 if (!strcmp(scheme, "file") && access(resource, R_OK))
3625 {
3626 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3627 "Unable to access URI: %s", strerror(errno));
3628 return;
3629 }
3630
3631 /*
3632 * Print the job...
3633 */
3634
3635 if ((job = create_job(client)) == NULL)
3636 {
3637 respond_ipp(client, IPP_STATUS_ERROR_BUSY,
3638 "Currently printing another job.");
3639 return;
3640 }
3641
3642 /*
3643 * Create a file for the request data...
3644 */
3645
3646 if (!strcasecmp(job->format, "image/jpeg"))
3647 snprintf(filename, sizeof(filename), "%s/%d.jpg",
3648 client->printer->directory, job->id);
3649 else if (!strcasecmp(job->format, "image/png"))
3650 snprintf(filename, sizeof(filename), "%s/%d.png",
3651 client->printer->directory, job->id);
3652 else if (!strcasecmp(job->format, "application/pdf"))
3653 snprintf(filename, sizeof(filename), "%s/%d.pdf",
3654 client->printer->directory, job->id);
3655 else if (!strcasecmp(job->format, "application/postscript"))
3656 snprintf(filename, sizeof(filename), "%s/%d.ps",
3657 client->printer->directory, job->id);
3658 else
3659 snprintf(filename, sizeof(filename), "%s/%d.prn",
3660 client->printer->directory, job->id);
3661
3662 if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
3663 {
3664 job->state = IPP_JSTATE_ABORTED;
3665
3666 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3667 "Unable to create print file: %s", strerror(errno));
3668 return;
3669 }
3670
3671 if (!strcmp(scheme, "file"))
3672 {
3673 if ((infile = open(resource, O_RDONLY)) < 0)
3674 {
3675 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3676 "Unable to access URI: %s", strerror(errno));
3677 return;
3678 }
3679
3680 do
3681 {
3682 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
3683 (errno == EAGAIN || errno == EINTR))
3684 bytes = 1;
3685 else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes)
3686 {
3687 int error = errno; /* Write error */
3688
3689 job->state = IPP_JSTATE_ABORTED;
3690
3691 close(job->fd);
3692 job->fd = -1;
3693
3694 unlink(filename);
3695 close(infile);
3696
3697 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3698 "Unable to write print file: %s", strerror(error));
3699 return;
3700 }
3701 }
3702 while (bytes > 0);
3703
3704 close(infile);
3705 }
3706 else
3707 {
3708 #ifdef HAVE_SSL
3709 if (port == 443 || !strcmp(scheme, "https"))
3710 encryption = HTTP_ENCRYPTION_ALWAYS;
3711 else
3712 #endif /* HAVE_SSL */
3713 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
3714
3715 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption,
3716 1, 30000, NULL)) == NULL)
3717 {
3718 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3719 "Unable to connect to %s: %s", hostname,
3720 cupsLastErrorString());
3721 job->state = IPP_JSTATE_ABORTED;
3722
3723 close(job->fd);
3724 job->fd = -1;
3725
3726 unlink(filename);
3727 return;
3728 }
3729
3730 httpClearFields(http);
3731 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
3732 if (httpGet(http, resource))
3733 {
3734 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3735 "Unable to GET URI: %s", strerror(errno));
3736
3737 job->state = IPP_JSTATE_ABORTED;
3738
3739 close(job->fd);
3740 job->fd = -1;
3741
3742 unlink(filename);
3743 httpClose(http);
3744 return;
3745 }
3746
3747 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
3748
3749 if (status != HTTP_STATUS_OK)
3750 {
3751 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3752 "Unable to GET URI: %s", httpStatus(status));
3753
3754 job->state = IPP_JSTATE_ABORTED;
3755
3756 close(job->fd);
3757 job->fd = -1;
3758
3759 unlink(filename);
3760 httpClose(http);
3761 return;
3762 }
3763
3764 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
3765 {
3766 if (write(job->fd, buffer, (size_t)bytes) < bytes)
3767 {
3768 int error = errno; /* Write error */
3769
3770 job->state = IPP_JSTATE_ABORTED;
3771
3772 close(job->fd);
3773 job->fd = -1;
3774
3775 unlink(filename);
3776 httpClose(http);
3777
3778 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3779 "Unable to write print file: %s", strerror(error));
3780 return;
3781 }
3782 }
3783
3784 httpClose(http);
3785 }
3786
3787 if (close(job->fd))
3788 {
3789 int error = errno; /* Write error */
3790
3791 job->state = IPP_JSTATE_ABORTED;
3792 job->fd = -1;
3793
3794 unlink(filename);
3795
3796 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3797 "Unable to write print file: %s", strerror(error));
3798 return;
3799 }
3800
3801 job->fd = -1;
3802 job->filename = strdup(filename);
3803 job->state = IPP_JSTATE_PENDING;
3804
3805 /*
3806 * Process the job...
3807 */
3808
3809 #if 0
3810 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3811 {
3812 job->state = IPP_JSTATE_ABORTED;
3813 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
3814 return;
3815 }
3816
3817 #else
3818 process_job(job);
3819 #endif /* 0 */
3820
3821 /*
3822 * Return the job info...
3823 */
3824
3825 respond_ipp(client, IPP_STATUS_OK, NULL);
3826
3827 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3828 cupsArrayAdd(ra, "job-id");
3829 cupsArrayAdd(ra, "job-state");
3830 cupsArrayAdd(ra, "job-state-reasons");
3831 cupsArrayAdd(ra, "job-uri");
3832
3833 copy_job_attributes(client, job, ra);
3834 cupsArrayDelete(ra);
3835 }
3836
3837
3838 /*
3839 * 'ipp_send_document()' - Add an attached document to a job object created with
3840 * Create-Job.
3841 */
3842
3843 static void
3844 ipp_send_document(_ipp_client_t *client)/* I - Client */
3845 {
3846 _ipp_job_t *job; /* Job information */
3847 char filename[1024], /* Filename buffer */
3848 buffer[4096]; /* Copy buffer */
3849 ssize_t bytes; /* Bytes read */
3850 ipp_attribute_t *attr; /* Current attribute */
3851 cups_array_t *ra; /* Attributes to send in response */
3852
3853
3854 /*
3855 * Get the job...
3856 */
3857
3858 if ((job = find_job(client)) == NULL)
3859 {
3860 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3861 httpFlush(client->http);
3862 return;
3863 }
3864
3865 /*
3866 * See if we already have a document for this job or the job has already
3867 * in a non-pending state...
3868 */
3869
3870 if (job->state > IPP_JSTATE_HELD)
3871 {
3872 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3873 "Job is not in a pending state.");
3874 httpFlush(client->http);
3875 return;
3876 }
3877 else if (job->filename || job->fd >= 0)
3878 {
3879 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED,
3880 "Multiple document jobs are not supported.");
3881 httpFlush(client->http);
3882 return;
3883 }
3884
3885 if ((attr = ippFindAttribute(client->request, "last-document",
3886 IPP_TAG_ZERO)) == NULL)
3887 {
3888 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3889 "Missing required last-document attribute.");
3890 httpFlush(client->http);
3891 return;
3892 }
3893 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 ||
3894 !ippGetBoolean(attr, 0))
3895 {
3896 respond_unsupported(client, attr);
3897 httpFlush(client->http);
3898 return;
3899 }
3900
3901 /*
3902 * Validate document attributes...
3903 */
3904
3905 if (!valid_doc_attributes(client))
3906 {
3907 httpFlush(client->http);
3908 return;
3909 }
3910
3911 copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
3912
3913 /*
3914 * Get the document format for the job...
3915 */
3916
3917 _cupsRWLockWrite(&(client->printer->rwlock));
3918
3919 if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
3920 job->format = ippGetString(attr, 0, NULL);
3921 else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
3922 job->format = ippGetString(attr, 0, NULL);
3923 else
3924 job->format = "application/octet-stream";
3925
3926 /*
3927 * Create a file for the request data...
3928 */
3929
3930 create_job_filename(client->printer, job, filename, sizeof(filename));
3931
3932 if (Verbosity)
3933 fprintf(stderr, "Creating job file \"%s\", format \"%s\".\n", filename, job->format);
3934
3935 job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
3936
3937 _cupsRWUnlock(&(client->printer->rwlock));
3938
3939 if (job->fd < 0)
3940 {
3941 job->state = IPP_JSTATE_ABORTED;
3942
3943 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3944 "Unable to create print file: %s", strerror(errno));
3945 return;
3946 }
3947
3948 while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
3949 {
3950 if (write(job->fd, buffer, (size_t)bytes) < bytes)
3951 {
3952 int error = errno; /* Write error */
3953
3954 job->state = IPP_JSTATE_ABORTED;
3955
3956 close(job->fd);
3957 job->fd = -1;
3958
3959 unlink(filename);
3960
3961 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3962 "Unable to write print file: %s", strerror(error));
3963 return;
3964 }
3965 }
3966
3967 if (bytes < 0)
3968 {
3969 /*
3970 * Got an error while reading the print data, so abort this job.
3971 */
3972
3973 job->state = IPP_JSTATE_ABORTED;
3974
3975 close(job->fd);
3976 job->fd = -1;
3977
3978 unlink(filename);
3979
3980 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3981 "Unable to read print file.");
3982 return;
3983 }
3984
3985 if (close(job->fd))
3986 {
3987 int error = errno; /* Write error */
3988
3989 job->state = IPP_JSTATE_ABORTED;
3990 job->fd = -1;
3991
3992 unlink(filename);
3993
3994 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3995 "Unable to write print file: %s", strerror(error));
3996 return;
3997 }
3998
3999 _cupsRWLockWrite(&(client->printer->rwlock));
4000
4001 job->fd = -1;
4002 job->filename = strdup(filename);
4003 job->state = IPP_JSTATE_PENDING;
4004
4005 _cupsRWUnlock(&(client->printer->rwlock));
4006
4007 /*
4008 * Process the job...
4009 */
4010
4011 #if 0
4012 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
4013 {
4014 job->state = IPP_JSTATE_ABORTED;
4015 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
4016 return;
4017 }
4018
4019 #else
4020 process_job(job);
4021 #endif /* 0 */
4022
4023 /*
4024 * Return the job info...
4025 */
4026
4027 respond_ipp(client, IPP_STATUS_OK, NULL);
4028
4029 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4030 cupsArrayAdd(ra, "job-id");
4031 cupsArrayAdd(ra, "job-state");
4032 cupsArrayAdd(ra, "job-state-reasons");
4033 cupsArrayAdd(ra, "job-uri");
4034
4035 copy_job_attributes(client, job, ra);
4036 cupsArrayDelete(ra);
4037 }
4038
4039
4040 /*
4041 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4042 * Create-Job.
4043 */
4044
4045 static void
4046 ipp_send_uri(_ipp_client_t *client) /* I - Client */
4047 {
4048 _ipp_job_t *job; /* Job information */
4049 ipp_attribute_t *uri; /* document-uri */
4050 char scheme[256], /* URI scheme */
4051 userpass[256], /* Username and password info */
4052 hostname[256], /* Hostname */
4053 resource[1024]; /* Resource path */
4054 int port; /* Port number */
4055 http_uri_status_t uri_status; /* URI decode status */
4056 http_encryption_t encryption; /* Encryption to use, if any */
4057 http_t *http; /* Connection for http/https URIs */
4058 http_status_t status; /* Access status for http/https URIs */
4059 int infile; /* Input file for local file URIs */
4060 char filename[1024], /* Filename buffer */
4061 buffer[4096]; /* Copy buffer */
4062 ssize_t bytes; /* Bytes read */
4063 ipp_attribute_t *attr; /* Current attribute */
4064 cups_array_t *ra; /* Attributes to send in response */
4065 static const char * const uri_status_strings[] =
4066 { /* URI decode errors */
4067 "URI too large.",
4068 "Bad arguments to function.",
4069 "Bad resource in URI.",
4070 "Bad port number in URI.",
4071 "Bad hostname in URI.",
4072 "Bad username in URI.",
4073 "Bad scheme in URI.",
4074 "Bad/empty URI."
4075 };
4076
4077
4078 /*
4079 * Get the job...
4080 */
4081
4082 if ((job = find_job(client)) == NULL)
4083 {
4084 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
4085 httpFlush(client->http);
4086 return;
4087 }
4088
4089 /*
4090 * See if we already have a document for this job or the job has already
4091 * in a non-pending state...
4092 */
4093
4094 if (job->state > IPP_JSTATE_HELD)
4095 {
4096 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
4097 "Job is not in a pending state.");
4098 httpFlush(client->http);
4099 return;
4100 }
4101 else if (job->filename || job->fd >= 0)
4102 {
4103 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED,
4104 "Multiple document jobs are not supported.");
4105 httpFlush(client->http);
4106 return;
4107 }
4108
4109 if ((attr = ippFindAttribute(client->request, "last-document",
4110 IPP_TAG_ZERO)) == NULL)
4111 {
4112 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4113 "Missing required last-document attribute.");
4114 httpFlush(client->http);
4115 return;
4116 }
4117 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 ||
4118 !ippGetBoolean(attr, 0))
4119 {
4120 respond_unsupported(client, attr);
4121 httpFlush(client->http);
4122 return;
4123 }
4124
4125 /*
4126 * Validate document attributes...
4127 */
4128
4129 if (!valid_doc_attributes(client))
4130 {
4131 httpFlush(client->http);
4132 return;
4133 }
4134
4135 /*
4136 * Do we have a file to print?
4137 */
4138
4139 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
4140 {
4141 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4142 "Unexpected document data following request.");
4143 return;
4144 }
4145
4146 /*
4147 * Do we have a document URI?
4148 */
4149
4150 if ((uri = ippFindAttribute(client->request, "document-uri",
4151 IPP_TAG_URI)) == NULL)
4152 {
4153 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
4154 return;
4155 }
4156
4157 if (ippGetCount(uri) != 1)
4158 {
4159 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4160 "Too many document-uri values.");
4161 return;
4162 }
4163
4164 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
4165 scheme, sizeof(scheme), userpass,
4166 sizeof(userpass), hostname, sizeof(hostname),
4167 &port, resource, sizeof(resource));
4168 if (uri_status < HTTP_URI_STATUS_OK)
4169 {
4170 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s",
4171 uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]);
4172 return;
4173 }
4174
4175 if (strcmp(scheme, "file") &&
4176 #ifdef HAVE_SSL
4177 strcmp(scheme, "https") &&
4178 #endif /* HAVE_SSL */
4179 strcmp(scheme, "http"))
4180 {
4181 respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME,
4182 "URI scheme \"%s\" not supported.", scheme);
4183 return;
4184 }
4185
4186 if (!strcmp(scheme, "file") && access(resource, R_OK))
4187 {
4188 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4189 "Unable to access URI: %s", strerror(errno));
4190 return;
4191 }
4192
4193 /*
4194 * Get the document format for the job...
4195 */
4196
4197 _cupsRWLockWrite(&(client->printer->rwlock));
4198
4199 if ((attr = ippFindAttribute(job->attrs, "document-format",
4200 IPP_TAG_MIMETYPE)) != NULL)
4201 job->format = ippGetString(attr, 0, NULL);
4202 else
4203 job->format = "application/octet-stream";
4204
4205 /*
4206 * Create a file for the request data...
4207 */
4208
4209 if (!strcasecmp(job->format, "image/jpeg"))
4210 snprintf(filename, sizeof(filename), "%s/%d.jpg",
4211 client->printer->directory, job->id);
4212 else if (!strcasecmp(job->format, "image/png"))
4213 snprintf(filename, sizeof(filename), "%s/%d.png",
4214 client->printer->directory, job->id);
4215 else if (!strcasecmp(job->format, "application/pdf"))
4216 snprintf(filename, sizeof(filename), "%s/%d.pdf",
4217 client->printer->directory, job->id);
4218 else if (!strcasecmp(job->format, "application/postscript"))
4219 snprintf(filename, sizeof(filename), "%s/%d.ps",
4220 client->printer->directory, job->id);
4221 else
4222 snprintf(filename, sizeof(filename), "%s/%d.prn",
4223 client->printer->directory, job->id);
4224
4225 job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
4226
4227 _cupsRWUnlock(&(client->printer->rwlock));
4228
4229 if (job->fd < 0)
4230 {
4231 job->state = IPP_JSTATE_ABORTED;
4232
4233 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4234 "Unable to create print file: %s", strerror(errno));
4235 return;
4236 }
4237
4238 if (!strcmp(scheme, "file"))
4239 {
4240 if ((infile = open(resource, O_RDONLY)) < 0)
4241 {
4242 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4243 "Unable to access URI: %s", strerror(errno));
4244 return;
4245 }
4246
4247 do
4248 {
4249 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
4250 (errno == EAGAIN || errno == EINTR))
4251 bytes = 1;
4252 else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes)
4253 {
4254 int error = errno; /* Write error */
4255
4256 job->state = IPP_JSTATE_ABORTED;
4257
4258 close(job->fd);
4259 job->fd = -1;
4260
4261 unlink(filename);
4262 close(infile);
4263
4264 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4265 "Unable to write print file: %s", strerror(error));
4266 return;
4267 }
4268 }
4269 while (bytes > 0);
4270
4271 close(infile);
4272 }
4273 else
4274 {
4275 #ifdef HAVE_SSL
4276 if (port == 443 || !strcmp(scheme, "https"))
4277 encryption = HTTP_ENCRYPTION_ALWAYS;
4278 else
4279 #endif /* HAVE_SSL */
4280 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
4281
4282 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption,
4283 1, 30000, NULL)) == NULL)
4284 {
4285 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4286 "Unable to connect to %s: %s", hostname,
4287 cupsLastErrorString());
4288 job->state = IPP_JSTATE_ABORTED;
4289
4290 close(job->fd);
4291 job->fd = -1;
4292
4293 unlink(filename);
4294 return;
4295 }
4296
4297 httpClearFields(http);
4298 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
4299 if (httpGet(http, resource))
4300 {
4301 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4302 "Unable to GET URI: %s", strerror(errno));
4303
4304 job->state = IPP_JSTATE_ABORTED;
4305
4306 close(job->fd);
4307 job->fd = -1;
4308
4309 unlink(filename);
4310 httpClose(http);
4311 return;
4312 }
4313
4314 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
4315
4316 if (status != HTTP_STATUS_OK)
4317 {
4318 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4319 "Unable to GET URI: %s", httpStatus(status));
4320
4321 job->state = IPP_JSTATE_ABORTED;
4322
4323 close(job->fd);
4324 job->fd = -1;
4325
4326 unlink(filename);
4327 httpClose(http);
4328 return;
4329 }
4330
4331 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
4332 {
4333 if (write(job->fd, buffer, (size_t)bytes) < bytes)
4334 {
4335 int error = errno; /* Write error */
4336
4337 job->state = IPP_JSTATE_ABORTED;
4338
4339 close(job->fd);
4340 job->fd = -1;
4341
4342 unlink(filename);
4343 httpClose(http);
4344
4345 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4346 "Unable to write print file: %s", strerror(error));
4347 return;
4348 }
4349 }
4350
4351 httpClose(http);
4352 }
4353
4354 if (close(job->fd))
4355 {
4356 int error = errno; /* Write error */
4357
4358 job->state = IPP_JSTATE_ABORTED;
4359 job->fd = -1;
4360
4361 unlink(filename);
4362
4363 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4364 "Unable to write print file: %s", strerror(error));
4365 return;
4366 }
4367
4368 _cupsRWLockWrite(&(client->printer->rwlock));
4369
4370 job->fd = -1;
4371 job->filename = strdup(filename);
4372 job->state = IPP_JSTATE_PENDING;
4373
4374 _cupsRWUnlock(&(client->printer->rwlock));
4375
4376 /*
4377 * Process the job...
4378 */
4379
4380 #if 0
4381 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
4382 {
4383 job->state = IPP_JSTATE_ABORTED;
4384 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
4385 return;
4386 }
4387
4388 #else
4389 process_job(job);
4390 #endif /* 0 */
4391
4392 /*
4393 * Return the job info...
4394 */
4395
4396 respond_ipp(client, IPP_STATUS_OK, NULL);
4397
4398 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4399 cupsArrayAdd(ra, "job-id");
4400 cupsArrayAdd(ra, "job-state");
4401 cupsArrayAdd(ra, "job-state-reasons");
4402 cupsArrayAdd(ra, "job-uri");
4403
4404 copy_job_attributes(client, job, ra);
4405 cupsArrayDelete(ra);
4406 }
4407
4408
4409 /*
4410 * 'ipp_validate_job()' - Validate job creation attributes.
4411 */
4412
4413 static void
4414 ipp_validate_job(_ipp_client_t *client) /* I - Client */
4415 {
4416 if (valid_job_attributes(client))
4417 respond_ipp(client, IPP_STATUS_OK, NULL);
4418 }
4419
4420
4421 /*
4422 * 'parse_options()' - Parse URL options into CUPS options.
4423 *
4424 * The client->options string is destroyed by this function.
4425 */
4426
4427 static int /* O - Number of options */
4428 parse_options(_ipp_client_t *client, /* I - Client */
4429 cups_option_t **options) /* O - Options */
4430 {
4431 char *name, /* Name */
4432 *value, /* Value */
4433 *next; /* Next name=value pair */
4434 int num_options = 0; /* Number of options */
4435
4436
4437 *options = NULL;
4438
4439 for (name = client->options; name && *name; name = next)
4440 {
4441 if ((value = strchr(name, '=')) == NULL)
4442 break;
4443
4444 *value++ = '\0';
4445 if ((next = strchr(value, '&')) != NULL)
4446 *next++ = '\0';
4447
4448 num_options = cupsAddOption(name, value, num_options, options);
4449 }
4450
4451 return (num_options);
4452 }
4453
4454
4455 /*
4456 * 'process_client()' - Process client requests on a thread.
4457 */
4458
4459 static void * /* O - Exit status */
4460 process_client(_ipp_client_t *client) /* I - Client */
4461 {
4462 /*
4463 * Loop until we are out of requests or timeout (30 seconds)...
4464 */
4465
4466 #ifdef HAVE_SSL
4467 int first_time = 1; /* First time request? */
4468 #endif /* HAVE_SSL */
4469
4470 while (httpWait(client->http, 30000))
4471 {
4472 #ifdef HAVE_SSL
4473 if (first_time)
4474 {
4475 /*
4476 * See if we need to negotiate a TLS connection...
4477 */
4478
4479 char buf[1]; /* First byte from client */
4480
4481 if (recv(httpGetFd(client->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0])))
4482 {
4483 fprintf(stderr, "%s Starting HTTPS session.\n", client->hostname);
4484
4485 if (httpEncryption(client->http, HTTP_ENCRYPTION_ALWAYS))
4486 {
4487 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
4488 break;
4489 }
4490
4491 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
4492 }
4493
4494 first_time = 0;
4495 }
4496 #endif /* HAVE_SSL */
4497
4498 if (!process_http(client))
4499 break;
4500 }
4501
4502 /*
4503 * Close the conection to the client and return...
4504 */
4505
4506 delete_client(client);
4507
4508 return (NULL);
4509 }
4510
4511
4512 /*
4513 * 'process_http()' - Process a HTTP request.
4514 */
4515
4516 int /* O - 1 on success, 0 on failure */
4517 process_http(_ipp_client_t *client) /* I - Client connection */
4518 {
4519 char uri[1024]; /* URI */
4520 http_state_t http_state; /* HTTP state */
4521 http_status_t http_status; /* HTTP status */
4522 ipp_state_t ipp_state; /* State of IPP transfer */
4523 char scheme[32], /* Method/scheme */
4524 userpass[128], /* Username:password */
4525 hostname[HTTP_MAX_HOST];
4526 /* Hostname */
4527 int port; /* Port number */
4528 const char *encoding; /* Content-Encoding value */
4529 static const char * const http_states[] =
4530 { /* Strings for logging HTTP method */
4531 "WAITING",
4532 "OPTIONS",
4533 "GET",
4534 "GET_SEND",
4535 "HEAD",
4536 "POST",
4537 "POST_RECV",
4538 "POST_SEND",
4539 "PUT",
4540 "PUT_RECV",
4541 "DELETE",
4542 "TRACE",
4543 "CONNECT",
4544 "STATUS",
4545 "UNKNOWN_METHOD",
4546 "UNKNOWN_VERSION"
4547 };
4548
4549
4550 /*
4551 * Clear state variables...
4552 */
4553
4554 ippDelete(client->request);
4555 ippDelete(client->response);
4556
4557 client->request = NULL;
4558 client->response = NULL;
4559 client->operation = HTTP_STATE_WAITING;
4560
4561 /*
4562 * Read a request from the connection...
4563 */
4564
4565 while ((http_state = httpReadRequest(client->http, uri,
4566 sizeof(uri))) == HTTP_STATE_WAITING)
4567 usleep(1);
4568
4569 /*
4570 * Parse the request line...
4571 */
4572
4573 if (http_state == HTTP_STATE_ERROR)
4574 {
4575 if (httpError(client->http) == EPIPE)
4576 fprintf(stderr, "%s Client closed connection.\n", client->hostname);
4577 else
4578 fprintf(stderr, "%s Bad request line (%s).\n", client->hostname,
4579 strerror(httpError(client->http)));
4580
4581 return (0);
4582 }
4583 else if (http_state == HTTP_STATE_UNKNOWN_METHOD)
4584 {
4585 fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname);
4586 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
4587 return (0);
4588 }
4589 else if (http_state == HTTP_STATE_UNKNOWN_VERSION)
4590 {
4591 fprintf(stderr, "%s Bad HTTP version.\n", client->hostname);
4592 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
4593 return (0);
4594 }
4595
4596 fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state],
4597 uri);
4598
4599 /*
4600 * Separate the URI into its components...
4601 */
4602
4603 if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
4604 userpass, sizeof(userpass),
4605 hostname, sizeof(hostname), &port,
4606 client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK &&
4607 (http_state != HTTP_STATE_OPTIONS || strcmp(uri, "*")))
4608 {
4609 fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri);
4610 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
4611 return (0);
4612 }
4613
4614 if ((client->options = strchr(client->uri, '?')) != NULL)
4615 *(client->options)++ = '\0';
4616
4617 /*
4618 * Process the request...
4619 */
4620
4621 client->start = time(NULL);
4622 client->operation = httpGetState(client->http);
4623
4624 /*
4625 * Parse incoming parameters until the status changes...
4626 */
4627
4628 while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE);
4629
4630 if (http_status != HTTP_STATUS_OK)
4631 {
4632 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
4633 return (0);
4634 }
4635
4636 if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] &&
4637 httpGetVersion(client->http) >= HTTP_VERSION_1_1)
4638 {
4639 /*
4640 * HTTP/1.1 and higher require the "Host:" field...
4641 */
4642
4643 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
4644 return (0);
4645 }
4646
4647 /*
4648 * Handle HTTP Upgrade...
4649 */
4650
4651 if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION),
4652 "Upgrade"))
4653 {
4654 #ifdef HAVE_SSL
4655 if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http))
4656 {
4657 if (!respond_http(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0))
4658 return (0);
4659
4660 fprintf(stderr, "%s Upgrading to encrypted connection.\n", client->hostname);
4661
4662 if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED))
4663 {
4664 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
4665 return (0);
4666 }
4667
4668 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
4669 }
4670 else
4671 #endif /* HAVE_SSL */
4672
4673 if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
4674 return (0);
4675 }
4676
4677 /*
4678 * Handle HTTP Expect...
4679 */
4680
4681 if (httpGetExpect(client->http) &&
4682 (client->operation == HTTP_STATE_POST ||
4683 client->operation == HTTP_STATE_PUT))
4684 {
4685 if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
4686 {
4687 /*
4688 * Send 100-continue header...
4689 */
4690
4691 if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
4692 return (0);
4693 }
4694 else
4695 {
4696 /*
4697 * Send 417-expectation-failed header...
4698 */
4699
4700 if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
4701 return (0);
4702 }
4703 }
4704
4705 /*
4706 * Handle new transfers...
4707 */
4708
4709 encoding = httpGetContentEncoding(client->http);
4710
4711 switch (client->operation)
4712 {
4713 case HTTP_STATE_OPTIONS :
4714 /*
4715 * Do OPTIONS command...
4716 */
4717
4718 return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
4719
4720 case HTTP_STATE_HEAD :
4721 if (!strcmp(client->uri, "/icon.png"))
4722 return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
4723 else if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies"))
4724 return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
4725 else
4726 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
4727
4728 case HTTP_STATE_GET :
4729 if (!strcmp(client->uri, "/icon.png"))
4730 {
4731 /*
4732 * Send PNG icon file.
4733 */
4734
4735 int fd; /* Icon file */
4736 struct stat fileinfo; /* Icon file information */
4737 char buffer[4096]; /* Copy buffer */
4738 ssize_t bytes; /* Bytes */
4739
4740 fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon);
4741
4742 if (!stat(client->printer->icon, &fileinfo) &&
4743 (fd = open(client->printer->icon, O_RDONLY)) >= 0)
4744 {
4745 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png",
4746 (size_t)fileinfo.st_size))
4747 {
4748 close(fd);
4749 return (0);
4750 }
4751
4752 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
4753 httpWrite2(client->http, buffer, (size_t)bytes);
4754
4755 httpFlushWrite(client->http);
4756
4757 close(fd);
4758 }
4759 else
4760 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
4761 }
4762 else if (!strcmp(client->uri, "/"))
4763 {
4764 /*
4765 * Show web status page...
4766 */
4767
4768 _ipp_job_t *job; /* Current job */
4769 int i; /* Looping var */
4770 _ipp_preason_t reason; /* Current reason */
4771 static const char * const reasons[] =
4772 { /* Reason strings */
4773 "Other",
4774 "Cover Open",
4775 "Input Tray Missing",
4776 "Marker Supply Empty",
4777 "Marker Supply Low",
4778 "Marker Waste Almost Full",
4779 "Marker Waste Full",
4780 "Media Empty",
4781 "Media Jam",
4782 "Media Low",
4783 "Media Needed",
4784 "Moving to Paused",
4785 "Paused",
4786 "Spool Area Full",
4787 "Toner Empty",
4788 "Toner Low"
4789 };
4790
4791 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
4792 return (0);
4793
4794 html_header(client, client->printer->name);
4795 html_printf(client,
4796 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION ")</b></p>\n"
4797 "<p>%s, %d job(s).", client->printer->state == IPP_PSTATE_IDLE ? "Idle" : client->printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(client->printer->jobs));
4798 for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
4799 if (client->printer->state_reasons & reason)
4800 html_printf(client, "\n<br>&nbsp;&nbsp;&nbsp;&nbsp;%s", reasons[i]);
4801 html_printf(client, "</p>\n");
4802
4803 if (cupsArrayCount(client->printer->jobs) > 0)
4804 {
4805 _cupsRWLockRead(&(client->printer->rwlock));
4806
4807 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");
4808 for (job = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs); job; job = (_ipp_job_t *)cupsArrayNext(client->printer->jobs))
4809 {
4810 char when[256], /* When job queued/started/finished */
4811 hhmmss[64]; /* Time HH:MM:SS */
4812
4813 switch (job->state)
4814 {
4815 case IPP_JSTATE_PENDING :
4816 case IPP_JSTATE_HELD :
4817 snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss)));
4818 break;
4819 case IPP_JSTATE_PROCESSING :
4820 case IPP_JSTATE_STOPPED :
4821 snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss)));
4822 break;
4823 case IPP_JSTATE_ABORTED :
4824 snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
4825 break;
4826 case IPP_JSTATE_CANCELED :
4827 snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
4828 break;
4829 case IPP_JSTATE_COMPLETED :
4830 snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
4831 break;
4832 }
4833
4834 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);
4835 }
4836 html_printf(client, "</tbody></table>\n");
4837
4838 _cupsRWUnlock(&(client->printer->rwlock));
4839 }
4840 html_footer(client);
4841
4842 return (1);
4843 }
4844 else if (!strcmp(client->uri, "/media"))
4845 {
4846 /*
4847 * Show web media page...
4848 */
4849
4850 int i, /* Looping var */
4851 num_options; /* Number of form options */
4852 cups_option_t *options; /* Form options */
4853 static const char * const sizes[] =
4854 { /* Size strings */
4855 "ISO A4",
4856 "ISO A5",
4857 "ISO A6",
4858 "DL Envelope",
4859 "US Legal",
4860 "US Letter",
4861 "#10 Envelope",
4862 "3x5 Photo",
4863 "3.5x5 Photo",
4864 "4x6 Photo",
4865 "5x7 Photo"
4866 };
4867 static const char * const types[] =
4868 /* Type strings */
4869 {
4870 "Auto",
4871 "Cardstock",
4872 "Envelope",
4873 "Labels",
4874 "Other",
4875 "Glossy Photo",
4876 "High-Gloss Photo",
4877 "Matte Photo",
4878 "Satin Photo",
4879 "Semi-Gloss Photo",
4880 "Plain",
4881 "Letterhead",
4882 "Transparency"
4883 };
4884 static const int sheets[] = /* Number of sheets */
4885 {
4886 250,
4887 100,
4888 25,
4889 5,
4890 0
4891 };
4892
4893 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
4894 return (0);
4895
4896 html_header(client, client->printer->name);
4897
4898 if ((num_options = parse_options(client, &options)) > 0)
4899 {
4900 /*
4901 * WARNING: A real printer/server implementation MUST NOT implement
4902 * media updates via a GET request - GET requests are supposed to be
4903 * idempotent (without side-effects) and we obviously are not
4904 * authenticating access here. This form is provided solely to
4905 * enable testing and development!
4906 */
4907
4908 const char *val; /* Form value */
4909
4910 if ((val = cupsGetOption("main_size", num_options, options)) != NULL)
4911 client->printer->main_size = atoi(val);
4912 if ((val = cupsGetOption("main_type", num_options, options)) != NULL)
4913 client->printer->main_type = atoi(val);
4914 if ((val = cupsGetOption("main_level", num_options, options)) != NULL)
4915 client->printer->main_level = atoi(val);
4916
4917 if ((val = cupsGetOption("envelope_size", num_options, options)) != NULL)
4918 client->printer->envelope_size = atoi(val);
4919 if ((val = cupsGetOption("envelope_level", num_options, options)) != NULL)
4920 client->printer->envelope_level = atoi(val);
4921
4922 if ((val = cupsGetOption("photo_size", num_options, options)) != NULL)
4923 client->printer->photo_size = atoi(val);
4924 if ((val = cupsGetOption("photo_type", num_options, options)) != NULL)
4925 client->printer->photo_type = atoi(val);
4926 if ((val = cupsGetOption("photo_level", num_options, options)) != NULL)
4927 client->printer->photo_level = atoi(val);
4928
4929 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))
4930 client->printer->state_reasons |= _IPP_PREASON_MEDIA_LOW;
4931 else
4932 client->printer->state_reasons &= (_ipp_preason_t)~_IPP_PREASON_MEDIA_LOW;
4933
4934 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))
4935 {
4936 client->printer->state_reasons |= _IPP_PREASON_MEDIA_EMPTY;
4937 if (client->printer->active_job)
4938 client->printer->state_reasons |= _IPP_PREASON_MEDIA_NEEDED;
4939 }
4940 else
4941 client->printer->state_reasons &= (_ipp_preason_t)~(_IPP_PREASON_MEDIA_EMPTY | _IPP_PREASON_MEDIA_NEEDED);
4942
4943 html_printf(client, "<blockquote>Media updated.</blockquote>\n");
4944 }
4945
4946 html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
4947
4948 html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
4949 html_printf(client, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
4950 for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
4951 if (!strstr(sizes[i], "Envelope") && !strstr(sizes[i], "Photo"))
4952 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->main_size ? " selected" : "", sizes[i]);
4953 html_printf(client, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
4954 for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i ++)
4955 if (!strstr(types[i], "Photo"))
4956 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->main_type ? " selected" : "", types[i]);
4957 html_printf(client, "</select> <select name=\"main_level\">");
4958 for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
4959 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->main_level ? " selected" : "", sheets[i]);
4960 html_printf(client, "</select></td></tr>\n");
4961
4962 html_printf(client,
4963 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
4964 for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
4965 if (strstr(sizes[i], "Envelope"))
4966 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->envelope_size ? " selected" : "", sizes[i]);
4967 html_printf(client, "</select> <select name=\"envelope_level\">");
4968 for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
4969 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->envelope_level ? " selected" : "", sheets[i]);
4970 html_printf(client, "</select></td></tr>\n");
4971
4972 html_printf(client,
4973 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
4974 for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
4975 if (strstr(sizes[i], "Photo"))
4976 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->photo_size ? " selected" : "", sizes[i]);
4977 html_printf(client, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
4978 for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i ++)
4979 if (strstr(types[i], "Photo"))
4980 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->photo_type ? " selected" : "", types[i]);
4981 html_printf(client, "</select> <select name=\"photo_level\">");
4982 for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
4983 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->photo_level ? " selected" : "", sheets[i]);
4984 html_printf(client, "</select></td></tr>\n");
4985
4986 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
4987 html_footer(client);
4988
4989 return (1);
4990 }
4991 else if (!strcmp(client->uri, "/supplies"))
4992 {
4993 /*
4994 * Show web supplies page...
4995 */
4996
4997 int i, j, /* Looping vars */
4998 num_options; /* Number of form options */
4999 cups_option_t *options; /* Form options */
5000 static const int levels[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5001
5002 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
5003 return (0);
5004
5005 html_header(client, client->printer->name);
5006
5007 if ((num_options = parse_options(client, &options)) > 0)
5008 {
5009 /*
5010 * WARNING: A real printer/server implementation MUST NOT implement
5011 * supply updates via a GET request - GET requests are supposed to be
5012 * idempotent (without side-effects) and we obviously are not
5013 * authenticating access here. This form is provided solely to
5014 * enable testing and development!
5015 */
5016
5017 char name[64]; /* Form field */
5018 const char *val; /* Form value */
5019
5020 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);
5021
5022 for (i = 0; i < (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])); i ++)
5023 {
5024 snprintf(name, sizeof(name), "supply_%d", i);
5025 if ((val = cupsGetOption(name, num_options, options)) != NULL)
5026 {
5027 int level = client->printer->supplies[i] = atoi(val);
5028 /* New level */
5029
5030 if (i < 4)
5031 {
5032 if (level == 0)
5033 client->printer->state_reasons |= _IPP_PREASON_TONER_EMPTY;
5034 else if (level < 10)
5035 client->printer->state_reasons |= _IPP_PREASON_TONER_LOW;
5036 }
5037 else
5038 {
5039 if (level == 100)
5040 client->printer->state_reasons |= _IPP_PREASON_MARKER_WASTE_FULL;
5041 else if (level > 90)
5042 client->printer->state_reasons |= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL;
5043 }
5044 }
5045 }
5046
5047 html_printf(client, "<blockquote>Supplies updated.</blockquote>\n");
5048 }
5049
5050 html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
5051
5052 html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
5053 for (i = 0; i < (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])); i ++)
5054 {
5055 html_printf(client, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies[i], i);
5056 for (j = 0; j < (int)(sizeof(levels) / sizeof(levels[0])); j ++)
5057 html_printf(client, "<option value=\"%d\"%s>%d%%</option>", levels[j], levels[j] == client->printer->supplies[i] ? " selected" : "", levels[j]);
5058 html_printf(client, "</select></td></tr>\n");
5059 }
5060 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5061 html_footer(client);
5062
5063 return (1);
5064 }
5065 else
5066 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
5067 break;
5068
5069 case HTTP_STATE_POST :
5070 if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
5071 "application/ipp"))
5072 {
5073 /*
5074 * Not an IPP request...
5075 */
5076
5077 return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
5078 }
5079
5080 /*
5081 * Read the IPP request...
5082 */
5083
5084 client->request = ippNew();
5085
5086 while ((ipp_state = ippRead(client->http,
5087 client->request)) != IPP_STATE_DATA)
5088 {
5089 if (ipp_state == IPP_STATE_ERROR)
5090 {
5091 fprintf(stderr, "%s IPP read error (%s).\n", client->hostname,
5092 cupsLastErrorString());
5093 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5094 return (0);
5095 }
5096 }
5097
5098 /*
5099 * Now that we have the IPP request, process the request...
5100 */
5101
5102 return (process_ipp(client));
5103
5104 default :
5105 break; /* Anti-compiler-warning-code */
5106 }
5107
5108 return (1);
5109 }
5110
5111
5112 /*
5113 * 'process_ipp()' - Process an IPP request.
5114 */
5115
5116 static int /* O - 1 on success, 0 on error */
5117 process_ipp(_ipp_client_t *client) /* I - Client */
5118 {
5119 ipp_tag_t group; /* Current group tag */
5120 ipp_attribute_t *attr; /* Current attribute */
5121 ipp_attribute_t *charset; /* Character set attribute */
5122 ipp_attribute_t *language; /* Language attribute */
5123 ipp_attribute_t *uri; /* Printer URI attribute */
5124 int major, minor; /* Version number */
5125 const char *name; /* Name of attribute */
5126
5127
5128 debug_attributes("Request", client->request, 1);
5129
5130 /*
5131 * First build an empty response message for this request...
5132 */
5133
5134 client->operation_id = ippGetOperation(client->request);
5135 client->response = ippNewResponse(client->request);
5136
5137 /*
5138 * Then validate the request header and required attributes...
5139 */
5140
5141 major = ippGetVersion(client->request, &minor);
5142
5143 if (major < 1 || major > 2)
5144 {
5145 /*
5146 * Return an error, since we only support IPP 1.x and 2.x.
5147 */
5148
5149 respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED,
5150 "Bad request version number %d.%d.", major, minor);
5151 }
5152 else if (ippGetRequestId(client->request) <= 0)
5153 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.",
5154 ippGetRequestId(client->request));
5155 else if (!ippFirstAttribute(client->request))
5156 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5157 "No attributes in request.");
5158 else
5159 {
5160 /*
5161 * Make sure that the attributes are provided in the correct order and
5162 * don't repeat groups...
5163 */
5164
5165 for (attr = ippFirstAttribute(client->request),
5166 group = ippGetGroupTag(attr);
5167 attr;
5168 attr = ippNextAttribute(client->request))
5169 {
5170 if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
5171 {
5172 /*
5173 * Out of order; return an error...
5174 */
5175
5176 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5177 "Attribute groups are out of order (%x < %x).",
5178 ippGetGroupTag(attr), group);
5179 break;
5180 }
5181 else
5182 group = ippGetGroupTag(attr);
5183 }
5184
5185 if (!attr)
5186 {
5187 /*
5188 * Then make sure that the first three attributes are:
5189 *
5190 * attributes-charset
5191 * attributes-natural-language
5192 * printer-uri/job-uri
5193 */
5194
5195 attr = ippFirstAttribute(client->request);
5196 name = ippGetName(attr);
5197 if (attr && name && !strcmp(name, "attributes-charset") &&
5198 ippGetValueTag(attr) == IPP_TAG_CHARSET)
5199 charset = attr;
5200 else
5201 charset = NULL;
5202
5203 attr = ippNextAttribute(client->request);
5204 name = ippGetName(attr);
5205
5206 if (attr && name && !strcmp(name, "attributes-natural-language") &&
5207 ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
5208 language = attr;
5209 else
5210 language = NULL;
5211
5212 if ((attr = ippFindAttribute(client->request, "printer-uri",
5213 IPP_TAG_URI)) != NULL)
5214 uri = attr;
5215 else if ((attr = ippFindAttribute(client->request, "job-uri",
5216 IPP_TAG_URI)) != NULL)
5217 uri = attr;
5218 else
5219 uri = NULL;
5220
5221 if (charset &&
5222 strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
5223 strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
5224 {
5225 /*
5226 * Bad character set...
5227 */
5228
5229 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5230 "Unsupported character set \"%s\".",
5231 ippGetString(charset, 0, NULL));
5232 }
5233 else if (!charset || !language || !uri)
5234 {
5235 /*
5236 * Return an error, since attributes-charset,
5237 * attributes-natural-language, and printer-uri/job-uri are required
5238 * for all operations.
5239 */
5240
5241 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5242 "Missing required attributes.");
5243 }
5244 else
5245 {
5246 char scheme[32], /* URI scheme */
5247 userpass[32], /* Username/password in URI */
5248 host[256], /* Host name in URI */
5249 resource[256]; /* Resource path in URI */
5250 int port; /* Port number in URI */
5251
5252 name = ippGetName(uri);
5253
5254 if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
5255 scheme, sizeof(scheme),
5256 userpass, sizeof(userpass),
5257 host, sizeof(host), &port,
5258 resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
5259 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
5260 "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
5261 else if ((!strcmp(name, "job-uri") &&
5262 strncmp(resource, "/ipp/print/", 11)) ||
5263 (!strcmp(name, "printer-uri") &&
5264 strcmp(resource, "/ipp/print")))
5265 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
5266 name, ippGetString(uri, 0, NULL));
5267 else
5268 {
5269 /*
5270 * Try processing the operation...
5271 */
5272
5273 switch (ippGetOperation(client->request))
5274 {
5275 case IPP_OP_PRINT_JOB :
5276 ipp_print_job(client);
5277 break;
5278
5279 case IPP_OP_PRINT_URI :
5280 ipp_print_uri(client);
5281 break;
5282
5283 case IPP_OP_VALIDATE_JOB :
5284 ipp_validate_job(client);
5285 break;
5286
5287 case IPP_OP_CREATE_JOB :
5288 ipp_create_job(client);
5289 break;
5290
5291 case IPP_OP_SEND_DOCUMENT :
5292 ipp_send_document(client);
5293 break;
5294
5295 case IPP_OP_SEND_URI :
5296 ipp_send_uri(client);
5297 break;
5298
5299 case IPP_OP_CANCEL_JOB :
5300 ipp_cancel_job(client);
5301 break;
5302
5303 case IPP_OP_GET_JOB_ATTRIBUTES :
5304 ipp_get_job_attributes(client);
5305 break;
5306
5307 case IPP_OP_GET_JOBS :
5308 ipp_get_jobs(client);
5309 break;
5310
5311 case IPP_OP_GET_PRINTER_ATTRIBUTES :
5312 ipp_get_printer_attributes(client);
5313 break;
5314
5315 case IPP_OP_CLOSE_JOB :
5316 ipp_close_job(client);
5317 break;
5318
5319 case IPP_OP_IDENTIFY_PRINTER :
5320 ipp_identify_printer(client);
5321 break;
5322
5323 default :
5324 respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
5325 "Operation not supported.");
5326 break;
5327 }
5328 }
5329 }
5330 }
5331 }
5332
5333 /*
5334 * Send the HTTP header and return...
5335 */
5336
5337 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
5338 httpFlush(client->http); /* Flush trailing (junk) data */
5339
5340 return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
5341 ippLength(client->response)));
5342 }
5343
5344
5345 /*
5346 * 'process_job()' - Process a print job.
5347 */
5348
5349 static void * /* O - Thread exit status */
5350 process_job(_ipp_job_t *job) /* I - Job */
5351 {
5352 job->state = IPP_JSTATE_PROCESSING;
5353 job->printer->state = IPP_PSTATE_PROCESSING;
5354 job->processing = time(NULL);
5355
5356 while (job->printer->state_reasons & _IPP_PREASON_MEDIA_EMPTY)
5357 {
5358 job->printer->state_reasons |= _IPP_PREASON_MEDIA_NEEDED;
5359
5360 sleep(1);
5361 }
5362
5363 job->printer->state_reasons &= (_ipp_preason_t)~_IPP_PREASON_MEDIA_NEEDED;
5364
5365 if (job->printer->command)
5366 {
5367 /*
5368 * Execute a command with the job spool file and wait for it to complete...
5369 */
5370
5371 int pid, /* Process ID */
5372 status; /* Exit status */
5373 time_t start, /* Start time */
5374 end; /* End time */
5375 char *myargv[3], /* Command-line arguments */
5376 *myenvp[200]; /* Environment variables */
5377 int myenvc; /* Number of environment variables */
5378 ipp_attribute_t *attr; /* Job attribute */
5379 char val[1280], /* IPP_NAME=value */
5380 *valptr; /* Pointer into string */
5381
5382 fprintf(stderr, "Running command \"%s %s\".\n", job->printer->command,
5383 job->filename);
5384 time(&start);
5385
5386 /*
5387 * Setup the command-line arguments...
5388 */
5389
5390 myargv[0] = job->printer->command;
5391 myargv[1] = job->filename;
5392 myargv[2] = NULL;
5393
5394 /*
5395 * Copy the current environment, then add ENV variables for every Job
5396 * attribute...
5397 */
5398
5399 for (myenvc = 0; environ[myenvc] && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); myenvc ++)
5400 myenvp[myenvc] = strdup(environ[myenvc]);
5401
5402 for (attr = ippFirstAttribute(job->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->attrs))
5403 {
5404 /*
5405 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
5406 * value(s) from the attribute.
5407 */
5408
5409 const char *name = ippGetName(attr);
5410 if (!name)
5411 continue;
5412
5413 valptr = val;
5414 *valptr++ = 'I';
5415 *valptr++ = 'P';
5416 *valptr++ = 'P';
5417 *valptr++ = '_';
5418 while (*name && valptr < (val + sizeof(val) - 2))
5419 {
5420 if (*name == '-')
5421 *valptr++ = '_';
5422 else
5423 *valptr++ = (char)toupper(*name & 255);
5424
5425 name ++;
5426 }
5427 *valptr++ = '=';
5428 ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
5429
5430 myenvp[myenvc++] = strdup(val);
5431 }
5432 myenvp[myenvc] = NULL;
5433
5434 /*
5435 * Now run the program...
5436 */
5437
5438 #ifdef WIN32
5439 status = _spawnvpe(_P_WAIT, job->printer->command, myargv, myenvp);
5440 #else
5441 if ((pid = fork()) == 0)
5442 {
5443 /*
5444 * Child comes here...
5445 */
5446
5447 execve(job->printer->command, myargv, myenvp);
5448 exit(errno);
5449 }
5450 else if (pid < 0)
5451 {
5452 /*
5453 * Unable to fork process...
5454 */
5455
5456 perror("Unable to start job processing command");
5457 status = -1;
5458 }
5459 else
5460 {
5461 /*
5462 * Free memory used for environment...
5463 */
5464
5465 while (myenvc > 0)
5466 free(myenvp[-- myenvc]);
5467
5468 /*
5469 * Wait for child to complete...
5470 */
5471
5472 # ifdef HAVE_WAITPID
5473 while (waitpid(pid, &status, 0) < 0);
5474 # else
5475 while (wait(&status) < 0);
5476 # endif /* HAVE_WAITPID */
5477 }
5478 #endif /* WIN32 */
5479
5480 if (status)
5481 {
5482 #ifndef WIN32
5483 if (WIFEXITED(status))
5484 #endif /* !WIN32 */
5485 fprintf(stderr, "Command \"%s\" exited with status %d.\n",
5486 job->printer->command, WEXITSTATUS(status));
5487 #ifndef WIN32
5488 else
5489 fprintf(stderr, "Command \"%s\" terminated with signal %d.\n",
5490 job->printer->command, WTERMSIG(status));
5491 #endif /* !WIN32 */
5492 job->state = IPP_JSTATE_ABORTED;
5493 }
5494 else if (status < 0)
5495 job->state = IPP_JSTATE_ABORTED;
5496 else
5497 fprintf(stderr, "Command \"%s\" completed successfully.\n",
5498 job->printer->command);
5499
5500 /*
5501 * Make sure processing takes at least 5 seconds...
5502 */
5503
5504 time(&end);
5505 if ((end - start) < 5)
5506 sleep(5);
5507 }
5508 else
5509 {
5510 /*
5511 * Sleep for a random amount of time to simulate job processing.
5512 */
5513
5514 sleep((unsigned)(5 + (rand() % 11)));
5515 }
5516
5517 if (job->cancel)
5518 job->state = IPP_JSTATE_CANCELED;
5519 else if (job->state == IPP_JSTATE_PROCESSING)
5520 job->state = IPP_JSTATE_COMPLETED;
5521
5522 job->completed = time(NULL);
5523 job->printer->state = IPP_PSTATE_IDLE;
5524 job->printer->active_job = NULL;
5525
5526 return (NULL);
5527 }
5528
5529
5530 /*
5531 * 'register_printer()' - Register a printer object via Bonjour.
5532 */
5533
5534 static int /* O - 1 on success, 0 on error */
5535 register_printer(
5536 _ipp_printer_t *printer, /* I - Printer */
5537 const char *location, /* I - Location */
5538 const char *make, /* I - Manufacturer */
5539 const char *model, /* I - Model name */
5540 const char *formats, /* I - Supported formats */
5541 const char *adminurl, /* I - Web interface URL */
5542 const char *uuid, /* I - Printer UUID */
5543 int color, /* I - 1 = color, 0 = monochrome */
5544 int duplex, /* I - 1 = duplex, 0 = simplex */
5545 const char *subtype) /* I - Service subtype */
5546 {
5547 _ipp_txt_t ipp_txt; /* Bonjour IPP TXT record */
5548 #ifdef HAVE_DNSSD
5549 DNSServiceErrorType error; /* Error from Bonjour */
5550 char make_model[256],/* Make and model together */
5551 product[256], /* Product string */
5552 regtype[256]; /* Bonjour service type */
5553
5554
5555 /*
5556 * Build the TXT record for IPP...
5557 */
5558
5559 snprintf(make_model, sizeof(make_model), "%s %s", make, model);
5560 snprintf(product, sizeof(product), "(%s)", model);
5561
5562 TXTRecordCreate(&ipp_txt, 1024, NULL);
5563 TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print");
5564 TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(make_model),
5565 make_model);
5566 TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(adminurl),
5567 adminurl);
5568 if (*location)
5569 TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(location),
5570 location);
5571 TXTRecordSetValue(&ipp_txt, "product", (uint8_t)strlen(product),
5572 product);
5573 TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats),
5574 formats);
5575 TXTRecordSetValue(&ipp_txt, "Color", 1, color ? "T" : "F");
5576 TXTRecordSetValue(&ipp_txt, "Duplex", 1, duplex ? "T" : "F");
5577 TXTRecordSetValue(&ipp_txt, "usb_MFG", (uint8_t)strlen(make),
5578 make);
5579 TXTRecordSetValue(&ipp_txt, "usb_MDL", (uint8_t)strlen(model),
5580 model);
5581 TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(uuid), uuid);
5582 # ifdef HAVE_SSL
5583 TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2");
5584 # endif /* HAVE_SSL */
5585
5586 /*
5587 * Register the _printer._tcp (LPD) service type with a port number of 0 to
5588 * defend our service name but not actually support LPD...
5589 */
5590
5591 printer->printer_ref = DNSSDMaster;
5592
5593 if ((error = DNSServiceRegister(&(printer->printer_ref),
5594 kDNSServiceFlagsShareConnection,
5595 0 /* interfaceIndex */, printer->dnssd_name,
5596 "_printer._tcp", NULL /* domain */,
5597 NULL /* host */, 0 /* port */, 0 /* txtLen */,
5598 NULL /* txtRecord */,
5599 (DNSServiceRegisterReply)dnssd_callback,
5600 printer)) != kDNSServiceErr_NoError)
5601 {
5602 fprintf(stderr, "Unable to register \"%s._printer._tcp\": %d\n",
5603 printer->dnssd_name, error);
5604 return (0);
5605 }
5606
5607 /*
5608 * Then register the _ipp._tcp (IPP) service type with the real port number to
5609 * advertise our IPP printer...
5610 */
5611
5612 printer->ipp_ref = DNSSDMaster;
5613
5614 if (subtype && *subtype)
5615 snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtype);
5616 else
5617 strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
5618
5619 if ((error = DNSServiceRegister(&(printer->ipp_ref),
5620 kDNSServiceFlagsShareConnection,
5621 0 /* interfaceIndex */, printer->dnssd_name,
5622 regtype, NULL /* domain */,
5623 NULL /* host */, htons(printer->port),
5624 TXTRecordGetLength(&ipp_txt),
5625 TXTRecordGetBytesPtr(&ipp_txt),
5626 (DNSServiceRegisterReply)dnssd_callback,
5627 printer)) != kDNSServiceErr_NoError)
5628 {
5629 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
5630 printer->dnssd_name, regtype, error);
5631 return (0);
5632 }
5633
5634 # ifdef HAVE_SSL
5635 /*
5636 * Then register the _ipps._tcp (IPP) service type with the real port number to
5637 * advertise our IPPS printer...
5638 */
5639
5640 printer->ipps_ref = DNSSDMaster;
5641
5642 if (subtype && *subtype)
5643 snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtype);
5644 else
5645 strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
5646
5647 if ((error = DNSServiceRegister(&(printer->ipps_ref),
5648 kDNSServiceFlagsShareConnection,
5649 0 /* interfaceIndex */, printer->dnssd_name,
5650 regtype, NULL /* domain */,
5651 NULL /* host */, htons(printer->port),
5652 TXTRecordGetLength(&ipp_txt),
5653 TXTRecordGetBytesPtr(&ipp_txt),
5654 (DNSServiceRegisterReply)dnssd_callback,
5655 printer)) != kDNSServiceErr_NoError)
5656 {
5657 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
5658 printer->dnssd_name, regtype, error);
5659 return (0);
5660 }
5661 # endif /* HAVE_SSL */
5662
5663 /*
5664 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
5665 * real port number to advertise our IPP printer...
5666 */
5667
5668 printer->http_ref = DNSSDMaster;
5669
5670 if ((error = DNSServiceRegister(&(printer->http_ref),
5671 kDNSServiceFlagsShareConnection,
5672 0 /* interfaceIndex */, printer->dnssd_name,
5673 "_http._tcp,_printer", NULL /* domain */,
5674 NULL /* host */, htons(printer->port),
5675 0 /* txtLen */, NULL, /* txtRecord */
5676 (DNSServiceRegisterReply)dnssd_callback,
5677 printer)) != kDNSServiceErr_NoError)
5678 {
5679 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
5680 printer->dnssd_name, regtype, error);
5681 return (0);
5682 }
5683
5684 TXTRecordDeallocate(&ipp_txt);
5685
5686 #elif defined(HAVE_AVAHI)
5687 char temp[256]; /* Subtype service string */
5688
5689 /*
5690 * Create the TXT record...
5691 */
5692
5693 ipp_txt = NULL;
5694 ipp_txt = avahi_string_list_add_printf(ipp_txt, "rp=ipp/print");
5695 ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s %s", make, model);
5696 ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", adminurl);
5697 if (*location)
5698 ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", location);
5699 ipp_txt = avahi_string_list_add_printf(ipp_txt, "product=(%s)", model);
5700 ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
5701 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", color ? "T" : "F");
5702 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", duplex ? "T" : "F");
5703 ipp_txt = avahi_string_list_add_printf(ipp_txt, "usb_MFG=%s", make);
5704 ipp_txt = avahi_string_list_add_printf(ipp_txt, "usb_MDL=%s", model);
5705 ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", uuid);
5706 # ifdef HAVE_SSL
5707 ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2");
5708 # endif /* HAVE_SSL */
5709
5710 /*
5711 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
5712 */
5713
5714 avahi_threaded_poll_lock(DNSSDMaster);
5715
5716 printer->ipp_ref = avahi_entry_group_new(DNSSDClient, dnssd_callback, NULL);
5717
5718 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);
5719
5720 /*
5721 * Then register the _ipp._tcp (IPP)...
5722 */
5723
5724 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);
5725 if (subtype && *subtype)
5726 {
5727 snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", subtype);
5728 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp);
5729 }
5730
5731 #ifdef HAVE_SSL
5732 /*
5733 * _ipps._tcp (IPPS) for secure printing...
5734 */
5735
5736 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);
5737 if (subtype && *subtype)
5738 {
5739 snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", subtype);
5740 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp);
5741 }
5742 #endif /* HAVE_SSL */
5743
5744 /*
5745 * Finally _http.tcp (HTTP) for the web interface...
5746 */
5747
5748 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);
5749 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");
5750
5751 /*
5752 * Commit it...
5753 */
5754
5755 avahi_entry_group_commit(printer->ipp_ref);
5756 avahi_threaded_poll_unlock(DNSSDMaster);
5757
5758 avahi_string_list_free(ipp_txt);
5759 #endif /* HAVE_DNSSD */
5760
5761 return (1);
5762 }
5763
5764
5765 /*
5766 * 'respond_http()' - Send a HTTP response.
5767 */
5768
5769 int /* O - 1 on success, 0 on failure */
5770 respond_http(
5771 _ipp_client_t *client, /* I - Client */
5772 http_status_t code, /* I - HTTP status of response */
5773 const char *content_encoding, /* I - Content-Encoding of response */
5774 const char *type, /* I - MIME media type of response */
5775 size_t length) /* I - Length of response */
5776 {
5777 char message[1024]; /* Text message */
5778
5779
5780 fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
5781
5782 if (code == HTTP_STATUS_CONTINUE)
5783 {
5784 /*
5785 * 100-continue doesn't send any headers...
5786 */
5787
5788 return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
5789 }
5790
5791 /*
5792 * Format an error message...
5793 */
5794
5795 if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
5796 {
5797 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
5798
5799 type = "text/plain";
5800 length = strlen(message);
5801 }
5802 else
5803 message[0] = '\0';
5804
5805 /*
5806 * Send the HTTP response header...
5807 */
5808
5809 httpClearFields(client->http);
5810
5811 if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
5812 client->operation == HTTP_STATE_OPTIONS)
5813 httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
5814
5815 if (type)
5816 {
5817 if (!strcmp(type, "text/html"))
5818 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
5819 "text/html; charset=utf-8");
5820 else
5821 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
5822
5823 if (content_encoding)
5824 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
5825 }
5826
5827 httpSetLength(client->http, length);
5828
5829 if (httpWriteResponse(client->http, code) < 0)
5830 return (0);
5831
5832 /*
5833 * Send the response data...
5834 */
5835
5836 if (message[0])
5837 {
5838 /*
5839 * Send a plain text message.
5840 */
5841
5842 if (httpPrintf(client->http, "%s", message) < 0)
5843 return (0);
5844
5845 if (httpWrite2(client->http, "", 0) < 0)
5846 return (0);
5847 }
5848 else if (client->response)
5849 {
5850 /*
5851 * Send an IPP response...
5852 */
5853
5854 debug_attributes("Response", client->response, 2);
5855
5856 ippSetState(client->response, IPP_STATE_IDLE);
5857
5858 if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
5859 return (0);
5860 }
5861
5862 return (1);
5863 }
5864
5865
5866 /*
5867 * 'respond_ipp()' - Send an IPP response.
5868 */
5869
5870 static void
5871 respond_ipp(_ipp_client_t *client, /* I - Client */
5872 ipp_status_t status, /* I - status-code */
5873 const char *message, /* I - printf-style status-message */
5874 ...) /* I - Additional args as needed */
5875 {
5876 const char *formatted = NULL; /* Formatted message */
5877
5878
5879 ippSetStatusCode(client->response, status);
5880
5881 if (message)
5882 {
5883 va_list ap; /* Pointer to additional args */
5884 ipp_attribute_t *attr; /* New status-message attribute */
5885
5886 va_start(ap, message);
5887 if ((attr = ippFindAttribute(client->response, "status-message",
5888 IPP_TAG_TEXT)) != NULL)
5889 ippSetStringfv(client->response, &attr, 0, message, ap);
5890 else
5891 attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
5892 "status-message", NULL, message, ap);
5893 va_end(ap);
5894
5895 formatted = ippGetString(attr, 0, NULL);
5896 }
5897
5898 if (formatted)
5899 fprintf(stderr, "%s %s %s (%s)\n", client->hostname,
5900 ippOpString(client->operation_id), ippErrorString(status),
5901 formatted);
5902 else
5903 fprintf(stderr, "%s %s %s\n", client->hostname,
5904 ippOpString(client->operation_id), ippErrorString(status));
5905 }
5906
5907
5908 /*
5909 * 'respond_unsupported()' - Respond with an unsupported attribute.
5910 */
5911
5912 static void
5913 respond_unsupported(
5914 _ipp_client_t *client, /* I - Client */
5915 ipp_attribute_t *attr) /* I - Atribute */
5916 {
5917 ipp_attribute_t *temp; /* Copy of attribute */
5918
5919
5920 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
5921 "Unsupported %s %s%s value.", ippGetName(attr),
5922 ippGetCount(attr) > 1 ? "1setOf " : "",
5923 ippTagString(ippGetValueTag(attr)));
5924
5925 temp = ippCopyAttribute(client->response, attr, 0);
5926 ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
5927 }
5928
5929
5930 /*
5931 * 'run_printer()' - Run the printer service.
5932 */
5933
5934 static void
5935 run_printer(_ipp_printer_t *printer) /* I - Printer */
5936 {
5937 int num_fds; /* Number of file descriptors */
5938 struct pollfd polldata[3]; /* poll() data */
5939 int timeout; /* Timeout for poll() */
5940 _ipp_client_t *client; /* New client */
5941
5942
5943 /*
5944 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5945 */
5946
5947 polldata[0].fd = printer->ipv4;
5948 polldata[0].events = POLLIN;
5949
5950 polldata[1].fd = printer->ipv6;
5951 polldata[1].events = POLLIN;
5952
5953 num_fds = 2;
5954
5955 #ifdef HAVE_DNSSD
5956 polldata[num_fds ].fd = DNSServiceRefSockFD(DNSSDMaster);
5957 polldata[num_fds ++].events = POLLIN;
5958 #endif /* HAVE_DNSSD */
5959
5960 /*
5961 * Loop until we are killed or have a hard error...
5962 */
5963
5964 for (;;)
5965 {
5966 if (cupsArrayCount(printer->jobs))
5967 timeout = 10;
5968 else
5969 timeout = -1;
5970
5971 if (poll(polldata, (nfds_t)num_fds, timeout) < 0 && errno != EINTR)
5972 {
5973 perror("poll() failed");
5974 break;
5975 }
5976
5977 if (polldata[0].revents & POLLIN)
5978 {
5979 if ((client = create_client(printer, printer->ipv4)) != NULL)
5980 {
5981 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
5982 {
5983 perror("Unable to create client thread");
5984 delete_client(client);
5985 }
5986 }
5987 }
5988
5989 if (polldata[1].revents & POLLIN)
5990 {
5991 if ((client = create_client(printer, printer->ipv6)) != NULL)
5992 {
5993 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
5994 {
5995 perror("Unable to create client thread");
5996 delete_client(client);
5997 }
5998 }
5999 }
6000
6001 #ifdef HAVE_DNSSD
6002 if (polldata[2].revents & POLLIN)
6003 DNSServiceProcessResult(DNSSDMaster);
6004 #endif /* HAVE_DNSSD */
6005
6006 /*
6007 * Clean out old jobs...
6008 */
6009
6010 clean_jobs(printer);
6011 }
6012 }
6013
6014
6015 /*
6016 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6017 */
6018
6019 static char *
6020 time_string(time_t tv, /* I - Time value */
6021 char *buffer, /* I - Buffer */
6022 size_t bufsize) /* I - Size of buffer */
6023 {
6024 struct tm *curtime = localtime(&tv);
6025 /* Local time */
6026
6027 strftime(buffer, bufsize, "%X", curtime);
6028 return (buffer);
6029 }
6030
6031
6032 /*
6033 * 'usage()' - Show program usage.
6034 */
6035
6036 static void
6037 usage(int status) /* O - Exit status */
6038 {
6039 if (!status)
6040 {
6041 puts(CUPS_SVERSION " - Copyright 2010-2014 by Apple Inc. All rights "
6042 "reserved.");
6043 puts("");
6044 }
6045
6046 puts("Usage: ippserver [options] \"name\"");
6047 puts("");
6048 puts("Options:");
6049 puts("-2 Supports 2-sided printing (default=1-sided)");
6050 puts("-M manufacturer Manufacturer name (default=Test)");
6051 puts("-P PIN printing mode");
6052 puts("-c command Run command for every print job");
6053 printf("-d spool-directory Spool directory "
6054 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6055 puts("-f type/subtype[,...] List of supported types "
6056 "(default=application/pdf,image/jpeg)");
6057 puts("-h Show program help");
6058 puts("-i iconfile.png PNG icon file (default=printer.png)");
6059 puts("-k Keep job spool files");
6060 puts("-l location Location of printer (default=empty string)");
6061 puts("-m model Model name (default=Printer)");
6062 puts("-n hostname Hostname for printer");
6063 puts("-p port Port number (default=auto)");
6064 puts("-r subtype Bonjour service subtype (default=_print)");
6065 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6066 puts("-v[vvv] Be (very) verbose");
6067
6068 exit(status);
6069 }
6070
6071
6072 /*
6073 * 'valid_doc_attributes()' - Determine whether the document attributes are
6074 * valid.
6075 *
6076 * When one or more document attributes are invalid, this function adds a
6077 * suitable response and attributes to the unsupported group.
6078 */
6079
6080 static int /* O - 1 if valid, 0 if not */
6081 valid_doc_attributes(
6082 _ipp_client_t *client) /* I - Client */
6083 {
6084 int valid = 1; /* Valid attributes? */
6085 ipp_op_t op = ippGetOperation(client->request);
6086 /* IPP operation */
6087 const char *op_name = ippOpString(op);
6088 /* IPP operation name */
6089 ipp_attribute_t *attr, /* Current attribute */
6090 *supported; /* xxx-supported attribute */
6091 const char *compression = NULL,
6092 /* compression value */
6093 *format = NULL; /* document-format value */
6094
6095
6096 /*
6097 * Check operation attributes...
6098 */
6099
6100 if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
6101 {
6102 /*
6103 * If compression is specified, only accept a supported value in a Print-Job
6104 * or Send-Document request...
6105 */
6106
6107 compression = ippGetString(attr, 0, NULL);
6108 supported = ippFindAttribute(client->printer->attrs,
6109 "compression-supported", IPP_TAG_KEYWORD);
6110
6111 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
6112 ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
6113 (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
6114 op != IPP_OP_VALIDATE_JOB) ||
6115 !ippContainsString(supported, compression))
6116 {
6117 respond_unsupported(client, attr);
6118 valid = 0;
6119 }
6120 else
6121 {
6122 fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression);
6123
6124 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression);
6125
6126 if (strcmp(compression, "none"))
6127 {
6128 if (Verbosity)
6129 fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression);
6130 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
6131 }
6132 }
6133 }
6134
6135 /*
6136 * Is it a format we support?
6137 */
6138
6139 if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
6140 {
6141 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
6142 ippGetGroupTag(attr) != IPP_TAG_OPERATION)
6143 {
6144 respond_unsupported(client, attr);
6145 valid = 0;
6146 }
6147 else
6148 {
6149 format = ippGetString(attr, 0, NULL);
6150
6151 fprintf(stderr, "%s %s document-format=\"%s\"\n",
6152 client->hostname, op_name, format);
6153
6154 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format);
6155 }
6156 }
6157 else
6158 {
6159 format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL);
6160 if (!format)
6161 format = "application/octet-stream"; /* Should never happen */
6162
6163 attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
6164 }
6165
6166 if (!strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
6167 {
6168 /*
6169 * Auto-type the file using the first 8 bytes of the file...
6170 */
6171
6172 unsigned char header[8]; /* First 8 bytes of file */
6173
6174 memset(header, 0, sizeof(header));
6175 httpPeek(client->http, (char *)header, sizeof(header));
6176
6177 if (!memcmp(header, "%PDF", 4))
6178 format = "application/pdf";
6179 else if (!memcmp(header, "%!", 2))
6180 format = "application/postscript";
6181 else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
6182 format = "image/jpeg";
6183 else if (!memcmp(header, "\211PNG", 4))
6184 format = "image/png";
6185 else if (!memcmp(header, "RAS2", 4))
6186 format = "image/pwg-raster";
6187 else if (!memcmp(header, "UNIRAST", 8))
6188 format = "image/urf";
6189 else
6190 format = NULL;
6191
6192 if (format)
6193 {
6194 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n",
6195 client->hostname, op_name, format);
6196
6197 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
6198 }
6199 }
6200
6201 if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
6202 {
6203 respond_unsupported(client, attr);
6204 valid = 0;
6205 }
6206
6207 /*
6208 * document-name
6209 */
6210
6211 if ((attr = ippFindAttribute(client->request, "document-name", IPP_TAG_NAME)) != NULL)
6212 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
6213
6214 return (valid);
6215 }
6216
6217
6218 /*
6219 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6220 *
6221 * When one or more job attributes are invalid, this function adds a suitable
6222 * response and attributes to the unsupported group.
6223 */
6224
6225 static int /* O - 1 if valid, 0 if not */
6226 valid_job_attributes(
6227 _ipp_client_t *client) /* I - Client */
6228 {
6229 int i, /* Looping var */
6230 valid = 1; /* Valid attributes? */
6231 ipp_attribute_t *attr, /* Current attribute */
6232 *supported; /* xxx-supported attribute */
6233
6234
6235 /*
6236 * Check operation attributes...
6237 */
6238
6239 valid = valid_doc_attributes(client);
6240
6241 /*
6242 * Check the various job template attributes...
6243 */
6244
6245 if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL)
6246 {
6247 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
6248 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
6249 {
6250 respond_unsupported(client, attr);
6251 valid = 0;
6252 }
6253 }
6254
6255 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
6256 {
6257 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
6258 {
6259 respond_unsupported(client, attr);
6260 valid = 0;
6261 }
6262 }
6263
6264 if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
6265 {
6266 if (ippGetCount(attr) != 1 ||
6267 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6268 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
6269 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
6270 strcmp(ippGetString(attr, 0, NULL), "no-hold"))
6271 {
6272 respond_unsupported(client, attr);
6273 valid = 0;
6274 }
6275 }
6276
6277 if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL)
6278 {
6279 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0)
6280 {
6281 respond_unsupported(client, attr);
6282 valid = 0;
6283 }
6284 }
6285
6286 if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL)
6287 {
6288 if (ippGetCount(attr) != 1 ||
6289 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6290 ippGetValueTag(attr) != IPP_TAG_NAMELANG))
6291 {
6292 respond_unsupported(client, attr);
6293 valid = 0;
6294 }
6295
6296 ippSetGroupTag(client->request, &attr, IPP_TAG_JOB);
6297 }
6298 else
6299 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
6300
6301 if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL)
6302 {
6303 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
6304 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
6305 {
6306 respond_unsupported(client, attr);
6307 valid = 0;
6308 }
6309 }
6310
6311 if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
6312 {
6313 if (ippGetCount(attr) != 1 ||
6314 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6315 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
6316 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
6317 strcmp(ippGetString(attr, 0, NULL), "none"))
6318 {
6319 respond_unsupported(client, attr);
6320 valid = 0;
6321 }
6322 }
6323
6324 if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
6325 {
6326 if (ippGetCount(attr) != 1 ||
6327 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6328 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
6329 ippGetValueTag(attr) != IPP_TAG_KEYWORD))
6330 {
6331 respond_unsupported(client, attr);
6332 valid = 0;
6333 }
6334 else
6335 {
6336 for (i = 0;
6337 i < (int)(sizeof(media_supported) / sizeof(media_supported[0]));
6338 i ++)
6339 if (!strcmp(ippGetString(attr, 0, NULL), media_supported[i]))
6340 break;
6341
6342 if (i >= (int)(sizeof(media_supported) / sizeof(media_supported[0])))
6343 {
6344 respond_unsupported(client, attr);
6345 valid = 0;
6346 }
6347 }
6348 }
6349
6350 if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL)
6351 {
6352 if (ippGetCount(attr) != 1 ||
6353 ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
6354 {
6355 respond_unsupported(client, attr);
6356 valid = 0;
6357 }
6358 /* TODO: check for valid media-col */
6359 }
6360
6361 if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL)
6362 {
6363 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
6364 (strcmp(ippGetString(attr, 0, NULL),
6365 "separate-documents-uncollated-copies") &&
6366 strcmp(ippGetString(attr, 0, NULL),
6367 "separate-documents-collated-copies")))
6368 {
6369 respond_unsupported(client, attr);
6370 valid = 0;
6371 }
6372 }
6373
6374 if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL)
6375 {
6376 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
6377 ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
6378 ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
6379 {
6380 respond_unsupported(client, attr);
6381 valid = 0;
6382 }
6383 }
6384
6385 if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
6386 {
6387 if (ippGetValueTag(attr) != IPP_TAG_RANGE)
6388 {
6389 respond_unsupported(client, attr);
6390 valid = 0;
6391 }
6392 }
6393
6394 if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL)
6395 {
6396 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
6397 ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
6398 ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
6399 {
6400 respond_unsupported(client, attr);
6401 valid = 0;
6402 }
6403 }
6404
6405 if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
6406 {
6407 supported = ippFindAttribute(client->printer->attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION);
6408
6409 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION ||
6410 !supported)
6411 {
6412 respond_unsupported(client, attr);
6413 valid = 0;
6414 }
6415 else
6416 {
6417 int count, /* Number of supported values */
6418 xdpi, /* Horizontal resolution for job template attribute */
6419 ydpi, /* Vertical resolution for job template attribute */
6420 sydpi; /* Vertical resolution for supported value */
6421 ipp_res_t units, /* Units for job template attribute */
6422 sunits; /* Units for supported value */
6423
6424 xdpi = ippGetResolution(attr, 0, &ydpi, &units);
6425 count = ippGetCount(supported);
6426
6427 for (i = 0; i < count; i ++)
6428 {
6429 if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits)
6430 break;
6431 }
6432
6433 if (i >= count)
6434 {
6435 respond_unsupported(client, attr);
6436 valid = 0;
6437 }
6438 }
6439 }
6440
6441 if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
6442 {
6443 const char *sides = ippGetString(attr, 0, NULL);
6444 /* "sides" value... */
6445
6446 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
6447 {
6448 respond_unsupported(client, attr);
6449 valid = 0;
6450 }
6451 else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
6452 {
6453 if (!ippContainsString(supported, sides))
6454 {
6455 respond_unsupported(client, attr);
6456 valid = 0;
6457 }
6458 }
6459 else if (strcmp(sides, "one-sided"))
6460 {
6461 respond_unsupported(client, attr);
6462 valid = 0;
6463 }
6464 }
6465
6466 return (valid);
6467 }
6468
6469
6470 /*
6471 * End of "$Id$".
6472 */