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