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