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