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