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