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