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