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