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