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