]> git.ipfire.org Git - thirdparty/cups.git/blame - tools/ippeveprinter.c
Also authenticate web IF.
[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
81d8eb3a 5737 if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION), "Upgrade"))
1106b00e 5738 {
fe202ff4
MS
5739#ifdef HAVE_SSL
5740 if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http))
5741 {
5742 if (!respond_http(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0))
5743 return (0);
5744
5745 fprintf(stderr, "%s Upgrading to encrypted connection.\n", client->hostname);
5746
5747 if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED))
5748 {
5749 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
5750 return (0);
5751 }
5752
5753 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
5754 }
5755 else
5756#endif /* HAVE_SSL */
5757
a469f8a5 5758 if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
1106b00e
MS
5759 return (0);
5760 }
5761
e60ec91f
MS
5762 /*
5763 * Handle HTTP Expect...
5764 */
5765
a469f8a5
MS
5766 if (httpGetExpect(client->http) &&
5767 (client->operation == HTTP_STATE_POST ||
5768 client->operation == HTTP_STATE_PUT))
e60ec91f 5769 {
a469f8a5 5770 if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
e60ec91f
MS
5771 {
5772 /*
5773 * Send 100-continue header...
5774 */
5775
a469f8a5 5776 if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
e60ec91f
MS
5777 return (0);
5778 }
5779 else
5780 {
5781 /*
5782 * Send 417-expectation-failed header...
5783 */
5784
a469f8a5 5785 if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
e60ec91f 5786 return (0);
e60ec91f
MS
5787 }
5788 }
5789
1106b00e
MS
5790 /*
5791 * Handle new transfers...
5792 */
5793
5794 switch (client->operation)
5795 {
a469f8a5 5796 case HTTP_STATE_OPTIONS :
1106b00e 5797 /*
fe202ff4 5798 * Do OPTIONS command...
1106b00e
MS
5799 */
5800
a469f8a5 5801 return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
1106b00e 5802
a469f8a5 5803 case HTTP_STATE_HEAD :
1106b00e 5804 if (!strcmp(client->uri, "/icon.png"))
a469f8a5 5805 return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
0b5ce83f 5806 else if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies"))
a469f8a5 5807 return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
1106b00e 5808 else
a469f8a5 5809 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e 5810
a469f8a5 5811 case HTTP_STATE_GET :
1106b00e
MS
5812 if (!strcmp(client->uri, "/icon.png"))
5813 {
5814 /*
5815 * Send PNG icon file.
5816 */
5817
3e5092db
MS
5818 if (client->printer->icon)
5819 {
5820 int fd; /* Icon file */
5821 struct stat fileinfo; /* Icon file information */
5822 char buffer[4096]; /* Copy buffer */
5823 ssize_t bytes; /* Bytes */
1106b00e 5824
3e5092db 5825 fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon);
a469f8a5 5826
3e5092db 5827 if (!stat(client->printer->icon, &fileinfo) && (fd = open(client->printer->icon, O_RDONLY)) >= 0)
1106b00e 5828 {
3e5092db
MS
5829 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
5830 {
5831 close(fd);
5832 return (0);
5833 }
5834
5835 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
5836 httpWrite2(client->http, buffer, (size_t)bytes);
5837
5838 httpFlushWrite(client->http);
5839
1106b00e 5840 close(fd);
1106b00e 5841 }
3e5092db
MS
5842 else
5843 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
5844 }
5845 else
5846 {
5847 fputs("Icon file is internal printer.png.\n", stderr);
1106b00e 5848
3e5092db
MS
5849 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_png)))
5850 return (0);
1106b00e 5851
3e5092db 5852 httpWrite2(client->http, (const char *)printer_png, sizeof(printer_png));
a469f8a5 5853 httpFlushWrite(client->http);
1106b00e 5854 }
1106b00e 5855 }
81d8eb3a 5856 else
1106b00e
MS
5857 {
5858 /*
81d8eb3a 5859 * Authenticate if needed...
1106b00e
MS
5860 */
5861
81d8eb3a
MS
5862 if ((http_status = authenticate_request(client)) != HTTP_STATUS_CONTINUE)
5863 {
5864 return (respond_http(client, http_status, NULL, NULL, 0));
5865 }
0b5ce83f 5866
81d8eb3a
MS
5867 if (!strcmp(client->uri, "/"))
5868 {
5869 /*
5870 * Show web status page...
5871 */
5872
5873 return (show_status(client));
5874 }
5875 else if (!strcmp(client->uri, "/media"))
5876 {
5877 /*
5878 * Show web media page...
5879 */
0b5ce83f 5880
81d8eb3a
MS
5881 return (show_media(client));
5882 }
5883 else if (!strcmp(client->uri, "/supplies"))
5884 {
5885 /*
5886 * Show web supplies page...
5887 */
5888
5889 return (show_supplies(client));
5890 }
5891 else
5892 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e 5893 }
1106b00e
MS
5894 break;
5895
a469f8a5
MS
5896 case HTTP_STATE_POST :
5897 if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
1106b00e
MS
5898 "application/ipp"))
5899 {
5900 /*
5901 * Not an IPP request...
5902 */
5903
a469f8a5 5904 return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
1106b00e
MS
5905 }
5906
5907 /*
5908 * Read the IPP request...
5909 */
5910
5911 client->request = ippNew();
5912
a469f8a5
MS
5913 while ((ipp_state = ippRead(client->http,
5914 client->request)) != IPP_STATE_DATA)
5915 {
5916 if (ipp_state == IPP_STATE_ERROR)
1106b00e 5917 {
6d56631f 5918 fprintf(stderr, "%s IPP read error (%s).\n", client->hostname, cupsLastErrorString());
a469f8a5 5919 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
5920 return (0);
5921 }
a469f8a5 5922 }
1106b00e
MS
5923
5924 /*
5925 * Now that we have the IPP request, process the request...
5926 */
5927
5928 return (process_ipp(client));
5929
5930 default :
5931 break; /* Anti-compiler-warning-code */
5932 }
5933
5934 return (1);
5935}
5936
5937
5938/*
5939 * 'process_ipp()' - Process an IPP request.
5940 */
5941
5942static int /* O - 1 on success, 0 on error */
d46dbe1b 5943process_ipp(ippeve_client_t *client) /* I - Client */
1106b00e
MS
5944{
5945 ipp_tag_t group; /* Current group tag */
5946 ipp_attribute_t *attr; /* Current attribute */
5947 ipp_attribute_t *charset; /* Character set attribute */
5948 ipp_attribute_t *language; /* Language attribute */
5949 ipp_attribute_t *uri; /* Printer URI attribute */
a469f8a5
MS
5950 int major, minor; /* Version number */
5951 const char *name; /* Name of attribute */
5ea07c61 5952 http_status_t status; /* Authentication status */
1106b00e
MS
5953
5954
83e08001 5955 debug_attributes("Request", client->request, 1);
1106b00e
MS
5956
5957 /*
5958 * First build an empty response message for this request...
5959 */
5960
a469f8a5
MS
5961 client->operation_id = ippGetOperation(client->request);
5962 client->response = ippNewResponse(client->request);
1106b00e
MS
5963
5964 /*
5965 * Then validate the request header and required attributes...
5966 */
5967
a469f8a5
MS
5968 major = ippGetVersion(client->request, &minor);
5969
5970 if (major < 1 || major > 2)
1106b00e
MS
5971 {
5972 /*
5973 * Return an error, since we only support IPP 1.x and 2.x.
5974 */
5975
b969d5af
MS
5976 respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, "Bad request version number %d.%d.", major, minor);
5977 }
5978 else if ((major * 10 + minor) > MaxVersion)
5979 {
5980 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
5981 httpFlush(client->http); /* Flush trailing (junk) data */
5982
5983 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5984 return (0);
1106b00e 5985 }
a469f8a5 5986 else if (ippGetRequestId(client->request) <= 0)
b969d5af
MS
5987 {
5988 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.", ippGetRequestId(client->request));
5989 }
a469f8a5 5990 else if (!ippFirstAttribute(client->request))
b969d5af
MS
5991 {
5992 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No attributes in request.");
5993 }
1106b00e
MS
5994 else
5995 {
5996 /*
5997 * Make sure that the attributes are provided in the correct order and
5998 * don't repeat groups...
5999 */
6000
a469f8a5
MS
6001 for (attr = ippFirstAttribute(client->request),
6002 group = ippGetGroupTag(attr);
1106b00e 6003 attr;
a469f8a5
MS
6004 attr = ippNextAttribute(client->request))
6005 {
6006 if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
1106b00e
MS
6007 {
6008 /*
6009 * Out of order; return an error...
6010 */
6011
a469f8a5
MS
6012 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6013 "Attribute groups are out of order (%x < %x).",
6014 ippGetGroupTag(attr), group);
1106b00e
MS
6015 break;
6016 }
6017 else
a469f8a5
MS
6018 group = ippGetGroupTag(attr);
6019 }
1106b00e
MS
6020
6021 if (!attr)
6022 {
6023 /*
6024 * Then make sure that the first three attributes are:
6025 *
6026 * attributes-charset
6027 * attributes-natural-language
6028 * printer-uri/job-uri
6029 */
6030
a469f8a5
MS
6031 attr = ippFirstAttribute(client->request);
6032 name = ippGetName(attr);
6033 if (attr && name && !strcmp(name, "attributes-charset") &&
6034 ippGetValueTag(attr) == IPP_TAG_CHARSET)
1106b00e
MS
6035 charset = attr;
6036 else
6037 charset = NULL;
6038
a469f8a5
MS
6039 attr = ippNextAttribute(client->request);
6040 name = ippGetName(attr);
1106b00e 6041
a469f8a5
MS
6042 if (attr && name && !strcmp(name, "attributes-natural-language") &&
6043 ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
1106b00e
MS
6044 language = attr;
6045 else
6046 language = NULL;
6047
6048 if ((attr = ippFindAttribute(client->request, "printer-uri",
6049 IPP_TAG_URI)) != NULL)
6050 uri = attr;
6051 else if ((attr = ippFindAttribute(client->request, "job-uri",
6052 IPP_TAG_URI)) != NULL)
6053 uri = attr;
6054 else
6055 uri = NULL;
6056
1106b00e 6057 if (charset &&
2cadf0f4
MS
6058 strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
6059 strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
1106b00e
MS
6060 {
6061 /*
6062 * Bad character set...
6063 */
6064
a469f8a5 6065 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
1106b00e 6066 "Unsupported character set \"%s\".",
a469f8a5 6067 ippGetString(charset, 0, NULL));
1106b00e
MS
6068 }
6069 else if (!charset || !language || !uri)
6070 {
6071 /*
6072 * Return an error, since attributes-charset,
6073 * attributes-natural-language, and printer-uri/job-uri are required
6074 * for all operations.
6075 */
6076
a469f8a5
MS
6077 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6078 "Missing required attributes.");
1106b00e 6079 }
1106b00e
MS
6080 else
6081 {
8a78aa37
MS
6082 char scheme[32], /* URI scheme */
6083 userpass[32], /* Username/password in URI */
6084 host[256], /* Host name in URI */
6085 resource[256]; /* Resource path in URI */
6086 int port; /* Port number in URI */
6087
6088 name = ippGetName(uri);
6089
6090 if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
6091 scheme, sizeof(scheme),
6092 userpass, sizeof(userpass),
6093 host, sizeof(host), &port,
6094 resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
6095 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
6096 "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
6097 else if ((!strcmp(name, "job-uri") &&
6098 strncmp(resource, "/ipp/print/", 11)) ||
6099 (!strcmp(name, "printer-uri") &&
6100 strcmp(resource, "/ipp/print")))
6101 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
6102 name, ippGetString(uri, 0, NULL));
5ea07c61
MS
6103 else if (client->operation_id != IPP_OP_GET_PRINTER_ATTRIBUTES && (status = authenticate_request(client)) != HTTP_STATUS_CONTINUE)
6104 {
6105 return (respond_http(client, status, NULL, NULL, 0));
6106 }
6107 else
1106b00e
MS
6108 {
6109 /*
8a78aa37 6110 * Try processing the operation...
1106b00e
MS
6111 */
6112
5ea07c61
MS
6113
6114 switch (client->operation_id)
8a78aa37
MS
6115 {
6116 case IPP_OP_PRINT_JOB :
6117 ipp_print_job(client);
6118 break;
6119
6120 case IPP_OP_PRINT_URI :
6121 ipp_print_uri(client);
6122 break;
6123
6124 case IPP_OP_VALIDATE_JOB :
6125 ipp_validate_job(client);
6126 break;
6127
6128 case IPP_OP_CREATE_JOB :
6129 ipp_create_job(client);
6130 break;
6131
6132 case IPP_OP_SEND_DOCUMENT :
6133 ipp_send_document(client);
6134 break;
6135
6136 case IPP_OP_SEND_URI :
6137 ipp_send_uri(client);
6138 break;
6139
6140 case IPP_OP_CANCEL_JOB :
6141 ipp_cancel_job(client);
6142 break;
6143
6144 case IPP_OP_GET_JOB_ATTRIBUTES :
6145 ipp_get_job_attributes(client);
6146 break;
6147
6148 case IPP_OP_GET_JOBS :
6149 ipp_get_jobs(client);
6150 break;
6151
6152 case IPP_OP_GET_PRINTER_ATTRIBUTES :
6153 ipp_get_printer_attributes(client);
6154 break;
6155
2cadf0f4
MS
6156 case IPP_OP_CLOSE_JOB :
6157 ipp_close_job(client);
6158 break;
6159
6160 case IPP_OP_IDENTIFY_PRINTER :
6161 ipp_identify_printer(client);
6162 break;
6163
8a78aa37
MS
6164 default :
6165 respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
6166 "Operation not supported.");
6167 break;
6168 }
1106b00e
MS
6169 }
6170 }
6171 }
6172 }
6173
6174 /*
6175 * Send the HTTP header and return...
6176 */
6177
a469f8a5
MS
6178 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
6179 httpFlush(client->http); /* Flush trailing (junk) data */
1106b00e 6180
a469f8a5 6181 return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
1106b00e
MS
6182 ippLength(client->response)));
6183}
6184
6185
6186/*
6187 * 'process_job()' - Process a print job.
6188 */
6189
6190static void * /* O - Thread exit status */
d46dbe1b 6191process_job(ippeve_job_t *job) /* I - Job */
1106b00e 6192{
a469f8a5
MS
6193 job->state = IPP_JSTATE_PROCESSING;
6194 job->printer->state = IPP_PSTATE_PROCESSING;
4a838088 6195 job->processing = time(NULL);
1106b00e 6196
d46dbe1b 6197 while (job->printer->state_reasons & IPPEVE_PREASON_MEDIA_EMPTY)
0b5ce83f 6198 {
d46dbe1b 6199 job->printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
0b5ce83f
MS
6200
6201 sleep(1);
6202 }
6203
d46dbe1b 6204 job->printer->state_reasons &= (ippeve_preason_t)~IPPEVE_PREASON_MEDIA_NEEDED;
0b5ce83f 6205
db8b865d
MS
6206 if (job->printer->command)
6207 {
6208 /*
6209 * Execute a command with the job spool file and wait for it to complete...
6210 */
6211
aa2a90ce
MS
6212 int pid, /* Process ID */
6213 status; /* Exit status */
58b64171 6214 struct timeval start, /* Start time */
aa2a90ce
MS
6215 end; /* End time */
6216 char *myargv[3], /* Command-line arguments */
6217 *myenvp[400]; /* Environment variables */
6218 int myenvc; /* Number of environment variables */
6219 ipp_attribute_t *attr; /* Job attribute */
6220 char val[1280], /* IPP_NAME=value */
6221 *valptr; /* Pointer into string */
24a06ed3 6222#ifndef _WIN32
cc108616 6223 int mystdout = -1; /* File for stdout */
aa2a90ce
MS
6224 int mypipe[2]; /* Pipe for stderr */
6225 char line[2048], /* Line from stderr */
6226 *ptr, /* Pointer into line */
6227 *endptr; /* End of line */
6228 ssize_t bytes; /* Bytes read */
24a06ed3 6229#endif /* !_WIN32 */
db8b865d 6230
58b64171
MS
6231 fprintf(stderr, "[Job %d] Running command \"%s %s\".\n", job->id, job->printer->command, job->filename);
6232 gettimeofday(&start, NULL);
db8b865d 6233
83ce8172
MS
6234 /*
6235 * Setup the command-line arguments...
6236 */
6237
6238 myargv[0] = job->printer->command;
6239 myargv[1] = job->filename;
6240 myargv[2] = NULL;
6241
6242 /*
aa2a90ce
MS
6243 * Copy the current environment, then add environment variables for every
6244 * Job attribute and Printer -default attributes...
83ce8172
MS
6245 */
6246
6247 for (myenvc = 0; environ[myenvc] && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); myenvc ++)
6248 myenvp[myenvc] = strdup(environ[myenvc]);
6249
aa2a90ce
MS
6250 if (myenvc > (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 32))
6251 {
58b64171 6252 fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id);
aa2a90ce
MS
6253 job->state = IPP_JSTATE_ABORTED;
6254 goto error;
6255 }
6256
bed16f19
MS
6257 snprintf(val, sizeof(val), "CONTENT_TYPE=%s", job->format);
6258 myenvp[myenvc ++] = strdup(val);
aa2a90ce 6259
bed16f19
MS
6260 if (job->printer->device_uri)
6261 {
6262 snprintf(val, sizeof(val), "DEVICE_URI=%s", job->printer->device_uri);
6263 myenvp[myenvc ++] = strdup(val);
6264 }
aa2a90ce 6265
ef4d439c
MS
6266 if (job->printer->output_format)
6267 {
6268 snprintf(val, sizeof(val), "OUTPUT_TYPE=%s", job->printer->output_format);
6269 myenvp[myenvc ++] = strdup(val);
6270 }
6271
6641bd0d 6272#if !CUPS_LITE
bed16f19
MS
6273 if (job->printer->ppdfile)
6274 {
6275 snprintf(val, sizeof(val), "PPD=%s", job->printer->ppdfile);
6276 myenvp[myenvc++] = strdup(val);
6277 }
6641bd0d 6278#endif /* !CUPS_LITE */
aa2a90ce 6279
aa2a90ce
MS
6280 for (attr = ippFirstAttribute(job->printer->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->printer->attrs))
6281 {
6282 /*
6283 * Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and
ef4d439c 6284 * "pwg-xxx" to "IPP_PWG_XXX", then add the value(s) from the attribute.
aa2a90ce
MS
6285 */
6286
6287 const char *name = ippGetName(attr),
6288 /* Attribute name */
6289 *suffix = strstr(name, "-default");
6290 /* Suffix on attribute name */
6291
ef4d439c 6292 if (strncmp(name, "pwg-", 4) && (!suffix || suffix[8]))
aa2a90ce
MS
6293 continue;
6294
6295 valptr = val;
6296 *valptr++ = 'I';
6297 *valptr++ = 'P';
6298 *valptr++ = 'P';
6299 *valptr++ = '_';
6300 while (*name && valptr < (val + sizeof(val) - 2))
6301 {
6302 if (*name == '-')
6303 *valptr++ = '_';
6304 else
6305 *valptr++ = (char)toupper(*name & 255);
6306
6307 name ++;
6308 }
6309 *valptr++ = '=';
6310 ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
6311
6312 myenvp[myenvc++] = strdup(val);
6313 }
6314
83ce8172
MS
6315 for (attr = ippFirstAttribute(job->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->attrs))
6316 {
6317 /*
6318 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6319 * value(s) from the attribute.
6320 */
6321
6322 const char *name = ippGetName(attr);
aa2a90ce
MS
6323 /* Attribute name */
6324
83ce8172
MS
6325 if (!name)
6326 continue;
6327
6328 valptr = val;
6329 *valptr++ = 'I';
6330 *valptr++ = 'P';
6331 *valptr++ = 'P';
6332 *valptr++ = '_';
6333 while (*name && valptr < (val + sizeof(val) - 2))
6334 {
6335 if (*name == '-')
6336 *valptr++ = '_';
6337 else
6338 *valptr++ = (char)toupper(*name & 255);
6339
6340 name ++;
6341 }
6342 *valptr++ = '=';
6343 ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
6344
6345 myenvp[myenvc++] = strdup(val);
6346 }
aa2a90ce
MS
6347
6348 if (attr)
6349 {
58b64171 6350 fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id);
aa2a90ce
MS
6351 job->state = IPP_JSTATE_ABORTED;
6352 goto error;
6353 }
6354
83ce8172
MS
6355 myenvp[myenvc] = NULL;
6356
6357 /*
6358 * Now run the program...
6359 */
6360
24a06ed3 6361#ifdef _WIN32
83ce8172 6362 status = _spawnvpe(_P_WAIT, job->printer->command, myargv, myenvp);
9610a474 6363
83ce8172 6364#else
cc108616
MS
6365 if (job->printer->device_uri)
6366 {
6367 char scheme[32], /* URI scheme */
6368 userpass[256], /* username:password (unused) */
6369 host[256], /* Hostname or IP address */
6370 resource[256]; /* Resource path */
6371 int port; /* Port number */
6372
6373
6374 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)
6375 {
58b64171 6376 fprintf(stderr, "[Job %d] Bad device URI \"%s\".\n", job->id, job->printer->device_uri);
cc108616
MS
6377 }
6378 else if (!strcmp(scheme, "file"))
6379 {
6380 struct stat fileinfo; /* See if this is a file or directory... */
6381
6382 if (stat(resource, &fileinfo))
6383 {
6384 if (errno == ENOENT)
6385 {
dd2b6166 6386 if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC, 0666)) >= 0)
58b64171 6387 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
dd2b6166 6388 else
58b64171 6389 fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno));
cc108616
MS
6390 }
6391 else
58b64171 6392 fprintf(stderr, "[Job %d] Unable to access \"%s\": %s\n", job->id, resource, strerror(errno));
cc108616
MS
6393 }
6394 else if (S_ISDIR(fileinfo.st_mode))
6395 {
dd2b6166 6396 if ((mystdout = create_job_file(job, line, sizeof(line), resource, "prn")) >= 0)
58b64171 6397 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line);
dd2b6166 6398 else
58b64171 6399 fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, line, strerror(errno));
cc108616
MS
6400 }
6401 else if (!S_ISREG(fileinfo.st_mode))
6402 {
dd2b6166 6403 if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC, 0666)) >= 0)
58b64171 6404 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
dd2b6166 6405 else
58b64171 6406 fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno));
cc108616 6407 }
dd2b6166 6408 else if ((mystdout = open(resource, O_WRONLY)) >= 0)
58b64171 6409 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
dd2b6166 6410 else
58b64171 6411 fprintf(stderr, "[Job %d] Unable to open \"%s\": %s\n", job->id, resource, strerror(errno));
cc108616
MS
6412 }
6413 else if (!strcmp(scheme, "socket"))
6414 {
6415 http_addrlist_t *addrlist; /* List of addresses */
6416 char service[32]; /* Service number */
6417
6418 snprintf(service, sizeof(service), "%d", port);
6419
6420 if ((addrlist = httpAddrGetList(host, AF_UNSPEC, service)) == NULL)
58b64171 6421 fprintf(stderr, "[Job %d] Unable to find \"%s\": %s\n", job->id, host, cupsLastErrorString());
cc108616 6422 else if (!httpAddrConnect2(addrlist, &mystdout, 30000, &(job->cancel)))
58b64171 6423 fprintf(stderr, "[Job %d] Unable to connect to \"%s\": %s\n", job->id, host, cupsLastErrorString());
cc108616
MS
6424
6425 httpAddrFreeList(addrlist);
6426 }
6427 else
6428 {
58b64171 6429 fprintf(stderr, "[Job %d] Unsupported device URI scheme \"%s\".\n", job->id, scheme);
cc108616
MS
6430 }
6431 }
dd2b6166 6432 else if ((mystdout = create_job_file(job, line, sizeof(line), job->printer->directory, "prn")) >= 0)
cc108616 6433 {
58b64171 6434 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line);
cc108616
MS
6435 }
6436
6437 if (mystdout < 0)
6438 mystdout = open("/dev/null", O_WRONLY);
6439
9610a474
MS
6440 if (pipe(mypipe))
6441 {
58b64171 6442 fprintf(stderr, "[Job %d] Unable to create pipe for stderr: %s\n", job->id, strerror(errno));
9610a474
MS
6443 mypipe[0] = mypipe[1] = -1;
6444 }
6445
db8b865d
MS
6446 if ((pid = fork()) == 0)
6447 {
6448 /*
6449 * Child comes here...
6450 */
6451
cc108616
MS
6452 close(1);
6453 dup2(mystdout, 1);
6454 close(mystdout);
6455
9610a474
MS
6456 close(2);
6457 dup2(mypipe[1], 2);
6458 close(mypipe[0]);
6459 close(mypipe[1]);
6460
83ce8172 6461 execve(job->printer->command, myargv, myenvp);
db8b865d
MS
6462 exit(errno);
6463 }
6464 else if (pid < 0)
6465 {
6466 /*
6467 * Unable to fork process...
6468 */
6469
58b64171 6470 fprintf(stderr, "[Job %d] Unable to start job processing command: %s\n", job->id, strerror(errno));
83ce8172 6471 status = -1;
1ec50c42 6472
cc108616 6473 close(mystdout);
9610a474
MS
6474 close(mypipe[0]);
6475 close(mypipe[1]);
6476
1ec50c42
MS
6477 /*
6478 * Free memory used for environment...
6479 */
6480
6481 while (myenvc > 0)
6482 free(myenvp[-- myenvc]);
db8b865d
MS
6483 }
6484 else
6485 {
83ce8172
MS
6486 /*
6487 * Free memory used for environment...
6488 */
6489
6490 while (myenvc > 0)
6491 free(myenvp[-- myenvc]);
9610a474 6492
cc108616
MS
6493 /*
6494 * Close the output file in the parent process...
6495 */
6496
6497 close(mystdout);
6498
9610a474
MS
6499 /*
6500 * If the pipe exists, read from it until EOF...
6501 */
6502
6503 if (mypipe[0] >= 0)
6504 {
6505 close(mypipe[1]);
6506
6507 endptr = line;
6508 while ((bytes = read(mypipe[0], endptr, sizeof(line) - (size_t)(endptr - line) - 1)) > 0)
6509 {
6510 endptr += bytes;
6511 *endptr = '\0';
6512
6513 while ((ptr = strchr(line, '\n')) != NULL)
6514 {
dc84a5a4
MS
6515 int level = 3; /* Message log level */
6516
9610a474
MS
6517 *ptr++ = '\0';
6518
dc84a5a4
MS
6519 if (!strncmp(line, "ATTR:", 5))
6520 {
6521 /*
6522 * Process job/printer attribute updates.
6523 */
58b64171 6524
dc84a5a4
MS
6525 process_attr_message(job, line);
6526 }
6527 else if (!strncmp(line, "DEBUG:", 6))
9610a474
MS
6528 {
6529 /*
dc84a5a4 6530 * Debug message...
9610a474
MS
6531 */
6532
dc84a5a4 6533 level = 2;
9610a474 6534 }
dc84a5a4 6535 else if (!strncmp(line, "ERROR:", 6))
9610a474
MS
6536 {
6537 /*
dc84a5a4 6538 * Error message...
9610a474
MS
6539 */
6540
dc84a5a4
MS
6541 level = 0;
6542 job->message = strdup(line + 6);
6543 job->msglevel = 0;
9610a474 6544 }
dc84a5a4
MS
6545 else if (!strncmp(line, "INFO:", 5))
6546 {
6547 /*
6548 * Informational/progress message...
6549 */
6550
6551 level = 1;
6552 if (job->msglevel)
6553 {
6554 job->message = strdup(line + 5);
6555 job->msglevel = 1;
6556 }
6557 }
6558 else if (!strncmp(line, "STATE:", 6))
6559 {
6560 /*
6561 * Process printer-state-reasons keywords.
6562 */
6563
6564 process_state_message(job, line);
6565 }
6566
6567 if (Verbosity >= level)
6568 fprintf(stderr, "[Job %d] Command - %s\n", job->id, line);
9610a474
MS
6569
6570 bytes = ptr - line;
6571 if (ptr < endptr)
63efa616 6572 memmove(line, ptr, (size_t)(endptr - ptr));
9610a474
MS
6573 endptr -= bytes;
6574 *endptr = '\0';
6575 }
6576 }
6577
6578 close(mypipe[0]);
6579 }
6580
db8b865d
MS
6581 /*
6582 * Wait for child to complete...
6583 */
6584
83ce8172 6585# ifdef HAVE_WAITPID
db8b865d 6586 while (waitpid(pid, &status, 0) < 0);
83ce8172 6587# else
db8b865d 6588 while (wait(&status) < 0);
83ce8172
MS
6589# endif /* HAVE_WAITPID */
6590 }
24a06ed3 6591#endif /* _WIN32 */
db8b865d 6592
83ce8172
MS
6593 if (status)
6594 {
24a06ed3 6595#ifndef _WIN32
83ce8172 6596 if (WIFEXITED(status))
24a06ed3 6597#endif /* !_WIN32 */
58b64171 6598 fprintf(stderr, "[Job %d] Command \"%s\" exited with status %d.\n", job->id, job->printer->command, WEXITSTATUS(status));
24a06ed3 6599#ifndef _WIN32
db8b865d 6600 else
58b64171 6601 fprintf(stderr, "[Job %d] Command \"%s\" terminated with signal %d.\n", job->id, job->printer->command, WTERMSIG(status));
24a06ed3 6602#endif /* !_WIN32 */
83ce8172 6603 job->state = IPP_JSTATE_ABORTED;
db8b865d 6604 }
83ce8172
MS
6605 else if (status < 0)
6606 job->state = IPP_JSTATE_ABORTED;
6607 else
58b64171 6608 fprintf(stderr, "[Job %d] Command \"%s\" completed successfully.\n", job->id, job->printer->command);
db8b865d
MS
6609
6610 /*
58b64171 6611 * Report the total processing time...
db8b865d
MS
6612 */
6613
58b64171
MS
6614 gettimeofday(&end, NULL);
6615
6616 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
6617 }
6618 else
6619 {
6620 /*
6621 * Sleep for a random amount of time to simulate job processing.
6622 */
6623
3f3b5353 6624 sleep((unsigned)(5 + (CUPS_RAND() % 11)));
db8b865d 6625 }
1106b00e
MS
6626
6627 if (job->cancel)
a469f8a5 6628 job->state = IPP_JSTATE_CANCELED;
4a838088 6629 else if (job->state == IPP_JSTATE_PROCESSING)
a469f8a5 6630 job->state = IPP_JSTATE_COMPLETED;
1106b00e 6631
aa2a90ce
MS
6632 error:
6633
1106b00e 6634 job->completed = time(NULL);
a469f8a5 6635 job->printer->state = IPP_PSTATE_IDLE;
1106b00e
MS
6636 job->printer->active_job = NULL;
6637
6638 return (NULL);
6639}
6640
6641
9610a474
MS
6642/*
6643 * 'process_state_message()' - Process a STATE: message from a command.
6644 */
6645
6646static void
6647process_state_message(
d46dbe1b 6648 ippeve_job_t *job, /* I - Job */
9610a474
MS
6649 char *message) /* I - Message */
6650{
6651 int i; /* Looping var */
d46dbe1b 6652 ippeve_preason_t state_reasons, /* printer-state-reasons values */
9610a474
MS
6653 bit; /* Current reason bit */
6654 char *ptr, /* Pointer into message */
6655 *next; /* Next keyword in message */
6656 int remove; /* Non-zero if we are removing keywords */
6657
6658
6659 /*
6660 * Skip leading "STATE:" and any whitespace...
6661 */
6662
6663 for (message += 6; *message; message ++)
6664 if (*message != ' ' && *message != '\t')
6665 break;
6666
6667 /*
6668 * Support the following forms of message:
6669 *
6670 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6671 *
6672 * "-keyword[,keyword,...]" to remove keywords.
6673 *
6674 * "+keyword[,keyword,...]" to add keywords.
6675 *
6676 * Keywords may or may not have a suffix (-report, -warning, -error) per
8a4ed632 6677 * RFC 8011.
9610a474
MS
6678 */
6679
6680 if (*message == '-')
6681 {
6682 remove = 1;
6683 state_reasons = job->printer->state_reasons;
6684 message ++;
6685 }
6686 else if (*message == '+')
6687 {
6688 remove = 0;
6689 state_reasons = job->printer->state_reasons;
6690 message ++;
6691 }
6692 else
6693 {
6694 remove = 0;
d46dbe1b 6695 state_reasons = IPPEVE_PREASON_NONE;
9610a474
MS
6696 }
6697
6698 while (*message)
6699 {
6700 if ((next = strchr(message, ',')) != NULL)
6701 *next++ = '\0';
6702
6703 if ((ptr = strstr(message, "-error")) != NULL)
6704 *ptr = '\0';
6705 else if ((ptr = strstr(message, "-report")) != NULL)
6706 *ptr = '\0';
6707 else if ((ptr = strstr(message, "-warning")) != NULL)
6708 *ptr = '\0';
6709
d46dbe1b 6710 for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
9610a474 6711 {
d46dbe1b 6712 if (!strcmp(message, ippeve_preason_strings[i]))
9610a474
MS
6713 {
6714 if (remove)
6715 state_reasons &= ~bit;
6716 else
6717 state_reasons |= bit;
6718 }
6719 }
6720
6721 if (next)
6722 message = next;
6723 else
6724 break;
6725 }
6726
6727 job->printer->state_reasons = state_reasons;
6728}
6729
6730
1106b00e
MS
6731/*
6732 * 'register_printer()' - Register a printer object via Bonjour.
6733 */
6734
6735static int /* O - 1 on success, 0 on error */
6736register_printer(
d46dbe1b 6737 ippeve_printer_t *printer, /* I - Printer */
1562b9a1 6738 const char *subtypes) /* I - Service subtype(s) */
1106b00e 6739{
0a15691a 6740#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
d46dbe1b 6741 ippeve_txt_t ipp_txt; /* Bonjour IPP TXT record */
1562b9a1
MS
6742 int i, /* Looping var */
6743 count; /* Number of values */
6744 ipp_attribute_t *color_supported,
6745 *document_format_supported,
6746 *printer_location,
6747 *printer_make_and_model,
6748 *printer_more_info,
6749 *printer_uuid,
6750 *sides_supported,
6751 *urf_supported; /* Printer attributes */
6752 const char *value; /* Value string */
6753 char formats[252], /* List of supported formats */
6754 urf[252], /* List of supported URF values */
6755 *ptr; /* Pointer into string */
6756
7a2be9fa
MS
6757
6758 if (!strcmp(subtypes, "off"))
6759 return (1);
6760
1562b9a1
MS
6761 color_supported = ippFindAttribute(printer->attrs, "color-supported", IPP_TAG_BOOLEAN);
6762 document_format_supported = ippFindAttribute(printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE);
6763 printer_location = ippFindAttribute(printer->attrs, "printer-location", IPP_TAG_TEXT);
6764 printer_make_and_model = ippFindAttribute(printer->attrs, "printer-make-and-model", IPP_TAG_TEXT);
6765 printer_more_info = ippFindAttribute(printer->attrs, "printer-more-info", IPP_TAG_URI);
6766 printer_uuid = ippFindAttribute(printer->attrs, "printer-uuid", IPP_TAG_URI);
6767 sides_supported = ippFindAttribute(printer->attrs, "sides-supported", IPP_TAG_KEYWORD);
6768 urf_supported = ippFindAttribute(printer->attrs, "urf-supported", IPP_TAG_KEYWORD);
6769
6770 for (i = 0, count = ippGetCount(document_format_supported), ptr = formats; i < count; i ++)
6771 {
6772 value = ippGetString(document_format_supported, i, NULL);
6773
6774 if (!strcasecmp(value, "application/octet-stream"))
6775 continue;
6776
6777 if (ptr > formats && ptr < (formats + sizeof(formats) - 1))
6778 *ptr++ = ',';
6779
6780 strlcpy(ptr, value, sizeof(formats) - (size_t)(ptr - formats));
6781 ptr += strlen(ptr);
6782
6783 if (ptr >= (formats + sizeof(formats) - 1))
6784 break;
6785 }
6786
6787 urf[0] = '\0';
6788 for (i = 0, count = ippGetCount(urf_supported), ptr = urf; i < count; i ++)
6789 {
6790 value = ippGetString(urf_supported, i, NULL);
6791
6792 if (ptr > urf && ptr < (urf + sizeof(urf) - 1))
6793 *ptr++ = ',';
6794
6795 strlcpy(ptr, value, sizeof(urf) - (size_t)(ptr - urf));
6796 ptr += strlen(ptr);
6797
6798 if (ptr >= (urf + sizeof(urf) - 1))
6799 break;
6800 }
6801
0a15691a 6802#endif /* HAVE_DNSSD || HAVE_AVAHI */
d6563739 6803#ifdef HAVE_DNSSD
1106b00e 6804 DNSServiceErrorType error; /* Error from Bonjour */
1562b9a1 6805 char regtype[256]; /* Bonjour service type */
7a2be9fa 6806 uint32_t interface; /* Interface index */
1106b00e
MS
6807
6808
6809 /*
6810 * Build the TXT record for IPP...
6811 */
6812
d6563739
MS
6813 TXTRecordCreate(&ipp_txt, 1024, NULL);
6814 TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print");
1562b9a1
MS
6815 if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
6816 TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(value), value);
6817 if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL)
6818 TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(value), value);
6819 if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
6820 TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(value), value);
6821 TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats), formats);
6822 TXTRecordSetValue(&ipp_txt, "Color", 1, ippGetBoolean(color_supported, 0) ? "T" : "F");
6823 TXTRecordSetValue(&ipp_txt, "Duplex", 1, ippGetCount(sides_supported) > 1 ? "T" : "F");
6824 if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
6825 TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(value) - 9, value + 9);
fe202ff4 6826# ifdef HAVE_SSL
d6563739 6827 TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2");
fe202ff4 6828# endif /* HAVE_SSL */
1562b9a1 6829 if (urf[0])
60d8f884 6830 TXTRecordSetValue(&ipp_txt, "URF", (uint8_t)strlen(urf), urf);
a2798463
MS
6831 TXTRecordSetValue(&ipp_txt, "txtvers", 1, "1");
6832 TXTRecordSetValue(&ipp_txt, "qtotal", 1, "1");
1106b00e 6833
1106b00e
MS
6834 /*
6835 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6836 * defend our service name but not actually support LPD...
6837 */
6838
7a2be9fa
MS
6839 interface = !strcmp(printer->hostname, "localhost") ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
6840
d6563739 6841 printer->printer_ref = DNSSDMaster;
1106b00e 6842
7a2be9fa 6843 if ((error = DNSServiceRegister(&(printer->printer_ref), kDNSServiceFlagsShareConnection, interface, printer->dnssd_name, "_printer._tcp", NULL /* domain */, NULL /* host */, 0 /* port */, 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
1562b9a1
MS
6844 {
6845 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_printer._tcp", error);
1106b00e
MS
6846 return (0);
6847 }
6848
6849 /*
1562b9a1 6850 * Then register the _ipp._tcp (IPP) service type with the real port number to
1106b00e
MS
6851 * advertise our IPP printer...
6852 */
6853
d6563739 6854 printer->ipp_ref = DNSSDMaster;
1106b00e 6855
1562b9a1
MS
6856 if (subtypes && *subtypes)
6857 snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtypes);
a469f8a5 6858 else
015214aa 6859 strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
a469f8a5 6860
7a2be9fa 6861 if ((error = DNSServiceRegister(&(printer->ipp_ref), kDNSServiceFlagsShareConnection, interface, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
1562b9a1
MS
6862 {
6863 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
1106b00e
MS
6864 return (0);
6865 }
6866
f93b32b6 6867# ifdef HAVE_SSL
a469f8a5 6868 /*
1562b9a1 6869 * Then register the _ipps._tcp (IPP) service type with the real port number to
d6563739 6870 * advertise our IPPS printer...
a469f8a5
MS
6871 */
6872
d6563739 6873 printer->ipps_ref = DNSSDMaster;
a469f8a5 6874
1562b9a1
MS
6875 if (subtypes && *subtypes)
6876 snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtypes);
a469f8a5 6877 else
015214aa 6878 strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
a469f8a5 6879
7a2be9fa 6880 if ((error = DNSServiceRegister(&(printer->ipps_ref), kDNSServiceFlagsShareConnection, interface, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
1562b9a1
MS
6881 {
6882 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
a469f8a5
MS
6883 return (0);
6884 }
6885# endif /* HAVE_SSL */
6886
1106b00e
MS
6887 /*
6888 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6889 * real port number to advertise our IPP printer...
6890 */
6891
d6563739 6892 printer->http_ref = DNSSDMaster;
1106b00e 6893
7a2be9fa 6894 if ((error = DNSServiceRegister(&(printer->http_ref), kDNSServiceFlagsShareConnection, interface, printer->dnssd_name, "_http._tcp,_printer", NULL /* domain */, NULL /* host */, htons(printer->port), 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
1562b9a1
MS
6895 {
6896 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_http._tcp,_printer", error);
1106b00e
MS
6897 return (0);
6898 }
6899
d6563739
MS
6900 TXTRecordDeallocate(&ipp_txt);
6901
6902#elif defined(HAVE_AVAHI)
6903 char temp[256]; /* Subtype service string */
6904
6905 /*
6906 * Create the TXT record...
6907 */
6908
6909 ipp_txt = NULL;
6910 ipp_txt = avahi_string_list_add_printf(ipp_txt, "rp=ipp/print");
1562b9a1
MS
6911 if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
6912 ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s", value);
6913 if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL)
6914 ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", value);
6915 if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
6916 ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", value);
d6563739 6917 ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
1562b9a1
MS
6918 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", ippGetBoolean(color_supported, 0) ? "T" : "F");
6919 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", ippGetCount(sides_supported) > 1 ? "T" : "F");
6920 if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
6921 ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", value + 9);
d6563739
MS
6922# ifdef HAVE_SSL
6923 ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2");
6924# endif /* HAVE_SSL */
1562b9a1
MS
6925 if (urf[0])
6926 ipp_txt = avahi_string_list_add_printf(ipp_txt, "URF=%s", urf);
6927 ipp_txt = avahi_string_list_add_printf(ipp_txt, "txtvers=1");
6928 ipp_txt = avahi_string_list_add_printf(ipp_txt, "qtotal=1");
d6563739
MS
6929
6930 /*
6931 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6932 */
6933
6934 avahi_threaded_poll_lock(DNSSDMaster);
6935
6936 printer->ipp_ref = avahi_entry_group_new(DNSSDClient, dnssd_callback, NULL);
6937
6938 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);
6939
6940 /*
400e67c1 6941 * Then register the _ipp._tcp (IPP)...
d6563739
MS
6942 */
6943
6944 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 6945 if (subtypes && *subtypes)
d6563739 6946 {
400e67c1
MS
6947 char *temptypes = strdup(subtypes), *start, *end;
6948
6949 for (start = temptypes; *start; start = end)
6950 {
6951 if ((end = strchr(start, ',')) != NULL)
6952 *end++ = '\0';
6953 else
6954 end = start + strlen(start);
6955
6956 snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", start);
6957 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp);
6958 }
6959
6960 free(temptypes);
d6563739
MS
6961 }
6962
6963#ifdef HAVE_SSL
6964 /*
400e67c1 6965 * _ipps._tcp (IPPS) for secure printing...
d6563739
MS
6966 */
6967
6968 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 6969 if (subtypes && *subtypes)
d6563739 6970 {
400e67c1
MS
6971 char *temptypes = strdup(subtypes), *start, *end;
6972
6973 for (start = temptypes; *start; start = end)
6974 {
6975 if ((end = strchr(start, ',')) != NULL)
6976 *end++ = '\0';
6977 else
6978 end = start + strlen(start);
6979
6980 snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", start);
6981 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp);
6982 }
6983
6984 free(temptypes);
d6563739
MS
6985 }
6986#endif /* HAVE_SSL */
6987
6988 /*
6989 * Finally _http.tcp (HTTP) for the web interface...
6990 */
6991
6992 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);
6993 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");
6994
6995 /*
6996 * Commit it...
6997 */
6998
6999 avahi_entry_group_commit(printer->ipp_ref);
7000 avahi_threaded_poll_unlock(DNSSDMaster);
7001
7002 avahi_string_list_free(ipp_txt);
7003#endif /* HAVE_DNSSD */
7004
1106b00e
MS
7005 return (1);
7006}
7007
7008
7009/*
7010 * 'respond_http()' - Send a HTTP response.
7011 */
7012
7013int /* O - 1 on success, 0 on failure */
a469f8a5 7014respond_http(
d46dbe1b 7015 ippeve_client_t *client, /* I - Client */
a469f8a5
MS
7016 http_status_t code, /* I - HTTP status of response */
7017 const char *content_encoding, /* I - Content-Encoding of response */
7018 const char *type, /* I - MIME media type of response */
7019 size_t length) /* I - Length of response */
1106b00e
MS
7020{
7021 char message[1024]; /* Text message */
7022
7023
a469f8a5 7024 fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
1106b00e 7025
a469f8a5 7026 if (code == HTTP_STATUS_CONTINUE)
1106b00e
MS
7027 {
7028 /*
7029 * 100-continue doesn't send any headers...
7030 */
7031
a469f8a5 7032 return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
1106b00e
MS
7033 }
7034
7035 /*
7036 * Format an error message...
7037 */
7038
fe202ff4 7039 if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
1106b00e
MS
7040 {
7041 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
7042
7043 type = "text/plain";
7044 length = strlen(message);
7045 }
7046 else
7047 message[0] = '\0';
7048
7049 /*
a469f8a5 7050 * Send the HTTP response header...
1106b00e
MS
7051 */
7052
a469f8a5 7053 httpClearFields(client->http);
1106b00e 7054
a469f8a5
MS
7055 if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
7056 client->operation == HTTP_STATE_OPTIONS)
7057 httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
1106b00e 7058
5ea07c61
MS
7059 if (code == HTTP_STATUS_UNAUTHORIZED)
7060 {
7061 char value[256]; /* WWW-Authenticate value */
7062
7063 snprintf(value, sizeof(value), "Basic realm=\"%s\"", PAMService);
7064 httpSetField(client->http, HTTP_FIELD_WWW_AUTHENTICATE, value);
7065 }
7066
1106b00e
MS
7067 if (type)
7068 {
7069 if (!strcmp(type, "text/html"))
a469f8a5
MS
7070 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
7071 "text/html; charset=utf-8");
7072 else
7073 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
1106b00e 7074
a469f8a5
MS
7075 if (content_encoding)
7076 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
1106b00e 7077 }
1106b00e 7078
a469f8a5
MS
7079 httpSetLength(client->http, length);
7080
7081 if (httpWriteResponse(client->http, code) < 0)
1106b00e
MS
7082 return (0);
7083
7084 /*
7085 * Send the response data...
7086 */
7087
7088 if (message[0])
7089 {
7090 /*
7091 * Send a plain text message.
7092 */
7093
a469f8a5
MS
7094 if (httpPrintf(client->http, "%s", message) < 0)
7095 return (0);
7096
7097 if (httpWrite2(client->http, "", 0) < 0)
1106b00e
MS
7098 return (0);
7099 }
7100 else if (client->response)
7101 {
7102 /*
7103 * Send an IPP response...
7104 */
7105
83e08001 7106 debug_attributes("Response", client->response, 2);
1106b00e 7107
a469f8a5 7108 ippSetState(client->response, IPP_STATE_IDLE);
1106b00e 7109
a469f8a5 7110 if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
1106b00e
MS
7111 return (0);
7112 }
1106b00e 7113
a469f8a5 7114 return (1);
1106b00e
MS
7115}
7116
7117
7118/*
7119 * 'respond_ipp()' - Send an IPP response.
7120 */
7121
7122static void
d46dbe1b 7123respond_ipp(ippeve_client_t *client, /* I - Client */
1106b00e
MS
7124 ipp_status_t status, /* I - status-code */
7125 const char *message, /* I - printf-style status-message */
7126 ...) /* I - Additional args as needed */
7127{
a469f8a5 7128 const char *formatted = NULL; /* Formatted message */
1106b00e
MS
7129
7130
a469f8a5 7131 ippSetStatusCode(client->response, status);
1106b00e
MS
7132
7133 if (message)
7134 {
a469f8a5
MS
7135 va_list ap; /* Pointer to additional args */
7136 ipp_attribute_t *attr; /* New status-message attribute */
7137
1106b00e 7138 va_start(ap, message);
6d56631f 7139 if ((attr = ippFindAttribute(client->response, "status-message", IPP_TAG_TEXT)) != NULL)
a469f8a5
MS
7140 ippSetStringfv(client->response, &attr, 0, message, ap);
7141 else
6d56631f 7142 attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, "status-message", NULL, message, ap);
1106b00e
MS
7143 va_end(ap);
7144
a469f8a5 7145 formatted = ippGetString(attr, 0, NULL);
1106b00e 7146 }
1106b00e 7147
a469f8a5 7148 if (formatted)
6d56631f 7149 fprintf(stderr, "%s %s %s (%s)\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status), formatted);
a469f8a5 7150 else
6d56631f 7151 fprintf(stderr, "%s %s %s\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status));
1106b00e
MS
7152}
7153
7154
83e08001
MS
7155/*
7156 * 'respond_unsupported()' - Respond with an unsupported attribute.
7157 */
7158
7159static void
7160respond_unsupported(
d46dbe1b 7161 ippeve_client_t *client, /* I - Client */
83e08001
MS
7162 ipp_attribute_t *attr) /* I - Atribute */
7163{
a2326b5b
MS
7164 ipp_attribute_t *temp; /* Copy of attribute */
7165
7166
6d56631f 7167 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Unsupported %s %s%s value.", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)));
83e08001 7168
a2326b5b
MS
7169 temp = ippCopyAttribute(client->response, attr, 0);
7170 ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
83e08001
MS
7171}
7172
7173
1106b00e
MS
7174/*
7175 * 'run_printer()' - Run the printer service.
7176 */
7177
7178static void
d46dbe1b 7179run_printer(ippeve_printer_t *printer) /* I - Printer */
1106b00e 7180{
0268488e 7181 int num_fds; /* Number of file descriptors */
1106b00e
MS
7182 struct pollfd polldata[3]; /* poll() data */
7183 int timeout; /* Timeout for poll() */
d46dbe1b 7184 ippeve_client_t *client; /* New client */
1106b00e
MS
7185
7186
7187 /*
7188 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
7189 */
7190
7191 polldata[0].fd = printer->ipv4;
7192 polldata[0].events = POLLIN;
7193
7194 polldata[1].fd = printer->ipv6;
7195 polldata[1].events = POLLIN;
7196
0268488e
MS
7197 num_fds = 2;
7198
7199#ifdef HAVE_DNSSD
d6563739 7200 polldata[num_fds ].fd = DNSServiceRefSockFD(DNSSDMaster);
0268488e
MS
7201 polldata[num_fds ++].events = POLLIN;
7202#endif /* HAVE_DNSSD */
1106b00e
MS
7203
7204 /*
7205 * Loop until we are killed or have a hard error...
7206 */
7207
7208 for (;;)
7209 {
7210 if (cupsArrayCount(printer->jobs))
7211 timeout = 10;
7212 else
7213 timeout = -1;
7214
7e86f2f6 7215 if (poll(polldata, (nfds_t)num_fds, timeout) < 0 && errno != EINTR)
1106b00e
MS
7216 {
7217 perror("poll() failed");
7218 break;
7219 }
7220
7221 if (polldata[0].revents & POLLIN)
7222 {
7223 if ((client = create_client(printer, printer->ipv4)) != 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
7239 if (polldata[1].revents & POLLIN)
7240 {
7241 if ((client = create_client(printer, printer->ipv6)) != NULL)
7242 {
116c301f
MS
7243 _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
7244
7245 if (t)
7246 {
7247 _cupsThreadDetach(t);
7248 }
7249 else
1106b00e
MS
7250 {
7251 perror("Unable to create client thread");
7252 delete_client(client);
7253 }
7254 }
7255 }
7256
0268488e 7257#ifdef HAVE_DNSSD
1106b00e 7258 if (polldata[2].revents & POLLIN)
d6563739 7259 DNSServiceProcessResult(DNSSDMaster);
0268488e 7260#endif /* HAVE_DNSSD */
1106b00e
MS
7261
7262 /*
7263 * Clean out old jobs...
7264 */
7265
7266 clean_jobs(printer);
7267 }
7268}
7269
7270
9141aa01
MS
7271/*
7272 * 'show_media()' - Show media load state.
7273 */
7274
7275static int /* O - 1 on success, 0 on failure */
7276show_media(ippeve_client_t *client) /* I - Client connection */
7277{
7278 ippeve_printer_t *printer = client->printer;
7279 /* Printer */
7280 int i, j, /* Looping vars */
7281 num_ready, /* Number of ready media */
7282 num_sizes, /* Number of media sizes */
7283 num_sources, /* Number of media sources */
7284 num_types; /* Number of media types */
7285 ipp_attribute_t *media_col_ready,/* media-col-ready attribute */
7286 *media_ready, /* media-ready attribute */
7287 *media_sizes, /* media-supported attribute */
7288 *media_sources, /* media-source-supported attribute */
7289 *media_types, /* media-type-supported attribute */
7290 *input_tray; /* printer-input-tray attribute */
7291 ipp_t *media_col; /* media-col value */
7292 const char *media_size, /* media value */
7293 *media_source, /* media-source value */
7294 *media_type, /* media-type value */
7295 *ready_size, /* media-col-ready media-size[-name] value */
7296 *ready_source, /* media-col-ready media-source value */
7297 *ready_tray, /* printer-input-tray value */
7298 *ready_type; /* media-col-ready media-type value */
7299 char tray_str[1024], /* printer-input-tray string value */
7300 *tray_ptr; /* Pointer into value */
7301 int tray_len; /* Length of printer-input-tray value */
7302 int ready_sheets; /* printer-input-tray sheets value */
f4f96318
MS
7303 int num_options = 0;/* Number of form options */
7304 cups_option_t *options = NULL;/* Form options */
9141aa01
MS
7305 static const int sheets[] = /* Number of sheets */
7306 {
7307 250,
2d6dcec1
MS
7308 125,
7309 50,
9141aa01
MS
7310 25,
7311 5,
6640ceec
MS
7312 0,
7313 -2
9141aa01
MS
7314 };
7315
7316
7317 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7318 return (0);
7319
6640ceec 7320 html_header(client, printer->name, 0);
9141aa01
MS
7321
7322 if ((media_col_ready = ippFindAttribute(printer->attrs, "media-col-ready", IPP_TAG_BEGIN_COLLECTION)) == NULL)
7323 {
7324 html_printf(client, "<p>Error: No media-col-ready defined for printer.</p>\n");
7325 html_footer(client);
7326 return (1);
7327 }
7328
7329 media_ready = ippFindAttribute(printer->attrs, "media-ready", IPP_TAG_ZERO);
7330
7331 if ((media_sizes = ippFindAttribute(printer->attrs, "media-supported", IPP_TAG_ZERO)) == NULL)
7332 {
7333 html_printf(client, "<p>Error: No media-supported defined for printer.</p>\n");
7334 html_footer(client);
7335 return (1);
7336 }
7337
7338 if ((media_sources = ippFindAttribute(printer->attrs, "media-source-supported", IPP_TAG_ZERO)) == NULL)
7339 {
7340 html_printf(client, "<p>Error: No media-source-supported defined for printer.</p>\n");
7341 html_footer(client);
7342 return (1);
7343 }
7344
7345 if ((media_types = ippFindAttribute(printer->attrs, "media-type-supported", IPP_TAG_ZERO)) == NULL)
7346 {
7347 html_printf(client, "<p>Error: No media-type-supported defined for printer.</p>\n");
7348 html_footer(client);
7349 return (1);
7350 }
7351
7352 if ((input_tray = ippFindAttribute(printer->attrs, "printer-input-tray", IPP_TAG_STRING)) == NULL)
7353 {
7354 html_printf(client, "<p>Error: No printer-input-tray defined for printer.</p>\n");
7355 html_footer(client);
7356 return (1);
7357 }
7358
7359 num_ready = ippGetCount(media_col_ready);
7360 num_sizes = ippGetCount(media_sizes);
7361 num_sources = ippGetCount(media_sources);
7362 num_types = ippGetCount(media_types);
7363
7364 if (num_sources != ippGetCount(input_tray))
7365 {
7366 html_printf(client, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n");
7367 html_footer(client);
7368 return (1);
7369 }
7370
7371 /*
7372 * Process form data if present...
7373 */
7374
820cb58e
MS
7375 if (printer->web_forms)
7376 num_options = parse_options(client, &options);
820cb58e
MS
7377
7378 if (num_options > 0)
9141aa01
MS
7379 {
7380 /*
7381 * WARNING: A real printer/server implementation MUST NOT implement
7382 * media updates via a GET request - GET requests are supposed to be
7383 * idempotent (without side-effects) and we obviously are not
7384 * authenticating access here. This form is provided solely to
7385 * enable testing and development!
7386 */
7387
7388 char name[255]; /* Form name */
7389 const char *val; /* Form value */
7390 pwg_media_t *media; /* Media info */
7391
7392 _cupsRWLockWrite(&printer->rwlock);
7393
9141aa01
MS
7394 ippDeleteAttribute(printer->attrs, media_col_ready);
7395 media_col_ready = NULL;
7396
7397 if (media_ready)
7398 {
7399 ippDeleteAttribute(printer->attrs, media_ready);
7400 media_ready = NULL;
7401 }
7402
7403 printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MEDIA_LOW | IPPEVE_PREASON_MEDIA_EMPTY | IPPEVE_PREASON_MEDIA_NEEDED);
7404
7405 for (i = 0; i < num_sources; i ++)
7406 {
7407 media_source = ippGetString(media_sources, i, NULL);
7408
2d6dcec1 7409 if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
6640ceec
MS
7410 continue;
7411
9141aa01
MS
7412 snprintf(name, sizeof(name), "size%d", i);
7413 if ((media_size = cupsGetOption(name, num_options, options)) != NULL && (media = pwgMediaForPWG(media_size)) != NULL)
7414 {
7415 snprintf(name, sizeof(name), "type%d", i);
7416 if ((media_type = cupsGetOption(name, num_options, options)) != NULL && !*media_type)
7417 media_type = NULL;
7418
7419 if (media_ready)
7420 ippSetString(printer->attrs, &media_ready, ippGetCount(media_ready), media_size);
7421 else
7422 media_ready = ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, media_size);
7423
7424 media_col = create_media_col(media_size, media_source, media_type, media->width, media->length, -1, -1, -1, -1);
7425
7426 if (media_col_ready)
7427 ippSetCollection(printer->attrs, &media_col_ready, ippGetCount(media_col_ready), media_col);
7428 else
7429 media_col_ready = ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-ready", media_col);
7430 ippDelete(media_col);
7431 }
7432 else
7433 media = NULL;
7434
7435 snprintf(name, sizeof(name), "level%d", i);
7436 if ((val = cupsGetOption(name, num_options, options)) != NULL)
7437 ready_sheets = atoi(val);
7438 else
7439 ready_sheets = 0;
7440
2d6dcec1 7441 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 7442
6640ceec 7443 ippSetOctetString(printer->attrs, &input_tray, i, tray_str, (int)strlen(tray_str));
9141aa01
MS
7444
7445 if (ready_sheets == 0)
7446 {
7447 printer->state_reasons |= IPPEVE_PREASON_MEDIA_EMPTY;
7448 if (printer->active_job)
7449 printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
7450 }
6640ceec 7451 else if (ready_sheets < 25 && ready_sheets > 0)
9141aa01
MS
7452 printer->state_reasons |= IPPEVE_PREASON_MEDIA_LOW;
7453 }
7454
7455 if (!media_col_ready)
7456 media_col_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-col-ready");
7457
7458 if (!media_ready)
7459 media_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-ready");
7460
7461 _cupsRWUnlock(&printer->rwlock);
9141aa01
MS
7462 }
7463
820cb58e
MS
7464 if (printer->web_forms)
7465 html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
9141aa01
MS
7466
7467 html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
7468 for (i = 0; i < num_sources; i ++)
7469 {
7470 media_source = ippGetString(media_sources, i, NULL);
7471
2d6dcec1 7472 if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
6640ceec
MS
7473 continue;
7474
9141aa01
MS
7475 for (j = 0, ready_size = NULL, ready_type = NULL; j < num_ready; j ++)
7476 {
7477 media_col = ippGetCollection(media_col_ready, j);
7478 ready_size = ippGetString(ippFindAttribute(media_col, "media-size-name", IPP_TAG_ZERO), 0, NULL);
7479 ready_source = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL);
7480 ready_type = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL);
7481
7482 if (ready_source && !strcmp(ready_source, media_source))
7483 break;
7484
7485 ready_source = NULL;
7486 ready_size = NULL;
7487 ready_type = NULL;
7488 }
7489
820cb58e
MS
7490 html_printf(client, "<tr><th>%s:</th>", media_source);
7491
9141aa01
MS
7492 /*
7493 * Media size...
7494 */
7495
820cb58e 7496 if (printer->web_forms)
9141aa01 7497 {
820cb58e
MS
7498 html_printf(client, "<td><select name=\"size%d\"><option value=\"\">None</option>", i);
7499 for (j = 0; j < num_sizes; j ++)
7500 {
7501 media_size = ippGetString(media_sizes, j, NULL);
9141aa01 7502
820cb58e
MS
7503 html_printf(client, "<option%s>%s</option>", (ready_size && !strcmp(ready_size, media_size)) ? " selected" : "", media_size);
7504 }
7505 html_printf(client, "</select>");
9141aa01 7506 }
820cb58e
MS
7507 else
7508 html_printf(client, "<td>%s", ready_size);
9141aa01
MS
7509
7510 /*
7511 * Media type...
7512 */
7513
820cb58e 7514 if (printer->web_forms)
9141aa01 7515 {
820cb58e
MS
7516 html_printf(client, " <select name=\"type%d\"><option value=\"\">None</option>", i);
7517 for (j = 0; j < num_types; j ++)
7518 {
7519 media_type = ippGetString(media_types, j, NULL);
9141aa01 7520
820cb58e
MS
7521 html_printf(client, "<option%s>%s</option>", (ready_type && !strcmp(ready_type, media_type)) ? " selected" : "", media_type);
7522 }
7523 html_printf(client, "</select>");
9141aa01 7524 }
e1074f48 7525 else if (ready_type)
820cb58e 7526 html_printf(client, ", %s", ready_type);
9141aa01
MS
7527
7528 /*
7529 * Level/sheets loaded...
7530 */
7531
7532 if ((ready_tray = ippGetOctetString(input_tray, i, &tray_len)) != NULL)
7533 {
60d8f884
MS
7534 if (tray_len > (int)(sizeof(tray_str) - 1))
7535 tray_len = (int)sizeof(tray_str) - 1;
9141aa01
MS
7536 memcpy(tray_str, ready_tray, (size_t)tray_len);
7537 tray_str[tray_len] = '\0';
7538
7539 if ((tray_ptr = strstr(tray_str, "level=")) != NULL)
7540 ready_sheets = atoi(tray_ptr + 6);
7541 else
7542 ready_sheets = 0;
7543 }
7544 else
7545 ready_sheets = 0;
7546
820cb58e 7547 if (printer->web_forms)
6640ceec 7548 {
820cb58e
MS
7549 html_printf(client, " <select name=\"level%d\">", i);
7550 for (j = 0; j < (int)(sizeof(sheets) / sizeof(sheets[0])); j ++)
7551 {
2d6dcec1 7552 if (!strcmp(media_source, "by-pass-tray") && sheets[j] > 25)
820cb58e 7553 continue;
6640ceec 7554
820cb58e
MS
7555 if (sheets[j] < 0)
7556 html_printf(client, "<option value=\"%d\"%s>Unknown</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "");
7557 else
7558 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "", sheets[j]);
7559 }
7560 html_printf(client, "</select></td></tr>\n");
6640ceec 7561 }
e1074f48
MS
7562 else if (ready_sheets == 1)
7563 html_printf(client, ", 1 sheet</td></tr>\n");
820cb58e
MS
7564 else if (ready_sheets > 0)
7565 html_printf(client, ", %d sheets</td></tr>\n", ready_sheets);
7566 else
7567 html_printf(client, "</td></tr>\n");
9141aa01
MS
7568 }
7569
820cb58e
MS
7570 if (printer->web_forms)
7571 {
7572 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\">");
7573 if (num_options > 0)
7574 html_printf(client, " <span class=\"badge\" id=\"status\">Media updated.</span>\n");
7575 html_printf(client, "</td></tr></table></form>\n");
7576
7577 if (num_options > 0)
7578 html_printf(client, "<script>\n"
7579 "setTimeout(hide_status, 3000);\n"
7580 "function hide_status() {\n"
7581 " var status = document.getElementById('status');\n"
7582 " status.style.display = 'none';\n"
7583 "}\n"
7584 "</script>\n");
7585 }
7586 else
7587 html_printf(client, "</table>\n");
7588
9141aa01
MS
7589 html_footer(client);
7590
7591 return (1);
7592}
7593
7594
7595/*
7596 * 'show_status()' - Show printer/system state.
7597 */
7598
7599static int /* O - 1 on success, 0 on failure */
7600show_status(ippeve_client_t *client) /* I - Client connection */
7601{
7602 ippeve_printer_t *printer = client->printer;
7603 /* Printer */
7604 ippeve_job_t *job; /* Current job */
7605 int i; /* Looping var */
7606 ippeve_preason_t reason; /* Current reason */
7607 static const char * const reasons[] = /* Reason strings */
7608 {
7609 "Other",
7610 "Cover Open",
7611 "Input Tray Missing",
7612 "Marker Supply Empty",
7613 "Marker Supply Low",
7614 "Marker Waste Almost Full",
7615 "Marker Waste Full",
7616 "Media Empty",
7617 "Media Jam",
7618 "Media Low",
7619 "Media Needed",
7620 "Moving to Paused",
7621 "Paused",
7622 "Spool Area Full",
7623 "Toner Empty",
7624 "Toner Low"
7625 };
6640ceec
MS
7626 static const char * const state_colors[] =
7627 { /* State colors */
7628 "#0C0", /* Idle */
7629 "#EE0", /* Processing */
7630 "#C00" /* Stopped */
7631 };
9141aa01
MS
7632
7633
7634 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7635 return (0);
7636
6640ceec
MS
7637 html_header(client, printer->name, printer->state == IPP_PSTATE_PROCESSING ? 5 : 15);
7638 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
7639 html_printf(client, "<p>%s, %d job(s).", printer->state == IPP_PSTATE_IDLE ? "Idle" : printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(printer->jobs));
7640 for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
7641 if (printer->state_reasons & reason)
7642 html_printf(client, "\n<br>&nbsp;&nbsp;&nbsp;&nbsp;%s", reasons[i]);
7643 html_printf(client, "</p>\n");
7644
7645 if (cupsArrayCount(printer->jobs) > 0)
7646 {
7647 _cupsRWLockRead(&(printer->rwlock));
7648
7649 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");
7650 for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs); job; job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
7651 {
7652 char when[256], /* When job queued/started/finished */
7653 hhmmss[64]; /* Time HH:MM:SS */
7654
7655 switch (job->state)
7656 {
7657 case IPP_JSTATE_PENDING :
7658 case IPP_JSTATE_HELD :
7659 snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss)));
7660 break;
7661 case IPP_JSTATE_PROCESSING :
7662 case IPP_JSTATE_STOPPED :
7663 snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss)));
7664 break;
7665 case IPP_JSTATE_ABORTED :
7666 snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7667 break;
7668 case IPP_JSTATE_CANCELED :
7669 snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7670 break;
7671 case IPP_JSTATE_COMPLETED :
7672 snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7673 break;
7674 }
7675
7676 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);
7677 }
7678 html_printf(client, "</tbody></table>\n");
7679
7680 _cupsRWUnlock(&(printer->rwlock));
7681 }
7682
7683 html_footer(client);
7684
7685 return (1);
7686}
7687
7688
7689/*
7690 * 'show_supplies()' - Show printer supplies.
7691 */
7692
7693static int /* O - 1 on success, 0 on failure */
7694show_supplies(
7695 ippeve_client_t *client) /* I - Client connection */
7696{
7697 ippeve_printer_t *printer = client->printer;
7698 /* Printer */
7699 int i, /* Looping var */
7700 num_supply; /* Number of supplies */
7701 ipp_attribute_t *supply, /* printer-supply attribute */
7702 *supply_desc; /* printer-supply-description attribute */
d0f469a7
MS
7703 int num_options = 0; /* Number of form options */
7704 cups_option_t *options = NULL; /* Form options */
9141aa01
MS
7705 int supply_len, /* Length of supply value */
7706 level; /* Supply level */
7707 const char *supply_value; /* Supply value */
7708 char supply_text[1024], /* Supply string */
7709 *supply_ptr; /* Pointer into supply string */
7710 static const char * const printer_supply[] =
7711 { /* printer-supply values */
7712 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
7713 "maxcapacity=100;level=%d;colorantname=unknown;",
7714 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
7715 "maxcapacity=100;level=%d;colorantname=black;",
7716 "index=3;class=supplyThatIsConsumed;type=toner;unit=percent;"
7717 "maxcapacity=100;level=%d;colorantname=cyan;",
7718 "index=4;class=supplyThatIsConsumed;type=toner;unit=percent;"
7719 "maxcapacity=100;level=%d;colorantname=magenta;",
7720 "index=5;class=supplyThatIsConsumed;type=toner;unit=percent;"
7721 "maxcapacity=100;level=%d;colorantname=yellow;"
7722 };
820cb58e
MS
7723 static const char * const backgrounds[] =
7724 { /* Background colors for the supply-level bars */
9141aa01
MS
7725 "#777 linear-gradient(#333,#777)",
7726 "#000 linear-gradient(#666,#000)",
7727 "#0FF linear-gradient(#6FF,#0FF)",
7728 "#F0F linear-gradient(#F6F,#F0F)",
7729 "#CC0 linear-gradient(#EE6,#EE0)"
7730 };
820cb58e
MS
7731 static const char * const colors[] = /* Text colors for the supply-level bars */
7732 {
7733 "#fff",
7734 "#fff",
7735 "#000",
7736 "#000",
7737 "#000"
7738 };
9141aa01
MS
7739
7740
7741 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7742 return (0);
7743
6640ceec 7744 html_header(client, printer->name, 0);
9141aa01
MS
7745
7746 if ((supply = ippFindAttribute(printer->attrs, "printer-supply", IPP_TAG_STRING)) == NULL)
7747 {
7748 html_printf(client, "<p>Error: No printer-supply defined for printer.</p>\n");
7749 html_footer(client);
7750 return (1);
7751 }
7752
7753 num_supply = ippGetCount(supply);
7754
7755 if ((supply_desc = ippFindAttribute(printer->attrs, "printer-supply-description", IPP_TAG_TEXT)) == NULL)
7756 {
7757 html_printf(client, "<p>Error: No printer-supply-description defined for printer.</p>\n");
7758 html_footer(client);
7759 return (1);
7760 }
7761
7762 if (num_supply != ippGetCount(supply_desc))
7763 {
7764 html_printf(client, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n");
7765 html_footer(client);
7766 return (1);
7767 }
7768
820cb58e
MS
7769 if (printer->web_forms)
7770 num_options = parse_options(client, &options);
820cb58e
MS
7771
7772 if (num_options > 0)
9141aa01
MS
7773 {
7774 /*
7775 * WARNING: A real printer/server implementation MUST NOT implement
7776 * supply updates via a GET request - GET requests are supposed to be
7777 * idempotent (without side-effects) and we obviously are not
7778 * authenticating access here. This form is provided solely to
7779 * enable testing and development!
7780 */
7781
7782 char name[64]; /* Form field */
7783 const char *val; /* Form value */
7784
7785 _cupsRWLockWrite(&printer->rwlock);
7786
7787 ippDeleteAttribute(printer->attrs, supply);
7788 supply = NULL;
7789
7790 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);
7791
7792 for (i = 0; i < num_supply; i ++)
7793 {
7794 snprintf(name, sizeof(name), "supply%d", i);
7795 if ((val = cupsGetOption(name, num_options, options)) != NULL)
7796 {
7797 level = atoi(val); /* New level */
7798
7799 snprintf(supply_text, sizeof(supply_text), printer_supply[i], level);
7800 if (supply)
7801 ippSetOctetString(printer->attrs, &supply, ippGetCount(supply), supply_text, (int)strlen(supply_text));
7802 else
7803 supply = ippAddOctetString(printer->attrs, IPP_TAG_PRINTER, "printer-supply", supply_text, (int)strlen(supply_text));
7804
7805 if (i == 0)
7806 {
7807 if (level == 100)
7808 printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_FULL;
7809 else if (level > 90)
7810 printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL;
7811 }
7812 else
7813 {
7814 if (level == 0)
7815 printer->state_reasons |= IPPEVE_PREASON_TONER_EMPTY;
7816 else if (level < 10)
7817 printer->state_reasons |= IPPEVE_PREASON_TONER_LOW;
7818 }
7819 }
7820 }
7821
7822 _cupsRWUnlock(&printer->rwlock);
9141aa01
MS
7823 }
7824
820cb58e
MS
7825 if (printer->web_forms)
7826 html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
9141aa01
MS
7827
7828 html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
7829 for (i = 0; i < num_supply; i ++)
7830 {
7831 supply_value = ippGetOctetString(supply, i, &supply_len);
60d8f884
MS
7832 if (supply_len > (int)(sizeof(supply_text) - 1))
7833 supply_len = (int)sizeof(supply_text) - 1;
9141aa01
MS
7834
7835 memcpy(supply_text, supply_value, (size_t)supply_len);
7836 supply_text[supply_len] = '\0';
7837
7838 if ((supply_ptr = strstr(supply_text, "level=")) != NULL)
7839 level = atoi(supply_ptr + 6);
7840 else
7841 level = 50;
7842
820cb58e
MS
7843 if (printer->web_forms)
7844 html_printf(client, "<tr><th>%s:</th><td><input name=\"supply%d\" size=\"3\" value=\"%d\"></td>", ippGetString(supply_desc, i, NULL), i, level);
7845 else
7846 html_printf(client, "<tr><th>%s:</th>", ippGetString(supply_desc, i, NULL));
7847
7848 if (level < 10)
7849 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);
7850 else
7851 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 7852 }
820cb58e
MS
7853
7854 if (printer->web_forms)
7855 {
7856 html_printf(client, "<tr><td></td><td colspan=\"2\"><input type=\"submit\" value=\"Update Supplies\">");
7857 if (num_options > 0)
7858 html_printf(client, " <span class=\"badge\" id=\"status\">Supplies updated.</span>\n");
7859 html_printf(client, "</td></tr>\n</table>\n</form>\n");
7860
7861 if (num_options > 0)
7862 html_printf(client, "<script>\n"
7863 "setTimeout(hide_status, 3000);\n"
7864 "function hide_status() {\n"
7865 " var status = document.getElementById('status');\n"
7866 " status.style.display = 'none';\n"
7867 "}\n"
7868 "</script>\n");
7869 }
7870 else
7871 html_printf(client, "</table>\n");
7872
9141aa01
MS
7873 html_footer(client);
7874
7875 return (1);
7876}
7877
7878
0b5ce83f
MS
7879/*
7880 * 'time_string()' - Return the local time in hours, minutes, and seconds.
7881 */
7882
7883static char *
7884time_string(time_t tv, /* I - Time value */
7885 char *buffer, /* I - Buffer */
7886 size_t bufsize) /* I - Size of buffer */
7887{
f4a99aeb
MS
7888 struct tm date; /* Local time and date */
7889
7890 localtime_r(&tv, &date);
7891
7892 strftime(buffer, bufsize, "%X", &date);
0b5ce83f 7893
0b5ce83f
MS
7894 return (buffer);
7895}
7896
7897
1106b00e
MS
7898/*
7899 * 'usage()' - Show program usage.
7900 */
7901
7902static void
7903usage(int status) /* O - Exit status */
7904{
6d56631f
MS
7905 _cupsLangPuts(stdout, _("Usage: ippeveprinter [options] \"name\""));
7906 _cupsLangPuts(stdout, _("Options:"));
5ea07c61
MS
7907 _cupsLangPuts(stdout, _("--help Show program help"));
7908 _cupsLangPuts(stdout, _("--no-web-forms Disable web forms for media and supplies"));
7909 _cupsLangPuts(stdout, _("--pam-service service Use the named PAM service"));
7910 _cupsLangPuts(stdout, _("--version Show program version"));
6d56631f 7911 _cupsLangPuts(stdout, _("-2 Set 2-sided printing support (default=1-sided)"));
5ea07c61 7912 _cupsLangPuts(stdout, _("-A Enable authentication"));
6d56631f 7913 _cupsLangPuts(stdout, _("-D device-uri Set the device URI for the printer"));
ef4d439c 7914 _cupsLangPuts(stdout, _("-F output-type/subtype Set the output format for the printer"));
6d56631f
MS
7915#ifdef HAVE_SSL
7916 _cupsLangPuts(stdout, _("-K keypath Set location of server X.509 certificates and keys."));
7917#endif /* HAVE_SSL */
7918 _cupsLangPuts(stdout, _("-M manufacturer Set manufacturer name (default=Test)"));
7919 _cupsLangPuts(stdout, _("-P filename.ppd Load printer attributes from PPD file"));
7920 _cupsLangPuts(stdout, _("-V version Set default IPP version"));
7921 _cupsLangPuts(stdout, _("-a filename.conf Load printer attributes from conf file"));
7922 _cupsLangPuts(stdout, _("-c command Set print command"));
7923 _cupsLangPuts(stdout, _("-d spool-directory Set spool directory"));
7924 _cupsLangPuts(stdout, _("-f type/subtype[,...] Set supported file types"));
7925 _cupsLangPuts(stdout, _("-i iconfile.png Set icon file"));
7926 _cupsLangPuts(stdout, _("-k Keep job spool files"));
7927 _cupsLangPuts(stdout, _("-l location Set location of printer"));
7928 _cupsLangPuts(stdout, _("-m model Set model name (default=Printer)"));
7929 _cupsLangPuts(stdout, _("-n hostname Set hostname for printer"));
7930 _cupsLangPuts(stdout, _("-p port Set port number for printer"));
dc84a5a4 7931 _cupsLangPuts(stdout, _("-r subtype,[subtype] Set DNS-SD service subtype"));
6d56631f 7932 _cupsLangPuts(stdout, _("-s speed[,color-speed] Set speed in pages per minute"));
5ea07c61 7933 _cupsLangPuts(stdout, _("-v Be verbose"));
1106b00e
MS
7934
7935 exit(status);
7936}
7937
7938
7939/*
83e08001
MS
7940 * 'valid_doc_attributes()' - Determine whether the document attributes are
7941 * valid.
1106b00e 7942 *
83e08001
MS
7943 * When one or more document attributes are invalid, this function adds a
7944 * suitable response and attributes to the unsupported group.
1106b00e
MS
7945 */
7946
7947static int /* O - 1 if valid, 0 if not */
83e08001 7948valid_doc_attributes(
d46dbe1b 7949 ippeve_client_t *client) /* I - Client */
1106b00e 7950{
a469f8a5
MS
7951 int valid = 1; /* Valid attributes? */
7952 ipp_op_t op = ippGetOperation(client->request);
7953 /* IPP operation */
7954 const char *op_name = ippOpString(op);
7955 /* IPP operation name */
1106b00e 7956 ipp_attribute_t *attr, /* Current attribute */
a469f8a5
MS
7957 *supported; /* xxx-supported attribute */
7958 const char *compression = NULL,
7959 /* compression value */
7960 *format = NULL; /* document-format value */
1106b00e
MS
7961
7962
7963 /*
7964 * Check operation attributes...
7965 */
7966
404dde30 7967 if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
1106b00e
MS
7968 {
7969 /*
a469f8a5
MS
7970 * If compression is specified, only accept a supported value in a Print-Job
7971 * or Send-Document request...
1106b00e
MS
7972 */
7973
a469f8a5
MS
7974 compression = ippGetString(attr, 0, NULL);
7975 supported = ippFindAttribute(client->printer->attrs,
7976 "compression-supported", IPP_TAG_KEYWORD);
7977
7978 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
7979 ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
db8b865d
MS
7980 (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
7981 op != IPP_OP_VALIDATE_JOB) ||
a469f8a5
MS
7982 !ippContainsString(supported, compression))
7983 {
1106b00e 7984 respond_unsupported(client, attr);
a469f8a5
MS
7985 valid = 0;
7986 }
1106b00e 7987 else
a469f8a5 7988 {
404dde30
MS
7989 fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression);
7990
7991 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression);
a469f8a5
MS
7992
7993 if (strcmp(compression, "none"))
a654c79d
MS
7994 {
7995 if (Verbosity)
7996 fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression);
a469f8a5 7997 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
a654c79d 7998 }
a469f8a5 7999 }
1106b00e
MS
8000 }
8001
8002 /*
8003 * Is it a format we support?
8004 */
8005
404dde30 8006 if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
1106b00e 8007 {
a469f8a5
MS
8008 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
8009 ippGetGroupTag(attr) != IPP_TAG_OPERATION)
8010 {
1106b00e 8011 respond_unsupported(client, attr);
a469f8a5
MS
8012 valid = 0;
8013 }
1106b00e 8014 else
e60ec91f 8015 {
a469f8a5 8016 format = ippGetString(attr, 0, NULL);
e60ec91f 8017
6d56631f 8018 fprintf(stderr, "%s %s document-format=\"%s\"\n", client->hostname, op_name, format);
4a838088
MS
8019
8020 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format);
e60ec91f 8021 }
1106b00e
MS
8022 }
8023 else
a469f8a5 8024 {
404dde30 8025 format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL);
8a78aa37
MS
8026 if (!format)
8027 format = "application/octet-stream"; /* Should never happen */
8028
4a838088 8029 attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
a469f8a5 8030 }
1106b00e 8031
7f500d89 8032 if (format && !strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
1106b00e
MS
8033 {
8034 /*
404dde30 8035 * Auto-type the file using the first 8 bytes of the file...
1106b00e
MS
8036 */
8037
404dde30 8038 unsigned char header[8]; /* First 8 bytes of file */
1106b00e
MS
8039
8040 memset(header, 0, sizeof(header));
a469f8a5 8041 httpPeek(client->http, (char *)header, sizeof(header));
1106b00e
MS
8042
8043 if (!memcmp(header, "%PDF", 4))
8044 format = "application/pdf";
8045 else if (!memcmp(header, "%!", 2))
8046 format = "application/postscript";
404dde30 8047 else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
1106b00e
MS
8048 format = "image/jpeg";
8049 else if (!memcmp(header, "\211PNG", 4))
8050 format = "image/png";
404dde30
MS
8051 else if (!memcmp(header, "RAS2", 4))
8052 format = "image/pwg-raster";
8053 else if (!memcmp(header, "UNIRAST", 8))
8054 format = "image/urf";
8055 else
8056 format = NULL;
1106b00e
MS
8057
8058 if (format)
404dde30 8059 {
6d56631f 8060 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n", client->hostname, op_name, format);
1106b00e 8061
404dde30
MS
8062 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
8063 }
1106b00e
MS
8064 }
8065
404dde30 8066 if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
1106b00e 8067 {
a469f8a5
MS
8068 respond_unsupported(client, attr);
8069 valid = 0;
1106b00e
MS
8070 }
8071
404dde30
MS
8072 /*
8073 * document-name
8074 */
8075
8076 if ((attr = ippFindAttribute(client->request, "document-name", IPP_TAG_NAME)) != NULL)
8077 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
8078
a469f8a5 8079 return (valid);
83e08001
MS
8080}
8081
8082
8083/*
8084 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
8085 *
8086 * When one or more job attributes are invalid, this function adds a suitable
8087 * response and attributes to the unsupported group.
8088 */
8089
8090static int /* O - 1 if valid, 0 if not */
8091valid_job_attributes(
d46dbe1b 8092 ippeve_client_t *client) /* I - Client */
83e08001 8093{
a469f8a5 8094 int i, /* Looping var */
63efa616 8095 count, /* Number of values */
a469f8a5 8096 valid = 1; /* Valid attributes? */
83e08001
MS
8097 ipp_attribute_t *attr, /* Current attribute */
8098 *supported; /* xxx-supported attribute */
8099
8100
8101 /*
8102 * Check operation attributes...
8103 */
8104
a469f8a5 8105 valid = valid_doc_attributes(client);
83e08001 8106
1106b00e
MS
8107 /*
8108 * Check the various job template attributes...
8109 */
8110
404dde30 8111 if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL)
1106b00e 8112 {
a469f8a5
MS
8113 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
8114 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
1106b00e
MS
8115 {
8116 respond_unsupported(client, attr);
a469f8a5 8117 valid = 0;
1106b00e
MS
8118 }
8119 }
8120
404dde30 8121 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
1106b00e 8122 {
a469f8a5 8123 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
1106b00e
MS
8124 {
8125 respond_unsupported(client, attr);
a469f8a5 8126 valid = 0;
1106b00e
MS
8127 }
8128 }
8129
404dde30 8130 if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
1106b00e 8131 {
a469f8a5
MS
8132 if (ippGetCount(attr) != 1 ||
8133 (ippGetValueTag(attr) != IPP_TAG_NAME &&
8134 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8135 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
8136 strcmp(ippGetString(attr, 0, NULL), "no-hold"))
1106b00e
MS
8137 {
8138 respond_unsupported(client, attr);
a469f8a5 8139 valid = 0;
1106b00e
MS
8140 }
8141 }
8142
404dde30
MS
8143 if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL)
8144 {
8145 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0)
8146 {
8147 respond_unsupported(client, attr);
8148 valid = 0;
8149 }
8150 }
8151
8152 if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL)
1106b00e 8153 {
a469f8a5
MS
8154 if (ippGetCount(attr) != 1 ||
8155 (ippGetValueTag(attr) != IPP_TAG_NAME &&
8156 ippGetValueTag(attr) != IPP_TAG_NAMELANG))
1106b00e
MS
8157 {
8158 respond_unsupported(client, attr);
a469f8a5 8159 valid = 0;
1106b00e 8160 }
404dde30
MS
8161
8162 ippSetGroupTag(client->request, &attr, IPP_TAG_JOB);
1106b00e 8163 }
404dde30
MS
8164 else
8165 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
1106b00e 8166
404dde30 8167 if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL)
1106b00e 8168 {
a469f8a5
MS
8169 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
8170 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
1106b00e
MS
8171 {
8172 respond_unsupported(client, attr);
a469f8a5 8173 valid = 0;
1106b00e
MS
8174 }
8175 }
8176
404dde30 8177 if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
1106b00e 8178 {
a469f8a5
MS
8179 if (ippGetCount(attr) != 1 ||
8180 (ippGetValueTag(attr) != IPP_TAG_NAME &&
8181 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8182 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
8183 strcmp(ippGetString(attr, 0, NULL), "none"))
1106b00e
MS
8184 {
8185 respond_unsupported(client, attr);
a469f8a5 8186 valid = 0;
1106b00e
MS
8187 }
8188 }
8189
404dde30 8190 if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
1106b00e 8191 {
a469f8a5
MS
8192 if (ippGetCount(attr) != 1 ||
8193 (ippGetValueTag(attr) != IPP_TAG_NAME &&
8194 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8195 ippGetValueTag(attr) != IPP_TAG_KEYWORD))
1106b00e
MS
8196 {
8197 respond_unsupported(client, attr);
a469f8a5 8198 valid = 0;
1106b00e
MS
8199 }
8200 else
8201 {
63efa616 8202 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
1106b00e 8203
63efa616 8204 if (!ippContainsString(supported, ippGetString(attr, 0, NULL)))
1106b00e
MS
8205 {
8206 respond_unsupported(client, attr);
a469f8a5 8207 valid = 0;
1106b00e
MS
8208 }
8209 }
8210 }
8211
404dde30 8212 if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL)
1106b00e 8213 {
63efa616
MS
8214 ipp_t *col, /* media-col collection */
8215 *size; /* media-size collection */
8216 ipp_attribute_t *member, /* Member attribute */
8217 *x_dim, /* x-dimension */
8218 *y_dim; /* y-dimension */
8219 int x_value, /* y-dimension value */
8220 y_value; /* x-dimension value */
8221
a469f8a5
MS
8222 if (ippGetCount(attr) != 1 ||
8223 ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
1106b00e
MS
8224 {
8225 respond_unsupported(client, attr);
a469f8a5 8226 valid = 0;
1106b00e 8227 }
63efa616
MS
8228
8229 col = ippGetCollection(attr, 0);
8230
8231 if ((member = ippFindAttribute(col, "media-size-name", IPP_TAG_ZERO)) != NULL)
8232 {
8233 if (ippGetCount(member) != 1 ||
8234 (ippGetValueTag(member) != IPP_TAG_NAME &&
8235 ippGetValueTag(member) != IPP_TAG_NAMELANG &&
8236 ippGetValueTag(member) != IPP_TAG_KEYWORD))
8237 {
8238 respond_unsupported(client, attr);
8239 valid = 0;
8240 }
8241 else
8242 {
8243 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
8244
8245 if (!ippContainsString(supported, ippGetString(member, 0, NULL)))
8246 {
8247 respond_unsupported(client, attr);
8248 valid = 0;
8249 }
8250 }
8251 }
8252 else if ((member = ippFindAttribute(col, "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
8253 {
8254 if (ippGetCount(member) != 1)
8255 {
8256 respond_unsupported(client, attr);
8257 valid = 0;
8258 }
8259 else
8260 {
8261 size = ippGetCollection(member, 0);
8262
8263 if ((x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(x_dim) != 1 ||
8264 (y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(y_dim) != 1)
8265 {
8266 respond_unsupported(client, attr);
8267 valid = 0;
8268 }
8269 else
8270 {
8271 x_value = ippGetInteger(x_dim, 0);
8272 y_value = ippGetInteger(y_dim, 0);
8273 supported = ippFindAttribute(client->printer->attrs, "media-size-supported", IPP_TAG_BEGIN_COLLECTION);
8274 count = ippGetCount(supported);
8275
8276 for (i = 0; i < count ; i ++)
8277 {
8278 size = ippGetCollection(supported, i);
8279 x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_ZERO);
8280 y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_ZERO);
8281
8282 if (ippContainsInteger(x_dim, x_value) && ippContainsInteger(y_dim, y_value))
8283 break;
8284 }
8285
8286 if (i >= count)
8287 {
8288 respond_unsupported(client, attr);
8289 valid = 0;
8290 }
8291 }
8292 }
8293 }
1106b00e
MS
8294 }
8295
404dde30 8296 if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL)
1106b00e 8297 {
a469f8a5
MS
8298 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
8299 (strcmp(ippGetString(attr, 0, NULL),
1106b00e 8300 "separate-documents-uncollated-copies") &&
a469f8a5 8301 strcmp(ippGetString(attr, 0, NULL),
1106b00e
MS
8302 "separate-documents-collated-copies")))
8303 {
8304 respond_unsupported(client, attr);
a469f8a5 8305 valid = 0;
1106b00e
MS
8306 }
8307 }
8308
404dde30 8309 if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL)
1106b00e 8310 {
a469f8a5
MS
8311 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
8312 ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
8313 ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
1106b00e
MS
8314 {
8315 respond_unsupported(client, attr);
a469f8a5 8316 valid = 0;
1106b00e
MS
8317 }
8318 }
8319
404dde30 8320 if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
1106b00e 8321 {
c2c30ebc
MS
8322 if (ippGetValueTag(attr) != IPP_TAG_RANGE)
8323 {
8324 respond_unsupported(client, attr);
a469f8a5 8325 valid = 0;
c2c30ebc 8326 }
1106b00e
MS
8327 }
8328
404dde30 8329 if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL)
1106b00e 8330 {
a469f8a5
MS
8331 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
8332 ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
8333 ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
1106b00e
MS
8334 {
8335 respond_unsupported(client, attr);
a469f8a5 8336 valid = 0;
1106b00e
MS
8337 }
8338 }
8339
404dde30 8340 if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
1106b00e 8341 {
4a838088 8342 supported = ippFindAttribute(client->printer->attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION);
c2c30ebc
MS
8343
8344 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION ||
8345 !supported)
8346 {
8347 respond_unsupported(client, attr);
8348 valid = 0;
8349 }
8350 else
8351 {
63efa616 8352 int xdpi, /* Horizontal resolution for job template attribute */
4a838088
MS
8353 ydpi, /* Vertical resolution for job template attribute */
8354 sydpi; /* Vertical resolution for supported value */
8355 ipp_res_t units, /* Units for job template attribute */
8356 sunits; /* Units for supported value */
c2c30ebc
MS
8357
8358 xdpi = ippGetResolution(attr, 0, &ydpi, &units);
8359 count = ippGetCount(supported);
8360
8361 for (i = 0; i < count; i ++)
8362 {
8363 if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits)
8364 break;
8365 }
8366
8367 if (i >= count)
8368 {
8369 respond_unsupported(client, attr);
8370 valid = 0;
8371 }
8372 }
1106b00e
MS
8373 }
8374
404dde30 8375 if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
1106b00e 8376 {
404dde30
MS
8377 const char *sides = ippGetString(attr, 0, NULL);
8378 /* "sides" value... */
ad29aeab 8379
a469f8a5 8380 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
1106b00e
MS
8381 {
8382 respond_unsupported(client, attr);
a469f8a5 8383 valid = 0;
1106b00e 8384 }
404dde30 8385 else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
1106b00e 8386 {
ad29aeab 8387 if (!ippContainsString(supported, sides))
1106b00e
MS
8388 {
8389 respond_unsupported(client, attr);
a469f8a5 8390 valid = 0;
1106b00e
MS
8391 }
8392 }
ad29aeab 8393 else if (strcmp(sides, "one-sided"))
1106b00e
MS
8394 {
8395 respond_unsupported(client, attr);
a469f8a5 8396 valid = 0;
1106b00e
MS
8397 }
8398 }
8399
a469f8a5 8400 return (valid);
1106b00e 8401}