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