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