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