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