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