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