]> git.ipfire.org Git - thirdparty/cups.git/blame - test/ippserver.c
Full sweep of all Clang warnings, plus some bug fixes for incorrect memcpy usage.
[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
2682 if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
2683 {
a469f8a5 2684 job->state = IPP_JSTATE_ABORTED;
1106b00e 2685
a469f8a5 2686 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
1106b00e
MS
2687 "Unable to create print file: %s", strerror(errno));
2688 return;
2689 }
2690
a469f8a5 2691 while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
1106b00e 2692 {
7e86f2f6 2693 if (write(job->fd, buffer, (size_t)bytes) < bytes)
1106b00e
MS
2694 {
2695 int error = errno; /* Write error */
2696
a469f8a5 2697 job->state = IPP_JSTATE_ABORTED;
1106b00e
MS
2698
2699 close(job->fd);
2700 job->fd = -1;
2701
2702 unlink(filename);
2703
a469f8a5 2704 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
1106b00e
MS
2705 "Unable to write print file: %s", strerror(error));
2706 return;
2707 }
2708 }
2709
2710 if (bytes < 0)
2711 {
2712 /*
2713 * Got an error while reading the print data, so abort this job.
2714 */
2715
a469f8a5 2716 job->state = IPP_JSTATE_ABORTED;
1106b00e
MS
2717
2718 close(job->fd);
2719 job->fd = -1;
2720
2721 unlink(filename);
2722
a469f8a5
MS
2723 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
2724 "Unable to read print file.");
1106b00e
MS
2725 return;
2726 }
2727
2728 if (close(job->fd))
2729 {
2730 int error = errno; /* Write error */
2731
a469f8a5 2732 job->state = IPP_JSTATE_ABORTED;
1106b00e
MS
2733 job->fd = -1;
2734
2735 unlink(filename);
2736
a469f8a5
MS
2737 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
2738 "Unable to write print file: %s", strerror(error));
1106b00e
MS
2739 return;
2740 }
2741
2742 job->fd = -1;
2743 job->filename = strdup(filename);
a469f8a5 2744 job->state = IPP_JSTATE_PENDING;
1106b00e
MS
2745
2746 /*
2747 * Process the job...
2748 */
2749
83e08001 2750#if 0
1106b00e
MS
2751 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
2752 {
a469f8a5
MS
2753 job->state = IPP_JSTATE_ABORTED;
2754 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
1106b00e
MS
2755 return;
2756 }
2757
83e08001
MS
2758#else
2759 process_job(job);
2760#endif /* 0 */
2761
1106b00e
MS
2762 /*
2763 * Return the job info...
2764 */
2765
a469f8a5 2766 respond_ipp(client, IPP_STATUS_OK, NULL);
1106b00e
MS
2767
2768 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2769 cupsArrayAdd(ra, "job-id");
2770 cupsArrayAdd(ra, "job-state");
2771 cupsArrayAdd(ra, "job-state-reasons");
2772 cupsArrayAdd(ra, "job-uri");
2773
2774 copy_job_attributes(client, job, ra);
e60ec91f 2775 cupsArrayDelete(ra);
1106b00e
MS
2776}
2777
2778
1106b00e 2779/*
83e08001 2780 * 'ipp_print_uri()' - Create a job object with a referenced document.
1106b00e
MS
2781 */
2782
2783static void
83e08001 2784ipp_print_uri(_ipp_client_t *client) /* I - Client */
1106b00e 2785{
83e08001
MS
2786 _ipp_job_t *job; /* New job */
2787 ipp_attribute_t *uri; /* document-uri */
2788 char scheme[256], /* URI scheme */
2789 userpass[256], /* Username and password info */
2790 hostname[256], /* Hostname */
2791 resource[1024]; /* Resource path */
2792 int port; /* Port number */
2793 http_uri_status_t uri_status; /* URI decode status */
2794 http_encryption_t encryption; /* Encryption to use, if any */
2795 http_t *http; /* Connection for http/https URIs */
2796 http_status_t status; /* Access status for http/https URIs */
2797 int infile; /* Input file for local file URIs */
2798 char filename[1024], /* Filename buffer */
2799 buffer[4096]; /* Copy buffer */
2800 ssize_t bytes; /* Bytes read */
2801 cups_array_t *ra; /* Attributes to send in response */
2802 static const char * const uri_status_strings[] =
2803 { /* URI decode errors */
2804 "URI too large.",
2805 "Bad arguments to function.",
2806 "Bad resource in URI.",
2807 "Bad port number in URI.",
2808 "Bad hostname in URI.",
2809 "Bad username in URI.",
2810 "Bad scheme in URI.",
2811 "Bad/empty URI."
2812 };
1106b00e 2813
1106b00e 2814
83e08001
MS
2815 /*
2816 * Validate print job attributes...
2817 */
1106b00e 2818
83e08001
MS
2819 if (!valid_job_attributes(client))
2820 {
a469f8a5 2821 httpFlush(client->http);
83e08001
MS
2822 return;
2823 }
1106b00e 2824
1106b00e 2825 /*
83e08001 2826 * Do we have a file to print?
1106b00e
MS
2827 */
2828
a469f8a5 2829 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
83e08001 2830 {
a469f8a5 2831 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
83e08001
MS
2832 "Unexpected document data following request.");
2833 return;
2834 }
1106b00e
MS
2835
2836 /*
83e08001 2837 * Do we have a document URI?
1106b00e
MS
2838 */
2839
83e08001
MS
2840 if ((uri = ippFindAttribute(client->request, "document-uri",
2841 IPP_TAG_URI)) == NULL)
2842 {
a469f8a5 2843 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
83e08001
MS
2844 return;
2845 }
1106b00e 2846
a469f8a5 2847 if (ippGetCount(uri) != 1)
83e08001 2848 {
a469f8a5
MS
2849 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
2850 "Too many document-uri values.");
83e08001
MS
2851 return;
2852 }
1106b00e 2853
a469f8a5 2854 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
83e08001
MS
2855 scheme, sizeof(scheme), userpass,
2856 sizeof(userpass), hostname, sizeof(hostname),
2857 &port, resource, sizeof(resource));
a469f8a5 2858 if (uri_status < HTTP_URI_STATUS_OK)
83e08001 2859 {
a469f8a5
MS
2860 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s",
2861 uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]);
83e08001
MS
2862 return;
2863 }
1106b00e 2864
83e08001
MS
2865 if (strcmp(scheme, "file") &&
2866#ifdef HAVE_SSL
2867 strcmp(scheme, "https") &&
2868#endif /* HAVE_SSL */
2869 strcmp(scheme, "http"))
2870 {
a469f8a5
MS
2871 respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME,
2872 "URI scheme \"%s\" not supported.", scheme);
83e08001
MS
2873 return;
2874 }
1106b00e 2875
83e08001
MS
2876 if (!strcmp(scheme, "file") && access(resource, R_OK))
2877 {
a469f8a5
MS
2878 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
2879 "Unable to access URI: %s", strerror(errno));
83e08001
MS
2880 return;
2881 }
1106b00e
MS
2882
2883 /*
83e08001 2884 * Print the job...
1106b00e
MS
2885 */
2886
83e08001
MS
2887 if ((job = create_job(client)) == NULL)
2888 {
a469f8a5
MS
2889 respond_ipp(client, IPP_STATUS_ERROR_BUSY,
2890 "Currently printing another job.");
83e08001
MS
2891 return;
2892 }
1106b00e
MS
2893
2894 /*
83e08001 2895 * Create a file for the request data...
1106b00e
MS
2896 */
2897
83e08001
MS
2898 if (!_cups_strcasecmp(job->format, "image/jpeg"))
2899 snprintf(filename, sizeof(filename), "%s/%d.jpg",
2900 client->printer->directory, job->id);
2901 else if (!_cups_strcasecmp(job->format, "image/png"))
2902 snprintf(filename, sizeof(filename), "%s/%d.png",
2903 client->printer->directory, job->id);
2904 else if (!_cups_strcasecmp(job->format, "application/pdf"))
2905 snprintf(filename, sizeof(filename), "%s/%d.pdf",
2906 client->printer->directory, job->id);
2907 else if (!_cups_strcasecmp(job->format, "application/postscript"))
2908 snprintf(filename, sizeof(filename), "%s/%d.ps",
2909 client->printer->directory, job->id);
2910 else
2911 snprintf(filename, sizeof(filename), "%s/%d.prn",
2912 client->printer->directory, job->id);
1106b00e 2913
83e08001
MS
2914 if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
2915 {
a469f8a5 2916 job->state = IPP_JSTATE_ABORTED;
1106b00e 2917
a469f8a5 2918 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
83e08001
MS
2919 "Unable to create print file: %s", strerror(errno));
2920 return;
2921 }
1106b00e 2922
83e08001
MS
2923 if (!strcmp(scheme, "file"))
2924 {
2925 if ((infile = open(resource, O_RDONLY)) < 0)
2926 {
a469f8a5
MS
2927 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
2928 "Unable to access URI: %s", strerror(errno));
83e08001
MS
2929 return;
2930 }
1106b00e 2931
83e08001
MS
2932 do
2933 {
2934 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
2935 (errno == EAGAIN || errno == EINTR))
2936 bytes = 1;
7e86f2f6 2937 else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes)
83e08001
MS
2938 {
2939 int error = errno; /* Write error */
1106b00e 2940
a469f8a5 2941 job->state = IPP_JSTATE_ABORTED;
1106b00e 2942
83e08001
MS
2943 close(job->fd);
2944 job->fd = -1;
1106b00e 2945
83e08001
MS
2946 unlink(filename);
2947 close(infile);
1106b00e 2948
a469f8a5 2949 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
83e08001
MS
2950 "Unable to write print file: %s", strerror(error));
2951 return;
2952 }
2953 }
2954 while (bytes > 0);
1106b00e 2955
83e08001 2956 close(infile);
1106b00e 2957 }
83e08001 2958 else
1106b00e 2959 {
83e08001
MS
2960#ifdef HAVE_SSL
2961 if (port == 443 || !strcmp(scheme, "https"))
a469f8a5 2962 encryption = HTTP_ENCRYPTION_ALWAYS;
83e08001
MS
2963 else
2964#endif /* HAVE_SSL */
a469f8a5 2965 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
1106b00e 2966
a469f8a5
MS
2967 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption,
2968 1, 30000, NULL)) == NULL)
1106b00e 2969 {
a469f8a5 2970 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
83e08001
MS
2971 "Unable to connect to %s: %s", hostname,
2972 cupsLastErrorString());
a469f8a5 2973 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
2974
2975 close(job->fd);
2976 job->fd = -1;
2977
2978 unlink(filename);
2979 return;
2980 }
2981
2982 httpClearFields(http);
2983 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
2984 if (httpGet(http, resource))
2985 {
a469f8a5
MS
2986 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
2987 "Unable to GET URI: %s", strerror(errno));
83e08001 2988
a469f8a5 2989 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
2990
2991 close(job->fd);
2992 job->fd = -1;
2993
2994 unlink(filename);
2995 httpClose(http);
2996 return;
2997 }
2998
a469f8a5 2999 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
83e08001 3000
a469f8a5 3001 if (status != HTTP_STATUS_OK)
83e08001 3002 {
a469f8a5
MS
3003 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3004 "Unable to GET URI: %s", httpStatus(status));
83e08001 3005
a469f8a5 3006 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3007
3008 close(job->fd);
3009 job->fd = -1;
3010
3011 unlink(filename);
3012 httpClose(http);
3013 return;
3014 }
3015
3016 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
3017 {
7e86f2f6 3018 if (write(job->fd, buffer, (size_t)bytes) < bytes)
83e08001
MS
3019 {
3020 int error = errno; /* Write error */
3021
a469f8a5 3022 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3023
3024 close(job->fd);
3025 job->fd = -1;
3026
3027 unlink(filename);
3028 httpClose(http);
3029
a469f8a5 3030 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
83e08001
MS
3031 "Unable to write print file: %s", strerror(error));
3032 return;
3033 }
3034 }
3035
3036 httpClose(http);
3037 }
3038
3039 if (close(job->fd))
3040 {
3041 int error = errno; /* Write error */
3042
a469f8a5 3043 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3044 job->fd = -1;
3045
3046 unlink(filename);
3047
a469f8a5
MS
3048 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3049 "Unable to write print file: %s", strerror(error));
83e08001
MS
3050 return;
3051 }
3052
3053 job->fd = -1;
3054 job->filename = strdup(filename);
a469f8a5 3055 job->state = IPP_JSTATE_PENDING;
83e08001
MS
3056
3057 /*
3058 * Process the job...
3059 */
3060
3061#if 0
3062 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3063 {
a469f8a5
MS
3064 job->state = IPP_JSTATE_ABORTED;
3065 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
83e08001
MS
3066 return;
3067 }
3068
3069#else
3070 process_job(job);
3071#endif /* 0 */
3072
3073 /*
3074 * Return the job info...
3075 */
3076
a469f8a5 3077 respond_ipp(client, IPP_STATUS_OK, NULL);
83e08001
MS
3078
3079 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3080 cupsArrayAdd(ra, "job-id");
3081 cupsArrayAdd(ra, "job-state");
3082 cupsArrayAdd(ra, "job-state-reasons");
3083 cupsArrayAdd(ra, "job-uri");
3084
3085 copy_job_attributes(client, job, ra);
3086 cupsArrayDelete(ra);
3087}
3088
3089
3090/*
3091 * 'ipp_send_document()' - Add an attached document to a job object created with
3092 * Create-Job.
3093 */
3094
3095static void
3096ipp_send_document(_ipp_client_t *client)/* I - Client */
3097{
3098 _ipp_job_t *job; /* Job information */
3099 char filename[1024], /* Filename buffer */
3100 buffer[4096]; /* Copy buffer */
3101 ssize_t bytes; /* Bytes read */
3102 ipp_attribute_t *attr; /* Current attribute */
3103 cups_array_t *ra; /* Attributes to send in response */
3104
3105
3106 /*
3107 * Get the job...
3108 */
3109
3110 if ((job = find_job(client)) == NULL)
3111 {
a469f8a5
MS
3112 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3113 httpFlush(client->http);
83e08001
MS
3114 return;
3115 }
3116
3117 /*
3118 * See if we already have a document for this job or the job has already
3119 * in a non-pending state...
3120 */
3121
a469f8a5 3122 if (job->state > IPP_JSTATE_HELD)
83e08001 3123 {
a469f8a5
MS
3124 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3125 "Job is not in a pending state.");
3126 httpFlush(client->http);
83e08001
MS
3127 return;
3128 }
3129 else if (job->filename || job->fd >= 0)
3130 {
a469f8a5 3131 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED,
83e08001 3132 "Multiple document jobs are not supported.");
a469f8a5 3133 httpFlush(client->http);
83e08001
MS
3134 return;
3135 }
3136
3137 if ((attr = ippFindAttribute(client->request, "last-document",
3138 IPP_TAG_ZERO)) == NULL)
3139 {
a469f8a5 3140 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
83e08001 3141 "Missing required last-document attribute.");
a469f8a5 3142 httpFlush(client->http);
83e08001
MS
3143 return;
3144 }
a469f8a5
MS
3145 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 ||
3146 !ippGetBoolean(attr, 0))
83e08001
MS
3147 {
3148 respond_unsupported(client, attr);
a469f8a5 3149 httpFlush(client->http);
83e08001
MS
3150 return;
3151 }
3152
3153 /*
3154 * Validate document attributes...
3155 */
3156
3157 if (!valid_doc_attributes(client))
3158 {
a469f8a5 3159 httpFlush(client->http);
83e08001
MS
3160 return;
3161 }
3162
3163 /*
3164 * Get the document format for the job...
3165 */
3166
3167 _cupsRWLockWrite(&(client->printer->rwlock));
3168
3169 if ((attr = ippFindAttribute(job->attrs, "document-format",
3170 IPP_TAG_MIMETYPE)) != NULL)
a469f8a5 3171 job->format = ippGetString(attr, 0, NULL);
83e08001
MS
3172 else
3173 job->format = "application/octet-stream";
3174
3175 /*
3176 * Create a file for the request data...
3177 */
3178
3179 if (!_cups_strcasecmp(job->format, "image/jpeg"))
3180 snprintf(filename, sizeof(filename), "%s/%d.jpg",
3181 client->printer->directory, job->id);
3182 else if (!_cups_strcasecmp(job->format, "image/png"))
3183 snprintf(filename, sizeof(filename), "%s/%d.png",
3184 client->printer->directory, job->id);
3185 else if (!_cups_strcasecmp(job->format, "application/pdf"))
3186 snprintf(filename, sizeof(filename), "%s/%d.pdf",
3187 client->printer->directory, job->id);
3188 else if (!_cups_strcasecmp(job->format, "application/postscript"))
3189 snprintf(filename, sizeof(filename), "%s/%d.ps",
3190 client->printer->directory, job->id);
3191 else
3192 snprintf(filename, sizeof(filename), "%s/%d.prn",
3193 client->printer->directory, job->id);
3194
3195 job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
3196
3197 _cupsRWUnlock(&(client->printer->rwlock));
3198
3199 if (job->fd < 0)
3200 {
a469f8a5 3201 job->state = IPP_JSTATE_ABORTED;
83e08001 3202
a469f8a5 3203 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
83e08001
MS
3204 "Unable to create print file: %s", strerror(errno));
3205 return;
3206 }
3207
a469f8a5 3208 while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
83e08001 3209 {
7e86f2f6 3210 if (write(job->fd, buffer, (size_t)bytes) < bytes)
83e08001
MS
3211 {
3212 int error = errno; /* Write error */
3213
a469f8a5 3214 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3215
3216 close(job->fd);
3217 job->fd = -1;
3218
3219 unlink(filename);
3220
a469f8a5 3221 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
83e08001
MS
3222 "Unable to write print file: %s", strerror(error));
3223 return;
3224 }
3225 }
3226
3227 if (bytes < 0)
3228 {
3229 /*
3230 * Got an error while reading the print data, so abort this job.
3231 */
3232
a469f8a5 3233 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3234
3235 close(job->fd);
3236 job->fd = -1;
3237
3238 unlink(filename);
3239
a469f8a5
MS
3240 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3241 "Unable to read print file.");
83e08001
MS
3242 return;
3243 }
3244
3245 if (close(job->fd))
3246 {
dcb445bc 3247 int error = errno; /* Write error */
83e08001 3248
a469f8a5 3249 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3250 job->fd = -1;
3251
3252 unlink(filename);
3253
a469f8a5
MS
3254 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3255 "Unable to write print file: %s", strerror(error));
83e08001
MS
3256 return;
3257 }
3258
3259 _cupsRWLockWrite(&(client->printer->rwlock));
3260
3261 job->fd = -1;
3262 job->filename = strdup(filename);
a469f8a5 3263 job->state = IPP_JSTATE_PENDING;
83e08001
MS
3264
3265 _cupsRWUnlock(&(client->printer->rwlock));
3266
3267 /*
3268 * Process the job...
3269 */
3270
3271#if 0
3272 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3273 {
a469f8a5
MS
3274 job->state = IPP_JSTATE_ABORTED;
3275 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
83e08001
MS
3276 return;
3277 }
3278
3279#else
3280 process_job(job);
3281#endif /* 0 */
3282
3283 /*
3284 * Return the job info...
3285 */
3286
a469f8a5 3287 respond_ipp(client, IPP_STATUS_OK, NULL);
83e08001
MS
3288
3289 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3290 cupsArrayAdd(ra, "job-id");
3291 cupsArrayAdd(ra, "job-state");
3292 cupsArrayAdd(ra, "job-state-reasons");
3293 cupsArrayAdd(ra, "job-uri");
3294
3295 copy_job_attributes(client, job, ra);
3296 cupsArrayDelete(ra);
3297}
3298
3299
3300/*
3301 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3302 * Create-Job.
3303 */
3304
3305static void
3306ipp_send_uri(_ipp_client_t *client) /* I - Client */
3307{
3308 _ipp_job_t *job; /* Job information */
3309 ipp_attribute_t *uri; /* document-uri */
3310 char scheme[256], /* URI scheme */
3311 userpass[256], /* Username and password info */
3312 hostname[256], /* Hostname */
3313 resource[1024]; /* Resource path */
3314 int port; /* Port number */
3315 http_uri_status_t uri_status; /* URI decode status */
3316 http_encryption_t encryption; /* Encryption to use, if any */
3317 http_t *http; /* Connection for http/https URIs */
3318 http_status_t status; /* Access status for http/https URIs */
3319 int infile; /* Input file for local file URIs */
3320 char filename[1024], /* Filename buffer */
3321 buffer[4096]; /* Copy buffer */
3322 ssize_t bytes; /* Bytes read */
3323 ipp_attribute_t *attr; /* Current attribute */
3324 cups_array_t *ra; /* Attributes to send in response */
3325 static const char * const uri_status_strings[] =
3326 { /* URI decode errors */
3327 "URI too large.",
3328 "Bad arguments to function.",
3329 "Bad resource in URI.",
3330 "Bad port number in URI.",
3331 "Bad hostname in URI.",
3332 "Bad username in URI.",
3333 "Bad scheme in URI.",
3334 "Bad/empty URI."
3335 };
3336
3337
3338 /*
3339 * Get the job...
3340 */
3341
3342 if ((job = find_job(client)) == NULL)
3343 {
a469f8a5
MS
3344 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3345 httpFlush(client->http);
83e08001
MS
3346 return;
3347 }
3348
3349 /*
3350 * See if we already have a document for this job or the job has already
3351 * in a non-pending state...
3352 */
3353
a469f8a5 3354 if (job->state > IPP_JSTATE_HELD)
83e08001 3355 {
a469f8a5
MS
3356 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3357 "Job is not in a pending state.");
3358 httpFlush(client->http);
83e08001
MS
3359 return;
3360 }
3361 else if (job->filename || job->fd >= 0)
3362 {
a469f8a5 3363 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED,
83e08001 3364 "Multiple document jobs are not supported.");
a469f8a5 3365 httpFlush(client->http);
83e08001
MS
3366 return;
3367 }
3368
3369 if ((attr = ippFindAttribute(client->request, "last-document",
3370 IPP_TAG_ZERO)) == NULL)
3371 {
a469f8a5 3372 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
83e08001 3373 "Missing required last-document attribute.");
a469f8a5 3374 httpFlush(client->http);
83e08001
MS
3375 return;
3376 }
a469f8a5
MS
3377 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 ||
3378 !ippGetBoolean(attr, 0))
83e08001
MS
3379 {
3380 respond_unsupported(client, attr);
a469f8a5 3381 httpFlush(client->http);
83e08001
MS
3382 return;
3383 }
3384
3385 /*
3386 * Validate document attributes...
3387 */
3388
3389 if (!valid_doc_attributes(client))
3390 {
a469f8a5 3391 httpFlush(client->http);
83e08001
MS
3392 return;
3393 }
3394
3395 /*
3396 * Do we have a file to print?
3397 */
3398
a469f8a5 3399 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
83e08001 3400 {
a469f8a5 3401 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
83e08001
MS
3402 "Unexpected document data following request.");
3403 return;
3404 }
3405
3406 /*
3407 * Do we have a document URI?
3408 */
3409
3410 if ((uri = ippFindAttribute(client->request, "document-uri",
3411 IPP_TAG_URI)) == NULL)
3412 {
a469f8a5 3413 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
83e08001
MS
3414 return;
3415 }
3416
a469f8a5 3417 if (ippGetCount(uri) != 1)
83e08001 3418 {
a469f8a5
MS
3419 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3420 "Too many document-uri values.");
83e08001
MS
3421 return;
3422 }
3423
a469f8a5 3424 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
83e08001
MS
3425 scheme, sizeof(scheme), userpass,
3426 sizeof(userpass), hostname, sizeof(hostname),
3427 &port, resource, sizeof(resource));
a469f8a5 3428 if (uri_status < HTTP_URI_STATUS_OK)
83e08001 3429 {
a469f8a5
MS
3430 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s",
3431 uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]);
83e08001
MS
3432 return;
3433 }
3434
3435 if (strcmp(scheme, "file") &&
3436#ifdef HAVE_SSL
3437 strcmp(scheme, "https") &&
3438#endif /* HAVE_SSL */
3439 strcmp(scheme, "http"))
3440 {
a469f8a5
MS
3441 respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME,
3442 "URI scheme \"%s\" not supported.", scheme);
83e08001
MS
3443 return;
3444 }
3445
3446 if (!strcmp(scheme, "file") && access(resource, R_OK))
3447 {
a469f8a5
MS
3448 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3449 "Unable to access URI: %s", strerror(errno));
83e08001
MS
3450 return;
3451 }
3452
3453 /*
3454 * Get the document format for the job...
3455 */
3456
3457 _cupsRWLockWrite(&(client->printer->rwlock));
3458
3459 if ((attr = ippFindAttribute(job->attrs, "document-format",
3460 IPP_TAG_MIMETYPE)) != NULL)
a469f8a5 3461 job->format = ippGetString(attr, 0, NULL);
83e08001
MS
3462 else
3463 job->format = "application/octet-stream";
3464
3465 /*
3466 * Create a file for the request data...
3467 */
3468
3469 if (!_cups_strcasecmp(job->format, "image/jpeg"))
3470 snprintf(filename, sizeof(filename), "%s/%d.jpg",
3471 client->printer->directory, job->id);
3472 else if (!_cups_strcasecmp(job->format, "image/png"))
3473 snprintf(filename, sizeof(filename), "%s/%d.png",
3474 client->printer->directory, job->id);
3475 else if (!_cups_strcasecmp(job->format, "application/pdf"))
3476 snprintf(filename, sizeof(filename), "%s/%d.pdf",
3477 client->printer->directory, job->id);
3478 else if (!_cups_strcasecmp(job->format, "application/postscript"))
3479 snprintf(filename, sizeof(filename), "%s/%d.ps",
3480 client->printer->directory, job->id);
3481 else
3482 snprintf(filename, sizeof(filename), "%s/%d.prn",
3483 client->printer->directory, job->id);
3484
3485 job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
3486
3487 _cupsRWUnlock(&(client->printer->rwlock));
3488
3489 if (job->fd < 0)
3490 {
a469f8a5 3491 job->state = IPP_JSTATE_ABORTED;
83e08001 3492
a469f8a5 3493 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
83e08001
MS
3494 "Unable to create print file: %s", strerror(errno));
3495 return;
3496 }
3497
3498 if (!strcmp(scheme, "file"))
3499 {
3500 if ((infile = open(resource, O_RDONLY)) < 0)
3501 {
a469f8a5
MS
3502 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3503 "Unable to access URI: %s", strerror(errno));
83e08001
MS
3504 return;
3505 }
3506
3507 do
3508 {
3509 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
3510 (errno == EAGAIN || errno == EINTR))
3511 bytes = 1;
7e86f2f6 3512 else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes)
83e08001
MS
3513 {
3514 int error = errno; /* Write error */
3515
a469f8a5 3516 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3517
3518 close(job->fd);
3519 job->fd = -1;
3520
3521 unlink(filename);
3522 close(infile);
3523
a469f8a5 3524 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
83e08001
MS
3525 "Unable to write print file: %s", strerror(error));
3526 return;
3527 }
3528 }
3529 while (bytes > 0);
3530
3531 close(infile);
3532 }
3533 else
3534 {
3535#ifdef HAVE_SSL
3536 if (port == 443 || !strcmp(scheme, "https"))
a469f8a5 3537 encryption = HTTP_ENCRYPTION_ALWAYS;
83e08001
MS
3538 else
3539#endif /* HAVE_SSL */
a469f8a5 3540 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
83e08001 3541
a469f8a5
MS
3542 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption,
3543 1, 30000, NULL)) == NULL)
83e08001 3544 {
a469f8a5 3545 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
83e08001
MS
3546 "Unable to connect to %s: %s", hostname,
3547 cupsLastErrorString());
a469f8a5 3548 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3549
3550 close(job->fd);
3551 job->fd = -1;
3552
3553 unlink(filename);
3554 return;
3555 }
3556
3557 httpClearFields(http);
3558 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
3559 if (httpGet(http, resource))
3560 {
a469f8a5
MS
3561 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3562 "Unable to GET URI: %s", strerror(errno));
83e08001 3563
a469f8a5 3564 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3565
3566 close(job->fd);
3567 job->fd = -1;
3568
3569 unlink(filename);
3570 httpClose(http);
3571 return;
3572 }
3573
a469f8a5 3574 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
83e08001 3575
a469f8a5 3576 if (status != HTTP_STATUS_OK)
83e08001 3577 {
a469f8a5
MS
3578 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3579 "Unable to GET URI: %s", httpStatus(status));
83e08001 3580
a469f8a5 3581 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3582
3583 close(job->fd);
3584 job->fd = -1;
3585
3586 unlink(filename);
3587 httpClose(http);
3588 return;
3589 }
3590
3591 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
3592 {
7e86f2f6 3593 if (write(job->fd, buffer, (size_t)bytes) < bytes)
83e08001
MS
3594 {
3595 int error = errno; /* Write error */
3596
a469f8a5 3597 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3598
3599 close(job->fd);
3600 job->fd = -1;
3601
3602 unlink(filename);
3603 httpClose(http);
3604
a469f8a5 3605 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
83e08001
MS
3606 "Unable to write print file: %s", strerror(error));
3607 return;
3608 }
3609 }
3610
3611 httpClose(http);
3612 }
3613
3614 if (close(job->fd))
3615 {
3616 int error = errno; /* Write error */
3617
a469f8a5 3618 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3619 job->fd = -1;
3620
3621 unlink(filename);
3622
a469f8a5
MS
3623 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3624 "Unable to write print file: %s", strerror(error));
83e08001
MS
3625 return;
3626 }
3627
3628 _cupsRWLockWrite(&(client->printer->rwlock));
3629
3630 job->fd = -1;
3631 job->filename = strdup(filename);
a469f8a5 3632 job->state = IPP_JSTATE_PENDING;
83e08001
MS
3633
3634 _cupsRWUnlock(&(client->printer->rwlock));
3635
3636 /*
3637 * Process the job...
3638 */
3639
3640#if 0
3641 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3642 {
a469f8a5
MS
3643 job->state = IPP_JSTATE_ABORTED;
3644 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
83e08001
MS
3645 return;
3646 }
3647
3648#else
3649 process_job(job);
3650#endif /* 0 */
3651
3652 /*
3653 * Return the job info...
3654 */
3655
a469f8a5 3656 respond_ipp(client, IPP_STATUS_OK, NULL);
83e08001
MS
3657
3658 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3659 cupsArrayAdd(ra, "job-id");
3660 cupsArrayAdd(ra, "job-state");
3661 cupsArrayAdd(ra, "job-state-reasons");
3662 cupsArrayAdd(ra, "job-uri");
3663
3664 copy_job_attributes(client, job, ra);
3665 cupsArrayDelete(ra);
3666}
3667
3668
3669/*
3670 * 'ipp_validate_job()' - Validate job creation attributes.
3671 */
3672
3673static void
3674ipp_validate_job(_ipp_client_t *client) /* I - Client */
3675{
3676 if (valid_job_attributes(client))
a469f8a5 3677 respond_ipp(client, IPP_STATUS_OK, NULL);
83e08001
MS
3678}
3679
3680
3681/*
3682 * 'process_client()' - Process client requests on a thread.
3683 */
3684
3685static void * /* O - Exit status */
3686process_client(_ipp_client_t *client) /* I - Client */
3687{
3688 /*
3689 * Loop until we are out of requests or timeout (30 seconds)...
3690 */
3691
a469f8a5 3692 while (httpWait(client->http, 30000))
83e08001
MS
3693 if (!process_http(client))
3694 break;
3695
3696 /*
3697 * Close the conection to the client and return...
3698 */
3699
3700 delete_client(client);
3701
3702 return (NULL);
3703}
3704
3705
3706/*
3707 * 'process_http()' - Process a HTTP request.
3708 */
3709
3710int /* O - 1 on success, 0 on failure */
3711process_http(_ipp_client_t *client) /* I - Client connection */
3712{
a469f8a5
MS
3713 char uri[1024]; /* URI */
3714 http_state_t http_state; /* HTTP state */
3715 http_status_t http_status; /* HTTP status */
3716 ipp_state_t ipp_state; /* State of IPP transfer */
3717 char scheme[32], /* Method/scheme */
3718 userpass[128], /* Username:password */
3719 hostname[HTTP_MAX_HOST];
3720 /* Hostname */
3721 int port; /* Port number */
3722 const char *encoding; /* Content-Encoding value */
3723 static const char * const http_states[] =
3724 { /* Strings for logging HTTP method */
3725 "WAITING",
3726 "OPTIONS",
3727 "GET",
3728 "GET_SEND",
3729 "HEAD",
3730 "POST",
3731 "POST_RECV",
3732 "POST_SEND",
3733 "PUT",
3734 "PUT_RECV",
3735 "DELETE",
3736 "TRACE",
3737 "CONNECT",
3738 "STATUS",
3739 "UNKNOWN_METHOD",
3740 "UNKNOWN_VERSION"
3741 };
83e08001 3742
83e08001
MS
3743
3744 /*
3745 * Clear state variables...
3746 */
3747
83e08001
MS
3748 ippDelete(client->request);
3749 ippDelete(client->response);
3750
a469f8a5
MS
3751 client->request = NULL;
3752 client->response = NULL;
3753 client->operation = HTTP_STATE_WAITING;
83e08001
MS
3754
3755 /*
3756 * Read a request from the connection...
3757 */
3758
a469f8a5
MS
3759 while ((http_state = httpReadRequest(client->http, uri,
3760 sizeof(uri))) == HTTP_STATE_WAITING)
3761 usleep(1);
83e08001
MS
3762
3763 /*
3764 * Parse the request line...
3765 */
3766
a469f8a5 3767 if (http_state == HTTP_STATE_ERROR)
83e08001 3768 {
a469f8a5
MS
3769 if (httpError(client->http) == EPIPE)
3770 fprintf(stderr, "%s Client closed connection.\n", client->hostname);
3771 else
db8b865d
MS
3772 fprintf(stderr, "%s Bad request line (%s).\n", client->hostname,
3773 strerror(httpError(client->http)));
83e08001 3774
a469f8a5 3775 return (0);
83e08001 3776 }
a469f8a5 3777 else if (http_state == HTTP_STATE_UNKNOWN_METHOD)
83e08001 3778 {
a469f8a5
MS
3779 fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname);
3780 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
3781 return (0);
1106b00e 3782 }
a469f8a5 3783 else if (http_state == HTTP_STATE_UNKNOWN_VERSION)
1106b00e 3784 {
a469f8a5
MS
3785 fprintf(stderr, "%s Bad HTTP version.\n", client->hostname);
3786 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
3787 return (0);
1106b00e
MS
3788 }
3789
a469f8a5
MS
3790 fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state],
3791 uri);
3792
1106b00e 3793 /*
a469f8a5 3794 * Separate the URI into its components...
1106b00e
MS
3795 */
3796
a469f8a5
MS
3797 if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
3798 userpass, sizeof(userpass),
3799 hostname, sizeof(hostname), &port,
3800 client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK)
1106b00e 3801 {
a469f8a5
MS
3802 fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri);
3803 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
3804 return (0);
3805 }
3806
a469f8a5
MS
3807 /*
3808 * Process the request...
3809 */
3810
3811 client->start = time(NULL);
3812 client->operation = httpGetState(client->http);
1106b00e
MS
3813
3814 /*
3815 * Parse incoming parameters until the status changes...
3816 */
3817
a469f8a5 3818 while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE);
1106b00e 3819
a469f8a5 3820 if (http_status != HTTP_STATUS_OK)
1106b00e 3821 {
a469f8a5 3822 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
3823 return (0);
3824 }
3825
a469f8a5
MS
3826 if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] &&
3827 httpGetVersion(client->http) >= HTTP_VERSION_1_1)
1106b00e
MS
3828 {
3829 /*
3830 * HTTP/1.1 and higher require the "Host:" field...
3831 */
3832
a469f8a5 3833 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
3834 return (0);
3835 }
3836
3837 /*
3838 * Handle HTTP Upgrade...
3839 */
3840
a469f8a5
MS
3841 if (!_cups_strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION),
3842 "Upgrade"))
1106b00e 3843 {
a469f8a5 3844 if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
1106b00e
MS
3845 return (0);
3846 }
3847
e60ec91f
MS
3848 /*
3849 * Handle HTTP Expect...
3850 */
3851
a469f8a5
MS
3852 if (httpGetExpect(client->http) &&
3853 (client->operation == HTTP_STATE_POST ||
3854 client->operation == HTTP_STATE_PUT))
e60ec91f 3855 {
a469f8a5 3856 if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
e60ec91f
MS
3857 {
3858 /*
3859 * Send 100-continue header...
3860 */
3861
a469f8a5 3862 if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
e60ec91f
MS
3863 return (0);
3864 }
3865 else
3866 {
3867 /*
3868 * Send 417-expectation-failed header...
3869 */
3870
a469f8a5 3871 if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
e60ec91f 3872 return (0);
e60ec91f
MS
3873 }
3874 }
3875
1106b00e
MS
3876 /*
3877 * Handle new transfers...
3878 */
3879
a469f8a5
MS
3880 encoding = httpGetContentEncoding(client->http);
3881
1106b00e
MS
3882 switch (client->operation)
3883 {
a469f8a5 3884 case HTTP_STATE_OPTIONS :
1106b00e
MS
3885 /*
3886 * Do HEAD/OPTIONS command...
3887 */
3888
a469f8a5 3889 return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
1106b00e 3890
a469f8a5 3891 case HTTP_STATE_HEAD :
1106b00e 3892 if (!strcmp(client->uri, "/icon.png"))
a469f8a5 3893 return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
1106b00e 3894 else if (!strcmp(client->uri, "/"))
a469f8a5 3895 return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
1106b00e 3896 else
a469f8a5 3897 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e 3898
a469f8a5 3899 case HTTP_STATE_GET :
1106b00e
MS
3900 if (!strcmp(client->uri, "/icon.png"))
3901 {
3902 /*
3903 * Send PNG icon file.
3904 */
3905
3906 int fd; /* Icon file */
3907 struct stat fileinfo; /* Icon file information */
3908 char buffer[4096]; /* Copy buffer */
3909 ssize_t bytes; /* Bytes */
3910
a469f8a5
MS
3911 fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon);
3912
1106b00e
MS
3913 if (!stat(client->printer->icon, &fileinfo) &&
3914 (fd = open(client->printer->icon, O_RDONLY)) >= 0)
3915 {
a469f8a5 3916 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png",
7e86f2f6 3917 (size_t)fileinfo.st_size))
1106b00e
MS
3918 {
3919 close(fd);
3920 return (0);
3921 }
3922
3923 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
7e86f2f6 3924 httpWrite2(client->http, buffer, (size_t)bytes);
1106b00e 3925
a469f8a5 3926 httpFlushWrite(client->http);
1106b00e
MS
3927
3928 close(fd);
3929 }
3930 else
a469f8a5 3931 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e
MS
3932 }
3933 else if (!strcmp(client->uri, "/"))
3934 {
3935 /*
3936 * Show web status page...
3937 */
3938
a469f8a5 3939 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
1106b00e
MS
3940 return (0);
3941
3942 html_printf(client,
3943 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
3944 "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
3945 "<html>\n"
3946 "<head>\n"
3947 "<title>%s</title>\n"
3948 "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
3949 "type=\"image/png\">\n"
3950 "</head>\n"
3951 "<body>\n"
3952 "</body>\n"
a469f8a5 3953 "<h1><img align=\"right\" src=\"/icon.png\">%s</h1>\n"
1106b00e
MS
3954 "<p>%s, %d job(s).</p>\n"
3955 "</body>\n"
3956 "</html>\n",
3957 client->printer->name, client->printer->name,
a469f8a5
MS
3958 client->printer->state == IPP_PSTATE_IDLE ? "Idle" :
3959 client->printer->state == IPP_PSTATE_PROCESSING ?
1106b00e
MS
3960 "Printing" : "Stopped",
3961 cupsArrayCount(client->printer->jobs));
a469f8a5 3962 httpWrite2(client->http, "", 0);
1106b00e
MS
3963
3964 return (1);
3965 }
3966 else
a469f8a5 3967 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e
MS
3968 break;
3969
a469f8a5
MS
3970 case HTTP_STATE_POST :
3971 if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
1106b00e
MS
3972 "application/ipp"))
3973 {
3974 /*
3975 * Not an IPP request...
3976 */
3977
a469f8a5 3978 return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
1106b00e
MS
3979 }
3980
3981 /*
3982 * Read the IPP request...
3983 */
3984
3985 client->request = ippNew();
3986
a469f8a5
MS
3987 while ((ipp_state = ippRead(client->http,
3988 client->request)) != IPP_STATE_DATA)
3989 {
3990 if (ipp_state == IPP_STATE_ERROR)
1106b00e 3991 {
a469f8a5 3992 fprintf(stderr, "%s IPP read error (%s).\n", client->hostname,
83e08001 3993 cupsLastErrorString());
a469f8a5 3994 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
3995 return (0);
3996 }
a469f8a5 3997 }
1106b00e
MS
3998
3999 /*
4000 * Now that we have the IPP request, process the request...
4001 */
4002
4003 return (process_ipp(client));
4004
4005 default :
4006 break; /* Anti-compiler-warning-code */
4007 }
4008
4009 return (1);
4010}
4011
4012
4013/*
4014 * 'process_ipp()' - Process an IPP request.
4015 */
4016
4017static int /* O - 1 on success, 0 on error */
4018process_ipp(_ipp_client_t *client) /* I - Client */
4019{
4020 ipp_tag_t group; /* Current group tag */
4021 ipp_attribute_t *attr; /* Current attribute */
4022 ipp_attribute_t *charset; /* Character set attribute */
4023 ipp_attribute_t *language; /* Language attribute */
4024 ipp_attribute_t *uri; /* Printer URI attribute */
a469f8a5
MS
4025 int major, minor; /* Version number */
4026 const char *name; /* Name of attribute */
1106b00e
MS
4027
4028
83e08001 4029 debug_attributes("Request", client->request, 1);
1106b00e
MS
4030
4031 /*
4032 * First build an empty response message for this request...
4033 */
4034
a469f8a5
MS
4035 client->operation_id = ippGetOperation(client->request);
4036 client->response = ippNewResponse(client->request);
1106b00e
MS
4037
4038 /*
4039 * Then validate the request header and required attributes...
4040 */
4041
a469f8a5
MS
4042 major = ippGetVersion(client->request, &minor);
4043
4044 if (major < 1 || major > 2)
1106b00e
MS
4045 {
4046 /*
4047 * Return an error, since we only support IPP 1.x and 2.x.
4048 */
4049
a469f8a5
MS
4050 respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED,
4051 "Bad request version number %d.%d.", major, minor);
1106b00e 4052 }
a469f8a5
MS
4053 else if (ippGetRequestId(client->request) <= 0)
4054 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.",
4055 ippGetRequestId(client->request));
4056 else if (!ippFirstAttribute(client->request))
4057 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4058 "No attributes in request.");
1106b00e
MS
4059 else
4060 {
4061 /*
4062 * Make sure that the attributes are provided in the correct order and
4063 * don't repeat groups...
4064 */
4065
a469f8a5
MS
4066 for (attr = ippFirstAttribute(client->request),
4067 group = ippGetGroupTag(attr);
1106b00e 4068 attr;
a469f8a5
MS
4069 attr = ippNextAttribute(client->request))
4070 {
4071 if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
1106b00e
MS
4072 {
4073 /*
4074 * Out of order; return an error...
4075 */
4076
a469f8a5
MS
4077 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4078 "Attribute groups are out of order (%x < %x).",
4079 ippGetGroupTag(attr), group);
1106b00e
MS
4080 break;
4081 }
4082 else
a469f8a5
MS
4083 group = ippGetGroupTag(attr);
4084 }
1106b00e
MS
4085
4086 if (!attr)
4087 {
4088 /*
4089 * Then make sure that the first three attributes are:
4090 *
4091 * attributes-charset
4092 * attributes-natural-language
4093 * printer-uri/job-uri
4094 */
4095
a469f8a5
MS
4096 attr = ippFirstAttribute(client->request);
4097 name = ippGetName(attr);
4098 if (attr && name && !strcmp(name, "attributes-charset") &&
4099 ippGetValueTag(attr) == IPP_TAG_CHARSET)
1106b00e
MS
4100 charset = attr;
4101 else
4102 charset = NULL;
4103
a469f8a5
MS
4104 attr = ippNextAttribute(client->request);
4105 name = ippGetName(attr);
1106b00e 4106
a469f8a5
MS
4107 if (attr && name && !strcmp(name, "attributes-natural-language") &&
4108 ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
1106b00e
MS
4109 language = attr;
4110 else
4111 language = NULL;
4112
4113 if ((attr = ippFindAttribute(client->request, "printer-uri",
4114 IPP_TAG_URI)) != NULL)
4115 uri = attr;
4116 else if ((attr = ippFindAttribute(client->request, "job-uri",
4117 IPP_TAG_URI)) != NULL)
4118 uri = attr;
4119 else
4120 uri = NULL;
4121
1106b00e 4122 if (charset &&
a469f8a5
MS
4123 _cups_strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
4124 _cups_strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
1106b00e
MS
4125 {
4126 /*
4127 * Bad character set...
4128 */
4129
a469f8a5 4130 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
1106b00e 4131 "Unsupported character set \"%s\".",
a469f8a5 4132 ippGetString(charset, 0, NULL));
1106b00e
MS
4133 }
4134 else if (!charset || !language || !uri)
4135 {
4136 /*
4137 * Return an error, since attributes-charset,
4138 * attributes-natural-language, and printer-uri/job-uri are required
4139 * for all operations.
4140 */
4141
a469f8a5
MS
4142 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4143 "Missing required attributes.");
1106b00e 4144 }
1106b00e
MS
4145 else
4146 {
8a78aa37
MS
4147 char scheme[32], /* URI scheme */
4148 userpass[32], /* Username/password in URI */
4149 host[256], /* Host name in URI */
4150 resource[256]; /* Resource path in URI */
4151 int port; /* Port number in URI */
4152
4153 name = ippGetName(uri);
4154
4155 if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
4156 scheme, sizeof(scheme),
4157 userpass, sizeof(userpass),
4158 host, sizeof(host), &port,
4159 resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
4160 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
4161 "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
4162 else if ((!strcmp(name, "job-uri") &&
4163 strncmp(resource, "/ipp/print/", 11)) ||
4164 (!strcmp(name, "printer-uri") &&
4165 strcmp(resource, "/ipp/print")))
4166 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
4167 name, ippGetString(uri, 0, NULL));
4168 else
1106b00e
MS
4169 {
4170 /*
8a78aa37 4171 * Try processing the operation...
1106b00e
MS
4172 */
4173
8a78aa37
MS
4174 switch (ippGetOperation(client->request))
4175 {
4176 case IPP_OP_PRINT_JOB :
4177 ipp_print_job(client);
4178 break;
4179
4180 case IPP_OP_PRINT_URI :
4181 ipp_print_uri(client);
4182 break;
4183
4184 case IPP_OP_VALIDATE_JOB :
4185 ipp_validate_job(client);
4186 break;
4187
4188 case IPP_OP_CREATE_JOB :
4189 ipp_create_job(client);
4190 break;
4191
4192 case IPP_OP_SEND_DOCUMENT :
4193 ipp_send_document(client);
4194 break;
4195
4196 case IPP_OP_SEND_URI :
4197 ipp_send_uri(client);
4198 break;
4199
4200 case IPP_OP_CANCEL_JOB :
4201 ipp_cancel_job(client);
4202 break;
4203
4204 case IPP_OP_GET_JOB_ATTRIBUTES :
4205 ipp_get_job_attributes(client);
4206 break;
4207
4208 case IPP_OP_GET_JOBS :
4209 ipp_get_jobs(client);
4210 break;
4211
4212 case IPP_OP_GET_PRINTER_ATTRIBUTES :
4213 ipp_get_printer_attributes(client);
4214 break;
4215
4216 default :
4217 respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
4218 "Operation not supported.");
4219 break;
4220 }
1106b00e
MS
4221 }
4222 }
4223 }
4224 }
4225
4226 /*
4227 * Send the HTTP header and return...
4228 */
4229
a469f8a5
MS
4230 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
4231 httpFlush(client->http); /* Flush trailing (junk) data */
1106b00e 4232
a469f8a5 4233 return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
1106b00e
MS
4234 ippLength(client->response)));
4235}
4236
4237
4238/*
4239 * 'process_job()' - Process a print job.
4240 */
4241
4242static void * /* O - Thread exit status */
4243process_job(_ipp_job_t *job) /* I - Job */
4244{
a469f8a5
MS
4245 job->state = IPP_JSTATE_PROCESSING;
4246 job->printer->state = IPP_PSTATE_PROCESSING;
1106b00e 4247
db8b865d
MS
4248 if (job->printer->command)
4249 {
4250 /*
4251 * Execute a command with the job spool file and wait for it to complete...
4252 */
4253
4254 int pid, /* Process ID */
4255 status; /* Exit status */
4256 time_t start, /* Start time */
4257 end; /* End time */
4258
4259 fprintf(stderr, "Running command \"%s %s\".\n", job->printer->command,
4260 job->filename);
4261 time(&start);
4262
4263 if ((pid = fork()) == 0)
4264 {
4265 /*
4266 * Child comes here...
4267 */
4268
4269 execlp(job->printer->command, job->printer->command, job->filename,
4270 (void *)NULL);
4271 exit(errno);
4272 }
4273 else if (pid < 0)
4274 {
4275 /*
4276 * Unable to fork process...
4277 */
4278
4279 perror("Unable to start job processing command");
4280 }
4281 else
4282 {
4283 /*
4284 * Wait for child to complete...
4285 */
4286
4287#ifdef HAVE_WAITPID
4288 while (waitpid(pid, &status, 0) < 0);
4289#else
4290 while (wait(&status) < 0);
4291#endif /* HAVE_WAITPID */
4292
4293 if (status)
4294 {
4295 if (WIFEXITED(status))
4296 fprintf(stderr, "Command \"%s\" exited with status %d.\n",
4297 job->printer->command, WEXITSTATUS(status));
4298 else
4299 fprintf(stderr, "Command \"%s\" terminated with signal %d.\n",
4300 job->printer->command, WTERMSIG(status));
4301 }
4302 else
4303 fprintf(stderr, "Command \"%s\" completed successfully.\n",
4304 job->printer->command);
4305 }
4306
4307 /*
4308 * Make sure processing takes at least 5 seconds...
4309 */
4310
4311 time(&end);
4312 if ((end - start) < 5)
4313 sleep(5);
4314 }
4315 else
4316 {
4317 /*
4318 * Sleep for a random amount of time to simulate job processing.
4319 */
4320
4321 sleep(5 + (rand() % 11));
4322 }
1106b00e
MS
4323
4324 if (job->cancel)
a469f8a5 4325 job->state = IPP_JSTATE_CANCELED;
1106b00e 4326 else
a469f8a5 4327 job->state = IPP_JSTATE_COMPLETED;
1106b00e
MS
4328
4329 job->completed = time(NULL);
a469f8a5 4330 job->printer->state = IPP_PSTATE_IDLE;
1106b00e
MS
4331 job->printer->active_job = NULL;
4332
4333 return (NULL);
4334}
4335
4336
0268488e 4337#ifdef HAVE_DNSSD
1106b00e
MS
4338/*
4339 * 'register_printer()' - Register a printer object via Bonjour.
4340 */
4341
4342static int /* O - 1 on success, 0 on error */
4343register_printer(
4344 _ipp_printer_t *printer, /* I - Printer */
4345 const char *location, /* I - Location */
4346 const char *make, /* I - Manufacturer */
4347 const char *model, /* I - Model name */
4348 const char *formats, /* I - Supported formats */
4349 const char *adminurl, /* I - Web interface URL */
4350 int color, /* I - 1 = color, 0 = monochrome */
4351 int duplex, /* I - 1 = duplex, 0 = simplex */
a469f8a5 4352 const char *subtype) /* I - Service subtype */
1106b00e
MS
4353{
4354 DNSServiceErrorType error; /* Error from Bonjour */
4355 char make_model[256],/* Make and model together */
a469f8a5
MS
4356 product[256], /* Product string */
4357 regtype[256]; /* Bonjour service type */
1106b00e
MS
4358
4359
4360 /*
4361 * Build the TXT record for IPP...
4362 */
4363
4364 snprintf(make_model, sizeof(make_model), "%s %s", make, model);
4365 snprintf(product, sizeof(product), "(%s)", model);
4366
4367 TXTRecordCreate(&(printer->ipp_txt), 1024, NULL);
8a78aa37 4368 TXTRecordSetValue(&(printer->ipp_txt), "rp", 9, "ipp/print");
1106b00e
MS
4369 TXTRecordSetValue(&(printer->ipp_txt), "ty", (uint8_t)strlen(make_model),
4370 make_model);
4371 TXTRecordSetValue(&(printer->ipp_txt), "adminurl", (uint8_t)strlen(adminurl),
4372 adminurl);
a469f8a5
MS
4373 if (*location)
4374 TXTRecordSetValue(&(printer->ipp_txt), "note", (uint8_t)strlen(location),
4375 location);
1106b00e
MS
4376 TXTRecordSetValue(&(printer->ipp_txt), "product", (uint8_t)strlen(product),
4377 product);
4378 TXTRecordSetValue(&(printer->ipp_txt), "pdl", (uint8_t)strlen(formats),
4379 formats);
4380 TXTRecordSetValue(&(printer->ipp_txt), "Color", 1, color ? "T" : "F");
4381 TXTRecordSetValue(&(printer->ipp_txt), "Duplex", 1, duplex ? "T" : "F");
4382 TXTRecordSetValue(&(printer->ipp_txt), "usb_MFG", (uint8_t)strlen(make),
4383 make);
4384 TXTRecordSetValue(&(printer->ipp_txt), "usb_MDL", (uint8_t)strlen(model),
4385 model);
1106b00e
MS
4386
4387 /*
4388 * Create a shared service reference for Bonjour...
4389 */
4390
4391 if ((error = DNSServiceCreateConnection(&(printer->common_ref)))
4392 != kDNSServiceErr_NoError)
4393 {
4394 fprintf(stderr, "Unable to create mDNSResponder connection: %d\n", error);
4395 return (0);
4396 }
4397
4398 /*
4399 * Register the _printer._tcp (LPD) service type with a port number of 0 to
4400 * defend our service name but not actually support LPD...
4401 */
4402
4403 printer->printer_ref = printer->common_ref;
4404
4405 if ((error = DNSServiceRegister(&(printer->printer_ref),
4406 kDNSServiceFlagsShareConnection,
4407 0 /* interfaceIndex */, printer->dnssd_name,
4408 "_printer._tcp", NULL /* domain */,
4409 NULL /* host */, 0 /* port */, 0 /* txtLen */,
4410 NULL /* txtRecord */,
4411 (DNSServiceRegisterReply)dnssd_callback,
4412 printer)) != kDNSServiceErr_NoError)
4413 {
4414 fprintf(stderr, "Unable to register \"%s._printer._tcp\": %d\n",
4415 printer->dnssd_name, error);
4416 return (0);
4417 }
4418
4419 /*
4420 * Then register the _ipp._tcp (IPP) service type with the real port number to
4421 * advertise our IPP printer...
4422 */
4423
4424 printer->ipp_ref = printer->common_ref;
4425
a469f8a5
MS
4426 if (subtype && *subtype)
4427 snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtype);
4428 else
4429 strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
4430
1106b00e
MS
4431 if ((error = DNSServiceRegister(&(printer->ipp_ref),
4432 kDNSServiceFlagsShareConnection,
4433 0 /* interfaceIndex */, printer->dnssd_name,
4434 regtype, NULL /* domain */,
4435 NULL /* host */, htons(printer->port),
4436 TXTRecordGetLength(&(printer->ipp_txt)),
4437 TXTRecordGetBytesPtr(&(printer->ipp_txt)),
4438 (DNSServiceRegisterReply)dnssd_callback,
4439 printer)) != kDNSServiceErr_NoError)
4440 {
4441 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
4442 printer->dnssd_name, regtype, error);
4443 return (0);
4444 }
4445
8a78aa37 4446# if 0 /* ifdef HAVE_SSL */
a469f8a5
MS
4447 /*
4448 * Then register the _ipps._tcp (IPP) service type with the real port number to
4449 * advertise our IPP printer...
4450 */
4451
4452 printer->ipps_ref = printer->common_ref;
4453
4454 if (subtype && *subtype)
4455 snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtype);
4456 else
4457 strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
4458
4459 if ((error = DNSServiceRegister(&(printer->ipps_ref),
4460 kDNSServiceFlagsShareConnection,
4461 0 /* interfaceIndex */, printer->dnssd_name,
4462 regtype, NULL /* domain */,
4463 NULL /* host */, htons(printer->port),
4464 TXTRecordGetLength(&(printer->ipp_txt)),
4465 TXTRecordGetBytesPtr(&(printer->ipp_txt)),
4466 (DNSServiceRegisterReply)dnssd_callback,
4467 printer)) != kDNSServiceErr_NoError)
4468 {
4469 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
4470 printer->dnssd_name, regtype, error);
4471 return (0);
4472 }
4473# endif /* HAVE_SSL */
4474
1106b00e
MS
4475 /*
4476 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
4477 * real port number to advertise our IPP printer...
4478 */
4479
4480 printer->http_ref = printer->common_ref;
4481
4482 if ((error = DNSServiceRegister(&(printer->http_ref),
4483 kDNSServiceFlagsShareConnection,
4484 0 /* interfaceIndex */, printer->dnssd_name,
4485 "_http._tcp,_printer", NULL /* domain */,
4486 NULL /* host */, htons(printer->port),
4487 0 /* txtLen */, NULL, /* txtRecord */
4488 (DNSServiceRegisterReply)dnssd_callback,
4489 printer)) != kDNSServiceErr_NoError)
4490 {
4491 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
4492 printer->dnssd_name, regtype, error);
4493 return (0);
4494 }
4495
4496 return (1);
4497}
0268488e 4498#endif /* HAVE_DNSSD */
1106b00e
MS
4499
4500
4501/*
4502 * 'respond_http()' - Send a HTTP response.
4503 */
4504
4505int /* O - 1 on success, 0 on failure */
a469f8a5
MS
4506respond_http(
4507 _ipp_client_t *client, /* I - Client */
4508 http_status_t code, /* I - HTTP status of response */
4509 const char *content_encoding, /* I - Content-Encoding of response */
4510 const char *type, /* I - MIME media type of response */
4511 size_t length) /* I - Length of response */
1106b00e
MS
4512{
4513 char message[1024]; /* Text message */
4514
4515
a469f8a5 4516 fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
1106b00e 4517
a469f8a5 4518 if (code == HTTP_STATUS_CONTINUE)
1106b00e
MS
4519 {
4520 /*
4521 * 100-continue doesn't send any headers...
4522 */
4523
a469f8a5 4524 return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
1106b00e
MS
4525 }
4526
4527 /*
4528 * Format an error message...
4529 */
4530
a469f8a5 4531 if (!type && !length && code != HTTP_STATUS_OK)
1106b00e
MS
4532 {
4533 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
4534
4535 type = "text/plain";
4536 length = strlen(message);
4537 }
4538 else
4539 message[0] = '\0';
4540
4541 /*
a469f8a5 4542 * Send the HTTP response header...
1106b00e
MS
4543 */
4544
a469f8a5 4545 httpClearFields(client->http);
1106b00e 4546
a469f8a5
MS
4547 if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
4548 client->operation == HTTP_STATE_OPTIONS)
4549 httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
1106b00e
MS
4550
4551 if (type)
4552 {
4553 if (!strcmp(type, "text/html"))
a469f8a5
MS
4554 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
4555 "text/html; charset=utf-8");
4556 else
4557 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
1106b00e 4558
a469f8a5
MS
4559 if (content_encoding)
4560 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
1106b00e 4561 }
1106b00e 4562
a469f8a5
MS
4563 httpSetLength(client->http, length);
4564
4565 if (httpWriteResponse(client->http, code) < 0)
1106b00e
MS
4566 return (0);
4567
4568 /*
4569 * Send the response data...
4570 */
4571
4572 if (message[0])
4573 {
4574 /*
4575 * Send a plain text message.
4576 */
4577
a469f8a5
MS
4578 if (httpPrintf(client->http, "%s", message) < 0)
4579 return (0);
4580
4581 if (httpWrite2(client->http, "", 0) < 0)
1106b00e
MS
4582 return (0);
4583 }
4584 else if (client->response)
4585 {
4586 /*
4587 * Send an IPP response...
4588 */
4589
83e08001 4590 debug_attributes("Response", client->response, 2);
1106b00e 4591
a469f8a5 4592 ippSetState(client->response, IPP_STATE_IDLE);
1106b00e 4593
a469f8a5 4594 if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
1106b00e
MS
4595 return (0);
4596 }
1106b00e 4597
a469f8a5 4598 return (1);
1106b00e
MS
4599}
4600
4601
4602/*
4603 * 'respond_ipp()' - Send an IPP response.
4604 */
4605
4606static void
4607respond_ipp(_ipp_client_t *client, /* I - Client */
4608 ipp_status_t status, /* I - status-code */
4609 const char *message, /* I - printf-style status-message */
4610 ...) /* I - Additional args as needed */
4611{
a469f8a5 4612 const char *formatted = NULL; /* Formatted message */
1106b00e
MS
4613
4614
a469f8a5 4615 ippSetStatusCode(client->response, status);
1106b00e
MS
4616
4617 if (message)
4618 {
a469f8a5
MS
4619 va_list ap; /* Pointer to additional args */
4620 ipp_attribute_t *attr; /* New status-message attribute */
4621
1106b00e 4622 va_start(ap, message);
a469f8a5
MS
4623 if ((attr = ippFindAttribute(client->response, "status-message",
4624 IPP_TAG_TEXT)) != NULL)
4625 ippSetStringfv(client->response, &attr, 0, message, ap);
4626 else
4627 attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
4628 "status-message", NULL, message, ap);
1106b00e
MS
4629 va_end(ap);
4630
a469f8a5 4631 formatted = ippGetString(attr, 0, NULL);
1106b00e 4632 }
1106b00e 4633
a469f8a5
MS
4634 if (formatted)
4635 fprintf(stderr, "%s %s %s (%s)\n", client->hostname,
4636 ippOpString(client->operation_id), ippErrorString(status),
4637 formatted);
4638 else
4639 fprintf(stderr, "%s %s %s\n", client->hostname,
4640 ippOpString(client->operation_id), ippErrorString(status));
1106b00e
MS
4641}
4642
4643
83e08001
MS
4644/*
4645 * 'respond_unsupported()' - Respond with an unsupported attribute.
4646 */
4647
4648static void
4649respond_unsupported(
4650 _ipp_client_t *client, /* I - Client */
4651 ipp_attribute_t *attr) /* I - Atribute */
4652{
a2326b5b
MS
4653 ipp_attribute_t *temp; /* Copy of attribute */
4654
4655
a469f8a5
MS
4656 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
4657 "Unsupported %s %s%s value.", ippGetName(attr),
4658 ippGetCount(attr) > 1 ? "1setOf " : "",
4659 ippTagString(ippGetValueTag(attr)));
83e08001 4660
a2326b5b
MS
4661 temp = ippCopyAttribute(client->response, attr, 0);
4662 ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
83e08001
MS
4663}
4664
4665
1106b00e
MS
4666/*
4667 * 'run_printer()' - Run the printer service.
4668 */
4669
4670static void
4671run_printer(_ipp_printer_t *printer) /* I - Printer */
4672{
0268488e 4673 int num_fds; /* Number of file descriptors */
1106b00e
MS
4674 struct pollfd polldata[3]; /* poll() data */
4675 int timeout; /* Timeout for poll() */
4676 _ipp_client_t *client; /* New client */
4677
4678
4679 /*
4680 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
4681 */
4682
4683 polldata[0].fd = printer->ipv4;
4684 polldata[0].events = POLLIN;
4685
4686 polldata[1].fd = printer->ipv6;
4687 polldata[1].events = POLLIN;
4688
0268488e
MS
4689 num_fds = 2;
4690
4691#ifdef HAVE_DNSSD
4692 polldata[num_fds ].fd = DNSServiceRefSockFD(printer->common_ref);
4693 polldata[num_fds ++].events = POLLIN;
4694#endif /* HAVE_DNSSD */
1106b00e
MS
4695
4696 /*
4697 * Loop until we are killed or have a hard error...
4698 */
4699
4700 for (;;)
4701 {
4702 if (cupsArrayCount(printer->jobs))
4703 timeout = 10;
4704 else
4705 timeout = -1;
4706
7e86f2f6 4707 if (poll(polldata, (nfds_t)num_fds, timeout) < 0 && errno != EINTR)
1106b00e
MS
4708 {
4709 perror("poll() failed");
4710 break;
4711 }
4712
4713 if (polldata[0].revents & POLLIN)
4714 {
4715 if ((client = create_client(printer, printer->ipv4)) != NULL)
4716 {
4717 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
4718 {
4719 perror("Unable to create client thread");
4720 delete_client(client);
4721 }
4722 }
4723 }
4724
4725 if (polldata[1].revents & POLLIN)
4726 {
4727 if ((client = create_client(printer, printer->ipv6)) != NULL)
4728 {
4729 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
4730 {
4731 perror("Unable to create client thread");
4732 delete_client(client);
4733 }
4734 }
4735 }
4736
0268488e 4737#ifdef HAVE_DNSSD
1106b00e
MS
4738 if (polldata[2].revents & POLLIN)
4739 DNSServiceProcessResult(printer->common_ref);
0268488e 4740#endif /* HAVE_DNSSD */
1106b00e
MS
4741
4742 /*
4743 * Clean out old jobs...
4744 */
4745
4746 clean_jobs(printer);
4747 }
4748}
4749
4750
4751/*
4752 * 'usage()' - Show program usage.
4753 */
4754
4755static void
4756usage(int status) /* O - Exit status */
4757{
4758 if (!status)
4759 {
d1f0f86b 4760 puts(CUPS_SVERSION " - Copyright 2010-2013 by Apple Inc. All rights "
a469f8a5 4761 "reserved.");
1106b00e
MS
4762 puts("");
4763 }
4764
4765 puts("Usage: ippserver [options] \"name\"");
4766 puts("");
4767 puts("Options:");
4768 puts("-2 Supports 2-sided printing (default=1-sided)");
4769 puts("-M manufacturer Manufacturer name (default=Test)");
d1f0f86b 4770 puts("-P PIN printing mode");
d48a1002 4771 puts("-c command Run command for every print job");
1106b00e
MS
4772 printf("-d spool-directory Spool directory "
4773 "(default=/tmp/ippserver.%d)\n", (int)getpid());
4774 puts("-f type/subtype[,...] List of supported types "
4775 "(default=application/pdf,image/jpeg)");
4776 puts("-h Show program help");
4777 puts("-i iconfile.png PNG icon file (default=printer.png)");
d1f0f86b 4778 puts("-k Keep job spool files");
1106b00e
MS
4779 puts("-l location Location of printer (default=empty string)");
4780 puts("-m model Model name (default=Printer)");
4781 puts("-n hostname Hostname for printer");
4782 puts("-p port Port number (default=auto)");
d1f0f86b 4783#ifdef HAVE_DNSSD
a469f8a5 4784 puts("-r subtype Bonjour service subtype (default=_print)");
d1f0f86b 4785#endif /* HAVE_DNSSD */
1106b00e
MS
4786 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
4787 puts("-v[vvv] Be (very) verbose");
4788
4789 exit(status);
4790}
4791
4792
4793/*
83e08001
MS
4794 * 'valid_doc_attributes()' - Determine whether the document attributes are
4795 * valid.
1106b00e 4796 *
83e08001
MS
4797 * When one or more document attributes are invalid, this function adds a
4798 * suitable response and attributes to the unsupported group.
1106b00e
MS
4799 */
4800
4801static int /* O - 1 if valid, 0 if not */
83e08001 4802valid_doc_attributes(
1106b00e
MS
4803 _ipp_client_t *client) /* I - Client */
4804{
a469f8a5
MS
4805 int valid = 1; /* Valid attributes? */
4806 ipp_op_t op = ippGetOperation(client->request);
4807 /* IPP operation */
4808 const char *op_name = ippOpString(op);
4809 /* IPP operation name */
1106b00e 4810 ipp_attribute_t *attr, /* Current attribute */
a469f8a5
MS
4811 *supported; /* xxx-supported attribute */
4812 const char *compression = NULL,
4813 /* compression value */
4814 *format = NULL; /* document-format value */
1106b00e
MS
4815
4816
4817 /*
4818 * Check operation attributes...
4819 */
4820
1106b00e
MS
4821 if ((attr = ippFindAttribute(client->request, "compression",
4822 IPP_TAG_ZERO)) != NULL)
4823 {
4824 /*
a469f8a5
MS
4825 * If compression is specified, only accept a supported value in a Print-Job
4826 * or Send-Document request...
1106b00e
MS
4827 */
4828
a469f8a5
MS
4829 compression = ippGetString(attr, 0, NULL);
4830 supported = ippFindAttribute(client->printer->attrs,
4831 "compression-supported", IPP_TAG_KEYWORD);
4832
4833 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
4834 ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
db8b865d
MS
4835 (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
4836 op != IPP_OP_VALIDATE_JOB) ||
a469f8a5
MS
4837 !ippContainsString(supported, compression))
4838 {
1106b00e 4839 respond_unsupported(client, attr);
a469f8a5
MS
4840 valid = 0;
4841 }
1106b00e 4842 else
a469f8a5 4843 {
83e08001 4844 fprintf(stderr, "%s %s compression=\"%s\"\n",
a469f8a5
MS
4845 client->hostname, op_name, compression);
4846
4847 if (strcmp(compression, "none"))
4848 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
4849 }
1106b00e
MS
4850 }
4851
4852 /*
4853 * Is it a format we support?
4854 */
4855
4856 if ((attr = ippFindAttribute(client->request, "document-format",
4857 IPP_TAG_ZERO)) != NULL)
4858 {
a469f8a5
MS
4859 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
4860 ippGetGroupTag(attr) != IPP_TAG_OPERATION)
4861 {
1106b00e 4862 respond_unsupported(client, attr);
a469f8a5
MS
4863 valid = 0;
4864 }
1106b00e 4865 else
e60ec91f 4866 {
a469f8a5 4867 format = ippGetString(attr, 0, NULL);
e60ec91f
MS
4868
4869 fprintf(stderr, "%s %s document-format=\"%s\"\n",
a469f8a5 4870 client->hostname, op_name, format);
e60ec91f 4871 }
1106b00e
MS
4872 }
4873 else
a469f8a5 4874 {
8a78aa37
MS
4875 format = ippGetString(ippFindAttribute(client->printer->attrs,
4876 "document-format-default",
4877 IPP_TAG_MIMETYPE), 0, NULL);
4878 if (!format)
4879 format = "application/octet-stream"; /* Should never happen */
4880
4881 attr = ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
4882 "document-format", NULL, format);
a469f8a5 4883 }
1106b00e
MS
4884
4885 if (!strcmp(format, "application/octet-stream") &&
a469f8a5
MS
4886 (ippGetOperation(client->request) == IPP_OP_PRINT_JOB ||
4887 ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
1106b00e
MS
4888 {
4889 /*
4890 * Auto-type the file using the first 4 bytes of the file...
4891 */
4892
4893 unsigned char header[4]; /* First 4 bytes of file */
4894
4895 memset(header, 0, sizeof(header));
a469f8a5 4896 httpPeek(client->http, (char *)header, sizeof(header));
1106b00e
MS
4897
4898 if (!memcmp(header, "%PDF", 4))
4899 format = "application/pdf";
4900 else if (!memcmp(header, "%!", 2))
4901 format = "application/postscript";
4902 else if (!memcmp(header, "\377\330\377", 3) &&
4903 header[3] >= 0xe0 && header[3] <= 0xef)
4904 format = "image/jpeg";
4905 else if (!memcmp(header, "\211PNG", 4))
4906 format = "image/png";
4907
4908 if (format)
4909 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n",
a469f8a5 4910 client->hostname, op_name, format);
1106b00e
MS
4911
4912 if (!attr)
83e08001
MS
4913 attr = ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
4914 "document-format", NULL, format);
1106b00e 4915 else
a469f8a5 4916 ippSetString(client->request, &attr, 0, format);
1106b00e
MS
4917 }
4918
a469f8a5 4919 if (op != IPP_OP_CREATE_JOB &&
83e08001 4920 (supported = ippFindAttribute(client->printer->attrs,
1106b00e 4921 "document-format-supported",
a469f8a5
MS
4922 IPP_TAG_MIMETYPE)) != NULL &&
4923 !ippContainsString(supported, format))
1106b00e 4924 {
a469f8a5
MS
4925 respond_unsupported(client, attr);
4926 valid = 0;
1106b00e
MS
4927 }
4928
a469f8a5 4929 return (valid);
83e08001
MS
4930}
4931
4932
4933/*
4934 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
4935 *
4936 * When one or more job attributes are invalid, this function adds a suitable
4937 * response and attributes to the unsupported group.
4938 */
4939
4940static int /* O - 1 if valid, 0 if not */
4941valid_job_attributes(
4942 _ipp_client_t *client) /* I - Client */
4943{
a469f8a5
MS
4944 int i, /* Looping var */
4945 valid = 1; /* Valid attributes? */
83e08001
MS
4946 ipp_attribute_t *attr, /* Current attribute */
4947 *supported; /* xxx-supported attribute */
4948
4949
4950 /*
4951 * Check operation attributes...
4952 */
4953
a469f8a5 4954 valid = valid_doc_attributes(client);
83e08001 4955
1106b00e
MS
4956 /*
4957 * Check the various job template attributes...
4958 */
4959
4960 if ((attr = ippFindAttribute(client->request, "copies",
4961 IPP_TAG_ZERO)) != NULL)
4962 {
a469f8a5
MS
4963 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
4964 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
1106b00e
MS
4965 {
4966 respond_unsupported(client, attr);
a469f8a5 4967 valid = 0;
1106b00e
MS
4968 }
4969 }
4970
4971 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity",
4972 IPP_TAG_ZERO)) != NULL)
4973 {
a469f8a5 4974 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
1106b00e
MS
4975 {
4976 respond_unsupported(client, attr);
a469f8a5 4977 valid = 0;
1106b00e
MS
4978 }
4979 }
4980
4981 if ((attr = ippFindAttribute(client->request, "job-hold-until",
4982 IPP_TAG_ZERO)) != NULL)
4983 {
a469f8a5
MS
4984 if (ippGetCount(attr) != 1 ||
4985 (ippGetValueTag(attr) != IPP_TAG_NAME &&
4986 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
4987 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
4988 strcmp(ippGetString(attr, 0, NULL), "no-hold"))
1106b00e
MS
4989 {
4990 respond_unsupported(client, attr);
a469f8a5 4991 valid = 0;
1106b00e
MS
4992 }
4993 }
4994
4995 if ((attr = ippFindAttribute(client->request, "job-name",
4996 IPP_TAG_ZERO)) != NULL)
4997 {
a469f8a5
MS
4998 if (ippGetCount(attr) != 1 ||
4999 (ippGetValueTag(attr) != IPP_TAG_NAME &&
5000 ippGetValueTag(attr) != IPP_TAG_NAMELANG))
1106b00e
MS
5001 {
5002 respond_unsupported(client, attr);
a469f8a5 5003 valid = 0;
1106b00e
MS
5004 }
5005 }
5006
5007 if ((attr = ippFindAttribute(client->request, "job-priority",
5008 IPP_TAG_ZERO)) != NULL)
5009 {
a469f8a5
MS
5010 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
5011 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
1106b00e
MS
5012 {
5013 respond_unsupported(client, attr);
a469f8a5 5014 valid = 0;
1106b00e
MS
5015 }
5016 }
5017
5018 if ((attr = ippFindAttribute(client->request, "job-sheets",
5019 IPP_TAG_ZERO)) != NULL)
5020 {
a469f8a5
MS
5021 if (ippGetCount(attr) != 1 ||
5022 (ippGetValueTag(attr) != IPP_TAG_NAME &&
5023 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
5024 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
5025 strcmp(ippGetString(attr, 0, NULL), "none"))
1106b00e
MS
5026 {
5027 respond_unsupported(client, attr);
a469f8a5 5028 valid = 0;
1106b00e
MS
5029 }
5030 }
5031
5032 if ((attr = ippFindAttribute(client->request, "media",
5033 IPP_TAG_ZERO)) != NULL)
5034 {
a469f8a5
MS
5035 if (ippGetCount(attr) != 1 ||
5036 (ippGetValueTag(attr) != IPP_TAG_NAME &&
5037 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
5038 ippGetValueTag(attr) != IPP_TAG_KEYWORD))
1106b00e
MS
5039 {
5040 respond_unsupported(client, attr);
a469f8a5 5041 valid = 0;
1106b00e
MS
5042 }
5043 else
5044 {
5045 for (i = 0;
5046 i < (int)(sizeof(media_supported) / sizeof(media_supported[0]));
5047 i ++)
a469f8a5 5048 if (!strcmp(ippGetString(attr, 0, NULL), media_supported[i]))
1106b00e
MS
5049 break;
5050
5051 if (i >= (int)(sizeof(media_supported) / sizeof(media_supported[0])))
5052 {
5053 respond_unsupported(client, attr);
a469f8a5 5054 valid = 0;
1106b00e
MS
5055 }
5056 }
5057 }
5058
5059 if ((attr = ippFindAttribute(client->request, "media-col",
5060 IPP_TAG_ZERO)) != NULL)
5061 {
a469f8a5
MS
5062 if (ippGetCount(attr) != 1 ||
5063 ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
1106b00e
MS
5064 {
5065 respond_unsupported(client, attr);
a469f8a5 5066 valid = 0;
1106b00e
MS
5067 }
5068 /* TODO: check for valid media-col */
5069 }
5070
5071 if ((attr = ippFindAttribute(client->request, "multiple-document-handling",
5072 IPP_TAG_ZERO)) != NULL)
5073 {
a469f8a5
MS
5074 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
5075 (strcmp(ippGetString(attr, 0, NULL),
1106b00e 5076 "separate-documents-uncollated-copies") &&
a469f8a5 5077 strcmp(ippGetString(attr, 0, NULL),
1106b00e
MS
5078 "separate-documents-collated-copies")))
5079 {
5080 respond_unsupported(client, attr);
a469f8a5 5081 valid = 0;
1106b00e
MS
5082 }
5083 }
5084
5085 if ((attr = ippFindAttribute(client->request, "orientation-requested",
5086 IPP_TAG_ZERO)) != NULL)
5087 {
a469f8a5
MS
5088 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
5089 ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
5090 ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
1106b00e
MS
5091 {
5092 respond_unsupported(client, attr);
a469f8a5 5093 valid = 0;
1106b00e
MS
5094 }
5095 }
5096
5097 if ((attr = ippFindAttribute(client->request, "page-ranges",
5098 IPP_TAG_ZERO)) != NULL)
5099 {
5100 respond_unsupported(client, attr);
a469f8a5 5101 valid = 0;
1106b00e
MS
5102 }
5103
5104 if ((attr = ippFindAttribute(client->request, "print-quality",
5105 IPP_TAG_ZERO)) != NULL)
5106 {
a469f8a5
MS
5107 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
5108 ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
5109 ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
1106b00e
MS
5110 {
5111 respond_unsupported(client, attr);
a469f8a5 5112 valid = 0;
1106b00e
MS
5113 }
5114 }
5115
5116 if ((attr = ippFindAttribute(client->request, "printer-resolution",
5117 IPP_TAG_ZERO)) != NULL)
5118 {
5119 respond_unsupported(client, attr);
a469f8a5 5120 valid = 0;
1106b00e
MS
5121 }
5122
5123 if ((attr = ippFindAttribute(client->request, "sides",
5124 IPP_TAG_ZERO)) != NULL)
5125 {
ad29aeab
MS
5126 const char *sides = NULL; /* "sides" value... */
5127
a469f8a5 5128 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
1106b00e
MS
5129 {
5130 respond_unsupported(client, attr);
a469f8a5 5131 valid = 0;
1106b00e
MS
5132 }
5133
ad29aeab
MS
5134 sides = ippGetString(attr, 0, NULL);
5135
5136 if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported",
1106b00e
MS
5137 IPP_TAG_KEYWORD)) != NULL)
5138 {
ad29aeab 5139 if (!ippContainsString(supported, sides))
1106b00e
MS
5140 {
5141 respond_unsupported(client, attr);
a469f8a5 5142 valid = 0;
1106b00e
MS
5143 }
5144 }
ad29aeab 5145 else if (strcmp(sides, "one-sided"))
1106b00e
MS
5146 {
5147 respond_unsupported(client, attr);
a469f8a5 5148 valid = 0;
1106b00e
MS
5149 }
5150 }
5151
a469f8a5 5152 return (valid);
1106b00e
MS
5153}
5154
5155
5156/*
5157 * End of "$Id$".
5158 */