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