]> git.ipfire.org Git - thirdparty/cups.git/blame - test/ippeveprinter.c
Fix PDF filtering.
[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 573 if (!serverport)
83ce8172 574 {
24a06ed3 575#ifdef _WIN32
015214aa 576 /*
d6563739
MS
577 * Windows is almost always used as a single user system, so use a default
578 * port number of 8631.
015214aa
MS
579 */
580
f8927099 581 serverport = 8631;
015214aa
MS
582
583#else
584 /*
585 * Use 8000 + UID mod 1000 for the default port number...
586 */
587
f8927099 588 serverport = 8000 + ((int)getuid() % 1000);
24a06ed3 589#endif /* _WIN32 */
015214aa 590
1562b9a1 591 _cupsLangPrintf(stderr, _("Listening on port %d."), serverport);
83ce8172
MS
592 }
593
1106b00e
MS
594 if (!directory[0])
595 {
12f009bb 596 const char *tmpdir; /* Temporary directory */
1106b00e 597
24a06ed3 598#ifdef _WIN32
12f009bb
MS
599 if ((tmpdir = getenv("TEMP")) == NULL)
600 tmpdir = "C:/TEMP";
3c2cb822 601#elif defined(__APPLE__) && TARGET_OS_OSX
12f009bb
MS
602 if ((tmpdir = getenv("TMPDIR")) == NULL)
603 tmpdir = "/private/tmp";
604#else
605 if ((tmpdir = getenv("TMPDIR")) == NULL)
606 tmpdir = "/tmp";
24a06ed3 607#endif /* _WIN32 */
12f009bb 608
f8927099 609 snprintf(directory, sizeof(directory), "%s/ippeveprinter.%d", tmpdir, (int)getpid());
12f009bb
MS
610
611 if (mkdir(directory, 0755) && errno != EEXIST)
1106b00e 612 {
1562b9a1 613 _cupsLangPrintf(stderr, _("Unable to create spool directory \"%s\": %s"), directory, strerror(errno));
1106b00e
MS
614 usage(1);
615 }
616
617 if (Verbosity)
f8927099 618 _cupsLangPrintf(stderr, _("Using spool directory \"%s\"."), directory);
1106b00e
MS
619 }
620
f93b32b6
MS
621#ifdef HAVE_SSL
622 cupsSetServerCredentials(keypath, servername, 1);
623#endif /* HAVE_SSL */
624
d6563739 625 /*
f8927099 626 * Initialize DNS-SD...
d6563739
MS
627 */
628
629 dnssd_init();
630
1106b00e
MS
631 /*
632 * Create the printer...
633 */
634
f8927099 635 if (!docformats)
ab8fab61 636 docformats = _cupsArrayNewStrings(ppm_color > 0 ? "image/jpeg,image/pwg-raster,image/urf": "image/pwg-raster,image/urf", ',');
f8927099
MS
637
638 if (attrfile)
1562b9a1 639 attrs = load_ippserver_attributes(servername, serverport, attrfile, docformats);
6641bd0d 640#if !CUPS_LITE
f8927099 641 else if (ppdfile)
ab8fab61 642 {
f8927099 643 attrs = load_ppd_attributes(ppdfile, docformats);
ab8fab61
MS
644
645 if (!command)
646 command = "ippeveps";
647 }
6641bd0d 648#endif /* !CUPS_LITE */
f8927099
MS
649 else
650 attrs = load_legacy_attributes(make, model, ppm, ppm_color, duplex, docformats);
651
6641bd0d 652 if ((printer = create_printer(servername, serverport, name, location, icon, docformats, subtypes, directory, command, device_uri, attrs)) == NULL)
1106b00e
MS
653 return (1);
654
820cb58e
MS
655 printer->web_forms = web_forms;
656
6641bd0d
MS
657#if !CUPS_LITE
658 if (ppdfile)
659 printer->ppdfile = strdup(ppdfile);
660#endif /* !CUPS_LITE */
661
1106b00e
MS
662 /*
663 * Run the print service...
664 */
665
666 run_printer(printer);
667
668 /*
669 * Destroy the printer and exit...
670 */
671
672 delete_printer(printer);
673
674 return (0);
675}
676
677
678/*
679 * 'clean_jobs()' - Clean out old (completed) jobs.
680 */
681
682static void
d46dbe1b 683clean_jobs(ippeve_printer_t *printer) /* I - Printer */
1106b00e 684{
d46dbe1b 685 ippeve_job_t *job; /* Current job */
1106b00e
MS
686 time_t cleantime; /* Clean time */
687
688
689 if (cupsArrayCount(printer->jobs) == 0)
690 return;
691
692 cleantime = time(NULL) - 60;
693
694 _cupsRWLockWrite(&(printer->rwlock));
d46dbe1b 695 for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs);
1106b00e 696 job;
d46dbe1b 697 job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
1106b00e
MS
698 if (job->completed && job->completed < cleantime)
699 {
700 cupsArrayRemove(printer->jobs, job);
701 delete_job(job);
702 }
703 else
704 break;
705 _cupsRWUnlock(&(printer->rwlock));
706}
707
708
709/*
710 * 'compare_jobs()' - Compare two jobs.
711 */
712
713static int /* O - Result of comparison */
d46dbe1b
MS
714compare_jobs(ippeve_job_t *a, /* I - First job */
715 ippeve_job_t *b) /* I - Second job */
1106b00e
MS
716{
717 return (b->id - a->id);
718}
719
720
1106b00e
MS
721/*
722 * 'copy_attributes()' - Copy attributes from one request to another.
723 */
724
725static void
726copy_attributes(ipp_t *to, /* I - Destination request */
727 ipp_t *from, /* I - Source request */
728 cups_array_t *ra, /* I - Requested attributes */
729 ipp_tag_t group_tag, /* I - Group to copy */
730 int quickcopy) /* I - Do a quick copy? */
731{
d46dbe1b 732 ippeve_filter_t filter; /* Filter data */
1106b00e
MS
733
734
c2c30ebc
MS
735 filter.ra = ra;
736 filter.group_tag = group_tag;
1106b00e 737
c2c30ebc 738 ippCopyAttributes(to, from, quickcopy, (ipp_copycb_t)filter_cb, &filter);
1106b00e
MS
739}
740
741
742/*
743 * 'copy_job_attrs()' - Copy job attributes to the response.
744 */
745
746static void
747copy_job_attributes(
d46dbe1b
MS
748 ippeve_client_t *client, /* I - Client */
749 ippeve_job_t *job, /* I - Job */
1106b00e
MS
750 cups_array_t *ra) /* I - requested-attributes */
751{
d7225fc2 752 copy_attributes(client->response, job->attrs, ra, IPP_TAG_JOB, 0);
1106b00e 753
e3a57e0b
MS
754 if (!ra || cupsArrayFind(ra, "date-time-at-completed"))
755 {
756 if (job->completed)
757 ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-completed", ippTimeToDate(job->completed));
758 else
759 ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-completed");
760 }
c2c30ebc 761
e3a57e0b
MS
762 if (!ra || cupsArrayFind(ra, "date-time-at-processing"))
763 {
764 if (job->processing)
765 ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-processing", ippTimeToDate(job->processing));
766 else
767 ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-processing");
768 }
c2c30ebc 769
4a838088
MS
770 if (!ra || cupsArrayFind(ra, "job-impressions"))
771 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions", job->impressions);
772
773 if (!ra || cupsArrayFind(ra, "job-impressions-completed"))
774 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions-completed", job->impcompleted);
775
1106b00e 776 if (!ra || cupsArrayFind(ra, "job-printer-up-time"))
4a838088 777 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-printer-up-time", (int)(time(NULL) - client->printer->start_time));
1106b00e
MS
778
779 if (!ra || cupsArrayFind(ra, "job-state"))
400e67c1 780 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state);
1106b00e 781
c2c30ebc
MS
782 if (!ra || cupsArrayFind(ra, "job-state-message"))
783 {
784 switch (job->state)
785 {
786 case IPP_JSTATE_PENDING :
787 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job pending.");
788 break;
789
790 case IPP_JSTATE_HELD :
791 if (job->fd >= 0)
792 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job incoming.");
793 else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
794 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job held.");
795 else
796 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job created.");
797 break;
798
799 case IPP_JSTATE_PROCESSING :
800 if (job->cancel)
801 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceling.");
802 else
803 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job printing.");
804 break;
805
806 case IPP_JSTATE_STOPPED :
807 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job stopped.");
808 break;
809
810 case IPP_JSTATE_CANCELED :
811 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceled.");
812 break;
813
814 case IPP_JSTATE_ABORTED :
815 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job aborted.");
816 break;
817
818 case IPP_JSTATE_COMPLETED :
819 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job completed.");
820 break;
821 }
822 }
823
1106b00e
MS
824 if (!ra || cupsArrayFind(ra, "job-state-reasons"))
825 {
826 switch (job->state)
827 {
a469f8a5 828 case IPP_JSTATE_PENDING :
e60ec91f 829 ippAddString(client->response, IPP_TAG_JOB,
7e86f2f6 830 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
e60ec91f 831 NULL, "none");
1106b00e
MS
832 break;
833
a469f8a5 834 case IPP_JSTATE_HELD :
1106b00e 835 if (job->fd >= 0)
e60ec91f 836 ippAddString(client->response, IPP_TAG_JOB,
7e86f2f6 837 IPP_CONST_TAG(IPP_TAG_KEYWORD),
a469f8a5 838 "job-state-reasons", NULL, "job-incoming");
1106b00e 839 else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
e60ec91f 840 ippAddString(client->response, IPP_TAG_JOB,
7e86f2f6 841 IPP_CONST_TAG(IPP_TAG_KEYWORD),
a469f8a5 842 "job-state-reasons", NULL, "job-hold-until-specified");
83e08001
MS
843 else
844 ippAddString(client->response, IPP_TAG_JOB,
7e86f2f6 845 IPP_CONST_TAG(IPP_TAG_KEYWORD),
a469f8a5 846 "job-state-reasons", NULL, "job-data-insufficient");
1106b00e
MS
847 break;
848
a469f8a5 849 case IPP_JSTATE_PROCESSING :
1106b00e 850 if (job->cancel)
e60ec91f 851 ippAddString(client->response, IPP_TAG_JOB,
7e86f2f6 852 IPP_CONST_TAG(IPP_TAG_KEYWORD),
a469f8a5 853 "job-state-reasons", NULL, "processing-to-stop-point");
1106b00e 854 else
e60ec91f 855 ippAddString(client->response, IPP_TAG_JOB,
7e86f2f6 856 IPP_CONST_TAG(IPP_TAG_KEYWORD),
a469f8a5 857 "job-state-reasons", NULL, "job-printing");
1106b00e
MS
858 break;
859
a469f8a5 860 case IPP_JSTATE_STOPPED :
e60ec91f 861 ippAddString(client->response, IPP_TAG_JOB,
7e86f2f6 862 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
e60ec91f 863 NULL, "job-stopped");
1106b00e
MS
864 break;
865
a469f8a5 866 case IPP_JSTATE_CANCELED :
e60ec91f 867 ippAddString(client->response, IPP_TAG_JOB,
7e86f2f6 868 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
e60ec91f 869 NULL, "job-canceled-by-user");
1106b00e
MS
870 break;
871
a469f8a5 872 case IPP_JSTATE_ABORTED :
e60ec91f 873 ippAddString(client->response, IPP_TAG_JOB,
7e86f2f6 874 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
e60ec91f 875 NULL, "aborted-by-system");
1106b00e
MS
876 break;
877
a469f8a5 878 case IPP_JSTATE_COMPLETED :
e60ec91f 879 ippAddString(client->response, IPP_TAG_JOB,
7e86f2f6 880 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
e60ec91f 881 NULL, "job-completed-successfully");
1106b00e
MS
882 break;
883 }
884 }
885
886 if (!ra || cupsArrayFind(ra, "time-at-completed"))
887 ippAddInteger(client->response, IPP_TAG_JOB,
888 job->completed ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
4a838088 889 "time-at-completed", (int)(job->completed - client->printer->start_time));
1106b00e
MS
890
891 if (!ra || cupsArrayFind(ra, "time-at-processing"))
892 ippAddInteger(client->response, IPP_TAG_JOB,
893 job->processing ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
4a838088 894 "time-at-processing", (int)(job->processing - client->printer->start_time));
1106b00e
MS
895}
896
897
898/*
899 * 'create_client()' - Accept a new network connection and create a client
900 * object.
901 */
902
d46dbe1b
MS
903static ippeve_client_t * /* O - Client */
904create_client(ippeve_printer_t *printer, /* I - Printer */
1106b00e
MS
905 int sock) /* I - Listen socket */
906{
d46dbe1b 907 ippeve_client_t *client; /* Client */
1106b00e
MS
908
909
d46dbe1b 910 if ((client = calloc(1, sizeof(ippeve_client_t))) == NULL)
1106b00e
MS
911 {
912 perror("Unable to allocate memory for client");
913 return (NULL);
914 }
915
a469f8a5 916 client->printer = printer;
1106b00e
MS
917
918 /*
919 * Accept the client and get the remote address...
920 */
921
a469f8a5 922 if ((client->http = httpAcceptConnection(sock, 1)) == NULL)
1106b00e
MS
923 {
924 perror("Unable to accept client connection");
925
926 free(client);
927
928 return (NULL);
929 }
930
a469f8a5 931 httpGetHostname(client->http, client->hostname, sizeof(client->hostname));
1106b00e
MS
932
933 if (Verbosity)
a469f8a5 934 fprintf(stderr, "Accepted connection from %s\n", client->hostname);
1106b00e
MS
935
936 return (client);
937}
938
939
940/*
941 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
942 * request.
943 */
944
d46dbe1b
MS
945static ippeve_job_t * /* O - Job */
946create_job(ippeve_client_t *client) /* I - Client */
1106b00e 947{
d46dbe1b 948 ippeve_job_t *job; /* Job */
1106b00e 949 ipp_attribute_t *attr; /* Job attribute */
4a838088
MS
950 char uri[1024], /* job-uri value */
951 uuid[64]; /* job-uuid value */
1106b00e
MS
952
953
954 _cupsRWLockWrite(&(client->printer->rwlock));
955 if (client->printer->active_job &&
a469f8a5 956 client->printer->active_job->state < IPP_JSTATE_CANCELED)
1106b00e
MS
957 {
958 /*
959 * Only accept a single job at a time...
960 */
961
c5755caf 962 _cupsRWUnlock(&(client->printer->rwlock));
1106b00e
MS
963 return (NULL);
964 }
965
966 /*
967 * Allocate and initialize the job object...
968 */
969
d46dbe1b 970 if ((job = calloc(1, sizeof(ippeve_job_t))) == NULL)
1106b00e
MS
971 {
972 perror("Unable to allocate memory for job");
973 return (NULL);
974 }
975
976 job->printer = client->printer;
4a838088 977 job->attrs = ippNew();
a469f8a5 978 job->state = IPP_JSTATE_HELD;
1106b00e 979 job->fd = -1;
1106b00e
MS
980
981 /*
4a838088 982 * Copy all of the job attributes...
1106b00e
MS
983 */
984
4a838088 985 copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
1106b00e
MS
986
987 /*
988 * Get the requesting-user-name, document format, and priority...
989 */
990
4a838088 991 if ((attr = ippFindAttribute(client->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
a469f8a5 992 job->username = ippGetString(attr, 0, NULL);
1106b00e
MS
993 else
994 job->username = "anonymous";
995
4a838088
MS
996 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name", NULL, job->username);
997
998 if (ippGetOperation(client->request) != IPP_OP_CREATE_JOB)
999 {
404dde30
MS
1000 if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
1001 job->format = ippGetString(attr, 0, NULL);
1002 else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
4a838088
MS
1003 job->format = ippGetString(attr, 0, NULL);
1004 else
1005 job->format = "application/octet-stream";
4a838088
MS
1006 }
1007
4a838088
MS
1008 if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_INTEGER)) != NULL)
1009 job->impressions = ippGetInteger(attr, 0);
1106b00e 1010
404dde30
MS
1011 if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_NAME)) != NULL)
1012 job->name = ippGetString(attr, 0, NULL);
1013
1106b00e
MS
1014 /*
1015 * Add job description attributes and add to the jobs array...
1016 */
1017
1018 job->id = client->printer->next_job_id ++;
1019
1020 snprintf(uri, sizeof(uri), "%s/%d", client->printer->uri, job->id);
4a838088 1021 httpAssembleUUID(client->printer->hostname, client->printer->port, client->printer->name, job->id, uuid, sizeof(uuid));
1106b00e 1022
0b5ce83f 1023 ippAddDate(job->attrs, IPP_TAG_JOB, "date-time-at-creation", ippTimeToDate(time(&job->created)));
1106b00e
MS
1024 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1025 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, uri);
4a838088 1026 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL, uuid);
5947e9d5
MS
1027 if ((attr = ippFindAttribute(client->request, "printer-uri", IPP_TAG_URI)) != NULL)
1028 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, ippGetString(attr, 0, NULL));
1029 else
1030 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, client->printer->uri);
0b5ce83f 1031 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", (int)(job->created - client->printer->start_time));
1106b00e
MS
1032
1033 cupsArrayAdd(client->printer->jobs, job);
1034 client->printer->active_job = job;
1035
1036 _cupsRWUnlock(&(client->printer->rwlock));
1037
1038 return (job);
1039}
1040
1041
404dde30 1042/*
aa2a90ce 1043 * 'create_job_file()' - Create a file for the document in a job.
404dde30
MS
1044 */
1045
aa2a90ce
MS
1046static int /* O - File descriptor or -1 on error */
1047create_job_file(
d46dbe1b 1048 ippeve_job_t *job, /* I - Job */
aa2a90ce
MS
1049 char *fname, /* I - Filename buffer */
1050 size_t fnamesize, /* I - Size of filename buffer */
dd2b6166 1051 const char *directory, /* I - Directory to store in */
aa2a90ce 1052 const char *ext) /* I - Extension (`NULL` for default) */
404dde30
MS
1053{
1054 char name[256], /* "Safe" filename */
1055 *nameptr; /* Pointer into filename */
aa2a90ce 1056 const char *job_name; /* job-name value */
404dde30
MS
1057
1058
1059 /*
1060 * Make a name from the job-name attribute...
1061 */
1062
aa2a90ce 1063 if ((job_name = ippGetString(ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME), 0, NULL)) == NULL)
404dde30
MS
1064 job_name = "untitled";
1065
1066 for (nameptr = name; *job_name && nameptr < (name + sizeof(name) - 1); job_name ++)
aa2a90ce 1067 {
404dde30 1068 if (isalnum(*job_name & 255) || *job_name == '-')
aa2a90ce 1069 {
4403acbb 1070 *nameptr++ = (char)tolower(*job_name & 255);
aa2a90ce 1071 }
404dde30 1072 else
aa2a90ce 1073 {
404dde30
MS
1074 *nameptr++ = '_';
1075
aa2a90ce
MS
1076 while (job_name[1] && !isalnum(job_name[1] & 255) && job_name[1] != '-')
1077 job_name ++;
1078 }
1079 }
1080
404dde30
MS
1081 *nameptr = '\0';
1082
1083 /*
1084 * Figure out the extension...
1085 */
1086
aa2a90ce
MS
1087 if (!ext)
1088 {
1089 if (!strcasecmp(job->format, "image/jpeg"))
1090 ext = "jpg";
1091 else if (!strcasecmp(job->format, "image/png"))
1092 ext = "png";
1093 else if (!strcasecmp(job->format, "image/pwg-raster"))
1094 ext = "pwg";
1095 else if (!strcasecmp(job->format, "image/urf"))
1096 ext = "urf";
1097 else if (!strcasecmp(job->format, "application/pdf"))
1098 ext = "pdf";
1099 else if (!strcasecmp(job->format, "application/postscript"))
1100 ext = "ps";
1101 else if (!strcasecmp(job->format, "application/vnd.hp-pcl"))
1102 ext = "pcl";
1103 else
1104 ext = "dat";
1105 }
404dde30
MS
1106
1107 /*
1108 * Create a filename with the job-id, job-name, and document-format (extension)...
1109 */
1110
dd2b6166 1111 snprintf(fname, fnamesize, "%s/%d-%s.%s", directory, job->id, name, ext);
aa2a90ce
MS
1112
1113 return (open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0666));
404dde30
MS
1114}
1115
1116
1106b00e
MS
1117/*
1118 * 'create_listener()' - Create a listener socket.
1119 */
1120
83ce8172 1121static int /* O - Listener socket or -1 on error */
1562b9a1
MS
1122create_listener(const char *name, /* I - Host name (`NULL` for any address) */
1123 int port, /* I - Port number */
1124 int family) /* I - Address family */
1106b00e 1125{
a469f8a5
MS
1126 int sock; /* Listener socket */
1127 http_addrlist_t *addrlist; /* Listen address */
1128 char service[255]; /* Service port */
1106b00e 1129
1106b00e 1130
83ce8172 1131 snprintf(service, sizeof(service), "%d", port);
92dad945 1132 if ((addrlist = httpAddrGetList(name, family, service)) == NULL)
1106b00e 1133 return (-1);
1106b00e 1134
83ce8172 1135 sock = httpAddrListen(&(addrlist->addr), port);
a469f8a5
MS
1136
1137 httpAddrFreeList(addrlist);
1106b00e
MS
1138
1139 return (sock);
1140}
1141
1142
1143/*
1144 * 'create_media_col()' - Create a media-col value.
1145 */
1146
1147static ipp_t * /* O - media-col collection */
1148create_media_col(const char *media, /* I - Media name */
1562b9a1
MS
1149 const char *source, /* I - Media source, if any */
1150 const char *type, /* I - Media type, if any */
1106b00e
MS
1151 int width, /* I - x-dimension in 2540ths */
1152 int length, /* I - y-dimension in 2540ths */
1562b9a1
MS
1153 int bottom, /* I - Bottom margin in 2540ths */
1154 int left, /* I - Left margin in 2540ths */
1155 int right, /* I - Right margin in 2540ths */
1156 int top) /* I - Top margin in 2540ths */
1106b00e 1157{
1562b9a1
MS
1158 ipp_t *media_col = ippNew(), /* media-col value */
1159 *media_size = create_media_size(width, length);
d7225fc2 1160 /* media-size value */
1562b9a1
MS
1161 char media_key[256]; /* media-key value */
1162 const char *media_key_suffix = ""; /* media-key suffix */
1163
1106b00e 1164
1562b9a1
MS
1165 if (bottom == 0 && left == 0 && right == 0 && top == 0)
1166 media_key_suffix = "_borderless";
1106b00e 1167
4a838088 1168 if (type && source)
1562b9a1 1169 snprintf(media_key, sizeof(media_key), "%s_%s_%s%s", media, source, type, media_key_suffix);
4a838088 1170 else if (type)
1562b9a1 1171 snprintf(media_key, sizeof(media_key), "%s__%s%s", media, type, media_key_suffix);
4a838088 1172 else if (source)
1562b9a1 1173 snprintf(media_key, sizeof(media_key), "%s_%s%s", media, source, media_key_suffix);
4a838088 1174 else
1562b9a1 1175 snprintf(media_key, sizeof(media_key), "%s%s", media, media_key_suffix);
1106b00e 1176
1562b9a1 1177 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-key", NULL, media_key);
1106b00e 1178 ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
4a838088 1179 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-size-name", NULL, media);
9141aa01
MS
1180 if (bottom >= 0)
1181 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin", bottom);
1182 if (left >= 0)
1183 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin", left);
1184 if (right >= 0)
1185 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin", right);
1186 if (top >= 0)
1187 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin", top);
4a838088
MS
1188 if (source)
1189 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source", NULL, source);
1190 if (type)
1191 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type", NULL, type);
1106b00e
MS
1192
1193 ippDelete(media_size);
1194
1195 return (media_col);
1196}
1197
1198
d7225fc2
MS
1199/*
1200 * 'create_media_size()' - Create a media-size value.
1201 */
1202
1203static ipp_t * /* O - media-col collection */
1204create_media_size(int width, /* I - x-dimension in 2540ths */
1205 int length) /* I - y-dimension in 2540ths */
1206{
1207 ipp_t *media_size = ippNew(); /* media-size value */
1208
1209
1562b9a1
MS
1210 ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension", width);
1211 ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension", length);
d7225fc2
MS
1212
1213 return (media_size);
1214}
1215
1216
1106b00e
MS
1217/*
1218 * 'create_printer()' - Create, register, and listen for connections to a
1219 * printer object.
1220 */
1221
8a4ed632 1222static ippeve_printer_t * /* O - Printer */
f8927099
MS
1223create_printer(
1224 const char *servername, /* I - Server hostname (NULL for default) */
1225 int serverport, /* I - Server port */
1226 const char *name, /* I - printer-name */
1227 const char *location, /* I - printer-location */
1228 const char *icon, /* I - printer-icons */
1229 cups_array_t *docformats, /* I - document-format-supported */
1230 const char *subtypes, /* I - Bonjour service subtype(s) */
1231 const char *directory, /* I - Spool directory */
1232 const char *command, /* I - Command to run on job files, if any */
f8927099
MS
1233 const char *device_uri, /* I - Output device, if any */
1234 ipp_t *attrs) /* I - Capability attributes */
1106b00e 1235{
d46dbe1b 1236 ippeve_printer_t *printer; /* Printer */
1562b9a1 1237 int i; /* Looping var */
24a06ed3 1238#ifndef _WIN32
83ce8172 1239 char path[1024]; /* Full path to command */
24a06ed3 1240#endif /* !_WIN32 */
83ce8172 1241 char uri[1024], /* Printer URI */
15a9714c
MS
1242#ifdef HAVE_SSL
1243 securi[1024], /* Secure printer URI */
1244 *uris[2], /* All URIs */
1245#endif /* HAVE_SSL */
1106b00e
MS
1246 icons[1024], /* printer-icons URI */
1247 adminurl[1024], /* printer-more-info URI */
4a838088 1248 supplyurl[1024],/* printer-supply-info-uri URI */
c2c30ebc 1249 uuid[128]; /* printer-uuid */
1106b00e 1250 int k_supported; /* Maximum file size supported */
1562b9a1
MS
1251 int num_formats; /* Number of supported document formats */
1252 const char *formats[100], /* Supported document formats */
1253 *format; /* Current format */
1254 int num_job_attrs; /* Number of supported job attributes */
1255 const char *job_attrs[100];/* Job attributes */
1256 char xxx_supported[256];
1257 /* Name of -supported attribute */
1258 _cups_globals_t *cg = _cupsGlobals();
1259 /* Global path values */
e60ec91f 1260#ifdef HAVE_STATVFS
1106b00e
MS
1261 struct statvfs spoolinfo; /* FS info for spool directory */
1262 double spoolsize; /* FS size */
e60ec91f
MS
1263#elif defined(HAVE_STATFS)
1264 struct statfs spoolinfo; /* FS info for spool directory */
1265 double spoolsize; /* FS size */
1266#endif /* HAVE_STATVFS */
1106b00e
MS
1267 static const char * const versions[] =/* ipp-versions-supported values */
1268 {
1106b00e 1269 "1.1",
92dad945 1270 "2.0"
1106b00e 1271 };
2cadf0f4
MS
1272 static const char * const features[] =/* ipp-features-supported values */
1273 {
1274 "ipp-everywhere"
1275 };
1106b00e
MS
1276 static const int ops[] = /* operations-supported values */
1277 {
a469f8a5
MS
1278 IPP_OP_PRINT_JOB,
1279 IPP_OP_PRINT_URI,
1280 IPP_OP_VALIDATE_JOB,
1281 IPP_OP_CREATE_JOB,
1282 IPP_OP_SEND_DOCUMENT,
1283 IPP_OP_SEND_URI,
1284 IPP_OP_CANCEL_JOB,
1285 IPP_OP_GET_JOB_ATTRIBUTES,
1286 IPP_OP_GET_JOBS,
2cadf0f4
MS
1287 IPP_OP_GET_PRINTER_ATTRIBUTES,
1288 IPP_OP_CANCEL_MY_JOBS,
1289 IPP_OP_CLOSE_JOB,
1290 IPP_OP_IDENTIFY_PRINTER
1106b00e
MS
1291 };
1292 static const char * const charsets[] =/* charset-supported values */
1293 {
1294 "us-ascii",
1295 "utf-8"
1296 };
a469f8a5
MS
1297 static const char * const compressions[] =/* compression-supported values */
1298 {
1299#ifdef HAVE_LIBZ
1300 "deflate",
1301 "gzip",
1302#endif /* HAVE_LIBZ */
1303 "none"
1304 };
2cadf0f4
MS
1305 static const char * const identify_actions[] =
1306 {
1307 "display",
1308 "sound"
1309 };
1106b00e
MS
1310 static const char * const job_creation[] =
1311 { /* job-creation-attributes-supported values */
1312 "copies",
1562b9a1
MS
1313 "document-password",
1314 "finishings",
1315 "finishings-col",
4d2df926
MS
1316 "job-password",
1317 "job-password-encryption",
1106b00e 1318 "orientation-requested",
1562b9a1
MS
1319 "output-bin",
1320 "overrides",
1321 "page-ranges",
1322 "print-color-mode",
1323 "print-content-optimize",
1324 "print-rendering-intent",
1106b00e 1325 "print-quality",
1562b9a1 1326 "printer-resolution",
1106b00e
MS
1327 "sides"
1328 };
1329 static const char * const media_col_supported[] =
1330 { /* media-col-supported values */
1331 "media-bottom-margin",
1332 "media-left-margin",
1333 "media-right-margin",
1334 "media-size",
1562b9a1 1335 "media-size-name",
4a838088 1336 "media-source",
1106b00e
MS
1337 "media-top-margin",
1338 "media-type"
1339 };
1106b00e
MS
1340 static const char * const multiple_document_handling[] =
1341 { /* multiple-document-handling-supported values */
1342 "separate-documents-uncollated-copies",
1343 "separate-documents-collated-copies"
1344 };
d7225fc2
MS
1345 static const char * const reference_uri_schemes_supported[] =
1346 { /* reference-uri-schemes-supported */
83e08001 1347 "file",
d7225fc2 1348 "ftp",
83e08001
MS
1349 "http"
1350#ifdef HAVE_SSL
1351 , "https"
1352#endif /* HAVE_SSL */
1353 };
15a9714c
MS
1354#ifdef HAVE_SSL
1355 static const char * const uri_authentication_supported[] =
1356 { /* uri-authentication-supported values */
1357 "none",
1358 "none"
1359 };
1360 static const char * const uri_security_supported[] =
1361 { /* uri-security-supported values */
1362 "none",
1363 "tls"
1364 };
1365#endif /* HAVE_SSL */
1106b00e
MS
1366 static const char * const which_jobs[] =
1367 { /* which-jobs-supported values */
1368 "completed",
1369 "not-completed",
1370 "aborted",
1371 "all",
1372 "canceled",
1373 "pending",
1374 "pending-held",
1375 "processing",
1376 "processing-stopped"
1377 };
1378
1379
24a06ed3 1380#ifndef _WIN32
83ce8172
MS
1381 /*
1382 * If a command was specified, make sure it exists and is executable...
1383 */
1384
1385 if (command)
1386 {
1387 if (*command == '/' || !strncmp(command, "./", 2))
1388 {
1389 if (access(command, X_OK))
1390 {
1562b9a1 1391 _cupsLangPrintf(stderr, _("Unable to execute command \"%s\": %s"), command, strerror(errno));
83ce8172
MS
1392 return (NULL);
1393 }
1394 }
1395 else
1396 {
1562b9a1
MS
1397 snprintf(path, sizeof(path), "%s/ippeveprinter/%s", cg->cups_serverbin, command);
1398
1399 if (access(command, X_OK))
83ce8172 1400 {
1562b9a1 1401 _cupsLangPrintf(stderr, _("Unable to execute command \"%s\": %s"), command, strerror(errno));
83ce8172
MS
1402 return (NULL);
1403 }
1404
1405 command = path;
1406 }
1407 }
24a06ed3 1408#endif /* !_WIN32 */
83ce8172 1409
1106b00e
MS
1410 /*
1411 * Allocate memory for the printer...
1412 */
1413
d46dbe1b 1414 if ((printer = calloc(1, sizeof(ippeve_printer_t))) == NULL)
1106b00e 1415 {
6d56631f 1416 _cupsLangPrintError(NULL, _("Unable to allocate memory for printer"));
1106b00e
MS
1417 return (NULL);
1418 }
1419
1420 printer->ipv4 = -1;
1421 printer->ipv6 = -1;
a469f8a5 1422 printer->name = strdup(name);
1562b9a1 1423 printer->dnssd_name = strdup(name);
db8b865d 1424 printer->command = command ? strdup(command) : NULL;
1562b9a1 1425 printer->device_uri = device_uri ? strdup(device_uri) : NULL;
a469f8a5 1426 printer->directory = strdup(directory);
1562b9a1
MS
1427 printer->icon = icon ? strdup(icon) : NULL;
1428 printer->port = serverport;
4a838088
MS
1429 printer->start_time = time(NULL);
1430 printer->config_time = printer->start_time;
a469f8a5 1431 printer->state = IPP_PSTATE_IDLE;
d46dbe1b 1432 printer->state_reasons = IPPEVE_PREASON_NONE;
4a838088 1433 printer->state_time = printer->start_time;
1106b00e
MS
1434 printer->jobs = cupsArrayNew((cups_array_func_t)compare_jobs, NULL);
1435 printer->next_job_id = 1;
1436
1562b9a1
MS
1437 if (servername)
1438 {
1439 printer->hostname = strdup(servername);
1440 }
1441 else
1442 {
1443 char temp[1024]; /* Temporary string */
4a838088 1444
1562b9a1
MS
1445 printer->hostname = strdup(httpGetHostname(NULL, temp, sizeof(temp)));
1446 }
4a838088 1447
1106b00e
MS
1448 _cupsRWInit(&(printer->rwlock));
1449
1450 /*
1451 * Create the listener sockets...
1452 */
1453
1562b9a1 1454 if ((printer->ipv4 = create_listener(servername, printer->port, AF_INET)) < 0)
1106b00e
MS
1455 {
1456 perror("Unable to create IPv4 listener");
1457 goto bad_printer;
1458 }
1459
1562b9a1 1460 if ((printer->ipv6 = create_listener(servername, printer->port, AF_INET6)) < 0)
1106b00e
MS
1461 {
1462 perror("Unable to create IPv6 listener");
1463 goto bad_printer;
1464 }
1465
1466 /*
1562b9a1 1467 * Prepare URI values for the printer attributes...
1106b00e
MS
1468 */
1469
1562b9a1
MS
1470 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, printer->hostname, printer->port, "/ipp/print");
1471 printer->uri = strdup(uri);
1472 printer->urilen = strlen(uri);
1473
1474#ifdef HAVE_SSL
1475 httpAssembleURI(HTTP_URI_CODING_ALL, securi, sizeof(securi), "ipps", NULL, printer->hostname, printer->port, "/ipp/print");
1476#endif /* HAVE_SSL */
1477
15a9714c
MS
1478 httpAssembleURI(HTTP_URI_CODING_ALL, icons, sizeof(icons), WEB_SCHEME, NULL, printer->hostname, printer->port, "/icon.png");
1479 httpAssembleURI(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), WEB_SCHEME, NULL, printer->hostname, printer->port, "/");
1480 httpAssembleURI(HTTP_URI_CODING_ALL, supplyurl, sizeof(supplyurl), WEB_SCHEME, NULL, printer->hostname, printer->port, "/supplies");
1562b9a1 1481 httpAssembleUUID(printer->hostname, serverport, name, 0, uuid, sizeof(uuid));
1106b00e
MS
1482
1483 if (Verbosity)
1484 {
1485 fprintf(stderr, "printer-more-info=\"%s\"\n", adminurl);
4a838088 1486 fprintf(stderr, "printer-supply-info-uri=\"%s\"\n", supplyurl);
1562b9a1 1487#ifdef HAVE_SSL
1106b00e 1488 fprintf(stderr, "printer-uri=\"%s\"\n", uri);
1562b9a1
MS
1489#else
1490 fprintf(stderr, "printer-uri=\"%s\",\"%s\"\n", uri, securi);
1491#endif /* HAVE_SSL */
7d5824d6 1492 }
1106b00e
MS
1493
1494 /*
1495 * Get the maximum spool size based on the size of the filesystem used for
1496 * the spool directory. If the host OS doesn't support the statfs call
1497 * or the filesystem is larger than 2TiB, always report INT_MAX.
1498 */
1499
e60ec91f
MS
1500#ifdef HAVE_STATVFS
1501 if (statvfs(printer->directory, &spoolinfo))
1106b00e 1502 k_supported = INT_MAX;
e60ec91f 1503 else if ((spoolsize = (double)spoolinfo.f_frsize *
1106b00e
MS
1504 spoolinfo.f_blocks / 1024) > INT_MAX)
1505 k_supported = INT_MAX;
1506 else
1507 k_supported = (int)spoolsize;
1508
e60ec91f
MS
1509#elif defined(HAVE_STATFS)
1510 if (statfs(printer->directory, &spoolinfo))
1106b00e 1511 k_supported = INT_MAX;
e60ec91f 1512 else if ((spoolsize = (double)spoolinfo.f_bsize *
1106b00e
MS
1513 spoolinfo.f_blocks / 1024) > INT_MAX)
1514 k_supported = INT_MAX;
1515 else
1516 k_supported = (int)spoolsize;
1517
1518#else
1519 k_supported = INT_MAX;
e60ec91f 1520#endif /* HAVE_STATVFS */
1106b00e
MS
1521
1522 /*
1562b9a1 1523 * Assemble the final list of document formats...
1106b00e
MS
1524 */
1525
1562b9a1
MS
1526 if (!cupsArrayFind(docformats, (void *)"application/octet-stream"))
1527 cupsArrayAdd(docformats, (void *)"application/octet-stream");
1106b00e 1528
1562b9a1
MS
1529 for (num_formats = 0, format = (const char *)cupsArrayFirst(docformats); format && num_formats < (int)(sizeof(formats) / sizeof(formats[0])); format = (const char *)cupsArrayNext(docformats))
1530 formats[num_formats ++] = format;
1531
1532 /*
1533 * Get the list of attributes that can be used when creating a job...
1534 */
1535
1536 num_job_attrs = 0;
1537 job_attrs[num_job_attrs ++] = "ipp-attribute-fidelity";
1538 job_attrs[num_job_attrs ++] = "job-name";
1539 job_attrs[num_job_attrs ++] = "job-priority";
4d2df926
MS
1540 job_attrs[num_job_attrs ++] = "media";
1541 job_attrs[num_job_attrs ++] = "media-col";
1562b9a1
MS
1542 job_attrs[num_job_attrs ++] = "multiple-document-handling";
1543
1544 for (i = 0; i < (int)(sizeof(job_creation) / sizeof(job_creation[0])) && num_job_attrs < (int)(sizeof(job_attrs) / sizeof(job_attrs[0])); i ++)
1545 {
1546 snprintf(xxx_supported, sizeof(xxx_supported), "%s-supported", job_creation[i]);
4d2df926 1547 if (ippFindAttribute(attrs, xxx_supported, IPP_TAG_ZERO))
1562b9a1
MS
1548 job_attrs[num_job_attrs ++] = job_creation[i];
1549 }
1550
1551 /*
1552 * Fill out the rest of the printer attributes.
1553 */
1554
1555 printer->attrs = attrs;
15a9714c 1556
1106b00e 1557 /* charset-configured */
15a9714c 1558 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-configured", NULL, "utf-8");
1106b00e
MS
1559
1560 /* charset-supported */
15a9714c 1561 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-supported", sizeof(charsets) / sizeof(charsets[0]), NULL, charsets);
1106b00e 1562
1106b00e 1563 /* compression-supported */
15a9714c
MS
1564 if (!ippFindAttribute(printer->attrs, "compression-supported", IPP_TAG_ZERO))
1565 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "compression-supported", (int)(sizeof(compressions) / sizeof(compressions[0])), NULL, compressions);
1106b00e 1566
1106b00e 1567 /* document-format-default */
1562b9a1 1568 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_MIMETYPE), "document-format-default", NULL, "application/octet-stream");
1106b00e
MS
1569
1570 /* document-format-supported */
1562b9a1 1571 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE, "document-format-supported", num_formats, NULL, formats);
1106b00e
MS
1572
1573 /* generated-natural-language-supported */
15a9714c 1574 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "generated-natural-language-supported", NULL, "en");
1106b00e 1575
2cadf0f4 1576 /* identify-actions-default */
1562b9a1 1577 ippAddString (printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-default", NULL, "sound");
2cadf0f4
MS
1578
1579 /* identify-actions-supported */
1562b9a1 1580 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
1581
1582 /* ipp-features-supported */
1562b9a1 1583 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-features-supported", sizeof(features) / sizeof(features[0]), NULL, features);
2cadf0f4 1584
1106b00e 1585 /* ipp-versions-supported */
1562b9a1
MS
1586 if (MaxVersion == 11)
1587 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", NULL, "1.1");
1588 else
1589 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", (int)(sizeof(versions) / sizeof(versions[0])), NULL, versions);
5a9febac 1590
1106b00e 1591 /* job-creation-attributes-supported */
1562b9a1 1592 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-creation-attributes-supported", num_job_attrs, NULL, job_attrs);
2cadf0f4
MS
1593
1594 /* job-ids-supported */
1595 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-ids-supported", 1);
1106b00e
MS
1596
1597 /* job-k-octets-supported */
1562b9a1 1598 ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0, k_supported);
5a9febac 1599
1106b00e 1600 /* job-priority-default */
1562b9a1 1601 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-default", 50);
1106b00e
MS
1602
1603 /* job-priority-supported */
1562b9a1 1604 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-supported", 1);
1106b00e
MS
1605
1606 /* job-sheets-default */
1562b9a1 1607 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-default", NULL, "none");
1106b00e
MS
1608
1609 /* job-sheets-supported */
15a9714c 1610 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-supported", NULL, "none");
1106b00e 1611
1106b00e 1612 /* media-col-supported */
1562b9a1 1613 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 1614
1106b00e 1615 /* multiple-document-handling-supported */
2cadf0f4 1616 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
1617
1618 /* multiple-document-jobs-supported */
2cadf0f4
MS
1619 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "multiple-document-jobs-supported", 0);
1620
1621 /* multiple-operation-time-out */
1622 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "multiple-operation-time-out", 60);
1623
1624 /* multiple-operation-time-out-action */
c2c30ebc 1625 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "multiple-operation-time-out-action", NULL, "abort-job");
1106b00e
MS
1626
1627 /* natural-language-configured */
1562b9a1 1628 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "natural-language-configured", NULL, "en");
1106b00e
MS
1629
1630 /* operations-supported */
15a9714c 1631 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
1106b00e 1632
1106b00e 1633 /* pdl-override-supported */
1562b9a1 1634 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pdl-override-supported", NULL, "attempted");
1106b00e 1635
5947e9d5
MS
1636 /* preferred-attributes-supported */
1637 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "preferred-attributes-supported", 0);
1638
2cadf0f4
MS
1639 /* printer-get-attributes-supported */
1640 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-get-attributes-supported", NULL, "document-format");
1641
1642 /* printer-geo-location */
1562b9a1 1643 ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_UNKNOWN, "printer-geo-location");
2cadf0f4 1644
1106b00e 1645 /* printer-icons */
1562b9a1 1646 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons", NULL, icons);
1106b00e
MS
1647
1648 /* printer-is-accepting-jobs */
15a9714c 1649 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
1106b00e
MS
1650
1651 /* printer-info */
15a9714c 1652 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL, name);
1106b00e
MS
1653
1654 /* printer-location */
1562b9a1 1655 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", NULL, location);
5a9febac 1656
1106b00e 1657 /* printer-more-info */
4a838088 1658 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info", NULL, adminurl);
1106b00e
MS
1659
1660 /* printer-name */
15a9714c 1661 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL, name);
1106b00e 1662
2cadf0f4 1663 /* printer-organization */
1562b9a1 1664 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organization", NULL, "");
2cadf0f4
MS
1665
1666 /* printer-organizational-unit */
1562b9a1 1667 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organizational-unit", NULL, "");
4a838088
MS
1668
1669 /* printer-supply-info-uri */
1670 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-supply-info-uri", NULL, supplyurl);
1671
1106b00e 1672 /* printer-uri-supported */
15a9714c
MS
1673#ifdef HAVE_SSL
1674 uris[0] = uri;
1675 uris[1] = securi;
1676
1677 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", 2, NULL, (const char **)uris);
1678
1679#else
c2c30ebc 1680 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri);
15a9714c 1681#endif /* HAVE_SSL */
c2c30ebc
MS
1682
1683 /* printer-uuid */
c2c30ebc 1684 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uuid", NULL, uuid);
1106b00e 1685
d7225fc2 1686 /* reference-uri-scheme-supported */
15a9714c 1687 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 1688
1106b00e 1689 /* uri-authentication-supported */
15a9714c
MS
1690#ifdef HAVE_SSL
1691 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", 2, NULL, uri_authentication_supported);
1692#else
1693 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", NULL, "none");
1694#endif /* HAVE_SSL */
1106b00e
MS
1695
1696 /* uri-security-supported */
15a9714c
MS
1697#ifdef HAVE_SSL
1698 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", 2, NULL, uri_security_supported);
1699#else
1700 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", NULL, "none");
1701#endif /* HAVE_SSL */
1106b00e
MS
1702
1703 /* which-jobs-supported */
15a9714c 1704 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 1705
83e08001 1706 debug_attributes("Printer", printer->attrs, 0);
1106b00e
MS
1707
1708 /*
1709 * Register the printer with Bonjour...
1710 */
1711
92dad945 1712 if (!register_printer(printer, subtypes))
1106b00e
MS
1713 goto bad_printer;
1714
1715 /*
1716 * Return it!
1717 */
1718
1719 return (printer);
1720
1721
1722 /*
1723 * If we get here we were unable to create the printer...
1724 */
1725
1726 bad_printer:
1727
1728 delete_printer(printer);
1562b9a1 1729
1106b00e
MS
1730 return (NULL);
1731}
1732
1733
1106b00e
MS
1734/*
1735 * 'debug_attributes()' - Print attributes in a request or response.
1736 */
1737
1738static void
1739debug_attributes(const char *title, /* I - Title */
83e08001
MS
1740 ipp_t *ipp, /* I - Request/response */
1741 int type) /* I - 0 = object, 1 = request, 2 = response */
1106b00e
MS
1742{
1743 ipp_tag_t group_tag; /* Current group */
1744 ipp_attribute_t *attr; /* Current attribute */
1745 char buffer[2048]; /* String buffer for value */
a469f8a5 1746 int major, minor; /* Version */
1106b00e
MS
1747
1748
1749 if (Verbosity <= 1)
1750 return;
1751
1752 fprintf(stderr, "%s:\n", title);
a469f8a5
MS
1753 major = ippGetVersion(ipp, &minor);
1754 fprintf(stderr, " version=%d.%d\n", major, minor);
83e08001
MS
1755 if (type == 1)
1756 fprintf(stderr, " operation-id=%s(%04x)\n",
a469f8a5 1757 ippOpString(ippGetOperation(ipp)), ippGetOperation(ipp));
83e08001
MS
1758 else if (type == 2)
1759 fprintf(stderr, " status-code=%s(%04x)\n",
a469f8a5
MS
1760 ippErrorString(ippGetStatusCode(ipp)), ippGetStatusCode(ipp));
1761 fprintf(stderr, " request-id=%d\n\n", ippGetRequestId(ipp));
83e08001 1762
a469f8a5
MS
1763 for (attr = ippFirstAttribute(ipp), group_tag = IPP_TAG_ZERO;
1764 attr;
1765 attr = ippNextAttribute(ipp))
1106b00e 1766 {
a469f8a5 1767 if (ippGetGroupTag(attr) != group_tag)
1106b00e 1768 {
a469f8a5 1769 group_tag = ippGetGroupTag(attr);
1106b00e
MS
1770 fprintf(stderr, " %s\n", ippTagString(group_tag));
1771 }
1772
a469f8a5 1773 if (ippGetName(attr))
1106b00e 1774 {
a2326b5b 1775 ippAttributeString(attr, buffer, sizeof(buffer));
a469f8a5
MS
1776 fprintf(stderr, " %s (%s%s) %s\n", ippGetName(attr),
1777 ippGetCount(attr) > 1 ? "1setOf " : "",
1778 ippTagString(ippGetValueTag(attr)), buffer);
1106b00e
MS
1779 }
1780 }
1781}
1782
1783
1784/*
1785 * 'delete_client()' - Close the socket and free all memory used by a client
1786 * object.
1787 */
1788
1789static void
d46dbe1b 1790delete_client(ippeve_client_t *client) /* I - Client */
1106b00e
MS
1791{
1792 if (Verbosity)
a469f8a5 1793 fprintf(stderr, "Closing connection from %s\n", client->hostname);
1106b00e
MS
1794
1795 /*
1796 * Flush pending writes before closing...
1797 */
1798
a469f8a5 1799 httpFlushWrite(client->http);
1106b00e
MS
1800
1801 /*
1802 * Free memory...
1803 */
1804
a469f8a5 1805 httpClose(client->http);
1106b00e
MS
1806
1807 ippDelete(client->request);
1106b00e
MS
1808 ippDelete(client->response);
1809
1810 free(client);
1811}
1812
1813
1814/*
1815 * 'delete_job()' - Remove from the printer and free all memory used by a job
1816 * object.
1817 */
1818
1819static void
d46dbe1b 1820delete_job(ippeve_job_t *job) /* I - Job */
1106b00e
MS
1821{
1822 if (Verbosity)
1823 fprintf(stderr, "Removing job #%d from history.\n", job->id);
1824
1825 ippDelete(job->attrs);
1826
1827 if (job->filename)
1828 {
1829 if (!KeepFiles)
1830 unlink(job->filename);
1831
1832 free(job->filename);
1833 }
1834
1835 free(job);
1836}
1837
1838
1839/*
1840 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
1841 * used by a printer object.
1842 */
1843
1844static void
d46dbe1b 1845delete_printer(ippeve_printer_t *printer) /* I - Printer */
1106b00e
MS
1846{
1847 if (printer->ipv4 >= 0)
1848 close(printer->ipv4);
1849
1850 if (printer->ipv6 >= 0)
1851 close(printer->ipv6);
1852
0268488e 1853#if HAVE_DNSSD
1106b00e
MS
1854 if (printer->printer_ref)
1855 DNSServiceRefDeallocate(printer->printer_ref);
1106b00e
MS
1856 if (printer->ipp_ref)
1857 DNSServiceRefDeallocate(printer->ipp_ref);
a469f8a5
MS
1858 if (printer->ipps_ref)
1859 DNSServiceRefDeallocate(printer->ipps_ref);
1106b00e
MS
1860 if (printer->http_ref)
1861 DNSServiceRefDeallocate(printer->http_ref);
d6563739
MS
1862#elif defined(HAVE_AVAHI)
1863 avahi_threaded_poll_lock(DNSSDMaster);
1106b00e 1864
d6563739
MS
1865 if (printer->printer_ref)
1866 avahi_entry_group_free(printer->printer_ref);
1867 if (printer->ipp_ref)
1868 avahi_entry_group_free(printer->ipp_ref);
1869 if (printer->ipps_ref)
1870 avahi_entry_group_free(printer->ipps_ref);
1871 if (printer->http_ref)
1872 avahi_entry_group_free(printer->http_ref);
1106b00e 1873
d6563739
MS
1874 avahi_threaded_poll_unlock(DNSSDMaster);
1875#endif /* HAVE_DNSSD */
1106b00e 1876
1106b00e 1877 if (printer->dnssd_name)
a469f8a5 1878 free(printer->dnssd_name);
0268488e 1879 if (printer->name)
a469f8a5 1880 free(printer->name);
1106b00e 1881 if (printer->icon)
a469f8a5 1882 free(printer->icon);
db8b865d
MS
1883 if (printer->command)
1884 free(printer->command);
3e5092db
MS
1885 if (printer->device_uri)
1886 free(printer->device_uri);
1887 if (printer->ppdfile)
1888 free(printer->ppdfile);
1106b00e 1889 if (printer->directory)
a469f8a5 1890 free(printer->directory);
1106b00e 1891 if (printer->hostname)
a469f8a5 1892 free(printer->hostname);
1106b00e 1893 if (printer->uri)
a469f8a5 1894 free(printer->uri);
1106b00e
MS
1895
1896 ippDelete(printer->attrs);
1897 cupsArrayDelete(printer->jobs);
1898
1899 free(printer);
1900}
1901
1902
0268488e 1903#ifdef HAVE_DNSSD
1106b00e
MS
1904/*
1905 * 'dnssd_callback()' - Handle Bonjour registration events.
1906 */
1907
726429cb 1908static void DNSSD_API
1106b00e
MS
1909dnssd_callback(
1910 DNSServiceRef sdRef, /* I - Service reference */
1911 DNSServiceFlags flags, /* I - Status flags */
1912 DNSServiceErrorType errorCode, /* I - Error, if any */
1913 const char *name, /* I - Service name */
1914 const char *regtype, /* I - Service type */
1915 const char *domain, /* I - Domain for service */
d46dbe1b 1916 ippeve_printer_t *printer) /* I - Printer */
1106b00e 1917{
7e86f2f6
MS
1918 (void)sdRef;
1919 (void)flags;
1920 (void)domain;
1921
1106b00e
MS
1922 if (errorCode)
1923 {
6d56631f 1924 fprintf(stderr, "DNSServiceRegister for %s failed with error %d.\n", regtype, (int)errorCode);
1106b00e
MS
1925 return;
1926 }
2cadf0f4 1927 else if (strcasecmp(name, printer->dnssd_name))
1106b00e
MS
1928 {
1929 if (Verbosity)
1930 fprintf(stderr, "Now using DNS-SD service name \"%s\".\n", name);
1931
1932 /* No lock needed since only the main thread accesses/changes this */
a469f8a5
MS
1933 free(printer->dnssd_name);
1934 printer->dnssd_name = strdup(name);
1106b00e
MS
1935 }
1936}
d6563739
MS
1937
1938
1939#elif defined(HAVE_AVAHI)
1940/*
1941 * 'dnssd_callback()' - Handle Bonjour registration events.
1942 */
1943
1944static void
1945dnssd_callback(
1946 AvahiEntryGroup *srv, /* I - Service */
1947 AvahiEntryGroupState state, /* I - Registration state */
1948 void *context) /* I - Printer */
1949{
1950 (void)srv;
1951 (void)state;
1952 (void)context;
1953}
1954
1955
1956/*
1957 * 'dnssd_client_cb()' - Client callback for Avahi.
1958 *
1959 * Called whenever the client or server state changes...
1960 */
1961
1962static void
1963dnssd_client_cb(
1964 AvahiClient *c, /* I - Client */
1965 AvahiClientState state, /* I - Current state */
1966 void *userdata) /* I - User data (unused) */
1967{
1968 (void)userdata;
1969
1970 if (!c)
1971 return;
1972
1973 switch (state)
1974 {
1975 default :
6d56631f 1976 fprintf(stderr, "Ignored Avahi state %d.\n", state);
d6563739
MS
1977 break;
1978
1979 case AVAHI_CLIENT_FAILURE:
1980 if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
1981 {
1982 fputs("Avahi server crashed, exiting.\n", stderr);
1983 exit(1);
1984 }
1985 break;
1986 }
1987}
1988#endif /* HAVE_DNSSD */
1989
1990
1991/*
1992 * 'dnssd_init()' - Initialize the DNS-SD service connections...
1993 */
1994
1995static void
1996dnssd_init(void)
1997{
1998#ifdef HAVE_DNSSD
1999 if (DNSServiceCreateConnection(&DNSSDMaster) != kDNSServiceErr_NoError)
2000 {
2001 fputs("Error: Unable to initialize Bonjour.\n", stderr);
2002 exit(1);
2003 }
2004
2005#elif defined(HAVE_AVAHI)
2006 int error; /* Error code, if any */
2007
2008 if ((DNSSDMaster = avahi_threaded_poll_new()) == NULL)
2009 {
2010 fputs("Error: Unable to initialize Bonjour.\n", stderr);
2011 exit(1);
2012 }
2013
2014 if ((DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssd_client_cb, NULL, &error)) == NULL)
2015 {
2016 fputs("Error: Unable to initialize Bonjour.\n", stderr);
2017 exit(1);
2018 }
2019
2020 avahi_threaded_poll_start(DNSSDMaster);
0268488e 2021#endif /* HAVE_DNSSD */
d6563739 2022}
1106b00e
MS
2023
2024
c2c30ebc
MS
2025/*
2026 * 'filter_cb()' - Filter printer attributes based on the requested array.
2027 */
2028
2029static int /* O - 1 to copy, 0 to ignore */
d46dbe1b 2030filter_cb(ippeve_filter_t *filter, /* I - Filter parameters */
c2c30ebc
MS
2031 ipp_t *dst, /* I - Destination (unused) */
2032 ipp_attribute_t *attr) /* I - Source attribute */
2033{
2034 /*
2035 * Filter attributes as needed...
2036 */
2037
24a06ed3 2038#ifndef _WIN32 /* Avoid MS compiler bug */
d6563739 2039 (void)dst;
24a06ed3 2040#endif /* !_WIN32 */
4a838088 2041
c2c30ebc
MS
2042 ipp_tag_t group = ippGetGroupTag(attr);
2043 const char *name = ippGetName(attr);
2044
2045 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)))
2046 return (0);
2047
2048 return (!filter->ra || cupsArrayFind(filter->ra, (void *)name) != NULL);
2049}
2050
2051
1106b00e
MS
2052/*
2053 * 'find_job()' - Find a job specified in a request.
2054 */
2055
d46dbe1b
MS
2056static ippeve_job_t * /* O - Job or NULL */
2057find_job(ippeve_client_t *client) /* I - Client */
1106b00e
MS
2058{
2059 ipp_attribute_t *attr; /* job-id or job-uri attribute */
d46dbe1b 2060 ippeve_job_t key, /* Job search key */
1106b00e
MS
2061 *job; /* Matching job, if any */
2062
2063
4a838088 2064 if ((attr = ippFindAttribute(client->request, "job-uri", IPP_TAG_URI)) != NULL)
1106b00e 2065 {
a469f8a5
MS
2066 const char *uri = ippGetString(attr, 0, NULL);
2067
2068 if (!strncmp(uri, client->printer->uri, client->printer->urilen) &&
2069 uri[client->printer->urilen] == '/')
2070 key.id = atoi(uri + client->printer->urilen + 1);
4a838088
MS
2071 else
2072 return (NULL);
1106b00e 2073 }
4a838088 2074 else if ((attr = ippFindAttribute(client->request, "job-id", IPP_TAG_INTEGER)) != NULL)
a469f8a5 2075 key.id = ippGetInteger(attr, 0);
1106b00e
MS
2076
2077 _cupsRWLockRead(&(client->printer->rwlock));
d46dbe1b 2078 job = (ippeve_job_t *)cupsArrayFind(client->printer->jobs, &key);
1106b00e
MS
2079 _cupsRWUnlock(&(client->printer->rwlock));
2080
2081 return (job);
2082}
2083
2084
cdb2e724
MS
2085/*
2086 * 'finish_document()' - Finish receiving a document file and start processing.
2087 */
2088
2089static void
a5ed2e39
MS
2090finish_document_data(
2091 ippeve_client_t *client, /* I - Client */
2092 ippeve_job_t *job) /* I - Job */
cdb2e724
MS
2093{
2094 char filename[1024], /* Filename buffer */
2095 buffer[4096]; /* Copy buffer */
2096 ssize_t bytes; /* Bytes read */
2097 cups_array_t *ra; /* Attributes to send in response */
2098 _cups_thread_t t; /* Thread */
2099
2100
2101 /*
2102 * Create a file for the request data...
2103 *
2104 * TODO: Update code to support piping large raster data to the print command.
2105 */
2106
dd2b6166 2107 if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0)
cdb2e724 2108 {
cdb2e724 2109 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno));
a5ed2e39
MS
2110
2111 goto abort_job;
cdb2e724
MS
2112 }
2113
2114 if (Verbosity)
2115 fprintf(stderr, "Created job file \"%s\", format \"%s\".\n", filename, job->format);
2116
2117 while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
2118 {
2119 if (write(job->fd, buffer, (size_t)bytes) < bytes)
2120 {
2121 int error = errno; /* Write error */
2122
cdb2e724
MS
2123 close(job->fd);
2124 job->fd = -1;
2125
2126 unlink(filename);
2127
2128 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
a5ed2e39
MS
2129
2130 goto abort_job;
cdb2e724
MS
2131 }
2132 }
2133
2134 if (bytes < 0)
2135 {
2136 /*
2137 * Got an error while reading the print data, so abort this job.
2138 */
2139
cdb2e724
MS
2140 close(job->fd);
2141 job->fd = -1;
2142
2143 unlink(filename);
2144
2145 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to read print file.");
a5ed2e39
MS
2146
2147 goto abort_job;
cdb2e724
MS
2148 }
2149
2150 if (close(job->fd))
2151 {
2152 int error = errno; /* Write error */
2153
a5ed2e39 2154 job->fd = -1;
cdb2e724
MS
2155
2156 unlink(filename);
2157
2158 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
a5ed2e39
MS
2159
2160 goto abort_job;
cdb2e724
MS
2161 }
2162
2163 job->fd = -1;
2164 job->filename = strdup(filename);
2165 job->state = IPP_JSTATE_PENDING;
2166
2167 /*
2168 * Process the job...
2169 */
2170
2171 t = _cupsThreadCreate((_cups_thread_func_t)process_job, job);
2172
2173 if (t)
2174 {
2175 _cupsThreadDetach(t);
2176 }
2177 else
2178 {
cdb2e724 2179 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
a5ed2e39 2180 goto abort_job;
cdb2e724
MS
2181 }
2182
2183 /*
2184 * Return the job info...
2185 */
2186
2187 respond_ipp(client, IPP_STATUS_OK, NULL);
2188
2189 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2190 cupsArrayAdd(ra, "job-id");
2191 cupsArrayAdd(ra, "job-state");
2192 cupsArrayAdd(ra, "job-state-message");
2193 cupsArrayAdd(ra, "job-state-reasons");
2194 cupsArrayAdd(ra, "job-uri");
2195
a5ed2e39
MS
2196 copy_job_attributes(client, job, ra);
2197 cupsArrayDelete(ra);
2198 return;
2199
2200 /*
2201 * If we get here we had to abort the job...
2202 */
2203
2204 abort_job:
2205
2206 job->state = IPP_JSTATE_ABORTED;
2207 job->completed = time(NULL);
2208
2209 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2210 cupsArrayAdd(ra, "job-id");
2211 cupsArrayAdd(ra, "job-state");
2212 cupsArrayAdd(ra, "job-state-reasons");
2213 cupsArrayAdd(ra, "job-uri");
2214
cdb2e724
MS
2215 copy_job_attributes(client, job, ra);
2216 cupsArrayDelete(ra);
2217}
2218
2219
2220/*
2221 * 'finish_uri()' - Finish fetching a document URI and start processing.
2222 */
2223
2224static void
a5ed2e39
MS
2225finish_document_uri(
2226 ippeve_client_t *client, /* I - Client */
2227 ippeve_job_t *job) /* I - Job */
cdb2e724 2228{
a5ed2e39
MS
2229 ipp_attribute_t *uri; /* document-uri */
2230 char scheme[256], /* URI scheme */
2231 userpass[256], /* Username and password info */
2232 hostname[256], /* Hostname */
2233 resource[1024]; /* Resource path */
2234 int port; /* Port number */
2235 http_uri_status_t uri_status; /* URI decode status */
2236 http_encryption_t encryption; /* Encryption to use, if any */
2237 http_t *http; /* Connection for http/https URIs */
2238 http_status_t status; /* Access status for http/https URIs */
2239 int infile; /* Input file for local file URIs */
2240 char filename[1024], /* Filename buffer */
2241 buffer[4096]; /* Copy buffer */
2242 ssize_t bytes; /* Bytes read */
2243 ipp_attribute_t *attr; /* Current attribute */
2244 cups_array_t *ra; /* Attributes to send in response */
2245
2246
2247 /*
2248 * Do we have a file to print?
2249 */
2250
2251 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
2252 {
2253 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Unexpected document data following request.");
2254
2255 goto abort_job;
2256 }
2257
2258 /*
2259 * Do we have a document URI?
2260 */
2261
2262 if ((uri = ippFindAttribute(client->request, "document-uri", IPP_TAG_URI)) == NULL)
2263 {
2264 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
2265
2266 goto abort_job;
2267 }
2268
2269 if (ippGetCount(uri) != 1)
2270 {
2271 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Too many document-uri values.");
2272
2273 goto abort_job;
2274 }
2275
2276 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
2277 scheme, sizeof(scheme), userpass,
2278 sizeof(userpass), hostname, sizeof(hostname),
2279 &port, resource, sizeof(resource));
2280 if (uri_status < HTTP_URI_STATUS_OK)
2281 {
2282 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s", httpURIStatusString(uri_status));
2283
2284 goto abort_job;
2285 }
2286
2287 if (strcmp(scheme, "file") &&
2288#ifdef HAVE_SSL
2289 strcmp(scheme, "https") &&
2290#endif /* HAVE_SSL */
2291 strcmp(scheme, "http"))
2292 {
2293 respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME, "URI scheme \"%s\" not supported.", scheme);
2294
2295 goto abort_job;
2296 }
2297
2298 if (!strcmp(scheme, "file") && access(resource, R_OK))
2299 {
2300 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno));
2301
2302 goto abort_job;
2303 }
2304
2305 /*
2306 * Get the document format for the job...
2307 */
2308
2309 _cupsRWLockWrite(&(client->printer->rwlock));
2310
2311 if ((attr = ippFindAttribute(job->attrs, "document-format", IPP_TAG_MIMETYPE)) != NULL)
2312 job->format = ippGetString(attr, 0, NULL);
2313 else
2314 job->format = "application/octet-stream";
2315
2316 /*
2317 * Create a file for the request data...
2318 */
2319
dd2b6166 2320 if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0)
a5ed2e39
MS
2321 {
2322 _cupsRWUnlock(&(client->printer->rwlock));
2323
2324 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno));
2325
2326 goto abort_job;
2327 }
2328
2329 _cupsRWUnlock(&(client->printer->rwlock));
2330
2331 if (!strcmp(scheme, "file"))
2332 {
2333 if ((infile = open(resource, O_RDONLY)) < 0)
2334 {
2335 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno));
2336
2337 goto abort_job;
2338 }
2339
2340 do
2341 {
2342 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
2343 (errno == EAGAIN || errno == EINTR))
2344 {
2345 bytes = 1;
2346 }
2347 else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes)
2348 {
2349 int error = errno; /* Write error */
2350
2351 close(job->fd);
2352 job->fd = -1;
2353
2354 unlink(filename);
2355 close(infile);
2356
2357 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2358
2359 goto abort_job;
2360 }
2361 }
2362 while (bytes > 0);
2363
2364 close(infile);
2365 }
2366 else
2367 {
2368#ifdef HAVE_SSL
2369 if (port == 443 || !strcmp(scheme, "https"))
2370 encryption = HTTP_ENCRYPTION_ALWAYS;
2371 else
2372#endif /* HAVE_SSL */
2373 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
2374
2375 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL)
2376 {
2377 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to connect to %s: %s", hostname, cupsLastErrorString());
2378
2379 close(job->fd);
2380 job->fd = -1;
2381
2382 unlink(filename);
2383
2384 goto abort_job;
2385 }
2386
2387 httpClearFields(http);
2388 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
2389 if (httpGet(http, resource))
2390 {
2391 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", strerror(errno));
2392
2393 close(job->fd);
2394 job->fd = -1;
2395
2396 unlink(filename);
2397 httpClose(http);
2398
2399 goto abort_job;
2400 }
2401
2402 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
2403
2404 if (status != HTTP_STATUS_OK)
2405 {
2406 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", httpStatus(status));
2407
2408 close(job->fd);
2409 job->fd = -1;
2410
2411 unlink(filename);
2412 httpClose(http);
2413
2414 goto abort_job;
2415 }
2416
2417 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
2418 {
2419 if (write(job->fd, buffer, (size_t)bytes) < bytes)
2420 {
2421 int error = errno; /* Write error */
2422
2423 close(job->fd);
2424 job->fd = -1;
2425
2426 unlink(filename);
2427 httpClose(http);
2428
2429 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
2430 "Unable to write print file: %s", strerror(error));
2431
2432 goto abort_job;
2433 }
2434 }
2435
2436 httpClose(http);
2437 }
2438
2439 if (close(job->fd))
2440 {
2441 int error = errno; /* Write error */
2442
2443 job->fd = -1;
2444
2445 unlink(filename);
2446
2447 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2448
2449 goto abort_job;
2450 }
2451
2452 _cupsRWLockWrite(&(client->printer->rwlock));
2453
2454 job->fd = -1;
2455 job->filename = strdup(filename);
2456 job->state = IPP_JSTATE_PENDING;
2457
2458 _cupsRWUnlock(&(client->printer->rwlock));
2459
2460 /*
2461 * Process the job...
2462 */
2463
2464 process_job(job);
2465
2466 /*
2467 * Return the job info...
2468 */
2469
2470 respond_ipp(client, IPP_STATUS_OK, NULL);
2471
2472 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2473 cupsArrayAdd(ra, "job-id");
2474 cupsArrayAdd(ra, "job-state");
2475 cupsArrayAdd(ra, "job-state-reasons");
2476 cupsArrayAdd(ra, "job-uri");
2477
2478 copy_job_attributes(client, job, ra);
2479 cupsArrayDelete(ra);
2480 return;
2481
2482 /*
2483 * If we get here we had to abort the job...
2484 */
2485
2486 abort_job:
2487
2488 job->state = IPP_JSTATE_ABORTED;
2489 job->completed = time(NULL);
2490
2491 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2492 cupsArrayAdd(ra, "job-id");
2493 cupsArrayAdd(ra, "job-state");
2494 cupsArrayAdd(ra, "job-state-reasons");
2495 cupsArrayAdd(ra, "job-uri");
2496
2497 copy_job_attributes(client, job, ra);
2498 cupsArrayDelete(ra);
cdb2e724
MS
2499}
2500
2501
1106b00e
MS
2502/*
2503 * 'html_escape()' - Write a HTML-safe string.
2504 */
2505
2506static void
d46dbe1b 2507html_escape(ippeve_client_t *client, /* I - Client */
1106b00e
MS
2508 const char *s, /* I - String to write */
2509 size_t slen) /* I - Number of characters to write */
2510{
2511 const char *start, /* Start of segment */
2512 *end; /* End of string */
2513
2514
2515 start = s;
2516 end = s + (slen > 0 ? slen : strlen(s));
2517
2518 while (*s && s < end)
2519 {
2520 if (*s == '&' || *s == '<')
2521 {
2522 if (s > start)
7e86f2f6 2523 httpWrite2(client->http, start, (size_t)(s - start));
1106b00e
MS
2524
2525 if (*s == '&')
a469f8a5 2526 httpWrite2(client->http, "&amp;", 5);
1106b00e 2527 else
a469f8a5 2528 httpWrite2(client->http, "&lt;", 4);
1106b00e
MS
2529
2530 start = s + 1;
2531 }
2532
2533 s ++;
2534 }
2535
2536 if (s > start)
7e86f2f6 2537 httpWrite2(client->http, start, (size_t)(s - start));
1106b00e
MS
2538}
2539
2540
0b5ce83f
MS
2541/*
2542 * 'html_footer()' - Show the web interface footer.
2543 *
2544 * This function also writes the trailing 0-length chunk.
2545 */
2546
2547static void
d46dbe1b 2548html_footer(ippeve_client_t *client) /* I - Client */
0b5ce83f
MS
2549{
2550 html_printf(client,
2551 "</div>\n"
2552 "</body>\n"
2553 "</html>\n");
2554 httpWrite2(client->http, "", 0);
2555}
2556
2557
2558/*
2559 * 'html_header()' - Show the web interface header and title.
2560 */
2561
2562static void
d46dbe1b 2563html_header(ippeve_client_t *client, /* I - Client */
6640ceec
MS
2564 const char *title, /* I - Title */
2565 int refresh) /* I - Refresh timer, if any */
0b5ce83f
MS
2566{
2567 html_printf(client,
2568 "<!doctype html>\n"
2569 "<html>\n"
2570 "<head>\n"
2571 "<title>%s</title>\n"
2572 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
04f71e64 2573 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
6640ceec
MS
2574 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n", title);
2575 if (refresh > 0)
2576 html_printf(client, "<meta http-equiv=\"refresh\" content=\"%d\">\n", refresh);
2577 html_printf(client,
0b5ce83f
MS
2578 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2579 "<style>\n"
2580 "body { font-family: sans-serif; margin: 0; }\n"
2581 "div.body { padding: 0px 10px 10px; }\n"
820cb58e
MS
2582 "span.badge { background: #090; border-radius: 5px; color: #fff; padding: 5px 10px; }\n"
2583 "span.bar { box-shadow: 0px 1px 5px #333; font-size: 75%%; }\n"
2584 "table.form { border-collapse: collapse; margin-left: auto; margin-right: auto; margin-top: 10px; width: auto; }\n"
2585 "table.form td, table.form th { padding: 5px 2px; }\n"
2586 "table.form td.meter { border-right: solid 1px #ccc; padding: 0px; width: 400px; }\n"
0b5ce83f
MS
2587 "table.form th { text-align: right; }\n"
2588 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2589 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2590 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2591 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2592 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2593 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2594 "table.nav td { margin: 0; text-align: center; }\n"
2595 "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"
2596 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2597 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2598 "td.nav:hover { background: #666; color: #fff; }\n"
2599 "td.nav:active { background: #000; color: #ff0; }\n"
2600 "</style>\n"
2601 "</head>\n"
2602 "<body>\n"
2603 "<table class=\"nav\"><tr>"
2604 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2605 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2606 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2607 "</tr></table>\n"
6640ceec 2608 "<div class=\"body\">\n", !strcmp(client->uri, "/") ? " sel" : "", !strcmp(client->uri, "/supplies") ? " sel" : "", !strcmp(client->uri, "/media") ? " sel" : "");
0b5ce83f
MS
2609}
2610
2611
1106b00e
MS
2612/*
2613 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2614 */
2615
2616static void
d46dbe1b 2617html_printf(ippeve_client_t *client, /* I - Client */
1106b00e
MS
2618 const char *format, /* I - Printf-style format string */
2619 ...) /* I - Additional arguments as needed */
2620{
2621 va_list ap; /* Pointer to arguments */
2622 const char *start; /* Start of string */
2623 char size, /* Size character (h, l, L) */
2624 type; /* Format type character */
2625 int width, /* Width of field */
2626 prec; /* Number of characters of precision */
2627 char tformat[100], /* Temporary format string for sprintf() */
2628 *tptr, /* Pointer into temporary format */
2629 temp[1024]; /* Buffer for formatted numbers */
2630 char *s; /* Pointer to string */
2631
2632
2633 /*
2634 * Loop through the format string, formatting as needed...
2635 */
2636
2637 va_start(ap, format);
2638 start = format;
2639
2640 while (*format)
2641 {
2642 if (*format == '%')
2643 {
2644 if (format > start)
7e86f2f6 2645 httpWrite2(client->http, start, (size_t)(format - start));
1106b00e
MS
2646
2647 tptr = tformat;
2648 *tptr++ = *format++;
2649
2650 if (*format == '%')
2651 {
a469f8a5 2652 httpWrite2(client->http, "%", 1);
1106b00e 2653 format ++;
0b5ce83f 2654 start = format;
1106b00e
MS
2655 continue;
2656 }
2657 else if (strchr(" -+#\'", *format))
2658 *tptr++ = *format++;
2659
2660 if (*format == '*')
2661 {
2662 /*
2663 * Get width from argument...
2664 */
2665
2666 format ++;
2667 width = va_arg(ap, int);
2668
7e86f2f6 2669 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
1106b00e
MS
2670 tptr += strlen(tptr);
2671 }
2672 else
2673 {
2674 width = 0;
2675
2676 while (isdigit(*format & 255))
2677 {
2678 if (tptr < (tformat + sizeof(tformat) - 1))
2679 *tptr++ = *format;
2680
2681 width = width * 10 + *format++ - '0';
2682 }
2683 }
2684
2685 if (*format == '.')
2686 {
2687 if (tptr < (tformat + sizeof(tformat) - 1))
2688 *tptr++ = *format;
2689
2690 format ++;
2691
2692 if (*format == '*')
2693 {
2694 /*
2695 * Get precision from argument...
2696 */
2697
2698 format ++;
2699 prec = va_arg(ap, int);
2700
7e86f2f6 2701 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
1106b00e
MS
2702 tptr += strlen(tptr);
2703 }
2704 else
2705 {
2706 prec = 0;
2707
2708 while (isdigit(*format & 255))
2709 {
2710 if (tptr < (tformat + sizeof(tformat) - 1))
2711 *tptr++ = *format;
2712
2713 prec = prec * 10 + *format++ - '0';
2714 }
2715 }
2716 }
2717
2718 if (*format == 'l' && format[1] == 'l')
2719 {
2720 size = 'L';
2721
2722 if (tptr < (tformat + sizeof(tformat) - 2))
2723 {
2724 *tptr++ = 'l';
2725 *tptr++ = 'l';
2726 }
2727
2728 format += 2;
2729 }
2730 else if (*format == 'h' || *format == 'l' || *format == 'L')
2731 {
2732 if (tptr < (tformat + sizeof(tformat) - 1))
2733 *tptr++ = *format;
2734
2735 size = *format++;
2736 }
2737 else
2738 size = 0;
2739
2740
2741 if (!*format)
2742 {
2743 start = format;
2744 break;
2745 }
2746
2747 if (tptr < (tformat + sizeof(tformat) - 1))
2748 *tptr++ = *format;
2749
2750 type = *format++;
2751 *tptr = '\0';
2752 start = format;
2753
2754 switch (type)
2755 {
2756 case 'E' : /* Floating point formats */
2757 case 'G' :
2758 case 'e' :
2759 case 'f' :
2760 case 'g' :
7e86f2f6 2761 if ((size_t)(width + 2) > sizeof(temp))
1106b00e
MS
2762 break;
2763
2764 sprintf(temp, tformat, va_arg(ap, double));
2765
a469f8a5 2766 httpWrite2(client->http, temp, strlen(temp));
1106b00e
MS
2767 break;
2768
2769 case 'B' : /* Integer formats */
2770 case 'X' :
2771 case 'b' :
2772 case 'd' :
2773 case 'i' :
2774 case 'o' :
2775 case 'u' :
2776 case 'x' :
7e86f2f6 2777 if ((size_t)(width + 2) > sizeof(temp))
1106b00e
MS
2778 break;
2779
2780# ifdef HAVE_LONG_LONG
2781 if (size == 'L')
2782 sprintf(temp, tformat, va_arg(ap, long long));
2783 else
2784# endif /* HAVE_LONG_LONG */
2785 if (size == 'l')
2786 sprintf(temp, tformat, va_arg(ap, long));
2787 else
2788 sprintf(temp, tformat, va_arg(ap, int));
2789
a469f8a5 2790 httpWrite2(client->http, temp, strlen(temp));
1106b00e
MS
2791 break;
2792
2793 case 'p' : /* Pointer value */
7e86f2f6 2794 if ((size_t)(width + 2) > sizeof(temp))
1106b00e
MS
2795 break;
2796
2797 sprintf(temp, tformat, va_arg(ap, void *));
2798
a469f8a5 2799 httpWrite2(client->http, temp, strlen(temp));
1106b00e
MS
2800 break;
2801
2802 case 'c' : /* Character or character array */
2803 if (width <= 1)
2804 {
7e86f2f6 2805 temp[0] = (char)va_arg(ap, int);
1106b00e
MS
2806 temp[1] = '\0';
2807 html_escape(client, temp, 1);
2808 }
2809 else
2810 html_escape(client, va_arg(ap, char *), (size_t)width);
2811 break;
2812
2813 case 's' : /* String */
2814 if ((s = va_arg(ap, char *)) == NULL)
2815 s = "(null)";
2816
2817 html_escape(client, s, strlen(s));
2818 break;
2819 }
2820 }
2821 else
2822 format ++;
2823 }
2824
2825 if (format > start)
7e86f2f6 2826 httpWrite2(client->http, start, (size_t)(format - start));
1106b00e
MS
2827
2828 va_end(ap);
2829}
2830
2831
2832/*
2833 * 'ipp_cancel_job()' - Cancel a job.
2834 */
2835
2836static void
d46dbe1b 2837ipp_cancel_job(ippeve_client_t *client) /* I - Client */
1106b00e 2838{
d46dbe1b 2839 ippeve_job_t *job; /* Job information */
1106b00e
MS
2840
2841
2842 /*
2843 * Get the job...
2844 */
2845
2846 if ((job = find_job(client)) == NULL)
83e08001 2847 {
a469f8a5 2848 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
1106b00e 2849 return;
83e08001 2850 }
1106b00e
MS
2851
2852 /*
2853 * See if the job is already completed, canceled, or aborted; if so,
2854 * we can't cancel...
2855 */
2856
2857 switch (job->state)
2858 {
a469f8a5
MS
2859 case IPP_JSTATE_CANCELED :
2860 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
1106b00e
MS
2861 "Job #%d is already canceled - can\'t cancel.", job->id);
2862 break;
2863
a469f8a5
MS
2864 case IPP_JSTATE_ABORTED :
2865 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
1106b00e
MS
2866 "Job #%d is already aborted - can\'t cancel.", job->id);
2867 break;
2868
a469f8a5
MS
2869 case IPP_JSTATE_COMPLETED :
2870 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
1106b00e
MS
2871 "Job #%d is already completed - can\'t cancel.", job->id);
2872 break;
2873
2874 default :
2875 /*
2876 * Cancel the job...
2877 */
2878
2879 _cupsRWLockWrite(&(client->printer->rwlock));
2880
a469f8a5
MS
2881 if (job->state == IPP_JSTATE_PROCESSING ||
2882 (job->state == IPP_JSTATE_HELD && job->fd >= 0))
1106b00e
MS
2883 job->cancel = 1;
2884 else
2885 {
a469f8a5 2886 job->state = IPP_JSTATE_CANCELED;
1106b00e
MS
2887 job->completed = time(NULL);
2888 }
2889
2890 _cupsRWUnlock(&(client->printer->rwlock));
2891
a469f8a5 2892 respond_ipp(client, IPP_STATUS_OK, NULL);
1106b00e
MS
2893 break;
2894 }
2895}
2896
2897
2cadf0f4
MS
2898/*
2899 * 'ipp_close_job()' - Close an open job.
2900 */
2901
2902static void
d46dbe1b 2903ipp_close_job(ippeve_client_t *client) /* I - Client */
2cadf0f4 2904{
d46dbe1b 2905 ippeve_job_t *job; /* Job information */
2cadf0f4
MS
2906
2907
2908 /*
2909 * Get the job...
2910 */
2911
2912 if ((job = find_job(client)) == NULL)
2913 {
2914 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
2915 return;
2916 }
2917
2918 /*
2919 * See if the job is already completed, canceled, or aborted; if so,
2920 * we can't cancel...
2921 */
2922
2923 switch (job->state)
2924 {
2925 case IPP_JSTATE_CANCELED :
2926 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2927 "Job #%d is canceled - can\'t close.", job->id);
2928 break;
2929
2930 case IPP_JSTATE_ABORTED :
2931 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2932 "Job #%d is aborted - can\'t close.", job->id);
2933 break;
2934
2935 case IPP_JSTATE_COMPLETED :
2936 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2937 "Job #%d is completed - can\'t close.", job->id);
2938 break;
2939
2940 case IPP_JSTATE_PROCESSING :
2941 case IPP_JSTATE_STOPPED :
2942 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2943 "Job #%d is already closed.", job->id);
2944 break;
2945
2946 default :
2947 respond_ipp(client, IPP_STATUS_OK, NULL);
2948 break;
2949 }
2950}
2951
2952
1106b00e
MS
2953/*
2954 * 'ipp_create_job()' - Create a job object.
2955 */
2956
2957static void
d46dbe1b 2958ipp_create_job(ippeve_client_t *client) /* I - Client */
1106b00e 2959{
d46dbe1b 2960 ippeve_job_t *job; /* New job */
83e08001
MS
2961 cups_array_t *ra; /* Attributes to send in response */
2962
2963
2964 /*
2965 * Validate print job attributes...
2966 */
2967
2968 if (!valid_job_attributes(client))
2969 {
a469f8a5 2970 httpFlush(client->http);
83e08001
MS
2971 return;
2972 }
2973
2974 /*
2975 * Do we have a file to print?
2976 */
2977
a469f8a5 2978 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
83e08001 2979 {
a469f8a5 2980 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
83e08001
MS
2981 "Unexpected document data following request.");
2982 return;
2983 }
2984
2985 /*
2986 * Create the job...
2987 */
2988
2989 if ((job = create_job(client)) == NULL)
2990 {
a469f8a5
MS
2991 respond_ipp(client, IPP_STATUS_ERROR_BUSY,
2992 "Currently printing another job.");
83e08001
MS
2993 return;
2994 }
2995
2996 /*
2997 * Return the job info...
2998 */
2999
a469f8a5 3000 respond_ipp(client, IPP_STATUS_OK, NULL);
83e08001
MS
3001
3002 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3003 cupsArrayAdd(ra, "job-id");
3004 cupsArrayAdd(ra, "job-state");
c2c30ebc 3005 cupsArrayAdd(ra, "job-state-message");
83e08001
MS
3006 cupsArrayAdd(ra, "job-state-reasons");
3007 cupsArrayAdd(ra, "job-uri");
3008
3009 copy_job_attributes(client, job, ra);
3010 cupsArrayDelete(ra);
1106b00e 3011}
1106b00e
MS
3012
3013
3014/*
3015 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3016 */
3017
3018static void
3019ipp_get_job_attributes(
d46dbe1b 3020 ippeve_client_t *client) /* I - Client */
1106b00e 3021{
d46dbe1b 3022 ippeve_job_t *job; /* Job */
1106b00e
MS
3023 cups_array_t *ra; /* requested-attributes */
3024
3025
3026 if ((job = find_job(client)) == NULL)
3027 {
a469f8a5 3028 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job not found.");
1106b00e
MS
3029 return;
3030 }
3031
a469f8a5 3032 respond_ipp(client, IPP_STATUS_OK, NULL);
1106b00e 3033
db8b865d 3034 ra = ippCreateRequestedArray(client->request);
1106b00e
MS
3035 copy_job_attributes(client, job, ra);
3036 cupsArrayDelete(ra);
3037}
3038
3039
3040/*
3041 * 'ipp_get_jobs()' - Get a list of job objects.
3042 */
3043
3044static void
d46dbe1b 3045ipp_get_jobs(ippeve_client_t *client) /* I - Client */
1106b00e
MS
3046{
3047 ipp_attribute_t *attr; /* Current attribute */
a469f8a5
MS
3048 const char *which_jobs = NULL;
3049 /* which-jobs values */
1106b00e
MS
3050 int job_comparison; /* Job comparison */
3051 ipp_jstate_t job_state; /* job-state value */
3052 int first_job_id, /* First job ID */
3053 limit, /* Maximum number of jobs to return */
3054 count; /* Number of jobs that match */
3055 const char *username; /* Username */
d46dbe1b 3056 ippeve_job_t *job; /* Current job pointer */
1106b00e
MS
3057 cups_array_t *ra; /* Requested attributes array */
3058
3059
3060 /*
3061 * See if the "which-jobs" attribute have been specified...
3062 */
3063
3064 if ((attr = ippFindAttribute(client->request, "which-jobs",
3065 IPP_TAG_KEYWORD)) != NULL)
a469f8a5
MS
3066 {
3067 which_jobs = ippGetString(attr, 0, NULL);
3068 fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->hostname, which_jobs);
3069 }
1106b00e 3070
a469f8a5 3071 if (!which_jobs || !strcmp(which_jobs, "not-completed"))
1106b00e
MS
3072 {
3073 job_comparison = -1;
a469f8a5 3074 job_state = IPP_JSTATE_STOPPED;
1106b00e 3075 }
a469f8a5 3076 else if (!strcmp(which_jobs, "completed"))
1106b00e
MS
3077 {
3078 job_comparison = 1;
a469f8a5 3079 job_state = IPP_JSTATE_CANCELED;
1106b00e 3080 }
a469f8a5 3081 else if (!strcmp(which_jobs, "aborted"))
1106b00e
MS
3082 {
3083 job_comparison = 0;
a469f8a5 3084 job_state = IPP_JSTATE_ABORTED;
1106b00e 3085 }
a469f8a5 3086 else if (!strcmp(which_jobs, "all"))
1106b00e
MS
3087 {
3088 job_comparison = 1;
a469f8a5 3089 job_state = IPP_JSTATE_PENDING;
1106b00e 3090 }
a469f8a5 3091 else if (!strcmp(which_jobs, "canceled"))
1106b00e
MS
3092 {
3093 job_comparison = 0;
a469f8a5 3094 job_state = IPP_JSTATE_CANCELED;
1106b00e 3095 }
a469f8a5 3096 else if (!strcmp(which_jobs, "pending"))
1106b00e
MS
3097 {
3098 job_comparison = 0;
a469f8a5 3099 job_state = IPP_JSTATE_PENDING;
1106b00e 3100 }
a469f8a5 3101 else if (!strcmp(which_jobs, "pending-held"))
1106b00e
MS
3102 {
3103 job_comparison = 0;
a469f8a5 3104 job_state = IPP_JSTATE_HELD;
1106b00e 3105 }
a469f8a5 3106 else if (!strcmp(which_jobs, "processing"))
1106b00e
MS
3107 {
3108 job_comparison = 0;
a469f8a5 3109 job_state = IPP_JSTATE_PROCESSING;
1106b00e 3110 }
a469f8a5 3111 else if (!strcmp(which_jobs, "processing-stopped"))
1106b00e
MS
3112 {
3113 job_comparison = 0;
a469f8a5 3114 job_state = IPP_JSTATE_STOPPED;
1106b00e
MS
3115 }
3116 else
3117 {
a469f8a5
MS
3118 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
3119 "The which-jobs value \"%s\" is not supported.", which_jobs);
1106b00e 3120 ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
a469f8a5 3121 "which-jobs", NULL, which_jobs);
1106b00e
MS
3122 return;
3123 }
3124
3125 /*
3126 * See if they want to limit the number of jobs reported...
3127 */
3128
3129 if ((attr = ippFindAttribute(client->request, "limit",
3130 IPP_TAG_INTEGER)) != NULL)
3131 {
a469f8a5 3132 limit = ippGetInteger(attr, 0);
1106b00e 3133
a469f8a5 3134 fprintf(stderr, "%s Get-Jobs limit=%d", client->hostname, limit);
1106b00e
MS
3135 }
3136 else
3137 limit = 0;
3138
3139 if ((attr = ippFindAttribute(client->request, "first-job-id",
3140 IPP_TAG_INTEGER)) != NULL)
3141 {
a469f8a5 3142 first_job_id = ippGetInteger(attr, 0);
1106b00e 3143
6d56631f 3144 fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->hostname, first_job_id);
1106b00e
MS
3145 }
3146 else
3147 first_job_id = 1;
3148
3149 /*
3150 * See if we only want to see jobs for a specific user...
3151 */
3152
3153 username = NULL;
3154
3155 if ((attr = ippFindAttribute(client->request, "my-jobs",
3156 IPP_TAG_BOOLEAN)) != NULL)
3157 {
a469f8a5
MS
3158 int my_jobs = ippGetBoolean(attr, 0);
3159
6d56631f 3160 fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->hostname, my_jobs ? "true" : "false");
1106b00e 3161
a469f8a5 3162 if (my_jobs)
1106b00e
MS
3163 {
3164 if ((attr = ippFindAttribute(client->request, "requesting-user-name",
3165 IPP_TAG_NAME)) == NULL)
3166 {
a469f8a5 3167 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
1106b00e
MS
3168 "Need requesting-user-name with my-jobs.");
3169 return;
3170 }
3171
a469f8a5 3172 username = ippGetString(attr, 0, NULL);
1106b00e 3173
6d56631f 3174 fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n", client->hostname, username);
1106b00e
MS
3175 }
3176 }
3177
3178 /*
3179 * OK, build a list of jobs for this printer...
3180 */
3181
db8b865d 3182 ra = ippCreateRequestedArray(client->request);
1106b00e 3183
a469f8a5 3184 respond_ipp(client, IPP_STATUS_OK, NULL);
1106b00e
MS
3185
3186 _cupsRWLockRead(&(client->printer->rwlock));
3187
d46dbe1b 3188 for (count = 0, job = (ippeve_job_t *)cupsArrayFirst(client->printer->jobs);
1106b00e 3189 (limit <= 0 || count < limit) && job;
d46dbe1b 3190 job = (ippeve_job_t *)cupsArrayNext(client->printer->jobs))
1106b00e
MS
3191 {
3192 /*
3193 * Filter out jobs that don't match...
3194 */
3195
3196 if ((job_comparison < 0 && job->state > job_state) ||
3197 (job_comparison == 0 && job->state != job_state) ||
3198 (job_comparison > 0 && job->state < job_state) ||
3199 job->id < first_job_id ||
a469f8a5 3200 (username && job->username &&
2cadf0f4 3201 strcasecmp(username, job->username)))
1106b00e
MS
3202 continue;
3203
3204 if (count > 0)
3205 ippAddSeparator(client->response);
3206
3207 count ++;
3208 copy_job_attributes(client, job, ra);
3209 }
3210
3211 cupsArrayDelete(ra);
3212
3213 _cupsRWUnlock(&(client->printer->rwlock));
3214}
3215
3216
3217/*
3218 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3219 */
3220
3221static void
3222ipp_get_printer_attributes(
d46dbe1b 3223 ippeve_client_t *client) /* I - Client */
1106b00e
MS
3224{
3225 cups_array_t *ra; /* Requested attributes array */
d46dbe1b 3226 ippeve_printer_t *printer; /* Printer */
1106b00e
MS
3227
3228
3229 /*
3230 * Send the attributes...
3231 */
3232
db8b865d 3233 ra = ippCreateRequestedArray(client->request);
1106b00e
MS
3234 printer = client->printer;
3235
a469f8a5 3236 respond_ipp(client, IPP_STATUS_OK, NULL);
1106b00e
MS
3237
3238 _cupsRWLockRead(&(printer->rwlock));
3239
3240 copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
a469f8a5 3241 IPP_TAG_CUPS_CONST);
1106b00e 3242
4a838088
MS
3243 if (!ra || cupsArrayFind(ra, "printer-config-change-date-time"))
3244 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-config-change-date-time", ippTimeToDate(printer->config_time));
3245
3246 if (!ra || cupsArrayFind(ra, "printer-config-change-time"))
3247 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-config-change-time", (int)(printer->config_time - printer->start_time));
3248
3249 if (!ra || cupsArrayFind(ra, "printer-current-time"))
3250 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(time(NULL)));
3251
3252
1106b00e 3253 if (!ra || cupsArrayFind(ra, "printer-state"))
400e67c1 3254 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state);
1106b00e 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{
ab8fab61
MS
4506 int i, j; /* Looping vars */
4507 ipp_t *attrs; /* Attributes */
4508 ipp_attribute_t *attr; /* Current attribute */
4509 ipp_t *col; /* Current collection value */
4510 ppd_file_t *ppd; /* PPD data */
4511 ppd_attr_t *ppd_attr; /* PPD attribute */
4512 ppd_size_t *ppd_size; /* Default PPD size */
4513 pwg_size_t *pwg_size, /* Current PWG size */
4514 *default_size = NULL; /* Default PWG size */
4515 pwg_map_t *pwg_map; /* Mapping from PWG to PPD keywords */
4516 _ppd_cache_t *pc; /* PPD cache */
4517 _pwg_finishings_t *finishings; /* Current finishings value */
4518 const char *template; /* Current finishings-template value */
4519 int num_margins; /* Number of media-xxx-margin-supported values */
4520 int margins[10]; /* media-xxx-margin-supported values */
4521 int xres, /* Default horizontal resolution */
4522 yres; /* Default vertical resolution */
4523 int num_urf; /* Number of urf-supported values */
4524 const char *urf[10]; /* urf-supported values */
4525 char urf_rs[32]; /* RS value */
4526 static const int orientation_requested_supported[4] =
4527 { /* orientation-requested-supported values */
4528 IPP_ORIENT_PORTRAIT,
4529 IPP_ORIENT_LANDSCAPE,
4530 IPP_ORIENT_REVERSE_LANDSCAPE,
4531 IPP_ORIENT_REVERSE_PORTRAIT
4532 };
4533 static const char * const overrides_supported[] =
1562b9a1 4534 { /* overrides-supported */
ab8fab61
MS
4535 "document-numbers",
4536 "media",
4537 "media-col",
4538 "orientation-requested",
1562b9a1
MS
4539 "pages"
4540 };
ab8fab61
MS
4541 static const char * const print_color_mode_supported[] =
4542 { /* print-color-mode-supported values */
4543 "monochrome"
4544 };
4545 static const char * const print_color_mode_supported_color[] =
4546 { /* print-color-mode-supported values */
4547 "auto",
4548 "color",
4549 "monochrome"
4550 };
4551 static const int print_quality_supported[] =
4552 { /* print-quality-supported values */
4553 IPP_QUALITY_DRAFT,
4554 IPP_QUALITY_NORMAL,
4555 IPP_QUALITY_HIGH
4556 };
4557 static const char * const printer_supply[] =
4558 { /* printer-supply values */
4559 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4560 "maxcapacity=100;level=25;colorantname=unknown;",
4561 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4562 "maxcapacity=100;level=75;colorantname=black;"
4563 };
4564 static const char * const printer_supply_color[] =
4565 { /* printer-supply values */
4566 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4567 "maxcapacity=100;level=25;colorantname=unknown;",
4568 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4569 "maxcapacity=100;level=75;colorantname=black;",
4570 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4571 "maxcapacity=100;level=50;colorantname=cyan;",
4572 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4573 "maxcapacity=100;level=33;colorantname=magenta;",
4574 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4575 "maxcapacity=100;level=67;colorantname=yellow;"
4576 };
4577 static const char * const printer_supply_description[] =
4578 { /* printer-supply-description values */
4579 "Toner Waste Tank",
4580 "Black Toner"
4581 };
4582 static const char * const printer_supply_description_color[] =
4583 { /* printer-supply-description values */
4584 "Ink Waste Tank",
4585 "Black Ink",
4586 "Cyan Ink",
4587 "Magenta Ink",
4588 "Yellow Ink"
4589 };
4590 static const char * const pwg_raster_document_type_supported[] =
4591 {
4592 "black_1",
4593 "sgray_8"
4594 };
4595 static const char * const pwg_raster_document_type_supported_color[] =
4596 {
4597 "black_1",
4598 "sgray_8",
4599 "srgb_8",
4600 "srgb_16"
4601 };
4602 static const char * const sides_supported[] =
4603 { /* sides-supported values */
4604 "one-sided",
4605 "two-sided-long-edge",
4606 "two-sided-short-edge"
4607 };
4608
4609
4610 /*
4611 * Open the PPD file...
4612 */
4613
4614 if ((ppd = ppdOpenFile(ppdfile)) == NULL)
4615 {
4616 ppd_status_t status; /* Load error */
4617
4618 status = ppdLastError(&i);
4619 _cupsLangPrintf(stderr, _("ippeveprinter: Unable to open \"%s\": %s on line %d."), ppdfile, ppdErrorString(status), i);
4620 return (NULL);
4621 }
4622
4623 ppdMarkDefaults(ppd);
4624
4625 pc = _ppdCacheCreateWithPPD(ppd);
4626
4627 if ((ppd_size = ppdPageSize(ppd, NULL)) != NULL)
4628 {
4629 /*
4630 * Look up default size...
4631 */
4632
4633 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
4634 {
4635 if (!strcmp(pwg_size->map.ppd, ppd_size->name))
4636 {
4637 default_size = pwg_size;
4638 break;
4639 }
4640 }
4641 }
4642
4643 if (!default_size)
4644 {
4645 /*
4646 * Default to A4 or Letter...
4647 */
4648
4649 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
4650 {
4651 if (!strcmp(pwg_size->map.ppd, "Letter") || !strcmp(pwg_size->map.ppd, "A4"))
4652 {
4653 default_size = pwg_size;
4654 break;
4655 }
4656 }
4657
4658 if (!default_size)
4659 default_size = pc->sizes; /* Last resort: first size */
4660 }
4661
4662 if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL)
4663 {
4664 /*
4665 * Use the PPD-defined default resolution...
4666 */
4667
4668 if ((i = sscanf(ppd_attr->value, "%dx%d", &xres, &yres)) == 1)
4669 yres = xres;
4670 else if (i < 0)
4671 xres = yres = 300;
4672 }
4673 else
4674 {
4675 /*
4676 * Use default of 300dpi...
4677 */
4678
4679 xres = yres = 300;
4680 }
4681
4682 snprintf(urf_rs, sizeof(urf_rs), "RS%d", yres < xres ? yres : xres);
4683
4684 num_urf = 0;
4685 urf[num_urf ++] = "V1.4";
4686 urf[num_urf ++] = "CP1";
4687 urf[num_urf ++] = urf_rs;
4688 urf[num_urf ++] = "W8";
4689 if (pc->sides_2sided_long)
4690 urf[num_urf ++] = "DM1";
4691 if (ppd->color_device)
4692 urf[num_urf ++] = "SRGB24";
4693
4694 /*
4695 * PostScript printers accept PDF via one of the CUPS PDF to PostScript
7331c1b6 4696 * filters, along with PostScript (of course)...
ab8fab61
MS
4697 */
4698
4699 cupsArrayAdd(docformats, "application/pdf");
7331c1b6 4700 cupsArrayAdd(docformats, "application/postscript");
ab8fab61
MS
4701
4702 /*
4703 * Create the attributes...
4704 */
4705
4706 attrs = ippNew();
4707
4708 /* color-supported */
4709 ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", (char)ppd->color_device);
4710
4711 /* copies-default */
4712 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
4713
4714 /* copies-supported */
4715 ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999);
1562b9a1
MS
4716
4717 /* document-password-supported */
ab8fab61
MS
4718 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 127);
4719
4720 /* finishing-template-supported */
4721 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template-supported", cupsArrayCount(pc->templates) + 1, NULL, NULL);
4722 ippSetString(attrs, &attr, 0, "none");
4723 for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
4724 ippSetString(attrs, &attr, i, template);
4725
4726 /* finishings-col-database */
4727 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-database", cupsArrayCount(pc->templates) + 1, NULL);
4728
4729 col = ippNew();
4730 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
4731 ippSetCollection(attrs, &attr, 0, col);
4732 ippDelete(col);
4733
4734 for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
4735 {
4736 col = ippNew();
4737 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template);
4738 ippSetCollection(attrs, &attr, i, col);
4739 ippDelete(col);
4740 }
4741
4742 /* finishings-col-default */
4743 col = ippNew();
4744 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
4745 ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-default", col);
4746 ippDelete(col);
4747
4748 /* finishings-col-ready */
4749 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-ready", cupsArrayCount(pc->templates) + 1, NULL);
4750
4751 col = ippNew();
4752 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
4753 ippSetCollection(attrs, &attr, 0, col);
4754 ippDelete(col);
4755
4756 for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
4757 {
4758 col = ippNew();
4759 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template);
4760 ippSetCollection(attrs, &attr, i, col);
4761 ippDelete(col);
4762 }
4763
4764 /* finishings-col-supported */
4765 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishings-col-supported", NULL, "finishing-template");
4766
4767 /* finishings-default */
4768 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
4769
4770 /* finishings-ready */
4771 attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-ready", cupsArrayCount(pc->finishings) + 1, NULL);
4772 ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
4773 for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
4774 ippSetInteger(attrs, &attr, i, finishings->value);
4775
4776 /* finishings-supported */
4777 attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", cupsArrayCount(pc->finishings) + 1, NULL);
4778 ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
4779 for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
4780 ippSetInteger(attrs, &attr, i, finishings->value);
4781
4782 /* media-bottom-margin-supported */
4783 for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
4784 {
4785 for (j = 0; j < num_margins; j ++)
4786 {
4787 if (margins[j] == pwg_size->bottom)
4788 break;
4789 }
4790
4791 if (j >= num_margins)
4792 margins[num_margins ++] = pwg_size->bottom;
4793 }
4794
4795 for (i = 0; i < (num_margins - 1); i ++)
4796 {
4797 for (j = i + 1; j < num_margins; j ++)
4798 {
4799 if (margins[i] > margins[j])
4800 {
4801 int mtemp = margins[i];
4802
4803 margins[i] = margins[j];
4804 margins[j] = mtemp;
4805 }
4806 }
4807 }
4808
4809 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", num_margins, margins);
4810
4811 /* media-col-database */
4812 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", pc->num_sizes, NULL);
4813 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
4814 {
4815 col = create_media_col(pwg_size->map.pwg, NULL, NULL, pwg_size->width, pwg_size->length, pwg_size->bottom, pwg_size->left, pwg_size->right, pwg_size->top);
4816 ippSetCollection(attrs, &attr, i, col);
4817 ippDelete(col);
4818 }
4819
4820 /* media-col-default */
4821 col = create_media_col(default_size->map.pwg, NULL, NULL, default_size->width, default_size->length, default_size->bottom, default_size->left, default_size->right, default_size->top);
4822 ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col);
4823 ippDelete(col);
4824
4825 /* media-col-ready */
4826 col = create_media_col(default_size->map.pwg, pc->num_sources > 0 ? pc->sources[0].pwg : NULL, pc->num_types > 0 ? pc->types[0].pwg : NULL, default_size->width, default_size->length, default_size->bottom, default_size->left, default_size->right, default_size->top);
4827 ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-ready", col);
4828 ippDelete(col);
4829
4830 /* media-default */
4831 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default", NULL, default_size->map.pwg);
4832
4833 /* media-left-margin-supported */
4834 for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
4835 {
4836 for (j = 0; j < num_margins; j ++)
4837 {
4838 if (margins[j] == pwg_size->left)
4839 break;
4840 }
4841
4842 if (j >= num_margins)
4843 margins[num_margins ++] = pwg_size->left;
4844 }
4845
4846 for (i = 0; i < (num_margins - 1); i ++)
4847 {
4848 for (j = i + 1; j < num_margins; j ++)
4849 {
4850 if (margins[i] > margins[j])
4851 {
4852 int mtemp = margins[i];
4853
4854 margins[i] = margins[j];
4855 margins[j] = mtemp;
4856 }
4857 }
4858 }
4859
4860 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", num_margins, margins);
4861
4862 /* media-ready */
4863 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, default_size->map.pwg);
4864
4865 /* media-right-margin-supported */
4866 for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
4867 {
4868 for (j = 0; j < num_margins; j ++)
4869 {
4870 if (margins[j] == pwg_size->right)
4871 break;
4872 }
4873
4874 if (j >= num_margins)
4875 margins[num_margins ++] = pwg_size->right;
4876 }
4877
4878 for (i = 0; i < (num_margins - 1); i ++)
4879 {
4880 for (j = i + 1; j < num_margins; j ++)
4881 {
4882 if (margins[i] > margins[j])
4883 {
4884 int mtemp = margins[i];
4885
4886 margins[i] = margins[j];
4887 margins[j] = mtemp;
4888 }
4889 }
4890 }
4891
4892 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", num_margins, margins);
4893
4894 /* media-supported */
4895 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-supported", pc->num_sizes, NULL, NULL);
4896 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
4897 ippSetString(attrs, &attr, i, pwg_size->map.pwg);
4898
4899 /* media-size-supported */
4900 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", pc->num_sizes, NULL);
4901 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
4902 {
4903 col = create_media_size(pwg_size->width, pwg_size->length);
4904 ippSetCollection(attrs, &attr, i, col);
4905 ippDelete(col);
4906 }
4907
4908 /* media-source-supported */
4909 if (pc->num_sources > 0)
4910 {
4911 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source-supported", pc->num_sources, NULL, NULL);
4912 for (i = 0, pwg_map = pc->sources; i < pc->num_sources; i ++, pwg_map ++)
4913 ippSetString(attrs, &attr, i, pwg_map->pwg);
4914 }
4915 else
4916 {
4917 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", NULL, "auto");
4918 }
4919
4920 /* media-top-margin-supported */
4921 for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
4922 {
4923 for (j = 0; j < num_margins; j ++)
4924 {
4925 if (margins[j] == pwg_size->top)
4926 break;
4927 }
4928
4929 if (j >= num_margins)
4930 margins[num_margins ++] = pwg_size->top;
4931 }
4932
4933 for (i = 0; i < (num_margins - 1); i ++)
4934 {
4935 for (j = i + 1; j < num_margins; j ++)
4936 {
4937 if (margins[i] > margins[j])
4938 {
4939 int mtemp = margins[i];
4940
4941 margins[i] = margins[j];
4942 margins[j] = mtemp;
4943 }
4944 }
4945 }
4946
4947 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", num_margins, margins);
4948
4949 /* media-type-supported */
4950 if (pc->num_types > 0)
4951 {
4952 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type-supported", pc->num_types, NULL, NULL);
4953 for (i = 0, pwg_map = pc->types; i < pc->num_types; i ++, pwg_map ++)
4954 ippSetString(attrs, &attr, i, pwg_map->pwg);
4955 }
4956 else
4957 {
4958 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", NULL, "auto");
4959 }
4960
4961 /* orientation-requested-default */
4962 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", IPP_ORIENT_PORTRAIT);
4963
4964 /* orientation-requested-supported */
4965 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported);
4966
4967 /* output-bin-default */
4968 if (pc->num_bins > 0)
4969 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-default", NULL, pc->bins->pwg);
4970 else
4971 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
4972
4973 /* output-bin-supported */
4974 if (pc->num_bins > 0)
4975 {
4976 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-supported", pc->num_bins, NULL, NULL);
4977 for (i = 0, pwg_map = pc->bins; i < pc->num_bins; i ++, pwg_map ++)
4978 ippSetString(attrs, &attr, i, pwg_map->pwg);
4979 }
4980 else
4981 {
4982 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
4983 }
1562b9a1
MS
4984
4985 /* overrides-supported */
ab8fab61 4986 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "overrides-supported", (int)(sizeof(overrides_supported) / sizeof(overrides_supported[0])), NULL, overrides_supported);
1562b9a1
MS
4987
4988 /* page-ranges-supported */
ab8fab61
MS
4989 ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1);
4990
1562b9a1 4991 /* pages-per-minute */
ab8fab61 4992 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppd->throughput);
1562b9a1
MS
4993
4994 /* pages-per-minute-color */
ab8fab61
MS
4995 if (ppd->color_device)
4996 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppd->throughput);
4997
4998 /* print-color-mode-default */
4999 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, ppd->color_device ? "auto" : "monochrome");
5000
5001 /* print-color-mode-supported */
5002 if (ppd->color_device)
5003 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);
5004 else
5005 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);
5006
5007 /* print-content-optimize-default */
5008 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
5009
5010 /* print-content-optimize-supported */
5011 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
5012
5013 /* print-quality-default */
5014 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
5015
5016 /* print-quality-supported */
5017 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
5018
5019 /* print-rendering-intent-default */
5020 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
5021
5022 /* print-rendering-intent-supported */
5023 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
5024
5025 /* printer-device-id */
5026 if ((ppd_attr = ppdFindAttr(ppd, "1284DeviceId", NULL)) != NULL)
5027 {
5028 /*
5029 * Use the device ID string from the PPD...
5030 */
5031
5032 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, ppd_attr->value);
5033 }
5034 else
5035 {
5036 /*
5037 * Synthesize a device ID string...
5038 */
5039
5040 char device_id[1024]; /* Device ID string */
5041
5042 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;CMD:PS;", ppd->manufacturer, ppd->modelname);
5043
5044 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id);
5045 }
5046
5047 /* printer-input-tray */
5048#if 0
1562b9a1 5049 if (ppm_color > 0)
ab8fab61
MS
5050 {
5051 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray_color[0], strlen(printer_input_tray_color[0]));
5052 for (i = 1; i < (int)(sizeof(printer_input_tray_color) / sizeof(printer_input_tray_color[0])); i ++)
5053 ippSetOctetString(attrs, &attr, i, printer_input_tray_color[i], strlen(printer_input_tray_color[i]));
5054 }
5055 else
5056 {
5057 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray[0], strlen(printer_input_tray[0]));
5058 for (i = 1; i < (int)(sizeof(printer_input_tray) / sizeof(printer_input_tray[0])); i ++)
5059 ippSetOctetString(attrs, &attr, i, printer_input_tray[i], strlen(printer_input_tray[i]));
5060 }
1562b9a1
MS
5061#endif /* 0 */
5062
ab8fab61
MS
5063 /* printer-make-and-model */
5064 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, ppd->nickname);
5065
5066 /* printer-resolution-default */
5067 ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, xres, yres);
5068
5069 /* printer-resolution-supported */
5070 ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, xres, yres);
5071
5072 /* printer-supply and printer-supply-description */
5073 if (ppd->color_device)
5074 {
5075 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], strlen(printer_supply_color[0]));
5076 for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++)
5077 ippSetOctetString(attrs, &attr, i, printer_supply_color[i], strlen(printer_supply_color[i]));
5078
5079 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);
5080 }
5081 else
5082 {
5083 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], strlen(printer_supply[0]));
5084 for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++)
5085 ippSetOctetString(attrs, &attr, i, printer_supply[i], strlen(printer_supply[i]));
5086
5087 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);
5088 }
5089
5090 /* pwg-raster-document-xxx-supported */
5091 if (cupsArrayFind(docformats, (void *)"image/pwg-raster"))
5092 {
5093 ippAddResolution(attrs, IPP_TAG_PRINTER, "pwg-raster-document-resolution-supported", IPP_RES_PER_INCH, xres, yres);
5094
5095 if (pc->sides_2sided_long)
5096 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal");
5097
5098 if (ppd->color_device)
5099 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);
5100 else
5101 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);
5102 }
5103
5104 /* sides-default */
5105 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
5106
5107 /* sides-supported */
5108 if (pc->sides_2sided_long)
5109 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported);
5110 else
5111 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided");
5112
5113 /* urf-supported */
5114 if (cupsArrayFind(docformats, (void *)"image/urf"))
5115 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", num_urf, NULL, urf);
5116
5117 /*
5118 * Free the PPD file and return the attributes...
5119 */
5120
5121 _ppdCacheDestroy(pc);
5122
5123 ppdClose(ppd);
5124
5125 return (attrs);
f8927099 5126}
6641bd0d 5127#endif /* !CUPS_LITE */
f8927099
MS
5128
5129
0b5ce83f
MS
5130/*
5131 * 'parse_options()' - Parse URL options into CUPS options.
5132 *
5133 * The client->options string is destroyed by this function.
5134 */
5135
5136static int /* O - Number of options */
d46dbe1b 5137parse_options(ippeve_client_t *client, /* I - Client */
92dad945 5138 cups_option_t **options)/* O - Options */
0b5ce83f
MS
5139{
5140 char *name, /* Name */
5141 *value, /* Value */
5142 *next; /* Next name=value pair */
5143 int num_options = 0; /* Number of options */
5144
5145
5146 *options = NULL;
5147
5148 for (name = client->options; name && *name; name = next)
5149 {
5150 if ((value = strchr(name, '=')) == NULL)
5151 break;
5152
5153 *value++ = '\0';
5154 if ((next = strchr(value, '&')) != NULL)
5155 *next++ = '\0';
5156
5157 num_options = cupsAddOption(name, value, num_options, options);
5158 }
5159
5160 return (num_options);
5161}
5162
5163
9610a474
MS
5164/*
5165 * 'process_attr_message()' - Process an ATTR: message from a command.
5166 */
5167
5168static void
5169process_attr_message(
d46dbe1b 5170 ippeve_job_t *job, /* I - Job */
9610a474
MS
5171 char *message) /* I - Message */
5172{
5173 (void)job;
5174 (void)message;
5175}
5176
5177
83e08001
MS
5178/*
5179 * 'process_client()' - Process client requests on a thread.
5180 */
5181
5182static void * /* O - Exit status */
d46dbe1b 5183process_client(ippeve_client_t *client) /* I - Client */
83e08001
MS
5184{
5185 /*
5186 * Loop until we are out of requests or timeout (30 seconds)...
5187 */
5188
f93b32b6
MS
5189#ifdef HAVE_SSL
5190 int first_time = 1; /* First time request? */
5191#endif /* HAVE_SSL */
5192
a469f8a5 5193 while (httpWait(client->http, 30000))
f93b32b6
MS
5194 {
5195#ifdef HAVE_SSL
5196 if (first_time)
5197 {
5198 /*
5199 * See if we need to negotiate a TLS connection...
5200 */
5201
5202 char buf[1]; /* First byte from client */
5203
5204 if (recv(httpGetFd(client->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0])))
5205 {
fe202ff4 5206 fprintf(stderr, "%s Starting HTTPS session.\n", client->hostname);
f93b32b6
MS
5207
5208 if (httpEncryption(client->http, HTTP_ENCRYPTION_ALWAYS))
5209 {
5210 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
5211 break;
5212 }
fe202ff4
MS
5213
5214 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
f93b32b6
MS
5215 }
5216
5217 first_time = 0;
5218 }
5219#endif /* HAVE_SSL */
5220
83e08001
MS
5221 if (!process_http(client))
5222 break;
f93b32b6 5223 }
83e08001
MS
5224
5225 /*
5226 * Close the conection to the client and return...
5227 */
5228
5229 delete_client(client);
5230
5231 return (NULL);
5232}
5233
5234
5235/*
5236 * 'process_http()' - Process a HTTP request.
5237 */
5238
5239int /* O - 1 on success, 0 on failure */
d46dbe1b 5240process_http(ippeve_client_t *client) /* I - Client connection */
83e08001 5241{
a469f8a5
MS
5242 char uri[1024]; /* URI */
5243 http_state_t http_state; /* HTTP state */
5244 http_status_t http_status; /* HTTP status */
5245 ipp_state_t ipp_state; /* State of IPP transfer */
5246 char scheme[32], /* Method/scheme */
5247 userpass[128], /* Username:password */
5248 hostname[HTTP_MAX_HOST];
5249 /* Hostname */
5250 int port; /* Port number */
a469f8a5
MS
5251 static const char * const http_states[] =
5252 { /* Strings for logging HTTP method */
5253 "WAITING",
5254 "OPTIONS",
5255 "GET",
5256 "GET_SEND",
5257 "HEAD",
5258 "POST",
5259 "POST_RECV",
5260 "POST_SEND",
5261 "PUT",
5262 "PUT_RECV",
5263 "DELETE",
5264 "TRACE",
5265 "CONNECT",
5266 "STATUS",
5267 "UNKNOWN_METHOD",
5268 "UNKNOWN_VERSION"
5269 };
83e08001 5270
83e08001
MS
5271
5272 /*
5273 * Clear state variables...
5274 */
5275
83e08001
MS
5276 ippDelete(client->request);
5277 ippDelete(client->response);
5278
a469f8a5
MS
5279 client->request = NULL;
5280 client->response = NULL;
5281 client->operation = HTTP_STATE_WAITING;
83e08001
MS
5282
5283 /*
5284 * Read a request from the connection...
5285 */
5286
a469f8a5
MS
5287 while ((http_state = httpReadRequest(client->http, uri,
5288 sizeof(uri))) == HTTP_STATE_WAITING)
5289 usleep(1);
83e08001
MS
5290
5291 /*
5292 * Parse the request line...
5293 */
5294
a469f8a5 5295 if (http_state == HTTP_STATE_ERROR)
83e08001 5296 {
a469f8a5
MS
5297 if (httpError(client->http) == EPIPE)
5298 fprintf(stderr, "%s Client closed connection.\n", client->hostname);
5299 else
6d56631f 5300 fprintf(stderr, "%s Bad request line (%s).\n", client->hostname, strerror(httpError(client->http)));
83e08001 5301
a469f8a5 5302 return (0);
83e08001 5303 }
a469f8a5 5304 else if (http_state == HTTP_STATE_UNKNOWN_METHOD)
83e08001 5305 {
a469f8a5
MS
5306 fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname);
5307 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5308 return (0);
1106b00e 5309 }
a469f8a5 5310 else if (http_state == HTTP_STATE_UNKNOWN_VERSION)
1106b00e 5311 {
a469f8a5
MS
5312 fprintf(stderr, "%s Bad HTTP version.\n", client->hostname);
5313 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5314 return (0);
1106b00e
MS
5315 }
5316
6d56631f 5317 fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state], uri);
a469f8a5 5318
1106b00e 5319 /*
a469f8a5 5320 * Separate the URI into its components...
1106b00e
MS
5321 */
5322
a469f8a5
MS
5323 if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
5324 userpass, sizeof(userpass),
5325 hostname, sizeof(hostname), &port,
fe202ff4
MS
5326 client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK &&
5327 (http_state != HTTP_STATE_OPTIONS || strcmp(uri, "*")))
1106b00e 5328 {
a469f8a5
MS
5329 fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri);
5330 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
5331 return (0);
5332 }
5333
0b5ce83f
MS
5334 if ((client->options = strchr(client->uri, '?')) != NULL)
5335 *(client->options)++ = '\0';
5336
a469f8a5
MS
5337 /*
5338 * Process the request...
5339 */
5340
5341 client->start = time(NULL);
5342 client->operation = httpGetState(client->http);
1106b00e
MS
5343
5344 /*
5345 * Parse incoming parameters until the status changes...
5346 */
5347
a469f8a5 5348 while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE);
1106b00e 5349
a469f8a5 5350 if (http_status != HTTP_STATUS_OK)
1106b00e 5351 {
a469f8a5 5352 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
5353 return (0);
5354 }
5355
a469f8a5
MS
5356 if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] &&
5357 httpGetVersion(client->http) >= HTTP_VERSION_1_1)
1106b00e
MS
5358 {
5359 /*
5360 * HTTP/1.1 and higher require the "Host:" field...
5361 */
5362
a469f8a5 5363 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
5364 return (0);
5365 }
5366
5367 /*
5368 * Handle HTTP Upgrade...
5369 */
5370
2cadf0f4 5371 if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION),
a469f8a5 5372 "Upgrade"))
1106b00e 5373 {
fe202ff4
MS
5374#ifdef HAVE_SSL
5375 if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http))
5376 {
5377 if (!respond_http(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0))
5378 return (0);
5379
5380 fprintf(stderr, "%s Upgrading to encrypted connection.\n", client->hostname);
5381
5382 if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED))
5383 {
5384 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
5385 return (0);
5386 }
5387
5388 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
5389 }
5390 else
5391#endif /* HAVE_SSL */
5392
a469f8a5 5393 if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
1106b00e
MS
5394 return (0);
5395 }
5396
e60ec91f
MS
5397 /*
5398 * Handle HTTP Expect...
5399 */
5400
a469f8a5
MS
5401 if (httpGetExpect(client->http) &&
5402 (client->operation == HTTP_STATE_POST ||
5403 client->operation == HTTP_STATE_PUT))
e60ec91f 5404 {
a469f8a5 5405 if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
e60ec91f
MS
5406 {
5407 /*
5408 * Send 100-continue header...
5409 */
5410
a469f8a5 5411 if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
e60ec91f
MS
5412 return (0);
5413 }
5414 else
5415 {
5416 /*
5417 * Send 417-expectation-failed header...
5418 */
5419
a469f8a5 5420 if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
e60ec91f 5421 return (0);
e60ec91f
MS
5422 }
5423 }
5424
1106b00e
MS
5425 /*
5426 * Handle new transfers...
5427 */
5428
5429 switch (client->operation)
5430 {
a469f8a5 5431 case HTTP_STATE_OPTIONS :
1106b00e 5432 /*
fe202ff4 5433 * Do OPTIONS command...
1106b00e
MS
5434 */
5435
a469f8a5 5436 return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
1106b00e 5437
a469f8a5 5438 case HTTP_STATE_HEAD :
1106b00e 5439 if (!strcmp(client->uri, "/icon.png"))
a469f8a5 5440 return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
0b5ce83f 5441 else if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies"))
a469f8a5 5442 return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
1106b00e 5443 else
a469f8a5 5444 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e 5445
a469f8a5 5446 case HTTP_STATE_GET :
1106b00e
MS
5447 if (!strcmp(client->uri, "/icon.png"))
5448 {
5449 /*
5450 * Send PNG icon file.
5451 */
5452
3e5092db
MS
5453 if (client->printer->icon)
5454 {
5455 int fd; /* Icon file */
5456 struct stat fileinfo; /* Icon file information */
5457 char buffer[4096]; /* Copy buffer */
5458 ssize_t bytes; /* Bytes */
1106b00e 5459
3e5092db 5460 fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon);
a469f8a5 5461
3e5092db 5462 if (!stat(client->printer->icon, &fileinfo) && (fd = open(client->printer->icon, O_RDONLY)) >= 0)
1106b00e 5463 {
3e5092db
MS
5464 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
5465 {
5466 close(fd);
5467 return (0);
5468 }
5469
5470 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
5471 httpWrite2(client->http, buffer, (size_t)bytes);
5472
5473 httpFlushWrite(client->http);
5474
1106b00e 5475 close(fd);
1106b00e 5476 }
3e5092db
MS
5477 else
5478 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
5479 }
5480 else
5481 {
5482 fputs("Icon file is internal printer.png.\n", stderr);
1106b00e 5483
3e5092db
MS
5484 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_png)))
5485 return (0);
1106b00e 5486
3e5092db 5487 httpWrite2(client->http, (const char *)printer_png, sizeof(printer_png));
a469f8a5 5488 httpFlushWrite(client->http);
1106b00e 5489 }
1106b00e
MS
5490 }
5491 else if (!strcmp(client->uri, "/"))
5492 {
5493 /*
5494 * Show web status page...
5495 */
5496
9141aa01 5497 return (show_status(client));
0b5ce83f
MS
5498 }
5499 else if (!strcmp(client->uri, "/media"))
5500 {
5501 /*
5502 * Show web media page...
5503 */
5504
9141aa01 5505 return (show_media(client));
0b5ce83f
MS
5506 }
5507 else if (!strcmp(client->uri, "/supplies"))
5508 {
5509 /*
5510 * Show web supplies page...
5511 */
5512
9141aa01 5513 return (show_supplies(client));
1106b00e
MS
5514 }
5515 else
a469f8a5 5516 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e
MS
5517 break;
5518
a469f8a5
MS
5519 case HTTP_STATE_POST :
5520 if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
1106b00e
MS
5521 "application/ipp"))
5522 {
5523 /*
5524 * Not an IPP request...
5525 */
5526
a469f8a5 5527 return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
1106b00e
MS
5528 }
5529
5530 /*
5531 * Read the IPP request...
5532 */
5533
5534 client->request = ippNew();
5535
a469f8a5
MS
5536 while ((ipp_state = ippRead(client->http,
5537 client->request)) != IPP_STATE_DATA)
5538 {
5539 if (ipp_state == IPP_STATE_ERROR)
1106b00e 5540 {
6d56631f 5541 fprintf(stderr, "%s IPP read error (%s).\n", client->hostname, cupsLastErrorString());
a469f8a5 5542 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
5543 return (0);
5544 }
a469f8a5 5545 }
1106b00e
MS
5546
5547 /*
5548 * Now that we have the IPP request, process the request...
5549 */
5550
5551 return (process_ipp(client));
5552
5553 default :
5554 break; /* Anti-compiler-warning-code */
5555 }
5556
5557 return (1);
5558}
5559
5560
5561/*
5562 * 'process_ipp()' - Process an IPP request.
5563 */
5564
5565static int /* O - 1 on success, 0 on error */
d46dbe1b 5566process_ipp(ippeve_client_t *client) /* I - Client */
1106b00e
MS
5567{
5568 ipp_tag_t group; /* Current group tag */
5569 ipp_attribute_t *attr; /* Current attribute */
5570 ipp_attribute_t *charset; /* Character set attribute */
5571 ipp_attribute_t *language; /* Language attribute */
5572 ipp_attribute_t *uri; /* Printer URI attribute */
a469f8a5
MS
5573 int major, minor; /* Version number */
5574 const char *name; /* Name of attribute */
1106b00e
MS
5575
5576
83e08001 5577 debug_attributes("Request", client->request, 1);
1106b00e
MS
5578
5579 /*
5580 * First build an empty response message for this request...
5581 */
5582
a469f8a5
MS
5583 client->operation_id = ippGetOperation(client->request);
5584 client->response = ippNewResponse(client->request);
1106b00e
MS
5585
5586 /*
5587 * Then validate the request header and required attributes...
5588 */
5589
a469f8a5
MS
5590 major = ippGetVersion(client->request, &minor);
5591
5592 if (major < 1 || major > 2)
1106b00e
MS
5593 {
5594 /*
5595 * Return an error, since we only support IPP 1.x and 2.x.
5596 */
5597
b969d5af
MS
5598 respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, "Bad request version number %d.%d.", major, minor);
5599 }
5600 else if ((major * 10 + minor) > MaxVersion)
5601 {
5602 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
5603 httpFlush(client->http); /* Flush trailing (junk) data */
5604
5605 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5606 return (0);
1106b00e 5607 }
a469f8a5 5608 else if (ippGetRequestId(client->request) <= 0)
b969d5af
MS
5609 {
5610 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.", ippGetRequestId(client->request));
5611 }
a469f8a5 5612 else if (!ippFirstAttribute(client->request))
b969d5af
MS
5613 {
5614 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No attributes in request.");
5615 }
1106b00e
MS
5616 else
5617 {
5618 /*
5619 * Make sure that the attributes are provided in the correct order and
5620 * don't repeat groups...
5621 */
5622
a469f8a5
MS
5623 for (attr = ippFirstAttribute(client->request),
5624 group = ippGetGroupTag(attr);
1106b00e 5625 attr;
a469f8a5
MS
5626 attr = ippNextAttribute(client->request))
5627 {
5628 if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
1106b00e
MS
5629 {
5630 /*
5631 * Out of order; return an error...
5632 */
5633
a469f8a5
MS
5634 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5635 "Attribute groups are out of order (%x < %x).",
5636 ippGetGroupTag(attr), group);
1106b00e
MS
5637 break;
5638 }
5639 else
a469f8a5
MS
5640 group = ippGetGroupTag(attr);
5641 }
1106b00e
MS
5642
5643 if (!attr)
5644 {
5645 /*
5646 * Then make sure that the first three attributes are:
5647 *
5648 * attributes-charset
5649 * attributes-natural-language
5650 * printer-uri/job-uri
5651 */
5652
a469f8a5
MS
5653 attr = ippFirstAttribute(client->request);
5654 name = ippGetName(attr);
5655 if (attr && name && !strcmp(name, "attributes-charset") &&
5656 ippGetValueTag(attr) == IPP_TAG_CHARSET)
1106b00e
MS
5657 charset = attr;
5658 else
5659 charset = NULL;
5660
a469f8a5
MS
5661 attr = ippNextAttribute(client->request);
5662 name = ippGetName(attr);
1106b00e 5663
a469f8a5
MS
5664 if (attr && name && !strcmp(name, "attributes-natural-language") &&
5665 ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
1106b00e
MS
5666 language = attr;
5667 else
5668 language = NULL;
5669
5670 if ((attr = ippFindAttribute(client->request, "printer-uri",
5671 IPP_TAG_URI)) != NULL)
5672 uri = attr;
5673 else if ((attr = ippFindAttribute(client->request, "job-uri",
5674 IPP_TAG_URI)) != NULL)
5675 uri = attr;
5676 else
5677 uri = NULL;
5678
1106b00e 5679 if (charset &&
2cadf0f4
MS
5680 strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
5681 strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
1106b00e
MS
5682 {
5683 /*
5684 * Bad character set...
5685 */
5686
a469f8a5 5687 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
1106b00e 5688 "Unsupported character set \"%s\".",
a469f8a5 5689 ippGetString(charset, 0, NULL));
1106b00e
MS
5690 }
5691 else if (!charset || !language || !uri)
5692 {
5693 /*
5694 * Return an error, since attributes-charset,
5695 * attributes-natural-language, and printer-uri/job-uri are required
5696 * for all operations.
5697 */
5698
a469f8a5
MS
5699 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5700 "Missing required attributes.");
1106b00e 5701 }
1106b00e
MS
5702 else
5703 {
8a78aa37
MS
5704 char scheme[32], /* URI scheme */
5705 userpass[32], /* Username/password in URI */
5706 host[256], /* Host name in URI */
5707 resource[256]; /* Resource path in URI */
5708 int port; /* Port number in URI */
5709
5710 name = ippGetName(uri);
5711
5712 if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
5713 scheme, sizeof(scheme),
5714 userpass, sizeof(userpass),
5715 host, sizeof(host), &port,
5716 resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
5717 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
5718 "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
5719 else if ((!strcmp(name, "job-uri") &&
5720 strncmp(resource, "/ipp/print/", 11)) ||
5721 (!strcmp(name, "printer-uri") &&
5722 strcmp(resource, "/ipp/print")))
5723 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
5724 name, ippGetString(uri, 0, NULL));
5725 else
1106b00e
MS
5726 {
5727 /*
8a78aa37 5728 * Try processing the operation...
1106b00e
MS
5729 */
5730
8a78aa37
MS
5731 switch (ippGetOperation(client->request))
5732 {
5733 case IPP_OP_PRINT_JOB :
5734 ipp_print_job(client);
5735 break;
5736
5737 case IPP_OP_PRINT_URI :
5738 ipp_print_uri(client);
5739 break;
5740
5741 case IPP_OP_VALIDATE_JOB :
5742 ipp_validate_job(client);
5743 break;
5744
5745 case IPP_OP_CREATE_JOB :
5746 ipp_create_job(client);
5747 break;
5748
5749 case IPP_OP_SEND_DOCUMENT :
5750 ipp_send_document(client);
5751 break;
5752
5753 case IPP_OP_SEND_URI :
5754 ipp_send_uri(client);
5755 break;
5756
5757 case IPP_OP_CANCEL_JOB :
5758 ipp_cancel_job(client);
5759 break;
5760
5761 case IPP_OP_GET_JOB_ATTRIBUTES :
5762 ipp_get_job_attributes(client);
5763 break;
5764
5765 case IPP_OP_GET_JOBS :
5766 ipp_get_jobs(client);
5767 break;
5768
5769 case IPP_OP_GET_PRINTER_ATTRIBUTES :
5770 ipp_get_printer_attributes(client);
5771 break;
5772
2cadf0f4
MS
5773 case IPP_OP_CLOSE_JOB :
5774 ipp_close_job(client);
5775 break;
5776
5777 case IPP_OP_IDENTIFY_PRINTER :
5778 ipp_identify_printer(client);
5779 break;
5780
8a78aa37
MS
5781 default :
5782 respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
5783 "Operation not supported.");
5784 break;
5785 }
1106b00e
MS
5786 }
5787 }
5788 }
5789 }
5790
5791 /*
5792 * Send the HTTP header and return...
5793 */
5794
a469f8a5
MS
5795 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
5796 httpFlush(client->http); /* Flush trailing (junk) data */
1106b00e 5797
a469f8a5 5798 return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
1106b00e
MS
5799 ippLength(client->response)));
5800}
5801
5802
5803/*
5804 * 'process_job()' - Process a print job.
5805 */
5806
5807static void * /* O - Thread exit status */
d46dbe1b 5808process_job(ippeve_job_t *job) /* I - Job */
1106b00e 5809{
a469f8a5
MS
5810 job->state = IPP_JSTATE_PROCESSING;
5811 job->printer->state = IPP_PSTATE_PROCESSING;
4a838088 5812 job->processing = time(NULL);
1106b00e 5813
d46dbe1b 5814 while (job->printer->state_reasons & IPPEVE_PREASON_MEDIA_EMPTY)
0b5ce83f 5815 {
d46dbe1b 5816 job->printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
0b5ce83f
MS
5817
5818 sleep(1);
5819 }
5820
d46dbe1b 5821 job->printer->state_reasons &= (ippeve_preason_t)~IPPEVE_PREASON_MEDIA_NEEDED;
0b5ce83f 5822
db8b865d
MS
5823 if (job->printer->command)
5824 {
5825 /*
5826 * Execute a command with the job spool file and wait for it to complete...
5827 */
5828
aa2a90ce
MS
5829 int pid, /* Process ID */
5830 status; /* Exit status */
5831 time_t start, /* Start time */
5832 end; /* End time */
5833 char *myargv[3], /* Command-line arguments */
5834 *myenvp[400]; /* Environment variables */
5835 int myenvc; /* Number of environment variables */
5836 ipp_attribute_t *attr; /* Job attribute */
5837 char val[1280], /* IPP_NAME=value */
5838 *valptr; /* Pointer into string */
24a06ed3 5839#ifndef _WIN32
cc108616 5840 int mystdout = -1; /* File for stdout */
aa2a90ce
MS
5841 int mypipe[2]; /* Pipe for stderr */
5842 char line[2048], /* Line from stderr */
5843 *ptr, /* Pointer into line */
5844 *endptr; /* End of line */
5845 ssize_t bytes; /* Bytes read */
24a06ed3 5846#endif /* !_WIN32 */
db8b865d 5847
aa2a90ce 5848 fprintf(stderr, "Running command \"%s %s\".\n", job->printer->command, job->filename);
db8b865d
MS
5849 time(&start);
5850
83ce8172
MS
5851 /*
5852 * Setup the command-line arguments...
5853 */
5854
5855 myargv[0] = job->printer->command;
5856 myargv[1] = job->filename;
5857 myargv[2] = NULL;
5858
5859 /*
aa2a90ce
MS
5860 * Copy the current environment, then add environment variables for every
5861 * Job attribute and Printer -default attributes...
83ce8172
MS
5862 */
5863
5864 for (myenvc = 0; environ[myenvc] && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); myenvc ++)
5865 myenvp[myenvc] = strdup(environ[myenvc]);
5866
aa2a90ce
MS
5867 if (myenvc > (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 32))
5868 {
5869 fprintf(stderr, "Too many environment variables to process job #%d.\n", job->id);
5870 job->state = IPP_JSTATE_ABORTED;
5871 goto error;
5872 }
5873
5874 if (asprintf(myenvp + myenvc, "CONTENT_TYPE=%s", job->format) > 0)
5875 myenvc ++;
5876
5877 if (job->printer->device_uri && asprintf(myenvp + myenvc, "DEVICE_URI=%s", job->printer->device_uri) > 0)
5878 myenvc ++;
5879
6641bd0d 5880#if !CUPS_LITE
aa2a90ce
MS
5881 if (job->printer->ppdfile && asprintf(myenvp + myenvc, "PPD=%s", job->printer->ppdfile) > 0)
5882 myenvc ++;
6641bd0d 5883#endif /* !CUPS_LITE */
aa2a90ce 5884
aa2a90ce
MS
5885 for (attr = ippFirstAttribute(job->printer->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->printer->attrs))
5886 {
5887 /*
5888 * Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and
5889 * then add the value(s) from the attribute.
5890 */
5891
5892 const char *name = ippGetName(attr),
5893 /* Attribute name */
5894 *suffix = strstr(name, "-default");
5895 /* Suffix on attribute name */
5896
5897 if (!suffix || suffix[8])
5898 continue;
5899
5900 valptr = val;
5901 *valptr++ = 'I';
5902 *valptr++ = 'P';
5903 *valptr++ = 'P';
5904 *valptr++ = '_';
5905 while (*name && valptr < (val + sizeof(val) - 2))
5906 {
5907 if (*name == '-')
5908 *valptr++ = '_';
5909 else
5910 *valptr++ = (char)toupper(*name & 255);
5911
5912 name ++;
5913 }
5914 *valptr++ = '=';
5915 ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
5916
5917 myenvp[myenvc++] = strdup(val);
5918 }
5919
83ce8172
MS
5920 for (attr = ippFirstAttribute(job->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->attrs))
5921 {
5922 /*
5923 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
5924 * value(s) from the attribute.
5925 */
5926
5927 const char *name = ippGetName(attr);
aa2a90ce
MS
5928 /* Attribute name */
5929
83ce8172
MS
5930 if (!name)
5931 continue;
5932
5933 valptr = val;
5934 *valptr++ = 'I';
5935 *valptr++ = 'P';
5936 *valptr++ = 'P';
5937 *valptr++ = '_';
5938 while (*name && valptr < (val + sizeof(val) - 2))
5939 {
5940 if (*name == '-')
5941 *valptr++ = '_';
5942 else
5943 *valptr++ = (char)toupper(*name & 255);
5944
5945 name ++;
5946 }
5947 *valptr++ = '=';
5948 ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
5949
5950 myenvp[myenvc++] = strdup(val);
5951 }
aa2a90ce
MS
5952
5953 if (attr)
5954 {
5955 fprintf(stderr, "Too many environment variables to process job #%d.\n", job->id);
5956 job->state = IPP_JSTATE_ABORTED;
5957 goto error;
5958 }
5959
83ce8172
MS
5960 myenvp[myenvc] = NULL;
5961
5962 /*
5963 * Now run the program...
5964 */
5965
24a06ed3 5966#ifdef _WIN32
83ce8172 5967 status = _spawnvpe(_P_WAIT, job->printer->command, myargv, myenvp);
9610a474 5968
83ce8172 5969#else
cc108616
MS
5970 if (job->printer->device_uri)
5971 {
5972 char scheme[32], /* URI scheme */
5973 userpass[256], /* username:password (unused) */
5974 host[256], /* Hostname or IP address */
5975 resource[256]; /* Resource path */
5976 int port; /* Port number */
5977
5978
5979 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)
5980 {
5981 fprintf(stderr, "Bad device URI \"%s\".\n", job->printer->device_uri);
5982 }
5983 else if (!strcmp(scheme, "file"))
5984 {
5985 struct stat fileinfo; /* See if this is a file or directory... */
5986
5987 if (stat(resource, &fileinfo))
5988 {
5989 if (errno == ENOENT)
5990 {
dd2b6166
MS
5991 if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC, 0666)) >= 0)
5992 fprintf(stderr, "Saving print command output to \"%s\".\n", resource);
5993 else
cc108616
MS
5994 fprintf(stderr, "Unable to create \"%s\": %s\n", resource, strerror(errno));
5995 }
5996 else
5997 fprintf(stderr, "Unable to access \"%s\": %s\n", resource, strerror(errno));
5998 }
5999 else if (S_ISDIR(fileinfo.st_mode))
6000 {
dd2b6166
MS
6001 if ((mystdout = create_job_file(job, line, sizeof(line), resource, "prn")) >= 0)
6002 fprintf(stderr, "Saving print command output to \"%s\".\n", line);
6003 else
cc108616
MS
6004 fprintf(stderr, "Unable to create \"%s\": %s\n", line, strerror(errno));
6005 }
6006 else if (!S_ISREG(fileinfo.st_mode))
6007 {
dd2b6166
MS
6008 if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC, 0666)) >= 0)
6009 fprintf(stderr, "Saving print command output to \"%s\".\n", resource);
6010 else
cc108616
MS
6011 fprintf(stderr, "Unable to create \"%s\": %s\n", resource, strerror(errno));
6012 }
dd2b6166
MS
6013 else if ((mystdout = open(resource, O_WRONLY)) >= 0)
6014 fprintf(stderr, "Saving print command output to \"%s\".\n", resource);
6015 else
cc108616
MS
6016 fprintf(stderr, "Unable to open \"%s\": %s\n", resource, strerror(errno));
6017 }
6018 else if (!strcmp(scheme, "socket"))
6019 {
6020 http_addrlist_t *addrlist; /* List of addresses */
6021 char service[32]; /* Service number */
6022
6023 snprintf(service, sizeof(service), "%d", port);
6024
6025 if ((addrlist = httpAddrGetList(host, AF_UNSPEC, service)) == NULL)
6026 fprintf(stderr, "Unable to find \"%s\": %s\n", host, cupsLastErrorString());
6027 else if (!httpAddrConnect2(addrlist, &mystdout, 30000, &(job->cancel)))
6028 fprintf(stderr, "Unable to connect to \"%s\": %s\n", host, cupsLastErrorString());
6029
6030 httpAddrFreeList(addrlist);
6031 }
6032 else
6033 {
6034 fprintf(stderr, "Unsupported device URI scheme \"%s\".\n", scheme);
6035 }
6036 }
dd2b6166 6037 else if ((mystdout = create_job_file(job, line, sizeof(line), job->printer->directory, "prn")) >= 0)
cc108616
MS
6038 {
6039 fprintf(stderr, "Saving print command output to \"%s\".\n", line);
6040 }
6041
6042 if (mystdout < 0)
6043 mystdout = open("/dev/null", O_WRONLY);
6044
9610a474
MS
6045 if (pipe(mypipe))
6046 {
6047 perror("Unable to create pipe for stderr");
6048 mypipe[0] = mypipe[1] = -1;
6049 }
6050
db8b865d
MS
6051 if ((pid = fork()) == 0)
6052 {
6053 /*
6054 * Child comes here...
6055 */
6056
cc108616
MS
6057 close(1);
6058 dup2(mystdout, 1);
6059 close(mystdout);
6060
9610a474
MS
6061 close(2);
6062 dup2(mypipe[1], 2);
6063 close(mypipe[0]);
6064 close(mypipe[1]);
6065
83ce8172 6066 execve(job->printer->command, myargv, myenvp);
db8b865d
MS
6067 exit(errno);
6068 }
6069 else if (pid < 0)
6070 {
6071 /*
6072 * Unable to fork process...
6073 */
6074
6075 perror("Unable to start job processing command");
83ce8172 6076 status = -1;
1ec50c42 6077
cc108616 6078 close(mystdout);
9610a474
MS
6079 close(mypipe[0]);
6080 close(mypipe[1]);
6081
1ec50c42
MS
6082 /*
6083 * Free memory used for environment...
6084 */
6085
6086 while (myenvc > 0)
6087 free(myenvp[-- myenvc]);
db8b865d
MS
6088 }
6089 else
6090 {
83ce8172
MS
6091 /*
6092 * Free memory used for environment...
6093 */
6094
6095 while (myenvc > 0)
6096 free(myenvp[-- myenvc]);
9610a474 6097
cc108616
MS
6098 /*
6099 * Close the output file in the parent process...
6100 */
6101
6102 close(mystdout);
6103
9610a474
MS
6104 /*
6105 * If the pipe exists, read from it until EOF...
6106 */
6107
6108 if (mypipe[0] >= 0)
6109 {
6110 close(mypipe[1]);
6111
6112 endptr = line;
6113 while ((bytes = read(mypipe[0], endptr, sizeof(line) - (size_t)(endptr - line) - 1)) > 0)
6114 {
6115 endptr += bytes;
6116 *endptr = '\0';
6117
6118 while ((ptr = strchr(line, '\n')) != NULL)
6119 {
6120 *ptr++ = '\0';
6121
6122 if (!strncmp(line, "STATE:", 6))
6123 {
6124 /*
6125 * Process printer-state-reasons keywords.
6126 */
6127
6128 process_state_message(job, line);
6129 }
6130 else if (!strncmp(line, "ATTR:", 5))
6131 {
6132 /*
6133 * Process printer attribute update.
6134 */
6135
6136 process_attr_message(job, line);
6137 }
6138 else if (Verbosity > 1)
6139 fprintf(stderr, "%s: %s\n", job->printer->command, line);
6140
6141 bytes = ptr - line;
6142 if (ptr < endptr)
63efa616 6143 memmove(line, ptr, (size_t)(endptr - ptr));
9610a474
MS
6144 endptr -= bytes;
6145 *endptr = '\0';
6146 }
6147 }
6148
6149 close(mypipe[0]);
6150 }
6151
db8b865d
MS
6152 /*
6153 * Wait for child to complete...
6154 */
6155
83ce8172 6156# ifdef HAVE_WAITPID
db8b865d 6157 while (waitpid(pid, &status, 0) < 0);
83ce8172 6158# else
db8b865d 6159 while (wait(&status) < 0);
83ce8172
MS
6160# endif /* HAVE_WAITPID */
6161 }
24a06ed3 6162#endif /* _WIN32 */
db8b865d 6163
83ce8172
MS
6164 if (status)
6165 {
24a06ed3 6166#ifndef _WIN32
83ce8172 6167 if (WIFEXITED(status))
24a06ed3 6168#endif /* !_WIN32 */
6d56631f 6169 fprintf(stderr, "Command \"%s\" exited with status %d.\n", job->printer->command, WEXITSTATUS(status));
24a06ed3 6170#ifndef _WIN32
db8b865d 6171 else
6d56631f 6172 fprintf(stderr, "Command \"%s\" terminated with signal %d.\n", job->printer->command, WTERMSIG(status));
24a06ed3 6173#endif /* !_WIN32 */
83ce8172 6174 job->state = IPP_JSTATE_ABORTED;
db8b865d 6175 }
83ce8172
MS
6176 else if (status < 0)
6177 job->state = IPP_JSTATE_ABORTED;
6178 else
6d56631f 6179 fprintf(stderr, "Command \"%s\" completed successfully.\n", job->printer->command);
db8b865d
MS
6180
6181 /*
6182 * Make sure processing takes at least 5 seconds...
6183 */
6184
6185 time(&end);
6186 if ((end - start) < 5)
6187 sleep(5);
6188 }
6189 else
6190 {
6191 /*
6192 * Sleep for a random amount of time to simulate job processing.
6193 */
6194
7d5824d6 6195 sleep((unsigned)(5 + (rand() % 11)));
db8b865d 6196 }
1106b00e
MS
6197
6198 if (job->cancel)
a469f8a5 6199 job->state = IPP_JSTATE_CANCELED;
4a838088 6200 else if (job->state == IPP_JSTATE_PROCESSING)
a469f8a5 6201 job->state = IPP_JSTATE_COMPLETED;
1106b00e 6202
aa2a90ce
MS
6203 error:
6204
1106b00e 6205 job->completed = time(NULL);
a469f8a5 6206 job->printer->state = IPP_PSTATE_IDLE;
1106b00e
MS
6207 job->printer->active_job = NULL;
6208
6209 return (NULL);
6210}
6211
6212
9610a474
MS
6213/*
6214 * 'process_state_message()' - Process a STATE: message from a command.
6215 */
6216
6217static void
6218process_state_message(
d46dbe1b 6219 ippeve_job_t *job, /* I - Job */
9610a474
MS
6220 char *message) /* I - Message */
6221{
6222 int i; /* Looping var */
d46dbe1b 6223 ippeve_preason_t state_reasons, /* printer-state-reasons values */
9610a474
MS
6224 bit; /* Current reason bit */
6225 char *ptr, /* Pointer into message */
6226 *next; /* Next keyword in message */
6227 int remove; /* Non-zero if we are removing keywords */
6228
6229
6230 /*
6231 * Skip leading "STATE:" and any whitespace...
6232 */
6233
6234 for (message += 6; *message; message ++)
6235 if (*message != ' ' && *message != '\t')
6236 break;
6237
6238 /*
6239 * Support the following forms of message:
6240 *
6241 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6242 *
6243 * "-keyword[,keyword,...]" to remove keywords.
6244 *
6245 * "+keyword[,keyword,...]" to add keywords.
6246 *
6247 * Keywords may or may not have a suffix (-report, -warning, -error) per
8a4ed632 6248 * RFC 8011.
9610a474
MS
6249 */
6250
6251 if (*message == '-')
6252 {
6253 remove = 1;
6254 state_reasons = job->printer->state_reasons;
6255 message ++;
6256 }
6257 else if (*message == '+')
6258 {
6259 remove = 0;
6260 state_reasons = job->printer->state_reasons;
6261 message ++;
6262 }
6263 else
6264 {
6265 remove = 0;
d46dbe1b 6266 state_reasons = IPPEVE_PREASON_NONE;
9610a474
MS
6267 }
6268
6269 while (*message)
6270 {
6271 if ((next = strchr(message, ',')) != NULL)
6272 *next++ = '\0';
6273
6274 if ((ptr = strstr(message, "-error")) != NULL)
6275 *ptr = '\0';
6276 else if ((ptr = strstr(message, "-report")) != NULL)
6277 *ptr = '\0';
6278 else if ((ptr = strstr(message, "-warning")) != NULL)
6279 *ptr = '\0';
6280
d46dbe1b 6281 for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
9610a474 6282 {
d46dbe1b 6283 if (!strcmp(message, ippeve_preason_strings[i]))
9610a474
MS
6284 {
6285 if (remove)
6286 state_reasons &= ~bit;
6287 else
6288 state_reasons |= bit;
6289 }
6290 }
6291
6292 if (next)
6293 message = next;
6294 else
6295 break;
6296 }
6297
6298 job->printer->state_reasons = state_reasons;
6299}
6300
6301
1106b00e
MS
6302/*
6303 * 'register_printer()' - Register a printer object via Bonjour.
6304 */
6305
6306static int /* O - 1 on success, 0 on error */
6307register_printer(
d46dbe1b 6308 ippeve_printer_t *printer, /* I - Printer */
1562b9a1 6309 const char *subtypes) /* I - Service subtype(s) */
1106b00e 6310{
0a15691a 6311#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
d46dbe1b 6312 ippeve_txt_t ipp_txt; /* Bonjour IPP TXT record */
1562b9a1
MS
6313 int i, /* Looping var */
6314 count; /* Number of values */
6315 ipp_attribute_t *color_supported,
6316 *document_format_supported,
6317 *printer_location,
6318 *printer_make_and_model,
6319 *printer_more_info,
6320 *printer_uuid,
6321 *sides_supported,
6322 *urf_supported; /* Printer attributes */
6323 const char *value; /* Value string */
6324 char formats[252], /* List of supported formats */
6325 urf[252], /* List of supported URF values */
6326 *ptr; /* Pointer into string */
6327
6328 color_supported = ippFindAttribute(printer->attrs, "color-supported", IPP_TAG_BOOLEAN);
6329 document_format_supported = ippFindAttribute(printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE);
6330 printer_location = ippFindAttribute(printer->attrs, "printer-location", IPP_TAG_TEXT);
6331 printer_make_and_model = ippFindAttribute(printer->attrs, "printer-make-and-model", IPP_TAG_TEXT);
6332 printer_more_info = ippFindAttribute(printer->attrs, "printer-more-info", IPP_TAG_URI);
6333 printer_uuid = ippFindAttribute(printer->attrs, "printer-uuid", IPP_TAG_URI);
6334 sides_supported = ippFindAttribute(printer->attrs, "sides-supported", IPP_TAG_KEYWORD);
6335 urf_supported = ippFindAttribute(printer->attrs, "urf-supported", IPP_TAG_KEYWORD);
6336
6337 for (i = 0, count = ippGetCount(document_format_supported), ptr = formats; i < count; i ++)
6338 {
6339 value = ippGetString(document_format_supported, i, NULL);
6340
6341 if (!strcasecmp(value, "application/octet-stream"))
6342 continue;
6343
6344 if (ptr > formats && ptr < (formats + sizeof(formats) - 1))
6345 *ptr++ = ',';
6346
6347 strlcpy(ptr, value, sizeof(formats) - (size_t)(ptr - formats));
6348 ptr += strlen(ptr);
6349
6350 if (ptr >= (formats + sizeof(formats) - 1))
6351 break;
6352 }
6353
6354 urf[0] = '\0';
6355 for (i = 0, count = ippGetCount(urf_supported), ptr = urf; i < count; i ++)
6356 {
6357 value = ippGetString(urf_supported, i, NULL);
6358
6359 if (ptr > urf && ptr < (urf + sizeof(urf) - 1))
6360 *ptr++ = ',';
6361
6362 strlcpy(ptr, value, sizeof(urf) - (size_t)(ptr - urf));
6363 ptr += strlen(ptr);
6364
6365 if (ptr >= (urf + sizeof(urf) - 1))
6366 break;
6367 }
6368
0a15691a 6369#endif /* HAVE_DNSSD || HAVE_AVAHI */
d6563739 6370#ifdef HAVE_DNSSD
1106b00e 6371 DNSServiceErrorType error; /* Error from Bonjour */
1562b9a1 6372 char regtype[256]; /* Bonjour service type */
1106b00e
MS
6373
6374
6375 /*
6376 * Build the TXT record for IPP...
6377 */
6378
d6563739
MS
6379 TXTRecordCreate(&ipp_txt, 1024, NULL);
6380 TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print");
1562b9a1
MS
6381 if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
6382 TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(value), value);
6383 if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL)
6384 TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(value), value);
6385 if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
6386 TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(value), value);
6387 TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats), formats);
6388 TXTRecordSetValue(&ipp_txt, "Color", 1, ippGetBoolean(color_supported, 0) ? "T" : "F");
6389 TXTRecordSetValue(&ipp_txt, "Duplex", 1, ippGetCount(sides_supported) > 1 ? "T" : "F");
6390 if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
6391 TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(value) - 9, value + 9);
fe202ff4 6392# ifdef HAVE_SSL
d6563739 6393 TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2");
fe202ff4 6394# endif /* HAVE_SSL */
1562b9a1 6395 if (urf[0])
60d8f884 6396 TXTRecordSetValue(&ipp_txt, "URF", (uint8_t)strlen(urf), urf);
a2798463
MS
6397 TXTRecordSetValue(&ipp_txt, "txtvers", 1, "1");
6398 TXTRecordSetValue(&ipp_txt, "qtotal", 1, "1");
1106b00e 6399
1106b00e
MS
6400 /*
6401 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6402 * defend our service name but not actually support LPD...
6403 */
6404
d6563739 6405 printer->printer_ref = DNSSDMaster;
1106b00e 6406
1562b9a1
MS
6407 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)
6408 {
6409 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_printer._tcp", error);
1106b00e
MS
6410 return (0);
6411 }
6412
6413 /*
1562b9a1 6414 * Then register the _ipp._tcp (IPP) service type with the real port number to
1106b00e
MS
6415 * advertise our IPP printer...
6416 */
6417
d6563739 6418 printer->ipp_ref = DNSSDMaster;
1106b00e 6419
1562b9a1
MS
6420 if (subtypes && *subtypes)
6421 snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtypes);
a469f8a5 6422 else
015214aa 6423 strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
a469f8a5 6424
1562b9a1
MS
6425 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)
6426 {
6427 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
1106b00e
MS
6428 return (0);
6429 }
6430
f93b32b6 6431# ifdef HAVE_SSL
a469f8a5 6432 /*
1562b9a1 6433 * Then register the _ipps._tcp (IPP) service type with the real port number to
d6563739 6434 * advertise our IPPS printer...
a469f8a5
MS
6435 */
6436
d6563739 6437 printer->ipps_ref = DNSSDMaster;
a469f8a5 6438
1562b9a1
MS
6439 if (subtypes && *subtypes)
6440 snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtypes);
a469f8a5 6441 else
015214aa 6442 strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
a469f8a5 6443
1562b9a1
MS
6444 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)
6445 {
6446 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
a469f8a5
MS
6447 return (0);
6448 }
6449# endif /* HAVE_SSL */
6450
1106b00e
MS
6451 /*
6452 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6453 * real port number to advertise our IPP printer...
6454 */
6455
d6563739 6456 printer->http_ref = DNSSDMaster;
1106b00e 6457
1562b9a1
MS
6458 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)
6459 {
6460 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_http._tcp,_printer", error);
1106b00e
MS
6461 return (0);
6462 }
6463
d6563739
MS
6464 TXTRecordDeallocate(&ipp_txt);
6465
6466#elif defined(HAVE_AVAHI)
6467 char temp[256]; /* Subtype service string */
6468
6469 /*
6470 * Create the TXT record...
6471 */
6472
6473 ipp_txt = NULL;
6474 ipp_txt = avahi_string_list_add_printf(ipp_txt, "rp=ipp/print");
1562b9a1
MS
6475 if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
6476 ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s", value);
6477 if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL)
6478 ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", value);
6479 if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
6480 ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", value);
d6563739 6481 ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
1562b9a1
MS
6482 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", ippGetBoolean(color_supported, 0) ? "T" : "F");
6483 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", ippGetCount(sides_supported) > 1 ? "T" : "F");
6484 if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
6485 ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", value + 9);
d6563739
MS
6486# ifdef HAVE_SSL
6487 ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2");
6488# endif /* HAVE_SSL */
1562b9a1
MS
6489 if (urf[0])
6490 ipp_txt = avahi_string_list_add_printf(ipp_txt, "URF=%s", urf);
6491 ipp_txt = avahi_string_list_add_printf(ipp_txt, "txtvers=1");
6492 ipp_txt = avahi_string_list_add_printf(ipp_txt, "qtotal=1");
d6563739
MS
6493
6494 /*
6495 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6496 */
6497
6498 avahi_threaded_poll_lock(DNSSDMaster);
6499
6500 printer->ipp_ref = avahi_entry_group_new(DNSSDClient, dnssd_callback, NULL);
6501
6502 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);
6503
6504 /*
400e67c1 6505 * Then register the _ipp._tcp (IPP)...
d6563739
MS
6506 */
6507
6508 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 6509 if (subtypes && *subtypes)
d6563739 6510 {
400e67c1
MS
6511 char *temptypes = strdup(subtypes), *start, *end;
6512
6513 for (start = temptypes; *start; start = end)
6514 {
6515 if ((end = strchr(start, ',')) != NULL)
6516 *end++ = '\0';
6517 else
6518 end = start + strlen(start);
6519
6520 snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", start);
6521 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp);
6522 }
6523
6524 free(temptypes);
d6563739
MS
6525 }
6526
6527#ifdef HAVE_SSL
6528 /*
400e67c1 6529 * _ipps._tcp (IPPS) for secure printing...
d6563739
MS
6530 */
6531
6532 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 6533 if (subtypes && *subtypes)
d6563739 6534 {
400e67c1
MS
6535 char *temptypes = strdup(subtypes), *start, *end;
6536
6537 for (start = temptypes; *start; start = end)
6538 {
6539 if ((end = strchr(start, ',')) != NULL)
6540 *end++ = '\0';
6541 else
6542 end = start + strlen(start);
6543
6544 snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", start);
6545 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp);
6546 }
6547
6548 free(temptypes);
d6563739
MS
6549 }
6550#endif /* HAVE_SSL */
6551
6552 /*
6553 * Finally _http.tcp (HTTP) for the web interface...
6554 */
6555
6556 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);
6557 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");
6558
6559 /*
6560 * Commit it...
6561 */
6562
6563 avahi_entry_group_commit(printer->ipp_ref);
6564 avahi_threaded_poll_unlock(DNSSDMaster);
6565
6566 avahi_string_list_free(ipp_txt);
6567#endif /* HAVE_DNSSD */
6568
1106b00e
MS
6569 return (1);
6570}
6571
6572
6573/*
6574 * 'respond_http()' - Send a HTTP response.
6575 */
6576
6577int /* O - 1 on success, 0 on failure */
a469f8a5 6578respond_http(
d46dbe1b 6579 ippeve_client_t *client, /* I - Client */
a469f8a5
MS
6580 http_status_t code, /* I - HTTP status of response */
6581 const char *content_encoding, /* I - Content-Encoding of response */
6582 const char *type, /* I - MIME media type of response */
6583 size_t length) /* I - Length of response */
1106b00e
MS
6584{
6585 char message[1024]; /* Text message */
6586
6587
a469f8a5 6588 fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
1106b00e 6589
a469f8a5 6590 if (code == HTTP_STATUS_CONTINUE)
1106b00e
MS
6591 {
6592 /*
6593 * 100-continue doesn't send any headers...
6594 */
6595
a469f8a5 6596 return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
1106b00e
MS
6597 }
6598
6599 /*
6600 * Format an error message...
6601 */
6602
fe202ff4 6603 if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
1106b00e
MS
6604 {
6605 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
6606
6607 type = "text/plain";
6608 length = strlen(message);
6609 }
6610 else
6611 message[0] = '\0';
6612
6613 /*
a469f8a5 6614 * Send the HTTP response header...
1106b00e
MS
6615 */
6616
a469f8a5 6617 httpClearFields(client->http);
1106b00e 6618
a469f8a5
MS
6619 if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
6620 client->operation == HTTP_STATE_OPTIONS)
6621 httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
1106b00e
MS
6622
6623 if (type)
6624 {
6625 if (!strcmp(type, "text/html"))
a469f8a5
MS
6626 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
6627 "text/html; charset=utf-8");
6628 else
6629 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
1106b00e 6630
a469f8a5
MS
6631 if (content_encoding)
6632 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
1106b00e 6633 }
1106b00e 6634
a469f8a5
MS
6635 httpSetLength(client->http, length);
6636
6637 if (httpWriteResponse(client->http, code) < 0)
1106b00e
MS
6638 return (0);
6639
6640 /*
6641 * Send the response data...
6642 */
6643
6644 if (message[0])
6645 {
6646 /*
6647 * Send a plain text message.
6648 */
6649
a469f8a5
MS
6650 if (httpPrintf(client->http, "%s", message) < 0)
6651 return (0);
6652
6653 if (httpWrite2(client->http, "", 0) < 0)
1106b00e
MS
6654 return (0);
6655 }
6656 else if (client->response)
6657 {
6658 /*
6659 * Send an IPP response...
6660 */
6661
83e08001 6662 debug_attributes("Response", client->response, 2);
1106b00e 6663
a469f8a5 6664 ippSetState(client->response, IPP_STATE_IDLE);
1106b00e 6665
a469f8a5 6666 if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
1106b00e
MS
6667 return (0);
6668 }
1106b00e 6669
a469f8a5 6670 return (1);
1106b00e
MS
6671}
6672
6673
6674/*
6675 * 'respond_ipp()' - Send an IPP response.
6676 */
6677
6678static void
d46dbe1b 6679respond_ipp(ippeve_client_t *client, /* I - Client */
1106b00e
MS
6680 ipp_status_t status, /* I - status-code */
6681 const char *message, /* I - printf-style status-message */
6682 ...) /* I - Additional args as needed */
6683{
a469f8a5 6684 const char *formatted = NULL; /* Formatted message */
1106b00e
MS
6685
6686
a469f8a5 6687 ippSetStatusCode(client->response, status);
1106b00e
MS
6688
6689 if (message)
6690 {
a469f8a5
MS
6691 va_list ap; /* Pointer to additional args */
6692 ipp_attribute_t *attr; /* New status-message attribute */
6693
1106b00e 6694 va_start(ap, message);
6d56631f 6695 if ((attr = ippFindAttribute(client->response, "status-message", IPP_TAG_TEXT)) != NULL)
a469f8a5
MS
6696 ippSetStringfv(client->response, &attr, 0, message, ap);
6697 else
6d56631f 6698 attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, "status-message", NULL, message, ap);
1106b00e
MS
6699 va_end(ap);
6700
a469f8a5 6701 formatted = ippGetString(attr, 0, NULL);
1106b00e 6702 }
1106b00e 6703
a469f8a5 6704 if (formatted)
6d56631f 6705 fprintf(stderr, "%s %s %s (%s)\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status), formatted);
a469f8a5 6706 else
6d56631f 6707 fprintf(stderr, "%s %s %s\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status));
1106b00e
MS
6708}
6709
6710
83e08001
MS
6711/*
6712 * 'respond_unsupported()' - Respond with an unsupported attribute.
6713 */
6714
6715static void
6716respond_unsupported(
d46dbe1b 6717 ippeve_client_t *client, /* I - Client */
83e08001
MS
6718 ipp_attribute_t *attr) /* I - Atribute */
6719{
a2326b5b
MS
6720 ipp_attribute_t *temp; /* Copy of attribute */
6721
6722
6d56631f 6723 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Unsupported %s %s%s value.", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)));
83e08001 6724
a2326b5b
MS
6725 temp = ippCopyAttribute(client->response, attr, 0);
6726 ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
83e08001
MS
6727}
6728
6729
1106b00e
MS
6730/*
6731 * 'run_printer()' - Run the printer service.
6732 */
6733
6734static void
d46dbe1b 6735run_printer(ippeve_printer_t *printer) /* I - Printer */
1106b00e 6736{
0268488e 6737 int num_fds; /* Number of file descriptors */
1106b00e
MS
6738 struct pollfd polldata[3]; /* poll() data */
6739 int timeout; /* Timeout for poll() */
d46dbe1b 6740 ippeve_client_t *client; /* New client */
1106b00e
MS
6741
6742
6743 /*
6744 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6745 */
6746
6747 polldata[0].fd = printer->ipv4;
6748 polldata[0].events = POLLIN;
6749
6750 polldata[1].fd = printer->ipv6;
6751 polldata[1].events = POLLIN;
6752
0268488e
MS
6753 num_fds = 2;
6754
6755#ifdef HAVE_DNSSD
d6563739 6756 polldata[num_fds ].fd = DNSServiceRefSockFD(DNSSDMaster);
0268488e
MS
6757 polldata[num_fds ++].events = POLLIN;
6758#endif /* HAVE_DNSSD */
1106b00e
MS
6759
6760 /*
6761 * Loop until we are killed or have a hard error...
6762 */
6763
6764 for (;;)
6765 {
6766 if (cupsArrayCount(printer->jobs))
6767 timeout = 10;
6768 else
6769 timeout = -1;
6770
7e86f2f6 6771 if (poll(polldata, (nfds_t)num_fds, timeout) < 0 && errno != EINTR)
1106b00e
MS
6772 {
6773 perror("poll() failed");
6774 break;
6775 }
6776
6777 if (polldata[0].revents & POLLIN)
6778 {
6779 if ((client = create_client(printer, printer->ipv4)) != NULL)
6780 {
116c301f
MS
6781 _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
6782
6783 if (t)
6784 {
6785 _cupsThreadDetach(t);
6786 }
6787 else
1106b00e
MS
6788 {
6789 perror("Unable to create client thread");
6790 delete_client(client);
6791 }
6792 }
6793 }
6794
6795 if (polldata[1].revents & POLLIN)
6796 {
6797 if ((client = create_client(printer, printer->ipv6)) != NULL)
6798 {
116c301f
MS
6799 _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
6800
6801 if (t)
6802 {
6803 _cupsThreadDetach(t);
6804 }
6805 else
1106b00e
MS
6806 {
6807 perror("Unable to create client thread");
6808 delete_client(client);
6809 }
6810 }
6811 }
6812
0268488e 6813#ifdef HAVE_DNSSD
1106b00e 6814 if (polldata[2].revents & POLLIN)
d6563739 6815 DNSServiceProcessResult(DNSSDMaster);
0268488e 6816#endif /* HAVE_DNSSD */
1106b00e
MS
6817
6818 /*
6819 * Clean out old jobs...
6820 */
6821
6822 clean_jobs(printer);
6823 }
6824}
6825
6826
9141aa01
MS
6827/*
6828 * 'show_media()' - Show media load state.
6829 */
6830
6831static int /* O - 1 on success, 0 on failure */
6832show_media(ippeve_client_t *client) /* I - Client connection */
6833{
6834 ippeve_printer_t *printer = client->printer;
6835 /* Printer */
6836 int i, j, /* Looping vars */
6837 num_ready, /* Number of ready media */
6838 num_sizes, /* Number of media sizes */
6839 num_sources, /* Number of media sources */
6840 num_types; /* Number of media types */
6841 ipp_attribute_t *media_col_ready,/* media-col-ready attribute */
6842 *media_ready, /* media-ready attribute */
6843 *media_sizes, /* media-supported attribute */
6844 *media_sources, /* media-source-supported attribute */
6845 *media_types, /* media-type-supported attribute */
6846 *input_tray; /* printer-input-tray attribute */
6847 ipp_t *media_col; /* media-col value */
6848 const char *media_size, /* media value */
6849 *media_source, /* media-source value */
6850 *media_type, /* media-type value */
6851 *ready_size, /* media-col-ready media-size[-name] value */
6852 *ready_source, /* media-col-ready media-source value */
6853 *ready_tray, /* printer-input-tray value */
6854 *ready_type; /* media-col-ready media-type value */
6855 char tray_str[1024], /* printer-input-tray string value */
6856 *tray_ptr; /* Pointer into value */
6857 int tray_len; /* Length of printer-input-tray value */
6858 int ready_sheets; /* printer-input-tray sheets value */
6859 int num_options; /* Number of form options */
6860 cups_option_t *options; /* Form options */
6861 static const int sheets[] = /* Number of sheets */
6862 {
6863 250,
6864 100,
6865 25,
6866 5,
6640ceec
MS
6867 0,
6868 -2
9141aa01
MS
6869 };
6870
6871
6872 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
6873 return (0);
6874
6640ceec 6875 html_header(client, printer->name, 0);
9141aa01
MS
6876
6877 if ((media_col_ready = ippFindAttribute(printer->attrs, "media-col-ready", IPP_TAG_BEGIN_COLLECTION)) == NULL)
6878 {
6879 html_printf(client, "<p>Error: No media-col-ready defined for printer.</p>\n");
6880 html_footer(client);
6881 return (1);
6882 }
6883
6884 media_ready = ippFindAttribute(printer->attrs, "media-ready", IPP_TAG_ZERO);
6885
6886 if ((media_sizes = ippFindAttribute(printer->attrs, "media-supported", IPP_TAG_ZERO)) == NULL)
6887 {
6888 html_printf(client, "<p>Error: No media-supported defined for printer.</p>\n");
6889 html_footer(client);
6890 return (1);
6891 }
6892
6893 if ((media_sources = ippFindAttribute(printer->attrs, "media-source-supported", IPP_TAG_ZERO)) == NULL)
6894 {
6895 html_printf(client, "<p>Error: No media-source-supported defined for printer.</p>\n");
6896 html_footer(client);
6897 return (1);
6898 }
6899
6900 if ((media_types = ippFindAttribute(printer->attrs, "media-type-supported", IPP_TAG_ZERO)) == NULL)
6901 {
6902 html_printf(client, "<p>Error: No media-type-supported defined for printer.</p>\n");
6903 html_footer(client);
6904 return (1);
6905 }
6906
6907 if ((input_tray = ippFindAttribute(printer->attrs, "printer-input-tray", IPP_TAG_STRING)) == NULL)
6908 {
6909 html_printf(client, "<p>Error: No printer-input-tray defined for printer.</p>\n");
6910 html_footer(client);
6911 return (1);
6912 }
6913
6914 num_ready = ippGetCount(media_col_ready);
6915 num_sizes = ippGetCount(media_sizes);
6916 num_sources = ippGetCount(media_sources);
6917 num_types = ippGetCount(media_types);
6918
6919 if (num_sources != ippGetCount(input_tray))
6920 {
6921 html_printf(client, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n");
6922 html_footer(client);
6923 return (1);
6924 }
6925
6926 /*
6927 * Process form data if present...
6928 */
6929
820cb58e
MS
6930 if (printer->web_forms)
6931 num_options = parse_options(client, &options);
6932 else
6933 num_options = 0;
6934
6935 if (num_options > 0)
9141aa01
MS
6936 {
6937 /*
6938 * WARNING: A real printer/server implementation MUST NOT implement
6939 * media updates via a GET request - GET requests are supposed to be
6940 * idempotent (without side-effects) and we obviously are not
6941 * authenticating access here. This form is provided solely to
6942 * enable testing and development!
6943 */
6944
6945 char name[255]; /* Form name */
6946 const char *val; /* Form value */
6947 pwg_media_t *media; /* Media info */
6948
6949 _cupsRWLockWrite(&printer->rwlock);
6950
9141aa01
MS
6951 ippDeleteAttribute(printer->attrs, media_col_ready);
6952 media_col_ready = NULL;
6953
6954 if (media_ready)
6955 {
6956 ippDeleteAttribute(printer->attrs, media_ready);
6957 media_ready = NULL;
6958 }
6959
6960 printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MEDIA_LOW | IPPEVE_PREASON_MEDIA_EMPTY | IPPEVE_PREASON_MEDIA_NEEDED);
6961
6962 for (i = 0; i < num_sources; i ++)
6963 {
6964 media_source = ippGetString(media_sources, i, NULL);
6965
6640ceec
MS
6966 if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual"))
6967 continue;
6968
9141aa01
MS
6969 snprintf(name, sizeof(name), "size%d", i);
6970 if ((media_size = cupsGetOption(name, num_options, options)) != NULL && (media = pwgMediaForPWG(media_size)) != NULL)
6971 {
6972 snprintf(name, sizeof(name), "type%d", i);
6973 if ((media_type = cupsGetOption(name, num_options, options)) != NULL && !*media_type)
6974 media_type = NULL;
6975
6976 if (media_ready)
6977 ippSetString(printer->attrs, &media_ready, ippGetCount(media_ready), media_size);
6978 else
6979 media_ready = ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, media_size);
6980
6981 media_col = create_media_col(media_size, media_source, media_type, media->width, media->length, -1, -1, -1, -1);
6982
6983 if (media_col_ready)
6984 ippSetCollection(printer->attrs, &media_col_ready, ippGetCount(media_col_ready), media_col);
6985 else
6986 media_col_ready = ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-ready", media_col);
6987 ippDelete(media_col);
6988 }
6989 else
6990 media = NULL;
6991
6992 snprintf(name, sizeof(name), "level%d", i);
6993 if ((val = cupsGetOption(name, num_options, options)) != NULL)
6994 ready_sheets = atoi(val);
6995 else
6996 ready_sheets = 0;
6997
6640ceec 6998 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 6999
6640ceec 7000 ippSetOctetString(printer->attrs, &input_tray, i, tray_str, (int)strlen(tray_str));
9141aa01
MS
7001
7002 if (ready_sheets == 0)
7003 {
7004 printer->state_reasons |= IPPEVE_PREASON_MEDIA_EMPTY;
7005 if (printer->active_job)
7006 printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
7007 }
6640ceec 7008 else if (ready_sheets < 25 && ready_sheets > 0)
9141aa01
MS
7009 printer->state_reasons |= IPPEVE_PREASON_MEDIA_LOW;
7010 }
7011
7012 if (!media_col_ready)
7013 media_col_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-col-ready");
7014
7015 if (!media_ready)
7016 media_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-ready");
7017
7018 _cupsRWUnlock(&printer->rwlock);
9141aa01
MS
7019 }
7020
820cb58e
MS
7021 if (printer->web_forms)
7022 html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
9141aa01
MS
7023
7024 html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
7025 for (i = 0; i < num_sources; i ++)
7026 {
7027 media_source = ippGetString(media_sources, i, NULL);
7028
6640ceec
MS
7029 if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual"))
7030 continue;
7031
9141aa01
MS
7032 for (j = 0, ready_size = NULL, ready_type = NULL; j < num_ready; j ++)
7033 {
7034 media_col = ippGetCollection(media_col_ready, j);
7035 ready_size = ippGetString(ippFindAttribute(media_col, "media-size-name", IPP_TAG_ZERO), 0, NULL);
7036 ready_source = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL);
7037 ready_type = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL);
7038
7039 if (ready_source && !strcmp(ready_source, media_source))
7040 break;
7041
7042 ready_source = NULL;
7043 ready_size = NULL;
7044 ready_type = NULL;
7045 }
7046
820cb58e
MS
7047 html_printf(client, "<tr><th>%s:</th>", media_source);
7048
9141aa01
MS
7049 /*
7050 * Media size...
7051 */
7052
820cb58e 7053 if (printer->web_forms)
9141aa01 7054 {
820cb58e
MS
7055 html_printf(client, "<td><select name=\"size%d\"><option value=\"\">None</option>", i);
7056 for (j = 0; j < num_sizes; j ++)
7057 {
7058 media_size = ippGetString(media_sizes, j, NULL);
9141aa01 7059
820cb58e
MS
7060 html_printf(client, "<option%s>%s</option>", (ready_size && !strcmp(ready_size, media_size)) ? " selected" : "", media_size);
7061 }
7062 html_printf(client, "</select>");
9141aa01 7063 }
820cb58e
MS
7064 else
7065 html_printf(client, "<td>%s", ready_size);
9141aa01
MS
7066
7067 /*
7068 * Media type...
7069 */
7070
820cb58e 7071 if (printer->web_forms)
9141aa01 7072 {
820cb58e
MS
7073 html_printf(client, " <select name=\"type%d\"><option value=\"\">None</option>", i);
7074 for (j = 0; j < num_types; j ++)
7075 {
7076 media_type = ippGetString(media_types, j, NULL);
9141aa01 7077
820cb58e
MS
7078 html_printf(client, "<option%s>%s</option>", (ready_type && !strcmp(ready_type, media_type)) ? " selected" : "", media_type);
7079 }
7080 html_printf(client, "</select>");
9141aa01 7081 }
820cb58e
MS
7082 else
7083 html_printf(client, ", %s", ready_type);
9141aa01
MS
7084
7085 /*
7086 * Level/sheets loaded...
7087 */
7088
7089 if ((ready_tray = ippGetOctetString(input_tray, i, &tray_len)) != NULL)
7090 {
60d8f884
MS
7091 if (tray_len > (int)(sizeof(tray_str) - 1))
7092 tray_len = (int)sizeof(tray_str) - 1;
9141aa01
MS
7093 memcpy(tray_str, ready_tray, (size_t)tray_len);
7094 tray_str[tray_len] = '\0';
7095
7096 if ((tray_ptr = strstr(tray_str, "level=")) != NULL)
7097 ready_sheets = atoi(tray_ptr + 6);
7098 else
7099 ready_sheets = 0;
7100 }
7101 else
7102 ready_sheets = 0;
7103
820cb58e 7104 if (printer->web_forms)
6640ceec 7105 {
820cb58e
MS
7106 html_printf(client, " <select name=\"level%d\">", i);
7107 for (j = 0; j < (int)(sizeof(sheets) / sizeof(sheets[0])); j ++)
7108 {
7109 if (strcmp(media_source, "main") && sheets[j] > 25)
7110 continue;
6640ceec 7111
820cb58e
MS
7112 if (sheets[j] < 0)
7113 html_printf(client, "<option value=\"%d\"%s>Unknown</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "");
7114 else
7115 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "", sheets[j]);
7116 }
7117 html_printf(client, "</select></td></tr>\n");
6640ceec 7118 }
820cb58e
MS
7119 else if (ready_sheets > 0)
7120 html_printf(client, ", %d sheets</td></tr>\n", ready_sheets);
7121 else
7122 html_printf(client, "</td></tr>\n");
9141aa01
MS
7123 }
7124
820cb58e
MS
7125 if (printer->web_forms)
7126 {
7127 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\">");
7128 if (num_options > 0)
7129 html_printf(client, " <span class=\"badge\" id=\"status\">Media updated.</span>\n");
7130 html_printf(client, "</td></tr></table></form>\n");
7131
7132 if (num_options > 0)
7133 html_printf(client, "<script>\n"
7134 "setTimeout(hide_status, 3000);\n"
7135 "function hide_status() {\n"
7136 " var status = document.getElementById('status');\n"
7137 " status.style.display = 'none';\n"
7138 "}\n"
7139 "</script>\n");
7140 }
7141 else
7142 html_printf(client, "</table>\n");
7143
9141aa01
MS
7144 html_footer(client);
7145
7146 return (1);
7147}
7148
7149
7150/*
7151 * 'show_status()' - Show printer/system state.
7152 */
7153
7154static int /* O - 1 on success, 0 on failure */
7155show_status(ippeve_client_t *client) /* I - Client connection */
7156{
7157 ippeve_printer_t *printer = client->printer;
7158 /* Printer */
7159 ippeve_job_t *job; /* Current job */
7160 int i; /* Looping var */
7161 ippeve_preason_t reason; /* Current reason */
7162 static const char * const reasons[] = /* Reason strings */
7163 {
7164 "Other",
7165 "Cover Open",
7166 "Input Tray Missing",
7167 "Marker Supply Empty",
7168 "Marker Supply Low",
7169 "Marker Waste Almost Full",
7170 "Marker Waste Full",
7171 "Media Empty",
7172 "Media Jam",
7173 "Media Low",
7174 "Media Needed",
7175 "Moving to Paused",
7176 "Paused",
7177 "Spool Area Full",
7178 "Toner Empty",
7179 "Toner Low"
7180 };
6640ceec
MS
7181 static const char * const state_colors[] =
7182 { /* State colors */
7183 "#0C0", /* Idle */
7184 "#EE0", /* Processing */
7185 "#C00" /* Stopped */
7186 };
9141aa01
MS
7187
7188
7189 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7190 return (0);
7191
6640ceec
MS
7192 html_header(client, printer->name, printer->state == IPP_PSTATE_PROCESSING ? 5 : 15);
7193 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
7194 html_printf(client, "<p>%s, %d job(s).", printer->state == IPP_PSTATE_IDLE ? "Idle" : printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(printer->jobs));
7195 for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
7196 if (printer->state_reasons & reason)
7197 html_printf(client, "\n<br>&nbsp;&nbsp;&nbsp;&nbsp;%s", reasons[i]);
7198 html_printf(client, "</p>\n");
7199
7200 if (cupsArrayCount(printer->jobs) > 0)
7201 {
7202 _cupsRWLockRead(&(printer->rwlock));
7203
7204 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");
7205 for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs); job; job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
7206 {
7207 char when[256], /* When job queued/started/finished */
7208 hhmmss[64]; /* Time HH:MM:SS */
7209
7210 switch (job->state)
7211 {
7212 case IPP_JSTATE_PENDING :
7213 case IPP_JSTATE_HELD :
7214 snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss)));
7215 break;
7216 case IPP_JSTATE_PROCESSING :
7217 case IPP_JSTATE_STOPPED :
7218 snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss)));
7219 break;
7220 case IPP_JSTATE_ABORTED :
7221 snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7222 break;
7223 case IPP_JSTATE_CANCELED :
7224 snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7225 break;
7226 case IPP_JSTATE_COMPLETED :
7227 snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7228 break;
7229 }
7230
7231 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);
7232 }
7233 html_printf(client, "</tbody></table>\n");
7234
7235 _cupsRWUnlock(&(printer->rwlock));
7236 }
7237
7238 html_footer(client);
7239
7240 return (1);
7241}
7242
7243
7244/*
7245 * 'show_supplies()' - Show printer supplies.
7246 */
7247
7248static int /* O - 1 on success, 0 on failure */
7249show_supplies(
7250 ippeve_client_t *client) /* I - Client connection */
7251{
7252 ippeve_printer_t *printer = client->printer;
7253 /* Printer */
7254 int i, /* Looping var */
7255 num_supply; /* Number of supplies */
7256 ipp_attribute_t *supply, /* printer-supply attribute */
7257 *supply_desc; /* printer-supply-description attribute */
7258 int num_options; /* Number of form options */
7259 cups_option_t *options; /* Form options */
7260 int supply_len, /* Length of supply value */
7261 level; /* Supply level */
7262 const char *supply_value; /* Supply value */
7263 char supply_text[1024], /* Supply string */
7264 *supply_ptr; /* Pointer into supply string */
7265 static const char * const printer_supply[] =
7266 { /* printer-supply values */
7267 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
7268 "maxcapacity=100;level=%d;colorantname=unknown;",
7269 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
7270 "maxcapacity=100;level=%d;colorantname=black;",
7271 "index=3;class=supplyThatIsConsumed;type=toner;unit=percent;"
7272 "maxcapacity=100;level=%d;colorantname=cyan;",
7273 "index=4;class=supplyThatIsConsumed;type=toner;unit=percent;"
7274 "maxcapacity=100;level=%d;colorantname=magenta;",
7275 "index=5;class=supplyThatIsConsumed;type=toner;unit=percent;"
7276 "maxcapacity=100;level=%d;colorantname=yellow;"
7277 };
820cb58e
MS
7278 static const char * const backgrounds[] =
7279 { /* Background colors for the supply-level bars */
9141aa01
MS
7280 "#777 linear-gradient(#333,#777)",
7281 "#000 linear-gradient(#666,#000)",
7282 "#0FF linear-gradient(#6FF,#0FF)",
7283 "#F0F linear-gradient(#F6F,#F0F)",
7284 "#CC0 linear-gradient(#EE6,#EE0)"
7285 };
820cb58e
MS
7286 static const char * const colors[] = /* Text colors for the supply-level bars */
7287 {
7288 "#fff",
7289 "#fff",
7290 "#000",
7291 "#000",
7292 "#000"
7293 };
9141aa01
MS
7294
7295
7296 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7297 return (0);
7298
6640ceec 7299 html_header(client, printer->name, 0);
9141aa01
MS
7300
7301 if ((supply = ippFindAttribute(printer->attrs, "printer-supply", IPP_TAG_STRING)) == NULL)
7302 {
7303 html_printf(client, "<p>Error: No printer-supply defined for printer.</p>\n");
7304 html_footer(client);
7305 return (1);
7306 }
7307
7308 num_supply = ippGetCount(supply);
7309
7310 if ((supply_desc = ippFindAttribute(printer->attrs, "printer-supply-description", IPP_TAG_TEXT)) == NULL)
7311 {
7312 html_printf(client, "<p>Error: No printer-supply-description defined for printer.</p>\n");
7313 html_footer(client);
7314 return (1);
7315 }
7316
7317 if (num_supply != ippGetCount(supply_desc))
7318 {
7319 html_printf(client, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n");
7320 html_footer(client);
7321 return (1);
7322 }
7323
820cb58e
MS
7324 if (printer->web_forms)
7325 num_options = parse_options(client, &options);
7326 else
7327 num_options = 0;
7328
7329 if (num_options > 0)
9141aa01
MS
7330 {
7331 /*
7332 * WARNING: A real printer/server implementation MUST NOT implement
7333 * supply updates via a GET request - GET requests are supposed to be
7334 * idempotent (without side-effects) and we obviously are not
7335 * authenticating access here. This form is provided solely to
7336 * enable testing and development!
7337 */
7338
7339 char name[64]; /* Form field */
7340 const char *val; /* Form value */
7341
7342 _cupsRWLockWrite(&printer->rwlock);
7343
7344 ippDeleteAttribute(printer->attrs, supply);
7345 supply = NULL;
7346
7347 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);
7348
7349 for (i = 0; i < num_supply; i ++)
7350 {
7351 snprintf(name, sizeof(name), "supply%d", i);
7352 if ((val = cupsGetOption(name, num_options, options)) != NULL)
7353 {
7354 level = atoi(val); /* New level */
7355
7356 snprintf(supply_text, sizeof(supply_text), printer_supply[i], level);
7357 if (supply)
7358 ippSetOctetString(printer->attrs, &supply, ippGetCount(supply), supply_text, (int)strlen(supply_text));
7359 else
7360 supply = ippAddOctetString(printer->attrs, IPP_TAG_PRINTER, "printer-supply", supply_text, (int)strlen(supply_text));
7361
7362 if (i == 0)
7363 {
7364 if (level == 100)
7365 printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_FULL;
7366 else if (level > 90)
7367 printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL;
7368 }
7369 else
7370 {
7371 if (level == 0)
7372 printer->state_reasons |= IPPEVE_PREASON_TONER_EMPTY;
7373 else if (level < 10)
7374 printer->state_reasons |= IPPEVE_PREASON_TONER_LOW;
7375 }
7376 }
7377 }
7378
7379 _cupsRWUnlock(&printer->rwlock);
9141aa01
MS
7380 }
7381
820cb58e
MS
7382 if (printer->web_forms)
7383 html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
9141aa01
MS
7384
7385 html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
7386 for (i = 0; i < num_supply; i ++)
7387 {
7388 supply_value = ippGetOctetString(supply, i, &supply_len);
60d8f884
MS
7389 if (supply_len > (int)(sizeof(supply_text) - 1))
7390 supply_len = (int)sizeof(supply_text) - 1;
9141aa01
MS
7391
7392 memcpy(supply_text, supply_value, (size_t)supply_len);
7393 supply_text[supply_len] = '\0';
7394
7395 if ((supply_ptr = strstr(supply_text, "level=")) != NULL)
7396 level = atoi(supply_ptr + 6);
7397 else
7398 level = 50;
7399
820cb58e
MS
7400 if (printer->web_forms)
7401 html_printf(client, "<tr><th>%s:</th><td><input name=\"supply%d\" size=\"3\" value=\"%d\"></td>", ippGetString(supply_desc, i, NULL), i, level);
7402 else
7403 html_printf(client, "<tr><th>%s:</th>", ippGetString(supply_desc, i, NULL));
7404
7405 if (level < 10)
7406 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);
7407 else
7408 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 7409 }
820cb58e
MS
7410
7411 if (printer->web_forms)
7412 {
7413 html_printf(client, "<tr><td></td><td colspan=\"2\"><input type=\"submit\" value=\"Update Supplies\">");
7414 if (num_options > 0)
7415 html_printf(client, " <span class=\"badge\" id=\"status\">Supplies updated.</span>\n");
7416 html_printf(client, "</td></tr>\n</table>\n</form>\n");
7417
7418 if (num_options > 0)
7419 html_printf(client, "<script>\n"
7420 "setTimeout(hide_status, 3000);\n"
7421 "function hide_status() {\n"
7422 " var status = document.getElementById('status');\n"
7423 " status.style.display = 'none';\n"
7424 "}\n"
7425 "</script>\n");
7426 }
7427 else
7428 html_printf(client, "</table>\n");
7429
9141aa01
MS
7430 html_footer(client);
7431
7432 return (1);
7433}
7434
7435
0b5ce83f
MS
7436/*
7437 * 'time_string()' - Return the local time in hours, minutes, and seconds.
7438 */
7439
7440static char *
7441time_string(time_t tv, /* I - Time value */
7442 char *buffer, /* I - Buffer */
7443 size_t bufsize) /* I - Size of buffer */
7444{
7445 struct tm *curtime = localtime(&tv);
7446 /* Local time */
7447
7448 strftime(buffer, bufsize, "%X", curtime);
7449 return (buffer);
7450}
7451
7452
1106b00e
MS
7453/*
7454 * 'usage()' - Show program usage.
7455 */
7456
7457static void
7458usage(int status) /* O - Exit status */
7459{
6d56631f
MS
7460 _cupsLangPuts(stdout, _("Usage: ippeveprinter [options] \"name\""));
7461 _cupsLangPuts(stdout, _("Options:"));
7462 _cupsLangPuts(stderr, _("--help Show program help"));
820cb58e 7463 _cupsLangPuts(stderr, _("--no-web-forms Disable web forms for media and supplies"));
6d56631f
MS
7464 _cupsLangPuts(stderr, _("--version Show program version"));
7465 _cupsLangPuts(stdout, _("-2 Set 2-sided printing support (default=1-sided)"));
7466 _cupsLangPuts(stdout, _("-D device-uri Set the device URI for the printer"));
7467#ifdef HAVE_SSL
7468 _cupsLangPuts(stdout, _("-K keypath Set location of server X.509 certificates and keys."));
7469#endif /* HAVE_SSL */
7470 _cupsLangPuts(stdout, _("-M manufacturer Set manufacturer name (default=Test)"));
7471 _cupsLangPuts(stdout, _("-P filename.ppd Load printer attributes from PPD file"));
7472 _cupsLangPuts(stdout, _("-V version Set default IPP version"));
7473 _cupsLangPuts(stdout, _("-a filename.conf Load printer attributes from conf file"));
7474 _cupsLangPuts(stdout, _("-c command Set print command"));
7475 _cupsLangPuts(stdout, _("-d spool-directory Set spool directory"));
7476 _cupsLangPuts(stdout, _("-f type/subtype[,...] Set supported file types"));
7477 _cupsLangPuts(stdout, _("-i iconfile.png Set icon file"));
7478 _cupsLangPuts(stdout, _("-k Keep job spool files"));
7479 _cupsLangPuts(stdout, _("-l location Set location of printer"));
7480 _cupsLangPuts(stdout, _("-m model Set model name (default=Printer)"));
7481 _cupsLangPuts(stdout, _("-n hostname Set hostname for printer"));
7482 _cupsLangPuts(stdout, _("-p port Set port number for printer"));
7483 _cupsLangPuts(stdout, _("-r subtype Set DNS-SD service subtype"));
7484 _cupsLangPuts(stdout, _("-s speed[,color-speed] Set speed in pages per minute"));
7485 _cupsLangPuts(stderr, _("-v Be verbose"));
1106b00e
MS
7486
7487 exit(status);
7488}
7489
7490
7491/*
83e08001
MS
7492 * 'valid_doc_attributes()' - Determine whether the document attributes are
7493 * valid.
1106b00e 7494 *
83e08001
MS
7495 * When one or more document attributes are invalid, this function adds a
7496 * suitable response and attributes to the unsupported group.
1106b00e
MS
7497 */
7498
7499static int /* O - 1 if valid, 0 if not */
83e08001 7500valid_doc_attributes(
d46dbe1b 7501 ippeve_client_t *client) /* I - Client */
1106b00e 7502{
a469f8a5
MS
7503 int valid = 1; /* Valid attributes? */
7504 ipp_op_t op = ippGetOperation(client->request);
7505 /* IPP operation */
7506 const char *op_name = ippOpString(op);
7507 /* IPP operation name */
1106b00e 7508 ipp_attribute_t *attr, /* Current attribute */
a469f8a5
MS
7509 *supported; /* xxx-supported attribute */
7510 const char *compression = NULL,
7511 /* compression value */
7512 *format = NULL; /* document-format value */
1106b00e
MS
7513
7514
7515 /*
7516 * Check operation attributes...
7517 */
7518
404dde30 7519 if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
1106b00e
MS
7520 {
7521 /*
a469f8a5
MS
7522 * If compression is specified, only accept a supported value in a Print-Job
7523 * or Send-Document request...
1106b00e
MS
7524 */
7525
a469f8a5
MS
7526 compression = ippGetString(attr, 0, NULL);
7527 supported = ippFindAttribute(client->printer->attrs,
7528 "compression-supported", IPP_TAG_KEYWORD);
7529
7530 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
7531 ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
db8b865d
MS
7532 (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
7533 op != IPP_OP_VALIDATE_JOB) ||
a469f8a5
MS
7534 !ippContainsString(supported, compression))
7535 {
1106b00e 7536 respond_unsupported(client, attr);
a469f8a5
MS
7537 valid = 0;
7538 }
1106b00e 7539 else
a469f8a5 7540 {
404dde30
MS
7541 fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression);
7542
7543 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression);
a469f8a5
MS
7544
7545 if (strcmp(compression, "none"))
a654c79d
MS
7546 {
7547 if (Verbosity)
7548 fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression);
a469f8a5 7549 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
a654c79d 7550 }
a469f8a5 7551 }
1106b00e
MS
7552 }
7553
7554 /*
7555 * Is it a format we support?
7556 */
7557
404dde30 7558 if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
1106b00e 7559 {
a469f8a5
MS
7560 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
7561 ippGetGroupTag(attr) != IPP_TAG_OPERATION)
7562 {
1106b00e 7563 respond_unsupported(client, attr);
a469f8a5
MS
7564 valid = 0;
7565 }
1106b00e 7566 else
e60ec91f 7567 {
a469f8a5 7568 format = ippGetString(attr, 0, NULL);
e60ec91f 7569
6d56631f 7570 fprintf(stderr, "%s %s document-format=\"%s\"\n", client->hostname, op_name, format);
4a838088
MS
7571
7572 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format);
e60ec91f 7573 }
1106b00e
MS
7574 }
7575 else
a469f8a5 7576 {
404dde30 7577 format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL);
8a78aa37
MS
7578 if (!format)
7579 format = "application/octet-stream"; /* Should never happen */
7580
4a838088 7581 attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
a469f8a5 7582 }
1106b00e 7583
7f500d89 7584 if (format && !strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
1106b00e
MS
7585 {
7586 /*
404dde30 7587 * Auto-type the file using the first 8 bytes of the file...
1106b00e
MS
7588 */
7589
404dde30 7590 unsigned char header[8]; /* First 8 bytes of file */
1106b00e
MS
7591
7592 memset(header, 0, sizeof(header));
a469f8a5 7593 httpPeek(client->http, (char *)header, sizeof(header));
1106b00e
MS
7594
7595 if (!memcmp(header, "%PDF", 4))
7596 format = "application/pdf";
7597 else if (!memcmp(header, "%!", 2))
7598 format = "application/postscript";
404dde30 7599 else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
1106b00e
MS
7600 format = "image/jpeg";
7601 else if (!memcmp(header, "\211PNG", 4))
7602 format = "image/png";
404dde30
MS
7603 else if (!memcmp(header, "RAS2", 4))
7604 format = "image/pwg-raster";
7605 else if (!memcmp(header, "UNIRAST", 8))
7606 format = "image/urf";
7607 else
7608 format = NULL;
1106b00e
MS
7609
7610 if (format)
404dde30 7611 {
6d56631f 7612 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n", client->hostname, op_name, format);
1106b00e 7613
404dde30
MS
7614 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
7615 }
1106b00e
MS
7616 }
7617
404dde30 7618 if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
1106b00e 7619 {
a469f8a5
MS
7620 respond_unsupported(client, attr);
7621 valid = 0;
1106b00e
MS
7622 }
7623
404dde30
MS
7624 /*
7625 * document-name
7626 */
7627
7628 if ((attr = ippFindAttribute(client->request, "document-name", IPP_TAG_NAME)) != NULL)
7629 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
7630
a469f8a5 7631 return (valid);
83e08001
MS
7632}
7633
7634
7635/*
7636 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
7637 *
7638 * When one or more job attributes are invalid, this function adds a suitable
7639 * response and attributes to the unsupported group.
7640 */
7641
7642static int /* O - 1 if valid, 0 if not */
7643valid_job_attributes(
d46dbe1b 7644 ippeve_client_t *client) /* I - Client */
83e08001 7645{
a469f8a5 7646 int i, /* Looping var */
63efa616 7647 count, /* Number of values */
a469f8a5 7648 valid = 1; /* Valid attributes? */
83e08001
MS
7649 ipp_attribute_t *attr, /* Current attribute */
7650 *supported; /* xxx-supported attribute */
7651
7652
7653 /*
7654 * Check operation attributes...
7655 */
7656
a469f8a5 7657 valid = valid_doc_attributes(client);
83e08001 7658
1106b00e
MS
7659 /*
7660 * Check the various job template attributes...
7661 */
7662
404dde30 7663 if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL)
1106b00e 7664 {
a469f8a5
MS
7665 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
7666 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
1106b00e
MS
7667 {
7668 respond_unsupported(client, attr);
a469f8a5 7669 valid = 0;
1106b00e
MS
7670 }
7671 }
7672
404dde30 7673 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
1106b00e 7674 {
a469f8a5 7675 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
1106b00e
MS
7676 {
7677 respond_unsupported(client, attr);
a469f8a5 7678 valid = 0;
1106b00e
MS
7679 }
7680 }
7681
404dde30 7682 if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
1106b00e 7683 {
a469f8a5
MS
7684 if (ippGetCount(attr) != 1 ||
7685 (ippGetValueTag(attr) != IPP_TAG_NAME &&
7686 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
7687 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
7688 strcmp(ippGetString(attr, 0, NULL), "no-hold"))
1106b00e
MS
7689 {
7690 respond_unsupported(client, attr);
a469f8a5 7691 valid = 0;
1106b00e
MS
7692 }
7693 }
7694
404dde30
MS
7695 if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL)
7696 {
7697 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0)
7698 {
7699 respond_unsupported(client, attr);
7700 valid = 0;
7701 }
7702 }
7703
7704 if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL)
1106b00e 7705 {
a469f8a5
MS
7706 if (ippGetCount(attr) != 1 ||
7707 (ippGetValueTag(attr) != IPP_TAG_NAME &&
7708 ippGetValueTag(attr) != IPP_TAG_NAMELANG))
1106b00e
MS
7709 {
7710 respond_unsupported(client, attr);
a469f8a5 7711 valid = 0;
1106b00e 7712 }
404dde30
MS
7713
7714 ippSetGroupTag(client->request, &attr, IPP_TAG_JOB);
1106b00e 7715 }
404dde30
MS
7716 else
7717 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
1106b00e 7718
404dde30 7719 if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL)
1106b00e 7720 {
a469f8a5
MS
7721 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
7722 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
1106b00e
MS
7723 {
7724 respond_unsupported(client, attr);
a469f8a5 7725 valid = 0;
1106b00e
MS
7726 }
7727 }
7728
404dde30 7729 if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
1106b00e 7730 {
a469f8a5
MS
7731 if (ippGetCount(attr) != 1 ||
7732 (ippGetValueTag(attr) != IPP_TAG_NAME &&
7733 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
7734 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
7735 strcmp(ippGetString(attr, 0, NULL), "none"))
1106b00e
MS
7736 {
7737 respond_unsupported(client, attr);
a469f8a5 7738 valid = 0;
1106b00e
MS
7739 }
7740 }
7741
404dde30 7742 if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
1106b00e 7743 {
a469f8a5
MS
7744 if (ippGetCount(attr) != 1 ||
7745 (ippGetValueTag(attr) != IPP_TAG_NAME &&
7746 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
7747 ippGetValueTag(attr) != IPP_TAG_KEYWORD))
1106b00e
MS
7748 {
7749 respond_unsupported(client, attr);
a469f8a5 7750 valid = 0;
1106b00e
MS
7751 }
7752 else
7753 {
63efa616 7754 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
1106b00e 7755
63efa616 7756 if (!ippContainsString(supported, ippGetString(attr, 0, NULL)))
1106b00e
MS
7757 {
7758 respond_unsupported(client, attr);
a469f8a5 7759 valid = 0;
1106b00e
MS
7760 }
7761 }
7762 }
7763
404dde30 7764 if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL)
1106b00e 7765 {
63efa616
MS
7766 ipp_t *col, /* media-col collection */
7767 *size; /* media-size collection */
7768 ipp_attribute_t *member, /* Member attribute */
7769 *x_dim, /* x-dimension */
7770 *y_dim; /* y-dimension */
7771 int x_value, /* y-dimension value */
7772 y_value; /* x-dimension value */
7773
a469f8a5
MS
7774 if (ippGetCount(attr) != 1 ||
7775 ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
1106b00e
MS
7776 {
7777 respond_unsupported(client, attr);
a469f8a5 7778 valid = 0;
1106b00e 7779 }
63efa616
MS
7780
7781 col = ippGetCollection(attr, 0);
7782
7783 if ((member = ippFindAttribute(col, "media-size-name", IPP_TAG_ZERO)) != NULL)
7784 {
7785 if (ippGetCount(member) != 1 ||
7786 (ippGetValueTag(member) != IPP_TAG_NAME &&
7787 ippGetValueTag(member) != IPP_TAG_NAMELANG &&
7788 ippGetValueTag(member) != IPP_TAG_KEYWORD))
7789 {
7790 respond_unsupported(client, attr);
7791 valid = 0;
7792 }
7793 else
7794 {
7795 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
7796
7797 if (!ippContainsString(supported, ippGetString(member, 0, NULL)))
7798 {
7799 respond_unsupported(client, attr);
7800 valid = 0;
7801 }
7802 }
7803 }
7804 else if ((member = ippFindAttribute(col, "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
7805 {
7806 if (ippGetCount(member) != 1)
7807 {
7808 respond_unsupported(client, attr);
7809 valid = 0;
7810 }
7811 else
7812 {
7813 size = ippGetCollection(member, 0);
7814
7815 if ((x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(x_dim) != 1 ||
7816 (y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(y_dim) != 1)
7817 {
7818 respond_unsupported(client, attr);
7819 valid = 0;
7820 }
7821 else
7822 {
7823 x_value = ippGetInteger(x_dim, 0);
7824 y_value = ippGetInteger(y_dim, 0);
7825 supported = ippFindAttribute(client->printer->attrs, "media-size-supported", IPP_TAG_BEGIN_COLLECTION);
7826 count = ippGetCount(supported);
7827
7828 for (i = 0; i < count ; i ++)
7829 {
7830 size = ippGetCollection(supported, i);
7831 x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_ZERO);
7832 y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_ZERO);
7833
7834 if (ippContainsInteger(x_dim, x_value) && ippContainsInteger(y_dim, y_value))
7835 break;
7836 }
7837
7838 if (i >= count)
7839 {
7840 respond_unsupported(client, attr);
7841 valid = 0;
7842 }
7843 }
7844 }
7845 }
1106b00e
MS
7846 }
7847
404dde30 7848 if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL)
1106b00e 7849 {
a469f8a5
MS
7850 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
7851 (strcmp(ippGetString(attr, 0, NULL),
1106b00e 7852 "separate-documents-uncollated-copies") &&
a469f8a5 7853 strcmp(ippGetString(attr, 0, NULL),
1106b00e
MS
7854 "separate-documents-collated-copies")))
7855 {
7856 respond_unsupported(client, attr);
a469f8a5 7857 valid = 0;
1106b00e
MS
7858 }
7859 }
7860
404dde30 7861 if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL)
1106b00e 7862 {
a469f8a5
MS
7863 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
7864 ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
7865 ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
1106b00e
MS
7866 {
7867 respond_unsupported(client, attr);
a469f8a5 7868 valid = 0;
1106b00e
MS
7869 }
7870 }
7871
404dde30 7872 if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
1106b00e 7873 {
c2c30ebc
MS
7874 if (ippGetValueTag(attr) != IPP_TAG_RANGE)
7875 {
7876 respond_unsupported(client, attr);
a469f8a5 7877 valid = 0;
c2c30ebc 7878 }
1106b00e
MS
7879 }
7880
404dde30 7881 if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL)
1106b00e 7882 {
a469f8a5
MS
7883 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
7884 ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
7885 ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
1106b00e
MS
7886 {
7887 respond_unsupported(client, attr);
a469f8a5 7888 valid = 0;
1106b00e
MS
7889 }
7890 }
7891
404dde30 7892 if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
1106b00e 7893 {
4a838088 7894 supported = ippFindAttribute(client->printer->attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION);
c2c30ebc
MS
7895
7896 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION ||
7897 !supported)
7898 {
7899 respond_unsupported(client, attr);
7900 valid = 0;
7901 }
7902 else
7903 {
63efa616 7904 int xdpi, /* Horizontal resolution for job template attribute */
4a838088
MS
7905 ydpi, /* Vertical resolution for job template attribute */
7906 sydpi; /* Vertical resolution for supported value */
7907 ipp_res_t units, /* Units for job template attribute */
7908 sunits; /* Units for supported value */
c2c30ebc
MS
7909
7910 xdpi = ippGetResolution(attr, 0, &ydpi, &units);
7911 count = ippGetCount(supported);
7912
7913 for (i = 0; i < count; i ++)
7914 {
7915 if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits)
7916 break;
7917 }
7918
7919 if (i >= count)
7920 {
7921 respond_unsupported(client, attr);
7922 valid = 0;
7923 }
7924 }
1106b00e
MS
7925 }
7926
404dde30 7927 if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
1106b00e 7928 {
404dde30
MS
7929 const char *sides = ippGetString(attr, 0, NULL);
7930 /* "sides" value... */
ad29aeab 7931
a469f8a5 7932 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
1106b00e
MS
7933 {
7934 respond_unsupported(client, attr);
a469f8a5 7935 valid = 0;
1106b00e 7936 }
404dde30 7937 else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
1106b00e 7938 {
ad29aeab 7939 if (!ippContainsString(supported, sides))
1106b00e
MS
7940 {
7941 respond_unsupported(client, attr);
a469f8a5 7942 valid = 0;
1106b00e
MS
7943 }
7944 }
ad29aeab 7945 else if (strcmp(sides, "one-sided"))
1106b00e
MS
7946 {
7947 respond_unsupported(client, attr);
a469f8a5 7948 valid = 0;
1106b00e
MS
7949 }
7950 }
7951
a469f8a5 7952 return (valid);
1106b00e 7953}