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