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