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