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