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