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