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