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