]> git.ipfire.org Git - thirdparty/cups.git/blame - test/ippeveprinter.c
Update supply and media pages to be based on attributes instead of member
[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);
aa2a90ce 238static int create_job_file(ippeve_printer_t *printer, ippeve_job_t *job, char *fname, size_t fnamesize, 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
MS
259static void html_footer(ippeve_client_t *client);
260static void html_header(ippeve_client_t *client, const char *title);
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
MS
1039 ippeve_printer_t *printer, /* I - Printer */
1040 ippeve_job_t *job, /* I - Job */
aa2a90ce
MS
1041 char *fname, /* I - Filename buffer */
1042 size_t fnamesize, /* I - Size of filename buffer */
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
1102 snprintf(fname, fnamesize, "%s/%d-%s.%s", printer->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
2096 if ((job->fd = create_job_file(client->printer, job, filename, sizeof(filename), NULL)) < 0)
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
2309 if ((job->fd = create_job_file(client->printer, job, filename, sizeof(filename), NULL)) < 0)
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 */
0b5ce83f
MS
2553 const char *title) /* I - Title */
2554{
2555 html_printf(client,
2556 "<!doctype html>\n"
2557 "<html>\n"
2558 "<head>\n"
2559 "<title>%s</title>\n"
2560 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
04f71e64 2561 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
0b5ce83f
MS
2562 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2563 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2564 "<style>\n"
2565 "body { font-family: sans-serif; margin: 0; }\n"
2566 "div.body { padding: 0px 10px 10px; }\n"
2567 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2568 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2569 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2570 "table.form th { text-align: right; }\n"
2571 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2572 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2573 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2574 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2575 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2576 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2577 "table.nav td { margin: 0; text-align: center; }\n"
2578 "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"
2579 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2580 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2581 "td.nav:hover { background: #666; color: #fff; }\n"
2582 "td.nav:active { background: #000; color: #ff0; }\n"
2583 "</style>\n"
2584 "</head>\n"
2585 "<body>\n"
2586 "<table class=\"nav\"><tr>"
2587 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2588 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2589 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2590 "</tr></table>\n"
2591 "<div class=\"body\">\n", title, !strcmp(client->uri, "/") ? " sel" : "", !strcmp(client->uri, "/supplies") ? " sel" : "", !strcmp(client->uri, "/media") ? " sel" : "");
2592}
2593
2594
1106b00e
MS
2595/*
2596 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2597 */
2598
2599static void
d46dbe1b 2600html_printf(ippeve_client_t *client, /* I - Client */
1106b00e
MS
2601 const char *format, /* I - Printf-style format string */
2602 ...) /* I - Additional arguments as needed */
2603{
2604 va_list ap; /* Pointer to arguments */
2605 const char *start; /* Start of string */
2606 char size, /* Size character (h, l, L) */
2607 type; /* Format type character */
2608 int width, /* Width of field */
2609 prec; /* Number of characters of precision */
2610 char tformat[100], /* Temporary format string for sprintf() */
2611 *tptr, /* Pointer into temporary format */
2612 temp[1024]; /* Buffer for formatted numbers */
2613 char *s; /* Pointer to string */
2614
2615
2616 /*
2617 * Loop through the format string, formatting as needed...
2618 */
2619
2620 va_start(ap, format);
2621 start = format;
2622
2623 while (*format)
2624 {
2625 if (*format == '%')
2626 {
2627 if (format > start)
7e86f2f6 2628 httpWrite2(client->http, start, (size_t)(format - start));
1106b00e
MS
2629
2630 tptr = tformat;
2631 *tptr++ = *format++;
2632
2633 if (*format == '%')
2634 {
a469f8a5 2635 httpWrite2(client->http, "%", 1);
1106b00e 2636 format ++;
0b5ce83f 2637 start = format;
1106b00e
MS
2638 continue;
2639 }
2640 else if (strchr(" -+#\'", *format))
2641 *tptr++ = *format++;
2642
2643 if (*format == '*')
2644 {
2645 /*
2646 * Get width from argument...
2647 */
2648
2649 format ++;
2650 width = va_arg(ap, int);
2651
7e86f2f6 2652 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
1106b00e
MS
2653 tptr += strlen(tptr);
2654 }
2655 else
2656 {
2657 width = 0;
2658
2659 while (isdigit(*format & 255))
2660 {
2661 if (tptr < (tformat + sizeof(tformat) - 1))
2662 *tptr++ = *format;
2663
2664 width = width * 10 + *format++ - '0';
2665 }
2666 }
2667
2668 if (*format == '.')
2669 {
2670 if (tptr < (tformat + sizeof(tformat) - 1))
2671 *tptr++ = *format;
2672
2673 format ++;
2674
2675 if (*format == '*')
2676 {
2677 /*
2678 * Get precision from argument...
2679 */
2680
2681 format ++;
2682 prec = va_arg(ap, int);
2683
7e86f2f6 2684 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
1106b00e
MS
2685 tptr += strlen(tptr);
2686 }
2687 else
2688 {
2689 prec = 0;
2690
2691 while (isdigit(*format & 255))
2692 {
2693 if (tptr < (tformat + sizeof(tformat) - 1))
2694 *tptr++ = *format;
2695
2696 prec = prec * 10 + *format++ - '0';
2697 }
2698 }
2699 }
2700
2701 if (*format == 'l' && format[1] == 'l')
2702 {
2703 size = 'L';
2704
2705 if (tptr < (tformat + sizeof(tformat) - 2))
2706 {
2707 *tptr++ = 'l';
2708 *tptr++ = 'l';
2709 }
2710
2711 format += 2;
2712 }
2713 else if (*format == 'h' || *format == 'l' || *format == 'L')
2714 {
2715 if (tptr < (tformat + sizeof(tformat) - 1))
2716 *tptr++ = *format;
2717
2718 size = *format++;
2719 }
2720 else
2721 size = 0;
2722
2723
2724 if (!*format)
2725 {
2726 start = format;
2727 break;
2728 }
2729
2730 if (tptr < (tformat + sizeof(tformat) - 1))
2731 *tptr++ = *format;
2732
2733 type = *format++;
2734 *tptr = '\0';
2735 start = format;
2736
2737 switch (type)
2738 {
2739 case 'E' : /* Floating point formats */
2740 case 'G' :
2741 case 'e' :
2742 case 'f' :
2743 case 'g' :
7e86f2f6 2744 if ((size_t)(width + 2) > sizeof(temp))
1106b00e
MS
2745 break;
2746
2747 sprintf(temp, tformat, va_arg(ap, double));
2748
a469f8a5 2749 httpWrite2(client->http, temp, strlen(temp));
1106b00e
MS
2750 break;
2751
2752 case 'B' : /* Integer formats */
2753 case 'X' :
2754 case 'b' :
2755 case 'd' :
2756 case 'i' :
2757 case 'o' :
2758 case 'u' :
2759 case 'x' :
7e86f2f6 2760 if ((size_t)(width + 2) > sizeof(temp))
1106b00e
MS
2761 break;
2762
2763# ifdef HAVE_LONG_LONG
2764 if (size == 'L')
2765 sprintf(temp, tformat, va_arg(ap, long long));
2766 else
2767# endif /* HAVE_LONG_LONG */
2768 if (size == 'l')
2769 sprintf(temp, tformat, va_arg(ap, long));
2770 else
2771 sprintf(temp, tformat, va_arg(ap, int));
2772
a469f8a5 2773 httpWrite2(client->http, temp, strlen(temp));
1106b00e
MS
2774 break;
2775
2776 case 'p' : /* Pointer value */
7e86f2f6 2777 if ((size_t)(width + 2) > sizeof(temp))
1106b00e
MS
2778 break;
2779
2780 sprintf(temp, tformat, va_arg(ap, void *));
2781
a469f8a5 2782 httpWrite2(client->http, temp, strlen(temp));
1106b00e
MS
2783 break;
2784
2785 case 'c' : /* Character or character array */
2786 if (width <= 1)
2787 {
7e86f2f6 2788 temp[0] = (char)va_arg(ap, int);
1106b00e
MS
2789 temp[1] = '\0';
2790 html_escape(client, temp, 1);
2791 }
2792 else
2793 html_escape(client, va_arg(ap, char *), (size_t)width);
2794 break;
2795
2796 case 's' : /* String */
2797 if ((s = va_arg(ap, char *)) == NULL)
2798 s = "(null)";
2799
2800 html_escape(client, s, strlen(s));
2801 break;
2802 }
2803 }
2804 else
2805 format ++;
2806 }
2807
2808 if (format > start)
7e86f2f6 2809 httpWrite2(client->http, start, (size_t)(format - start));
1106b00e
MS
2810
2811 va_end(ap);
2812}
2813
2814
2815/*
2816 * 'ipp_cancel_job()' - Cancel a job.
2817 */
2818
2819static void
d46dbe1b 2820ipp_cancel_job(ippeve_client_t *client) /* I - Client */
1106b00e 2821{
d46dbe1b 2822 ippeve_job_t *job; /* Job information */
1106b00e
MS
2823
2824
2825 /*
2826 * Get the job...
2827 */
2828
2829 if ((job = find_job(client)) == NULL)
83e08001 2830 {
a469f8a5 2831 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
1106b00e 2832 return;
83e08001 2833 }
1106b00e
MS
2834
2835 /*
2836 * See if the job is already completed, canceled, or aborted; if so,
2837 * we can't cancel...
2838 */
2839
2840 switch (job->state)
2841 {
a469f8a5
MS
2842 case IPP_JSTATE_CANCELED :
2843 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
1106b00e
MS
2844 "Job #%d is already canceled - can\'t cancel.", job->id);
2845 break;
2846
a469f8a5
MS
2847 case IPP_JSTATE_ABORTED :
2848 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
1106b00e
MS
2849 "Job #%d is already aborted - can\'t cancel.", job->id);
2850 break;
2851
a469f8a5
MS
2852 case IPP_JSTATE_COMPLETED :
2853 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
1106b00e
MS
2854 "Job #%d is already completed - can\'t cancel.", job->id);
2855 break;
2856
2857 default :
2858 /*
2859 * Cancel the job...
2860 */
2861
2862 _cupsRWLockWrite(&(client->printer->rwlock));
2863
a469f8a5
MS
2864 if (job->state == IPP_JSTATE_PROCESSING ||
2865 (job->state == IPP_JSTATE_HELD && job->fd >= 0))
1106b00e
MS
2866 job->cancel = 1;
2867 else
2868 {
a469f8a5 2869 job->state = IPP_JSTATE_CANCELED;
1106b00e
MS
2870 job->completed = time(NULL);
2871 }
2872
2873 _cupsRWUnlock(&(client->printer->rwlock));
2874
a469f8a5 2875 respond_ipp(client, IPP_STATUS_OK, NULL);
1106b00e
MS
2876 break;
2877 }
2878}
2879
2880
2cadf0f4
MS
2881/*
2882 * 'ipp_close_job()' - Close an open job.
2883 */
2884
2885static void
d46dbe1b 2886ipp_close_job(ippeve_client_t *client) /* I - Client */
2cadf0f4 2887{
d46dbe1b 2888 ippeve_job_t *job; /* Job information */
2cadf0f4
MS
2889
2890
2891 /*
2892 * Get the job...
2893 */
2894
2895 if ((job = find_job(client)) == NULL)
2896 {
2897 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
2898 return;
2899 }
2900
2901 /*
2902 * See if the job is already completed, canceled, or aborted; if so,
2903 * we can't cancel...
2904 */
2905
2906 switch (job->state)
2907 {
2908 case IPP_JSTATE_CANCELED :
2909 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2910 "Job #%d is canceled - can\'t close.", job->id);
2911 break;
2912
2913 case IPP_JSTATE_ABORTED :
2914 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2915 "Job #%d is aborted - can\'t close.", job->id);
2916 break;
2917
2918 case IPP_JSTATE_COMPLETED :
2919 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2920 "Job #%d is completed - can\'t close.", job->id);
2921 break;
2922
2923 case IPP_JSTATE_PROCESSING :
2924 case IPP_JSTATE_STOPPED :
2925 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2926 "Job #%d is already closed.", job->id);
2927 break;
2928
2929 default :
2930 respond_ipp(client, IPP_STATUS_OK, NULL);
2931 break;
2932 }
2933}
2934
2935
1106b00e
MS
2936/*
2937 * 'ipp_create_job()' - Create a job object.
2938 */
2939
2940static void
d46dbe1b 2941ipp_create_job(ippeve_client_t *client) /* I - Client */
1106b00e 2942{
d46dbe1b 2943 ippeve_job_t *job; /* New job */
83e08001
MS
2944 cups_array_t *ra; /* Attributes to send in response */
2945
2946
2947 /*
2948 * Validate print job attributes...
2949 */
2950
2951 if (!valid_job_attributes(client))
2952 {
a469f8a5 2953 httpFlush(client->http);
83e08001
MS
2954 return;
2955 }
2956
2957 /*
2958 * Do we have a file to print?
2959 */
2960
a469f8a5 2961 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
83e08001 2962 {
a469f8a5 2963 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
83e08001
MS
2964 "Unexpected document data following request.");
2965 return;
2966 }
2967
2968 /*
2969 * Create the job...
2970 */
2971
2972 if ((job = create_job(client)) == NULL)
2973 {
a469f8a5
MS
2974 respond_ipp(client, IPP_STATUS_ERROR_BUSY,
2975 "Currently printing another job.");
83e08001
MS
2976 return;
2977 }
2978
2979 /*
2980 * Return the job info...
2981 */
2982
a469f8a5 2983 respond_ipp(client, IPP_STATUS_OK, NULL);
83e08001
MS
2984
2985 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2986 cupsArrayAdd(ra, "job-id");
2987 cupsArrayAdd(ra, "job-state");
c2c30ebc 2988 cupsArrayAdd(ra, "job-state-message");
83e08001
MS
2989 cupsArrayAdd(ra, "job-state-reasons");
2990 cupsArrayAdd(ra, "job-uri");
2991
2992 copy_job_attributes(client, job, ra);
2993 cupsArrayDelete(ra);
1106b00e 2994}
1106b00e
MS
2995
2996
2997/*
2998 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2999 */
3000
3001static void
3002ipp_get_job_attributes(
d46dbe1b 3003 ippeve_client_t *client) /* I - Client */
1106b00e 3004{
d46dbe1b 3005 ippeve_job_t *job; /* Job */
1106b00e
MS
3006 cups_array_t *ra; /* requested-attributes */
3007
3008
3009 if ((job = find_job(client)) == NULL)
3010 {
a469f8a5 3011 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job not found.");
1106b00e
MS
3012 return;
3013 }
3014
a469f8a5 3015 respond_ipp(client, IPP_STATUS_OK, NULL);
1106b00e 3016
db8b865d 3017 ra = ippCreateRequestedArray(client->request);
1106b00e
MS
3018 copy_job_attributes(client, job, ra);
3019 cupsArrayDelete(ra);
3020}
3021
3022
3023/*
3024 * 'ipp_get_jobs()' - Get a list of job objects.
3025 */
3026
3027static void
d46dbe1b 3028ipp_get_jobs(ippeve_client_t *client) /* I - Client */
1106b00e
MS
3029{
3030 ipp_attribute_t *attr; /* Current attribute */
a469f8a5
MS
3031 const char *which_jobs = NULL;
3032 /* which-jobs values */
1106b00e
MS
3033 int job_comparison; /* Job comparison */
3034 ipp_jstate_t job_state; /* job-state value */
3035 int first_job_id, /* First job ID */
3036 limit, /* Maximum number of jobs to return */
3037 count; /* Number of jobs that match */
3038 const char *username; /* Username */
d46dbe1b 3039 ippeve_job_t *job; /* Current job pointer */
1106b00e
MS
3040 cups_array_t *ra; /* Requested attributes array */
3041
3042
3043 /*
3044 * See if the "which-jobs" attribute have been specified...
3045 */
3046
3047 if ((attr = ippFindAttribute(client->request, "which-jobs",
3048 IPP_TAG_KEYWORD)) != NULL)
a469f8a5
MS
3049 {
3050 which_jobs = ippGetString(attr, 0, NULL);
3051 fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->hostname, which_jobs);
3052 }
1106b00e 3053
a469f8a5 3054 if (!which_jobs || !strcmp(which_jobs, "not-completed"))
1106b00e
MS
3055 {
3056 job_comparison = -1;
a469f8a5 3057 job_state = IPP_JSTATE_STOPPED;
1106b00e 3058 }
a469f8a5 3059 else if (!strcmp(which_jobs, "completed"))
1106b00e
MS
3060 {
3061 job_comparison = 1;
a469f8a5 3062 job_state = IPP_JSTATE_CANCELED;
1106b00e 3063 }
a469f8a5 3064 else if (!strcmp(which_jobs, "aborted"))
1106b00e
MS
3065 {
3066 job_comparison = 0;
a469f8a5 3067 job_state = IPP_JSTATE_ABORTED;
1106b00e 3068 }
a469f8a5 3069 else if (!strcmp(which_jobs, "all"))
1106b00e
MS
3070 {
3071 job_comparison = 1;
a469f8a5 3072 job_state = IPP_JSTATE_PENDING;
1106b00e 3073 }
a469f8a5 3074 else if (!strcmp(which_jobs, "canceled"))
1106b00e
MS
3075 {
3076 job_comparison = 0;
a469f8a5 3077 job_state = IPP_JSTATE_CANCELED;
1106b00e 3078 }
a469f8a5 3079 else if (!strcmp(which_jobs, "pending"))
1106b00e
MS
3080 {
3081 job_comparison = 0;
a469f8a5 3082 job_state = IPP_JSTATE_PENDING;
1106b00e 3083 }
a469f8a5 3084 else if (!strcmp(which_jobs, "pending-held"))
1106b00e
MS
3085 {
3086 job_comparison = 0;
a469f8a5 3087 job_state = IPP_JSTATE_HELD;
1106b00e 3088 }
a469f8a5 3089 else if (!strcmp(which_jobs, "processing"))
1106b00e
MS
3090 {
3091 job_comparison = 0;
a469f8a5 3092 job_state = IPP_JSTATE_PROCESSING;
1106b00e 3093 }
a469f8a5 3094 else if (!strcmp(which_jobs, "processing-stopped"))
1106b00e
MS
3095 {
3096 job_comparison = 0;
a469f8a5 3097 job_state = IPP_JSTATE_STOPPED;
1106b00e
MS
3098 }
3099 else
3100 {
a469f8a5
MS
3101 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
3102 "The which-jobs value \"%s\" is not supported.", which_jobs);
1106b00e 3103 ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
a469f8a5 3104 "which-jobs", NULL, which_jobs);
1106b00e
MS
3105 return;
3106 }
3107
3108 /*
3109 * See if they want to limit the number of jobs reported...
3110 */
3111
3112 if ((attr = ippFindAttribute(client->request, "limit",
3113 IPP_TAG_INTEGER)) != NULL)
3114 {
a469f8a5 3115 limit = ippGetInteger(attr, 0);
1106b00e 3116
a469f8a5 3117 fprintf(stderr, "%s Get-Jobs limit=%d", client->hostname, limit);
1106b00e
MS
3118 }
3119 else
3120 limit = 0;
3121
3122 if ((attr = ippFindAttribute(client->request, "first-job-id",
3123 IPP_TAG_INTEGER)) != NULL)
3124 {
a469f8a5 3125 first_job_id = ippGetInteger(attr, 0);
1106b00e 3126
6d56631f 3127 fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->hostname, first_job_id);
1106b00e
MS
3128 }
3129 else
3130 first_job_id = 1;
3131
3132 /*
3133 * See if we only want to see jobs for a specific user...
3134 */
3135
3136 username = NULL;
3137
3138 if ((attr = ippFindAttribute(client->request, "my-jobs",
3139 IPP_TAG_BOOLEAN)) != NULL)
3140 {
a469f8a5
MS
3141 int my_jobs = ippGetBoolean(attr, 0);
3142
6d56631f 3143 fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->hostname, my_jobs ? "true" : "false");
1106b00e 3144
a469f8a5 3145 if (my_jobs)
1106b00e
MS
3146 {
3147 if ((attr = ippFindAttribute(client->request, "requesting-user-name",
3148 IPP_TAG_NAME)) == NULL)
3149 {
a469f8a5 3150 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
1106b00e
MS
3151 "Need requesting-user-name with my-jobs.");
3152 return;
3153 }
3154
a469f8a5 3155 username = ippGetString(attr, 0, NULL);
1106b00e 3156
6d56631f 3157 fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n", client->hostname, username);
1106b00e
MS
3158 }
3159 }
3160
3161 /*
3162 * OK, build a list of jobs for this printer...
3163 */
3164
db8b865d 3165 ra = ippCreateRequestedArray(client->request);
1106b00e 3166
a469f8a5 3167 respond_ipp(client, IPP_STATUS_OK, NULL);
1106b00e
MS
3168
3169 _cupsRWLockRead(&(client->printer->rwlock));
3170
d46dbe1b 3171 for (count = 0, job = (ippeve_job_t *)cupsArrayFirst(client->printer->jobs);
1106b00e 3172 (limit <= 0 || count < limit) && job;
d46dbe1b 3173 job = (ippeve_job_t *)cupsArrayNext(client->printer->jobs))
1106b00e
MS
3174 {
3175 /*
3176 * Filter out jobs that don't match...
3177 */
3178
3179 if ((job_comparison < 0 && job->state > job_state) ||
3180 (job_comparison == 0 && job->state != job_state) ||
3181 (job_comparison > 0 && job->state < job_state) ||
3182 job->id < first_job_id ||
a469f8a5 3183 (username && job->username &&
2cadf0f4 3184 strcasecmp(username, job->username)))
1106b00e
MS
3185 continue;
3186
3187 if (count > 0)
3188 ippAddSeparator(client->response);
3189
3190 count ++;
3191 copy_job_attributes(client, job, ra);
3192 }
3193
3194 cupsArrayDelete(ra);
3195
3196 _cupsRWUnlock(&(client->printer->rwlock));
3197}
3198
3199
3200/*
3201 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3202 */
3203
3204static void
3205ipp_get_printer_attributes(
d46dbe1b 3206 ippeve_client_t *client) /* I - Client */
1106b00e
MS
3207{
3208 cups_array_t *ra; /* Requested attributes array */
d46dbe1b 3209 ippeve_printer_t *printer; /* Printer */
1106b00e
MS
3210
3211
3212 /*
3213 * Send the attributes...
3214 */
3215
db8b865d 3216 ra = ippCreateRequestedArray(client->request);
1106b00e
MS
3217 printer = client->printer;
3218
a469f8a5 3219 respond_ipp(client, IPP_STATUS_OK, NULL);
1106b00e
MS
3220
3221 _cupsRWLockRead(&(printer->rwlock));
3222
3223 copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
a469f8a5 3224 IPP_TAG_CUPS_CONST);
1106b00e 3225
4a838088
MS
3226 if (!ra || cupsArrayFind(ra, "printer-config-change-date-time"))
3227 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-config-change-date-time", ippTimeToDate(printer->config_time));
3228
3229 if (!ra || cupsArrayFind(ra, "printer-config-change-time"))
3230 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-config-change-time", (int)(printer->config_time - printer->start_time));
3231
3232 if (!ra || cupsArrayFind(ra, "printer-current-time"))
3233 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(time(NULL)));
3234
3235
1106b00e
MS
3236 if (!ra || cupsArrayFind(ra, "printer-state"))
3237 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
3238 "printer-state", printer->state);
3239
4a838088
MS
3240 if (!ra || cupsArrayFind(ra, "printer-state-change-date-time"))
3241 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-state-change-date-time", ippTimeToDate(printer->state_time));
3242
3243 if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
3244 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-state-change-time", (int)(printer->state_time - printer->start_time));
3245
3246 if (!ra || cupsArrayFind(ra, "printer-state-message"))
3247 {
3248 static const char * const messages[] = { "Idle.", "Printing.", "Stopped." };
3249
3250 ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-state-message", NULL, messages[printer->state - IPP_PSTATE_IDLE]);
3251 }
3252
1106b00e
MS
3253 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
3254 {
d46dbe1b 3255 if (printer->state_reasons == IPPEVE_PREASON_NONE)
1562b9a1
MS
3256 {
3257 ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-state-reasons", NULL, "none");
3258 }
1106b00e
MS
3259 else
3260 {
9610a474 3261 ipp_attribute_t *attr = NULL; /* printer-state-reasons */
d46dbe1b 3262 ippeve_preason_t bit; /* Reason bit */
9610a474
MS
3263 int i; /* Looping var */
3264 char reason[32]; /* Reason string */
3265
d46dbe1b 3266 for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
9610a474
MS
3267 {
3268 if (printer->state_reasons & bit)
3269 {
d46dbe1b 3270 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
3271 if (attr)
3272 ippSetString(client->response, &attr, ippGetCount(attr), reason);
3273 else
3274 attr = ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "printer-state-reasons", NULL, reason);
1562b9a1
MS
3275 }
3276 }
4a838088
MS
3277 }
3278 }
3279
1106b00e 3280 if (!ra || cupsArrayFind(ra, "printer-up-time"))
4a838088 3281 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-up-time", (int)(time(NULL) - printer->start_time));
1106b00e
MS
3282
3283 if (!ra || cupsArrayFind(ra, "queued-job-count"))
1562b9a1 3284 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "queued-job-count", printer->active_job && printer->active_job->state < IPP_JSTATE_CANCELED);
1106b00e
MS
3285
3286 _cupsRWUnlock(&(printer->rwlock));
3287
3288 cupsArrayDelete(ra);
3289}
3290
3291
2cadf0f4
MS
3292/*
3293 * 'ipp_identify_printer()' - Beep or display a message.
3294 */
3295
3296static void
3297ipp_identify_printer(
d46dbe1b 3298 ippeve_client_t *client) /* I - Client */
2cadf0f4 3299{
7fd8cbfa
MS
3300 ipp_attribute_t *actions, /* identify-actions */
3301 *message; /* message */
3302
3303
3304 actions = ippFindAttribute(client->request, "identify-actions", IPP_TAG_KEYWORD);
3305 message = ippFindAttribute(client->request, "message", IPP_TAG_TEXT);
3306
3307 if (!actions || ippContainsString(actions, "sound"))
3308 {
3309 putchar(0x07);
3310 fflush(stdout);
3311 }
3312
3313 if (ippContainsString(actions, "display"))
3314 printf("IDENTIFY from %s: %s\n", client->hostname, message ? ippGetString(message, 0, NULL) : "No message supplied");
2cadf0f4
MS
3315
3316 respond_ipp(client, IPP_STATUS_OK, NULL);
3317}
3318
3319
1106b00e
MS
3320/*
3321 * 'ipp_print_job()' - Create a job object with an attached document.
3322 */
3323
3324static void
d46dbe1b 3325ipp_print_job(ippeve_client_t *client) /* I - Client */
1106b00e 3326{
d46dbe1b 3327 ippeve_job_t *job; /* New job */
1106b00e
MS
3328
3329
3330 /*
3331 * Validate print job attributes...
3332 */
3333
3334 if (!valid_job_attributes(client))
3335 {
a469f8a5 3336 httpFlush(client->http);
1106b00e
MS
3337 return;
3338 }
3339
3340 /*
3341 * Do we have a file to print?
3342 */
3343
a469f8a5 3344 if (httpGetState(client->http) == HTTP_STATE_POST_SEND)
1106b00e 3345 {
a469f8a5 3346 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No file in request.");
1106b00e
MS
3347 return;
3348 }
3349
3350 /*
cdb2e724 3351 * Create the job...
1106b00e
MS
3352 */
3353
3354 if ((job = create_job(client)) == NULL)
3355 {
aa2a90ce 3356 respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job.");
1106b00e
MS
3357 return;
3358 }
3359
3360 /*
cdb2e724 3361 * Then finish getting the document data and process things...
1106b00e
MS
3362 */
3363
a5ed2e39 3364 finish_document_data(client, job);
1106b00e
MS
3365}
3366
3367
1106b00e 3368/*
83e08001 3369 * 'ipp_print_uri()' - Create a job object with a referenced document.
1106b00e
MS
3370 */
3371
3372static void
d46dbe1b 3373ipp_print_uri(ippeve_client_t *client) /* I - Client */
1106b00e 3374{
d46dbe1b 3375 ippeve_job_t *job; /* New job */
1106b00e 3376
1106b00e 3377
83e08001
MS
3378 /*
3379 * Validate print job attributes...
3380 */
1106b00e 3381
83e08001
MS
3382 if (!valid_job_attributes(client))
3383 {
a469f8a5 3384 httpFlush(client->http);
83e08001
MS
3385 return;
3386 }
1106b00e 3387
1106b00e 3388 /*
a5ed2e39 3389 * Create the job...
1106b00e
MS
3390 */
3391
83e08001
MS
3392 if ((job = create_job(client)) == NULL)
3393 {
aa2a90ce 3394 respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job.");
83e08001
MS
3395 return;
3396 }
1106b00e 3397
a5ed2e39
MS
3398 /*
3399 * Then finish getting the document data and process things...
3400 */
83e08001 3401
a5ed2e39 3402 finish_document_uri(client, job);
83e08001
MS
3403}
3404
3405
3406/*
3407 * 'ipp_send_document()' - Add an attached document to a job object created with
3408 * Create-Job.
3409 */
3410
3411static void
aa2a90ce
MS
3412ipp_send_document(
3413 ippeve_client_t *client) /* I - Client */
83e08001 3414{
d46dbe1b 3415 ippeve_job_t *job; /* Job information */
83e08001 3416 ipp_attribute_t *attr; /* Current attribute */
83e08001
MS
3417
3418
3419 /*
3420 * Get the job...
3421 */
3422
3423 if ((job = find_job(client)) == NULL)
3424 {
a469f8a5
MS
3425 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3426 httpFlush(client->http);
83e08001
MS
3427 return;
3428 }
3429
3430 /*
3431 * See if we already have a document for this job or the job has already
3432 * in a non-pending state...
3433 */
3434
a469f8a5 3435 if (job->state > IPP_JSTATE_HELD)
83e08001 3436 {
cdb2e724 3437 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state.");
a469f8a5 3438 httpFlush(client->http);
83e08001
MS
3439 return;
3440 }
3441 else if (job->filename || job->fd >= 0)
3442 {
cdb2e724 3443 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported.");
a469f8a5 3444 httpFlush(client->http);
83e08001
MS
3445 return;
3446 }
3447
cdb2e724
MS
3448 /*
3449 * Make sure we have the "last-document" operation attribute...
3450 */
3451
3452 if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL)
83e08001 3453 {
cdb2e724
MS
3454 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute.");
3455 httpFlush(client->http);
3456 return;
3457 }
3458 else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION)
3459 {
3460 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group.");
a469f8a5 3461 httpFlush(client->http);
83e08001
MS
3462 return;
3463 }
cdb2e724 3464 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 || !ippGetBoolean(attr, 0))
83e08001
MS
3465 {
3466 respond_unsupported(client, attr);
a469f8a5 3467 httpFlush(client->http);
83e08001
MS
3468 return;
3469 }
3470
3471 /*
3472 * Validate document attributes...
3473 */
3474
3475 if (!valid_doc_attributes(client))
3476 {
a469f8a5 3477 httpFlush(client->http);
83e08001
MS
3478 return;
3479 }
3480
3481 /*
cdb2e724 3482 * Then finish getting the document data and process things...
83e08001
MS
3483 */
3484
3485 _cupsRWLockWrite(&(client->printer->rwlock));
3486
a5ed2e39
MS
3487 copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
3488
404dde30
MS
3489 if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
3490 job->format = ippGetString(attr, 0, NULL);
3491 else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
a469f8a5 3492 job->format = ippGetString(attr, 0, NULL);
83e08001
MS
3493 else
3494 job->format = "application/octet-stream";
3495
83e08001
MS
3496 _cupsRWUnlock(&(client->printer->rwlock));
3497
a5ed2e39 3498 finish_document_data(client, job);
83e08001
MS
3499}
3500
3501
3502/*
3503 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3504 * Create-Job.
3505 */
3506
3507static void
d46dbe1b 3508ipp_send_uri(ippeve_client_t *client) /* I - Client */
83e08001 3509{
d46dbe1b 3510 ippeve_job_t *job; /* Job information */
83e08001 3511 ipp_attribute_t *attr; /* Current attribute */
83e08001
MS
3512
3513
3514 /*
3515 * Get the job...
3516 */
3517
3518 if ((job = find_job(client)) == NULL)
3519 {
a469f8a5
MS
3520 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3521 httpFlush(client->http);
83e08001
MS
3522 return;
3523 }
3524
3525 /*
3526 * See if we already have a document for this job or the job has already
3527 * in a non-pending state...
3528 */
3529
a469f8a5 3530 if (job->state > IPP_JSTATE_HELD)
83e08001 3531 {
a5ed2e39 3532 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state.");
a469f8a5 3533 httpFlush(client->http);
83e08001
MS
3534 return;
3535 }
3536 else if (job->filename || job->fd >= 0)
3537 {
a5ed2e39 3538 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported.");
a469f8a5 3539 httpFlush(client->http);
83e08001
MS
3540 return;
3541 }
3542
a5ed2e39 3543 if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL)
83e08001 3544 {
a5ed2e39 3545 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute.");
a469f8a5 3546 httpFlush(client->http);
83e08001
MS
3547 return;
3548 }
a5ed2e39 3549 else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION)
83e08001 3550 {
a5ed2e39 3551 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group.");
a469f8a5 3552 httpFlush(client->http);
83e08001
MS
3553 return;
3554 }
a5ed2e39 3555 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 || !ippGetBoolean(attr, 0))
83e08001 3556 {
a5ed2e39 3557 respond_unsupported(client, attr);
a469f8a5 3558 httpFlush(client->http);
83e08001
MS
3559 return;
3560 }
3561
3562 /*
a5ed2e39 3563 * Validate document attributes...
83e08001
MS
3564 */
3565
a5ed2e39 3566 if (!valid_doc_attributes(client))
83e08001 3567 {
a5ed2e39 3568 httpFlush(client->http);
83e08001
MS
3569 return;
3570 }
3571
3572 /*
a5ed2e39 3573 * Then finish getting the document data and process things...
83e08001
MS
3574 */
3575
3576 _cupsRWLockWrite(&(client->printer->rwlock));
3577
a5ed2e39
MS
3578 copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
3579
3580 if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
3581 job->format = ippGetString(attr, 0, NULL);
3582 else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
a469f8a5 3583 job->format = ippGetString(attr, 0, NULL);
83e08001
MS
3584 else
3585 job->format = "application/octet-stream";
3586
83e08001
MS
3587 _cupsRWUnlock(&(client->printer->rwlock));
3588
a5ed2e39 3589 finish_document_uri(client, job);
83e08001
MS
3590}
3591
3592
3593/*
3594 * 'ipp_validate_job()' - Validate job creation attributes.
3595 */
3596
3597static void
d46dbe1b 3598ipp_validate_job(ippeve_client_t *client) /* I - Client */
83e08001
MS
3599{
3600 if (valid_job_attributes(client))
a469f8a5 3601 respond_ipp(client, IPP_STATUS_OK, NULL);
83e08001
MS
3602}
3603
3604
15a9714c 3605/*
92dad945 3606 * 'ippserver_attr_cb()' - Determine whether an attribute should be loaded.
15a9714c
MS
3607 */
3608
92dad945
MS
3609static int /* O - 1 to use, 0 to ignore */
3610ippserver_attr_cb(
3611 _ipp_file_t *f, /* I - IPP file */
3612 void *user_data, /* I - User data pointer (unused) */
3613 const char *attr) /* I - Attribute name */
15a9714c 3614{
92dad945
MS
3615 int i, /* Current element */
3616 result; /* Result of comparison */
3617 static const char * const ignored[] =
3618 { /* Ignored attributes */
3619 "attributes-charset",
3620 "attributes-natural-language",
3621 "charset-configured",
3622 "charset-supported",
3623 "device-service-count",
3624 "device-uuid",
3625 "document-format-varying-attributes",
3626 "generated-natural-language-supported",
1562b9a1
MS
3627 "identify-actions-default",
3628 "identify-actions-supported",
3629 "ipp-features-supported",
3630 "ipp-versions-supproted",
92dad945
MS
3631 "ippget-event-life",
3632 "job-hold-until-supported",
3633 "job-hold-until-time-supported",
3634 "job-ids-supported",
3635 "job-k-octets-supported",
3636 "job-settable-attributes-supported",
3637 "multiple-document-jobs-supported",
3638 "multiple-operation-time-out",
3639 "multiple-operation-time-out-action",
3640 "natural-language-configured",
3641 "notify-attributes-supported",
3642 "notify-events-default",
3643 "notify-events-supported",
3644 "notify-lease-duration-default",
3645 "notify-lease-duration-supported",
3646 "notify-max-events-supported",
3647 "notify-pull-method-supported",
3648 "operations-supported",
3649 "printer-alert",
3650 "printer-alert-description",
3651 "printer-camera-image-uri",
3652 "printer-charge-info",
3653 "printer-charge-info-uri",
3654 "printer-config-change-date-time",
3655 "printer-config-change-time",
3656 "printer-current-time",
3657 "printer-detailed-status-messages",
3658 "printer-dns-sd-name",
3659 "printer-fax-log-uri",
3660 "printer-get-attributes-supported",
3661 "printer-icons",
3662 "printer-id",
3663 "printer-info",
3664 "printer-is-accepting-jobs",
3665 "printer-message-date-time",
3666 "printer-message-from-operator",
3667 "printer-message-time",
3668 "printer-more-info",
3669 "printer-service-type",
3670 "printer-settable-attributes-supported",
3671 "printer-state",
3672 "printer-state-message",
3673 "printer-state-reasons",
3674 "printer-static-resource-directory-uri",
3675 "printer-static-resource-k-octets-free",
3676 "printer-static-resource-k-octets-supported",
3677 "printer-strings-languages-supported",
3678 "printer-strings-uri",
3679 "printer-supply-info-uri",
3680 "printer-up-time",
3681 "printer-uri-supported",
3682 "printer-xri-supported",
3683 "queued-job-count",
3684 "reference-uri-scheme-supported",
3685 "uri-authentication-supported",
3686 "uri-security-supported",
3687 "which-jobs-supported",
3688 "xri-authentication-supported",
3689 "xri-security-supported",
3690 "xri-uri-scheme-supported"
3691 };
3692
15a9714c 3693
92dad945
MS
3694 (void)f;
3695 (void)user_data;
15a9714c 3696
92dad945 3697 for (i = 0, result = 1; i < (int)(sizeof(ignored) / sizeof(ignored[0])); i ++)
15a9714c 3698 {
92dad945
MS
3699 if ((result = strcmp(attr, ignored[i])) <= 0)
3700 break;
15a9714c
MS
3701 }
3702
92dad945
MS
3703 return (result != 0);
3704}
3705
3706
3707/*
3708 * 'ippserver_error_cb()' - Log an error message.
3709 */
3710
3711static int /* O - 1 to continue, 0 to stop */
3712ippserver_error_cb(
3713 _ipp_file_t *f, /* I - IPP file data */
3714 void *user_data, /* I - User data pointer (unused) */
3715 const char *error) /* I - Error message */
3716{
3717 (void)f;
3718 (void)user_data;
3719
3720 _cupsLangPrintf(stderr, "%s\n", error);
3721
3722 return (1);
3723}
3724
3725
3726/*
3727 * 'ippserver_token_cb()' - Process ippserver-specific config file tokens.
3728 */
3729
3730static int /* O - 1 to continue, 0 to stop */
3731ippserver_token_cb(
3732 _ipp_file_t *f, /* I - IPP file data */
3733 _ipp_vars_t *vars, /* I - IPP variables */
3734 void *user_data, /* I - User data pointer (unused) */
3735 const char *token) /* I - Current token */
3736{
1562b9a1 3737 (void)vars;
92dad945
MS
3738 (void)user_data;
3739
3740 if (!token)
15a9714c 3741 {
92dad945
MS
3742 /*
3743 * NULL token means do the initial setup - create an empty IPP message and
3744 * return...
3745 */
15a9714c 3746
92dad945
MS
3747 f->attrs = ippNew();
3748 f->group_tag = IPP_TAG_PRINTER;
3749 }
3750 else
3751 {
3752 _cupsLangPrintf(stderr, _("Unknown directive \"%s\" on line %d of \"%s\" ignored."), token, f->linenum, f->filename);
3753 }
15a9714c 3754
92dad945
MS
3755 return (1);
3756}
15a9714c 3757
15a9714c 3758
92dad945
MS
3759/*
3760 * 'load_ippserver_attributes()' - Load IPP attributes from an ippserver file.
3761 */
15a9714c 3762
92dad945
MS
3763static ipp_t * /* O - IPP attributes or `NULL` on error */
3764load_ippserver_attributes(
f8927099
MS
3765 const char *servername, /* I - Server name or `NULL` for default */
3766 int serverport, /* I - Server port number */
3767 const char *filename, /* I - ippserver attribute filename */
3768 cups_array_t *docformats) /* I - document-format-supported values */
92dad945
MS
3769{
3770 ipp_t *attrs; /* IPP attributes */
3771 _ipp_vars_t vars; /* IPP variables */
3772 char temp[256]; /* Temporary string */
15a9714c 3773
15a9714c 3774
92dad945
MS
3775 /*
3776 * Setup callbacks and variables for the printer configuration file...
3777 *
3778 * The following additional variables are supported:
3779 *
3780 * - SERVERNAME: The host name of the server.
3781 * - SERVERPORT: The default port of the server.
3782 */
15a9714c 3783
92dad945 3784 _ippVarsInit(&vars, (_ipp_fattr_cb_t)ippserver_attr_cb, (_ipp_ferror_cb_t)ippserver_error_cb, (_ipp_ftoken_cb_t)ippserver_token_cb);
15a9714c 3785
92dad945
MS
3786 if (servername)
3787 {
3788 _ippVarsSet(&vars, "SERVERNAME", servername);
3789 }
3790 else
3791 {
3792 httpGetHostname(NULL, temp, sizeof(temp));
3793 _ippVarsSet(&vars, "SERVERNAME", temp);
3794 }
15a9714c 3795
92dad945
MS
3796 snprintf(temp, sizeof(temp), "%d", serverport);
3797 _ippVarsSet(&vars, "SERVERPORT", temp);
15a9714c 3798
92dad945
MS
3799 /*
3800 * Load attributes and values for the printer...
3801 */
15a9714c 3802
92dad945 3803 attrs = _ippFileParse(&vars, filename, NULL);
15a9714c 3804
92dad945
MS
3805 /*
3806 * Free memory and return...
3807 */
15a9714c 3808
92dad945 3809 _ippVarsDeinit(&vars);
15a9714c 3810
92dad945
MS
3811 return (attrs);
3812}
15a9714c 3813
15a9714c 3814
92dad945 3815/*
f8927099 3816 * 'load_legacy_attributes()' - Load IPP attributes using the old ippserver
6d56631f 3817 * options.
92dad945 3818 */
15a9714c 3819
92dad945 3820static ipp_t * /* O - IPP attributes or `NULL` on error */
f8927099
MS
3821load_legacy_attributes(
3822 const char *make, /* I - Manufacturer name */
3823 const char *model, /* I - Model name */
3824 int ppm, /* I - pages-per-minute */
3825 int ppm_color, /* I - pages-per-minute-color */
3826 int duplex, /* I - Duplex support? */
3827 cups_array_t *docformats) /* I - document-format-supported values */
92dad945 3828{
1562b9a1
MS
3829 int i; /* Looping var */
3830 ipp_t *attrs, /* IPP attributes */
3831 *col; /* Collection value */
3832 ipp_attribute_t *attr; /* Current attribute */
92dad945 3833 char device_id[1024],/* printer-device-id */
1562b9a1 3834 *ptr, /* Pointer into device ID */
92dad945 3835 make_model[128];/* printer-make-and-model */
1562b9a1
MS
3836 const char *format, /* Current document format */
3837 *prefix; /* Prefix for device ID */
3838 int num_media; /* Number of media */
3839 const char * const *media; /* List of media */
3840 int num_ready; /* Number of loaded media */
3841 const char * const *ready; /* List of loaded media */
3842 pwg_media_t *pwg; /* PWG media size information */
3843 static const char * const media_supported[] =
3844 { /* media-supported values */
3845 "na_letter_8.5x11in", /* Letter */
3846 "na_legal_8.5x14in", /* Legal */
3847 "iso_a4_210x297mm", /* A4 */
3848 "na_number-10_4.125x9.5in", /* #10 Envelope */
3849 "iso_dl_110x220mm" /* DL Envelope */
3850 };
3851 static const char * const media_supported_color[] =
3852 { /* media-supported values */
3853 "na_letter_8.5x11in", /* Letter */
3854 "na_legal_8.5x14in", /* Legal */
3855 "iso_a4_210x297mm", /* A4 */
3856 "na_number-10_4.125x9.5in", /* #10 Envelope */
3857 "iso_dl_110x220mm", /* DL Envelope */
3858 "na_index-3x5_3x5in", /* Photo 3x5 */
3859 "oe_photo-l_3.5x5in", /* Photo L */
3860 "na_index-4x6_4x6in", /* Photo 4x6 */
3861 "iso_a6_105x148mm", /* A6 */
3862 "na_5x7_5x7in" /* Photo 5x7 aka 2L */
3863 "iso_a5_148x210mm", /* A5 */
3864 };
3865 static const char * const media_ready[] =
3866 { /* media-ready values */
3867 "na_letter_8.5x11in", /* Letter */
3868 "na_number-10_4.125x9.5in" /* #10 */
3869 };
3870 static const char * const media_ready_color[] =
3871 { /* media-ready values */
3872 "na_letter_8.5x11in", /* Letter */
3873 "na_index-4x6_4x6in" /* Photo 4x6 */
3874 };
3875 static const char * const media_source_supported[] =
3876 { /* media-source-supported values */
3877 "auto",
3878 "main",
3879 "manual",
3880 "by-pass-tray" /* AKA multi-purpose tray */
3881 };
3882 static const char * const media_source_supported_color[] =
3883 { /* media-source-supported values */
3884 "auto",
3885 "main",
3886 "manual",
3887 "by-pass-tray", /* AKA multi-purpose tray */
3888 "photo"
3889 };
3890 static const char * const media_type_supported[] =
3891 { /* media-type-supported values */
3892 "auto",
3893 "cardstock",
3894 "envelope",
3895 "labels",
3896 "other",
3897 "stationery",
3898 "stationery-letterhead",
3899 "transparency"
3900 };
3901 static const char * const media_type_supported_color[] =
3902 { /* media-type-supported values */
3903 "auto",
3904 "cardstock",
3905 "envelope",
3906 "labels",
3907 "other",
3908 "stationery",
3909 "stationery-letterhead",
3910 "transparency",
3911 "photographic-glossy",
3912 "photographic-high-gloss",
3913 "photographic-matte",
3914 "photographic-satin",
3915 "photographic-semi-gloss"
3916 };
3917 static const int media_bottom_margin_supported[] =
3918 { /* media-bottom-margin-supported values */
3919 635 /* 1/4" */
3920 };
3921 static const int media_bottom_margin_supported_color[] =
3922 { /* media-bottom/top-margin-supported values */
3923 0, /* Borderless */
3924 1168 /* 0.46" (common HP inkjet bottom margin) */
3925 };
3926 static const int media_lr_margin_supported[] =
3927 { /* media-left/right-margin-supported values */
3928 340, /* 3.4mm (historical HP PCL A4 margin) */
3929 635 /* 1/4" */
3930 };
3931 static const int media_lr_margin_supported_color[] =
3932 { /* media-left/right-margin-supported values */
3933 0, /* Borderless */
3934 340, /* 3.4mm (historical HP PCL A4 margin) */
3935 635 /* 1/4" */
3936 };
3937 static const int media_top_margin_supported[] =
3938 { /* media-top-margin-supported values */
3939 635 /* 1/4" */
3940 };
3941 static const int media_top_margin_supported_color[] =
3942 { /* media-top/top-margin-supported values */
3943 0, /* Borderless */
3944 102 /* 0.04" (common HP inkjet top margin */
3945 };
92dad945
MS
3946 static const int orientation_requested_supported[4] =
3947 { /* orientation-requested-supported values */
3948 IPP_ORIENT_PORTRAIT,
3949 IPP_ORIENT_LANDSCAPE,
3950 IPP_ORIENT_REVERSE_LANDSCAPE,
3951 IPP_ORIENT_REVERSE_PORTRAIT
3952 };
3953 static const char * const print_color_mode_supported[] =
1562b9a1
MS
3954 { /* print-color-mode-supported values */
3955 "monochrome"
3956 };
3957 static const char * const print_color_mode_supported_color[] =
92dad945
MS
3958 { /* print-color-mode-supported values */
3959 "auto",
3960 "color",
3961 "monochrome"
3962 };
3963 static const int print_quality_supported[] =
3964 { /* print-quality-supported values */
3965 IPP_QUALITY_DRAFT,
3966 IPP_QUALITY_NORMAL,
3967 IPP_QUALITY_HIGH
3968 };
1562b9a1
MS
3969 static const char * const printer_supply[] =
3970 { /* printer-supply values */
3971 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
3972 "maxcapacity=100;level=25;colorantname=unknown;",
3973 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
3974 "maxcapacity=100;level=75;colorantname=black;"
3975 };
3976 static const char * const printer_supply_color[] =
3977 { /* printer-supply values */
3978 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
3979 "maxcapacity=100;level=25;colorantname=unknown;",
3980 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
3981 "maxcapacity=100;level=75;colorantname=black;",
3982 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
3983 "maxcapacity=100;level=50;colorantname=cyan;",
3984 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
3985 "maxcapacity=100;level=33;colorantname=magenta;",
3986 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
3987 "maxcapacity=100;level=67;colorantname=yellow;"
3988 };
3989 static const char * const printer_supply_description[] =
3990 { /* printer-supply-description values */
3991 "Toner Waste Tank",
3992 "Black Toner"
3993 };
3994 static const char * const printer_supply_description_color[] =
3995 { /* printer-supply-description values */
3996 "Ink Waste Tank",
3997 "Black Ink",
3998 "Cyan Ink",
3999 "Magenta Ink",
4000 "Yellow Ink"
4001 };
92dad945
MS
4002 static const int pwg_raster_document_resolution_supported[] =
4003 {
1562b9a1
MS
4004 300,
4005 600
92dad945
MS
4006 };
4007 static const char * const pwg_raster_document_type_supported[] =
4008 {
4009 "black_1",
1562b9a1
MS
4010 "sgray_8"
4011 };
4012 static const char * const pwg_raster_document_type_supported_color[] =
4013 {
4014 "black_1",
92dad945
MS
4015 "sgray_8",
4016 "srgb_8",
4017 "srgb_16"
4018 };
4019 static const char * const sides_supported[] =
4020 { /* sides-supported values */
4021 "one-sided",
4022 "two-sided-long-edge",
4023 "two-sided-short-edge"
4024 };
4025 static const char * const urf_supported[] =
4026 { /* urf-supported values */
4027 "CP1",
1562b9a1
MS
4028 "IS1-4-5-19",
4029 "MT1-2-3-4-5-6",
4030 "RS600",
4031 "V1.4",
4032 "W8"
4033 };
4034 static const char * const urf_supported_color[] =
4035 { /* urf-supported values */
4036 "CP1",
4037 "IS1-4-5-7-19",
4038 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4039 "RS600",
4040 "SRGB24",
4041 "V1.4",
4042 "W8"
4043 };
4044 static const char * const urf_supported_color_duplex[] =
4045 { /* urf-supported values */
4046 "CP1",
4047 "IS1-4-5-7-19",
92dad945 4048 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1562b9a1 4049 "RS600",
92dad945
MS
4050 "SRGB24",
4051 "V1.4",
4052 "W8",
1562b9a1
MS
4053 "DM3"
4054 };
4055 static const char * const urf_supported_duplex[] =
4056 { /* urf-supported values */
4057 "CP1",
4058 "IS1-4-5-19",
4059 "MT1-2-3-4-5-6",
4060 "RS600",
4061 "V1.4",
4062 "W8",
92dad945
MS
4063 "DM1"
4064 };
15a9714c 4065
15a9714c 4066
92dad945 4067 attrs = ippNew();
15a9714c 4068
1562b9a1
MS
4069 if (ppm_color > 0)
4070 {
4071 num_media = (int)(sizeof(media_supported_color) / sizeof(media_supported_color[0]));
4072 media = media_supported_color;
4073 num_ready = (int)(sizeof(media_ready_color) / sizeof(media_ready_color[0]));
4074 ready = media_ready_color;
4075 }
4076 else
4077 {
4078 num_media = (int)(sizeof(media_supported) / sizeof(media_supported[0]));
4079 media = media_supported;
4080 num_ready = (int)(sizeof(media_ready) / sizeof(media_ready[0]));
4081 ready = media_ready;
4082 }
4083
92dad945
MS
4084 /* color-supported */
4085 ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", ppm_color > 0);
15a9714c 4086
92dad945
MS
4087 /* copies-default */
4088 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
15a9714c 4089
92dad945 4090 /* copies-supported */
1562b9a1 4091 ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, (cupsArrayFind(docformats, (void *)"application/pdf") != NULL || cupsArrayFind(docformats, (void *)"image/jpeg") != NULL) ? 999 : 1);
15a9714c 4092
92dad945
MS
4093 /* finishings-default */
4094 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
15a9714c 4095
92dad945
MS
4096 /* finishings-supported */
4097 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", IPP_FINISHINGS_NONE);
15a9714c 4098
92dad945 4099 /* media-bottom-margin-supported */
1562b9a1
MS
4100 if (ppm_color > 0)
4101 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);
4102 else
4103 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 4104
1562b9a1
MS
4105 /* media-col-database and media-col-default */
4106 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", num_media, NULL);
4107 for (i = 0; i < num_media; i ++)
92dad945 4108 {
1562b9a1
MS
4109 int bottom, left, /* media-xxx-margins */
4110 right, top;
4111 const char *source; /* media-source, if any */
4112
4113 pwg = pwgMediaForPWG(media[i]);
4114
4115 if (pwg->width < 21000 && pwg->length < 21000)
92dad945 4116 {
1562b9a1
MS
4117 source = "photo"; /* Photo size media from photo tray */
4118 bottom = /* Borderless margins */
4119 left =
4120 right =
4121 top = 0;
92dad945 4122 }
1562b9a1 4123 else if (pwg->width < 21000)
92dad945 4124 {
1562b9a1
MS
4125 source = "by-pass-tray"; /* Envelopes from multi-purpose tray */
4126 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4127 left = /* Left/right margins are standard */
4128 right = media_lr_margin_supported[1];
4129 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4130 }
4131 else if (pwg->width == 21000)
4132 {
4133 source = NULL; /* A4 from any tray */
4134 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4135 left = /* Left/right margins are reduced */
4136 right = media_lr_margin_supported[0];
4137 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4138 }
4139 else
4140 {
4141 source = NULL; /* Other size media from any tray */
4142 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4143 left = /* Left/right margins are standard */
4144 right = media_lr_margin_supported[1];
4145 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4146 }
15a9714c 4147
1562b9a1
MS
4148 col = create_media_col(media[i], source, NULL, pwg->width, pwg->length, bottom, left, right, top);
4149 ippSetCollection(attrs, &attr, i, col);
92dad945 4150
1562b9a1
MS
4151 if (i == 0)
4152 {
4153 /* media-col-default */
4154 ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col);
15a9714c 4155 }
1562b9a1
MS
4156
4157 ippDelete(col);
92dad945
MS
4158 }
4159
1562b9a1
MS
4160 /* media-col-ready */
4161 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", num_ready, NULL);
4162 for (i = 0; i < num_ready; i ++)
92dad945 4163 {
1562b9a1
MS
4164 int bottom, left, /* media-xxx-margins */
4165 right, top;
4166 const char *source, /* media-source */
4167 *type; /* media-type */
92dad945 4168
1562b9a1 4169 pwg = pwgMediaForPWG(ready[i]);
92dad945 4170
1562b9a1
MS
4171 if (pwg->width < 21000 && pwg->length < 21000)
4172 {
4173 source = "photo"; /* Photo size media from photo tray */
4174 type = "photographic-glossy"; /* Glossy photo paper */
4175 bottom = /* Borderless margins */
4176 left =
4177 right =
4178 top = 0;
4179 }
4180 else if (pwg->width < 21000)
4181 {
4182 source = "by-pass-tray"; /* Envelopes from multi-purpose tray */
4183 type = "envelope"; /* Envelope */
4184 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4185 left = /* Left/right margins are standard */
4186 right = media_lr_margin_supported[1];
4187 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4188 }
4189 else if (pwg->width == 21000)
4190 {
4191 source = "main"; /* A4 from main tray */
4192 type = "stationery"; /* Plain paper */
4193 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4194 left = /* Left/right margins are reduced */
4195 right = media_lr_margin_supported[0];
4196 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4197 }
4198 else
4199 {
4200 source = "main"; /* A4 from main tray */
4201 type = "stationery"; /* Plain paper */
4202 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4203 left = /* Left/right margins are standard */
4204 right = media_lr_margin_supported[1];
4205 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4206 }
4207
4208 col = create_media_col(media[i], source, NULL, pwg->width, pwg->length, bottom, left, right, top);
4209 ippSetCollection(attrs, &attr, i, col);
4210 ippDelete(col);
4211 }
92dad945
MS
4212
4213 /* media-default */
1562b9a1 4214 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-default", NULL, media[0]);
92dad945 4215
1562b9a1
MS
4216 /* media-left/right-margin-supported */
4217 if (ppm_color > 0)
4218 {
4219 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);
4220 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);
4221 }
4222 else
4223 {
4224 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);
4225 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);
4226 }
92dad945 4227
1562b9a1
MS
4228 /* media-ready */
4229 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", num_ready, NULL, ready);
92dad945
MS
4230
4231 /* media-supported */
1562b9a1 4232 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-supported", num_media, NULL, media);
92dad945
MS
4233
4234 /* media-size-supported */
1562b9a1
MS
4235 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", num_media, NULL);
4236 for (i = 0; i < num_media; i ++)
92dad945 4237 {
1562b9a1
MS
4238 pwg = pwgMediaForPWG(media[i]);
4239 col = create_media_size(pwg->width, pwg->length);
92dad945 4240
1562b9a1
MS
4241 ippSetCollection(attrs, &attr, i, col);
4242 ippDelete(col);
15a9714c
MS
4243 }
4244
92dad945 4245 /* media-source-supported */
1562b9a1
MS
4246 if (ppm_color > 0)
4247 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);
4248 else
92dad945 4249 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 4250
92dad945 4251 /* media-top-margin-supported */
1562b9a1
MS
4252 if (ppm_color > 0)
4253 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);
4254 else
4255 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 4256
92dad945 4257 /* media-type-supported */
1562b9a1
MS
4258 if (ppm_color > 0)
4259 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);
4260 else
92dad945 4261 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 4262
92dad945 4263 /* orientation-requested-supported */
1562b9a1
MS
4264 if (cupsArrayFind(docformats, (void *)"application/pdf") || cupsArrayFind(docformats, (void *)"image/jpeg"))
4265 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported);
4266 else
4267 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", IPP_ORIENT_PORTRAIT);
92dad945
MS
4268
4269 /* output-bin-default */
1562b9a1
MS
4270 if (ppm_color > 0)
4271 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-up");
4272 else
92dad945
MS
4273 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
4274
4275 /* output-bin-supported */
1562b9a1
MS
4276 if (ppm_color > 0)
4277 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-up");
4278 else
92dad945
MS
4279 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
4280
92dad945 4281 /* page-ranges-supported */
1562b9a1 4282 ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", cupsArrayFind(docformats, (void *)"application/pdf") != NULL);
92dad945
MS
4283
4284 /* pages-per-minute */
1562b9a1 4285 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppm);
92dad945
MS
4286
4287 /* pages-per-minute-color */
4288 if (ppm_color > 0)
1562b9a1 4289 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppm_color);
92dad945
MS
4290
4291 /* print-color-mode-default */
1562b9a1 4292 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, "auto");
92dad945
MS
4293
4294 /* print-color-mode-supported */
1562b9a1
MS
4295 if (ppm_color > 0)
4296 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);
4297 else
92dad945
MS
4298 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);
4299
4300 /* print-content-optimize-default */
1562b9a1 4301 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
92dad945
MS
4302
4303 /* print-content-optimize-supported */
1562b9a1 4304 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
92dad945
MS
4305
4306 /* print-quality-default */
1562b9a1 4307 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
92dad945
MS
4308
4309 /* print-quality-supported */
1562b9a1
MS
4310 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
4311
4312 /* print-rendering-intent-default */
4313 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
4314
4315 /* print-rendering-intent-supported */
4316 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
92dad945
MS
4317
4318 /* printer-device-id */
1562b9a1
MS
4319 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make, model);
4320 ptr = device_id + strlen(device_id);
4321 prefix = "CMD:";
4322 for (format = (const char *)cupsArrayFirst(docformats); format; format = (const char *)cupsArrayNext(docformats))
4323 {
4324 if (!strcasecmp(format, "application/pdf"))
4325 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPDF", prefix);
4326 else if (!strcasecmp(format, "application/postscript"))
4327 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPS", prefix);
4328 else if (!strcasecmp(format, "application/vnd.hp-PCL"))
4329 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPCL", prefix);
4330 else if (!strcasecmp(format, "image/jpeg"))
4331 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sJPEG", prefix);
4332 else if (!strcasecmp(format, "image/png"))
4333 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPNG", prefix);
4334 else if (!strcasecmp(format, "image/pwg-raster"))
4335 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPWG", prefix);
4336 else if (!strcasecmp(format, "image/urf"))
4337 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sURF", prefix);
4338 else
4339 continue;
4340
4341 ptr += strlen(ptr);
4342 prefix = ",";
4343 }
4344 if (ptr < (device_id + sizeof(device_id) - 1))
4345 {
4346 *ptr++ = ';';
4347 *ptr = '\0';
4348 }
92dad945
MS
4349 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id);
4350
4351 /* printer-make-and-model */
4352 snprintf(make_model, sizeof(make_model), "%s %s", make, model);
4353 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, make_model);
4354
4355 /* printer-resolution-default */
1562b9a1 4356 ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, 600, 600);
92dad945
MS
4357
4358 /* printer-resolution-supported */
1562b9a1 4359 ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600);
92dad945 4360
1562b9a1
MS
4361 /* printer-supply and printer-supply-description */
4362 if (ppm_color > 0)
4363 {
4364 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], strlen(printer_supply_color[0]));
4365 for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++)
4366 ippSetOctetString(attrs, &attr, i, printer_supply_color[i], strlen(printer_supply_color[i]));
92dad945 4367
1562b9a1
MS
4368 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);
4369 }
4370 else
4371 {
4372 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], strlen(printer_supply[0]));
3e5092db 4373 for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++)
1562b9a1
MS
4374 ippSetOctetString(attrs, &attr, i, printer_supply[i], strlen(printer_supply[i]));
4375
4376 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);
4377 }
92dad945 4378
1562b9a1
MS
4379 /* pwg-raster-document-xxx-supported */
4380 if (cupsArrayFind(docformats, (void *)"image/pwg-raster"))
92dad945 4381 {
1562b9a1
MS
4382 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);
4383
4384 if (ppm_color > 0 && duplex)
4385 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "rotated");
4386 else if (duplex)
4387 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal");
4388
4389 if (ppm_color > 0)
4390 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);
4391 else
4392 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
4393 }
4394
4395 /* sides-default */
1562b9a1 4396 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
92dad945
MS
4397
4398 /* sides-supported */
1562b9a1
MS
4399 if (duplex)
4400 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported);
4401 else
4402 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided");
92dad945
MS
4403
4404 /* urf-supported */
1562b9a1
MS
4405 if (cupsArrayFind(docformats, (void *)"image/urf"))
4406 {
4407 if (ppm_color > 0)
4408 {
4409 if (duplex)
4410 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);
4411 else
4412 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_color) / sizeof(urf_supported_color[0])), NULL, urf_supported_color);
4413 }
4414 else if (duplex)
4415 {
4416 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_duplex) / sizeof(urf_supported_duplex[0])), NULL, urf_supported_duplex);
4417 }
4418 else
4419 {
4420 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported) / sizeof(urf_supported[0])), NULL, urf_supported);
4421 }
4422 }
92dad945
MS
4423
4424 return (attrs);
8a4ed632
MS
4425}
4426
4427
6641bd0d 4428#if !CUPS_LITE
f8927099
MS
4429/*
4430 * 'load_ppd_attributes()' - Load IPP attributes from a PPD file.
4431 */
4432
4433static ipp_t * /* O - IPP attributes or `NULL` on error */
4434load_ppd_attributes(
4435 const char *ppdfile, /* I - PPD filename */
4436 cups_array_t *docformats) /* I - document-format-supported values */
4437{
1562b9a1
MS
4438 (void)ppdfile;
4439 (void)docformats;
4440
4441#if 0
4442 static const char * const overrides[] =
4443 { /* overrides-supported */
4444 "document-number",
4445 "pages"
4446 };
4447
4448 /* document-password-supported */
4449 if (!ippFindAttribute(printer->attrs, "document-password-supported", IPP_TAG_ZERO))
4450 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 127);
4451
4452 /* overrides-supported */
4453 if (!ippFindAttribute(printer->attrs, "overrides-supported", IPP_TAG_ZERO))
4454 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "overrides-supported", (int)(sizeof(overrides) / sizeof(overrides[0])), NULL, overrides);
4455
4456 /* page-ranges-supported */
4457 if (!ippFindAttribute(printer->attrs, "page-ranges-supported", IPP_TAG_ZERO))
4458 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1);
4459 /* pages-per-minute */
4460 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4461 "pages-per-minute", ppm);
4462
4463 /* pages-per-minute-color */
4464 if (ppm_color > 0)
4465 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4466 "pages-per-minute-color", ppm_color);
4467#endif /* 0 */
4468
4469 return (NULL);
f8927099 4470}
6641bd0d 4471#endif /* !CUPS_LITE */
f8927099
MS
4472
4473
0b5ce83f
MS
4474/*
4475 * 'parse_options()' - Parse URL options into CUPS options.
4476 *
4477 * The client->options string is destroyed by this function.
4478 */
4479
4480static int /* O - Number of options */
d46dbe1b 4481parse_options(ippeve_client_t *client, /* I - Client */
92dad945 4482 cups_option_t **options)/* O - Options */
0b5ce83f
MS
4483{
4484 char *name, /* Name */
4485 *value, /* Value */
4486 *next; /* Next name=value pair */
4487 int num_options = 0; /* Number of options */
4488
4489
4490 *options = NULL;
4491
4492 for (name = client->options; name && *name; name = next)
4493 {
4494 if ((value = strchr(name, '=')) == NULL)
4495 break;
4496
4497 *value++ = '\0';
4498 if ((next = strchr(value, '&')) != NULL)
4499 *next++ = '\0';
4500
4501 num_options = cupsAddOption(name, value, num_options, options);
4502 }
4503
4504 return (num_options);
4505}
4506
4507
9610a474
MS
4508/*
4509 * 'process_attr_message()' - Process an ATTR: message from a command.
4510 */
4511
4512static void
4513process_attr_message(
d46dbe1b 4514 ippeve_job_t *job, /* I - Job */
9610a474
MS
4515 char *message) /* I - Message */
4516{
4517 (void)job;
4518 (void)message;
4519}
4520
4521
83e08001
MS
4522/*
4523 * 'process_client()' - Process client requests on a thread.
4524 */
4525
4526static void * /* O - Exit status */
d46dbe1b 4527process_client(ippeve_client_t *client) /* I - Client */
83e08001
MS
4528{
4529 /*
4530 * Loop until we are out of requests or timeout (30 seconds)...
4531 */
4532
f93b32b6
MS
4533#ifdef HAVE_SSL
4534 int first_time = 1; /* First time request? */
4535#endif /* HAVE_SSL */
4536
a469f8a5 4537 while (httpWait(client->http, 30000))
f93b32b6
MS
4538 {
4539#ifdef HAVE_SSL
4540 if (first_time)
4541 {
4542 /*
4543 * See if we need to negotiate a TLS connection...
4544 */
4545
4546 char buf[1]; /* First byte from client */
4547
4548 if (recv(httpGetFd(client->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0])))
4549 {
fe202ff4 4550 fprintf(stderr, "%s Starting HTTPS session.\n", client->hostname);
f93b32b6
MS
4551
4552 if (httpEncryption(client->http, HTTP_ENCRYPTION_ALWAYS))
4553 {
4554 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
4555 break;
4556 }
fe202ff4
MS
4557
4558 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
f93b32b6
MS
4559 }
4560
4561 first_time = 0;
4562 }
4563#endif /* HAVE_SSL */
4564
83e08001
MS
4565 if (!process_http(client))
4566 break;
f93b32b6 4567 }
83e08001
MS
4568
4569 /*
4570 * Close the conection to the client and return...
4571 */
4572
4573 delete_client(client);
4574
4575 return (NULL);
4576}
4577
4578
4579/*
4580 * 'process_http()' - Process a HTTP request.
4581 */
4582
4583int /* O - 1 on success, 0 on failure */
d46dbe1b 4584process_http(ippeve_client_t *client) /* I - Client connection */
83e08001 4585{
a469f8a5
MS
4586 char uri[1024]; /* URI */
4587 http_state_t http_state; /* HTTP state */
4588 http_status_t http_status; /* HTTP status */
4589 ipp_state_t ipp_state; /* State of IPP transfer */
4590 char scheme[32], /* Method/scheme */
4591 userpass[128], /* Username:password */
4592 hostname[HTTP_MAX_HOST];
4593 /* Hostname */
4594 int port; /* Port number */
4595 const char *encoding; /* Content-Encoding value */
4596 static const char * const http_states[] =
4597 { /* Strings for logging HTTP method */
4598 "WAITING",
4599 "OPTIONS",
4600 "GET",
4601 "GET_SEND",
4602 "HEAD",
4603 "POST",
4604 "POST_RECV",
4605 "POST_SEND",
4606 "PUT",
4607 "PUT_RECV",
4608 "DELETE",
4609 "TRACE",
4610 "CONNECT",
4611 "STATUS",
4612 "UNKNOWN_METHOD",
4613 "UNKNOWN_VERSION"
4614 };
83e08001 4615
83e08001
MS
4616
4617 /*
4618 * Clear state variables...
4619 */
4620
83e08001
MS
4621 ippDelete(client->request);
4622 ippDelete(client->response);
4623
a469f8a5
MS
4624 client->request = NULL;
4625 client->response = NULL;
4626 client->operation = HTTP_STATE_WAITING;
83e08001
MS
4627
4628 /*
4629 * Read a request from the connection...
4630 */
4631
a469f8a5
MS
4632 while ((http_state = httpReadRequest(client->http, uri,
4633 sizeof(uri))) == HTTP_STATE_WAITING)
4634 usleep(1);
83e08001
MS
4635
4636 /*
4637 * Parse the request line...
4638 */
4639
a469f8a5 4640 if (http_state == HTTP_STATE_ERROR)
83e08001 4641 {
a469f8a5
MS
4642 if (httpError(client->http) == EPIPE)
4643 fprintf(stderr, "%s Client closed connection.\n", client->hostname);
4644 else
6d56631f 4645 fprintf(stderr, "%s Bad request line (%s).\n", client->hostname, strerror(httpError(client->http)));
83e08001 4646
a469f8a5 4647 return (0);
83e08001 4648 }
a469f8a5 4649 else if (http_state == HTTP_STATE_UNKNOWN_METHOD)
83e08001 4650 {
a469f8a5
MS
4651 fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname);
4652 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
4653 return (0);
1106b00e 4654 }
a469f8a5 4655 else if (http_state == HTTP_STATE_UNKNOWN_VERSION)
1106b00e 4656 {
a469f8a5
MS
4657 fprintf(stderr, "%s Bad HTTP version.\n", client->hostname);
4658 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
4659 return (0);
1106b00e
MS
4660 }
4661
6d56631f 4662 fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state], uri);
a469f8a5 4663
1106b00e 4664 /*
a469f8a5 4665 * Separate the URI into its components...
1106b00e
MS
4666 */
4667
a469f8a5
MS
4668 if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
4669 userpass, sizeof(userpass),
4670 hostname, sizeof(hostname), &port,
fe202ff4
MS
4671 client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK &&
4672 (http_state != HTTP_STATE_OPTIONS || strcmp(uri, "*")))
1106b00e 4673 {
a469f8a5
MS
4674 fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri);
4675 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
4676 return (0);
4677 }
4678
0b5ce83f
MS
4679 if ((client->options = strchr(client->uri, '?')) != NULL)
4680 *(client->options)++ = '\0';
4681
a469f8a5
MS
4682 /*
4683 * Process the request...
4684 */
4685
4686 client->start = time(NULL);
4687 client->operation = httpGetState(client->http);
1106b00e
MS
4688
4689 /*
4690 * Parse incoming parameters until the status changes...
4691 */
4692
a469f8a5 4693 while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE);
1106b00e 4694
a469f8a5 4695 if (http_status != HTTP_STATUS_OK)
1106b00e 4696 {
a469f8a5 4697 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
4698 return (0);
4699 }
4700
a469f8a5
MS
4701 if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] &&
4702 httpGetVersion(client->http) >= HTTP_VERSION_1_1)
1106b00e
MS
4703 {
4704 /*
4705 * HTTP/1.1 and higher require the "Host:" field...
4706 */
4707
a469f8a5 4708 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
4709 return (0);
4710 }
4711
4712 /*
4713 * Handle HTTP Upgrade...
4714 */
4715
2cadf0f4 4716 if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION),
a469f8a5 4717 "Upgrade"))
1106b00e 4718 {
fe202ff4
MS
4719#ifdef HAVE_SSL
4720 if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http))
4721 {
4722 if (!respond_http(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0))
4723 return (0);
4724
4725 fprintf(stderr, "%s Upgrading to encrypted connection.\n", client->hostname);
4726
4727 if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED))
4728 {
4729 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
4730 return (0);
4731 }
4732
4733 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
4734 }
4735 else
4736#endif /* HAVE_SSL */
4737
a469f8a5 4738 if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
1106b00e
MS
4739 return (0);
4740 }
4741
e60ec91f
MS
4742 /*
4743 * Handle HTTP Expect...
4744 */
4745
a469f8a5
MS
4746 if (httpGetExpect(client->http) &&
4747 (client->operation == HTTP_STATE_POST ||
4748 client->operation == HTTP_STATE_PUT))
e60ec91f 4749 {
a469f8a5 4750 if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
e60ec91f
MS
4751 {
4752 /*
4753 * Send 100-continue header...
4754 */
4755
a469f8a5 4756 if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
e60ec91f
MS
4757 return (0);
4758 }
4759 else
4760 {
4761 /*
4762 * Send 417-expectation-failed header...
4763 */
4764
a469f8a5 4765 if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
e60ec91f 4766 return (0);
e60ec91f
MS
4767 }
4768 }
4769
1106b00e
MS
4770 /*
4771 * Handle new transfers...
4772 */
4773
a469f8a5
MS
4774 encoding = httpGetContentEncoding(client->http);
4775
1106b00e
MS
4776 switch (client->operation)
4777 {
a469f8a5 4778 case HTTP_STATE_OPTIONS :
1106b00e 4779 /*
fe202ff4 4780 * Do OPTIONS command...
1106b00e
MS
4781 */
4782
a469f8a5 4783 return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
1106b00e 4784
a469f8a5 4785 case HTTP_STATE_HEAD :
1106b00e 4786 if (!strcmp(client->uri, "/icon.png"))
a469f8a5 4787 return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
0b5ce83f 4788 else if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies"))
a469f8a5 4789 return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
1106b00e 4790 else
a469f8a5 4791 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e 4792
a469f8a5 4793 case HTTP_STATE_GET :
1106b00e
MS
4794 if (!strcmp(client->uri, "/icon.png"))
4795 {
4796 /*
4797 * Send PNG icon file.
4798 */
4799
3e5092db
MS
4800 if (client->printer->icon)
4801 {
4802 int fd; /* Icon file */
4803 struct stat fileinfo; /* Icon file information */
4804 char buffer[4096]; /* Copy buffer */
4805 ssize_t bytes; /* Bytes */
1106b00e 4806
3e5092db 4807 fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon);
a469f8a5 4808
3e5092db 4809 if (!stat(client->printer->icon, &fileinfo) && (fd = open(client->printer->icon, O_RDONLY)) >= 0)
1106b00e 4810 {
3e5092db
MS
4811 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
4812 {
4813 close(fd);
4814 return (0);
4815 }
4816
4817 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
4818 httpWrite2(client->http, buffer, (size_t)bytes);
4819
4820 httpFlushWrite(client->http);
4821
1106b00e 4822 close(fd);
1106b00e 4823 }
3e5092db
MS
4824 else
4825 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
4826 }
4827 else
4828 {
4829 fputs("Icon file is internal printer.png.\n", stderr);
1106b00e 4830
3e5092db
MS
4831 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_png)))
4832 return (0);
1106b00e 4833
3e5092db 4834 httpWrite2(client->http, (const char *)printer_png, sizeof(printer_png));
a469f8a5 4835 httpFlushWrite(client->http);
1106b00e 4836 }
1106b00e
MS
4837 }
4838 else if (!strcmp(client->uri, "/"))
4839 {
4840 /*
4841 * Show web status page...
4842 */
4843
9141aa01 4844 return (show_status(client));
0b5ce83f
MS
4845 }
4846 else if (!strcmp(client->uri, "/media"))
4847 {
4848 /*
4849 * Show web media page...
4850 */
4851
9141aa01 4852 return (show_media(client));
0b5ce83f
MS
4853 }
4854 else if (!strcmp(client->uri, "/supplies"))
4855 {
4856 /*
4857 * Show web supplies page...
4858 */
4859
9141aa01 4860 return (show_supplies(client));
1106b00e
MS
4861 }
4862 else
a469f8a5 4863 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e
MS
4864 break;
4865
a469f8a5
MS
4866 case HTTP_STATE_POST :
4867 if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
1106b00e
MS
4868 "application/ipp"))
4869 {
4870 /*
4871 * Not an IPP request...
4872 */
4873
a469f8a5 4874 return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
1106b00e
MS
4875 }
4876
4877 /*
4878 * Read the IPP request...
4879 */
4880
4881 client->request = ippNew();
4882
a469f8a5
MS
4883 while ((ipp_state = ippRead(client->http,
4884 client->request)) != IPP_STATE_DATA)
4885 {
4886 if (ipp_state == IPP_STATE_ERROR)
1106b00e 4887 {
6d56631f 4888 fprintf(stderr, "%s IPP read error (%s).\n", client->hostname, cupsLastErrorString());
a469f8a5 4889 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
4890 return (0);
4891 }
a469f8a5 4892 }
1106b00e
MS
4893
4894 /*
4895 * Now that we have the IPP request, process the request...
4896 */
4897
4898 return (process_ipp(client));
4899
4900 default :
4901 break; /* Anti-compiler-warning-code */
4902 }
4903
4904 return (1);
4905}
4906
4907
4908/*
4909 * 'process_ipp()' - Process an IPP request.
4910 */
4911
4912static int /* O - 1 on success, 0 on error */
d46dbe1b 4913process_ipp(ippeve_client_t *client) /* I - Client */
1106b00e
MS
4914{
4915 ipp_tag_t group; /* Current group tag */
4916 ipp_attribute_t *attr; /* Current attribute */
4917 ipp_attribute_t *charset; /* Character set attribute */
4918 ipp_attribute_t *language; /* Language attribute */
4919 ipp_attribute_t *uri; /* Printer URI attribute */
a469f8a5
MS
4920 int major, minor; /* Version number */
4921 const char *name; /* Name of attribute */
1106b00e
MS
4922
4923
83e08001 4924 debug_attributes("Request", client->request, 1);
1106b00e
MS
4925
4926 /*
4927 * First build an empty response message for this request...
4928 */
4929
a469f8a5
MS
4930 client->operation_id = ippGetOperation(client->request);
4931 client->response = ippNewResponse(client->request);
1106b00e
MS
4932
4933 /*
4934 * Then validate the request header and required attributes...
4935 */
4936
a469f8a5
MS
4937 major = ippGetVersion(client->request, &minor);
4938
4939 if (major < 1 || major > 2)
1106b00e
MS
4940 {
4941 /*
4942 * Return an error, since we only support IPP 1.x and 2.x.
4943 */
4944
b969d5af
MS
4945 respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, "Bad request version number %d.%d.", major, minor);
4946 }
4947 else if ((major * 10 + minor) > MaxVersion)
4948 {
4949 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
4950 httpFlush(client->http); /* Flush trailing (junk) data */
4951
4952 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
4953 return (0);
1106b00e 4954 }
a469f8a5 4955 else if (ippGetRequestId(client->request) <= 0)
b969d5af
MS
4956 {
4957 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.", ippGetRequestId(client->request));
4958 }
a469f8a5 4959 else if (!ippFirstAttribute(client->request))
b969d5af
MS
4960 {
4961 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No attributes in request.");
4962 }
1106b00e
MS
4963 else
4964 {
4965 /*
4966 * Make sure that the attributes are provided in the correct order and
4967 * don't repeat groups...
4968 */
4969
a469f8a5
MS
4970 for (attr = ippFirstAttribute(client->request),
4971 group = ippGetGroupTag(attr);
1106b00e 4972 attr;
a469f8a5
MS
4973 attr = ippNextAttribute(client->request))
4974 {
4975 if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
1106b00e
MS
4976 {
4977 /*
4978 * Out of order; return an error...
4979 */
4980
a469f8a5
MS
4981 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4982 "Attribute groups are out of order (%x < %x).",
4983 ippGetGroupTag(attr), group);
1106b00e
MS
4984 break;
4985 }
4986 else
a469f8a5
MS
4987 group = ippGetGroupTag(attr);
4988 }
1106b00e
MS
4989
4990 if (!attr)
4991 {
4992 /*
4993 * Then make sure that the first three attributes are:
4994 *
4995 * attributes-charset
4996 * attributes-natural-language
4997 * printer-uri/job-uri
4998 */
4999
a469f8a5
MS
5000 attr = ippFirstAttribute(client->request);
5001 name = ippGetName(attr);
5002 if (attr && name && !strcmp(name, "attributes-charset") &&
5003 ippGetValueTag(attr) == IPP_TAG_CHARSET)
1106b00e
MS
5004 charset = attr;
5005 else
5006 charset = NULL;
5007
a469f8a5
MS
5008 attr = ippNextAttribute(client->request);
5009 name = ippGetName(attr);
1106b00e 5010
a469f8a5
MS
5011 if (attr && name && !strcmp(name, "attributes-natural-language") &&
5012 ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
1106b00e
MS
5013 language = attr;
5014 else
5015 language = NULL;
5016
5017 if ((attr = ippFindAttribute(client->request, "printer-uri",
5018 IPP_TAG_URI)) != NULL)
5019 uri = attr;
5020 else if ((attr = ippFindAttribute(client->request, "job-uri",
5021 IPP_TAG_URI)) != NULL)
5022 uri = attr;
5023 else
5024 uri = NULL;
5025
1106b00e 5026 if (charset &&
2cadf0f4
MS
5027 strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
5028 strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
1106b00e
MS
5029 {
5030 /*
5031 * Bad character set...
5032 */
5033
a469f8a5 5034 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
1106b00e 5035 "Unsupported character set \"%s\".",
a469f8a5 5036 ippGetString(charset, 0, NULL));
1106b00e
MS
5037 }
5038 else if (!charset || !language || !uri)
5039 {
5040 /*
5041 * Return an error, since attributes-charset,
5042 * attributes-natural-language, and printer-uri/job-uri are required
5043 * for all operations.
5044 */
5045
a469f8a5
MS
5046 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5047 "Missing required attributes.");
1106b00e 5048 }
1106b00e
MS
5049 else
5050 {
8a78aa37
MS
5051 char scheme[32], /* URI scheme */
5052 userpass[32], /* Username/password in URI */
5053 host[256], /* Host name in URI */
5054 resource[256]; /* Resource path in URI */
5055 int port; /* Port number in URI */
5056
5057 name = ippGetName(uri);
5058
5059 if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
5060 scheme, sizeof(scheme),
5061 userpass, sizeof(userpass),
5062 host, sizeof(host), &port,
5063 resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
5064 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
5065 "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
5066 else if ((!strcmp(name, "job-uri") &&
5067 strncmp(resource, "/ipp/print/", 11)) ||
5068 (!strcmp(name, "printer-uri") &&
5069 strcmp(resource, "/ipp/print")))
5070 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
5071 name, ippGetString(uri, 0, NULL));
5072 else
1106b00e
MS
5073 {
5074 /*
8a78aa37 5075 * Try processing the operation...
1106b00e
MS
5076 */
5077
8a78aa37
MS
5078 switch (ippGetOperation(client->request))
5079 {
5080 case IPP_OP_PRINT_JOB :
5081 ipp_print_job(client);
5082 break;
5083
5084 case IPP_OP_PRINT_URI :
5085 ipp_print_uri(client);
5086 break;
5087
5088 case IPP_OP_VALIDATE_JOB :
5089 ipp_validate_job(client);
5090 break;
5091
5092 case IPP_OP_CREATE_JOB :
5093 ipp_create_job(client);
5094 break;
5095
5096 case IPP_OP_SEND_DOCUMENT :
5097 ipp_send_document(client);
5098 break;
5099
5100 case IPP_OP_SEND_URI :
5101 ipp_send_uri(client);
5102 break;
5103
5104 case IPP_OP_CANCEL_JOB :
5105 ipp_cancel_job(client);
5106 break;
5107
5108 case IPP_OP_GET_JOB_ATTRIBUTES :
5109 ipp_get_job_attributes(client);
5110 break;
5111
5112 case IPP_OP_GET_JOBS :
5113 ipp_get_jobs(client);
5114 break;
5115
5116 case IPP_OP_GET_PRINTER_ATTRIBUTES :
5117 ipp_get_printer_attributes(client);
5118 break;
5119
2cadf0f4
MS
5120 case IPP_OP_CLOSE_JOB :
5121 ipp_close_job(client);
5122 break;
5123
5124 case IPP_OP_IDENTIFY_PRINTER :
5125 ipp_identify_printer(client);
5126 break;
5127
8a78aa37
MS
5128 default :
5129 respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
5130 "Operation not supported.");
5131 break;
5132 }
1106b00e
MS
5133 }
5134 }
5135 }
5136 }
5137
5138 /*
5139 * Send the HTTP header and return...
5140 */
5141
a469f8a5
MS
5142 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
5143 httpFlush(client->http); /* Flush trailing (junk) data */
1106b00e 5144
a469f8a5 5145 return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
1106b00e
MS
5146 ippLength(client->response)));
5147}
5148
5149
5150/*
5151 * 'process_job()' - Process a print job.
5152 */
5153
5154static void * /* O - Thread exit status */
d46dbe1b 5155process_job(ippeve_job_t *job) /* I - Job */
1106b00e 5156{
a469f8a5
MS
5157 job->state = IPP_JSTATE_PROCESSING;
5158 job->printer->state = IPP_PSTATE_PROCESSING;
4a838088 5159 job->processing = time(NULL);
1106b00e 5160
d46dbe1b 5161 while (job->printer->state_reasons & IPPEVE_PREASON_MEDIA_EMPTY)
0b5ce83f 5162 {
d46dbe1b 5163 job->printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
0b5ce83f
MS
5164
5165 sleep(1);
5166 }
5167
d46dbe1b 5168 job->printer->state_reasons &= (ippeve_preason_t)~IPPEVE_PREASON_MEDIA_NEEDED;
0b5ce83f 5169
db8b865d
MS
5170 if (job->printer->command)
5171 {
5172 /*
5173 * Execute a command with the job spool file and wait for it to complete...
5174 */
5175
aa2a90ce
MS
5176 int pid, /* Process ID */
5177 status; /* Exit status */
5178 time_t start, /* Start time */
5179 end; /* End time */
5180 char *myargv[3], /* Command-line arguments */
5181 *myenvp[400]; /* Environment variables */
5182 int myenvc; /* Number of environment variables */
5183 ipp_attribute_t *attr; /* Job attribute */
5184 char val[1280], /* IPP_NAME=value */
5185 *valptr; /* Pointer into string */
24a06ed3 5186#ifndef _WIN32
aa2a90ce
MS
5187 int mypipe[2]; /* Pipe for stderr */
5188 char line[2048], /* Line from stderr */
5189 *ptr, /* Pointer into line */
5190 *endptr; /* End of line */
5191 ssize_t bytes; /* Bytes read */
24a06ed3 5192#endif /* !_WIN32 */
db8b865d 5193
aa2a90ce 5194 fprintf(stderr, "Running command \"%s %s\".\n", job->printer->command, job->filename);
db8b865d
MS
5195 time(&start);
5196
83ce8172
MS
5197 /*
5198 * Setup the command-line arguments...
5199 */
5200
5201 myargv[0] = job->printer->command;
5202 myargv[1] = job->filename;
5203 myargv[2] = NULL;
5204
5205 /*
aa2a90ce
MS
5206 * Copy the current environment, then add environment variables for every
5207 * Job attribute and Printer -default attributes...
83ce8172
MS
5208 */
5209
5210 for (myenvc = 0; environ[myenvc] && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); myenvc ++)
5211 myenvp[myenvc] = strdup(environ[myenvc]);
5212
aa2a90ce
MS
5213 if (myenvc > (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 32))
5214 {
5215 fprintf(stderr, "Too many environment variables to process job #%d.\n", job->id);
5216 job->state = IPP_JSTATE_ABORTED;
5217 goto error;
5218 }
5219
5220 if (asprintf(myenvp + myenvc, "CONTENT_TYPE=%s", job->format) > 0)
5221 myenvc ++;
5222
5223 if (job->printer->device_uri && asprintf(myenvp + myenvc, "DEVICE_URI=%s", job->printer->device_uri) > 0)
5224 myenvc ++;
5225
6641bd0d 5226#if !CUPS_LITE
aa2a90ce
MS
5227 if (job->printer->ppdfile && asprintf(myenvp + myenvc, "PPD=%s", job->printer->ppdfile) > 0)
5228 myenvc ++;
6641bd0d 5229#endif /* !CUPS_LITE */
aa2a90ce
MS
5230
5231 if ((attr = ippFindAttribute(job->attrs, "document-name", IPP_TAG_NAME)) != NULL && asprintf(myenvp + myenvc, "DOCUMENT_NAME=%s", ippGetString(attr, 0, NULL)) > 0)
5232 myenvc ++;
5233
5234 for (attr = ippFirstAttribute(job->printer->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->printer->attrs))
5235 {
5236 /*
5237 * Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and
5238 * then add the value(s) from the attribute.
5239 */
5240
5241 const char *name = ippGetName(attr),
5242 /* Attribute name */
5243 *suffix = strstr(name, "-default");
5244 /* Suffix on attribute name */
5245
5246 if (!suffix || suffix[8])
5247 continue;
5248
5249 valptr = val;
5250 *valptr++ = 'I';
5251 *valptr++ = 'P';
5252 *valptr++ = 'P';
5253 *valptr++ = '_';
5254 while (*name && valptr < (val + sizeof(val) - 2))
5255 {
5256 if (*name == '-')
5257 *valptr++ = '_';
5258 else
5259 *valptr++ = (char)toupper(*name & 255);
5260
5261 name ++;
5262 }
5263 *valptr++ = '=';
5264 ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
5265
5266 myenvp[myenvc++] = strdup(val);
5267 }
5268
83ce8172
MS
5269 for (attr = ippFirstAttribute(job->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->attrs))
5270 {
5271 /*
5272 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
5273 * value(s) from the attribute.
5274 */
5275
5276 const char *name = ippGetName(attr);
aa2a90ce
MS
5277 /* Attribute name */
5278
83ce8172
MS
5279 if (!name)
5280 continue;
5281
5282 valptr = val;
5283 *valptr++ = 'I';
5284 *valptr++ = 'P';
5285 *valptr++ = 'P';
5286 *valptr++ = '_';
5287 while (*name && valptr < (val + sizeof(val) - 2))
5288 {
5289 if (*name == '-')
5290 *valptr++ = '_';
5291 else
5292 *valptr++ = (char)toupper(*name & 255);
5293
5294 name ++;
5295 }
5296 *valptr++ = '=';
5297 ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
5298
5299 myenvp[myenvc++] = strdup(val);
5300 }
aa2a90ce
MS
5301
5302 if (attr)
5303 {
5304 fprintf(stderr, "Too many environment variables to process job #%d.\n", job->id);
5305 job->state = IPP_JSTATE_ABORTED;
5306 goto error;
5307 }
5308
83ce8172
MS
5309 myenvp[myenvc] = NULL;
5310
5311 /*
5312 * Now run the program...
5313 */
5314
24a06ed3 5315#ifdef _WIN32
83ce8172 5316 status = _spawnvpe(_P_WAIT, job->printer->command, myargv, myenvp);
9610a474 5317
83ce8172 5318#else
9610a474
MS
5319 if (pipe(mypipe))
5320 {
5321 perror("Unable to create pipe for stderr");
5322 mypipe[0] = mypipe[1] = -1;
5323 }
5324
db8b865d
MS
5325 if ((pid = fork()) == 0)
5326 {
5327 /*
5328 * Child comes here...
5329 */
5330
9610a474
MS
5331 close(2);
5332 dup2(mypipe[1], 2);
5333 close(mypipe[0]);
5334 close(mypipe[1]);
5335
83ce8172 5336 execve(job->printer->command, myargv, myenvp);
db8b865d
MS
5337 exit(errno);
5338 }
5339 else if (pid < 0)
5340 {
5341 /*
5342 * Unable to fork process...
5343 */
5344
5345 perror("Unable to start job processing command");
83ce8172 5346 status = -1;
1ec50c42 5347
9610a474
MS
5348 close(mypipe[0]);
5349 close(mypipe[1]);
5350
1ec50c42
MS
5351 /*
5352 * Free memory used for environment...
5353 */
5354
5355 while (myenvc > 0)
5356 free(myenvp[-- myenvc]);
db8b865d
MS
5357 }
5358 else
5359 {
83ce8172
MS
5360 /*
5361 * Free memory used for environment...
5362 */
5363
5364 while (myenvc > 0)
5365 free(myenvp[-- myenvc]);
9610a474
MS
5366
5367 /*
5368 * If the pipe exists, read from it until EOF...
5369 */
5370
5371 if (mypipe[0] >= 0)
5372 {
5373 close(mypipe[1]);
5374
5375 endptr = line;
5376 while ((bytes = read(mypipe[0], endptr, sizeof(line) - (size_t)(endptr - line) - 1)) > 0)
5377 {
5378 endptr += bytes;
5379 *endptr = '\0';
5380
5381 while ((ptr = strchr(line, '\n')) != NULL)
5382 {
5383 *ptr++ = '\0';
5384
5385 if (!strncmp(line, "STATE:", 6))
5386 {
5387 /*
5388 * Process printer-state-reasons keywords.
5389 */
5390
5391 process_state_message(job, line);
5392 }
5393 else if (!strncmp(line, "ATTR:", 5))
5394 {
5395 /*
5396 * Process printer attribute update.
5397 */
5398
5399 process_attr_message(job, line);
5400 }
5401 else if (Verbosity > 1)
5402 fprintf(stderr, "%s: %s\n", job->printer->command, line);
5403
5404 bytes = ptr - line;
5405 if (ptr < endptr)
63efa616 5406 memmove(line, ptr, (size_t)(endptr - ptr));
9610a474
MS
5407 endptr -= bytes;
5408 *endptr = '\0';
5409 }
5410 }
5411
5412 close(mypipe[0]);
5413 }
5414
db8b865d
MS
5415 /*
5416 * Wait for child to complete...
5417 */
5418
83ce8172 5419# ifdef HAVE_WAITPID
db8b865d 5420 while (waitpid(pid, &status, 0) < 0);
83ce8172 5421# else
db8b865d 5422 while (wait(&status) < 0);
83ce8172
MS
5423# endif /* HAVE_WAITPID */
5424 }
24a06ed3 5425#endif /* _WIN32 */
db8b865d 5426
83ce8172
MS
5427 if (status)
5428 {
24a06ed3 5429#ifndef _WIN32
83ce8172 5430 if (WIFEXITED(status))
24a06ed3 5431#endif /* !_WIN32 */
6d56631f 5432 fprintf(stderr, "Command \"%s\" exited with status %d.\n", job->printer->command, WEXITSTATUS(status));
24a06ed3 5433#ifndef _WIN32
db8b865d 5434 else
6d56631f 5435 fprintf(stderr, "Command \"%s\" terminated with signal %d.\n", job->printer->command, WTERMSIG(status));
24a06ed3 5436#endif /* !_WIN32 */
83ce8172 5437 job->state = IPP_JSTATE_ABORTED;
db8b865d 5438 }
83ce8172
MS
5439 else if (status < 0)
5440 job->state = IPP_JSTATE_ABORTED;
5441 else
6d56631f 5442 fprintf(stderr, "Command \"%s\" completed successfully.\n", job->printer->command);
db8b865d
MS
5443
5444 /*
5445 * Make sure processing takes at least 5 seconds...
5446 */
5447
5448 time(&end);
5449 if ((end - start) < 5)
5450 sleep(5);
5451 }
5452 else
5453 {
5454 /*
5455 * Sleep for a random amount of time to simulate job processing.
5456 */
5457
7d5824d6 5458 sleep((unsigned)(5 + (rand() % 11)));
db8b865d 5459 }
1106b00e
MS
5460
5461 if (job->cancel)
a469f8a5 5462 job->state = IPP_JSTATE_CANCELED;
4a838088 5463 else if (job->state == IPP_JSTATE_PROCESSING)
a469f8a5 5464 job->state = IPP_JSTATE_COMPLETED;
1106b00e 5465
aa2a90ce
MS
5466 error:
5467
1106b00e 5468 job->completed = time(NULL);
a469f8a5 5469 job->printer->state = IPP_PSTATE_IDLE;
1106b00e
MS
5470 job->printer->active_job = NULL;
5471
5472 return (NULL);
5473}
5474
5475
9610a474
MS
5476/*
5477 * 'process_state_message()' - Process a STATE: message from a command.
5478 */
5479
5480static void
5481process_state_message(
d46dbe1b 5482 ippeve_job_t *job, /* I - Job */
9610a474
MS
5483 char *message) /* I - Message */
5484{
5485 int i; /* Looping var */
d46dbe1b 5486 ippeve_preason_t state_reasons, /* printer-state-reasons values */
9610a474
MS
5487 bit; /* Current reason bit */
5488 char *ptr, /* Pointer into message */
5489 *next; /* Next keyword in message */
5490 int remove; /* Non-zero if we are removing keywords */
5491
5492
5493 /*
5494 * Skip leading "STATE:" and any whitespace...
5495 */
5496
5497 for (message += 6; *message; message ++)
5498 if (*message != ' ' && *message != '\t')
5499 break;
5500
5501 /*
5502 * Support the following forms of message:
5503 *
5504 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
5505 *
5506 * "-keyword[,keyword,...]" to remove keywords.
5507 *
5508 * "+keyword[,keyword,...]" to add keywords.
5509 *
5510 * Keywords may or may not have a suffix (-report, -warning, -error) per
8a4ed632 5511 * RFC 8011.
9610a474
MS
5512 */
5513
5514 if (*message == '-')
5515 {
5516 remove = 1;
5517 state_reasons = job->printer->state_reasons;
5518 message ++;
5519 }
5520 else if (*message == '+')
5521 {
5522 remove = 0;
5523 state_reasons = job->printer->state_reasons;
5524 message ++;
5525 }
5526 else
5527 {
5528 remove = 0;
d46dbe1b 5529 state_reasons = IPPEVE_PREASON_NONE;
9610a474
MS
5530 }
5531
5532 while (*message)
5533 {
5534 if ((next = strchr(message, ',')) != NULL)
5535 *next++ = '\0';
5536
5537 if ((ptr = strstr(message, "-error")) != NULL)
5538 *ptr = '\0';
5539 else if ((ptr = strstr(message, "-report")) != NULL)
5540 *ptr = '\0';
5541 else if ((ptr = strstr(message, "-warning")) != NULL)
5542 *ptr = '\0';
5543
d46dbe1b 5544 for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
9610a474 5545 {
d46dbe1b 5546 if (!strcmp(message, ippeve_preason_strings[i]))
9610a474
MS
5547 {
5548 if (remove)
5549 state_reasons &= ~bit;
5550 else
5551 state_reasons |= bit;
5552 }
5553 }
5554
5555 if (next)
5556 message = next;
5557 else
5558 break;
5559 }
5560
5561 job->printer->state_reasons = state_reasons;
5562}
5563
5564
1106b00e
MS
5565/*
5566 * 'register_printer()' - Register a printer object via Bonjour.
5567 */
5568
5569static int /* O - 1 on success, 0 on error */
5570register_printer(
d46dbe1b 5571 ippeve_printer_t *printer, /* I - Printer */
1562b9a1 5572 const char *subtypes) /* I - Service subtype(s) */
1106b00e 5573{
0a15691a 5574#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
d46dbe1b 5575 ippeve_txt_t ipp_txt; /* Bonjour IPP TXT record */
1562b9a1
MS
5576 int i, /* Looping var */
5577 count; /* Number of values */
5578 ipp_attribute_t *color_supported,
5579 *document_format_supported,
5580 *printer_location,
5581 *printer_make_and_model,
5582 *printer_more_info,
5583 *printer_uuid,
5584 *sides_supported,
5585 *urf_supported; /* Printer attributes */
5586 const char *value; /* Value string */
5587 char formats[252], /* List of supported formats */
5588 urf[252], /* List of supported URF values */
5589 *ptr; /* Pointer into string */
5590
5591 color_supported = ippFindAttribute(printer->attrs, "color-supported", IPP_TAG_BOOLEAN);
5592 document_format_supported = ippFindAttribute(printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE);
5593 printer_location = ippFindAttribute(printer->attrs, "printer-location", IPP_TAG_TEXT);
5594 printer_make_and_model = ippFindAttribute(printer->attrs, "printer-make-and-model", IPP_TAG_TEXT);
5595 printer_more_info = ippFindAttribute(printer->attrs, "printer-more-info", IPP_TAG_URI);
5596 printer_uuid = ippFindAttribute(printer->attrs, "printer-uuid", IPP_TAG_URI);
5597 sides_supported = ippFindAttribute(printer->attrs, "sides-supported", IPP_TAG_KEYWORD);
5598 urf_supported = ippFindAttribute(printer->attrs, "urf-supported", IPP_TAG_KEYWORD);
5599
5600 for (i = 0, count = ippGetCount(document_format_supported), ptr = formats; i < count; i ++)
5601 {
5602 value = ippGetString(document_format_supported, i, NULL);
5603
5604 if (!strcasecmp(value, "application/octet-stream"))
5605 continue;
5606
5607 if (ptr > formats && ptr < (formats + sizeof(formats) - 1))
5608 *ptr++ = ',';
5609
5610 strlcpy(ptr, value, sizeof(formats) - (size_t)(ptr - formats));
5611 ptr += strlen(ptr);
5612
5613 if (ptr >= (formats + sizeof(formats) - 1))
5614 break;
5615 }
5616
5617 urf[0] = '\0';
5618 for (i = 0, count = ippGetCount(urf_supported), ptr = urf; i < count; i ++)
5619 {
5620 value = ippGetString(urf_supported, i, NULL);
5621
5622 if (ptr > urf && ptr < (urf + sizeof(urf) - 1))
5623 *ptr++ = ',';
5624
5625 strlcpy(ptr, value, sizeof(urf) - (size_t)(ptr - urf));
5626 ptr += strlen(ptr);
5627
5628 if (ptr >= (urf + sizeof(urf) - 1))
5629 break;
5630 }
5631
0a15691a 5632#endif /* HAVE_DNSSD || HAVE_AVAHI */
d6563739 5633#ifdef HAVE_DNSSD
1106b00e 5634 DNSServiceErrorType error; /* Error from Bonjour */
1562b9a1 5635 char regtype[256]; /* Bonjour service type */
1106b00e
MS
5636
5637
5638 /*
5639 * Build the TXT record for IPP...
5640 */
5641
d6563739
MS
5642 TXTRecordCreate(&ipp_txt, 1024, NULL);
5643 TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print");
1562b9a1
MS
5644 if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
5645 TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(value), value);
5646 if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL)
5647 TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(value), value);
5648 if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
5649 TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(value), value);
5650 TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats), formats);
5651 TXTRecordSetValue(&ipp_txt, "Color", 1, ippGetBoolean(color_supported, 0) ? "T" : "F");
5652 TXTRecordSetValue(&ipp_txt, "Duplex", 1, ippGetCount(sides_supported) > 1 ? "T" : "F");
5653 if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
5654 TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(value) - 9, value + 9);
fe202ff4 5655# ifdef HAVE_SSL
d6563739 5656 TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2");
fe202ff4 5657# endif /* HAVE_SSL */
1562b9a1
MS
5658 if (urf[0])
5659 TXTRecordSetValue(&ipp_txt, "URF", strlen(urf), urf);
a2798463
MS
5660 TXTRecordSetValue(&ipp_txt, "txtvers", 1, "1");
5661 TXTRecordSetValue(&ipp_txt, "qtotal", 1, "1");
1106b00e 5662
1106b00e
MS
5663 /*
5664 * Register the _printer._tcp (LPD) service type with a port number of 0 to
5665 * defend our service name but not actually support LPD...
5666 */
5667
d6563739 5668 printer->printer_ref = DNSSDMaster;
1106b00e 5669
1562b9a1
MS
5670 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)
5671 {
5672 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_printer._tcp", error);
1106b00e
MS
5673 return (0);
5674 }
5675
5676 /*
1562b9a1 5677 * Then register the _ipp._tcp (IPP) service type with the real port number to
1106b00e
MS
5678 * advertise our IPP printer...
5679 */
5680
d6563739 5681 printer->ipp_ref = DNSSDMaster;
1106b00e 5682
1562b9a1
MS
5683 if (subtypes && *subtypes)
5684 snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtypes);
a469f8a5 5685 else
015214aa 5686 strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
a469f8a5 5687
1562b9a1
MS
5688 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)
5689 {
5690 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
1106b00e
MS
5691 return (0);
5692 }
5693
f93b32b6 5694# ifdef HAVE_SSL
a469f8a5 5695 /*
1562b9a1 5696 * Then register the _ipps._tcp (IPP) service type with the real port number to
d6563739 5697 * advertise our IPPS printer...
a469f8a5
MS
5698 */
5699
d6563739 5700 printer->ipps_ref = DNSSDMaster;
a469f8a5 5701
1562b9a1
MS
5702 if (subtypes && *subtypes)
5703 snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtypes);
a469f8a5 5704 else
015214aa 5705 strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
a469f8a5 5706
1562b9a1
MS
5707 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)
5708 {
5709 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
a469f8a5
MS
5710 return (0);
5711 }
5712# endif /* HAVE_SSL */
5713
1106b00e
MS
5714 /*
5715 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
5716 * real port number to advertise our IPP printer...
5717 */
5718
d6563739 5719 printer->http_ref = DNSSDMaster;
1106b00e 5720
1562b9a1
MS
5721 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)
5722 {
5723 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_http._tcp,_printer", error);
1106b00e
MS
5724 return (0);
5725 }
5726
d6563739
MS
5727 TXTRecordDeallocate(&ipp_txt);
5728
5729#elif defined(HAVE_AVAHI)
5730 char temp[256]; /* Subtype service string */
5731
5732 /*
5733 * Create the TXT record...
5734 */
5735
5736 ipp_txt = NULL;
5737 ipp_txt = avahi_string_list_add_printf(ipp_txt, "rp=ipp/print");
1562b9a1
MS
5738 if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
5739 ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s", value);
5740 if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL)
5741 ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", value);
5742 if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
5743 ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", value);
d6563739 5744 ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
1562b9a1
MS
5745 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", ippGetBoolean(color_supported, 0) ? "T" : "F");
5746 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", ippGetCount(sides_supported) > 1 ? "T" : "F");
5747 if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
5748 ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", value + 9);
d6563739
MS
5749# ifdef HAVE_SSL
5750 ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2");
5751# endif /* HAVE_SSL */
1562b9a1
MS
5752 if (urf[0])
5753 ipp_txt = avahi_string_list_add_printf(ipp_txt, "URF=%s", urf);
5754 ipp_txt = avahi_string_list_add_printf(ipp_txt, "txtvers=1");
5755 ipp_txt = avahi_string_list_add_printf(ipp_txt, "qtotal=1");
d6563739
MS
5756
5757 /*
5758 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
5759 */
5760
5761 avahi_threaded_poll_lock(DNSSDMaster);
5762
5763 printer->ipp_ref = avahi_entry_group_new(DNSSDClient, dnssd_callback, NULL);
5764
5765 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);
5766
5767 /*
d46dbe1b 5768 * Then register the ippeve._tcp (IPP)...
d6563739
MS
5769 */
5770
5771 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 5772 if (subtypes && *subtypes)
d6563739
MS
5773 {
5774 snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", subtype);
5775 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp);
5776 }
5777
5778#ifdef HAVE_SSL
5779 /*
d46dbe1b 5780 * ippeves._tcp (IPPS) for secure printing...
d6563739
MS
5781 */
5782
5783 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 5784 if (subtypes && *subtypes)
d6563739
MS
5785 {
5786 snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", subtype);
5787 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp);
5788 }
5789#endif /* HAVE_SSL */
5790
5791 /*
5792 * Finally _http.tcp (HTTP) for the web interface...
5793 */
5794
5795 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);
5796 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");
5797
5798 /*
5799 * Commit it...
5800 */
5801
5802 avahi_entry_group_commit(printer->ipp_ref);
5803 avahi_threaded_poll_unlock(DNSSDMaster);
5804
5805 avahi_string_list_free(ipp_txt);
5806#endif /* HAVE_DNSSD */
5807
1106b00e
MS
5808 return (1);
5809}
5810
5811
5812/*
5813 * 'respond_http()' - Send a HTTP response.
5814 */
5815
5816int /* O - 1 on success, 0 on failure */
a469f8a5 5817respond_http(
d46dbe1b 5818 ippeve_client_t *client, /* I - Client */
a469f8a5
MS
5819 http_status_t code, /* I - HTTP status of response */
5820 const char *content_encoding, /* I - Content-Encoding of response */
5821 const char *type, /* I - MIME media type of response */
5822 size_t length) /* I - Length of response */
1106b00e
MS
5823{
5824 char message[1024]; /* Text message */
5825
5826
a469f8a5 5827 fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
1106b00e 5828
a469f8a5 5829 if (code == HTTP_STATUS_CONTINUE)
1106b00e
MS
5830 {
5831 /*
5832 * 100-continue doesn't send any headers...
5833 */
5834
a469f8a5 5835 return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
1106b00e
MS
5836 }
5837
5838 /*
5839 * Format an error message...
5840 */
5841
fe202ff4 5842 if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
1106b00e
MS
5843 {
5844 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
5845
5846 type = "text/plain";
5847 length = strlen(message);
5848 }
5849 else
5850 message[0] = '\0';
5851
5852 /*
a469f8a5 5853 * Send the HTTP response header...
1106b00e
MS
5854 */
5855
a469f8a5 5856 httpClearFields(client->http);
1106b00e 5857
a469f8a5
MS
5858 if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
5859 client->operation == HTTP_STATE_OPTIONS)
5860 httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
1106b00e
MS
5861
5862 if (type)
5863 {
5864 if (!strcmp(type, "text/html"))
a469f8a5
MS
5865 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
5866 "text/html; charset=utf-8");
5867 else
5868 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
1106b00e 5869
a469f8a5
MS
5870 if (content_encoding)
5871 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
1106b00e 5872 }
1106b00e 5873
a469f8a5
MS
5874 httpSetLength(client->http, length);
5875
5876 if (httpWriteResponse(client->http, code) < 0)
1106b00e
MS
5877 return (0);
5878
5879 /*
5880 * Send the response data...
5881 */
5882
5883 if (message[0])
5884 {
5885 /*
5886 * Send a plain text message.
5887 */
5888
a469f8a5
MS
5889 if (httpPrintf(client->http, "%s", message) < 0)
5890 return (0);
5891
5892 if (httpWrite2(client->http, "", 0) < 0)
1106b00e
MS
5893 return (0);
5894 }
5895 else if (client->response)
5896 {
5897 /*
5898 * Send an IPP response...
5899 */
5900
83e08001 5901 debug_attributes("Response", client->response, 2);
1106b00e 5902
a469f8a5 5903 ippSetState(client->response, IPP_STATE_IDLE);
1106b00e 5904
a469f8a5 5905 if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
1106b00e
MS
5906 return (0);
5907 }
1106b00e 5908
a469f8a5 5909 return (1);
1106b00e
MS
5910}
5911
5912
5913/*
5914 * 'respond_ipp()' - Send an IPP response.
5915 */
5916
5917static void
d46dbe1b 5918respond_ipp(ippeve_client_t *client, /* I - Client */
1106b00e
MS
5919 ipp_status_t status, /* I - status-code */
5920 const char *message, /* I - printf-style status-message */
5921 ...) /* I - Additional args as needed */
5922{
a469f8a5 5923 const char *formatted = NULL; /* Formatted message */
1106b00e
MS
5924
5925
a469f8a5 5926 ippSetStatusCode(client->response, status);
1106b00e
MS
5927
5928 if (message)
5929 {
a469f8a5
MS
5930 va_list ap; /* Pointer to additional args */
5931 ipp_attribute_t *attr; /* New status-message attribute */
5932
1106b00e 5933 va_start(ap, message);
6d56631f 5934 if ((attr = ippFindAttribute(client->response, "status-message", IPP_TAG_TEXT)) != NULL)
a469f8a5
MS
5935 ippSetStringfv(client->response, &attr, 0, message, ap);
5936 else
6d56631f 5937 attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, "status-message", NULL, message, ap);
1106b00e
MS
5938 va_end(ap);
5939
a469f8a5 5940 formatted = ippGetString(attr, 0, NULL);
1106b00e 5941 }
1106b00e 5942
a469f8a5 5943 if (formatted)
6d56631f 5944 fprintf(stderr, "%s %s %s (%s)\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status), formatted);
a469f8a5 5945 else
6d56631f 5946 fprintf(stderr, "%s %s %s\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status));
1106b00e
MS
5947}
5948
5949
83e08001
MS
5950/*
5951 * 'respond_unsupported()' - Respond with an unsupported attribute.
5952 */
5953
5954static void
5955respond_unsupported(
d46dbe1b 5956 ippeve_client_t *client, /* I - Client */
83e08001
MS
5957 ipp_attribute_t *attr) /* I - Atribute */
5958{
a2326b5b
MS
5959 ipp_attribute_t *temp; /* Copy of attribute */
5960
5961
6d56631f 5962 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Unsupported %s %s%s value.", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)));
83e08001 5963
a2326b5b
MS
5964 temp = ippCopyAttribute(client->response, attr, 0);
5965 ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
83e08001
MS
5966}
5967
5968
1106b00e
MS
5969/*
5970 * 'run_printer()' - Run the printer service.
5971 */
5972
5973static void
d46dbe1b 5974run_printer(ippeve_printer_t *printer) /* I - Printer */
1106b00e 5975{
0268488e 5976 int num_fds; /* Number of file descriptors */
1106b00e
MS
5977 struct pollfd polldata[3]; /* poll() data */
5978 int timeout; /* Timeout for poll() */
d46dbe1b 5979 ippeve_client_t *client; /* New client */
1106b00e
MS
5980
5981
5982 /*
5983 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5984 */
5985
5986 polldata[0].fd = printer->ipv4;
5987 polldata[0].events = POLLIN;
5988
5989 polldata[1].fd = printer->ipv6;
5990 polldata[1].events = POLLIN;
5991
0268488e
MS
5992 num_fds = 2;
5993
5994#ifdef HAVE_DNSSD
d6563739 5995 polldata[num_fds ].fd = DNSServiceRefSockFD(DNSSDMaster);
0268488e
MS
5996 polldata[num_fds ++].events = POLLIN;
5997#endif /* HAVE_DNSSD */
1106b00e
MS
5998
5999 /*
6000 * Loop until we are killed or have a hard error...
6001 */
6002
6003 for (;;)
6004 {
6005 if (cupsArrayCount(printer->jobs))
6006 timeout = 10;
6007 else
6008 timeout = -1;
6009
7e86f2f6 6010 if (poll(polldata, (nfds_t)num_fds, timeout) < 0 && errno != EINTR)
1106b00e
MS
6011 {
6012 perror("poll() failed");
6013 break;
6014 }
6015
6016 if (polldata[0].revents & POLLIN)
6017 {
6018 if ((client = create_client(printer, printer->ipv4)) != NULL)
6019 {
116c301f
MS
6020 _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
6021
6022 if (t)
6023 {
6024 _cupsThreadDetach(t);
6025 }
6026 else
1106b00e
MS
6027 {
6028 perror("Unable to create client thread");
6029 delete_client(client);
6030 }
6031 }
6032 }
6033
6034 if (polldata[1].revents & POLLIN)
6035 {
6036 if ((client = create_client(printer, printer->ipv6)) != NULL)
6037 {
116c301f
MS
6038 _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
6039
6040 if (t)
6041 {
6042 _cupsThreadDetach(t);
6043 }
6044 else
1106b00e
MS
6045 {
6046 perror("Unable to create client thread");
6047 delete_client(client);
6048 }
6049 }
6050 }
6051
0268488e 6052#ifdef HAVE_DNSSD
1106b00e 6053 if (polldata[2].revents & POLLIN)
d6563739 6054 DNSServiceProcessResult(DNSSDMaster);
0268488e 6055#endif /* HAVE_DNSSD */
1106b00e
MS
6056
6057 /*
6058 * Clean out old jobs...
6059 */
6060
6061 clean_jobs(printer);
6062 }
6063}
6064
6065
9141aa01
MS
6066/*
6067 * 'show_media()' - Show media load state.
6068 */
6069
6070static int /* O - 1 on success, 0 on failure */
6071show_media(ippeve_client_t *client) /* I - Client connection */
6072{
6073 ippeve_printer_t *printer = client->printer;
6074 /* Printer */
6075 int i, j, /* Looping vars */
6076 num_ready, /* Number of ready media */
6077 num_sizes, /* Number of media sizes */
6078 num_sources, /* Number of media sources */
6079 num_types; /* Number of media types */
6080 ipp_attribute_t *media_col_ready,/* media-col-ready attribute */
6081 *media_ready, /* media-ready attribute */
6082 *media_sizes, /* media-supported attribute */
6083 *media_sources, /* media-source-supported attribute */
6084 *media_types, /* media-type-supported attribute */
6085 *input_tray; /* printer-input-tray attribute */
6086 ipp_t *media_col; /* media-col value */
6087 const char *media_size, /* media value */
6088 *media_source, /* media-source value */
6089 *media_type, /* media-type value */
6090 *ready_size, /* media-col-ready media-size[-name] value */
6091 *ready_source, /* media-col-ready media-source value */
6092 *ready_tray, /* printer-input-tray value */
6093 *ready_type; /* media-col-ready media-type value */
6094 char tray_str[1024], /* printer-input-tray string value */
6095 *tray_ptr; /* Pointer into value */
6096 int tray_len; /* Length of printer-input-tray value */
6097 int ready_sheets; /* printer-input-tray sheets value */
6098 int num_options; /* Number of form options */
6099 cups_option_t *options; /* Form options */
6100 static const int sheets[] = /* Number of sheets */
6101 {
6102 250,
6103 100,
6104 25,
6105 5,
6106 0
6107 };
6108
6109
6110 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
6111 return (0);
6112
6113 html_header(client, printer->name);
6114
6115 if ((media_col_ready = ippFindAttribute(printer->attrs, "media-col-ready", IPP_TAG_BEGIN_COLLECTION)) == NULL)
6116 {
6117 html_printf(client, "<p>Error: No media-col-ready defined for printer.</p>\n");
6118 html_footer(client);
6119 return (1);
6120 }
6121
6122 media_ready = ippFindAttribute(printer->attrs, "media-ready", IPP_TAG_ZERO);
6123
6124 if ((media_sizes = ippFindAttribute(printer->attrs, "media-supported", IPP_TAG_ZERO)) == NULL)
6125 {
6126 html_printf(client, "<p>Error: No media-supported defined for printer.</p>\n");
6127 html_footer(client);
6128 return (1);
6129 }
6130
6131 if ((media_sources = ippFindAttribute(printer->attrs, "media-source-supported", IPP_TAG_ZERO)) == NULL)
6132 {
6133 html_printf(client, "<p>Error: No media-source-supported defined for printer.</p>\n");
6134 html_footer(client);
6135 return (1);
6136 }
6137
6138 if ((media_types = ippFindAttribute(printer->attrs, "media-type-supported", IPP_TAG_ZERO)) == NULL)
6139 {
6140 html_printf(client, "<p>Error: No media-type-supported defined for printer.</p>\n");
6141 html_footer(client);
6142 return (1);
6143 }
6144
6145 if ((input_tray = ippFindAttribute(printer->attrs, "printer-input-tray", IPP_TAG_STRING)) == NULL)
6146 {
6147 html_printf(client, "<p>Error: No printer-input-tray defined for printer.</p>\n");
6148 html_footer(client);
6149 return (1);
6150 }
6151
6152 num_ready = ippGetCount(media_col_ready);
6153 num_sizes = ippGetCount(media_sizes);
6154 num_sources = ippGetCount(media_sources);
6155 num_types = ippGetCount(media_types);
6156
6157 if (num_sources != ippGetCount(input_tray))
6158 {
6159 html_printf(client, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n");
6160 html_footer(client);
6161 return (1);
6162 }
6163
6164 /*
6165 * Process form data if present...
6166 */
6167
6168 if ((num_options = parse_options(client, &options)) > 0)
6169 {
6170 /*
6171 * WARNING: A real printer/server implementation MUST NOT implement
6172 * media updates via a GET request - GET requests are supposed to be
6173 * idempotent (without side-effects) and we obviously are not
6174 * authenticating access here. This form is provided solely to
6175 * enable testing and development!
6176 */
6177
6178 char name[255]; /* Form name */
6179 const char *val; /* Form value */
6180 pwg_media_t *media; /* Media info */
6181
6182 _cupsRWLockWrite(&printer->rwlock);
6183
6184 ippDeleteAttribute(printer->attrs, input_tray);
6185 input_tray = NULL;
6186
6187 ippDeleteAttribute(printer->attrs, media_col_ready);
6188 media_col_ready = NULL;
6189
6190 if (media_ready)
6191 {
6192 ippDeleteAttribute(printer->attrs, media_ready);
6193 media_ready = NULL;
6194 }
6195
6196 printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MEDIA_LOW | IPPEVE_PREASON_MEDIA_EMPTY | IPPEVE_PREASON_MEDIA_NEEDED);
6197
6198 for (i = 0; i < num_sources; i ++)
6199 {
6200 media_source = ippGetString(media_sources, i, NULL);
6201
6202 snprintf(name, sizeof(name), "size%d", i);
6203 if ((media_size = cupsGetOption(name, num_options, options)) != NULL && (media = pwgMediaForPWG(media_size)) != NULL)
6204 {
6205 snprintf(name, sizeof(name), "type%d", i);
6206 if ((media_type = cupsGetOption(name, num_options, options)) != NULL && !*media_type)
6207 media_type = NULL;
6208
6209 if (media_ready)
6210 ippSetString(printer->attrs, &media_ready, ippGetCount(media_ready), media_size);
6211 else
6212 media_ready = ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, media_size);
6213
6214 media_col = create_media_col(media_size, media_source, media_type, media->width, media->length, -1, -1, -1, -1);
6215
6216 if (media_col_ready)
6217 ippSetCollection(printer->attrs, &media_col_ready, ippGetCount(media_col_ready), media_col);
6218 else
6219 media_col_ready = ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-ready", media_col);
6220 ippDelete(media_col);
6221 }
6222 else
6223 media = NULL;
6224
6225 snprintf(name, sizeof(name), "level%d", i);
6226 if ((val = cupsGetOption(name, num_options, options)) != NULL)
6227 ready_sheets = atoi(val);
6228 else
6229 ready_sheets = 0;
6230
6231 snprintf(tray_str, sizeof(tray_str), "type=sheetFeedAutoRemovableTray;mediafeed=%d;mediaxfeed=%d;maxcapacity=250;level=%d;status=0;name=%s;", media ? media->length : 0, media ? media->width : 0, ready_sheets, media_source);
6232
6233 if (input_tray)
6234 ippSetOctetString(printer->attrs, &input_tray, ippGetCount(input_tray), tray_str, (int)strlen(tray_str));
6235 else
6236 input_tray = ippAddOctetString(printer->attrs, IPP_TAG_PRINTER, "printer-input-tray", tray_str, (int)strlen(tray_str));
6237
6238 if (ready_sheets == 0)
6239 {
6240 printer->state_reasons |= IPPEVE_PREASON_MEDIA_EMPTY;
6241 if (printer->active_job)
6242 printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
6243 }
6244 else if (ready_sheets < 25)
6245 printer->state_reasons |= IPPEVE_PREASON_MEDIA_LOW;
6246 }
6247
6248 if (!media_col_ready)
6249 media_col_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-col-ready");
6250
6251 if (!media_ready)
6252 media_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-ready");
6253
6254 _cupsRWUnlock(&printer->rwlock);
6255
6256 html_printf(client, "<blockquote>Media updated.</blockquote>\n");
6257 }
6258
6259 html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
6260
6261 html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
6262 for (i = 0; i < num_sources; i ++)
6263 {
6264 media_source = ippGetString(media_sources, i, NULL);
6265
6266 for (j = 0, ready_size = NULL, ready_type = NULL; j < num_ready; j ++)
6267 {
6268 media_col = ippGetCollection(media_col_ready, j);
6269 ready_size = ippGetString(ippFindAttribute(media_col, "media-size-name", IPP_TAG_ZERO), 0, NULL);
6270 ready_source = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL);
6271 ready_type = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL);
6272
6273 if (ready_source && !strcmp(ready_source, media_source))
6274 break;
6275
6276 ready_source = NULL;
6277 ready_size = NULL;
6278 ready_type = NULL;
6279 }
6280
6281 /*
6282 * Media size...
6283 */
6284
6285 html_printf(client, "<tr><th>%s:</th><td><select name=\"size%d\"><option value=\"\">None</option>", media_source, i);
6286 for (j = 0; j < num_sizes; j ++)
6287 {
6288 media_size = ippGetString(media_sizes, j, NULL);
6289
6290 html_printf(client, "<option%s>%s</option>", (ready_size && !strcmp(ready_size, media_size)) ? " selected" : "", media_size);
6291 }
6292 html_printf(client, "</select>\n");
6293
6294 /*
6295 * Media type...
6296 */
6297
6298 html_printf(client, "<select name=\"type%d\"><option value=\"\">None</option>", i);
6299 for (j = 0; j < num_types; j ++)
6300 {
6301 media_type = ippGetString(media_types, j, NULL);
6302
6303 html_printf(client, "<option%s>%s</option>", (ready_type && !strcmp(ready_type, media_type)) ? " selected" : "", media_type);
6304 }
6305 html_printf(client, "</select>\n");
6306
6307 /*
6308 * Level/sheets loaded...
6309 */
6310
6311 if ((ready_tray = ippGetOctetString(input_tray, i, &tray_len)) != NULL)
6312 {
6313 if (tray_len > (sizeof(tray_str) - 1))
6314 tray_len = sizeof(tray_str) - 1;
6315 memcpy(tray_str, ready_tray, (size_t)tray_len);
6316 tray_str[tray_len] = '\0';
6317
6318 if ((tray_ptr = strstr(tray_str, "level=")) != NULL)
6319 ready_sheets = atoi(tray_ptr + 6);
6320 else
6321 ready_sheets = 0;
6322 }
6323 else
6324 ready_sheets = 0;
6325
6326 html_printf(client, "<select name=\"level%d\">", i);
6327 for (j = 0; j < (int)(sizeof(sheets) / sizeof(sheets[0])); j ++)
6328 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "", sheets[j]);
6329 html_printf(client, "</select></td></tr>\n");
6330 }
6331
6332 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
6333 html_footer(client);
6334
6335 return (1);
6336}
6337
6338
6339/*
6340 * 'show_status()' - Show printer/system state.
6341 */
6342
6343static int /* O - 1 on success, 0 on failure */
6344show_status(ippeve_client_t *client) /* I - Client connection */
6345{
6346 ippeve_printer_t *printer = client->printer;
6347 /* Printer */
6348 ippeve_job_t *job; /* Current job */
6349 int i; /* Looping var */
6350 ippeve_preason_t reason; /* Current reason */
6351 static const char * const reasons[] = /* Reason strings */
6352 {
6353 "Other",
6354 "Cover Open",
6355 "Input Tray Missing",
6356 "Marker Supply Empty",
6357 "Marker Supply Low",
6358 "Marker Waste Almost Full",
6359 "Marker Waste Full",
6360 "Media Empty",
6361 "Media Jam",
6362 "Media Low",
6363 "Media Needed",
6364 "Moving to Paused",
6365 "Paused",
6366 "Spool Area Full",
6367 "Toner Empty",
6368 "Toner Low"
6369 };
6370
6371
6372 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
6373 return (0);
6374
6375 html_header(client, printer->name);
6376 html_printf(client, "<h1><img align=\"left\" src=\"/icon.png\" width=\"64\" height=\"64\">%s Jobs</h1>\n", printer->name);
6377 html_printf(client, "<p>%s, %d job(s).", printer->state == IPP_PSTATE_IDLE ? "Idle" : printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(printer->jobs));
6378 for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
6379 if (printer->state_reasons & reason)
6380 html_printf(client, "\n<br>&nbsp;&nbsp;&nbsp;&nbsp;%s", reasons[i]);
6381 html_printf(client, "</p>\n");
6382
6383 if (cupsArrayCount(printer->jobs) > 0)
6384 {
6385 _cupsRWLockRead(&(printer->rwlock));
6386
6387 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");
6388 for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs); job; job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
6389 {
6390 char when[256], /* When job queued/started/finished */
6391 hhmmss[64]; /* Time HH:MM:SS */
6392
6393 switch (job->state)
6394 {
6395 case IPP_JSTATE_PENDING :
6396 case IPP_JSTATE_HELD :
6397 snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss)));
6398 break;
6399 case IPP_JSTATE_PROCESSING :
6400 case IPP_JSTATE_STOPPED :
6401 snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss)));
6402 break;
6403 case IPP_JSTATE_ABORTED :
6404 snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
6405 break;
6406 case IPP_JSTATE_CANCELED :
6407 snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
6408 break;
6409 case IPP_JSTATE_COMPLETED :
6410 snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
6411 break;
6412 }
6413
6414 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);
6415 }
6416 html_printf(client, "</tbody></table>\n");
6417
6418 _cupsRWUnlock(&(printer->rwlock));
6419 }
6420
6421 html_footer(client);
6422
6423 return (1);
6424}
6425
6426
6427/*
6428 * 'show_supplies()' - Show printer supplies.
6429 */
6430
6431static int /* O - 1 on success, 0 on failure */
6432show_supplies(
6433 ippeve_client_t *client) /* I - Client connection */
6434{
6435 ippeve_printer_t *printer = client->printer;
6436 /* Printer */
6437 int i, /* Looping var */
6438 num_supply; /* Number of supplies */
6439 ipp_attribute_t *supply, /* printer-supply attribute */
6440 *supply_desc; /* printer-supply-description attribute */
6441 int num_options; /* Number of form options */
6442 cups_option_t *options; /* Form options */
6443 int supply_len, /* Length of supply value */
6444 level; /* Supply level */
6445 const char *supply_value; /* Supply value */
6446 char supply_text[1024], /* Supply string */
6447 *supply_ptr; /* Pointer into supply string */
6448 static const char * const printer_supply[] =
6449 { /* printer-supply values */
6450 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
6451 "maxcapacity=100;level=%d;colorantname=unknown;",
6452 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
6453 "maxcapacity=100;level=%d;colorantname=black;",
6454 "index=3;class=supplyThatIsConsumed;type=toner;unit=percent;"
6455 "maxcapacity=100;level=%d;colorantname=cyan;",
6456 "index=4;class=supplyThatIsConsumed;type=toner;unit=percent;"
6457 "maxcapacity=100;level=%d;colorantname=magenta;",
6458 "index=5;class=supplyThatIsConsumed;type=toner;unit=percent;"
6459 "maxcapacity=100;level=%d;colorantname=yellow;"
6460 };
6461 static const char * const colors[] = /* Colors for the supply-level bars */
6462 {
6463 "#777 linear-gradient(#333,#777)",
6464 "#000 linear-gradient(#666,#000)",
6465 "#0FF linear-gradient(#6FF,#0FF)",
6466 "#F0F linear-gradient(#F6F,#F0F)",
6467 "#CC0 linear-gradient(#EE6,#EE0)"
6468 };
6469
6470
6471 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
6472 return (0);
6473
6474 html_header(client, printer->name);
6475
6476 if ((supply = ippFindAttribute(printer->attrs, "printer-supply", IPP_TAG_STRING)) == NULL)
6477 {
6478 html_printf(client, "<p>Error: No printer-supply defined for printer.</p>\n");
6479 html_footer(client);
6480 return (1);
6481 }
6482
6483 num_supply = ippGetCount(supply);
6484
6485 if ((supply_desc = ippFindAttribute(printer->attrs, "printer-supply-description", IPP_TAG_TEXT)) == NULL)
6486 {
6487 html_printf(client, "<p>Error: No printer-supply-description defined for printer.</p>\n");
6488 html_footer(client);
6489 return (1);
6490 }
6491
6492 if (num_supply != ippGetCount(supply_desc))
6493 {
6494 html_printf(client, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n");
6495 html_footer(client);
6496 return (1);
6497 }
6498
6499 if ((num_options = parse_options(client, &options)) > 0)
6500 {
6501 /*
6502 * WARNING: A real printer/server implementation MUST NOT implement
6503 * supply updates via a GET request - GET requests are supposed to be
6504 * idempotent (without side-effects) and we obviously are not
6505 * authenticating access here. This form is provided solely to
6506 * enable testing and development!
6507 */
6508
6509 char name[64]; /* Form field */
6510 const char *val; /* Form value */
6511
6512 _cupsRWLockWrite(&printer->rwlock);
6513
6514 ippDeleteAttribute(printer->attrs, supply);
6515 supply = NULL;
6516
6517 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);
6518
6519 for (i = 0; i < num_supply; i ++)
6520 {
6521 snprintf(name, sizeof(name), "supply%d", i);
6522 if ((val = cupsGetOption(name, num_options, options)) != NULL)
6523 {
6524 level = atoi(val); /* New level */
6525
6526 snprintf(supply_text, sizeof(supply_text), printer_supply[i], level);
6527 if (supply)
6528 ippSetOctetString(printer->attrs, &supply, ippGetCount(supply), supply_text, (int)strlen(supply_text));
6529 else
6530 supply = ippAddOctetString(printer->attrs, IPP_TAG_PRINTER, "printer-supply", supply_text, (int)strlen(supply_text));
6531
6532 if (i == 0)
6533 {
6534 if (level == 100)
6535 printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_FULL;
6536 else if (level > 90)
6537 printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL;
6538 }
6539 else
6540 {
6541 if (level == 0)
6542 printer->state_reasons |= IPPEVE_PREASON_TONER_EMPTY;
6543 else if (level < 10)
6544 printer->state_reasons |= IPPEVE_PREASON_TONER_LOW;
6545 }
6546 }
6547 }
6548
6549 _cupsRWUnlock(&printer->rwlock);
6550
6551 html_printf(client, "<blockquote>Supplies updated.</blockquote>\n");
6552 }
6553
6554 html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
6555
6556 html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
6557 for (i = 0; i < num_supply; i ++)
6558 {
6559 supply_value = ippGetOctetString(supply, i, &supply_len);
6560 if (supply_len > (sizeof(supply_text) - 1))
6561 supply_len = sizeof(supply_text) - 1;
6562
6563 memcpy(supply_text, supply_value, (size_t)supply_len);
6564 supply_text[supply_len] = '\0';
6565
6566 if ((supply_ptr = strstr(supply_text, "level=")) != NULL)
6567 level = atoi(supply_ptr + 6);
6568 else
6569 level = 50;
6570
6571 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);
6572 }
6573 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
6574 html_footer(client);
6575
6576 return (1);
6577}
6578
6579
0b5ce83f
MS
6580/*
6581 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6582 */
6583
6584static char *
6585time_string(time_t tv, /* I - Time value */
6586 char *buffer, /* I - Buffer */
6587 size_t bufsize) /* I - Size of buffer */
6588{
6589 struct tm *curtime = localtime(&tv);
6590 /* Local time */
6591
6592 strftime(buffer, bufsize, "%X", curtime);
6593 return (buffer);
6594}
6595
6596
1106b00e
MS
6597/*
6598 * 'usage()' - Show program usage.
6599 */
6600
6601static void
6602usage(int status) /* O - Exit status */
6603{
6d56631f
MS
6604 _cupsLangPuts(stdout, _("Usage: ippeveprinter [options] \"name\""));
6605 _cupsLangPuts(stdout, _("Options:"));
6606 _cupsLangPuts(stderr, _("--help Show program help"));
6607 _cupsLangPuts(stderr, _("--version Show program version"));
6608 _cupsLangPuts(stdout, _("-2 Set 2-sided printing support (default=1-sided)"));
6609 _cupsLangPuts(stdout, _("-D device-uri Set the device URI for the printer"));
6610#ifdef HAVE_SSL
6611 _cupsLangPuts(stdout, _("-K keypath Set location of server X.509 certificates and keys."));
6612#endif /* HAVE_SSL */
6613 _cupsLangPuts(stdout, _("-M manufacturer Set manufacturer name (default=Test)"));
6614 _cupsLangPuts(stdout, _("-P filename.ppd Load printer attributes from PPD file"));
6615 _cupsLangPuts(stdout, _("-V version Set default IPP version"));
6616 _cupsLangPuts(stdout, _("-a filename.conf Load printer attributes from conf file"));
6617 _cupsLangPuts(stdout, _("-c command Set print command"));
6618 _cupsLangPuts(stdout, _("-d spool-directory Set spool directory"));
6619 _cupsLangPuts(stdout, _("-f type/subtype[,...] Set supported file types"));
6620 _cupsLangPuts(stdout, _("-i iconfile.png Set icon file"));
6621 _cupsLangPuts(stdout, _("-k Keep job spool files"));
6622 _cupsLangPuts(stdout, _("-l location Set location of printer"));
6623 _cupsLangPuts(stdout, _("-m model Set model name (default=Printer)"));
6624 _cupsLangPuts(stdout, _("-n hostname Set hostname for printer"));
6625 _cupsLangPuts(stdout, _("-p port Set port number for printer"));
6626 _cupsLangPuts(stdout, _("-r subtype Set DNS-SD service subtype"));
6627 _cupsLangPuts(stdout, _("-s speed[,color-speed] Set speed in pages per minute"));
6628 _cupsLangPuts(stderr, _("-v Be verbose"));
1106b00e
MS
6629
6630 exit(status);
6631}
6632
6633
6634/*
83e08001
MS
6635 * 'valid_doc_attributes()' - Determine whether the document attributes are
6636 * valid.
1106b00e 6637 *
83e08001
MS
6638 * When one or more document attributes are invalid, this function adds a
6639 * suitable response and attributes to the unsupported group.
1106b00e
MS
6640 */
6641
6642static int /* O - 1 if valid, 0 if not */
83e08001 6643valid_doc_attributes(
d46dbe1b 6644 ippeve_client_t *client) /* I - Client */
1106b00e 6645{
a469f8a5
MS
6646 int valid = 1; /* Valid attributes? */
6647 ipp_op_t op = ippGetOperation(client->request);
6648 /* IPP operation */
6649 const char *op_name = ippOpString(op);
6650 /* IPP operation name */
1106b00e 6651 ipp_attribute_t *attr, /* Current attribute */
a469f8a5
MS
6652 *supported; /* xxx-supported attribute */
6653 const char *compression = NULL,
6654 /* compression value */
6655 *format = NULL; /* document-format value */
1106b00e
MS
6656
6657
6658 /*
6659 * Check operation attributes...
6660 */
6661
404dde30 6662 if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
1106b00e
MS
6663 {
6664 /*
a469f8a5
MS
6665 * If compression is specified, only accept a supported value in a Print-Job
6666 * or Send-Document request...
1106b00e
MS
6667 */
6668
a469f8a5
MS
6669 compression = ippGetString(attr, 0, NULL);
6670 supported = ippFindAttribute(client->printer->attrs,
6671 "compression-supported", IPP_TAG_KEYWORD);
6672
6673 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
6674 ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
db8b865d
MS
6675 (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
6676 op != IPP_OP_VALIDATE_JOB) ||
a469f8a5
MS
6677 !ippContainsString(supported, compression))
6678 {
1106b00e 6679 respond_unsupported(client, attr);
a469f8a5
MS
6680 valid = 0;
6681 }
1106b00e 6682 else
a469f8a5 6683 {
404dde30
MS
6684 fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression);
6685
6686 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression);
a469f8a5
MS
6687
6688 if (strcmp(compression, "none"))
a654c79d
MS
6689 {
6690 if (Verbosity)
6691 fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression);
a469f8a5 6692 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
a654c79d 6693 }
a469f8a5 6694 }
1106b00e
MS
6695 }
6696
6697 /*
6698 * Is it a format we support?
6699 */
6700
404dde30 6701 if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
1106b00e 6702 {
a469f8a5
MS
6703 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
6704 ippGetGroupTag(attr) != IPP_TAG_OPERATION)
6705 {
1106b00e 6706 respond_unsupported(client, attr);
a469f8a5
MS
6707 valid = 0;
6708 }
1106b00e 6709 else
e60ec91f 6710 {
a469f8a5 6711 format = ippGetString(attr, 0, NULL);
e60ec91f 6712
6d56631f 6713 fprintf(stderr, "%s %s document-format=\"%s\"\n", client->hostname, op_name, format);
4a838088
MS
6714
6715 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format);
e60ec91f 6716 }
1106b00e
MS
6717 }
6718 else
a469f8a5 6719 {
404dde30 6720 format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL);
8a78aa37
MS
6721 if (!format)
6722 format = "application/octet-stream"; /* Should never happen */
6723
4a838088 6724 attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
a469f8a5 6725 }
1106b00e 6726
7f500d89 6727 if (format && !strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
1106b00e
MS
6728 {
6729 /*
404dde30 6730 * Auto-type the file using the first 8 bytes of the file...
1106b00e
MS
6731 */
6732
404dde30 6733 unsigned char header[8]; /* First 8 bytes of file */
1106b00e
MS
6734
6735 memset(header, 0, sizeof(header));
a469f8a5 6736 httpPeek(client->http, (char *)header, sizeof(header));
1106b00e
MS
6737
6738 if (!memcmp(header, "%PDF", 4))
6739 format = "application/pdf";
6740 else if (!memcmp(header, "%!", 2))
6741 format = "application/postscript";
404dde30 6742 else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
1106b00e
MS
6743 format = "image/jpeg";
6744 else if (!memcmp(header, "\211PNG", 4))
6745 format = "image/png";
404dde30
MS
6746 else if (!memcmp(header, "RAS2", 4))
6747 format = "image/pwg-raster";
6748 else if (!memcmp(header, "UNIRAST", 8))
6749 format = "image/urf";
6750 else
6751 format = NULL;
1106b00e
MS
6752
6753 if (format)
404dde30 6754 {
6d56631f 6755 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n", client->hostname, op_name, format);
1106b00e 6756
404dde30
MS
6757 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
6758 }
1106b00e
MS
6759 }
6760
404dde30 6761 if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
1106b00e 6762 {
a469f8a5
MS
6763 respond_unsupported(client, attr);
6764 valid = 0;
1106b00e
MS
6765 }
6766
404dde30
MS
6767 /*
6768 * document-name
6769 */
6770
6771 if ((attr = ippFindAttribute(client->request, "document-name", IPP_TAG_NAME)) != NULL)
6772 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
6773
a469f8a5 6774 return (valid);
83e08001
MS
6775}
6776
6777
6778/*
6779 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6780 *
6781 * When one or more job attributes are invalid, this function adds a suitable
6782 * response and attributes to the unsupported group.
6783 */
6784
6785static int /* O - 1 if valid, 0 if not */
6786valid_job_attributes(
d46dbe1b 6787 ippeve_client_t *client) /* I - Client */
83e08001 6788{
a469f8a5 6789 int i, /* Looping var */
63efa616 6790 count, /* Number of values */
a469f8a5 6791 valid = 1; /* Valid attributes? */
83e08001
MS
6792 ipp_attribute_t *attr, /* Current attribute */
6793 *supported; /* xxx-supported attribute */
6794
6795
6796 /*
6797 * Check operation attributes...
6798 */
6799
a469f8a5 6800 valid = valid_doc_attributes(client);
83e08001 6801
1106b00e
MS
6802 /*
6803 * Check the various job template attributes...
6804 */
6805
404dde30 6806 if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL)
1106b00e 6807 {
a469f8a5
MS
6808 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
6809 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
1106b00e
MS
6810 {
6811 respond_unsupported(client, attr);
a469f8a5 6812 valid = 0;
1106b00e
MS
6813 }
6814 }
6815
404dde30 6816 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
1106b00e 6817 {
a469f8a5 6818 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
1106b00e
MS
6819 {
6820 respond_unsupported(client, attr);
a469f8a5 6821 valid = 0;
1106b00e
MS
6822 }
6823 }
6824
404dde30 6825 if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
1106b00e 6826 {
a469f8a5
MS
6827 if (ippGetCount(attr) != 1 ||
6828 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6829 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
6830 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
6831 strcmp(ippGetString(attr, 0, NULL), "no-hold"))
1106b00e
MS
6832 {
6833 respond_unsupported(client, attr);
a469f8a5 6834 valid = 0;
1106b00e
MS
6835 }
6836 }
6837
404dde30
MS
6838 if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL)
6839 {
6840 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0)
6841 {
6842 respond_unsupported(client, attr);
6843 valid = 0;
6844 }
6845 }
6846
6847 if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL)
1106b00e 6848 {
a469f8a5
MS
6849 if (ippGetCount(attr) != 1 ||
6850 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6851 ippGetValueTag(attr) != IPP_TAG_NAMELANG))
1106b00e
MS
6852 {
6853 respond_unsupported(client, attr);
a469f8a5 6854 valid = 0;
1106b00e 6855 }
404dde30
MS
6856
6857 ippSetGroupTag(client->request, &attr, IPP_TAG_JOB);
1106b00e 6858 }
404dde30
MS
6859 else
6860 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
1106b00e 6861
404dde30 6862 if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL)
1106b00e 6863 {
a469f8a5
MS
6864 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
6865 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
1106b00e
MS
6866 {
6867 respond_unsupported(client, attr);
a469f8a5 6868 valid = 0;
1106b00e
MS
6869 }
6870 }
6871
404dde30 6872 if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
1106b00e 6873 {
a469f8a5
MS
6874 if (ippGetCount(attr) != 1 ||
6875 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6876 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
6877 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
6878 strcmp(ippGetString(attr, 0, NULL), "none"))
1106b00e
MS
6879 {
6880 respond_unsupported(client, attr);
a469f8a5 6881 valid = 0;
1106b00e
MS
6882 }
6883 }
6884
404dde30 6885 if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
1106b00e 6886 {
a469f8a5
MS
6887 if (ippGetCount(attr) != 1 ||
6888 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6889 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
6890 ippGetValueTag(attr) != IPP_TAG_KEYWORD))
1106b00e
MS
6891 {
6892 respond_unsupported(client, attr);
a469f8a5 6893 valid = 0;
1106b00e
MS
6894 }
6895 else
6896 {
63efa616 6897 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
1106b00e 6898
63efa616 6899 if (!ippContainsString(supported, ippGetString(attr, 0, NULL)))
1106b00e
MS
6900 {
6901 respond_unsupported(client, attr);
a469f8a5 6902 valid = 0;
1106b00e
MS
6903 }
6904 }
6905 }
6906
404dde30 6907 if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL)
1106b00e 6908 {
63efa616
MS
6909 ipp_t *col, /* media-col collection */
6910 *size; /* media-size collection */
6911 ipp_attribute_t *member, /* Member attribute */
6912 *x_dim, /* x-dimension */
6913 *y_dim; /* y-dimension */
6914 int x_value, /* y-dimension value */
6915 y_value; /* x-dimension value */
6916
a469f8a5
MS
6917 if (ippGetCount(attr) != 1 ||
6918 ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
1106b00e
MS
6919 {
6920 respond_unsupported(client, attr);
a469f8a5 6921 valid = 0;
1106b00e 6922 }
63efa616
MS
6923
6924 col = ippGetCollection(attr, 0);
6925
6926 if ((member = ippFindAttribute(col, "media-size-name", IPP_TAG_ZERO)) != NULL)
6927 {
6928 if (ippGetCount(member) != 1 ||
6929 (ippGetValueTag(member) != IPP_TAG_NAME &&
6930 ippGetValueTag(member) != IPP_TAG_NAMELANG &&
6931 ippGetValueTag(member) != IPP_TAG_KEYWORD))
6932 {
6933 respond_unsupported(client, attr);
6934 valid = 0;
6935 }
6936 else
6937 {
6938 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
6939
6940 if (!ippContainsString(supported, ippGetString(member, 0, NULL)))
6941 {
6942 respond_unsupported(client, attr);
6943 valid = 0;
6944 }
6945 }
6946 }
6947 else if ((member = ippFindAttribute(col, "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
6948 {
6949 if (ippGetCount(member) != 1)
6950 {
6951 respond_unsupported(client, attr);
6952 valid = 0;
6953 }
6954 else
6955 {
6956 size = ippGetCollection(member, 0);
6957
6958 if ((x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(x_dim) != 1 ||
6959 (y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(y_dim) != 1)
6960 {
6961 respond_unsupported(client, attr);
6962 valid = 0;
6963 }
6964 else
6965 {
6966 x_value = ippGetInteger(x_dim, 0);
6967 y_value = ippGetInteger(y_dim, 0);
6968 supported = ippFindAttribute(client->printer->attrs, "media-size-supported", IPP_TAG_BEGIN_COLLECTION);
6969 count = ippGetCount(supported);
6970
6971 for (i = 0; i < count ; i ++)
6972 {
6973 size = ippGetCollection(supported, i);
6974 x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_ZERO);
6975 y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_ZERO);
6976
6977 if (ippContainsInteger(x_dim, x_value) && ippContainsInteger(y_dim, y_value))
6978 break;
6979 }
6980
6981 if (i >= count)
6982 {
6983 respond_unsupported(client, attr);
6984 valid = 0;
6985 }
6986 }
6987 }
6988 }
1106b00e
MS
6989 }
6990
404dde30 6991 if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL)
1106b00e 6992 {
a469f8a5
MS
6993 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
6994 (strcmp(ippGetString(attr, 0, NULL),
1106b00e 6995 "separate-documents-uncollated-copies") &&
a469f8a5 6996 strcmp(ippGetString(attr, 0, NULL),
1106b00e
MS
6997 "separate-documents-collated-copies")))
6998 {
6999 respond_unsupported(client, attr);
a469f8a5 7000 valid = 0;
1106b00e
MS
7001 }
7002 }
7003
404dde30 7004 if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL)
1106b00e 7005 {
a469f8a5
MS
7006 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
7007 ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
7008 ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
1106b00e
MS
7009 {
7010 respond_unsupported(client, attr);
a469f8a5 7011 valid = 0;
1106b00e
MS
7012 }
7013 }
7014
404dde30 7015 if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
1106b00e 7016 {
c2c30ebc
MS
7017 if (ippGetValueTag(attr) != IPP_TAG_RANGE)
7018 {
7019 respond_unsupported(client, attr);
a469f8a5 7020 valid = 0;
c2c30ebc 7021 }
1106b00e
MS
7022 }
7023
404dde30 7024 if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL)
1106b00e 7025 {
a469f8a5
MS
7026 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
7027 ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
7028 ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
1106b00e
MS
7029 {
7030 respond_unsupported(client, attr);
a469f8a5 7031 valid = 0;
1106b00e
MS
7032 }
7033 }
7034
404dde30 7035 if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
1106b00e 7036 {
4a838088 7037 supported = ippFindAttribute(client->printer->attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION);
c2c30ebc
MS
7038
7039 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION ||
7040 !supported)
7041 {
7042 respond_unsupported(client, attr);
7043 valid = 0;
7044 }
7045 else
7046 {
63efa616 7047 int xdpi, /* Horizontal resolution for job template attribute */
4a838088
MS
7048 ydpi, /* Vertical resolution for job template attribute */
7049 sydpi; /* Vertical resolution for supported value */
7050 ipp_res_t units, /* Units for job template attribute */
7051 sunits; /* Units for supported value */
c2c30ebc
MS
7052
7053 xdpi = ippGetResolution(attr, 0, &ydpi, &units);
7054 count = ippGetCount(supported);
7055
7056 for (i = 0; i < count; i ++)
7057 {
7058 if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits)
7059 break;
7060 }
7061
7062 if (i >= count)
7063 {
7064 respond_unsupported(client, attr);
7065 valid = 0;
7066 }
7067 }
1106b00e
MS
7068 }
7069
404dde30 7070 if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
1106b00e 7071 {
404dde30
MS
7072 const char *sides = ippGetString(attr, 0, NULL);
7073 /* "sides" value... */
ad29aeab 7074
a469f8a5 7075 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
1106b00e
MS
7076 {
7077 respond_unsupported(client, attr);
a469f8a5 7078 valid = 0;
1106b00e 7079 }
404dde30 7080 else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
1106b00e 7081 {
ad29aeab 7082 if (!ippContainsString(supported, sides))
1106b00e
MS
7083 {
7084 respond_unsupported(client, attr);
a469f8a5 7085 valid = 0;
1106b00e
MS
7086 }
7087 }
ad29aeab 7088 else if (strcmp(sides, "one-sided"))
1106b00e
MS
7089 {
7090 respond_unsupported(client, attr);
a469f8a5 7091 valid = 0;
1106b00e
MS
7092 }
7093 }
7094
a469f8a5 7095 return (valid);
1106b00e 7096}