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