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