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