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