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