]> git.ipfire.org Git - thirdparty/cups.git/blame - test/ippeveprinter.c
Update man pages.
[thirdparty/cups.git] / test / ippeveprinter.c
CommitLineData
1106b00e 1/*
d46dbe1b 2 * IPP Everywhere printer application for CUPS.
1106b00e 3 *
d46dbe1b 4 * Copyright © 2010-2019 by Apple Inc.
1106b00e 5 *
b969d5af 6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
60d8f884 7 * information.º
92dad945
MS
8 *
9 * Note: This program began life as the "ippserver" sample code that first
10 * appeared in CUPS 1.4. The name has been changed in order to distinguish it
11 * from the PWG's much more ambitious "ippserver" program, which supports
12 * different kinds of IPP services and multiple services per instance - the
13 * "ippeveprinter" program exposes a single print service conforming to the
1562b9a1 14 * current IPP Everywhere specification, thus the new name.
1106b00e
MS
15 */
16
17/*
18 * Include necessary headers...
19 */
20
92dad945 21#include <cups/cups-private.h>
6641bd0d
MS
22#if !CUPS_LITE
23# include <cups/ppd-private.h>
24#endif /* !CUPS_LITE */
25
015214aa
MS
26#include <limits.h>
27#include <sys/stat.h>
db8b865d 28
24a06ed3 29#ifdef _WIN32
015214aa
MS
30# include <fcntl.h>
31# include <io.h>
32# include <process.h>
33# define WEXITSTATUS(s) (s)
34# include <winsock2.h>
35typedef ULONG nfds_t;
36# define poll WSAPoll
37#else
83ce8172
MS
38extern char **environ;
39
015214aa
MS
40# include <sys/fcntl.h>
41# include <sys/wait.h>
42# include <poll.h>
24a06ed3 43#endif /* _WIN32 */
015214aa 44
0268488e
MS
45#ifdef HAVE_DNSSD
46# include <dns_sd.h>
d6563739
MS
47#elif defined(HAVE_AVAHI)
48# include <avahi-client/client.h>
49# include <avahi-client/publish.h>
50# include <avahi-common/error.h>
51# include <avahi-common/thread-watch.h>
0268488e 52#endif /* HAVE_DNSSD */
1106b00e
MS
53#ifdef HAVE_SYS_MOUNT_H
54# include <sys/mount.h>
55#endif /* HAVE_SYS_MOUNT_H */
56#ifdef HAVE_SYS_STATFS_H
57# include <sys/statfs.h>
58#endif /* HAVE_SYS_STATFS_H */
59#ifdef HAVE_SYS_STATVFS_H
60# include <sys/statvfs.h>
61#endif /* HAVE_SYS_STATVFS_H */
62#ifdef HAVE_SYS_VFS_H
63# include <sys/vfs.h>
64#endif /* HAVE_SYS_VFS_H */
65
3e5092db
MS
66#include "printer-png.h"
67
1106b00e
MS
68
69/*
70 * Constants...
71 */
72
d46dbe1b 73enum ippeve_preason_e /* printer-state-reasons bit values */
1106b00e 74{
d46dbe1b 75 IPPEVE_PREASON_NONE = 0x0000, /* none */
60d8f884 76 IPPEVE_PREASON_OTHER = 0x0001, /* other */
d46dbe1b
MS
77 IPPEVE_PREASON_COVER_OPEN = 0x0002, /* cover-open */
78 IPPEVE_PREASON_INPUT_TRAY_MISSING = 0x0004,
1106b00e 79 /* input-tray-missing */
d46dbe1b 80 IPPEVE_PREASON_MARKER_SUPPLY_EMPTY = 0x0008,
1106b00e 81 /* marker-supply-empty */
d46dbe1b 82 IPPEVE_PREASON_MARKER_SUPPLY_LOW = 0x0010,
0b5ce83f 83 /* marker-supply-low */
d46dbe1b 84 IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL = 0x0020,
1106b00e 85 /* marker-waste-almost-full */
d46dbe1b 86 IPPEVE_PREASON_MARKER_WASTE_FULL = 0x0040,
1106b00e 87 /* marker-waste-full */
d46dbe1b
MS
88 IPPEVE_PREASON_MEDIA_EMPTY = 0x0080, /* media-empty */
89 IPPEVE_PREASON_MEDIA_JAM = 0x0100, /* media-jam */
90 IPPEVE_PREASON_MEDIA_LOW = 0x0200, /* media-low */
91 IPPEVE_PREASON_MEDIA_NEEDED = 0x0400, /* media-needed */
92 IPPEVE_PREASON_MOVING_TO_PAUSED = 0x0800,
1106b00e 93 /* moving-to-paused */
60d8f884 94 IPPEVE_PREASON_PAUSED = 0x1000, /* paused */
d46dbe1b
MS
95 IPPEVE_PREASON_SPOOL_AREA_FULL = 0x2000,/* spool-area-full */
96 IPPEVE_PREASON_TONER_EMPTY = 0x4000, /* toner-empty */
97 IPPEVE_PREASON_TONER_LOW = 0x8000 /* toner-low */
1106b00e 98};
d46dbe1b
MS
99typedef unsigned int ippeve_preason_t; /* Bitfield for printer-state-reasons */
100static const char * const ippeve_preason_strings[] =
9610a474
MS
101{ /* Strings for each bit */
102 /* "none" is implied for no bits set */
103 "other",
104 "cover-open",
105 "input-tray-missing",
106 "marker-supply-empty",
107 "marker-supply-low",
108 "marker-waste-almost-full",
109 "marker-waste-full",
110 "media-empty",
111 "media-jam",
112 "media-low",
113 "media-needed",
114 "moving-to-paused",
115 "paused",
116 "spool-area-full",
117 "toner-empty",
118 "toner-low"
119};
1106b00e 120
4a838088 121
63efa616
MS
122/*
123 * URL scheme for web resources...
124 */
125
126#ifdef HAVE_SSL
127# define WEB_SCHEME "https"
128#else
129# define WEB_SCHEME "http"
130#endif /* HAVE_SSL */
131
1106b00e
MS
132
133/*
134 * Structures...
135 */
136
d6563739 137#ifdef HAVE_DNSSD
d46dbe1b
MS
138typedef DNSServiceRef ippeve_srv_t; /* Service reference */
139typedef TXTRecordRef ippeve_txt_t; /* TXT record */
d6563739
MS
140
141#elif defined(HAVE_AVAHI)
51ddec40
MS
142typedef AvahiEntryGroup *ippeve_srv_t; /* Service reference */
143typedef AvahiStringList *ippeve_txt_t; /* TXT record */
d6563739
MS
144
145#else
51ddec40
MS
146typedef void *ippeve_srv_t; /* Service reference */
147typedef void *ippeve_txt_t; /* TXT record */
d6563739
MS
148#endif /* HAVE_DNSSD */
149
d46dbe1b 150typedef struct ippeve_filter_s /**** Attribute filter ****/
c2c30ebc
MS
151{
152 cups_array_t *ra; /* Requested attributes */
153 ipp_tag_t group_tag; /* Group to copy */
d46dbe1b 154} ippeve_filter_t;
c2c30ebc 155
d46dbe1b 156typedef struct ippeve_job_s ippeve_job_t;
1106b00e 157
d46dbe1b 158typedef struct ippeve_printer_s /**** Printer data ****/
1106b00e 159{
6d56631f 160 /* TODO: One IPv4 and one IPv6 listener are really not sufficient */
1106b00e
MS
161 int ipv4, /* IPv4 listener */
162 ipv6; /* IPv6 listener */
d46dbe1b 163 ippeve_srv_t ipp_ref, /* Bonjour IPP service */
a469f8a5 164 ipps_ref, /* Bonjour IPPS service */
1106b00e
MS
165 http_ref, /* Bonjour HTTP service */
166 printer_ref; /* Bonjour LPD service */
d6563739
MS
167 char *dnssd_name, /* printer-dnssd-name */
168 *name, /* printer-name */
1106b00e
MS
169 *icon, /* Icon filename */
170 *directory, /* Spool directory */
171 *hostname, /* Hostname */
db8b865d 172 *uri, /* printer-uri-supported */
1562b9a1 173 *device_uri, /* Device URI (if any) */
6641bd0d 174#if !CUPS_LITE
1562b9a1 175 *ppdfile, /* PPD file (if any) */
6641bd0d 176#endif /* !CUPS_LITE */
db8b865d 177 *command; /* Command to run with job file */
1106b00e 178 int port; /* Port */
820cb58e 179 int web_forms; /* Enable web interface forms? */
1106b00e
MS
180 size_t urilen; /* Length of printer URI */
181 ipp_t *attrs; /* Static attributes */
4a838088
MS
182 time_t start_time; /* Startup time */
183 time_t config_time; /* printer-config-change-time */
1106b00e 184 ipp_pstate_t state; /* printer-state value */
d46dbe1b 185 ippeve_preason_t state_reasons; /* printer-state-reasons values */
4a838088 186 time_t state_time; /* printer-state-change-time */
1106b00e 187 cups_array_t *jobs; /* Jobs */
d46dbe1b 188 ippeve_job_t *active_job; /* Current active/pending job */
1106b00e
MS
189 int next_job_id; /* Next job-id value */
190 _cups_rwlock_t rwlock; /* Printer lock */
d46dbe1b 191} ippeve_printer_t;
1106b00e 192
d46dbe1b 193struct ippeve_job_s /**** Job data ****/
1106b00e
MS
194{
195 int id; /* Job ID */
a469f8a5 196 const char *name, /* job-name */
1106b00e
MS
197 *username, /* job-originating-user-name */
198 *format; /* document-format */
199 ipp_jstate_t state; /* job-state value */
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);
1899 if (printer->ppdfile)
1900 free(printer->ppdfile);
1106b00e 1901 if (printer->directory)
a469f8a5 1902 free(printer->directory);
1106b00e 1903 if (printer->hostname)
a469f8a5 1904 free(printer->hostname);
1106b00e 1905 if (printer->uri)
a469f8a5 1906 free(printer->uri);
1106b00e
MS
1907
1908 ippDelete(printer->attrs);
1909 cupsArrayDelete(printer->jobs);
1910
1911 free(printer);
1912}
1913
1914
0268488e 1915#ifdef HAVE_DNSSD
1106b00e
MS
1916/*
1917 * 'dnssd_callback()' - Handle Bonjour registration events.
1918 */
1919
726429cb 1920static void DNSSD_API
1106b00e
MS
1921dnssd_callback(
1922 DNSServiceRef sdRef, /* I - Service reference */
1923 DNSServiceFlags flags, /* I - Status flags */
1924 DNSServiceErrorType errorCode, /* I - Error, if any */
1925 const char *name, /* I - Service name */
1926 const char *regtype, /* I - Service type */
1927 const char *domain, /* I - Domain for service */
d46dbe1b 1928 ippeve_printer_t *printer) /* I - Printer */
1106b00e 1929{
7e86f2f6
MS
1930 (void)sdRef;
1931 (void)flags;
1932 (void)domain;
1933
1106b00e
MS
1934 if (errorCode)
1935 {
6d56631f 1936 fprintf(stderr, "DNSServiceRegister for %s failed with error %d.\n", regtype, (int)errorCode);
1106b00e
MS
1937 return;
1938 }
2cadf0f4 1939 else if (strcasecmp(name, printer->dnssd_name))
1106b00e
MS
1940 {
1941 if (Verbosity)
1942 fprintf(stderr, "Now using DNS-SD service name \"%s\".\n", name);
1943
1944 /* No lock needed since only the main thread accesses/changes this */
a469f8a5
MS
1945 free(printer->dnssd_name);
1946 printer->dnssd_name = strdup(name);
1106b00e
MS
1947 }
1948}
d6563739
MS
1949
1950
1951#elif defined(HAVE_AVAHI)
1952/*
1953 * 'dnssd_callback()' - Handle Bonjour registration events.
1954 */
1955
1956static void
1957dnssd_callback(
1958 AvahiEntryGroup *srv, /* I - Service */
1959 AvahiEntryGroupState state, /* I - Registration state */
1960 void *context) /* I - Printer */
1961{
1962 (void)srv;
1963 (void)state;
1964 (void)context;
1965}
1966
1967
1968/*
1969 * 'dnssd_client_cb()' - Client callback for Avahi.
1970 *
1971 * Called whenever the client or server state changes...
1972 */
1973
1974static void
1975dnssd_client_cb(
1976 AvahiClient *c, /* I - Client */
1977 AvahiClientState state, /* I - Current state */
1978 void *userdata) /* I - User data (unused) */
1979{
1980 (void)userdata;
1981
1982 if (!c)
1983 return;
1984
1985 switch (state)
1986 {
1987 default :
6d56631f 1988 fprintf(stderr, "Ignored Avahi state %d.\n", state);
d6563739
MS
1989 break;
1990
1991 case AVAHI_CLIENT_FAILURE:
1992 if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
1993 {
1994 fputs("Avahi server crashed, exiting.\n", stderr);
1995 exit(1);
1996 }
1997 break;
1998 }
1999}
2000#endif /* HAVE_DNSSD */
2001
2002
2003/*
2004 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2005 */
2006
2007static void
2008dnssd_init(void)
2009{
2010#ifdef HAVE_DNSSD
2011 if (DNSServiceCreateConnection(&DNSSDMaster) != kDNSServiceErr_NoError)
2012 {
2013 fputs("Error: Unable to initialize Bonjour.\n", stderr);
2014 exit(1);
2015 }
2016
2017#elif defined(HAVE_AVAHI)
2018 int error; /* Error code, if any */
2019
2020 if ((DNSSDMaster = avahi_threaded_poll_new()) == NULL)
2021 {
2022 fputs("Error: Unable to initialize Bonjour.\n", stderr);
2023 exit(1);
2024 }
2025
2026 if ((DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssd_client_cb, NULL, &error)) == NULL)
2027 {
2028 fputs("Error: Unable to initialize Bonjour.\n", stderr);
2029 exit(1);
2030 }
2031
2032 avahi_threaded_poll_start(DNSSDMaster);
0268488e 2033#endif /* HAVE_DNSSD */
d6563739 2034}
1106b00e
MS
2035
2036
c2c30ebc
MS
2037/*
2038 * 'filter_cb()' - Filter printer attributes based on the requested array.
2039 */
2040
2041static int /* O - 1 to copy, 0 to ignore */
d46dbe1b 2042filter_cb(ippeve_filter_t *filter, /* I - Filter parameters */
c2c30ebc
MS
2043 ipp_t *dst, /* I - Destination (unused) */
2044 ipp_attribute_t *attr) /* I - Source attribute */
2045{
2046 /*
2047 * Filter attributes as needed...
2048 */
2049
24a06ed3 2050#ifndef _WIN32 /* Avoid MS compiler bug */
d6563739 2051 (void)dst;
24a06ed3 2052#endif /* !_WIN32 */
4a838088 2053
c2c30ebc
MS
2054 ipp_tag_t group = ippGetGroupTag(attr);
2055 const char *name = ippGetName(attr);
2056
2057 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)))
2058 return (0);
2059
2060 return (!filter->ra || cupsArrayFind(filter->ra, (void *)name) != NULL);
2061}
2062
2063
1106b00e
MS
2064/*
2065 * 'find_job()' - Find a job specified in a request.
2066 */
2067
d46dbe1b
MS
2068static ippeve_job_t * /* O - Job or NULL */
2069find_job(ippeve_client_t *client) /* I - Client */
1106b00e
MS
2070{
2071 ipp_attribute_t *attr; /* job-id or job-uri attribute */
d46dbe1b 2072 ippeve_job_t key, /* Job search key */
1106b00e
MS
2073 *job; /* Matching job, if any */
2074
2075
4a838088 2076 if ((attr = ippFindAttribute(client->request, "job-uri", IPP_TAG_URI)) != NULL)
1106b00e 2077 {
a469f8a5
MS
2078 const char *uri = ippGetString(attr, 0, NULL);
2079
2080 if (!strncmp(uri, client->printer->uri, client->printer->urilen) &&
2081 uri[client->printer->urilen] == '/')
2082 key.id = atoi(uri + client->printer->urilen + 1);
4a838088
MS
2083 else
2084 return (NULL);
1106b00e 2085 }
4a838088 2086 else if ((attr = ippFindAttribute(client->request, "job-id", IPP_TAG_INTEGER)) != NULL)
a469f8a5 2087 key.id = ippGetInteger(attr, 0);
1106b00e
MS
2088
2089 _cupsRWLockRead(&(client->printer->rwlock));
d46dbe1b 2090 job = (ippeve_job_t *)cupsArrayFind(client->printer->jobs, &key);
1106b00e
MS
2091 _cupsRWUnlock(&(client->printer->rwlock));
2092
2093 return (job);
2094}
2095
2096
cdb2e724
MS
2097/*
2098 * 'finish_document()' - Finish receiving a document file and start processing.
2099 */
2100
2101static void
a5ed2e39
MS
2102finish_document_data(
2103 ippeve_client_t *client, /* I - Client */
2104 ippeve_job_t *job) /* I - Job */
cdb2e724
MS
2105{
2106 char filename[1024], /* Filename buffer */
2107 buffer[4096]; /* Copy buffer */
2108 ssize_t bytes; /* Bytes read */
2109 cups_array_t *ra; /* Attributes to send in response */
2110 _cups_thread_t t; /* Thread */
2111
2112
2113 /*
2114 * Create a file for the request data...
2115 *
2116 * TODO: Update code to support piping large raster data to the print command.
2117 */
2118
dd2b6166 2119 if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0)
cdb2e724 2120 {
cdb2e724 2121 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno));
a5ed2e39
MS
2122
2123 goto abort_job;
cdb2e724
MS
2124 }
2125
2126 if (Verbosity)
2127 fprintf(stderr, "Created job file \"%s\", format \"%s\".\n", filename, job->format);
2128
2129 while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
2130 {
2131 if (write(job->fd, buffer, (size_t)bytes) < bytes)
2132 {
2133 int error = errno; /* Write error */
2134
cdb2e724
MS
2135 close(job->fd);
2136 job->fd = -1;
2137
2138 unlink(filename);
2139
2140 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
a5ed2e39
MS
2141
2142 goto abort_job;
cdb2e724
MS
2143 }
2144 }
2145
2146 if (bytes < 0)
2147 {
2148 /*
2149 * Got an error while reading the print data, so abort this job.
2150 */
2151
cdb2e724
MS
2152 close(job->fd);
2153 job->fd = -1;
2154
2155 unlink(filename);
2156
2157 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to read print file.");
a5ed2e39
MS
2158
2159 goto abort_job;
cdb2e724
MS
2160 }
2161
2162 if (close(job->fd))
2163 {
2164 int error = errno; /* Write error */
2165
a5ed2e39 2166 job->fd = -1;
cdb2e724
MS
2167
2168 unlink(filename);
2169
2170 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
a5ed2e39
MS
2171
2172 goto abort_job;
cdb2e724
MS
2173 }
2174
2175 job->fd = -1;
2176 job->filename = strdup(filename);
2177 job->state = IPP_JSTATE_PENDING;
2178
2179 /*
2180 * Process the job...
2181 */
2182
2183 t = _cupsThreadCreate((_cups_thread_func_t)process_job, job);
2184
2185 if (t)
2186 {
2187 _cupsThreadDetach(t);
2188 }
2189 else
2190 {
cdb2e724 2191 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
a5ed2e39 2192 goto abort_job;
cdb2e724
MS
2193 }
2194
2195 /*
2196 * Return the job info...
2197 */
2198
2199 respond_ipp(client, IPP_STATUS_OK, NULL);
2200
2201 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2202 cupsArrayAdd(ra, "job-id");
2203 cupsArrayAdd(ra, "job-state");
2204 cupsArrayAdd(ra, "job-state-message");
2205 cupsArrayAdd(ra, "job-state-reasons");
2206 cupsArrayAdd(ra, "job-uri");
2207
a5ed2e39
MS
2208 copy_job_attributes(client, job, ra);
2209 cupsArrayDelete(ra);
2210 return;
2211
2212 /*
2213 * If we get here we had to abort the job...
2214 */
2215
2216 abort_job:
2217
2218 job->state = IPP_JSTATE_ABORTED;
2219 job->completed = time(NULL);
2220
2221 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2222 cupsArrayAdd(ra, "job-id");
2223 cupsArrayAdd(ra, "job-state");
2224 cupsArrayAdd(ra, "job-state-reasons");
2225 cupsArrayAdd(ra, "job-uri");
2226
cdb2e724
MS
2227 copy_job_attributes(client, job, ra);
2228 cupsArrayDelete(ra);
2229}
2230
2231
2232/*
2233 * 'finish_uri()' - Finish fetching a document URI and start processing.
2234 */
2235
2236static void
a5ed2e39
MS
2237finish_document_uri(
2238 ippeve_client_t *client, /* I - Client */
2239 ippeve_job_t *job) /* I - Job */
cdb2e724 2240{
a5ed2e39
MS
2241 ipp_attribute_t *uri; /* document-uri */
2242 char scheme[256], /* URI scheme */
2243 userpass[256], /* Username and password info */
2244 hostname[256], /* Hostname */
2245 resource[1024]; /* Resource path */
2246 int port; /* Port number */
2247 http_uri_status_t uri_status; /* URI decode status */
2248 http_encryption_t encryption; /* Encryption to use, if any */
2249 http_t *http; /* Connection for http/https URIs */
2250 http_status_t status; /* Access status for http/https URIs */
2251 int infile; /* Input file for local file URIs */
2252 char filename[1024], /* Filename buffer */
2253 buffer[4096]; /* Copy buffer */
2254 ssize_t bytes; /* Bytes read */
2255 ipp_attribute_t *attr; /* Current attribute */
2256 cups_array_t *ra; /* Attributes to send in response */
2257
2258
2259 /*
2260 * Do we have a file to print?
2261 */
2262
2263 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
2264 {
2265 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Unexpected document data following request.");
2266
2267 goto abort_job;
2268 }
2269
2270 /*
2271 * Do we have a document URI?
2272 */
2273
2274 if ((uri = ippFindAttribute(client->request, "document-uri", IPP_TAG_URI)) == NULL)
2275 {
2276 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
2277
2278 goto abort_job;
2279 }
2280
2281 if (ippGetCount(uri) != 1)
2282 {
2283 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Too many document-uri values.");
2284
2285 goto abort_job;
2286 }
2287
2288 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
2289 scheme, sizeof(scheme), userpass,
2290 sizeof(userpass), hostname, sizeof(hostname),
2291 &port, resource, sizeof(resource));
2292 if (uri_status < HTTP_URI_STATUS_OK)
2293 {
2294 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s", httpURIStatusString(uri_status));
2295
2296 goto abort_job;
2297 }
2298
2299 if (strcmp(scheme, "file") &&
2300#ifdef HAVE_SSL
2301 strcmp(scheme, "https") &&
2302#endif /* HAVE_SSL */
2303 strcmp(scheme, "http"))
2304 {
2305 respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME, "URI scheme \"%s\" not supported.", scheme);
2306
2307 goto abort_job;
2308 }
2309
2310 if (!strcmp(scheme, "file") && access(resource, R_OK))
2311 {
2312 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno));
2313
2314 goto abort_job;
2315 }
2316
2317 /*
2318 * Get the document format for the job...
2319 */
2320
2321 _cupsRWLockWrite(&(client->printer->rwlock));
2322
2323 if ((attr = ippFindAttribute(job->attrs, "document-format", IPP_TAG_MIMETYPE)) != NULL)
2324 job->format = ippGetString(attr, 0, NULL);
2325 else
2326 job->format = "application/octet-stream";
2327
2328 /*
2329 * Create a file for the request data...
2330 */
2331
dd2b6166 2332 if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0)
a5ed2e39
MS
2333 {
2334 _cupsRWUnlock(&(client->printer->rwlock));
2335
2336 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno));
2337
2338 goto abort_job;
2339 }
2340
2341 _cupsRWUnlock(&(client->printer->rwlock));
2342
2343 if (!strcmp(scheme, "file"))
2344 {
2345 if ((infile = open(resource, O_RDONLY)) < 0)
2346 {
2347 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno));
2348
2349 goto abort_job;
2350 }
2351
2352 do
2353 {
2354 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
2355 (errno == EAGAIN || errno == EINTR))
2356 {
2357 bytes = 1;
2358 }
2359 else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes)
2360 {
2361 int error = errno; /* Write error */
2362
2363 close(job->fd);
2364 job->fd = -1;
2365
2366 unlink(filename);
2367 close(infile);
2368
2369 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2370
2371 goto abort_job;
2372 }
2373 }
2374 while (bytes > 0);
2375
2376 close(infile);
2377 }
2378 else
2379 {
2380#ifdef HAVE_SSL
2381 if (port == 443 || !strcmp(scheme, "https"))
2382 encryption = HTTP_ENCRYPTION_ALWAYS;
2383 else
2384#endif /* HAVE_SSL */
2385 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
2386
2387 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL)
2388 {
2389 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to connect to %s: %s", hostname, cupsLastErrorString());
2390
2391 close(job->fd);
2392 job->fd = -1;
2393
2394 unlink(filename);
2395
2396 goto abort_job;
2397 }
2398
2399 httpClearFields(http);
2400 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
2401 if (httpGet(http, resource))
2402 {
2403 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", strerror(errno));
2404
2405 close(job->fd);
2406 job->fd = -1;
2407
2408 unlink(filename);
2409 httpClose(http);
2410
2411 goto abort_job;
2412 }
2413
2414 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
2415
2416 if (status != HTTP_STATUS_OK)
2417 {
2418 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", httpStatus(status));
2419
2420 close(job->fd);
2421 job->fd = -1;
2422
2423 unlink(filename);
2424 httpClose(http);
2425
2426 goto abort_job;
2427 }
2428
2429 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
2430 {
2431 if (write(job->fd, buffer, (size_t)bytes) < bytes)
2432 {
2433 int error = errno; /* Write error */
2434
2435 close(job->fd);
2436 job->fd = -1;
2437
2438 unlink(filename);
2439 httpClose(http);
2440
2441 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
2442 "Unable to write print file: %s", strerror(error));
2443
2444 goto abort_job;
2445 }
2446 }
2447
2448 httpClose(http);
2449 }
2450
2451 if (close(job->fd))
2452 {
2453 int error = errno; /* Write error */
2454
2455 job->fd = -1;
2456
2457 unlink(filename);
2458
2459 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2460
2461 goto abort_job;
2462 }
2463
2464 _cupsRWLockWrite(&(client->printer->rwlock));
2465
2466 job->fd = -1;
2467 job->filename = strdup(filename);
2468 job->state = IPP_JSTATE_PENDING;
2469
2470 _cupsRWUnlock(&(client->printer->rwlock));
2471
2472 /*
2473 * Process the job...
2474 */
2475
2476 process_job(job);
2477
2478 /*
2479 * Return the job info...
2480 */
2481
2482 respond_ipp(client, IPP_STATUS_OK, NULL);
2483
2484 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2485 cupsArrayAdd(ra, "job-id");
2486 cupsArrayAdd(ra, "job-state");
2487 cupsArrayAdd(ra, "job-state-reasons");
2488 cupsArrayAdd(ra, "job-uri");
2489
2490 copy_job_attributes(client, job, ra);
2491 cupsArrayDelete(ra);
2492 return;
2493
2494 /*
2495 * If we get here we had to abort the job...
2496 */
2497
2498 abort_job:
2499
2500 job->state = IPP_JSTATE_ABORTED;
2501 job->completed = time(NULL);
2502
2503 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2504 cupsArrayAdd(ra, "job-id");
2505 cupsArrayAdd(ra, "job-state");
2506 cupsArrayAdd(ra, "job-state-reasons");
2507 cupsArrayAdd(ra, "job-uri");
2508
2509 copy_job_attributes(client, job, ra);
2510 cupsArrayDelete(ra);
cdb2e724
MS
2511}
2512
2513
1106b00e
MS
2514/*
2515 * 'html_escape()' - Write a HTML-safe string.
2516 */
2517
2518static void
d46dbe1b 2519html_escape(ippeve_client_t *client, /* I - Client */
1106b00e
MS
2520 const char *s, /* I - String to write */
2521 size_t slen) /* I - Number of characters to write */
2522{
2523 const char *start, /* Start of segment */
2524 *end; /* End of string */
2525
2526
2527 start = s;
2528 end = s + (slen > 0 ? slen : strlen(s));
2529
2530 while (*s && s < end)
2531 {
2532 if (*s == '&' || *s == '<')
2533 {
2534 if (s > start)
7e86f2f6 2535 httpWrite2(client->http, start, (size_t)(s - start));
1106b00e
MS
2536
2537 if (*s == '&')
a469f8a5 2538 httpWrite2(client->http, "&amp;", 5);
1106b00e 2539 else
a469f8a5 2540 httpWrite2(client->http, "&lt;", 4);
1106b00e
MS
2541
2542 start = s + 1;
2543 }
2544
2545 s ++;
2546 }
2547
2548 if (s > start)
7e86f2f6 2549 httpWrite2(client->http, start, (size_t)(s - start));
1106b00e
MS
2550}
2551
2552
0b5ce83f
MS
2553/*
2554 * 'html_footer()' - Show the web interface footer.
2555 *
2556 * This function also writes the trailing 0-length chunk.
2557 */
2558
2559static void
d46dbe1b 2560html_footer(ippeve_client_t *client) /* I - Client */
0b5ce83f
MS
2561{
2562 html_printf(client,
2563 "</div>\n"
2564 "</body>\n"
2565 "</html>\n");
2566 httpWrite2(client->http, "", 0);
2567}
2568
2569
2570/*
2571 * 'html_header()' - Show the web interface header and title.
2572 */
2573
2574static void
d46dbe1b 2575html_header(ippeve_client_t *client, /* I - Client */
6640ceec
MS
2576 const char *title, /* I - Title */
2577 int refresh) /* I - Refresh timer, if any */
0b5ce83f
MS
2578{
2579 html_printf(client,
2580 "<!doctype html>\n"
2581 "<html>\n"
2582 "<head>\n"
2583 "<title>%s</title>\n"
2584 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
04f71e64 2585 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
6640ceec
MS
2586 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n", title);
2587 if (refresh > 0)
2588 html_printf(client, "<meta http-equiv=\"refresh\" content=\"%d\">\n", refresh);
2589 html_printf(client,
0b5ce83f
MS
2590 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2591 "<style>\n"
2592 "body { font-family: sans-serif; margin: 0; }\n"
2593 "div.body { padding: 0px 10px 10px; }\n"
820cb58e
MS
2594 "span.badge { background: #090; border-radius: 5px; color: #fff; padding: 5px 10px; }\n"
2595 "span.bar { box-shadow: 0px 1px 5px #333; font-size: 75%%; }\n"
2596 "table.form { border-collapse: collapse; margin-left: auto; margin-right: auto; margin-top: 10px; width: auto; }\n"
2597 "table.form td, table.form th { padding: 5px 2px; }\n"
2598 "table.form td.meter { border-right: solid 1px #ccc; padding: 0px; width: 400px; }\n"
0b5ce83f
MS
2599 "table.form th { text-align: right; }\n"
2600 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2601 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2602 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2603 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2604 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2605 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2606 "table.nav td { margin: 0; text-align: center; }\n"
2607 "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"
2608 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2609 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2610 "td.nav:hover { background: #666; color: #fff; }\n"
2611 "td.nav:active { background: #000; color: #ff0; }\n"
2612 "</style>\n"
2613 "</head>\n"
2614 "<body>\n"
2615 "<table class=\"nav\"><tr>"
2616 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2617 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2618 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2619 "</tr></table>\n"
6640ceec 2620 "<div class=\"body\">\n", !strcmp(client->uri, "/") ? " sel" : "", !strcmp(client->uri, "/supplies") ? " sel" : "", !strcmp(client->uri, "/media") ? " sel" : "");
0b5ce83f
MS
2621}
2622
2623
1106b00e
MS
2624/*
2625 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2626 */
2627
2628static void
d46dbe1b 2629html_printf(ippeve_client_t *client, /* I - Client */
1106b00e
MS
2630 const char *format, /* I - Printf-style format string */
2631 ...) /* I - Additional arguments as needed */
2632{
2633 va_list ap; /* Pointer to arguments */
2634 const char *start; /* Start of string */
2635 char size, /* Size character (h, l, L) */
2636 type; /* Format type character */
2637 int width, /* Width of field */
2638 prec; /* Number of characters of precision */
2639 char tformat[100], /* Temporary format string for sprintf() */
2640 *tptr, /* Pointer into temporary format */
2641 temp[1024]; /* Buffer for formatted numbers */
2642 char *s; /* Pointer to string */
2643
2644
2645 /*
2646 * Loop through the format string, formatting as needed...
2647 */
2648
2649 va_start(ap, format);
2650 start = format;
2651
2652 while (*format)
2653 {
2654 if (*format == '%')
2655 {
2656 if (format > start)
7e86f2f6 2657 httpWrite2(client->http, start, (size_t)(format - start));
1106b00e
MS
2658
2659 tptr = tformat;
2660 *tptr++ = *format++;
2661
2662 if (*format == '%')
2663 {
a469f8a5 2664 httpWrite2(client->http, "%", 1);
1106b00e 2665 format ++;
0b5ce83f 2666 start = format;
1106b00e
MS
2667 continue;
2668 }
2669 else if (strchr(" -+#\'", *format))
2670 *tptr++ = *format++;
2671
2672 if (*format == '*')
2673 {
2674 /*
2675 * Get width from argument...
2676 */
2677
2678 format ++;
2679 width = va_arg(ap, int);
2680
7e86f2f6 2681 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
1106b00e
MS
2682 tptr += strlen(tptr);
2683 }
2684 else
2685 {
2686 width = 0;
2687
2688 while (isdigit(*format & 255))
2689 {
2690 if (tptr < (tformat + sizeof(tformat) - 1))
2691 *tptr++ = *format;
2692
2693 width = width * 10 + *format++ - '0';
2694 }
2695 }
2696
2697 if (*format == '.')
2698 {
2699 if (tptr < (tformat + sizeof(tformat) - 1))
2700 *tptr++ = *format;
2701
2702 format ++;
2703
2704 if (*format == '*')
2705 {
2706 /*
2707 * Get precision from argument...
2708 */
2709
2710 format ++;
2711 prec = va_arg(ap, int);
2712
7e86f2f6 2713 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
1106b00e
MS
2714 tptr += strlen(tptr);
2715 }
2716 else
2717 {
2718 prec = 0;
2719
2720 while (isdigit(*format & 255))
2721 {
2722 if (tptr < (tformat + sizeof(tformat) - 1))
2723 *tptr++ = *format;
2724
2725 prec = prec * 10 + *format++ - '0';
2726 }
2727 }
2728 }
2729
2730 if (*format == 'l' && format[1] == 'l')
2731 {
2732 size = 'L';
2733
2734 if (tptr < (tformat + sizeof(tformat) - 2))
2735 {
2736 *tptr++ = 'l';
2737 *tptr++ = 'l';
2738 }
2739
2740 format += 2;
2741 }
2742 else if (*format == 'h' || *format == 'l' || *format == 'L')
2743 {
2744 if (tptr < (tformat + sizeof(tformat) - 1))
2745 *tptr++ = *format;
2746
2747 size = *format++;
2748 }
2749 else
2750 size = 0;
2751
2752
2753 if (!*format)
2754 {
2755 start = format;
2756 break;
2757 }
2758
2759 if (tptr < (tformat + sizeof(tformat) - 1))
2760 *tptr++ = *format;
2761
2762 type = *format++;
2763 *tptr = '\0';
2764 start = format;
2765
2766 switch (type)
2767 {
2768 case 'E' : /* Floating point formats */
2769 case 'G' :
2770 case 'e' :
2771 case 'f' :
2772 case 'g' :
7e86f2f6 2773 if ((size_t)(width + 2) > sizeof(temp))
1106b00e
MS
2774 break;
2775
2776 sprintf(temp, tformat, va_arg(ap, double));
2777
a469f8a5 2778 httpWrite2(client->http, temp, strlen(temp));
1106b00e
MS
2779 break;
2780
2781 case 'B' : /* Integer formats */
2782 case 'X' :
2783 case 'b' :
2784 case 'd' :
2785 case 'i' :
2786 case 'o' :
2787 case 'u' :
2788 case 'x' :
7e86f2f6 2789 if ((size_t)(width + 2) > sizeof(temp))
1106b00e
MS
2790 break;
2791
2792# ifdef HAVE_LONG_LONG
2793 if (size == 'L')
2794 sprintf(temp, tformat, va_arg(ap, long long));
2795 else
2796# endif /* HAVE_LONG_LONG */
2797 if (size == 'l')
2798 sprintf(temp, tformat, va_arg(ap, long));
2799 else
2800 sprintf(temp, tformat, va_arg(ap, int));
2801
a469f8a5 2802 httpWrite2(client->http, temp, strlen(temp));
1106b00e
MS
2803 break;
2804
2805 case 'p' : /* Pointer value */
7e86f2f6 2806 if ((size_t)(width + 2) > sizeof(temp))
1106b00e
MS
2807 break;
2808
2809 sprintf(temp, tformat, va_arg(ap, void *));
2810
a469f8a5 2811 httpWrite2(client->http, temp, strlen(temp));
1106b00e
MS
2812 break;
2813
2814 case 'c' : /* Character or character array */
2815 if (width <= 1)
2816 {
7e86f2f6 2817 temp[0] = (char)va_arg(ap, int);
1106b00e
MS
2818 temp[1] = '\0';
2819 html_escape(client, temp, 1);
2820 }
2821 else
2822 html_escape(client, va_arg(ap, char *), (size_t)width);
2823 break;
2824
2825 case 's' : /* String */
2826 if ((s = va_arg(ap, char *)) == NULL)
2827 s = "(null)";
2828
2829 html_escape(client, s, strlen(s));
2830 break;
2831 }
2832 }
2833 else
2834 format ++;
2835 }
2836
2837 if (format > start)
7e86f2f6 2838 httpWrite2(client->http, start, (size_t)(format - start));
1106b00e
MS
2839
2840 va_end(ap);
2841}
2842
2843
2844/*
2845 * 'ipp_cancel_job()' - Cancel a job.
2846 */
2847
2848static void
d46dbe1b 2849ipp_cancel_job(ippeve_client_t *client) /* I - Client */
1106b00e 2850{
d46dbe1b 2851 ippeve_job_t *job; /* Job information */
1106b00e
MS
2852
2853
2854 /*
2855 * Get the job...
2856 */
2857
2858 if ((job = find_job(client)) == NULL)
83e08001 2859 {
a469f8a5 2860 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
1106b00e 2861 return;
83e08001 2862 }
1106b00e
MS
2863
2864 /*
2865 * See if the job is already completed, canceled, or aborted; if so,
2866 * we can't cancel...
2867 */
2868
2869 switch (job->state)
2870 {
a469f8a5
MS
2871 case IPP_JSTATE_CANCELED :
2872 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
1106b00e
MS
2873 "Job #%d is already canceled - can\'t cancel.", job->id);
2874 break;
2875
a469f8a5
MS
2876 case IPP_JSTATE_ABORTED :
2877 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
1106b00e
MS
2878 "Job #%d is already aborted - can\'t cancel.", job->id);
2879 break;
2880
a469f8a5
MS
2881 case IPP_JSTATE_COMPLETED :
2882 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
1106b00e
MS
2883 "Job #%d is already completed - can\'t cancel.", job->id);
2884 break;
2885
2886 default :
2887 /*
2888 * Cancel the job...
2889 */
2890
2891 _cupsRWLockWrite(&(client->printer->rwlock));
2892
a469f8a5
MS
2893 if (job->state == IPP_JSTATE_PROCESSING ||
2894 (job->state == IPP_JSTATE_HELD && job->fd >= 0))
1106b00e
MS
2895 job->cancel = 1;
2896 else
2897 {
a469f8a5 2898 job->state = IPP_JSTATE_CANCELED;
1106b00e
MS
2899 job->completed = time(NULL);
2900 }
2901
2902 _cupsRWUnlock(&(client->printer->rwlock));
2903
a469f8a5 2904 respond_ipp(client, IPP_STATUS_OK, NULL);
1106b00e
MS
2905 break;
2906 }
2907}
2908
2909
2cadf0f4
MS
2910/*
2911 * 'ipp_close_job()' - Close an open job.
2912 */
2913
2914static void
d46dbe1b 2915ipp_close_job(ippeve_client_t *client) /* I - Client */
2cadf0f4 2916{
d46dbe1b 2917 ippeve_job_t *job; /* Job information */
2cadf0f4
MS
2918
2919
2920 /*
2921 * Get the job...
2922 */
2923
2924 if ((job = find_job(client)) == NULL)
2925 {
2926 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
2927 return;
2928 }
2929
2930 /*
2931 * See if the job is already completed, canceled, or aborted; if so,
2932 * we can't cancel...
2933 */
2934
2935 switch (job->state)
2936 {
2937 case IPP_JSTATE_CANCELED :
2938 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2939 "Job #%d is canceled - can\'t close.", job->id);
2940 break;
2941
2942 case IPP_JSTATE_ABORTED :
2943 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2944 "Job #%d is aborted - can\'t close.", job->id);
2945 break;
2946
2947 case IPP_JSTATE_COMPLETED :
2948 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2949 "Job #%d is completed - can\'t close.", job->id);
2950 break;
2951
2952 case IPP_JSTATE_PROCESSING :
2953 case IPP_JSTATE_STOPPED :
2954 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2955 "Job #%d is already closed.", job->id);
2956 break;
2957
2958 default :
2959 respond_ipp(client, IPP_STATUS_OK, NULL);
2960 break;
2961 }
2962}
2963
2964
1106b00e
MS
2965/*
2966 * 'ipp_create_job()' - Create a job object.
2967 */
2968
2969static void
d46dbe1b 2970ipp_create_job(ippeve_client_t *client) /* I - Client */
1106b00e 2971{
d46dbe1b 2972 ippeve_job_t *job; /* New job */
83e08001
MS
2973 cups_array_t *ra; /* Attributes to send in response */
2974
2975
2976 /*
2977 * Validate print job attributes...
2978 */
2979
2980 if (!valid_job_attributes(client))
2981 {
a469f8a5 2982 httpFlush(client->http);
83e08001
MS
2983 return;
2984 }
2985
2986 /*
2987 * Do we have a file to print?
2988 */
2989
a469f8a5 2990 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
83e08001 2991 {
a469f8a5 2992 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
83e08001
MS
2993 "Unexpected document data following request.");
2994 return;
2995 }
2996
2997 /*
2998 * Create the job...
2999 */
3000
3001 if ((job = create_job(client)) == NULL)
3002 {
a469f8a5
MS
3003 respond_ipp(client, IPP_STATUS_ERROR_BUSY,
3004 "Currently printing another job.");
83e08001
MS
3005 return;
3006 }
3007
3008 /*
3009 * Return the job info...
3010 */
3011
a469f8a5 3012 respond_ipp(client, IPP_STATUS_OK, NULL);
83e08001
MS
3013
3014 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3015 cupsArrayAdd(ra, "job-id");
3016 cupsArrayAdd(ra, "job-state");
c2c30ebc 3017 cupsArrayAdd(ra, "job-state-message");
83e08001
MS
3018 cupsArrayAdd(ra, "job-state-reasons");
3019 cupsArrayAdd(ra, "job-uri");
3020
3021 copy_job_attributes(client, job, ra);
3022 cupsArrayDelete(ra);
1106b00e 3023}
1106b00e
MS
3024
3025
3026/*
3027 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3028 */
3029
3030static void
3031ipp_get_job_attributes(
d46dbe1b 3032 ippeve_client_t *client) /* I - Client */
1106b00e 3033{
d46dbe1b 3034 ippeve_job_t *job; /* Job */
1106b00e
MS
3035 cups_array_t *ra; /* requested-attributes */
3036
3037
3038 if ((job = find_job(client)) == NULL)
3039 {
a469f8a5 3040 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job not found.");
1106b00e
MS
3041 return;
3042 }
3043
a469f8a5 3044 respond_ipp(client, IPP_STATUS_OK, NULL);
1106b00e 3045
db8b865d 3046 ra = ippCreateRequestedArray(client->request);
1106b00e
MS
3047 copy_job_attributes(client, job, ra);
3048 cupsArrayDelete(ra);
3049}
3050
3051
3052/*
3053 * 'ipp_get_jobs()' - Get a list of job objects.
3054 */
3055
3056static void
d46dbe1b 3057ipp_get_jobs(ippeve_client_t *client) /* I - Client */
1106b00e
MS
3058{
3059 ipp_attribute_t *attr; /* Current attribute */
a469f8a5
MS
3060 const char *which_jobs = NULL;
3061 /* which-jobs values */
1106b00e
MS
3062 int job_comparison; /* Job comparison */
3063 ipp_jstate_t job_state; /* job-state value */
3064 int first_job_id, /* First job ID */
3065 limit, /* Maximum number of jobs to return */
3066 count; /* Number of jobs that match */
3067 const char *username; /* Username */
d46dbe1b 3068 ippeve_job_t *job; /* Current job pointer */
1106b00e
MS
3069 cups_array_t *ra; /* Requested attributes array */
3070
3071
3072 /*
3073 * See if the "which-jobs" attribute have been specified...
3074 */
3075
3076 if ((attr = ippFindAttribute(client->request, "which-jobs",
3077 IPP_TAG_KEYWORD)) != NULL)
a469f8a5
MS
3078 {
3079 which_jobs = ippGetString(attr, 0, NULL);
3080 fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->hostname, which_jobs);
3081 }
1106b00e 3082
a469f8a5 3083 if (!which_jobs || !strcmp(which_jobs, "not-completed"))
1106b00e
MS
3084 {
3085 job_comparison = -1;
a469f8a5 3086 job_state = IPP_JSTATE_STOPPED;
1106b00e 3087 }
a469f8a5 3088 else if (!strcmp(which_jobs, "completed"))
1106b00e
MS
3089 {
3090 job_comparison = 1;
a469f8a5 3091 job_state = IPP_JSTATE_CANCELED;
1106b00e 3092 }
a469f8a5 3093 else if (!strcmp(which_jobs, "aborted"))
1106b00e
MS
3094 {
3095 job_comparison = 0;
a469f8a5 3096 job_state = IPP_JSTATE_ABORTED;
1106b00e 3097 }
a469f8a5 3098 else if (!strcmp(which_jobs, "all"))
1106b00e
MS
3099 {
3100 job_comparison = 1;
a469f8a5 3101 job_state = IPP_JSTATE_PENDING;
1106b00e 3102 }
a469f8a5 3103 else if (!strcmp(which_jobs, "canceled"))
1106b00e
MS
3104 {
3105 job_comparison = 0;
a469f8a5 3106 job_state = IPP_JSTATE_CANCELED;
1106b00e 3107 }
a469f8a5 3108 else if (!strcmp(which_jobs, "pending"))
1106b00e
MS
3109 {
3110 job_comparison = 0;
a469f8a5 3111 job_state = IPP_JSTATE_PENDING;
1106b00e 3112 }
a469f8a5 3113 else if (!strcmp(which_jobs, "pending-held"))
1106b00e
MS
3114 {
3115 job_comparison = 0;
a469f8a5 3116 job_state = IPP_JSTATE_HELD;
1106b00e 3117 }
a469f8a5 3118 else if (!strcmp(which_jobs, "processing"))
1106b00e
MS
3119 {
3120 job_comparison = 0;
a469f8a5 3121 job_state = IPP_JSTATE_PROCESSING;
1106b00e 3122 }
a469f8a5 3123 else if (!strcmp(which_jobs, "processing-stopped"))
1106b00e
MS
3124 {
3125 job_comparison = 0;
a469f8a5 3126 job_state = IPP_JSTATE_STOPPED;
1106b00e
MS
3127 }
3128 else
3129 {
a469f8a5
MS
3130 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
3131 "The which-jobs value \"%s\" is not supported.", which_jobs);
1106b00e 3132 ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
a469f8a5 3133 "which-jobs", NULL, which_jobs);
1106b00e
MS
3134 return;
3135 }
3136
3137 /*
3138 * See if they want to limit the number of jobs reported...
3139 */
3140
3141 if ((attr = ippFindAttribute(client->request, "limit",
3142 IPP_TAG_INTEGER)) != NULL)
3143 {
a469f8a5 3144 limit = ippGetInteger(attr, 0);
1106b00e 3145
a469f8a5 3146 fprintf(stderr, "%s Get-Jobs limit=%d", client->hostname, limit);
1106b00e
MS
3147 }
3148 else
3149 limit = 0;
3150
3151 if ((attr = ippFindAttribute(client->request, "first-job-id",
3152 IPP_TAG_INTEGER)) != NULL)
3153 {
a469f8a5 3154 first_job_id = ippGetInteger(attr, 0);
1106b00e 3155
6d56631f 3156 fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->hostname, first_job_id);
1106b00e
MS
3157 }
3158 else
3159 first_job_id = 1;
3160
3161 /*
3162 * See if we only want to see jobs for a specific user...
3163 */
3164
3165 username = NULL;
3166
3167 if ((attr = ippFindAttribute(client->request, "my-jobs",
3168 IPP_TAG_BOOLEAN)) != NULL)
3169 {
a469f8a5
MS
3170 int my_jobs = ippGetBoolean(attr, 0);
3171
6d56631f 3172 fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->hostname, my_jobs ? "true" : "false");
1106b00e 3173
a469f8a5 3174 if (my_jobs)
1106b00e
MS
3175 {
3176 if ((attr = ippFindAttribute(client->request, "requesting-user-name",
3177 IPP_TAG_NAME)) == NULL)
3178 {
a469f8a5 3179 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
1106b00e
MS
3180 "Need requesting-user-name with my-jobs.");
3181 return;
3182 }
3183
a469f8a5 3184 username = ippGetString(attr, 0, NULL);
1106b00e 3185
6d56631f 3186 fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n", client->hostname, username);
1106b00e
MS
3187 }
3188 }
3189
3190 /*
3191 * OK, build a list of jobs for this printer...
3192 */
3193
db8b865d 3194 ra = ippCreateRequestedArray(client->request);
1106b00e 3195
a469f8a5 3196 respond_ipp(client, IPP_STATUS_OK, NULL);
1106b00e
MS
3197
3198 _cupsRWLockRead(&(client->printer->rwlock));
3199
d46dbe1b 3200 for (count = 0, job = (ippeve_job_t *)cupsArrayFirst(client->printer->jobs);
1106b00e 3201 (limit <= 0 || count < limit) && job;
d46dbe1b 3202 job = (ippeve_job_t *)cupsArrayNext(client->printer->jobs))
1106b00e
MS
3203 {
3204 /*
3205 * Filter out jobs that don't match...
3206 */
3207
3208 if ((job_comparison < 0 && job->state > job_state) ||
3209 (job_comparison == 0 && job->state != job_state) ||
3210 (job_comparison > 0 && job->state < job_state) ||
3211 job->id < first_job_id ||
a469f8a5 3212 (username && job->username &&
2cadf0f4 3213 strcasecmp(username, job->username)))
1106b00e
MS
3214 continue;
3215
3216 if (count > 0)
3217 ippAddSeparator(client->response);
3218
3219 count ++;
3220 copy_job_attributes(client, job, ra);
3221 }
3222
3223 cupsArrayDelete(ra);
3224
3225 _cupsRWUnlock(&(client->printer->rwlock));
3226}
3227
3228
3229/*
3230 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3231 */
3232
3233static void
3234ipp_get_printer_attributes(
d46dbe1b 3235 ippeve_client_t *client) /* I - Client */
1106b00e
MS
3236{
3237 cups_array_t *ra; /* Requested attributes array */
d46dbe1b 3238 ippeve_printer_t *printer; /* Printer */
1106b00e
MS
3239
3240
3241 /*
3242 * Send the attributes...
3243 */
3244
db8b865d 3245 ra = ippCreateRequestedArray(client->request);
1106b00e
MS
3246 printer = client->printer;
3247
a469f8a5 3248 respond_ipp(client, IPP_STATUS_OK, NULL);
1106b00e
MS
3249
3250 _cupsRWLockRead(&(printer->rwlock));
3251
3252 copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
a469f8a5 3253 IPP_TAG_CUPS_CONST);
1106b00e 3254
4a838088
MS
3255 if (!ra || cupsArrayFind(ra, "printer-config-change-date-time"))
3256 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-config-change-date-time", ippTimeToDate(printer->config_time));
3257
3258 if (!ra || cupsArrayFind(ra, "printer-config-change-time"))
3259 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-config-change-time", (int)(printer->config_time - printer->start_time));
3260
3261 if (!ra || cupsArrayFind(ra, "printer-current-time"))
3262 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(time(NULL)));
3263
3264
1106b00e 3265 if (!ra || cupsArrayFind(ra, "printer-state"))
400e67c1 3266 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state);
1106b00e 3267
4a838088
MS
3268 if (!ra || cupsArrayFind(ra, "printer-state-change-date-time"))
3269 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-state-change-date-time", ippTimeToDate(printer->state_time));
3270
3271 if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
3272 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-state-change-time", (int)(printer->state_time - printer->start_time));
3273
3274 if (!ra || cupsArrayFind(ra, "printer-state-message"))
3275 {
3276 static const char * const messages[] = { "Idle.", "Printing.", "Stopped." };
3277
3278 ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-state-message", NULL, messages[printer->state - IPP_PSTATE_IDLE]);
3279 }
3280
1106b00e
MS
3281 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
3282 {
d46dbe1b 3283 if (printer->state_reasons == IPPEVE_PREASON_NONE)
1562b9a1
MS
3284 {
3285 ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-state-reasons", NULL, "none");
3286 }
1106b00e
MS
3287 else
3288 {
9610a474 3289 ipp_attribute_t *attr = NULL; /* printer-state-reasons */
d46dbe1b 3290 ippeve_preason_t bit; /* Reason bit */
9610a474
MS
3291 int i; /* Looping var */
3292 char reason[32]; /* Reason string */
3293
d46dbe1b 3294 for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
9610a474
MS
3295 {
3296 if (printer->state_reasons & bit)
3297 {
d46dbe1b 3298 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
3299 if (attr)
3300 ippSetString(client->response, &attr, ippGetCount(attr), reason);
3301 else
3302 attr = ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "printer-state-reasons", NULL, reason);
1562b9a1
MS
3303 }
3304 }
4a838088
MS
3305 }
3306 }
3307
1106b00e 3308 if (!ra || cupsArrayFind(ra, "printer-up-time"))
4a838088 3309 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-up-time", (int)(time(NULL) - printer->start_time));
1106b00e
MS
3310
3311 if (!ra || cupsArrayFind(ra, "queued-job-count"))
1562b9a1 3312 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "queued-job-count", printer->active_job && printer->active_job->state < IPP_JSTATE_CANCELED);
1106b00e
MS
3313
3314 _cupsRWUnlock(&(printer->rwlock));
3315
3316 cupsArrayDelete(ra);
3317}
3318
3319
2cadf0f4
MS
3320/*
3321 * 'ipp_identify_printer()' - Beep or display a message.
3322 */
3323
3324static void
3325ipp_identify_printer(
d46dbe1b 3326 ippeve_client_t *client) /* I - Client */
2cadf0f4 3327{
7fd8cbfa
MS
3328 ipp_attribute_t *actions, /* identify-actions */
3329 *message; /* message */
3330
3331
3332 actions = ippFindAttribute(client->request, "identify-actions", IPP_TAG_KEYWORD);
3333 message = ippFindAttribute(client->request, "message", IPP_TAG_TEXT);
3334
3335 if (!actions || ippContainsString(actions, "sound"))
3336 {
3337 putchar(0x07);
3338 fflush(stdout);
3339 }
3340
3341 if (ippContainsString(actions, "display"))
3342 printf("IDENTIFY from %s: %s\n", client->hostname, message ? ippGetString(message, 0, NULL) : "No message supplied");
2cadf0f4
MS
3343
3344 respond_ipp(client, IPP_STATUS_OK, NULL);
3345}
3346
3347
1106b00e
MS
3348/*
3349 * 'ipp_print_job()' - Create a job object with an attached document.
3350 */
3351
3352static void
d46dbe1b 3353ipp_print_job(ippeve_client_t *client) /* I - Client */
1106b00e 3354{
d46dbe1b 3355 ippeve_job_t *job; /* New job */
1106b00e
MS
3356
3357
3358 /*
3359 * Validate print job attributes...
3360 */
3361
3362 if (!valid_job_attributes(client))
3363 {
a469f8a5 3364 httpFlush(client->http);
1106b00e
MS
3365 return;
3366 }
3367
3368 /*
3369 * Do we have a file to print?
3370 */
3371
a469f8a5 3372 if (httpGetState(client->http) == HTTP_STATE_POST_SEND)
1106b00e 3373 {
a469f8a5 3374 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No file in request.");
1106b00e
MS
3375 return;
3376 }
3377
3378 /*
cdb2e724 3379 * Create the job...
1106b00e
MS
3380 */
3381
3382 if ((job = create_job(client)) == NULL)
3383 {
aa2a90ce 3384 respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job.");
1106b00e
MS
3385 return;
3386 }
3387
3388 /*
cdb2e724 3389 * Then finish getting the document data and process things...
1106b00e
MS
3390 */
3391
a5ed2e39 3392 finish_document_data(client, job);
1106b00e
MS
3393}
3394
3395
1106b00e 3396/*
83e08001 3397 * 'ipp_print_uri()' - Create a job object with a referenced document.
1106b00e
MS
3398 */
3399
3400static void
d46dbe1b 3401ipp_print_uri(ippeve_client_t *client) /* I - Client */
1106b00e 3402{
d46dbe1b 3403 ippeve_job_t *job; /* New job */
1106b00e 3404
1106b00e 3405
83e08001
MS
3406 /*
3407 * Validate print job attributes...
3408 */
1106b00e 3409
83e08001
MS
3410 if (!valid_job_attributes(client))
3411 {
a469f8a5 3412 httpFlush(client->http);
83e08001
MS
3413 return;
3414 }
1106b00e 3415
1106b00e 3416 /*
a5ed2e39 3417 * Create the job...
1106b00e
MS
3418 */
3419
83e08001
MS
3420 if ((job = create_job(client)) == NULL)
3421 {
aa2a90ce 3422 respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job.");
83e08001
MS
3423 return;
3424 }
1106b00e 3425
a5ed2e39
MS
3426 /*
3427 * Then finish getting the document data and process things...
3428 */
83e08001 3429
a5ed2e39 3430 finish_document_uri(client, job);
83e08001
MS
3431}
3432
3433
3434/*
3435 * 'ipp_send_document()' - Add an attached document to a job object created with
3436 * Create-Job.
3437 */
3438
3439static void
aa2a90ce
MS
3440ipp_send_document(
3441 ippeve_client_t *client) /* I - Client */
83e08001 3442{
d46dbe1b 3443 ippeve_job_t *job; /* Job information */
83e08001 3444 ipp_attribute_t *attr; /* Current attribute */
83e08001
MS
3445
3446
3447 /*
3448 * Get the job...
3449 */
3450
3451 if ((job = find_job(client)) == NULL)
3452 {
a469f8a5
MS
3453 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3454 httpFlush(client->http);
83e08001
MS
3455 return;
3456 }
3457
3458 /*
3459 * See if we already have a document for this job or the job has already
3460 * in a non-pending state...
3461 */
3462
a469f8a5 3463 if (job->state > IPP_JSTATE_HELD)
83e08001 3464 {
cdb2e724 3465 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state.");
a469f8a5 3466 httpFlush(client->http);
83e08001
MS
3467 return;
3468 }
3469 else if (job->filename || job->fd >= 0)
3470 {
cdb2e724 3471 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported.");
a469f8a5 3472 httpFlush(client->http);
83e08001
MS
3473 return;
3474 }
3475
cdb2e724
MS
3476 /*
3477 * Make sure we have the "last-document" operation attribute...
3478 */
3479
3480 if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL)
83e08001 3481 {
cdb2e724
MS
3482 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute.");
3483 httpFlush(client->http);
3484 return;
3485 }
3486 else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION)
3487 {
3488 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group.");
a469f8a5 3489 httpFlush(client->http);
83e08001
MS
3490 return;
3491 }
cdb2e724 3492 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 || !ippGetBoolean(attr, 0))
83e08001
MS
3493 {
3494 respond_unsupported(client, attr);
a469f8a5 3495 httpFlush(client->http);
83e08001
MS
3496 return;
3497 }
3498
3499 /*
3500 * Validate document attributes...
3501 */
3502
3503 if (!valid_doc_attributes(client))
3504 {
a469f8a5 3505 httpFlush(client->http);
83e08001
MS
3506 return;
3507 }
3508
3509 /*
cdb2e724 3510 * Then finish getting the document data and process things...
83e08001
MS
3511 */
3512
3513 _cupsRWLockWrite(&(client->printer->rwlock));
3514
a5ed2e39
MS
3515 copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
3516
404dde30
MS
3517 if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
3518 job->format = ippGetString(attr, 0, NULL);
3519 else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
a469f8a5 3520 job->format = ippGetString(attr, 0, NULL);
83e08001
MS
3521 else
3522 job->format = "application/octet-stream";
3523
83e08001
MS
3524 _cupsRWUnlock(&(client->printer->rwlock));
3525
a5ed2e39 3526 finish_document_data(client, job);
83e08001
MS
3527}
3528
3529
3530/*
3531 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3532 * Create-Job.
3533 */
3534
3535static void
d46dbe1b 3536ipp_send_uri(ippeve_client_t *client) /* I - Client */
83e08001 3537{
d46dbe1b 3538 ippeve_job_t *job; /* Job information */
83e08001 3539 ipp_attribute_t *attr; /* Current attribute */
83e08001
MS
3540
3541
3542 /*
3543 * Get the job...
3544 */
3545
3546 if ((job = find_job(client)) == NULL)
3547 {
a469f8a5
MS
3548 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3549 httpFlush(client->http);
83e08001
MS
3550 return;
3551 }
3552
3553 /*
3554 * See if we already have a document for this job or the job has already
3555 * in a non-pending state...
3556 */
3557
a469f8a5 3558 if (job->state > IPP_JSTATE_HELD)
83e08001 3559 {
a5ed2e39 3560 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state.");
a469f8a5 3561 httpFlush(client->http);
83e08001
MS
3562 return;
3563 }
3564 else if (job->filename || job->fd >= 0)
3565 {
a5ed2e39 3566 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported.");
a469f8a5 3567 httpFlush(client->http);
83e08001
MS
3568 return;
3569 }
3570
a5ed2e39 3571 if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL)
83e08001 3572 {
a5ed2e39 3573 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute.");
a469f8a5 3574 httpFlush(client->http);
83e08001
MS
3575 return;
3576 }
a5ed2e39 3577 else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION)
83e08001 3578 {
a5ed2e39 3579 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group.");
a469f8a5 3580 httpFlush(client->http);
83e08001
MS
3581 return;
3582 }
a5ed2e39 3583 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 || !ippGetBoolean(attr, 0))
83e08001 3584 {
a5ed2e39 3585 respond_unsupported(client, attr);
a469f8a5 3586 httpFlush(client->http);
83e08001
MS
3587 return;
3588 }
3589
3590 /*
a5ed2e39 3591 * Validate document attributes...
83e08001
MS
3592 */
3593
a5ed2e39 3594 if (!valid_doc_attributes(client))
83e08001 3595 {
a5ed2e39 3596 httpFlush(client->http);
83e08001
MS
3597 return;
3598 }
3599
3600 /*
a5ed2e39 3601 * Then finish getting the document data and process things...
83e08001
MS
3602 */
3603
3604 _cupsRWLockWrite(&(client->printer->rwlock));
3605
a5ed2e39
MS
3606 copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
3607
3608 if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
3609 job->format = ippGetString(attr, 0, NULL);
3610 else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
a469f8a5 3611 job->format = ippGetString(attr, 0, NULL);
83e08001
MS
3612 else
3613 job->format = "application/octet-stream";
3614
83e08001
MS
3615 _cupsRWUnlock(&(client->printer->rwlock));
3616
a5ed2e39 3617 finish_document_uri(client, job);
83e08001
MS
3618}
3619
3620
3621/*
3622 * 'ipp_validate_job()' - Validate job creation attributes.
3623 */
3624
3625static void
d46dbe1b 3626ipp_validate_job(ippeve_client_t *client) /* I - Client */
83e08001
MS
3627{
3628 if (valid_job_attributes(client))
a469f8a5 3629 respond_ipp(client, IPP_STATUS_OK, NULL);
83e08001
MS
3630}
3631
3632
15a9714c 3633/*
92dad945 3634 * 'ippserver_attr_cb()' - Determine whether an attribute should be loaded.
15a9714c
MS
3635 */
3636
92dad945
MS
3637static int /* O - 1 to use, 0 to ignore */
3638ippserver_attr_cb(
3639 _ipp_file_t *f, /* I - IPP file */
3640 void *user_data, /* I - User data pointer (unused) */
3641 const char *attr) /* I - Attribute name */
15a9714c 3642{
92dad945
MS
3643 int i, /* Current element */
3644 result; /* Result of comparison */
3645 static const char * const ignored[] =
3646 { /* Ignored attributes */
3647 "attributes-charset",
3648 "attributes-natural-language",
3649 "charset-configured",
3650 "charset-supported",
3651 "device-service-count",
3652 "device-uuid",
3653 "document-format-varying-attributes",
3654 "generated-natural-language-supported",
1562b9a1
MS
3655 "identify-actions-default",
3656 "identify-actions-supported",
3657 "ipp-features-supported",
3658 "ipp-versions-supproted",
92dad945
MS
3659 "ippget-event-life",
3660 "job-hold-until-supported",
3661 "job-hold-until-time-supported",
3662 "job-ids-supported",
3663 "job-k-octets-supported",
3664 "job-settable-attributes-supported",
3665 "multiple-document-jobs-supported",
3666 "multiple-operation-time-out",
3667 "multiple-operation-time-out-action",
3668 "natural-language-configured",
3669 "notify-attributes-supported",
3670 "notify-events-default",
3671 "notify-events-supported",
3672 "notify-lease-duration-default",
3673 "notify-lease-duration-supported",
3674 "notify-max-events-supported",
3675 "notify-pull-method-supported",
3676 "operations-supported",
3677 "printer-alert",
3678 "printer-alert-description",
3679 "printer-camera-image-uri",
3680 "printer-charge-info",
3681 "printer-charge-info-uri",
3682 "printer-config-change-date-time",
3683 "printer-config-change-time",
3684 "printer-current-time",
3685 "printer-detailed-status-messages",
3686 "printer-dns-sd-name",
3687 "printer-fax-log-uri",
3688 "printer-get-attributes-supported",
3689 "printer-icons",
3690 "printer-id",
3691 "printer-info",
3692 "printer-is-accepting-jobs",
3693 "printer-message-date-time",
3694 "printer-message-from-operator",
3695 "printer-message-time",
3696 "printer-more-info",
3697 "printer-service-type",
3698 "printer-settable-attributes-supported",
3699 "printer-state",
3700 "printer-state-message",
3701 "printer-state-reasons",
3702 "printer-static-resource-directory-uri",
3703 "printer-static-resource-k-octets-free",
3704 "printer-static-resource-k-octets-supported",
3705 "printer-strings-languages-supported",
3706 "printer-strings-uri",
3707 "printer-supply-info-uri",
3708 "printer-up-time",
3709 "printer-uri-supported",
3710 "printer-xri-supported",
3711 "queued-job-count",
3712 "reference-uri-scheme-supported",
3713 "uri-authentication-supported",
3714 "uri-security-supported",
3715 "which-jobs-supported",
3716 "xri-authentication-supported",
3717 "xri-security-supported",
3718 "xri-uri-scheme-supported"
3719 };
3720
15a9714c 3721
92dad945
MS
3722 (void)f;
3723 (void)user_data;
15a9714c 3724
92dad945 3725 for (i = 0, result = 1; i < (int)(sizeof(ignored) / sizeof(ignored[0])); i ++)
15a9714c 3726 {
92dad945
MS
3727 if ((result = strcmp(attr, ignored[i])) <= 0)
3728 break;
15a9714c
MS
3729 }
3730
92dad945
MS
3731 return (result != 0);
3732}
3733
3734
3735/*
3736 * 'ippserver_error_cb()' - Log an error message.
3737 */
3738
3739static int /* O - 1 to continue, 0 to stop */
3740ippserver_error_cb(
3741 _ipp_file_t *f, /* I - IPP file data */
3742 void *user_data, /* I - User data pointer (unused) */
3743 const char *error) /* I - Error message */
3744{
3745 (void)f;
3746 (void)user_data;
3747
3748 _cupsLangPrintf(stderr, "%s\n", error);
3749
3750 return (1);
3751}
3752
3753
3754/*
3755 * 'ippserver_token_cb()' - Process ippserver-specific config file tokens.
3756 */
3757
3758static int /* O - 1 to continue, 0 to stop */
3759ippserver_token_cb(
3760 _ipp_file_t *f, /* I - IPP file data */
3761 _ipp_vars_t *vars, /* I - IPP variables */
3762 void *user_data, /* I - User data pointer (unused) */
3763 const char *token) /* I - Current token */
3764{
1562b9a1 3765 (void)vars;
92dad945
MS
3766 (void)user_data;
3767
3768 if (!token)
15a9714c 3769 {
92dad945
MS
3770 /*
3771 * NULL token means do the initial setup - create an empty IPP message and
3772 * return...
3773 */
15a9714c 3774
92dad945
MS
3775 f->attrs = ippNew();
3776 f->group_tag = IPP_TAG_PRINTER;
3777 }
3778 else
3779 {
3780 _cupsLangPrintf(stderr, _("Unknown directive \"%s\" on line %d of \"%s\" ignored."), token, f->linenum, f->filename);
3781 }
15a9714c 3782
92dad945
MS
3783 return (1);
3784}
15a9714c 3785
15a9714c 3786
92dad945
MS
3787/*
3788 * 'load_ippserver_attributes()' - Load IPP attributes from an ippserver file.
3789 */
15a9714c 3790
92dad945
MS
3791static ipp_t * /* O - IPP attributes or `NULL` on error */
3792load_ippserver_attributes(
f8927099
MS
3793 const char *servername, /* I - Server name or `NULL` for default */
3794 int serverport, /* I - Server port number */
3795 const char *filename, /* I - ippserver attribute filename */
3796 cups_array_t *docformats) /* I - document-format-supported values */
92dad945
MS
3797{
3798 ipp_t *attrs; /* IPP attributes */
3799 _ipp_vars_t vars; /* IPP variables */
3800 char temp[256]; /* Temporary string */
15a9714c 3801
15a9714c 3802
60d8f884
MS
3803 (void)docformats; /* for now */
3804
92dad945
MS
3805 /*
3806 * Setup callbacks and variables for the printer configuration file...
3807 *
3808 * The following additional variables are supported:
3809 *
3810 * - SERVERNAME: The host name of the server.
3811 * - SERVERPORT: The default port of the server.
3812 */
15a9714c 3813
92dad945 3814 _ippVarsInit(&vars, (_ipp_fattr_cb_t)ippserver_attr_cb, (_ipp_ferror_cb_t)ippserver_error_cb, (_ipp_ftoken_cb_t)ippserver_token_cb);
15a9714c 3815
92dad945
MS
3816 if (servername)
3817 {
3818 _ippVarsSet(&vars, "SERVERNAME", servername);
3819 }
3820 else
3821 {
3822 httpGetHostname(NULL, temp, sizeof(temp));
3823 _ippVarsSet(&vars, "SERVERNAME", temp);
3824 }
15a9714c 3825
92dad945
MS
3826 snprintf(temp, sizeof(temp), "%d", serverport);
3827 _ippVarsSet(&vars, "SERVERPORT", temp);
15a9714c 3828
92dad945
MS
3829 /*
3830 * Load attributes and values for the printer...
3831 */
15a9714c 3832
92dad945 3833 attrs = _ippFileParse(&vars, filename, NULL);
15a9714c 3834
92dad945
MS
3835 /*
3836 * Free memory and return...
3837 */
15a9714c 3838
92dad945 3839 _ippVarsDeinit(&vars);
15a9714c 3840
92dad945
MS
3841 return (attrs);
3842}
15a9714c 3843
15a9714c 3844
92dad945 3845/*
f8927099 3846 * 'load_legacy_attributes()' - Load IPP attributes using the old ippserver
6d56631f 3847 * options.
92dad945 3848 */
15a9714c 3849
92dad945 3850static ipp_t * /* O - IPP attributes or `NULL` on error */
f8927099
MS
3851load_legacy_attributes(
3852 const char *make, /* I - Manufacturer name */
3853 const char *model, /* I - Model name */
3854 int ppm, /* I - pages-per-minute */
3855 int ppm_color, /* I - pages-per-minute-color */
3856 int duplex, /* I - Duplex support? */
3857 cups_array_t *docformats) /* I - document-format-supported values */
92dad945 3858{
1562b9a1
MS
3859 int i; /* Looping var */
3860 ipp_t *attrs, /* IPP attributes */
3861 *col; /* Collection value */
3862 ipp_attribute_t *attr; /* Current attribute */
92dad945 3863 char device_id[1024],/* printer-device-id */
1562b9a1 3864 *ptr, /* Pointer into device ID */
92dad945 3865 make_model[128];/* printer-make-and-model */
1562b9a1
MS
3866 const char *format, /* Current document format */
3867 *prefix; /* Prefix for device ID */
3868 int num_media; /* Number of media */
3869 const char * const *media; /* List of media */
3870 int num_ready; /* Number of loaded media */
3871 const char * const *ready; /* List of loaded media */
3872 pwg_media_t *pwg; /* PWG media size information */
3873 static const char * const media_supported[] =
3874 { /* media-supported values */
3875 "na_letter_8.5x11in", /* Letter */
3876 "na_legal_8.5x14in", /* Legal */
3877 "iso_a4_210x297mm", /* A4 */
3878 "na_number-10_4.125x9.5in", /* #10 Envelope */
3879 "iso_dl_110x220mm" /* DL Envelope */
3880 };
3881 static const char * const media_supported_color[] =
3882 { /* media-supported values */
3883 "na_letter_8.5x11in", /* Letter */
3884 "na_legal_8.5x14in", /* Legal */
3885 "iso_a4_210x297mm", /* A4 */
3886 "na_number-10_4.125x9.5in", /* #10 Envelope */
3887 "iso_dl_110x220mm", /* DL Envelope */
3888 "na_index-3x5_3x5in", /* Photo 3x5 */
3889 "oe_photo-l_3.5x5in", /* Photo L */
3890 "na_index-4x6_4x6in", /* Photo 4x6 */
3891 "iso_a6_105x148mm", /* A6 */
3892 "na_5x7_5x7in" /* Photo 5x7 aka 2L */
3893 "iso_a5_148x210mm", /* A5 */
3894 };
3895 static const char * const media_ready[] =
3896 { /* media-ready values */
3897 "na_letter_8.5x11in", /* Letter */
3898 "na_number-10_4.125x9.5in" /* #10 */
3899 };
3900 static const char * const media_ready_color[] =
3901 { /* media-ready values */
3902 "na_letter_8.5x11in", /* Letter */
3903 "na_index-4x6_4x6in" /* Photo 4x6 */
3904 };
3905 static const char * const media_source_supported[] =
3906 { /* media-source-supported values */
3907 "auto",
3908 "main",
3909 "manual",
3910 "by-pass-tray" /* AKA multi-purpose tray */
3911 };
3912 static const char * const media_source_supported_color[] =
3913 { /* media-source-supported values */
3914 "auto",
3915 "main",
1562b9a1
MS
3916 "photo"
3917 };
3918 static const char * const media_type_supported[] =
3919 { /* media-type-supported values */
3920 "auto",
3921 "cardstock",
3922 "envelope",
3923 "labels",
3924 "other",
3925 "stationery",
3926 "stationery-letterhead",
3927 "transparency"
3928 };
3929 static const char * const media_type_supported_color[] =
3930 { /* media-type-supported values */
3931 "auto",
3932 "cardstock",
3933 "envelope",
3934 "labels",
3935 "other",
3936 "stationery",
3937 "stationery-letterhead",
3938 "transparency",
3939 "photographic-glossy",
3940 "photographic-high-gloss",
3941 "photographic-matte",
3942 "photographic-satin",
3943 "photographic-semi-gloss"
3944 };
3945 static const int media_bottom_margin_supported[] =
3946 { /* media-bottom-margin-supported values */
3947 635 /* 1/4" */
3948 };
3949 static const int media_bottom_margin_supported_color[] =
3950 { /* media-bottom/top-margin-supported values */
3951 0, /* Borderless */
3952 1168 /* 0.46" (common HP inkjet bottom margin) */
3953 };
3954 static const int media_lr_margin_supported[] =
3955 { /* media-left/right-margin-supported values */
3956 340, /* 3.4mm (historical HP PCL A4 margin) */
3957 635 /* 1/4" */
3958 };
3959 static const int media_lr_margin_supported_color[] =
3960 { /* media-left/right-margin-supported values */
3961 0, /* Borderless */
3962 340, /* 3.4mm (historical HP PCL A4 margin) */
3963 635 /* 1/4" */
3964 };
3965 static const int media_top_margin_supported[] =
3966 { /* media-top-margin-supported values */
3967 635 /* 1/4" */
3968 };
3969 static const int media_top_margin_supported_color[] =
3970 { /* media-top/top-margin-supported values */
3971 0, /* Borderless */
3972 102 /* 0.04" (common HP inkjet top margin */
3973 };
92dad945
MS
3974 static const int orientation_requested_supported[4] =
3975 { /* orientation-requested-supported values */
3976 IPP_ORIENT_PORTRAIT,
3977 IPP_ORIENT_LANDSCAPE,
3978 IPP_ORIENT_REVERSE_LANDSCAPE,
3979 IPP_ORIENT_REVERSE_PORTRAIT
3980 };
4d2df926
MS
3981 static const char * const overrides_supported[] =
3982 { /* overrides-supported values */
3983 "document-numbers",
3984 "media",
3985 "media-col",
3986 "orientation-requested",
3987 "pages"
3988 };
92dad945 3989 static const char * const print_color_mode_supported[] =
1562b9a1
MS
3990 { /* print-color-mode-supported values */
3991 "monochrome"
3992 };
3993 static const char * const print_color_mode_supported_color[] =
92dad945
MS
3994 { /* print-color-mode-supported values */
3995 "auto",
3996 "color",
3997 "monochrome"
3998 };
3999 static const int print_quality_supported[] =
4000 { /* print-quality-supported values */
4001 IPP_QUALITY_DRAFT,
4002 IPP_QUALITY_NORMAL,
4003 IPP_QUALITY_HIGH
4004 };
6640ceec
MS
4005 static const char * const printer_input_tray[] =
4006 { /* printer-input-tray values */
4007 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4008 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=100;status=0;name=main",
4009 "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=manual",
4010 "type=sheetFeedAutoNonRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=by-pass-tray"
4011 };
4012 static const char * const printer_input_tray_color[] =
4013 { /* printer-input-tray values */
4014 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4015 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=-2;status=0;name=main",
4016 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=photo"
4017 };
1562b9a1
MS
4018 static const char * const printer_supply[] =
4019 { /* printer-supply values */
4020 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4021 "maxcapacity=100;level=25;colorantname=unknown;",
4022 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4023 "maxcapacity=100;level=75;colorantname=black;"
4024 };
4025 static const char * const printer_supply_color[] =
4026 { /* printer-supply values */
4027 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4028 "maxcapacity=100;level=25;colorantname=unknown;",
4029 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4030 "maxcapacity=100;level=75;colorantname=black;",
4031 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4032 "maxcapacity=100;level=50;colorantname=cyan;",
4033 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4034 "maxcapacity=100;level=33;colorantname=magenta;",
4035 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4036 "maxcapacity=100;level=67;colorantname=yellow;"
4037 };
4038 static const char * const printer_supply_description[] =
4039 { /* printer-supply-description values */
4040 "Toner Waste Tank",
4041 "Black Toner"
4042 };
4043 static const char * const printer_supply_description_color[] =
4044 { /* printer-supply-description values */
4045 "Ink Waste Tank",
4046 "Black Ink",
4047 "Cyan Ink",
4048 "Magenta Ink",
4049 "Yellow Ink"
4050 };
92dad945
MS
4051 static const int pwg_raster_document_resolution_supported[] =
4052 {
1562b9a1
MS
4053 300,
4054 600
92dad945
MS
4055 };
4056 static const char * const pwg_raster_document_type_supported[] =
4057 {
4058 "black_1",
1562b9a1
MS
4059 "sgray_8"
4060 };
4061 static const char * const pwg_raster_document_type_supported_color[] =
4062 {
4063 "black_1",
92dad945
MS
4064 "sgray_8",
4065 "srgb_8",
4066 "srgb_16"
4067 };
4068 static const char * const sides_supported[] =
4069 { /* sides-supported values */
4070 "one-sided",
4071 "two-sided-long-edge",
4072 "two-sided-short-edge"
4073 };
4074 static const char * const urf_supported[] =
4075 { /* urf-supported values */
4076 "CP1",
1562b9a1
MS
4077 "IS1-4-5-19",
4078 "MT1-2-3-4-5-6",
4079 "RS600",
4080 "V1.4",
4081 "W8"
4082 };
4083 static const char * const urf_supported_color[] =
4084 { /* urf-supported values */
4085 "CP1",
4086 "IS1-4-5-7-19",
4087 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4088 "RS600",
4089 "SRGB24",
4090 "V1.4",
4091 "W8"
4092 };
4093 static const char * const urf_supported_color_duplex[] =
4094 { /* urf-supported values */
4095 "CP1",
4096 "IS1-4-5-7-19",
92dad945 4097 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1562b9a1 4098 "RS600",
92dad945
MS
4099 "SRGB24",
4100 "V1.4",
4101 "W8",
1562b9a1
MS
4102 "DM3"
4103 };
4104 static const char * const urf_supported_duplex[] =
4105 { /* urf-supported values */
4106 "CP1",
4107 "IS1-4-5-19",
4108 "MT1-2-3-4-5-6",
4109 "RS600",
4110 "V1.4",
4111 "W8",
92dad945
MS
4112 "DM1"
4113 };
15a9714c 4114
15a9714c 4115
92dad945 4116 attrs = ippNew();
15a9714c 4117
1562b9a1
MS
4118 if (ppm_color > 0)
4119 {
4120 num_media = (int)(sizeof(media_supported_color) / sizeof(media_supported_color[0]));
4121 media = media_supported_color;
4122 num_ready = (int)(sizeof(media_ready_color) / sizeof(media_ready_color[0]));
4123 ready = media_ready_color;
4124 }
4125 else
4126 {
4127 num_media = (int)(sizeof(media_supported) / sizeof(media_supported[0]));
4128 media = media_supported;
4129 num_ready = (int)(sizeof(media_ready) / sizeof(media_ready[0]));
4130 ready = media_ready;
4131 }
4132
92dad945
MS
4133 /* color-supported */
4134 ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", ppm_color > 0);
15a9714c 4135
92dad945
MS
4136 /* copies-default */
4137 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
15a9714c 4138
92dad945 4139 /* copies-supported */
1562b9a1 4140 ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, (cupsArrayFind(docformats, (void *)"application/pdf") != NULL || cupsArrayFind(docformats, (void *)"image/jpeg") != NULL) ? 999 : 1);
15a9714c 4141
4d2df926
MS
4142 /* document-password-supported */
4143 if (cupsArrayFind(docformats, (void *)"application/pdf"))
4144 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 1023);
4145
92dad945
MS
4146 /* finishings-default */
4147 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
15a9714c 4148
92dad945
MS
4149 /* finishings-supported */
4150 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", IPP_FINISHINGS_NONE);
15a9714c 4151
92dad945 4152 /* media-bottom-margin-supported */
1562b9a1
MS
4153 if (ppm_color > 0)
4154 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);
4155 else
4156 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 4157
1562b9a1
MS
4158 /* media-col-database and media-col-default */
4159 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", num_media, NULL);
4160 for (i = 0; i < num_media; i ++)
92dad945 4161 {
1562b9a1
MS
4162 int bottom, left, /* media-xxx-margins */
4163 right, top;
4164 const char *source; /* media-source, if any */
4165
4166 pwg = pwgMediaForPWG(media[i]);
4167
4168 if (pwg->width < 21000 && pwg->length < 21000)
92dad945 4169 {
1562b9a1
MS
4170 source = "photo"; /* Photo size media from photo tray */
4171 bottom = /* Borderless margins */
4172 left =
4173 right =
4174 top = 0;
92dad945 4175 }
1562b9a1 4176 else if (pwg->width < 21000)
92dad945 4177 {
1562b9a1
MS
4178 source = "by-pass-tray"; /* Envelopes from multi-purpose tray */
4179 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4180 left = /* Left/right margins are standard */
4181 right = media_lr_margin_supported[1];
4182 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4183 }
4184 else if (pwg->width == 21000)
4185 {
4186 source = NULL; /* A4 from any tray */
4187 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4188 left = /* Left/right margins are reduced */
4189 right = media_lr_margin_supported[0];
4190 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4191 }
4192 else
4193 {
4194 source = NULL; /* Other size media from any tray */
4195 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4196 left = /* Left/right margins are standard */
4197 right = media_lr_margin_supported[1];
4198 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4199 }
15a9714c 4200
1562b9a1
MS
4201 col = create_media_col(media[i], source, NULL, pwg->width, pwg->length, bottom, left, right, top);
4202 ippSetCollection(attrs, &attr, i, col);
92dad945 4203
1562b9a1 4204 ippDelete(col);
92dad945
MS
4205 }
4206
4d2df926
MS
4207 /* media-col-default */
4208 pwg = pwgMediaForPWG(ready[0]);
4209
4210 if (pwg->width == 21000)
4211 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]);
4212 else
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[1], media_lr_margin_supported[1], ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]);
4214
4215 ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col);
4216
4217 ippDelete(col);
4218
1562b9a1 4219 /* media-col-ready */
6640ceec 4220 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-ready", num_ready, NULL);
1562b9a1 4221 for (i = 0; i < num_ready; i ++)
92dad945 4222 {
1562b9a1
MS
4223 int bottom, left, /* media-xxx-margins */
4224 right, top;
4225 const char *source, /* media-source */
4226 *type; /* media-type */
92dad945 4227
1562b9a1 4228 pwg = pwgMediaForPWG(ready[i]);
92dad945 4229
1562b9a1
MS
4230 if (pwg->width < 21000 && pwg->length < 21000)
4231 {
4232 source = "photo"; /* Photo size media from photo tray */
4233 type = "photographic-glossy"; /* Glossy photo paper */
4234 bottom = /* Borderless margins */
4235 left =
4236 right =
4237 top = 0;
4238 }
4239 else if (pwg->width < 21000)
4240 {
4241 source = "by-pass-tray"; /* Envelopes from multi-purpose tray */
4242 type = "envelope"; /* Envelope */
4243 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4244 left = /* Left/right margins are standard */
4245 right = media_lr_margin_supported[1];
4246 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4247 }
4248 else if (pwg->width == 21000)
4249 {
4250 source = "main"; /* A4 from main tray */
4251 type = "stationery"; /* Plain paper */
4252 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4253 left = /* Left/right margins are reduced */
4254 right = media_lr_margin_supported[0];
4255 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4256 }
4257 else
4258 {
4259 source = "main"; /* A4 from main tray */
4260 type = "stationery"; /* Plain paper */
4261 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4262 left = /* Left/right margins are standard */
4263 right = media_lr_margin_supported[1];
4264 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4265 }
4266
4d2df926 4267 col = create_media_col(ready[i], source, type, pwg->width, pwg->length, bottom, left, right, top);
1562b9a1
MS
4268 ippSetCollection(attrs, &attr, i, col);
4269 ippDelete(col);
4270 }
92dad945
MS
4271
4272 /* media-default */
1562b9a1 4273 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-default", NULL, media[0]);
92dad945 4274
1562b9a1
MS
4275 /* media-left/right-margin-supported */
4276 if (ppm_color > 0)
4277 {
4278 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);
4279 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);
4280 }
4281 else
4282 {
4283 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);
4284 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);
4285 }
92dad945 4286
1562b9a1
MS
4287 /* media-ready */
4288 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", num_ready, NULL, ready);
92dad945
MS
4289
4290 /* media-supported */
1562b9a1 4291 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-supported", num_media, NULL, media);
92dad945
MS
4292
4293 /* media-size-supported */
1562b9a1
MS
4294 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", num_media, NULL);
4295 for (i = 0; i < num_media; i ++)
92dad945 4296 {
1562b9a1
MS
4297 pwg = pwgMediaForPWG(media[i]);
4298 col = create_media_size(pwg->width, pwg->length);
92dad945 4299
1562b9a1
MS
4300 ippSetCollection(attrs, &attr, i, col);
4301 ippDelete(col);
15a9714c
MS
4302 }
4303
92dad945 4304 /* media-source-supported */
1562b9a1
MS
4305 if (ppm_color > 0)
4306 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);
4307 else
92dad945 4308 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 4309
92dad945 4310 /* media-top-margin-supported */
1562b9a1
MS
4311 if (ppm_color > 0)
4312 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);
4313 else
4314 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 4315
92dad945 4316 /* media-type-supported */
1562b9a1
MS
4317 if (ppm_color > 0)
4318 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);
4319 else
92dad945 4320 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 4321
4d2df926
MS
4322 /* orientation-requested-default */
4323 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", IPP_ORIENT_PORTRAIT);
4324
92dad945 4325 /* orientation-requested-supported */
1562b9a1
MS
4326 if (cupsArrayFind(docformats, (void *)"application/pdf") || cupsArrayFind(docformats, (void *)"image/jpeg"))
4327 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported);
4328 else
4329 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", IPP_ORIENT_PORTRAIT);
92dad945
MS
4330
4331 /* output-bin-default */
1562b9a1
MS
4332 if (ppm_color > 0)
4333 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-up");
4334 else
92dad945
MS
4335 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
4336
4337 /* output-bin-supported */
1562b9a1
MS
4338 if (ppm_color > 0)
4339 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-up");
4340 else
92dad945
MS
4341 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
4342
4d2df926
MS
4343 /* overrides-supported */
4344 if (cupsArrayFind(docformats, (void *)"application/pdf"))
4345 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "overrides-supported", (int)(sizeof(overrides_supported) / sizeof(overrides_supported[0])), NULL, overrides_supported);
4346
92dad945 4347 /* page-ranges-supported */
1562b9a1 4348 ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", cupsArrayFind(docformats, (void *)"application/pdf") != NULL);
92dad945
MS
4349
4350 /* pages-per-minute */
1562b9a1 4351 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppm);
92dad945
MS
4352
4353 /* pages-per-minute-color */
4354 if (ppm_color > 0)
1562b9a1 4355 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppm_color);
92dad945
MS
4356
4357 /* print-color-mode-default */
4d2df926 4358 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, ppm_color > 0 ? "auto" : "monochrome");
92dad945
MS
4359
4360 /* print-color-mode-supported */
1562b9a1
MS
4361 if (ppm_color > 0)
4362 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);
4363 else
92dad945
MS
4364 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);
4365
4366 /* print-content-optimize-default */
1562b9a1 4367 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
92dad945
MS
4368
4369 /* print-content-optimize-supported */
1562b9a1 4370 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
92dad945
MS
4371
4372 /* print-quality-default */
1562b9a1 4373 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
92dad945
MS
4374
4375 /* print-quality-supported */
1562b9a1
MS
4376 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
4377
4378 /* print-rendering-intent-default */
4379 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
4380
4381 /* print-rendering-intent-supported */
4382 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
92dad945
MS
4383
4384 /* printer-device-id */
1562b9a1
MS
4385 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make, model);
4386 ptr = device_id + strlen(device_id);
4387 prefix = "CMD:";
4388 for (format = (const char *)cupsArrayFirst(docformats); format; format = (const char *)cupsArrayNext(docformats))
4389 {
4390 if (!strcasecmp(format, "application/pdf"))
4391 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPDF", prefix);
4392 else if (!strcasecmp(format, "application/postscript"))
4393 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPS", prefix);
4394 else if (!strcasecmp(format, "application/vnd.hp-PCL"))
4395 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPCL", prefix);
4396 else if (!strcasecmp(format, "image/jpeg"))
4397 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sJPEG", prefix);
4398 else if (!strcasecmp(format, "image/png"))
4399 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPNG", prefix);
4400 else if (!strcasecmp(format, "image/pwg-raster"))
4401 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPWG", prefix);
4402 else if (!strcasecmp(format, "image/urf"))
4403 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sURF", prefix);
4404 else
4405 continue;
4406
4407 ptr += strlen(ptr);
4408 prefix = ",";
4409 }
4410 if (ptr < (device_id + sizeof(device_id) - 1))
4411 {
4412 *ptr++ = ';';
4413 *ptr = '\0';
4414 }
92dad945
MS
4415 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id);
4416
6640ceec
MS
4417 /* printer-input-tray */
4418 if (ppm_color > 0)
4419 {
4420 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray_color[0], strlen(printer_input_tray_color[0]));
4421 for (i = 1; i < (int)(sizeof(printer_input_tray_color) / sizeof(printer_input_tray_color[0])); i ++)
4422 ippSetOctetString(attrs, &attr, i, printer_input_tray_color[i], strlen(printer_input_tray_color[i]));
4423 }
4424 else
4425 {
4426 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray[0], strlen(printer_input_tray[0]));
4427 for (i = 1; i < (int)(sizeof(printer_input_tray) / sizeof(printer_input_tray[0])); i ++)
4428 ippSetOctetString(attrs, &attr, i, printer_input_tray[i], strlen(printer_input_tray[i]));
4429 }
4430
92dad945
MS
4431 /* printer-make-and-model */
4432 snprintf(make_model, sizeof(make_model), "%s %s", make, model);
4433 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, make_model);
4434
4435 /* printer-resolution-default */
1562b9a1 4436 ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, 600, 600);
92dad945
MS
4437
4438 /* printer-resolution-supported */
1562b9a1 4439 ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600);
92dad945 4440
1562b9a1
MS
4441 /* printer-supply and printer-supply-description */
4442 if (ppm_color > 0)
4443 {
4444 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], strlen(printer_supply_color[0]));
4445 for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++)
4446 ippSetOctetString(attrs, &attr, i, printer_supply_color[i], strlen(printer_supply_color[i]));
92dad945 4447
1562b9a1
MS
4448 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);
4449 }
4450 else
4451 {
4452 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], strlen(printer_supply[0]));
3e5092db 4453 for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++)
1562b9a1
MS
4454 ippSetOctetString(attrs, &attr, i, printer_supply[i], strlen(printer_supply[i]));
4455
4456 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);
4457 }
92dad945 4458
1562b9a1
MS
4459 /* pwg-raster-document-xxx-supported */
4460 if (cupsArrayFind(docformats, (void *)"image/pwg-raster"))
92dad945 4461 {
1562b9a1
MS
4462 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);
4463
4464 if (ppm_color > 0 && duplex)
4465 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "rotated");
4466 else if (duplex)
4467 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal");
4468
4469 if (ppm_color > 0)
4470 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);
4471 else
4472 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
4473 }
4474
4475 /* sides-default */
1562b9a1 4476 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
92dad945
MS
4477
4478 /* sides-supported */
1562b9a1
MS
4479 if (duplex)
4480 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported);
4481 else
4482 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided");
92dad945
MS
4483
4484 /* urf-supported */
1562b9a1
MS
4485 if (cupsArrayFind(docformats, (void *)"image/urf"))
4486 {
4487 if (ppm_color > 0)
4488 {
4489 if (duplex)
4490 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);
4491 else
4492 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_color) / sizeof(urf_supported_color[0])), NULL, urf_supported_color);
4493 }
4494 else if (duplex)
4495 {
4496 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_duplex) / sizeof(urf_supported_duplex[0])), NULL, urf_supported_duplex);
4497 }
4498 else
4499 {
4500 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported) / sizeof(urf_supported[0])), NULL, urf_supported);
4501 }
4502 }
92dad945
MS
4503
4504 return (attrs);
8a4ed632
MS
4505}
4506
4507
6641bd0d 4508#if !CUPS_LITE
f8927099
MS
4509/*
4510 * 'load_ppd_attributes()' - Load IPP attributes from a PPD file.
4511 */
4512
4513static ipp_t * /* O - IPP attributes or `NULL` on error */
4514load_ppd_attributes(
4515 const char *ppdfile, /* I - PPD filename */
4516 cups_array_t *docformats) /* I - document-format-supported values */
4517{
ab8fab61
MS
4518 int i, j; /* Looping vars */
4519 ipp_t *attrs; /* Attributes */
4520 ipp_attribute_t *attr; /* Current attribute */
4521 ipp_t *col; /* Current collection value */
4522 ppd_file_t *ppd; /* PPD data */
4523 ppd_attr_t *ppd_attr; /* PPD attribute */
2d6dcec1 4524 ppd_choice_t *ppd_choice; /* PPD choice */
ab8fab61
MS
4525 ppd_size_t *ppd_size; /* Default PPD size */
4526 pwg_size_t *pwg_size, /* Current PWG size */
4527 *default_size = NULL; /* Default PWG size */
2d6dcec1
MS
4528 const char *default_source = NULL, /* Default media source */
4529 *default_type = NULL; /* Default media type */
ab8fab61
MS
4530 pwg_map_t *pwg_map; /* Mapping from PWG to PPD keywords */
4531 _ppd_cache_t *pc; /* PPD cache */
4532 _pwg_finishings_t *finishings; /* Current finishings value */
4533 const char *template; /* Current finishings-template value */
4534 int num_margins; /* Number of media-xxx-margin-supported values */
4535 int margins[10]; /* media-xxx-margin-supported values */
4536 int xres, /* Default horizontal resolution */
4537 yres; /* Default vertical resolution */
4538 int num_urf; /* Number of urf-supported values */
4539 const char *urf[10]; /* urf-supported values */
4540 char urf_rs[32]; /* RS value */
4541 static const int orientation_requested_supported[4] =
4542 { /* orientation-requested-supported values */
4543 IPP_ORIENT_PORTRAIT,
4544 IPP_ORIENT_LANDSCAPE,
4545 IPP_ORIENT_REVERSE_LANDSCAPE,
4546 IPP_ORIENT_REVERSE_PORTRAIT
4547 };
4548 static const char * const overrides_supported[] =
1562b9a1 4549 { /* overrides-supported */
ab8fab61
MS
4550 "document-numbers",
4551 "media",
4552 "media-col",
4553 "orientation-requested",
1562b9a1
MS
4554 "pages"
4555 };
ab8fab61
MS
4556 static const char * const print_color_mode_supported[] =
4557 { /* print-color-mode-supported values */
4558 "monochrome"
4559 };
4560 static const char * const print_color_mode_supported_color[] =
4561 { /* print-color-mode-supported values */
4562 "auto",
4563 "color",
4564 "monochrome"
4565 };
4566 static const int print_quality_supported[] =
4567 { /* print-quality-supported values */
4568 IPP_QUALITY_DRAFT,
4569 IPP_QUALITY_NORMAL,
4570 IPP_QUALITY_HIGH
4571 };
4572 static const char * const printer_supply[] =
4573 { /* printer-supply values */
4574 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4575 "maxcapacity=100;level=25;colorantname=unknown;",
4576 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4577 "maxcapacity=100;level=75;colorantname=black;"
4578 };
4579 static const char * const printer_supply_color[] =
4580 { /* printer-supply values */
4581 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4582 "maxcapacity=100;level=25;colorantname=unknown;",
4583 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4584 "maxcapacity=100;level=75;colorantname=black;",
4585 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4586 "maxcapacity=100;level=50;colorantname=cyan;",
4587 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4588 "maxcapacity=100;level=33;colorantname=magenta;",
4589 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4590 "maxcapacity=100;level=67;colorantname=yellow;"
4591 };
4592 static const char * const printer_supply_description[] =
4593 { /* printer-supply-description values */
4594 "Toner Waste Tank",
4595 "Black Toner"
4596 };
4597 static const char * const printer_supply_description_color[] =
4598 { /* printer-supply-description values */
4599 "Ink Waste Tank",
4600 "Black Ink",
4601 "Cyan Ink",
4602 "Magenta Ink",
4603 "Yellow Ink"
4604 };
4605 static const char * const pwg_raster_document_type_supported[] =
4606 {
4607 "black_1",
4608 "sgray_8"
4609 };
4610 static const char * const pwg_raster_document_type_supported_color[] =
4611 {
4612 "black_1",
4613 "sgray_8",
4614 "srgb_8",
4615 "srgb_16"
4616 };
4617 static const char * const sides_supported[] =
4618 { /* sides-supported values */
4619 "one-sided",
4620 "two-sided-long-edge",
4621 "two-sided-short-edge"
4622 };
4623
4624
4625 /*
4626 * Open the PPD file...
4627 */
4628
4629 if ((ppd = ppdOpenFile(ppdfile)) == NULL)
4630 {
4631 ppd_status_t status; /* Load error */
4632
4633 status = ppdLastError(&i);
4634 _cupsLangPrintf(stderr, _("ippeveprinter: Unable to open \"%s\": %s on line %d."), ppdfile, ppdErrorString(status), i);
4635 return (NULL);
4636 }
4637
4638 ppdMarkDefaults(ppd);
4639
4640 pc = _ppdCacheCreateWithPPD(ppd);
4641
4642 if ((ppd_size = ppdPageSize(ppd, NULL)) != NULL)
4643 {
4644 /*
4645 * Look up default size...
4646 */
4647
4648 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
4649 {
4650 if (!strcmp(pwg_size->map.ppd, ppd_size->name))
4651 {
4652 default_size = pwg_size;
4653 break;
4654 }
4655 }
4656 }
4657
4658 if (!default_size)
4659 {
4660 /*
4661 * Default to A4 or Letter...
4662 */
4663
4664 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
4665 {
4666 if (!strcmp(pwg_size->map.ppd, "Letter") || !strcmp(pwg_size->map.ppd, "A4"))
4667 {
4668 default_size = pwg_size;
4669 break;
4670 }
4671 }
4672
4673 if (!default_size)
4674 default_size = pc->sizes; /* Last resort: first size */
4675 }
4676
2d6dcec1
MS
4677 if ((ppd_choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
4678 default_source = _ppdCacheGetSource(pc, ppd_choice->choice);
4679
4680 if ((ppd_choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL)
4681 default_source = _ppdCacheGetType(pc, ppd_choice->choice);
4682
ab8fab61
MS
4683 if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL)
4684 {
4685 /*
4686 * Use the PPD-defined default resolution...
4687 */
4688
4689 if ((i = sscanf(ppd_attr->value, "%dx%d", &xres, &yres)) == 1)
4690 yres = xres;
4691 else if (i < 0)
4692 xres = yres = 300;
4693 }
4694 else
4695 {
4696 /*
4697 * Use default of 300dpi...
4698 */
4699
4700 xres = yres = 300;
4701 }
4702
4703 snprintf(urf_rs, sizeof(urf_rs), "RS%d", yres < xres ? yres : xres);
4704
4705 num_urf = 0;
4706 urf[num_urf ++] = "V1.4";
4707 urf[num_urf ++] = "CP1";
4708 urf[num_urf ++] = urf_rs;
4709 urf[num_urf ++] = "W8";
4710 if (pc->sides_2sided_long)
4711 urf[num_urf ++] = "DM1";
4712 if (ppd->color_device)
4713 urf[num_urf ++] = "SRGB24";
4714
4715 /*
4716 * PostScript printers accept PDF via one of the CUPS PDF to PostScript
2d6dcec1 4717 * filters, along with PostScript (of course) and JPEG...
ab8fab61
MS
4718 */
4719
4720 cupsArrayAdd(docformats, "application/pdf");
7331c1b6 4721 cupsArrayAdd(docformats, "application/postscript");
2d6dcec1 4722 cupsArrayAdd(docformats, "image/jpeg");
ab8fab61
MS
4723
4724 /*
4725 * Create the attributes...
4726 */
4727
4728 attrs = ippNew();
4729
4730 /* color-supported */
4731 ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", (char)ppd->color_device);
4732
4733 /* copies-default */
4734 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
4735
4736 /* copies-supported */
4737 ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999);
1562b9a1
MS
4738
4739 /* document-password-supported */
ab8fab61
MS
4740 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 127);
4741
4742 /* finishing-template-supported */
4743 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template-supported", cupsArrayCount(pc->templates) + 1, NULL, NULL);
4744 ippSetString(attrs, &attr, 0, "none");
4745 for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
4746 ippSetString(attrs, &attr, i, template);
4747
4748 /* finishings-col-database */
4749 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-database", cupsArrayCount(pc->templates) + 1, NULL);
4750
4751 col = ippNew();
4752 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
4753 ippSetCollection(attrs, &attr, 0, col);
4754 ippDelete(col);
4755
4756 for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
4757 {
4758 col = ippNew();
4759 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template);
4760 ippSetCollection(attrs, &attr, i, col);
4761 ippDelete(col);
4762 }
4763
4764 /* finishings-col-default */
4765 col = ippNew();
4766 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
4767 ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-default", col);
4768 ippDelete(col);
4769
4770 /* finishings-col-ready */
4771 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-ready", cupsArrayCount(pc->templates) + 1, NULL);
4772
4773 col = ippNew();
4774 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
4775 ippSetCollection(attrs, &attr, 0, col);
4776 ippDelete(col);
4777
4778 for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
4779 {
4780 col = ippNew();
4781 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template);
4782 ippSetCollection(attrs, &attr, i, col);
4783 ippDelete(col);
4784 }
4785
4786 /* finishings-col-supported */
4787 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishings-col-supported", NULL, "finishing-template");
4788
4789 /* finishings-default */
4790 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
4791
4792 /* finishings-ready */
4793 attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-ready", cupsArrayCount(pc->finishings) + 1, NULL);
4794 ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
4795 for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
4796 ippSetInteger(attrs, &attr, i, finishings->value);
4797
4798 /* finishings-supported */
4799 attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", cupsArrayCount(pc->finishings) + 1, NULL);
4800 ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
4801 for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
4802 ippSetInteger(attrs, &attr, i, finishings->value);
4803
4804 /* media-bottom-margin-supported */
4805 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 ++)
4806 {
4807 for (j = 0; j < num_margins; j ++)
4808 {
4809 if (margins[j] == pwg_size->bottom)
4810 break;
4811 }
4812
4813 if (j >= num_margins)
4814 margins[num_margins ++] = pwg_size->bottom;
4815 }
4816
4817 for (i = 0; i < (num_margins - 1); i ++)
4818 {
4819 for (j = i + 1; j < num_margins; j ++)
4820 {
4821 if (margins[i] > margins[j])
4822 {
4823 int mtemp = margins[i];
4824
4825 margins[i] = margins[j];
4826 margins[j] = mtemp;
4827 }
4828 }
4829 }
4830
4831 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", num_margins, margins);
4832
4833 /* media-col-database */
4834 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", pc->num_sizes, NULL);
4835 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
4836 {
4837 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);
4838 ippSetCollection(attrs, &attr, i, col);
4839 ippDelete(col);
4840 }
4841
4842 /* media-col-default */
2d6dcec1 4843 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
4844 ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col);
4845 ippDelete(col);
4846
4847 /* media-col-ready */
2d6dcec1 4848 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
4849 ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-ready", col);
4850 ippDelete(col);
4851
4852 /* media-default */
4853 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default", NULL, default_size->map.pwg);
4854
4855 /* media-left-margin-supported */
4856 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 ++)
4857 {
4858 for (j = 0; j < num_margins; j ++)
4859 {
4860 if (margins[j] == pwg_size->left)
4861 break;
4862 }
4863
4864 if (j >= num_margins)
4865 margins[num_margins ++] = pwg_size->left;
4866 }
4867
4868 for (i = 0; i < (num_margins - 1); i ++)
4869 {
4870 for (j = i + 1; j < num_margins; j ++)
4871 {
4872 if (margins[i] > margins[j])
4873 {
4874 int mtemp = margins[i];
4875
4876 margins[i] = margins[j];
4877 margins[j] = mtemp;
4878 }
4879 }
4880 }
4881
4882 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", num_margins, margins);
4883
4884 /* media-ready */
4885 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, default_size->map.pwg);
4886
4887 /* media-right-margin-supported */
4888 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 ++)
4889 {
4890 for (j = 0; j < num_margins; j ++)
4891 {
4892 if (margins[j] == pwg_size->right)
4893 break;
4894 }
4895
4896 if (j >= num_margins)
4897 margins[num_margins ++] = pwg_size->right;
4898 }
4899
4900 for (i = 0; i < (num_margins - 1); i ++)
4901 {
4902 for (j = i + 1; j < num_margins; j ++)
4903 {
4904 if (margins[i] > margins[j])
4905 {
4906 int mtemp = margins[i];
4907
4908 margins[i] = margins[j];
4909 margins[j] = mtemp;
4910 }
4911 }
4912 }
4913
4914 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", num_margins, margins);
4915
4916 /* media-supported */
4917 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-supported", pc->num_sizes, NULL, NULL);
4918 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
4919 ippSetString(attrs, &attr, i, pwg_size->map.pwg);
4920
4921 /* media-size-supported */
4922 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", pc->num_sizes, NULL);
4923 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
4924 {
4925 col = create_media_size(pwg_size->width, pwg_size->length);
4926 ippSetCollection(attrs, &attr, i, col);
4927 ippDelete(col);
4928 }
4929
4930 /* media-source-supported */
4931 if (pc->num_sources > 0)
4932 {
4933 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source-supported", pc->num_sources, NULL, NULL);
4934 for (i = 0, pwg_map = pc->sources; i < pc->num_sources; i ++, pwg_map ++)
4935 ippSetString(attrs, &attr, i, pwg_map->pwg);
4936 }
4937 else
4938 {
4939 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", NULL, "auto");
4940 }
4941
4942 /* media-top-margin-supported */
4943 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 ++)
4944 {
4945 for (j = 0; j < num_margins; j ++)
4946 {
4947 if (margins[j] == pwg_size->top)
4948 break;
4949 }
4950
4951 if (j >= num_margins)
4952 margins[num_margins ++] = pwg_size->top;
4953 }
4954
4955 for (i = 0; i < (num_margins - 1); i ++)
4956 {
4957 for (j = i + 1; j < num_margins; j ++)
4958 {
4959 if (margins[i] > margins[j])
4960 {
4961 int mtemp = margins[i];
4962
4963 margins[i] = margins[j];
4964 margins[j] = mtemp;
4965 }
4966 }
4967 }
4968
4969 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", num_margins, margins);
4970
4971 /* media-type-supported */
4972 if (pc->num_types > 0)
4973 {
4974 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type-supported", pc->num_types, NULL, NULL);
4975 for (i = 0, pwg_map = pc->types; i < pc->num_types; i ++, pwg_map ++)
4976 ippSetString(attrs, &attr, i, pwg_map->pwg);
4977 }
4978 else
4979 {
4980 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", NULL, "auto");
4981 }
4982
4983 /* orientation-requested-default */
4984 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", IPP_ORIENT_PORTRAIT);
4985
4986 /* orientation-requested-supported */
4987 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported);
4988
4989 /* output-bin-default */
4990 if (pc->num_bins > 0)
4991 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-default", NULL, pc->bins->pwg);
4992 else
4993 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
4994
4995 /* output-bin-supported */
4996 if (pc->num_bins > 0)
4997 {
4998 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-supported", pc->num_bins, NULL, NULL);
4999 for (i = 0, pwg_map = pc->bins; i < pc->num_bins; i ++, pwg_map ++)
5000 ippSetString(attrs, &attr, i, pwg_map->pwg);
5001 }
5002 else
5003 {
5004 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
5005 }
1562b9a1
MS
5006
5007 /* overrides-supported */
ab8fab61 5008 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
5009
5010 /* page-ranges-supported */
ab8fab61
MS
5011 ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1);
5012
1562b9a1 5013 /* pages-per-minute */
ab8fab61 5014 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppd->throughput);
1562b9a1
MS
5015
5016 /* pages-per-minute-color */
ab8fab61
MS
5017 if (ppd->color_device)
5018 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppd->throughput);
5019
5020 /* print-color-mode-default */
5021 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, ppd->color_device ? "auto" : "monochrome");
5022
5023 /* print-color-mode-supported */
5024 if (ppd->color_device)
5025 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);
5026 else
5027 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);
5028
5029 /* print-content-optimize-default */
5030 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
5031
5032 /* print-content-optimize-supported */
5033 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
5034
5035 /* print-quality-default */
5036 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
5037
5038 /* print-quality-supported */
5039 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
5040
5041 /* print-rendering-intent-default */
5042 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
5043
5044 /* print-rendering-intent-supported */
5045 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
5046
5047 /* printer-device-id */
5048 if ((ppd_attr = ppdFindAttr(ppd, "1284DeviceId", NULL)) != NULL)
5049 {
5050 /*
5051 * Use the device ID string from the PPD...
5052 */
5053
5054 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, ppd_attr->value);
5055 }
5056 else
5057 {
5058 /*
5059 * Synthesize a device ID string...
5060 */
5061
5062 char device_id[1024]; /* Device ID string */
5063
5064 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;CMD:PS;", ppd->manufacturer, ppd->modelname);
5065
5066 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id);
5067 }
5068
5069 /* printer-input-tray */
2d6dcec1 5070 if (pc->num_sources > 0)
ab8fab61 5071 {
2d6dcec1
MS
5072 for (i = 0, attr = NULL; i < pc->num_sources; i ++)
5073 {
5074 char input_tray[1024]; /* printer-input-tray value */
5075
5076 if (!strcmp(pc->sources[i].pwg, "manual") || strstr(pc->sources[i].pwg, "-man") != NULL)
5077 snprintf(input_tray, sizeof(input_tray), "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=%s", pc->sources[i].pwg);
5078 else
5079 snprintf(input_tray, sizeof(input_tray), "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=125;status=0;name=%s", pc->sources[i].pwg);
5080
5081 if (attr)
5082 ippSetOctetString(attrs, &attr, i, input_tray, (int)strlen(input_tray));
5083 else
5084 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", input_tray, (int)strlen(input_tray));
5085 }
ab8fab61
MS
5086 }
5087 else
5088 {
2d6dcec1
MS
5089 static const char *printer_input_tray = "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto";
5090
5091 ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray, (int)strlen(printer_input_tray));
ab8fab61 5092 }
1562b9a1 5093
ab8fab61
MS
5094 /* printer-make-and-model */
5095 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, ppd->nickname);
5096
5097 /* printer-resolution-default */
5098 ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, xres, yres);
5099
5100 /* printer-resolution-supported */
5101 ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, xres, yres);
5102
5103 /* printer-supply and printer-supply-description */
5104 if (ppd->color_device)
5105 {
5106 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], strlen(printer_supply_color[0]));
5107 for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++)
5108 ippSetOctetString(attrs, &attr, i, printer_supply_color[i], strlen(printer_supply_color[i]));
5109
5110 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);
5111 }
5112 else
5113 {
5114 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], strlen(printer_supply[0]));
5115 for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++)
5116 ippSetOctetString(attrs, &attr, i, printer_supply[i], strlen(printer_supply[i]));
5117
5118 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);
5119 }
5120
5121 /* pwg-raster-document-xxx-supported */
5122 if (cupsArrayFind(docformats, (void *)"image/pwg-raster"))
5123 {
5124 ippAddResolution(attrs, IPP_TAG_PRINTER, "pwg-raster-document-resolution-supported", IPP_RES_PER_INCH, xres, yres);
5125
5126 if (pc->sides_2sided_long)
5127 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal");
5128
5129 if (ppd->color_device)
5130 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);
5131 else
5132 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);
5133 }
5134
5135 /* sides-default */
5136 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
5137
5138 /* sides-supported */
5139 if (pc->sides_2sided_long)
5140 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported);
5141 else
5142 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided");
5143
5144 /* urf-supported */
5145 if (cupsArrayFind(docformats, (void *)"image/urf"))
5146 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", num_urf, NULL, urf);
5147
5148 /*
5149 * Free the PPD file and return the attributes...
5150 */
5151
5152 _ppdCacheDestroy(pc);
5153
5154 ppdClose(ppd);
5155
5156 return (attrs);
f8927099 5157}
6641bd0d 5158#endif /* !CUPS_LITE */
f8927099
MS
5159
5160
0b5ce83f
MS
5161/*
5162 * 'parse_options()' - Parse URL options into CUPS options.
5163 *
5164 * The client->options string is destroyed by this function.
5165 */
5166
5167static int /* O - Number of options */
d46dbe1b 5168parse_options(ippeve_client_t *client, /* I - Client */
92dad945 5169 cups_option_t **options)/* O - Options */
0b5ce83f
MS
5170{
5171 char *name, /* Name */
5172 *value, /* Value */
5173 *next; /* Next name=value pair */
5174 int num_options = 0; /* Number of options */
5175
5176
5177 *options = NULL;
5178
5179 for (name = client->options; name && *name; name = next)
5180 {
5181 if ((value = strchr(name, '=')) == NULL)
5182 break;
5183
5184 *value++ = '\0';
5185 if ((next = strchr(value, '&')) != NULL)
5186 *next++ = '\0';
5187
5188 num_options = cupsAddOption(name, value, num_options, options);
5189 }
5190
5191 return (num_options);
5192}
5193
5194
9610a474
MS
5195/*
5196 * 'process_attr_message()' - Process an ATTR: message from a command.
5197 */
5198
5199static void
5200process_attr_message(
d46dbe1b 5201 ippeve_job_t *job, /* I - Job */
9610a474
MS
5202 char *message) /* I - Message */
5203{
58b64171
MS
5204 int i, /* Looping var */
5205 num_options = 0; /* Number of name=value pairs */
5206 cups_option_t *options = NULL, /* name=value pairs from message */
5207 *option; /* Current option */
5208 ipp_attribute_t *attr; /* Current attribute */
5209
5210
5211 /*
5212 * Grab attributes from the message line...
5213 */
5214
5215 num_options = cupsParseOptions(message + 5, num_options, &options);
5216
5217 /*
5218 * Loop through the options and record them in the printer or job objects...
5219 */
5220
5221 for (i = num_options, option = options; i > 0; i --, option ++)
5222 {
5223 if (!strcmp(option->name, "job-impressions"))
5224 {
5225 /*
5226 * Update job-impressions attribute...
5227 */
5228
5229 job->impressions = atoi(option->value);
5230 }
5231 else if (!strcmp(option->name, "job-impressions-completed"))
5232 {
5233 /*
5234 * Update job-impressions-completed attribute...
5235 */
5236
5237 job->impcompleted = atoi(option->value);
5238 }
dc84a5a4 5239 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
5240 {
5241 /*
5242 * Update Printer Status attribute...
5243 */
5244
5245 _cupsRWLockWrite(&job->printer->rwlock);
5246
5247 if ((attr = ippFindAttribute(job->printer->attrs, option->name, IPP_TAG_ZERO)) != NULL)
5248 ippDeleteAttribute(job->printer->attrs, attr);
5249
5250 cupsEncodeOption(job->printer->attrs, IPP_TAG_PRINTER, option->name, option->value);
5251
5252 _cupsRWUnlock(&job->printer->rwlock);
5253 }
5254 else
5255 {
5256 /*
5257 * Something else that isn't currently supported...
5258 */
5259
5260 fprintf(stderr, "[Job %d] Ignoring update of attribute \"%s\" with value \"%s\".\n", job->id, option->name, option->value);
5261 }
5262 }
5263
5264 cupsFreeOptions(num_options, options);
9610a474
MS
5265}
5266
5267
83e08001
MS
5268/*
5269 * 'process_client()' - Process client requests on a thread.
5270 */
5271
5272static void * /* O - Exit status */
d46dbe1b 5273process_client(ippeve_client_t *client) /* I - Client */
83e08001
MS
5274{
5275 /*
5276 * Loop until we are out of requests or timeout (30 seconds)...
5277 */
5278
f93b32b6
MS
5279#ifdef HAVE_SSL
5280 int first_time = 1; /* First time request? */
5281#endif /* HAVE_SSL */
5282
a469f8a5 5283 while (httpWait(client->http, 30000))
f93b32b6
MS
5284 {
5285#ifdef HAVE_SSL
5286 if (first_time)
5287 {
5288 /*
5289 * See if we need to negotiate a TLS connection...
5290 */
5291
5292 char buf[1]; /* First byte from client */
5293
5294 if (recv(httpGetFd(client->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0])))
5295 {
fe202ff4 5296 fprintf(stderr, "%s Starting HTTPS session.\n", client->hostname);
f93b32b6
MS
5297
5298 if (httpEncryption(client->http, HTTP_ENCRYPTION_ALWAYS))
5299 {
5300 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
5301 break;
5302 }
fe202ff4
MS
5303
5304 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
f93b32b6
MS
5305 }
5306
5307 first_time = 0;
5308 }
5309#endif /* HAVE_SSL */
5310
83e08001
MS
5311 if (!process_http(client))
5312 break;
f93b32b6 5313 }
83e08001
MS
5314
5315 /*
5316 * Close the conection to the client and return...
5317 */
5318
5319 delete_client(client);
5320
5321 return (NULL);
5322}
5323
5324
5325/*
5326 * 'process_http()' - Process a HTTP request.
5327 */
5328
5329int /* O - 1 on success, 0 on failure */
d46dbe1b 5330process_http(ippeve_client_t *client) /* I - Client connection */
83e08001 5331{
a469f8a5
MS
5332 char uri[1024]; /* URI */
5333 http_state_t http_state; /* HTTP state */
5334 http_status_t http_status; /* HTTP status */
5335 ipp_state_t ipp_state; /* State of IPP transfer */
5336 char scheme[32], /* Method/scheme */
5337 userpass[128], /* Username:password */
5338 hostname[HTTP_MAX_HOST];
5339 /* Hostname */
5340 int port; /* Port number */
a469f8a5
MS
5341 static const char * const http_states[] =
5342 { /* Strings for logging HTTP method */
5343 "WAITING",
5344 "OPTIONS",
5345 "GET",
5346 "GET_SEND",
5347 "HEAD",
5348 "POST",
5349 "POST_RECV",
5350 "POST_SEND",
5351 "PUT",
5352 "PUT_RECV",
5353 "DELETE",
5354 "TRACE",
5355 "CONNECT",
5356 "STATUS",
5357 "UNKNOWN_METHOD",
5358 "UNKNOWN_VERSION"
5359 };
83e08001 5360
83e08001
MS
5361
5362 /*
5363 * Clear state variables...
5364 */
5365
83e08001
MS
5366 ippDelete(client->request);
5367 ippDelete(client->response);
5368
a469f8a5
MS
5369 client->request = NULL;
5370 client->response = NULL;
5371 client->operation = HTTP_STATE_WAITING;
83e08001
MS
5372
5373 /*
5374 * Read a request from the connection...
5375 */
5376
a469f8a5
MS
5377 while ((http_state = httpReadRequest(client->http, uri,
5378 sizeof(uri))) == HTTP_STATE_WAITING)
5379 usleep(1);
83e08001
MS
5380
5381 /*
5382 * Parse the request line...
5383 */
5384
a469f8a5 5385 if (http_state == HTTP_STATE_ERROR)
83e08001 5386 {
a469f8a5
MS
5387 if (httpError(client->http) == EPIPE)
5388 fprintf(stderr, "%s Client closed connection.\n", client->hostname);
5389 else
6d56631f 5390 fprintf(stderr, "%s Bad request line (%s).\n", client->hostname, strerror(httpError(client->http)));
83e08001 5391
a469f8a5 5392 return (0);
83e08001 5393 }
a469f8a5 5394 else if (http_state == HTTP_STATE_UNKNOWN_METHOD)
83e08001 5395 {
a469f8a5
MS
5396 fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname);
5397 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5398 return (0);
1106b00e 5399 }
a469f8a5 5400 else if (http_state == HTTP_STATE_UNKNOWN_VERSION)
1106b00e 5401 {
a469f8a5
MS
5402 fprintf(stderr, "%s Bad HTTP version.\n", client->hostname);
5403 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5404 return (0);
1106b00e
MS
5405 }
5406
6d56631f 5407 fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state], uri);
a469f8a5 5408
1106b00e 5409 /*
a469f8a5 5410 * Separate the URI into its components...
1106b00e
MS
5411 */
5412
a469f8a5
MS
5413 if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
5414 userpass, sizeof(userpass),
5415 hostname, sizeof(hostname), &port,
fe202ff4
MS
5416 client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK &&
5417 (http_state != HTTP_STATE_OPTIONS || strcmp(uri, "*")))
1106b00e 5418 {
a469f8a5
MS
5419 fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri);
5420 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
5421 return (0);
5422 }
5423
0b5ce83f
MS
5424 if ((client->options = strchr(client->uri, '?')) != NULL)
5425 *(client->options)++ = '\0';
5426
a469f8a5
MS
5427 /*
5428 * Process the request...
5429 */
5430
5431 client->start = time(NULL);
5432 client->operation = httpGetState(client->http);
1106b00e
MS
5433
5434 /*
5435 * Parse incoming parameters until the status changes...
5436 */
5437
a469f8a5 5438 while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE);
1106b00e 5439
a469f8a5 5440 if (http_status != HTTP_STATUS_OK)
1106b00e 5441 {
a469f8a5 5442 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
5443 return (0);
5444 }
5445
a469f8a5
MS
5446 if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] &&
5447 httpGetVersion(client->http) >= HTTP_VERSION_1_1)
1106b00e
MS
5448 {
5449 /*
5450 * HTTP/1.1 and higher require the "Host:" field...
5451 */
5452
a469f8a5 5453 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
5454 return (0);
5455 }
5456
5457 /*
5458 * Handle HTTP Upgrade...
5459 */
5460
2cadf0f4 5461 if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION),
a469f8a5 5462 "Upgrade"))
1106b00e 5463 {
fe202ff4
MS
5464#ifdef HAVE_SSL
5465 if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http))
5466 {
5467 if (!respond_http(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0))
5468 return (0);
5469
5470 fprintf(stderr, "%s Upgrading to encrypted connection.\n", client->hostname);
5471
5472 if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED))
5473 {
5474 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
5475 return (0);
5476 }
5477
5478 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
5479 }
5480 else
5481#endif /* HAVE_SSL */
5482
a469f8a5 5483 if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
1106b00e
MS
5484 return (0);
5485 }
5486
e60ec91f
MS
5487 /*
5488 * Handle HTTP Expect...
5489 */
5490
a469f8a5
MS
5491 if (httpGetExpect(client->http) &&
5492 (client->operation == HTTP_STATE_POST ||
5493 client->operation == HTTP_STATE_PUT))
e60ec91f 5494 {
a469f8a5 5495 if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
e60ec91f
MS
5496 {
5497 /*
5498 * Send 100-continue header...
5499 */
5500
a469f8a5 5501 if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
e60ec91f
MS
5502 return (0);
5503 }
5504 else
5505 {
5506 /*
5507 * Send 417-expectation-failed header...
5508 */
5509
a469f8a5 5510 if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
e60ec91f 5511 return (0);
e60ec91f
MS
5512 }
5513 }
5514
1106b00e
MS
5515 /*
5516 * Handle new transfers...
5517 */
5518
5519 switch (client->operation)
5520 {
a469f8a5 5521 case HTTP_STATE_OPTIONS :
1106b00e 5522 /*
fe202ff4 5523 * Do OPTIONS command...
1106b00e
MS
5524 */
5525
a469f8a5 5526 return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
1106b00e 5527
a469f8a5 5528 case HTTP_STATE_HEAD :
1106b00e 5529 if (!strcmp(client->uri, "/icon.png"))
a469f8a5 5530 return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
0b5ce83f 5531 else if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies"))
a469f8a5 5532 return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
1106b00e 5533 else
a469f8a5 5534 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e 5535
a469f8a5 5536 case HTTP_STATE_GET :
1106b00e
MS
5537 if (!strcmp(client->uri, "/icon.png"))
5538 {
5539 /*
5540 * Send PNG icon file.
5541 */
5542
3e5092db
MS
5543 if (client->printer->icon)
5544 {
5545 int fd; /* Icon file */
5546 struct stat fileinfo; /* Icon file information */
5547 char buffer[4096]; /* Copy buffer */
5548 ssize_t bytes; /* Bytes */
1106b00e 5549
3e5092db 5550 fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon);
a469f8a5 5551
3e5092db 5552 if (!stat(client->printer->icon, &fileinfo) && (fd = open(client->printer->icon, O_RDONLY)) >= 0)
1106b00e 5553 {
3e5092db
MS
5554 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
5555 {
5556 close(fd);
5557 return (0);
5558 }
5559
5560 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
5561 httpWrite2(client->http, buffer, (size_t)bytes);
5562
5563 httpFlushWrite(client->http);
5564
1106b00e 5565 close(fd);
1106b00e 5566 }
3e5092db
MS
5567 else
5568 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
5569 }
5570 else
5571 {
5572 fputs("Icon file is internal printer.png.\n", stderr);
1106b00e 5573
3e5092db
MS
5574 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_png)))
5575 return (0);
1106b00e 5576
3e5092db 5577 httpWrite2(client->http, (const char *)printer_png, sizeof(printer_png));
a469f8a5 5578 httpFlushWrite(client->http);
1106b00e 5579 }
1106b00e
MS
5580 }
5581 else if (!strcmp(client->uri, "/"))
5582 {
5583 /*
5584 * Show web status page...
5585 */
5586
9141aa01 5587 return (show_status(client));
0b5ce83f
MS
5588 }
5589 else if (!strcmp(client->uri, "/media"))
5590 {
5591 /*
5592 * Show web media page...
5593 */
5594
9141aa01 5595 return (show_media(client));
0b5ce83f
MS
5596 }
5597 else if (!strcmp(client->uri, "/supplies"))
5598 {
5599 /*
5600 * Show web supplies page...
5601 */
5602
9141aa01 5603 return (show_supplies(client));
1106b00e
MS
5604 }
5605 else
a469f8a5 5606 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e
MS
5607 break;
5608
a469f8a5
MS
5609 case HTTP_STATE_POST :
5610 if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
1106b00e
MS
5611 "application/ipp"))
5612 {
5613 /*
5614 * Not an IPP request...
5615 */
5616
a469f8a5 5617 return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
1106b00e
MS
5618 }
5619
5620 /*
5621 * Read the IPP request...
5622 */
5623
5624 client->request = ippNew();
5625
a469f8a5
MS
5626 while ((ipp_state = ippRead(client->http,
5627 client->request)) != IPP_STATE_DATA)
5628 {
5629 if (ipp_state == IPP_STATE_ERROR)
1106b00e 5630 {
6d56631f 5631 fprintf(stderr, "%s IPP read error (%s).\n", client->hostname, cupsLastErrorString());
a469f8a5 5632 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
5633 return (0);
5634 }
a469f8a5 5635 }
1106b00e
MS
5636
5637 /*
5638 * Now that we have the IPP request, process the request...
5639 */
5640
5641 return (process_ipp(client));
5642
5643 default :
5644 break; /* Anti-compiler-warning-code */
5645 }
5646
5647 return (1);
5648}
5649
5650
5651/*
5652 * 'process_ipp()' - Process an IPP request.
5653 */
5654
5655static int /* O - 1 on success, 0 on error */
d46dbe1b 5656process_ipp(ippeve_client_t *client) /* I - Client */
1106b00e
MS
5657{
5658 ipp_tag_t group; /* Current group tag */
5659 ipp_attribute_t *attr; /* Current attribute */
5660 ipp_attribute_t *charset; /* Character set attribute */
5661 ipp_attribute_t *language; /* Language attribute */
5662 ipp_attribute_t *uri; /* Printer URI attribute */
a469f8a5
MS
5663 int major, minor; /* Version number */
5664 const char *name; /* Name of attribute */
1106b00e
MS
5665
5666
83e08001 5667 debug_attributes("Request", client->request, 1);
1106b00e
MS
5668
5669 /*
5670 * First build an empty response message for this request...
5671 */
5672
a469f8a5
MS
5673 client->operation_id = ippGetOperation(client->request);
5674 client->response = ippNewResponse(client->request);
1106b00e
MS
5675
5676 /*
5677 * Then validate the request header and required attributes...
5678 */
5679
a469f8a5
MS
5680 major = ippGetVersion(client->request, &minor);
5681
5682 if (major < 1 || major > 2)
1106b00e
MS
5683 {
5684 /*
5685 * Return an error, since we only support IPP 1.x and 2.x.
5686 */
5687
b969d5af
MS
5688 respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, "Bad request version number %d.%d.", major, minor);
5689 }
5690 else if ((major * 10 + minor) > MaxVersion)
5691 {
5692 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
5693 httpFlush(client->http); /* Flush trailing (junk) data */
5694
5695 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5696 return (0);
1106b00e 5697 }
a469f8a5 5698 else if (ippGetRequestId(client->request) <= 0)
b969d5af
MS
5699 {
5700 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.", ippGetRequestId(client->request));
5701 }
a469f8a5 5702 else if (!ippFirstAttribute(client->request))
b969d5af
MS
5703 {
5704 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No attributes in request.");
5705 }
1106b00e
MS
5706 else
5707 {
5708 /*
5709 * Make sure that the attributes are provided in the correct order and
5710 * don't repeat groups...
5711 */
5712
a469f8a5
MS
5713 for (attr = ippFirstAttribute(client->request),
5714 group = ippGetGroupTag(attr);
1106b00e 5715 attr;
a469f8a5
MS
5716 attr = ippNextAttribute(client->request))
5717 {
5718 if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
1106b00e
MS
5719 {
5720 /*
5721 * Out of order; return an error...
5722 */
5723
a469f8a5
MS
5724 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5725 "Attribute groups are out of order (%x < %x).",
5726 ippGetGroupTag(attr), group);
1106b00e
MS
5727 break;
5728 }
5729 else
a469f8a5
MS
5730 group = ippGetGroupTag(attr);
5731 }
1106b00e
MS
5732
5733 if (!attr)
5734 {
5735 /*
5736 * Then make sure that the first three attributes are:
5737 *
5738 * attributes-charset
5739 * attributes-natural-language
5740 * printer-uri/job-uri
5741 */
5742
a469f8a5
MS
5743 attr = ippFirstAttribute(client->request);
5744 name = ippGetName(attr);
5745 if (attr && name && !strcmp(name, "attributes-charset") &&
5746 ippGetValueTag(attr) == IPP_TAG_CHARSET)
1106b00e
MS
5747 charset = attr;
5748 else
5749 charset = NULL;
5750
a469f8a5
MS
5751 attr = ippNextAttribute(client->request);
5752 name = ippGetName(attr);
1106b00e 5753
a469f8a5
MS
5754 if (attr && name && !strcmp(name, "attributes-natural-language") &&
5755 ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
1106b00e
MS
5756 language = attr;
5757 else
5758 language = NULL;
5759
5760 if ((attr = ippFindAttribute(client->request, "printer-uri",
5761 IPP_TAG_URI)) != NULL)
5762 uri = attr;
5763 else if ((attr = ippFindAttribute(client->request, "job-uri",
5764 IPP_TAG_URI)) != NULL)
5765 uri = attr;
5766 else
5767 uri = NULL;
5768
1106b00e 5769 if (charset &&
2cadf0f4
MS
5770 strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
5771 strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
1106b00e
MS
5772 {
5773 /*
5774 * Bad character set...
5775 */
5776
a469f8a5 5777 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
1106b00e 5778 "Unsupported character set \"%s\".",
a469f8a5 5779 ippGetString(charset, 0, NULL));
1106b00e
MS
5780 }
5781 else if (!charset || !language || !uri)
5782 {
5783 /*
5784 * Return an error, since attributes-charset,
5785 * attributes-natural-language, and printer-uri/job-uri are required
5786 * for all operations.
5787 */
5788
a469f8a5
MS
5789 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5790 "Missing required attributes.");
1106b00e 5791 }
1106b00e
MS
5792 else
5793 {
8a78aa37
MS
5794 char scheme[32], /* URI scheme */
5795 userpass[32], /* Username/password in URI */
5796 host[256], /* Host name in URI */
5797 resource[256]; /* Resource path in URI */
5798 int port; /* Port number in URI */
5799
5800 name = ippGetName(uri);
5801
5802 if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
5803 scheme, sizeof(scheme),
5804 userpass, sizeof(userpass),
5805 host, sizeof(host), &port,
5806 resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
5807 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
5808 "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
5809 else if ((!strcmp(name, "job-uri") &&
5810 strncmp(resource, "/ipp/print/", 11)) ||
5811 (!strcmp(name, "printer-uri") &&
5812 strcmp(resource, "/ipp/print")))
5813 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
5814 name, ippGetString(uri, 0, NULL));
5815 else
1106b00e
MS
5816 {
5817 /*
8a78aa37 5818 * Try processing the operation...
1106b00e
MS
5819 */
5820
8a78aa37
MS
5821 switch (ippGetOperation(client->request))
5822 {
5823 case IPP_OP_PRINT_JOB :
5824 ipp_print_job(client);
5825 break;
5826
5827 case IPP_OP_PRINT_URI :
5828 ipp_print_uri(client);
5829 break;
5830
5831 case IPP_OP_VALIDATE_JOB :
5832 ipp_validate_job(client);
5833 break;
5834
5835 case IPP_OP_CREATE_JOB :
5836 ipp_create_job(client);
5837 break;
5838
5839 case IPP_OP_SEND_DOCUMENT :
5840 ipp_send_document(client);
5841 break;
5842
5843 case IPP_OP_SEND_URI :
5844 ipp_send_uri(client);
5845 break;
5846
5847 case IPP_OP_CANCEL_JOB :
5848 ipp_cancel_job(client);
5849 break;
5850
5851 case IPP_OP_GET_JOB_ATTRIBUTES :
5852 ipp_get_job_attributes(client);
5853 break;
5854
5855 case IPP_OP_GET_JOBS :
5856 ipp_get_jobs(client);
5857 break;
5858
5859 case IPP_OP_GET_PRINTER_ATTRIBUTES :
5860 ipp_get_printer_attributes(client);
5861 break;
5862
2cadf0f4
MS
5863 case IPP_OP_CLOSE_JOB :
5864 ipp_close_job(client);
5865 break;
5866
5867 case IPP_OP_IDENTIFY_PRINTER :
5868 ipp_identify_printer(client);
5869 break;
5870
8a78aa37
MS
5871 default :
5872 respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
5873 "Operation not supported.");
5874 break;
5875 }
1106b00e
MS
5876 }
5877 }
5878 }
5879 }
5880
5881 /*
5882 * Send the HTTP header and return...
5883 */
5884
a469f8a5
MS
5885 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
5886 httpFlush(client->http); /* Flush trailing (junk) data */
1106b00e 5887
a469f8a5 5888 return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
1106b00e
MS
5889 ippLength(client->response)));
5890}
5891
5892
5893/*
5894 * 'process_job()' - Process a print job.
5895 */
5896
5897static void * /* O - Thread exit status */
d46dbe1b 5898process_job(ippeve_job_t *job) /* I - Job */
1106b00e 5899{
a469f8a5
MS
5900 job->state = IPP_JSTATE_PROCESSING;
5901 job->printer->state = IPP_PSTATE_PROCESSING;
4a838088 5902 job->processing = time(NULL);
1106b00e 5903
d46dbe1b 5904 while (job->printer->state_reasons & IPPEVE_PREASON_MEDIA_EMPTY)
0b5ce83f 5905 {
d46dbe1b 5906 job->printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
0b5ce83f
MS
5907
5908 sleep(1);
5909 }
5910
d46dbe1b 5911 job->printer->state_reasons &= (ippeve_preason_t)~IPPEVE_PREASON_MEDIA_NEEDED;
0b5ce83f 5912
db8b865d
MS
5913 if (job->printer->command)
5914 {
5915 /*
5916 * Execute a command with the job spool file and wait for it to complete...
5917 */
5918
aa2a90ce
MS
5919 int pid, /* Process ID */
5920 status; /* Exit status */
58b64171 5921 struct timeval start, /* Start time */
aa2a90ce
MS
5922 end; /* End time */
5923 char *myargv[3], /* Command-line arguments */
5924 *myenvp[400]; /* Environment variables */
5925 int myenvc; /* Number of environment variables */
5926 ipp_attribute_t *attr; /* Job attribute */
5927 char val[1280], /* IPP_NAME=value */
5928 *valptr; /* Pointer into string */
24a06ed3 5929#ifndef _WIN32
cc108616 5930 int mystdout = -1; /* File for stdout */
aa2a90ce
MS
5931 int mypipe[2]; /* Pipe for stderr */
5932 char line[2048], /* Line from stderr */
5933 *ptr, /* Pointer into line */
5934 *endptr; /* End of line */
5935 ssize_t bytes; /* Bytes read */
24a06ed3 5936#endif /* !_WIN32 */
db8b865d 5937
58b64171
MS
5938 fprintf(stderr, "[Job %d] Running command \"%s %s\".\n", job->id, job->printer->command, job->filename);
5939 gettimeofday(&start, NULL);
db8b865d 5940
83ce8172
MS
5941 /*
5942 * Setup the command-line arguments...
5943 */
5944
5945 myargv[0] = job->printer->command;
5946 myargv[1] = job->filename;
5947 myargv[2] = NULL;
5948
5949 /*
aa2a90ce
MS
5950 * Copy the current environment, then add environment variables for every
5951 * Job attribute and Printer -default attributes...
83ce8172
MS
5952 */
5953
5954 for (myenvc = 0; environ[myenvc] && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); myenvc ++)
5955 myenvp[myenvc] = strdup(environ[myenvc]);
5956
aa2a90ce
MS
5957 if (myenvc > (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 32))
5958 {
58b64171 5959 fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id);
aa2a90ce
MS
5960 job->state = IPP_JSTATE_ABORTED;
5961 goto error;
5962 }
5963
5964 if (asprintf(myenvp + myenvc, "CONTENT_TYPE=%s", job->format) > 0)
5965 myenvc ++;
5966
5967 if (job->printer->device_uri && asprintf(myenvp + myenvc, "DEVICE_URI=%s", job->printer->device_uri) > 0)
5968 myenvc ++;
5969
6641bd0d 5970#if !CUPS_LITE
aa2a90ce
MS
5971 if (job->printer->ppdfile && asprintf(myenvp + myenvc, "PPD=%s", job->printer->ppdfile) > 0)
5972 myenvc ++;
6641bd0d 5973#endif /* !CUPS_LITE */
aa2a90ce 5974
aa2a90ce
MS
5975 for (attr = ippFirstAttribute(job->printer->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->printer->attrs))
5976 {
5977 /*
5978 * Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and
5979 * then add the value(s) from the attribute.
5980 */
5981
5982 const char *name = ippGetName(attr),
5983 /* Attribute name */
5984 *suffix = strstr(name, "-default");
5985 /* Suffix on attribute name */
5986
5987 if (!suffix || suffix[8])
5988 continue;
5989
5990 valptr = val;
5991 *valptr++ = 'I';
5992 *valptr++ = 'P';
5993 *valptr++ = 'P';
5994 *valptr++ = '_';
5995 while (*name && valptr < (val + sizeof(val) - 2))
5996 {
5997 if (*name == '-')
5998 *valptr++ = '_';
5999 else
6000 *valptr++ = (char)toupper(*name & 255);
6001
6002 name ++;
6003 }
6004 *valptr++ = '=';
6005 ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
6006
6007 myenvp[myenvc++] = strdup(val);
6008 }
6009
83ce8172
MS
6010 for (attr = ippFirstAttribute(job->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->attrs))
6011 {
6012 /*
6013 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6014 * value(s) from the attribute.
6015 */
6016
6017 const char *name = ippGetName(attr);
aa2a90ce
MS
6018 /* Attribute name */
6019
83ce8172
MS
6020 if (!name)
6021 continue;
6022
6023 valptr = val;
6024 *valptr++ = 'I';
6025 *valptr++ = 'P';
6026 *valptr++ = 'P';
6027 *valptr++ = '_';
6028 while (*name && valptr < (val + sizeof(val) - 2))
6029 {
6030 if (*name == '-')
6031 *valptr++ = '_';
6032 else
6033 *valptr++ = (char)toupper(*name & 255);
6034
6035 name ++;
6036 }
6037 *valptr++ = '=';
6038 ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
6039
6040 myenvp[myenvc++] = strdup(val);
6041 }
aa2a90ce
MS
6042
6043 if (attr)
6044 {
58b64171 6045 fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id);
aa2a90ce
MS
6046 job->state = IPP_JSTATE_ABORTED;
6047 goto error;
6048 }
6049
83ce8172
MS
6050 myenvp[myenvc] = NULL;
6051
6052 /*
6053 * Now run the program...
6054 */
6055
24a06ed3 6056#ifdef _WIN32
83ce8172 6057 status = _spawnvpe(_P_WAIT, job->printer->command, myargv, myenvp);
9610a474 6058
83ce8172 6059#else
cc108616
MS
6060 if (job->printer->device_uri)
6061 {
6062 char scheme[32], /* URI scheme */
6063 userpass[256], /* username:password (unused) */
6064 host[256], /* Hostname or IP address */
6065 resource[256]; /* Resource path */
6066 int port; /* Port number */
6067
6068
6069 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)
6070 {
58b64171 6071 fprintf(stderr, "[Job %d] Bad device URI \"%s\".\n", job->id, job->printer->device_uri);
cc108616
MS
6072 }
6073 else if (!strcmp(scheme, "file"))
6074 {
6075 struct stat fileinfo; /* See if this is a file or directory... */
6076
6077 if (stat(resource, &fileinfo))
6078 {
6079 if (errno == ENOENT)
6080 {
dd2b6166 6081 if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC, 0666)) >= 0)
58b64171 6082 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
dd2b6166 6083 else
58b64171 6084 fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno));
cc108616
MS
6085 }
6086 else
58b64171 6087 fprintf(stderr, "[Job %d] Unable to access \"%s\": %s\n", job->id, resource, strerror(errno));
cc108616
MS
6088 }
6089 else if (S_ISDIR(fileinfo.st_mode))
6090 {
dd2b6166 6091 if ((mystdout = create_job_file(job, line, sizeof(line), resource, "prn")) >= 0)
58b64171 6092 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line);
dd2b6166 6093 else
58b64171 6094 fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, line, strerror(errno));
cc108616
MS
6095 }
6096 else if (!S_ISREG(fileinfo.st_mode))
6097 {
dd2b6166 6098 if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC, 0666)) >= 0)
58b64171 6099 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
dd2b6166 6100 else
58b64171 6101 fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno));
cc108616 6102 }
dd2b6166 6103 else if ((mystdout = open(resource, O_WRONLY)) >= 0)
58b64171 6104 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
dd2b6166 6105 else
58b64171 6106 fprintf(stderr, "[Job %d] Unable to open \"%s\": %s\n", job->id, resource, strerror(errno));
cc108616
MS
6107 }
6108 else if (!strcmp(scheme, "socket"))
6109 {
6110 http_addrlist_t *addrlist; /* List of addresses */
6111 char service[32]; /* Service number */
6112
6113 snprintf(service, sizeof(service), "%d", port);
6114
6115 if ((addrlist = httpAddrGetList(host, AF_UNSPEC, service)) == NULL)
58b64171 6116 fprintf(stderr, "[Job %d] Unable to find \"%s\": %s\n", job->id, host, cupsLastErrorString());
cc108616 6117 else if (!httpAddrConnect2(addrlist, &mystdout, 30000, &(job->cancel)))
58b64171 6118 fprintf(stderr, "[Job %d] Unable to connect to \"%s\": %s\n", job->id, host, cupsLastErrorString());
cc108616
MS
6119
6120 httpAddrFreeList(addrlist);
6121 }
6122 else
6123 {
58b64171 6124 fprintf(stderr, "[Job %d] Unsupported device URI scheme \"%s\".\n", job->id, scheme);
cc108616
MS
6125 }
6126 }
dd2b6166 6127 else if ((mystdout = create_job_file(job, line, sizeof(line), job->printer->directory, "prn")) >= 0)
cc108616 6128 {
58b64171 6129 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line);
cc108616
MS
6130 }
6131
6132 if (mystdout < 0)
6133 mystdout = open("/dev/null", O_WRONLY);
6134
9610a474
MS
6135 if (pipe(mypipe))
6136 {
58b64171 6137 fprintf(stderr, "[Job %d] Unable to create pipe for stderr: %s\n", job->id, strerror(errno));
9610a474
MS
6138 mypipe[0] = mypipe[1] = -1;
6139 }
6140
db8b865d
MS
6141 if ((pid = fork()) == 0)
6142 {
6143 /*
6144 * Child comes here...
6145 */
6146
cc108616
MS
6147 close(1);
6148 dup2(mystdout, 1);
6149 close(mystdout);
6150
9610a474
MS
6151 close(2);
6152 dup2(mypipe[1], 2);
6153 close(mypipe[0]);
6154 close(mypipe[1]);
6155
83ce8172 6156 execve(job->printer->command, myargv, myenvp);
db8b865d
MS
6157 exit(errno);
6158 }
6159 else if (pid < 0)
6160 {
6161 /*
6162 * Unable to fork process...
6163 */
6164
58b64171 6165 fprintf(stderr, "[Job %d] Unable to start job processing command: %s\n", job->id, strerror(errno));
83ce8172 6166 status = -1;
1ec50c42 6167
cc108616 6168 close(mystdout);
9610a474
MS
6169 close(mypipe[0]);
6170 close(mypipe[1]);
6171
1ec50c42
MS
6172 /*
6173 * Free memory used for environment...
6174 */
6175
6176 while (myenvc > 0)
6177 free(myenvp[-- myenvc]);
db8b865d
MS
6178 }
6179 else
6180 {
83ce8172
MS
6181 /*
6182 * Free memory used for environment...
6183 */
6184
6185 while (myenvc > 0)
6186 free(myenvp[-- myenvc]);
9610a474 6187
cc108616
MS
6188 /*
6189 * Close the output file in the parent process...
6190 */
6191
6192 close(mystdout);
6193
9610a474
MS
6194 /*
6195 * If the pipe exists, read from it until EOF...
6196 */
6197
6198 if (mypipe[0] >= 0)
6199 {
6200 close(mypipe[1]);
6201
6202 endptr = line;
6203 while ((bytes = read(mypipe[0], endptr, sizeof(line) - (size_t)(endptr - line) - 1)) > 0)
6204 {
6205 endptr += bytes;
6206 *endptr = '\0';
6207
6208 while ((ptr = strchr(line, '\n')) != NULL)
6209 {
dc84a5a4
MS
6210 int level = 3; /* Message log level */
6211
9610a474
MS
6212 *ptr++ = '\0';
6213
dc84a5a4
MS
6214 if (!strncmp(line, "ATTR:", 5))
6215 {
6216 /*
6217 * Process job/printer attribute updates.
6218 */
58b64171 6219
dc84a5a4
MS
6220 process_attr_message(job, line);
6221 }
6222 else if (!strncmp(line, "DEBUG:", 6))
9610a474
MS
6223 {
6224 /*
dc84a5a4 6225 * Debug message...
9610a474
MS
6226 */
6227
dc84a5a4 6228 level = 2;
9610a474 6229 }
dc84a5a4 6230 else if (!strncmp(line, "ERROR:", 6))
9610a474
MS
6231 {
6232 /*
dc84a5a4 6233 * Error message...
9610a474
MS
6234 */
6235
dc84a5a4
MS
6236 level = 0;
6237 job->message = strdup(line + 6);
6238 job->msglevel = 0;
9610a474 6239 }
dc84a5a4
MS
6240 else if (!strncmp(line, "INFO:", 5))
6241 {
6242 /*
6243 * Informational/progress message...
6244 */
6245
6246 level = 1;
6247 if (job->msglevel)
6248 {
6249 job->message = strdup(line + 5);
6250 job->msglevel = 1;
6251 }
6252 }
6253 else if (!strncmp(line, "STATE:", 6))
6254 {
6255 /*
6256 * Process printer-state-reasons keywords.
6257 */
6258
6259 process_state_message(job, line);
6260 }
6261
6262 if (Verbosity >= level)
6263 fprintf(stderr, "[Job %d] Command - %s\n", job->id, line);
9610a474
MS
6264
6265 bytes = ptr - line;
6266 if (ptr < endptr)
63efa616 6267 memmove(line, ptr, (size_t)(endptr - ptr));
9610a474
MS
6268 endptr -= bytes;
6269 *endptr = '\0';
6270 }
6271 }
6272
6273 close(mypipe[0]);
6274 }
6275
db8b865d
MS
6276 /*
6277 * Wait for child to complete...
6278 */
6279
83ce8172 6280# ifdef HAVE_WAITPID
db8b865d 6281 while (waitpid(pid, &status, 0) < 0);
83ce8172 6282# else
db8b865d 6283 while (wait(&status) < 0);
83ce8172
MS
6284# endif /* HAVE_WAITPID */
6285 }
24a06ed3 6286#endif /* _WIN32 */
db8b865d 6287
83ce8172
MS
6288 if (status)
6289 {
24a06ed3 6290#ifndef _WIN32
83ce8172 6291 if (WIFEXITED(status))
24a06ed3 6292#endif /* !_WIN32 */
58b64171 6293 fprintf(stderr, "[Job %d] Command \"%s\" exited with status %d.\n", job->id, job->printer->command, WEXITSTATUS(status));
24a06ed3 6294#ifndef _WIN32
db8b865d 6295 else
58b64171 6296 fprintf(stderr, "[Job %d] Command \"%s\" terminated with signal %d.\n", job->id, job->printer->command, WTERMSIG(status));
24a06ed3 6297#endif /* !_WIN32 */
83ce8172 6298 job->state = IPP_JSTATE_ABORTED;
db8b865d 6299 }
83ce8172
MS
6300 else if (status < 0)
6301 job->state = IPP_JSTATE_ABORTED;
6302 else
58b64171 6303 fprintf(stderr, "[Job %d] Command \"%s\" completed successfully.\n", job->id, job->printer->command);
db8b865d
MS
6304
6305 /*
58b64171 6306 * Report the total processing time...
db8b865d
MS
6307 */
6308
58b64171
MS
6309 gettimeofday(&end, NULL);
6310
6311 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
6312 }
6313 else
6314 {
6315 /*
6316 * Sleep for a random amount of time to simulate job processing.
6317 */
6318
7d5824d6 6319 sleep((unsigned)(5 + (rand() % 11)));
db8b865d 6320 }
1106b00e
MS
6321
6322 if (job->cancel)
a469f8a5 6323 job->state = IPP_JSTATE_CANCELED;
4a838088 6324 else if (job->state == IPP_JSTATE_PROCESSING)
a469f8a5 6325 job->state = IPP_JSTATE_COMPLETED;
1106b00e 6326
aa2a90ce
MS
6327 error:
6328
1106b00e 6329 job->completed = time(NULL);
a469f8a5 6330 job->printer->state = IPP_PSTATE_IDLE;
1106b00e
MS
6331 job->printer->active_job = NULL;
6332
6333 return (NULL);
6334}
6335
6336
9610a474
MS
6337/*
6338 * 'process_state_message()' - Process a STATE: message from a command.
6339 */
6340
6341static void
6342process_state_message(
d46dbe1b 6343 ippeve_job_t *job, /* I - Job */
9610a474
MS
6344 char *message) /* I - Message */
6345{
6346 int i; /* Looping var */
d46dbe1b 6347 ippeve_preason_t state_reasons, /* printer-state-reasons values */
9610a474
MS
6348 bit; /* Current reason bit */
6349 char *ptr, /* Pointer into message */
6350 *next; /* Next keyword in message */
6351 int remove; /* Non-zero if we are removing keywords */
6352
6353
6354 /*
6355 * Skip leading "STATE:" and any whitespace...
6356 */
6357
6358 for (message += 6; *message; message ++)
6359 if (*message != ' ' && *message != '\t')
6360 break;
6361
6362 /*
6363 * Support the following forms of message:
6364 *
6365 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6366 *
6367 * "-keyword[,keyword,...]" to remove keywords.
6368 *
6369 * "+keyword[,keyword,...]" to add keywords.
6370 *
6371 * Keywords may or may not have a suffix (-report, -warning, -error) per
8a4ed632 6372 * RFC 8011.
9610a474
MS
6373 */
6374
6375 if (*message == '-')
6376 {
6377 remove = 1;
6378 state_reasons = job->printer->state_reasons;
6379 message ++;
6380 }
6381 else if (*message == '+')
6382 {
6383 remove = 0;
6384 state_reasons = job->printer->state_reasons;
6385 message ++;
6386 }
6387 else
6388 {
6389 remove = 0;
d46dbe1b 6390 state_reasons = IPPEVE_PREASON_NONE;
9610a474
MS
6391 }
6392
6393 while (*message)
6394 {
6395 if ((next = strchr(message, ',')) != NULL)
6396 *next++ = '\0';
6397
6398 if ((ptr = strstr(message, "-error")) != NULL)
6399 *ptr = '\0';
6400 else if ((ptr = strstr(message, "-report")) != NULL)
6401 *ptr = '\0';
6402 else if ((ptr = strstr(message, "-warning")) != NULL)
6403 *ptr = '\0';
6404
d46dbe1b 6405 for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
9610a474 6406 {
d46dbe1b 6407 if (!strcmp(message, ippeve_preason_strings[i]))
9610a474
MS
6408 {
6409 if (remove)
6410 state_reasons &= ~bit;
6411 else
6412 state_reasons |= bit;
6413 }
6414 }
6415
6416 if (next)
6417 message = next;
6418 else
6419 break;
6420 }
6421
6422 job->printer->state_reasons = state_reasons;
6423}
6424
6425
1106b00e
MS
6426/*
6427 * 'register_printer()' - Register a printer object via Bonjour.
6428 */
6429
6430static int /* O - 1 on success, 0 on error */
6431register_printer(
d46dbe1b 6432 ippeve_printer_t *printer, /* I - Printer */
1562b9a1 6433 const char *subtypes) /* I - Service subtype(s) */
1106b00e 6434{
0a15691a 6435#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
d46dbe1b 6436 ippeve_txt_t ipp_txt; /* Bonjour IPP TXT record */
1562b9a1
MS
6437 int i, /* Looping var */
6438 count; /* Number of values */
6439 ipp_attribute_t *color_supported,
6440 *document_format_supported,
6441 *printer_location,
6442 *printer_make_and_model,
6443 *printer_more_info,
6444 *printer_uuid,
6445 *sides_supported,
6446 *urf_supported; /* Printer attributes */
6447 const char *value; /* Value string */
6448 char formats[252], /* List of supported formats */
6449 urf[252], /* List of supported URF values */
6450 *ptr; /* Pointer into string */
6451
6452 color_supported = ippFindAttribute(printer->attrs, "color-supported", IPP_TAG_BOOLEAN);
6453 document_format_supported = ippFindAttribute(printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE);
6454 printer_location = ippFindAttribute(printer->attrs, "printer-location", IPP_TAG_TEXT);
6455 printer_make_and_model = ippFindAttribute(printer->attrs, "printer-make-and-model", IPP_TAG_TEXT);
6456 printer_more_info = ippFindAttribute(printer->attrs, "printer-more-info", IPP_TAG_URI);
6457 printer_uuid = ippFindAttribute(printer->attrs, "printer-uuid", IPP_TAG_URI);
6458 sides_supported = ippFindAttribute(printer->attrs, "sides-supported", IPP_TAG_KEYWORD);
6459 urf_supported = ippFindAttribute(printer->attrs, "urf-supported", IPP_TAG_KEYWORD);
6460
6461 for (i = 0, count = ippGetCount(document_format_supported), ptr = formats; i < count; i ++)
6462 {
6463 value = ippGetString(document_format_supported, i, NULL);
6464
6465 if (!strcasecmp(value, "application/octet-stream"))
6466 continue;
6467
6468 if (ptr > formats && ptr < (formats + sizeof(formats) - 1))
6469 *ptr++ = ',';
6470
6471 strlcpy(ptr, value, sizeof(formats) - (size_t)(ptr - formats));
6472 ptr += strlen(ptr);
6473
6474 if (ptr >= (formats + sizeof(formats) - 1))
6475 break;
6476 }
6477
6478 urf[0] = '\0';
6479 for (i = 0, count = ippGetCount(urf_supported), ptr = urf; i < count; i ++)
6480 {
6481 value = ippGetString(urf_supported, i, NULL);
6482
6483 if (ptr > urf && ptr < (urf + sizeof(urf) - 1))
6484 *ptr++ = ',';
6485
6486 strlcpy(ptr, value, sizeof(urf) - (size_t)(ptr - urf));
6487 ptr += strlen(ptr);
6488
6489 if (ptr >= (urf + sizeof(urf) - 1))
6490 break;
6491 }
6492
0a15691a 6493#endif /* HAVE_DNSSD || HAVE_AVAHI */
d6563739 6494#ifdef HAVE_DNSSD
1106b00e 6495 DNSServiceErrorType error; /* Error from Bonjour */
1562b9a1 6496 char regtype[256]; /* Bonjour service type */
1106b00e
MS
6497
6498
6499 /*
6500 * Build the TXT record for IPP...
6501 */
6502
d6563739
MS
6503 TXTRecordCreate(&ipp_txt, 1024, NULL);
6504 TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print");
1562b9a1
MS
6505 if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
6506 TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(value), value);
6507 if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL)
6508 TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(value), value);
6509 if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
6510 TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(value), value);
6511 TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats), formats);
6512 TXTRecordSetValue(&ipp_txt, "Color", 1, ippGetBoolean(color_supported, 0) ? "T" : "F");
6513 TXTRecordSetValue(&ipp_txt, "Duplex", 1, ippGetCount(sides_supported) > 1 ? "T" : "F");
6514 if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
6515 TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(value) - 9, value + 9);
fe202ff4 6516# ifdef HAVE_SSL
d6563739 6517 TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2");
fe202ff4 6518# endif /* HAVE_SSL */
1562b9a1 6519 if (urf[0])
60d8f884 6520 TXTRecordSetValue(&ipp_txt, "URF", (uint8_t)strlen(urf), urf);
a2798463
MS
6521 TXTRecordSetValue(&ipp_txt, "txtvers", 1, "1");
6522 TXTRecordSetValue(&ipp_txt, "qtotal", 1, "1");
1106b00e 6523
1106b00e
MS
6524 /*
6525 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6526 * defend our service name but not actually support LPD...
6527 */
6528
d6563739 6529 printer->printer_ref = DNSSDMaster;
1106b00e 6530
1562b9a1
MS
6531 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)
6532 {
6533 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_printer._tcp", error);
1106b00e
MS
6534 return (0);
6535 }
6536
6537 /*
1562b9a1 6538 * Then register the _ipp._tcp (IPP) service type with the real port number to
1106b00e
MS
6539 * advertise our IPP printer...
6540 */
6541
d6563739 6542 printer->ipp_ref = DNSSDMaster;
1106b00e 6543
1562b9a1
MS
6544 if (subtypes && *subtypes)
6545 snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtypes);
a469f8a5 6546 else
015214aa 6547 strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
a469f8a5 6548
1562b9a1
MS
6549 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)
6550 {
6551 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
1106b00e
MS
6552 return (0);
6553 }
6554
f93b32b6 6555# ifdef HAVE_SSL
a469f8a5 6556 /*
1562b9a1 6557 * Then register the _ipps._tcp (IPP) service type with the real port number to
d6563739 6558 * advertise our IPPS printer...
a469f8a5
MS
6559 */
6560
d6563739 6561 printer->ipps_ref = DNSSDMaster;
a469f8a5 6562
1562b9a1
MS
6563 if (subtypes && *subtypes)
6564 snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtypes);
a469f8a5 6565 else
015214aa 6566 strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
a469f8a5 6567
1562b9a1
MS
6568 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)
6569 {
6570 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
a469f8a5
MS
6571 return (0);
6572 }
6573# endif /* HAVE_SSL */
6574
1106b00e
MS
6575 /*
6576 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6577 * real port number to advertise our IPP printer...
6578 */
6579
d6563739 6580 printer->http_ref = DNSSDMaster;
1106b00e 6581
1562b9a1
MS
6582 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)
6583 {
6584 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_http._tcp,_printer", error);
1106b00e
MS
6585 return (0);
6586 }
6587
d6563739
MS
6588 TXTRecordDeallocate(&ipp_txt);
6589
6590#elif defined(HAVE_AVAHI)
6591 char temp[256]; /* Subtype service string */
6592
6593 /*
6594 * Create the TXT record...
6595 */
6596
6597 ipp_txt = NULL;
6598 ipp_txt = avahi_string_list_add_printf(ipp_txt, "rp=ipp/print");
1562b9a1
MS
6599 if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
6600 ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s", value);
6601 if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL)
6602 ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", value);
6603 if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
6604 ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", value);
d6563739 6605 ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
1562b9a1
MS
6606 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", ippGetBoolean(color_supported, 0) ? "T" : "F");
6607 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", ippGetCount(sides_supported) > 1 ? "T" : "F");
6608 if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
6609 ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", value + 9);
d6563739
MS
6610# ifdef HAVE_SSL
6611 ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2");
6612# endif /* HAVE_SSL */
1562b9a1
MS
6613 if (urf[0])
6614 ipp_txt = avahi_string_list_add_printf(ipp_txt, "URF=%s", urf);
6615 ipp_txt = avahi_string_list_add_printf(ipp_txt, "txtvers=1");
6616 ipp_txt = avahi_string_list_add_printf(ipp_txt, "qtotal=1");
d6563739
MS
6617
6618 /*
6619 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6620 */
6621
6622 avahi_threaded_poll_lock(DNSSDMaster);
6623
6624 printer->ipp_ref = avahi_entry_group_new(DNSSDClient, dnssd_callback, NULL);
6625
6626 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);
6627
6628 /*
400e67c1 6629 * Then register the _ipp._tcp (IPP)...
d6563739
MS
6630 */
6631
6632 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 6633 if (subtypes && *subtypes)
d6563739 6634 {
400e67c1
MS
6635 char *temptypes = strdup(subtypes), *start, *end;
6636
6637 for (start = temptypes; *start; start = end)
6638 {
6639 if ((end = strchr(start, ',')) != NULL)
6640 *end++ = '\0';
6641 else
6642 end = start + strlen(start);
6643
6644 snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", start);
6645 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp);
6646 }
6647
6648 free(temptypes);
d6563739
MS
6649 }
6650
6651#ifdef HAVE_SSL
6652 /*
400e67c1 6653 * _ipps._tcp (IPPS) for secure printing...
d6563739
MS
6654 */
6655
6656 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 6657 if (subtypes && *subtypes)
d6563739 6658 {
400e67c1
MS
6659 char *temptypes = strdup(subtypes), *start, *end;
6660
6661 for (start = temptypes; *start; start = end)
6662 {
6663 if ((end = strchr(start, ',')) != NULL)
6664 *end++ = '\0';
6665 else
6666 end = start + strlen(start);
6667
6668 snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", start);
6669 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp);
6670 }
6671
6672 free(temptypes);
d6563739
MS
6673 }
6674#endif /* HAVE_SSL */
6675
6676 /*
6677 * Finally _http.tcp (HTTP) for the web interface...
6678 */
6679
6680 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);
6681 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");
6682
6683 /*
6684 * Commit it...
6685 */
6686
6687 avahi_entry_group_commit(printer->ipp_ref);
6688 avahi_threaded_poll_unlock(DNSSDMaster);
6689
6690 avahi_string_list_free(ipp_txt);
6691#endif /* HAVE_DNSSD */
6692
1106b00e
MS
6693 return (1);
6694}
6695
6696
6697/*
6698 * 'respond_http()' - Send a HTTP response.
6699 */
6700
6701int /* O - 1 on success, 0 on failure */
a469f8a5 6702respond_http(
d46dbe1b 6703 ippeve_client_t *client, /* I - Client */
a469f8a5
MS
6704 http_status_t code, /* I - HTTP status of response */
6705 const char *content_encoding, /* I - Content-Encoding of response */
6706 const char *type, /* I - MIME media type of response */
6707 size_t length) /* I - Length of response */
1106b00e
MS
6708{
6709 char message[1024]; /* Text message */
6710
6711
a469f8a5 6712 fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
1106b00e 6713
a469f8a5 6714 if (code == HTTP_STATUS_CONTINUE)
1106b00e
MS
6715 {
6716 /*
6717 * 100-continue doesn't send any headers...
6718 */
6719
a469f8a5 6720 return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
1106b00e
MS
6721 }
6722
6723 /*
6724 * Format an error message...
6725 */
6726
fe202ff4 6727 if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
1106b00e
MS
6728 {
6729 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
6730
6731 type = "text/plain";
6732 length = strlen(message);
6733 }
6734 else
6735 message[0] = '\0';
6736
6737 /*
a469f8a5 6738 * Send the HTTP response header...
1106b00e
MS
6739 */
6740
a469f8a5 6741 httpClearFields(client->http);
1106b00e 6742
a469f8a5
MS
6743 if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
6744 client->operation == HTTP_STATE_OPTIONS)
6745 httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
1106b00e
MS
6746
6747 if (type)
6748 {
6749 if (!strcmp(type, "text/html"))
a469f8a5
MS
6750 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
6751 "text/html; charset=utf-8");
6752 else
6753 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
1106b00e 6754
a469f8a5
MS
6755 if (content_encoding)
6756 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
1106b00e 6757 }
1106b00e 6758
a469f8a5
MS
6759 httpSetLength(client->http, length);
6760
6761 if (httpWriteResponse(client->http, code) < 0)
1106b00e
MS
6762 return (0);
6763
6764 /*
6765 * Send the response data...
6766 */
6767
6768 if (message[0])
6769 {
6770 /*
6771 * Send a plain text message.
6772 */
6773
a469f8a5
MS
6774 if (httpPrintf(client->http, "%s", message) < 0)
6775 return (0);
6776
6777 if (httpWrite2(client->http, "", 0) < 0)
1106b00e
MS
6778 return (0);
6779 }
6780 else if (client->response)
6781 {
6782 /*
6783 * Send an IPP response...
6784 */
6785
83e08001 6786 debug_attributes("Response", client->response, 2);
1106b00e 6787
a469f8a5 6788 ippSetState(client->response, IPP_STATE_IDLE);
1106b00e 6789
a469f8a5 6790 if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
1106b00e
MS
6791 return (0);
6792 }
1106b00e 6793
a469f8a5 6794 return (1);
1106b00e
MS
6795}
6796
6797
6798/*
6799 * 'respond_ipp()' - Send an IPP response.
6800 */
6801
6802static void
d46dbe1b 6803respond_ipp(ippeve_client_t *client, /* I - Client */
1106b00e
MS
6804 ipp_status_t status, /* I - status-code */
6805 const char *message, /* I - printf-style status-message */
6806 ...) /* I - Additional args as needed */
6807{
a469f8a5 6808 const char *formatted = NULL; /* Formatted message */
1106b00e
MS
6809
6810
a469f8a5 6811 ippSetStatusCode(client->response, status);
1106b00e
MS
6812
6813 if (message)
6814 {
a469f8a5
MS
6815 va_list ap; /* Pointer to additional args */
6816 ipp_attribute_t *attr; /* New status-message attribute */
6817
1106b00e 6818 va_start(ap, message);
6d56631f 6819 if ((attr = ippFindAttribute(client->response, "status-message", IPP_TAG_TEXT)) != NULL)
a469f8a5
MS
6820 ippSetStringfv(client->response, &attr, 0, message, ap);
6821 else
6d56631f 6822 attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, "status-message", NULL, message, ap);
1106b00e
MS
6823 va_end(ap);
6824
a469f8a5 6825 formatted = ippGetString(attr, 0, NULL);
1106b00e 6826 }
1106b00e 6827
a469f8a5 6828 if (formatted)
6d56631f 6829 fprintf(stderr, "%s %s %s (%s)\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status), formatted);
a469f8a5 6830 else
6d56631f 6831 fprintf(stderr, "%s %s %s\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status));
1106b00e
MS
6832}
6833
6834
83e08001
MS
6835/*
6836 * 'respond_unsupported()' - Respond with an unsupported attribute.
6837 */
6838
6839static void
6840respond_unsupported(
d46dbe1b 6841 ippeve_client_t *client, /* I - Client */
83e08001
MS
6842 ipp_attribute_t *attr) /* I - Atribute */
6843{
a2326b5b
MS
6844 ipp_attribute_t *temp; /* Copy of attribute */
6845
6846
6d56631f 6847 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Unsupported %s %s%s value.", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)));
83e08001 6848
a2326b5b
MS
6849 temp = ippCopyAttribute(client->response, attr, 0);
6850 ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
83e08001
MS
6851}
6852
6853
1106b00e
MS
6854/*
6855 * 'run_printer()' - Run the printer service.
6856 */
6857
6858static void
d46dbe1b 6859run_printer(ippeve_printer_t *printer) /* I - Printer */
1106b00e 6860{
0268488e 6861 int num_fds; /* Number of file descriptors */
1106b00e
MS
6862 struct pollfd polldata[3]; /* poll() data */
6863 int timeout; /* Timeout for poll() */
d46dbe1b 6864 ippeve_client_t *client; /* New client */
1106b00e
MS
6865
6866
6867 /*
6868 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6869 */
6870
6871 polldata[0].fd = printer->ipv4;
6872 polldata[0].events = POLLIN;
6873
6874 polldata[1].fd = printer->ipv6;
6875 polldata[1].events = POLLIN;
6876
0268488e
MS
6877 num_fds = 2;
6878
6879#ifdef HAVE_DNSSD
d6563739 6880 polldata[num_fds ].fd = DNSServiceRefSockFD(DNSSDMaster);
0268488e
MS
6881 polldata[num_fds ++].events = POLLIN;
6882#endif /* HAVE_DNSSD */
1106b00e
MS
6883
6884 /*
6885 * Loop until we are killed or have a hard error...
6886 */
6887
6888 for (;;)
6889 {
6890 if (cupsArrayCount(printer->jobs))
6891 timeout = 10;
6892 else
6893 timeout = -1;
6894
7e86f2f6 6895 if (poll(polldata, (nfds_t)num_fds, timeout) < 0 && errno != EINTR)
1106b00e
MS
6896 {
6897 perror("poll() failed");
6898 break;
6899 }
6900
6901 if (polldata[0].revents & POLLIN)
6902 {
6903 if ((client = create_client(printer, printer->ipv4)) != NULL)
6904 {
116c301f
MS
6905 _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
6906
6907 if (t)
6908 {
6909 _cupsThreadDetach(t);
6910 }
6911 else
1106b00e
MS
6912 {
6913 perror("Unable to create client thread");
6914 delete_client(client);
6915 }
6916 }
6917 }
6918
6919 if (polldata[1].revents & POLLIN)
6920 {
6921 if ((client = create_client(printer, printer->ipv6)) != NULL)
6922 {
116c301f
MS
6923 _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
6924
6925 if (t)
6926 {
6927 _cupsThreadDetach(t);
6928 }
6929 else
1106b00e
MS
6930 {
6931 perror("Unable to create client thread");
6932 delete_client(client);
6933 }
6934 }
6935 }
6936
0268488e 6937#ifdef HAVE_DNSSD
1106b00e 6938 if (polldata[2].revents & POLLIN)
d6563739 6939 DNSServiceProcessResult(DNSSDMaster);
0268488e 6940#endif /* HAVE_DNSSD */
1106b00e
MS
6941
6942 /*
6943 * Clean out old jobs...
6944 */
6945
6946 clean_jobs(printer);
6947 }
6948}
6949
6950
9141aa01
MS
6951/*
6952 * 'show_media()' - Show media load state.
6953 */
6954
6955static int /* O - 1 on success, 0 on failure */
6956show_media(ippeve_client_t *client) /* I - Client connection */
6957{
6958 ippeve_printer_t *printer = client->printer;
6959 /* Printer */
6960 int i, j, /* Looping vars */
6961 num_ready, /* Number of ready media */
6962 num_sizes, /* Number of media sizes */
6963 num_sources, /* Number of media sources */
6964 num_types; /* Number of media types */
6965 ipp_attribute_t *media_col_ready,/* media-col-ready attribute */
6966 *media_ready, /* media-ready attribute */
6967 *media_sizes, /* media-supported attribute */
6968 *media_sources, /* media-source-supported attribute */
6969 *media_types, /* media-type-supported attribute */
6970 *input_tray; /* printer-input-tray attribute */
6971 ipp_t *media_col; /* media-col value */
6972 const char *media_size, /* media value */
6973 *media_source, /* media-source value */
6974 *media_type, /* media-type value */
6975 *ready_size, /* media-col-ready media-size[-name] value */
6976 *ready_source, /* media-col-ready media-source value */
6977 *ready_tray, /* printer-input-tray value */
6978 *ready_type; /* media-col-ready media-type value */
6979 char tray_str[1024], /* printer-input-tray string value */
6980 *tray_ptr; /* Pointer into value */
6981 int tray_len; /* Length of printer-input-tray value */
6982 int ready_sheets; /* printer-input-tray sheets value */
6983 int num_options; /* Number of form options */
6984 cups_option_t *options; /* Form options */
6985 static const int sheets[] = /* Number of sheets */
6986 {
6987 250,
2d6dcec1
MS
6988 125,
6989 50,
9141aa01
MS
6990 25,
6991 5,
6640ceec
MS
6992 0,
6993 -2
9141aa01
MS
6994 };
6995
6996
6997 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
6998 return (0);
6999
6640ceec 7000 html_header(client, printer->name, 0);
9141aa01
MS
7001
7002 if ((media_col_ready = ippFindAttribute(printer->attrs, "media-col-ready", IPP_TAG_BEGIN_COLLECTION)) == NULL)
7003 {
7004 html_printf(client, "<p>Error: No media-col-ready defined for printer.</p>\n");
7005 html_footer(client);
7006 return (1);
7007 }
7008
7009 media_ready = ippFindAttribute(printer->attrs, "media-ready", IPP_TAG_ZERO);
7010
7011 if ((media_sizes = ippFindAttribute(printer->attrs, "media-supported", IPP_TAG_ZERO)) == NULL)
7012 {
7013 html_printf(client, "<p>Error: No media-supported defined for printer.</p>\n");
7014 html_footer(client);
7015 return (1);
7016 }
7017
7018 if ((media_sources = ippFindAttribute(printer->attrs, "media-source-supported", IPP_TAG_ZERO)) == NULL)
7019 {
7020 html_printf(client, "<p>Error: No media-source-supported defined for printer.</p>\n");
7021 html_footer(client);
7022 return (1);
7023 }
7024
7025 if ((media_types = ippFindAttribute(printer->attrs, "media-type-supported", IPP_TAG_ZERO)) == NULL)
7026 {
7027 html_printf(client, "<p>Error: No media-type-supported defined for printer.</p>\n");
7028 html_footer(client);
7029 return (1);
7030 }
7031
7032 if ((input_tray = ippFindAttribute(printer->attrs, "printer-input-tray", IPP_TAG_STRING)) == NULL)
7033 {
7034 html_printf(client, "<p>Error: No printer-input-tray defined for printer.</p>\n");
7035 html_footer(client);
7036 return (1);
7037 }
7038
7039 num_ready = ippGetCount(media_col_ready);
7040 num_sizes = ippGetCount(media_sizes);
7041 num_sources = ippGetCount(media_sources);
7042 num_types = ippGetCount(media_types);
7043
7044 if (num_sources != ippGetCount(input_tray))
7045 {
7046 html_printf(client, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n");
7047 html_footer(client);
7048 return (1);
7049 }
7050
7051 /*
7052 * Process form data if present...
7053 */
7054
820cb58e
MS
7055 if (printer->web_forms)
7056 num_options = parse_options(client, &options);
7057 else
7058 num_options = 0;
7059
7060 if (num_options > 0)
9141aa01
MS
7061 {
7062 /*
7063 * WARNING: A real printer/server implementation MUST NOT implement
7064 * media updates via a GET request - GET requests are supposed to be
7065 * idempotent (without side-effects) and we obviously are not
7066 * authenticating access here. This form is provided solely to
7067 * enable testing and development!
7068 */
7069
7070 char name[255]; /* Form name */
7071 const char *val; /* Form value */
7072 pwg_media_t *media; /* Media info */
7073
7074 _cupsRWLockWrite(&printer->rwlock);
7075
9141aa01
MS
7076 ippDeleteAttribute(printer->attrs, media_col_ready);
7077 media_col_ready = NULL;
7078
7079 if (media_ready)
7080 {
7081 ippDeleteAttribute(printer->attrs, media_ready);
7082 media_ready = NULL;
7083 }
7084
7085 printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MEDIA_LOW | IPPEVE_PREASON_MEDIA_EMPTY | IPPEVE_PREASON_MEDIA_NEEDED);
7086
7087 for (i = 0; i < num_sources; i ++)
7088 {
7089 media_source = ippGetString(media_sources, i, NULL);
7090
2d6dcec1 7091 if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
6640ceec
MS
7092 continue;
7093
9141aa01
MS
7094 snprintf(name, sizeof(name), "size%d", i);
7095 if ((media_size = cupsGetOption(name, num_options, options)) != NULL && (media = pwgMediaForPWG(media_size)) != NULL)
7096 {
7097 snprintf(name, sizeof(name), "type%d", i);
7098 if ((media_type = cupsGetOption(name, num_options, options)) != NULL && !*media_type)
7099 media_type = NULL;
7100
7101 if (media_ready)
7102 ippSetString(printer->attrs, &media_ready, ippGetCount(media_ready), media_size);
7103 else
7104 media_ready = ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, media_size);
7105
7106 media_col = create_media_col(media_size, media_source, media_type, media->width, media->length, -1, -1, -1, -1);
7107
7108 if (media_col_ready)
7109 ippSetCollection(printer->attrs, &media_col_ready, ippGetCount(media_col_ready), media_col);
7110 else
7111 media_col_ready = ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-ready", media_col);
7112 ippDelete(media_col);
7113 }
7114 else
7115 media = NULL;
7116
7117 snprintf(name, sizeof(name), "level%d", i);
7118 if ((val = cupsGetOption(name, num_options, options)) != NULL)
7119 ready_sheets = atoi(val);
7120 else
7121 ready_sheets = 0;
7122
2d6dcec1 7123 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 7124
6640ceec 7125 ippSetOctetString(printer->attrs, &input_tray, i, tray_str, (int)strlen(tray_str));
9141aa01
MS
7126
7127 if (ready_sheets == 0)
7128 {
7129 printer->state_reasons |= IPPEVE_PREASON_MEDIA_EMPTY;
7130 if (printer->active_job)
7131 printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
7132 }
6640ceec 7133 else if (ready_sheets < 25 && ready_sheets > 0)
9141aa01
MS
7134 printer->state_reasons |= IPPEVE_PREASON_MEDIA_LOW;
7135 }
7136
7137 if (!media_col_ready)
7138 media_col_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-col-ready");
7139
7140 if (!media_ready)
7141 media_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-ready");
7142
7143 _cupsRWUnlock(&printer->rwlock);
9141aa01
MS
7144 }
7145
820cb58e
MS
7146 if (printer->web_forms)
7147 html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
9141aa01
MS
7148
7149 html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
7150 for (i = 0; i < num_sources; i ++)
7151 {
7152 media_source = ippGetString(media_sources, i, NULL);
7153
2d6dcec1 7154 if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
6640ceec
MS
7155 continue;
7156
9141aa01
MS
7157 for (j = 0, ready_size = NULL, ready_type = NULL; j < num_ready; j ++)
7158 {
7159 media_col = ippGetCollection(media_col_ready, j);
7160 ready_size = ippGetString(ippFindAttribute(media_col, "media-size-name", IPP_TAG_ZERO), 0, NULL);
7161 ready_source = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL);
7162 ready_type = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL);
7163
7164 if (ready_source && !strcmp(ready_source, media_source))
7165 break;
7166
7167 ready_source = NULL;
7168 ready_size = NULL;
7169 ready_type = NULL;
7170 }
7171
820cb58e
MS
7172 html_printf(client, "<tr><th>%s:</th>", media_source);
7173
9141aa01
MS
7174 /*
7175 * Media size...
7176 */
7177
820cb58e 7178 if (printer->web_forms)
9141aa01 7179 {
820cb58e
MS
7180 html_printf(client, "<td><select name=\"size%d\"><option value=\"\">None</option>", i);
7181 for (j = 0; j < num_sizes; j ++)
7182 {
7183 media_size = ippGetString(media_sizes, j, NULL);
9141aa01 7184
820cb58e
MS
7185 html_printf(client, "<option%s>%s</option>", (ready_size && !strcmp(ready_size, media_size)) ? " selected" : "", media_size);
7186 }
7187 html_printf(client, "</select>");
9141aa01 7188 }
820cb58e
MS
7189 else
7190 html_printf(client, "<td>%s", ready_size);
9141aa01
MS
7191
7192 /*
7193 * Media type...
7194 */
7195
820cb58e 7196 if (printer->web_forms)
9141aa01 7197 {
820cb58e
MS
7198 html_printf(client, " <select name=\"type%d\"><option value=\"\">None</option>", i);
7199 for (j = 0; j < num_types; j ++)
7200 {
7201 media_type = ippGetString(media_types, j, NULL);
9141aa01 7202
820cb58e
MS
7203 html_printf(client, "<option%s>%s</option>", (ready_type && !strcmp(ready_type, media_type)) ? " selected" : "", media_type);
7204 }
7205 html_printf(client, "</select>");
9141aa01 7206 }
820cb58e
MS
7207 else
7208 html_printf(client, ", %s", ready_type);
9141aa01
MS
7209
7210 /*
7211 * Level/sheets loaded...
7212 */
7213
7214 if ((ready_tray = ippGetOctetString(input_tray, i, &tray_len)) != NULL)
7215 {
60d8f884
MS
7216 if (tray_len > (int)(sizeof(tray_str) - 1))
7217 tray_len = (int)sizeof(tray_str) - 1;
9141aa01
MS
7218 memcpy(tray_str, ready_tray, (size_t)tray_len);
7219 tray_str[tray_len] = '\0';
7220
7221 if ((tray_ptr = strstr(tray_str, "level=")) != NULL)
7222 ready_sheets = atoi(tray_ptr + 6);
7223 else
7224 ready_sheets = 0;
7225 }
7226 else
7227 ready_sheets = 0;
7228
820cb58e 7229 if (printer->web_forms)
6640ceec 7230 {
820cb58e
MS
7231 html_printf(client, " <select name=\"level%d\">", i);
7232 for (j = 0; j < (int)(sizeof(sheets) / sizeof(sheets[0])); j ++)
7233 {
2d6dcec1 7234 if (!strcmp(media_source, "by-pass-tray") && sheets[j] > 25)
820cb58e 7235 continue;
6640ceec 7236
820cb58e
MS
7237 if (sheets[j] < 0)
7238 html_printf(client, "<option value=\"%d\"%s>Unknown</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "");
7239 else
7240 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "", sheets[j]);
7241 }
7242 html_printf(client, "</select></td></tr>\n");
6640ceec 7243 }
820cb58e
MS
7244 else if (ready_sheets > 0)
7245 html_printf(client, ", %d sheets</td></tr>\n", ready_sheets);
7246 else
7247 html_printf(client, "</td></tr>\n");
9141aa01
MS
7248 }
7249
820cb58e
MS
7250 if (printer->web_forms)
7251 {
7252 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\">");
7253 if (num_options > 0)
7254 html_printf(client, " <span class=\"badge\" id=\"status\">Media updated.</span>\n");
7255 html_printf(client, "</td></tr></table></form>\n");
7256
7257 if (num_options > 0)
7258 html_printf(client, "<script>\n"
7259 "setTimeout(hide_status, 3000);\n"
7260 "function hide_status() {\n"
7261 " var status = document.getElementById('status');\n"
7262 " status.style.display = 'none';\n"
7263 "}\n"
7264 "</script>\n");
7265 }
7266 else
7267 html_printf(client, "</table>\n");
7268
9141aa01
MS
7269 html_footer(client);
7270
7271 return (1);
7272}
7273
7274
7275/*
7276 * 'show_status()' - Show printer/system state.
7277 */
7278
7279static int /* O - 1 on success, 0 on failure */
7280show_status(ippeve_client_t *client) /* I - Client connection */
7281{
7282 ippeve_printer_t *printer = client->printer;
7283 /* Printer */
7284 ippeve_job_t *job; /* Current job */
7285 int i; /* Looping var */
7286 ippeve_preason_t reason; /* Current reason */
7287 static const char * const reasons[] = /* Reason strings */
7288 {
7289 "Other",
7290 "Cover Open",
7291 "Input Tray Missing",
7292 "Marker Supply Empty",
7293 "Marker Supply Low",
7294 "Marker Waste Almost Full",
7295 "Marker Waste Full",
7296 "Media Empty",
7297 "Media Jam",
7298 "Media Low",
7299 "Media Needed",
7300 "Moving to Paused",
7301 "Paused",
7302 "Spool Area Full",
7303 "Toner Empty",
7304 "Toner Low"
7305 };
6640ceec
MS
7306 static const char * const state_colors[] =
7307 { /* State colors */
7308 "#0C0", /* Idle */
7309 "#EE0", /* Processing */
7310 "#C00" /* Stopped */
7311 };
9141aa01
MS
7312
7313
7314 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7315 return (0);
7316
6640ceec
MS
7317 html_header(client, printer->name, printer->state == IPP_PSTATE_PROCESSING ? 5 : 15);
7318 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
7319 html_printf(client, "<p>%s, %d job(s).", printer->state == IPP_PSTATE_IDLE ? "Idle" : printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(printer->jobs));
7320 for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
7321 if (printer->state_reasons & reason)
7322 html_printf(client, "\n<br>&nbsp;&nbsp;&nbsp;&nbsp;%s", reasons[i]);
7323 html_printf(client, "</p>\n");
7324
7325 if (cupsArrayCount(printer->jobs) > 0)
7326 {
7327 _cupsRWLockRead(&(printer->rwlock));
7328
7329 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");
7330 for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs); job; job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
7331 {
7332 char when[256], /* When job queued/started/finished */
7333 hhmmss[64]; /* Time HH:MM:SS */
7334
7335 switch (job->state)
7336 {
7337 case IPP_JSTATE_PENDING :
7338 case IPP_JSTATE_HELD :
7339 snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss)));
7340 break;
7341 case IPP_JSTATE_PROCESSING :
7342 case IPP_JSTATE_STOPPED :
7343 snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss)));
7344 break;
7345 case IPP_JSTATE_ABORTED :
7346 snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7347 break;
7348 case IPP_JSTATE_CANCELED :
7349 snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7350 break;
7351 case IPP_JSTATE_COMPLETED :
7352 snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7353 break;
7354 }
7355
7356 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);
7357 }
7358 html_printf(client, "</tbody></table>\n");
7359
7360 _cupsRWUnlock(&(printer->rwlock));
7361 }
7362
7363 html_footer(client);
7364
7365 return (1);
7366}
7367
7368
7369/*
7370 * 'show_supplies()' - Show printer supplies.
7371 */
7372
7373static int /* O - 1 on success, 0 on failure */
7374show_supplies(
7375 ippeve_client_t *client) /* I - Client connection */
7376{
7377 ippeve_printer_t *printer = client->printer;
7378 /* Printer */
7379 int i, /* Looping var */
7380 num_supply; /* Number of supplies */
7381 ipp_attribute_t *supply, /* printer-supply attribute */
7382 *supply_desc; /* printer-supply-description attribute */
7383 int num_options; /* Number of form options */
7384 cups_option_t *options; /* Form options */
7385 int supply_len, /* Length of supply value */
7386 level; /* Supply level */
7387 const char *supply_value; /* Supply value */
7388 char supply_text[1024], /* Supply string */
7389 *supply_ptr; /* Pointer into supply string */
7390 static const char * const printer_supply[] =
7391 { /* printer-supply values */
7392 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
7393 "maxcapacity=100;level=%d;colorantname=unknown;",
7394 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
7395 "maxcapacity=100;level=%d;colorantname=black;",
7396 "index=3;class=supplyThatIsConsumed;type=toner;unit=percent;"
7397 "maxcapacity=100;level=%d;colorantname=cyan;",
7398 "index=4;class=supplyThatIsConsumed;type=toner;unit=percent;"
7399 "maxcapacity=100;level=%d;colorantname=magenta;",
7400 "index=5;class=supplyThatIsConsumed;type=toner;unit=percent;"
7401 "maxcapacity=100;level=%d;colorantname=yellow;"
7402 };
820cb58e
MS
7403 static const char * const backgrounds[] =
7404 { /* Background colors for the supply-level bars */
9141aa01
MS
7405 "#777 linear-gradient(#333,#777)",
7406 "#000 linear-gradient(#666,#000)",
7407 "#0FF linear-gradient(#6FF,#0FF)",
7408 "#F0F linear-gradient(#F6F,#F0F)",
7409 "#CC0 linear-gradient(#EE6,#EE0)"
7410 };
820cb58e
MS
7411 static const char * const colors[] = /* Text colors for the supply-level bars */
7412 {
7413 "#fff",
7414 "#fff",
7415 "#000",
7416 "#000",
7417 "#000"
7418 };
9141aa01
MS
7419
7420
7421 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7422 return (0);
7423
6640ceec 7424 html_header(client, printer->name, 0);
9141aa01
MS
7425
7426 if ((supply = ippFindAttribute(printer->attrs, "printer-supply", IPP_TAG_STRING)) == NULL)
7427 {
7428 html_printf(client, "<p>Error: No printer-supply defined for printer.</p>\n");
7429 html_footer(client);
7430 return (1);
7431 }
7432
7433 num_supply = ippGetCount(supply);
7434
7435 if ((supply_desc = ippFindAttribute(printer->attrs, "printer-supply-description", IPP_TAG_TEXT)) == NULL)
7436 {
7437 html_printf(client, "<p>Error: No printer-supply-description defined for printer.</p>\n");
7438 html_footer(client);
7439 return (1);
7440 }
7441
7442 if (num_supply != ippGetCount(supply_desc))
7443 {
7444 html_printf(client, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n");
7445 html_footer(client);
7446 return (1);
7447 }
7448
820cb58e
MS
7449 if (printer->web_forms)
7450 num_options = parse_options(client, &options);
7451 else
7452 num_options = 0;
7453
7454 if (num_options > 0)
9141aa01
MS
7455 {
7456 /*
7457 * WARNING: A real printer/server implementation MUST NOT implement
7458 * supply updates via a GET request - GET requests are supposed to be
7459 * idempotent (without side-effects) and we obviously are not
7460 * authenticating access here. This form is provided solely to
7461 * enable testing and development!
7462 */
7463
7464 char name[64]; /* Form field */
7465 const char *val; /* Form value */
7466
7467 _cupsRWLockWrite(&printer->rwlock);
7468
7469 ippDeleteAttribute(printer->attrs, supply);
7470 supply = NULL;
7471
7472 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);
7473
7474 for (i = 0; i < num_supply; i ++)
7475 {
7476 snprintf(name, sizeof(name), "supply%d", i);
7477 if ((val = cupsGetOption(name, num_options, options)) != NULL)
7478 {
7479 level = atoi(val); /* New level */
7480
7481 snprintf(supply_text, sizeof(supply_text), printer_supply[i], level);
7482 if (supply)
7483 ippSetOctetString(printer->attrs, &supply, ippGetCount(supply), supply_text, (int)strlen(supply_text));
7484 else
7485 supply = ippAddOctetString(printer->attrs, IPP_TAG_PRINTER, "printer-supply", supply_text, (int)strlen(supply_text));
7486
7487 if (i == 0)
7488 {
7489 if (level == 100)
7490 printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_FULL;
7491 else if (level > 90)
7492 printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL;
7493 }
7494 else
7495 {
7496 if (level == 0)
7497 printer->state_reasons |= IPPEVE_PREASON_TONER_EMPTY;
7498 else if (level < 10)
7499 printer->state_reasons |= IPPEVE_PREASON_TONER_LOW;
7500 }
7501 }
7502 }
7503
7504 _cupsRWUnlock(&printer->rwlock);
9141aa01
MS
7505 }
7506
820cb58e
MS
7507 if (printer->web_forms)
7508 html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
9141aa01
MS
7509
7510 html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
7511 for (i = 0; i < num_supply; i ++)
7512 {
7513 supply_value = ippGetOctetString(supply, i, &supply_len);
60d8f884
MS
7514 if (supply_len > (int)(sizeof(supply_text) - 1))
7515 supply_len = (int)sizeof(supply_text) - 1;
9141aa01
MS
7516
7517 memcpy(supply_text, supply_value, (size_t)supply_len);
7518 supply_text[supply_len] = '\0';
7519
7520 if ((supply_ptr = strstr(supply_text, "level=")) != NULL)
7521 level = atoi(supply_ptr + 6);
7522 else
7523 level = 50;
7524
820cb58e
MS
7525 if (printer->web_forms)
7526 html_printf(client, "<tr><th>%s:</th><td><input name=\"supply%d\" size=\"3\" value=\"%d\"></td>", ippGetString(supply_desc, i, NULL), i, level);
7527 else
7528 html_printf(client, "<tr><th>%s:</th>", ippGetString(supply_desc, i, NULL));
7529
7530 if (level < 10)
7531 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);
7532 else
7533 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 7534 }
820cb58e
MS
7535
7536 if (printer->web_forms)
7537 {
7538 html_printf(client, "<tr><td></td><td colspan=\"2\"><input type=\"submit\" value=\"Update Supplies\">");
7539 if (num_options > 0)
7540 html_printf(client, " <span class=\"badge\" id=\"status\">Supplies updated.</span>\n");
7541 html_printf(client, "</td></tr>\n</table>\n</form>\n");
7542
7543 if (num_options > 0)
7544 html_printf(client, "<script>\n"
7545 "setTimeout(hide_status, 3000);\n"
7546 "function hide_status() {\n"
7547 " var status = document.getElementById('status');\n"
7548 " status.style.display = 'none';\n"
7549 "}\n"
7550 "</script>\n");
7551 }
7552 else
7553 html_printf(client, "</table>\n");
7554
9141aa01
MS
7555 html_footer(client);
7556
7557 return (1);
7558}
7559
7560
0b5ce83f
MS
7561/*
7562 * 'time_string()' - Return the local time in hours, minutes, and seconds.
7563 */
7564
7565static char *
7566time_string(time_t tv, /* I - Time value */
7567 char *buffer, /* I - Buffer */
7568 size_t bufsize) /* I - Size of buffer */
7569{
7570 struct tm *curtime = localtime(&tv);
7571 /* Local time */
7572
7573 strftime(buffer, bufsize, "%X", curtime);
7574 return (buffer);
7575}
7576
7577
1106b00e
MS
7578/*
7579 * 'usage()' - Show program usage.
7580 */
7581
7582static void
7583usage(int status) /* O - Exit status */
7584{
6d56631f
MS
7585 _cupsLangPuts(stdout, _("Usage: ippeveprinter [options] \"name\""));
7586 _cupsLangPuts(stdout, _("Options:"));
7587 _cupsLangPuts(stderr, _("--help Show program help"));
820cb58e 7588 _cupsLangPuts(stderr, _("--no-web-forms Disable web forms for media and supplies"));
6d56631f
MS
7589 _cupsLangPuts(stderr, _("--version Show program version"));
7590 _cupsLangPuts(stdout, _("-2 Set 2-sided printing support (default=1-sided)"));
7591 _cupsLangPuts(stdout, _("-D device-uri Set the device URI for the printer"));
7592#ifdef HAVE_SSL
7593 _cupsLangPuts(stdout, _("-K keypath Set location of server X.509 certificates and keys."));
7594#endif /* HAVE_SSL */
7595 _cupsLangPuts(stdout, _("-M manufacturer Set manufacturer name (default=Test)"));
7596 _cupsLangPuts(stdout, _("-P filename.ppd Load printer attributes from PPD file"));
7597 _cupsLangPuts(stdout, _("-V version Set default IPP version"));
7598 _cupsLangPuts(stdout, _("-a filename.conf Load printer attributes from conf file"));
7599 _cupsLangPuts(stdout, _("-c command Set print command"));
7600 _cupsLangPuts(stdout, _("-d spool-directory Set spool directory"));
7601 _cupsLangPuts(stdout, _("-f type/subtype[,...] Set supported file types"));
7602 _cupsLangPuts(stdout, _("-i iconfile.png Set icon file"));
7603 _cupsLangPuts(stdout, _("-k Keep job spool files"));
7604 _cupsLangPuts(stdout, _("-l location Set location of printer"));
7605 _cupsLangPuts(stdout, _("-m model Set model name (default=Printer)"));
7606 _cupsLangPuts(stdout, _("-n hostname Set hostname for printer"));
7607 _cupsLangPuts(stdout, _("-p port Set port number for printer"));
dc84a5a4 7608 _cupsLangPuts(stdout, _("-r subtype,[subtype] Set DNS-SD service subtype"));
6d56631f
MS
7609 _cupsLangPuts(stdout, _("-s speed[,color-speed] Set speed in pages per minute"));
7610 _cupsLangPuts(stderr, _("-v Be verbose"));
1106b00e
MS
7611
7612 exit(status);
7613}
7614
7615
7616/*
83e08001
MS
7617 * 'valid_doc_attributes()' - Determine whether the document attributes are
7618 * valid.
1106b00e 7619 *
83e08001
MS
7620 * When one or more document attributes are invalid, this function adds a
7621 * suitable response and attributes to the unsupported group.
1106b00e
MS
7622 */
7623
7624static int /* O - 1 if valid, 0 if not */
83e08001 7625valid_doc_attributes(
d46dbe1b 7626 ippeve_client_t *client) /* I - Client */
1106b00e 7627{
a469f8a5
MS
7628 int valid = 1; /* Valid attributes? */
7629 ipp_op_t op = ippGetOperation(client->request);
7630 /* IPP operation */
7631 const char *op_name = ippOpString(op);
7632 /* IPP operation name */
1106b00e 7633 ipp_attribute_t *attr, /* Current attribute */
a469f8a5
MS
7634 *supported; /* xxx-supported attribute */
7635 const char *compression = NULL,
7636 /* compression value */
7637 *format = NULL; /* document-format value */
1106b00e
MS
7638
7639
7640 /*
7641 * Check operation attributes...
7642 */
7643
404dde30 7644 if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
1106b00e
MS
7645 {
7646 /*
a469f8a5
MS
7647 * If compression is specified, only accept a supported value in a Print-Job
7648 * or Send-Document request...
1106b00e
MS
7649 */
7650
a469f8a5
MS
7651 compression = ippGetString(attr, 0, NULL);
7652 supported = ippFindAttribute(client->printer->attrs,
7653 "compression-supported", IPP_TAG_KEYWORD);
7654
7655 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
7656 ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
db8b865d
MS
7657 (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
7658 op != IPP_OP_VALIDATE_JOB) ||
a469f8a5
MS
7659 !ippContainsString(supported, compression))
7660 {
1106b00e 7661 respond_unsupported(client, attr);
a469f8a5
MS
7662 valid = 0;
7663 }
1106b00e 7664 else
a469f8a5 7665 {
404dde30
MS
7666 fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression);
7667
7668 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression);
a469f8a5
MS
7669
7670 if (strcmp(compression, "none"))
a654c79d
MS
7671 {
7672 if (Verbosity)
7673 fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression);
a469f8a5 7674 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
a654c79d 7675 }
a469f8a5 7676 }
1106b00e
MS
7677 }
7678
7679 /*
7680 * Is it a format we support?
7681 */
7682
404dde30 7683 if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
1106b00e 7684 {
a469f8a5
MS
7685 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
7686 ippGetGroupTag(attr) != IPP_TAG_OPERATION)
7687 {
1106b00e 7688 respond_unsupported(client, attr);
a469f8a5
MS
7689 valid = 0;
7690 }
1106b00e 7691 else
e60ec91f 7692 {
a469f8a5 7693 format = ippGetString(attr, 0, NULL);
e60ec91f 7694
6d56631f 7695 fprintf(stderr, "%s %s document-format=\"%s\"\n", client->hostname, op_name, format);
4a838088
MS
7696
7697 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format);
e60ec91f 7698 }
1106b00e
MS
7699 }
7700 else
a469f8a5 7701 {
404dde30 7702 format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL);
8a78aa37
MS
7703 if (!format)
7704 format = "application/octet-stream"; /* Should never happen */
7705
4a838088 7706 attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
a469f8a5 7707 }
1106b00e 7708
7f500d89 7709 if (format && !strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
1106b00e
MS
7710 {
7711 /*
404dde30 7712 * Auto-type the file using the first 8 bytes of the file...
1106b00e
MS
7713 */
7714
404dde30 7715 unsigned char header[8]; /* First 8 bytes of file */
1106b00e
MS
7716
7717 memset(header, 0, sizeof(header));
a469f8a5 7718 httpPeek(client->http, (char *)header, sizeof(header));
1106b00e
MS
7719
7720 if (!memcmp(header, "%PDF", 4))
7721 format = "application/pdf";
7722 else if (!memcmp(header, "%!", 2))
7723 format = "application/postscript";
404dde30 7724 else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
1106b00e
MS
7725 format = "image/jpeg";
7726 else if (!memcmp(header, "\211PNG", 4))
7727 format = "image/png";
404dde30
MS
7728 else if (!memcmp(header, "RAS2", 4))
7729 format = "image/pwg-raster";
7730 else if (!memcmp(header, "UNIRAST", 8))
7731 format = "image/urf";
7732 else
7733 format = NULL;
1106b00e
MS
7734
7735 if (format)
404dde30 7736 {
6d56631f 7737 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n", client->hostname, op_name, format);
1106b00e 7738
404dde30
MS
7739 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
7740 }
1106b00e
MS
7741 }
7742
404dde30 7743 if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
1106b00e 7744 {
a469f8a5
MS
7745 respond_unsupported(client, attr);
7746 valid = 0;
1106b00e
MS
7747 }
7748
404dde30
MS
7749 /*
7750 * document-name
7751 */
7752
7753 if ((attr = ippFindAttribute(client->request, "document-name", IPP_TAG_NAME)) != NULL)
7754 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
7755
a469f8a5 7756 return (valid);
83e08001
MS
7757}
7758
7759
7760/*
7761 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
7762 *
7763 * When one or more job attributes are invalid, this function adds a suitable
7764 * response and attributes to the unsupported group.
7765 */
7766
7767static int /* O - 1 if valid, 0 if not */
7768valid_job_attributes(
d46dbe1b 7769 ippeve_client_t *client) /* I - Client */
83e08001 7770{
a469f8a5 7771 int i, /* Looping var */
63efa616 7772 count, /* Number of values */
a469f8a5 7773 valid = 1; /* Valid attributes? */
83e08001
MS
7774 ipp_attribute_t *attr, /* Current attribute */
7775 *supported; /* xxx-supported attribute */
7776
7777
7778 /*
7779 * Check operation attributes...
7780 */
7781
a469f8a5 7782 valid = valid_doc_attributes(client);
83e08001 7783
1106b00e
MS
7784 /*
7785 * Check the various job template attributes...
7786 */
7787
404dde30 7788 if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL)
1106b00e 7789 {
a469f8a5
MS
7790 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
7791 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
1106b00e
MS
7792 {
7793 respond_unsupported(client, attr);
a469f8a5 7794 valid = 0;
1106b00e
MS
7795 }
7796 }
7797
404dde30 7798 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
1106b00e 7799 {
a469f8a5 7800 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
1106b00e
MS
7801 {
7802 respond_unsupported(client, attr);
a469f8a5 7803 valid = 0;
1106b00e
MS
7804 }
7805 }
7806
404dde30 7807 if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
1106b00e 7808 {
a469f8a5
MS
7809 if (ippGetCount(attr) != 1 ||
7810 (ippGetValueTag(attr) != IPP_TAG_NAME &&
7811 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
7812 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
7813 strcmp(ippGetString(attr, 0, NULL), "no-hold"))
1106b00e
MS
7814 {
7815 respond_unsupported(client, attr);
a469f8a5 7816 valid = 0;
1106b00e
MS
7817 }
7818 }
7819
404dde30
MS
7820 if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL)
7821 {
7822 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0)
7823 {
7824 respond_unsupported(client, attr);
7825 valid = 0;
7826 }
7827 }
7828
7829 if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL)
1106b00e 7830 {
a469f8a5
MS
7831 if (ippGetCount(attr) != 1 ||
7832 (ippGetValueTag(attr) != IPP_TAG_NAME &&
7833 ippGetValueTag(attr) != IPP_TAG_NAMELANG))
1106b00e
MS
7834 {
7835 respond_unsupported(client, attr);
a469f8a5 7836 valid = 0;
1106b00e 7837 }
404dde30
MS
7838
7839 ippSetGroupTag(client->request, &attr, IPP_TAG_JOB);
1106b00e 7840 }
404dde30
MS
7841 else
7842 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
1106b00e 7843
404dde30 7844 if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL)
1106b00e 7845 {
a469f8a5
MS
7846 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
7847 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
1106b00e
MS
7848 {
7849 respond_unsupported(client, attr);
a469f8a5 7850 valid = 0;
1106b00e
MS
7851 }
7852 }
7853
404dde30 7854 if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
1106b00e 7855 {
a469f8a5
MS
7856 if (ippGetCount(attr) != 1 ||
7857 (ippGetValueTag(attr) != IPP_TAG_NAME &&
7858 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
7859 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
7860 strcmp(ippGetString(attr, 0, NULL), "none"))
1106b00e
MS
7861 {
7862 respond_unsupported(client, attr);
a469f8a5 7863 valid = 0;
1106b00e
MS
7864 }
7865 }
7866
404dde30 7867 if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
1106b00e 7868 {
a469f8a5
MS
7869 if (ippGetCount(attr) != 1 ||
7870 (ippGetValueTag(attr) != IPP_TAG_NAME &&
7871 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
7872 ippGetValueTag(attr) != IPP_TAG_KEYWORD))
1106b00e
MS
7873 {
7874 respond_unsupported(client, attr);
a469f8a5 7875 valid = 0;
1106b00e
MS
7876 }
7877 else
7878 {
63efa616 7879 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
1106b00e 7880
63efa616 7881 if (!ippContainsString(supported, ippGetString(attr, 0, NULL)))
1106b00e
MS
7882 {
7883 respond_unsupported(client, attr);
a469f8a5 7884 valid = 0;
1106b00e
MS
7885 }
7886 }
7887 }
7888
404dde30 7889 if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL)
1106b00e 7890 {
63efa616
MS
7891 ipp_t *col, /* media-col collection */
7892 *size; /* media-size collection */
7893 ipp_attribute_t *member, /* Member attribute */
7894 *x_dim, /* x-dimension */
7895 *y_dim; /* y-dimension */
7896 int x_value, /* y-dimension value */
7897 y_value; /* x-dimension value */
7898
a469f8a5
MS
7899 if (ippGetCount(attr) != 1 ||
7900 ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
1106b00e
MS
7901 {
7902 respond_unsupported(client, attr);
a469f8a5 7903 valid = 0;
1106b00e 7904 }
63efa616
MS
7905
7906 col = ippGetCollection(attr, 0);
7907
7908 if ((member = ippFindAttribute(col, "media-size-name", IPP_TAG_ZERO)) != NULL)
7909 {
7910 if (ippGetCount(member) != 1 ||
7911 (ippGetValueTag(member) != IPP_TAG_NAME &&
7912 ippGetValueTag(member) != IPP_TAG_NAMELANG &&
7913 ippGetValueTag(member) != IPP_TAG_KEYWORD))
7914 {
7915 respond_unsupported(client, attr);
7916 valid = 0;
7917 }
7918 else
7919 {
7920 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
7921
7922 if (!ippContainsString(supported, ippGetString(member, 0, NULL)))
7923 {
7924 respond_unsupported(client, attr);
7925 valid = 0;
7926 }
7927 }
7928 }
7929 else if ((member = ippFindAttribute(col, "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
7930 {
7931 if (ippGetCount(member) != 1)
7932 {
7933 respond_unsupported(client, attr);
7934 valid = 0;
7935 }
7936 else
7937 {
7938 size = ippGetCollection(member, 0);
7939
7940 if ((x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(x_dim) != 1 ||
7941 (y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(y_dim) != 1)
7942 {
7943 respond_unsupported(client, attr);
7944 valid = 0;
7945 }
7946 else
7947 {
7948 x_value = ippGetInteger(x_dim, 0);
7949 y_value = ippGetInteger(y_dim, 0);
7950 supported = ippFindAttribute(client->printer->attrs, "media-size-supported", IPP_TAG_BEGIN_COLLECTION);
7951 count = ippGetCount(supported);
7952
7953 for (i = 0; i < count ; i ++)
7954 {
7955 size = ippGetCollection(supported, i);
7956 x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_ZERO);
7957 y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_ZERO);
7958
7959 if (ippContainsInteger(x_dim, x_value) && ippContainsInteger(y_dim, y_value))
7960 break;
7961 }
7962
7963 if (i >= count)
7964 {
7965 respond_unsupported(client, attr);
7966 valid = 0;
7967 }
7968 }
7969 }
7970 }
1106b00e
MS
7971 }
7972
404dde30 7973 if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL)
1106b00e 7974 {
a469f8a5
MS
7975 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
7976 (strcmp(ippGetString(attr, 0, NULL),
1106b00e 7977 "separate-documents-uncollated-copies") &&
a469f8a5 7978 strcmp(ippGetString(attr, 0, NULL),
1106b00e
MS
7979 "separate-documents-collated-copies")))
7980 {
7981 respond_unsupported(client, attr);
a469f8a5 7982 valid = 0;
1106b00e
MS
7983 }
7984 }
7985
404dde30 7986 if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL)
1106b00e 7987 {
a469f8a5
MS
7988 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
7989 ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
7990 ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
1106b00e
MS
7991 {
7992 respond_unsupported(client, attr);
a469f8a5 7993 valid = 0;
1106b00e
MS
7994 }
7995 }
7996
404dde30 7997 if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
1106b00e 7998 {
c2c30ebc
MS
7999 if (ippGetValueTag(attr) != IPP_TAG_RANGE)
8000 {
8001 respond_unsupported(client, attr);
a469f8a5 8002 valid = 0;
c2c30ebc 8003 }
1106b00e
MS
8004 }
8005
404dde30 8006 if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL)
1106b00e 8007 {
a469f8a5
MS
8008 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
8009 ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
8010 ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
1106b00e
MS
8011 {
8012 respond_unsupported(client, attr);
a469f8a5 8013 valid = 0;
1106b00e
MS
8014 }
8015 }
8016
404dde30 8017 if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
1106b00e 8018 {
4a838088 8019 supported = ippFindAttribute(client->printer->attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION);
c2c30ebc
MS
8020
8021 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION ||
8022 !supported)
8023 {
8024 respond_unsupported(client, attr);
8025 valid = 0;
8026 }
8027 else
8028 {
63efa616 8029 int xdpi, /* Horizontal resolution for job template attribute */
4a838088
MS
8030 ydpi, /* Vertical resolution for job template attribute */
8031 sydpi; /* Vertical resolution for supported value */
8032 ipp_res_t units, /* Units for job template attribute */
8033 sunits; /* Units for supported value */
c2c30ebc
MS
8034
8035 xdpi = ippGetResolution(attr, 0, &ydpi, &units);
8036 count = ippGetCount(supported);
8037
8038 for (i = 0; i < count; i ++)
8039 {
8040 if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits)
8041 break;
8042 }
8043
8044 if (i >= count)
8045 {
8046 respond_unsupported(client, attr);
8047 valid = 0;
8048 }
8049 }
1106b00e
MS
8050 }
8051
404dde30 8052 if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
1106b00e 8053 {
404dde30
MS
8054 const char *sides = ippGetString(attr, 0, NULL);
8055 /* "sides" value... */
ad29aeab 8056
a469f8a5 8057 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
1106b00e
MS
8058 {
8059 respond_unsupported(client, attr);
a469f8a5 8060 valid = 0;
1106b00e 8061 }
404dde30 8062 else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
1106b00e 8063 {
ad29aeab 8064 if (!ippContainsString(supported, sides))
1106b00e
MS
8065 {
8066 respond_unsupported(client, attr);
a469f8a5 8067 valid = 0;
1106b00e
MS
8068 }
8069 }
ad29aeab 8070 else if (strcmp(sides, "one-sided"))
1106b00e
MS
8071 {
8072 respond_unsupported(client, attr);
a469f8a5 8073 valid = 0;
1106b00e
MS
8074 }
8075 }
8076
a469f8a5 8077 return (valid);
1106b00e 8078}