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