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