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