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