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