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