]> git.ipfire.org Git - thirdparty/cups.git/blame_incremental - tools/ippeveprinter.c
Update default PAM service (cups instead of other)
[thirdparty/cups.git] / tools / ippeveprinter.c
... / ...
CommitLineData
1/*
2 * IPP Everywhere printer application for CUPS.
3 *
4 * Copyright © 2010-2019 by Apple Inc.
5 *
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
7 * information.º
8 *
9 * Note: This program began life as the "ippserver" sample code that first
10 * appeared in CUPS 1.4. The name has been changed in order to distinguish it
11 * from the PWG's much more ambitious "ippserver" program, which supports
12 * different kinds of IPP services and multiple services per instance - the
13 * "ippeveprinter" program exposes a single print service conforming to the
14 * current IPP Everywhere specification, thus the new name.
15 */
16
17/*
18 * Include necessary headers...
19 */
20
21#include <cups/cups-private.h>
22#include <cups/debug-private.h>
23#if !CUPS_LITE
24# include <cups/ppd-private.h>
25#endif /* !CUPS_LITE */
26
27#include <limits.h>
28#include <sys/stat.h>
29
30#ifdef _WIN32
31# include <fcntl.h>
32# include <io.h>
33# include <process.h>
34# define WEXITSTATUS(s) (s)
35# include <winsock2.h>
36typedef ULONG nfds_t;
37# define poll WSAPoll
38#else
39extern char **environ;
40
41# include <sys/fcntl.h>
42# include <sys/wait.h>
43# include <poll.h>
44#endif /* _WIN32 */
45
46#ifdef HAVE_DNSSD
47# include <dns_sd.h>
48#elif defined(HAVE_AVAHI)
49# include <avahi-client/client.h>
50# include <avahi-client/publish.h>
51# include <avahi-common/error.h>
52# include <avahi-common/thread-watch.h>
53#endif /* HAVE_DNSSD */
54
55#ifdef HAVE_SYS_MOUNT_H
56# include <sys/mount.h>
57#endif /* HAVE_SYS_MOUNT_H */
58#ifdef HAVE_SYS_STATFS_H
59# include <sys/statfs.h>
60#endif /* HAVE_SYS_STATFS_H */
61#ifdef HAVE_SYS_STATVFS_H
62# include <sys/statvfs.h>
63#endif /* HAVE_SYS_STATVFS_H */
64#ifdef HAVE_SYS_VFS_H
65# include <sys/vfs.h>
66#endif /* HAVE_SYS_VFS_H */
67
68#if HAVE_LIBPAM
69# ifdef HAVE_PAM_PAM_APPL_H
70# include <pam/pam_appl.h>
71# else
72# include <security/pam_appl.h>
73# endif /* HAVE_PAM_PAM_APPL_H */
74#endif /* HAVE_LIBPAM */
75
76#include "printer-png.h"
77
78
79/*
80 * Constants...
81 */
82
83enum ippeve_preason_e /* printer-state-reasons bit values */
84{
85 IPPEVE_PREASON_NONE = 0x0000, /* none */
86 IPPEVE_PREASON_OTHER = 0x0001, /* other */
87 IPPEVE_PREASON_COVER_OPEN = 0x0002, /* cover-open */
88 IPPEVE_PREASON_INPUT_TRAY_MISSING = 0x0004,
89 /* input-tray-missing */
90 IPPEVE_PREASON_MARKER_SUPPLY_EMPTY = 0x0008,
91 /* marker-supply-empty */
92 IPPEVE_PREASON_MARKER_SUPPLY_LOW = 0x0010,
93 /* marker-supply-low */
94 IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL = 0x0020,
95 /* marker-waste-almost-full */
96 IPPEVE_PREASON_MARKER_WASTE_FULL = 0x0040,
97 /* marker-waste-full */
98 IPPEVE_PREASON_MEDIA_EMPTY = 0x0080, /* media-empty */
99 IPPEVE_PREASON_MEDIA_JAM = 0x0100, /* media-jam */
100 IPPEVE_PREASON_MEDIA_LOW = 0x0200, /* media-low */
101 IPPEVE_PREASON_MEDIA_NEEDED = 0x0400, /* media-needed */
102 IPPEVE_PREASON_MOVING_TO_PAUSED = 0x0800,
103 /* moving-to-paused */
104 IPPEVE_PREASON_PAUSED = 0x1000, /* paused */
105 IPPEVE_PREASON_SPOOL_AREA_FULL = 0x2000,/* spool-area-full */
106 IPPEVE_PREASON_TONER_EMPTY = 0x4000, /* toner-empty */
107 IPPEVE_PREASON_TONER_LOW = 0x8000 /* toner-low */
108};
109typedef unsigned int ippeve_preason_t; /* Bitfield for printer-state-reasons */
110static const char * const ippeve_preason_strings[] =
111{ /* Strings for each bit */
112 /* "none" is implied for no bits set */
113 "other",
114 "cover-open",
115 "input-tray-missing",
116 "marker-supply-empty",
117 "marker-supply-low",
118 "marker-waste-almost-full",
119 "marker-waste-full",
120 "media-empty",
121 "media-jam",
122 "media-low",
123 "media-needed",
124 "moving-to-paused",
125 "paused",
126 "spool-area-full",
127 "toner-empty",
128 "toner-low"
129};
130
131
132/*
133 * URL scheme for web resources...
134 */
135
136#ifdef HAVE_SSL
137# define WEB_SCHEME "https"
138#else
139# define WEB_SCHEME "http"
140#endif /* HAVE_SSL */
141
142
143/*
144 * Structures...
145 */
146
147#ifdef HAVE_DNSSD
148typedef DNSServiceRef ippeve_srv_t; /* Service reference */
149typedef TXTRecordRef ippeve_txt_t; /* TXT record */
150
151#elif defined(HAVE_AVAHI)
152typedef AvahiEntryGroup *ippeve_srv_t; /* Service reference */
153typedef AvahiStringList *ippeve_txt_t; /* TXT record */
154
155#else
156typedef void *ippeve_srv_t; /* Service reference */
157typedef void *ippeve_txt_t; /* TXT record */
158#endif /* HAVE_DNSSD */
159
160#if HAVE_LIBPAM
161typedef struct ippeve_authdata_s /* Authentication data */
162{
163 char username[HTTP_MAX_VALUE], /* Username string */
164 *password; /* Password string */
165} ippeve_authdata_t;
166#endif /* HAVE_LIBPAM */
167
168typedef struct ippeve_filter_s /**** Attribute filter ****/
169{
170 cups_array_t *ra; /* Requested attributes */
171 ipp_tag_t group_tag; /* Group to copy */
172} ippeve_filter_t;
173
174typedef struct ippeve_job_s ippeve_job_t;
175
176typedef struct ippeve_printer_s /**** Printer data ****/
177{
178 /* TODO: One IPv4 and one IPv6 listener are really not sufficient */
179 int ipv4, /* IPv4 listener */
180 ipv6; /* IPv6 listener */
181 ippeve_srv_t ipp_ref, /* Bonjour IPP service */
182 ipps_ref, /* Bonjour IPPS service */
183 http_ref, /* Bonjour HTTP service */
184 printer_ref; /* Bonjour LPD service */
185 char *dnssd_name, /* printer-dnssd-name */
186 *name, /* printer-name */
187 *icon, /* Icon filename */
188 *directory, /* Spool directory */
189 *hostname, /* Hostname */
190 *uri, /* printer-uri-supported */
191 *device_uri, /* Device URI (if any) */
192 *output_format, /* Output format */
193#if !CUPS_LITE
194 *ppdfile, /* PPD file (if any) */
195#endif /* !CUPS_LITE */
196 *command; /* Command to run with job file */
197 int port; /* Port */
198 int web_forms; /* Enable web interface forms? */
199 size_t urilen; /* Length of printer URI */
200 ipp_t *attrs; /* Static attributes */
201 time_t start_time; /* Startup time */
202 time_t config_time; /* printer-config-change-time */
203 ipp_pstate_t state; /* printer-state value */
204 ippeve_preason_t state_reasons; /* printer-state-reasons values */
205 time_t state_time; /* printer-state-change-time */
206 cups_array_t *jobs; /* Jobs */
207 ippeve_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} ippeve_printer_t;
211
212struct ippeve_job_s /**** Job data ****/
213{
214 int id; /* Job ID */
215 const char *name, /* job-name */
216 *username, /* job-originating-user-name */
217 *format; /* document-format */
218 ipp_jstate_t state; /* job-state value */
219 char *message; /* job-state-message value */
220 int msglevel; /* job-state-message log level (0=error, 1=info) */
221 time_t created, /* time-at-creation value */
222 processing, /* time-at-processing value */
223 completed; /* time-at-completed value */
224 int impressions, /* job-impressions value */
225 impcompleted; /* job-impressions-completed value */
226 ipp_t *attrs; /* Static attributes */
227 int cancel; /* Non-zero when job canceled */
228 char *filename; /* Print file name */
229 int fd; /* Print file descriptor */
230 ippeve_printer_t *printer; /* Printer */
231};
232
233typedef struct ippeve_client_s /**** Client data ****/
234{
235 http_t *http; /* HTTP connection */
236 ipp_t *request, /* IPP request */
237 *response; /* IPP response */
238 time_t start; /* Request start time */
239 http_state_t operation; /* Request operation */
240 ipp_op_t operation_id; /* IPP operation-id */
241 char uri[1024], /* Request URI */
242 *options; /* URI options */
243 http_addr_t addr; /* Client address */
244 char hostname[256], /* Client hostname */
245 username[HTTP_MAX_VALUE];
246 /* Authenticated username, if any */
247 ippeve_printer_t *printer; /* Printer */
248 ippeve_job_t *job; /* Current job, if any */
249} ippeve_client_t;
250
251
252/*
253 * Local functions...
254 */
255
256static http_status_t authenticate_request(ippeve_client_t *client);
257static void clean_jobs(ippeve_printer_t *printer);
258static int compare_jobs(ippeve_job_t *a, ippeve_job_t *b);
259static void copy_attributes(ipp_t *to, ipp_t *from, cups_array_t *ra, ipp_tag_t group_tag, int quickcopy);
260static void copy_job_attributes(ippeve_client_t *client, ippeve_job_t *job, cups_array_t *ra);
261static ippeve_client_t *create_client(ippeve_printer_t *printer, int sock);
262static ippeve_job_t *create_job(ippeve_client_t *client);
263static int create_job_file(ippeve_job_t *job, char *fname, size_t fnamesize, const char *dir, const char *ext);
264static int create_listener(const char *name, int port, int family);
265static ipp_t *create_media_col(const char *media, const char *source, const char *type, int width, int length, int bottom, int left, int right, int top);
266static ipp_t *create_media_size(int width, int length);
267static ippeve_printer_t *create_printer(const char *servername, int serverport, const char *name, const char *location, const char *icon, cups_array_t *docformats, const char *subtypes, const char *directory, const char *command, const char *device_uri, const char *output_format, ipp_t *attrs);
268static void debug_attributes(const char *title, ipp_t *ipp, int response);
269static void delete_client(ippeve_client_t *client);
270static void delete_job(ippeve_job_t *job);
271static void delete_printer(ippeve_printer_t *printer);
272#ifdef HAVE_DNSSD
273static void DNSSD_API dnssd_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, ippeve_printer_t *printer);
274#elif defined(HAVE_AVAHI)
275static void dnssd_callback(AvahiEntryGroup *p, AvahiEntryGroupState state, void *context);
276static void dnssd_client_cb(AvahiClient *c, AvahiClientState state, void *userdata);
277#endif /* HAVE_DNSSD */
278static void dnssd_init(void);
279static int filter_cb(ippeve_filter_t *filter, ipp_t *dst, ipp_attribute_t *attr);
280static ippeve_job_t *find_job(ippeve_client_t *client);
281static void finish_document_data(ippeve_client_t *client, ippeve_job_t *job);
282static void finish_document_uri(ippeve_client_t *client, ippeve_job_t *job);
283static void html_escape(ippeve_client_t *client, const char *s, size_t slen);
284static void html_footer(ippeve_client_t *client);
285static void html_header(ippeve_client_t *client, const char *title, int refresh);
286static void html_printf(ippeve_client_t *client, const char *format, ...) _CUPS_FORMAT(2, 3);
287static void ipp_cancel_job(ippeve_client_t *client);
288static void ipp_close_job(ippeve_client_t *client);
289static void ipp_create_job(ippeve_client_t *client);
290static void ipp_get_job_attributes(ippeve_client_t *client);
291static void ipp_get_jobs(ippeve_client_t *client);
292static void ipp_get_printer_attributes(ippeve_client_t *client);
293static void ipp_identify_printer(ippeve_client_t *client);
294static void ipp_print_job(ippeve_client_t *client);
295static void ipp_print_uri(ippeve_client_t *client);
296static void ipp_send_document(ippeve_client_t *client);
297static void ipp_send_uri(ippeve_client_t *client);
298static void ipp_validate_job(ippeve_client_t *client);
299static ipp_t *load_ippserver_attributes(const char *servername, int serverport, const char *filename, cups_array_t *docformats);
300static ipp_t *load_legacy_attributes(const char *make, const char *model, int ppm, int ppm_color, int duplex, cups_array_t *docformats);
301#if !CUPS_LITE
302static ipp_t *load_ppd_attributes(const char *ppdfile, cups_array_t *docformats);
303#endif /* !CUPS_LITE */
304#if HAVE_LIBPAM
305static int pam_func(int, const struct pam_message **, struct pam_response **, void *);
306#endif /* HAVE_LIBPAM */
307static int parse_options(ippeve_client_t *client, cups_option_t **options);
308static void process_attr_message(ippeve_job_t *job, char *message);
309static void *process_client(ippeve_client_t *client);
310static int process_http(ippeve_client_t *client);
311static int process_ipp(ippeve_client_t *client);
312static void *process_job(ippeve_job_t *job);
313static void process_state_message(ippeve_job_t *job, char *message);
314static int register_printer(ippeve_printer_t *printer, const char *subtypes);
315static int respond_http(ippeve_client_t *client, http_status_t code, const char *content_coding, const char *type, size_t length);
316static void respond_ipp(ippeve_client_t *client, ipp_status_t status, const char *message, ...) _CUPS_FORMAT(3, 4);
317static void respond_unsupported(ippeve_client_t *client, ipp_attribute_t *attr);
318static void run_printer(ippeve_printer_t *printer);
319static int show_media(ippeve_client_t *client);
320static int show_status(ippeve_client_t *client);
321static int show_supplies(ippeve_client_t *client);
322static char *time_string(time_t tv, char *buffer, size_t bufsize);
323static void usage(int status) _CUPS_NORETURN;
324static int valid_doc_attributes(ippeve_client_t *client);
325static int valid_job_attributes(ippeve_client_t *client);
326
327
328/*
329 * Globals...
330 */
331
332#ifdef HAVE_DNSSD
333static DNSServiceRef DNSSDMaster = NULL;
334#elif defined(HAVE_AVAHI)
335static AvahiThreadedPoll *DNSSDMaster = NULL;
336static AvahiClient *DNSSDClient = NULL;
337#endif /* HAVE_DNSSD */
338
339static int KeepFiles = 0, /* Keep spooled job files? */
340 MaxVersion = 20,/* Maximum IPP version (20 = 2.0, 11 = 1.1, etc.) */
341 Verbosity = 0; /* Verbosity level */
342static const char *PAMService = NULL;
343 /* PAM service */
344
345
346/*
347 * 'main()' - Main entry to the sample server.
348 */
349
350int /* O - Exit status */
351main(int argc, /* I - Number of command-line args */
352 char *argv[]) /* I - Command-line arguments */
353{
354 int i; /* Looping var */
355 const char *opt, /* Current option character */
356 *attrfile = NULL, /* ippserver attributes file */
357 *command = NULL, /* Command to run with job files */
358 *device_uri = NULL, /* Device URI */
359 *output_format = NULL, /* Output format */
360 *icon = NULL, /* Icon file */
361#ifdef HAVE_SSL
362 *keypath = NULL, /* Keychain path */
363#endif /* HAVE_SSL */
364 *location = "", /* Location of printer */
365 *make = "Example", /* Manufacturer */
366 *model = "Printer", /* Model */
367 *name = NULL, /* Printer name */
368#if !CUPS_LITE
369 *ppdfile = NULL, /* PPD file */
370#endif /* !CUPS_LITE */
371 *subtypes = "_print"; /* DNS-SD service subtype */
372 int legacy = 0, /* Legacy mode? */
373 duplex = 0, /* Duplex mode */
374 ppm = 10, /* Pages per minute for mono */
375 ppm_color = 0, /* Pages per minute for color */
376 web_forms = 1; /* Enable web site forms? */
377 ipp_t *attrs = NULL; /* Printer attributes */
378 char directory[1024] = ""; /* Spool directory */
379 cups_array_t *docformats = NULL; /* Supported formats */
380 const char *servername = NULL; /* Server host name */
381 int serverport = 0; /* Server port number (0 = auto) */
382 ippeve_printer_t *printer; /* Printer object */
383
384
385 /*
386 * Parse command-line arguments...
387 */
388
389 for (i = 1; i < argc; i ++)
390 {
391 if (!strcmp(argv[i], "--help"))
392 {
393 usage(0);
394 }
395 else if (!strcmp(argv[i], "--no-web-forms"))
396 {
397 web_forms = 0;
398 }
399 else if (!strcmp(argv[i], "--pam-service"))
400 {
401 i ++;
402 if (i >= argc)
403 usage(1);
404
405 PAMService = argv[i];
406 }
407 else if (!strcmp(argv[i], "--version"))
408 {
409 puts(CUPS_SVERSION);
410 return (0);
411 }
412 else if (!strncmp(argv[i], "--", 2))
413 {
414 _cupsLangPrintf(stderr, _("%s: Unknown option \"%s\"."), argv[0], argv[i]);
415 usage(1);
416 }
417 else if (argv[i][0] == '-')
418 {
419 for (opt = argv[i] + 1; *opt; opt ++)
420 {
421 switch (*opt)
422 {
423 case '2' : /* -2 (enable 2-sided printing) */
424 duplex = 1;
425 legacy = 1;
426 break;
427
428 case 'A' : /* -A (enable authentication) */
429 if (!PAMService)
430 PAMService = "cups";
431 break;
432
433 case 'D' : /* -D device-uri */
434 i ++;
435 if (i >= argc)
436 usage(1);
437
438 device_uri = argv[i];
439 break;
440
441 case 'F' : /* -F output/format */
442 i ++;
443 if (i >= argc)
444 usage(1);
445
446 output_format = argv[i];
447 break;
448
449#ifdef HAVE_SSL
450 case 'K' : /* -K keypath */
451 i ++;
452 if (i >= argc)
453 usage(1);
454
455 keypath = argv[i];
456 break;
457#endif /* HAVE_SSL */
458
459 case 'M' : /* -M manufacturer */
460 i ++;
461 if (i >= argc)
462 usage(1);
463
464 make = argv[i];
465 legacy = 1;
466 break;
467
468#if !CUPS_LITE
469 case 'P' : /* -P filename.ppd */
470 i ++;
471 if (i >= argc)
472 usage(1);
473
474 ppdfile = argv[i];
475 break;
476#endif /* !CUPS_LITE */
477
478 case 'V' : /* -V max-version */
479 i ++;
480 if (i >= argc)
481 usage(1);
482
483 if (!strcmp(argv[i], "2.0"))
484 MaxVersion = 20;
485 else if (!strcmp(argv[i], "1.1"))
486 MaxVersion = 11;
487 else
488 usage(1);
489 break;
490
491 case 'a' : /* -a attributes-file */
492 i ++;
493 if (i >= argc)
494 usage(1);
495
496 attrfile = argv[i];
497 break;
498
499 case 'c' : /* -c command */
500 i ++;
501 if (i >= argc)
502 usage(1);
503
504 command = argv[i];
505 break;
506
507 case 'd' : /* -d spool-directory */
508 i ++;
509 if (i >= argc)
510 usage(1);
511
512 strlcpy(directory, argv[i], sizeof(directory));
513 break;
514
515 case 'f' : /* -f type/subtype[,...] */
516 i ++;
517 if (i >= argc)
518 usage(1);
519
520 docformats = _cupsArrayNewStrings(argv[i], ',');
521 legacy = 1;
522 break;
523
524 case 'i' : /* -i icon.png */
525 i ++;
526 if (i >= argc)
527 usage(1);
528
529 icon = argv[i];
530 break;
531
532 case 'k' : /* -k (keep files) */
533 KeepFiles = 1;
534 break;
535
536 case 'l' : /* -l location */
537 i ++;
538 if (i >= argc)
539 usage(1);
540
541 location = argv[i];
542 break;
543
544 case 'm' : /* -m model */
545 i ++;
546 if (i >= argc)
547 usage(1);
548
549 model = argv[i];
550 legacy = 1;
551 break;
552
553 case 'n' : /* -n hostname */
554 i ++;
555 if (i >= argc)
556 usage(1);
557
558 servername = argv[i];
559 break;
560
561 case 'p' : /* -p port */
562 i ++;
563 if (i >= argc || !isdigit(argv[i][0] & 255))
564 usage(1);
565
566 serverport = atoi(argv[i]);
567 break;
568
569 case 'r' : /* -r subtype */
570 i ++;
571 if (i >= argc)
572 usage(1);
573
574 subtypes = argv[i];
575 break;
576
577 case 's' : /* -s speed[,color-speed] */
578 i ++;
579 if (i >= argc)
580 usage(1);
581
582 if (sscanf(argv[i], "%d,%d", &ppm, &ppm_color) < 1)
583 usage(1);
584
585 legacy = 1;
586 break;
587
588 case 'v' : /* -v (be verbose) */
589 Verbosity ++;
590 break;
591
592 default : /* Unknown */
593 _cupsLangPrintf(stderr, _("%s: Unknown option \"-%c\"."), argv[0], *opt);
594 usage(1);
595 }
596 }
597 }
598 else if (!name)
599 {
600 name = argv[i];
601 }
602 else
603 {
604 _cupsLangPrintf(stderr, _("%s: Unknown option \"%s\"."), argv[0], argv[i]);
605 usage(1);
606 }
607 }
608
609 if (!name)
610 usage(1);
611
612#if CUPS_LITE
613 if (attrfile != NULL && legacy)
614 usage(1);
615#else
616 if (((ppdfile != NULL) + (attrfile != NULL) + legacy) > 1)
617 usage(1);
618#endif /* CUPS_LITE */
619
620 /*
621 * Apply defaults as needed...
622 */
623
624 if (!serverport)
625 {
626#ifdef _WIN32
627 /*
628 * Windows is almost always used as a single user system, so use a default
629 * port number of 8631.
630 */
631
632 serverport = 8631;
633
634#else
635 /*
636 * Use 8000 + UID mod 1000 for the default port number...
637 */
638
639 serverport = 8000 + ((int)getuid() % 1000);
640#endif /* _WIN32 */
641
642 _cupsLangPrintf(stderr, _("Listening on port %d."), serverport);
643 }
644
645 if (!directory[0])
646 {
647 const char *tmpdir; /* Temporary directory */
648
649#ifdef _WIN32
650 if ((tmpdir = getenv("TEMP")) == NULL)
651 tmpdir = "C:/TEMP";
652#elif defined(__APPLE__) && TARGET_OS_OSX
653 if ((tmpdir = getenv("TMPDIR")) == NULL)
654 tmpdir = "/private/tmp";
655#else
656 if ((tmpdir = getenv("TMPDIR")) == NULL)
657 tmpdir = "/tmp";
658#endif /* _WIN32 */
659
660 snprintf(directory, sizeof(directory), "%s/ippeveprinter.%d", tmpdir, (int)getpid());
661
662 if (mkdir(directory, 0755) && errno != EEXIST)
663 {
664 _cupsLangPrintf(stderr, _("Unable to create spool directory \"%s\": %s"), directory, strerror(errno));
665 usage(1);
666 }
667
668 if (Verbosity)
669 _cupsLangPrintf(stderr, _("Using spool directory \"%s\"."), directory);
670 }
671
672 /*
673 * Initialize DNS-SD...
674 */
675
676 dnssd_init();
677
678 /*
679 * Create the printer...
680 */
681
682 if (!docformats)
683 docformats = _cupsArrayNewStrings(ppm_color > 0 ? "image/jpeg,image/pwg-raster,image/urf": "image/pwg-raster,image/urf", ',');
684
685 if (attrfile)
686 attrs = load_ippserver_attributes(servername, serverport, attrfile, docformats);
687#if !CUPS_LITE
688 else if (ppdfile)
689 {
690 attrs = load_ppd_attributes(ppdfile, docformats);
691
692 if (!command)
693 command = "ippeveps";
694
695 if (!output_format)
696 output_format = "application/postscript";
697 }
698#endif /* !CUPS_LITE */
699 else
700 attrs = load_legacy_attributes(make, model, ppm, ppm_color, duplex, docformats);
701
702 if ((printer = create_printer(servername, serverport, name, location, icon, docformats, subtypes, directory, command, device_uri, output_format, attrs)) == NULL)
703 return (1);
704
705 printer->web_forms = web_forms;
706
707#if !CUPS_LITE
708 if (ppdfile)
709 printer->ppdfile = strdup(ppdfile);
710#endif /* !CUPS_LITE */
711
712#ifdef HAVE_SSL
713 cupsSetServerCredentials(keypath, printer->hostname, 1);
714#endif /* HAVE_SSL */
715
716 /*
717 * Run the print service...
718 */
719
720 run_printer(printer);
721
722 /*
723 * Destroy the printer and exit...
724 */
725
726 delete_printer(printer);
727
728 return (0);
729}
730
731
732/*
733 * 'authenticate_request()' - Try to authenticate the request.
734 */
735
736static http_status_t /* O - HTTP_STATUS_CONTINUE to keep going, otherwise status to return */
737authenticate_request(
738 ippeve_client_t *client) /* I - Client */
739{
740#if HAVE_LIBPAM
741 /*
742 * If PAM isn't enabled, return 'continue' now...
743 */
744
745 const char *authorization; /* Pointer into Authorization string */
746 int userlen; /* Username:password length */
747 pam_handle_t *pamh; /* PAM authentication handle */
748 int pamerr; /* PAM error code */
749 struct pam_conv pamdata; /* PAM conversation data */
750 ippeve_authdata_t data; /* Authentication data */
751
752
753 if (!PAMService)
754 return (HTTP_STATUS_CONTINUE);
755
756 /*
757 * Try authenticating using PAM...
758 */
759
760 authorization = httpGetField(client->http, HTTP_FIELD_AUTHORIZATION);
761
762 if (!*authorization)
763 return (HTTP_STATUS_UNAUTHORIZED);
764
765 if (strncmp(authorization, "Basic ", 6))
766 {
767 fputs("Unsupported scheme in Authorization header.\n", stderr);
768 return (HTTP_STATUS_BAD_REQUEST);
769 }
770
771 authorization += 5;
772 while (isspace(*authorization & 255))
773 authorization ++;
774
775 userlen = sizeof(data.username);
776 httpDecode64_2(data.username, &userlen, authorization);
777
778 if ((data.password = strchr(data.username, ':')) == NULL)
779 {
780 fputs("No password in Authorization header.\n", stderr);
781 return (HTTP_STATUS_BAD_REQUEST);
782 }
783
784 *(data.password)++ = '\0';
785
786 if (!data.username[0])
787 {
788 fputs("No username in Authorization header.\n", stderr);
789 return (HTTP_STATUS_BAD_REQUEST);
790 }
791
792 pamdata.conv = pam_func;
793 pamdata.appdata_ptr = &data;
794
795 if ((pamerr = pam_start(PAMService, data.username, &pamdata, &pamh)) != PAM_SUCCESS)
796 {
797 fprintf(stderr, "pam_start() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
798 return (HTTP_STATUS_SERVER_ERROR);
799 }
800
801 if ((pamerr = pam_authenticate(pamh, PAM_SILENT)) != PAM_SUCCESS)
802 {
803 fprintf(stderr, "pam_authenticate() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
804 pam_end(pamh, 0);
805 return (HTTP_STATUS_UNAUTHORIZED);
806 }
807
808 if ((pamerr = pam_acct_mgmt(pamh, PAM_SILENT)) != PAM_SUCCESS)
809 {
810 fprintf(stderr, "pam_acct_mgmt() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
811 pam_end(pamh, 0);
812 return (HTTP_STATUS_SERVER_ERROR);
813 }
814
815 strlcpy(client->username, data.username, sizeof(client->username));
816
817 pam_end(pamh, PAM_SUCCESS);
818
819 return (HTTP_STATUS_CONTINUE);
820
821#else
822 /*
823 * No authentication support built-in, return 'continue'...
824 */
825
826 return (HTTP_STATUS_CONTINUE);
827#endif /* HAVE_LIBPAM */
828}
829
830
831/*
832 * 'clean_jobs()' - Clean out old (completed) jobs.
833 */
834
835static void
836clean_jobs(ippeve_printer_t *printer) /* I - Printer */
837{
838 ippeve_job_t *job; /* Current job */
839 time_t cleantime; /* Clean time */
840
841
842 if (cupsArrayCount(printer->jobs) == 0)
843 return;
844
845 cleantime = time(NULL) - 60;
846
847 _cupsRWLockWrite(&(printer->rwlock));
848 for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs);
849 job;
850 job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
851 if (job->completed && job->completed < cleantime)
852 {
853 cupsArrayRemove(printer->jobs, job);
854 delete_job(job);
855 }
856 else
857 break;
858 _cupsRWUnlock(&(printer->rwlock));
859}
860
861
862/*
863 * 'compare_jobs()' - Compare two jobs.
864 */
865
866static int /* O - Result of comparison */
867compare_jobs(ippeve_job_t *a, /* I - First job */
868 ippeve_job_t *b) /* I - Second job */
869{
870 return (b->id - a->id);
871}
872
873
874/*
875 * 'copy_attributes()' - Copy attributes from one request to another.
876 */
877
878static void
879copy_attributes(ipp_t *to, /* I - Destination request */
880 ipp_t *from, /* I - Source request */
881 cups_array_t *ra, /* I - Requested attributes */
882 ipp_tag_t group_tag, /* I - Group to copy */
883 int quickcopy) /* I - Do a quick copy? */
884{
885 ippeve_filter_t filter; /* Filter data */
886
887
888 filter.ra = ra;
889 filter.group_tag = group_tag;
890
891 ippCopyAttributes(to, from, quickcopy, (ipp_copycb_t)filter_cb, &filter);
892}
893
894
895/*
896 * 'copy_job_attrs()' - Copy job attributes to the response.
897 */
898
899static void
900copy_job_attributes(
901 ippeve_client_t *client, /* I - Client */
902 ippeve_job_t *job, /* I - Job */
903 cups_array_t *ra) /* I - requested-attributes */
904{
905 copy_attributes(client->response, job->attrs, ra, IPP_TAG_JOB, 0);
906
907 if (!ra || cupsArrayFind(ra, "date-time-at-completed"))
908 {
909 if (job->completed)
910 ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-completed", ippTimeToDate(job->completed));
911 else
912 ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-completed");
913 }
914
915 if (!ra || cupsArrayFind(ra, "date-time-at-processing"))
916 {
917 if (job->processing)
918 ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-processing", ippTimeToDate(job->processing));
919 else
920 ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-processing");
921 }
922
923 if (!ra || cupsArrayFind(ra, "job-impressions"))
924 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions", job->impressions);
925
926 if (!ra || cupsArrayFind(ra, "job-impressions-completed"))
927 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions-completed", job->impcompleted);
928
929 if (!ra || cupsArrayFind(ra, "job-printer-up-time"))
930 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-printer-up-time", (int)(time(NULL) - client->printer->start_time));
931
932 if (!ra || cupsArrayFind(ra, "job-state"))
933 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state);
934
935 if (!ra || cupsArrayFind(ra, "job-state-message"))
936 {
937 if (job->message)
938 {
939 ippAddString(client->response, IPP_TAG_JOB, IPP_TAG_TEXT, "job-state-message", NULL, job->message);
940 }
941 else
942 {
943 switch (job->state)
944 {
945 case IPP_JSTATE_PENDING :
946 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job pending.");
947 break;
948
949 case IPP_JSTATE_HELD :
950 if (job->fd >= 0)
951 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job incoming.");
952 else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
953 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job held.");
954 else
955 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job created.");
956 break;
957
958 case IPP_JSTATE_PROCESSING :
959 if (job->cancel)
960 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceling.");
961 else
962 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job printing.");
963 break;
964
965 case IPP_JSTATE_STOPPED :
966 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job stopped.");
967 break;
968
969 case IPP_JSTATE_CANCELED :
970 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceled.");
971 break;
972
973 case IPP_JSTATE_ABORTED :
974 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job aborted.");
975 break;
976
977 case IPP_JSTATE_COMPLETED :
978 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job completed.");
979 break;
980 }
981 }
982 }
983
984 if (!ra || cupsArrayFind(ra, "job-state-reasons"))
985 {
986 switch (job->state)
987 {
988 case IPP_JSTATE_PENDING :
989 ippAddString(client->response, IPP_TAG_JOB,
990 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
991 NULL, "none");
992 break;
993
994 case IPP_JSTATE_HELD :
995 if (job->fd >= 0)
996 ippAddString(client->response, IPP_TAG_JOB,
997 IPP_CONST_TAG(IPP_TAG_KEYWORD),
998 "job-state-reasons", NULL, "job-incoming");
999 else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
1000 ippAddString(client->response, IPP_TAG_JOB,
1001 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1002 "job-state-reasons", NULL, "job-hold-until-specified");
1003 else
1004 ippAddString(client->response, IPP_TAG_JOB,
1005 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1006 "job-state-reasons", NULL, "job-data-insufficient");
1007 break;
1008
1009 case IPP_JSTATE_PROCESSING :
1010 if (job->cancel)
1011 ippAddString(client->response, IPP_TAG_JOB,
1012 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1013 "job-state-reasons", NULL, "processing-to-stop-point");
1014 else
1015 ippAddString(client->response, IPP_TAG_JOB,
1016 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1017 "job-state-reasons", NULL, "job-printing");
1018 break;
1019
1020 case IPP_JSTATE_STOPPED :
1021 ippAddString(client->response, IPP_TAG_JOB,
1022 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1023 NULL, "job-stopped");
1024 break;
1025
1026 case IPP_JSTATE_CANCELED :
1027 ippAddString(client->response, IPP_TAG_JOB,
1028 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1029 NULL, "job-canceled-by-user");
1030 break;
1031
1032 case IPP_JSTATE_ABORTED :
1033 ippAddString(client->response, IPP_TAG_JOB,
1034 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1035 NULL, "aborted-by-system");
1036 break;
1037
1038 case IPP_JSTATE_COMPLETED :
1039 ippAddString(client->response, IPP_TAG_JOB,
1040 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1041 NULL, "job-completed-successfully");
1042 break;
1043 }
1044 }
1045
1046 if (!ra || cupsArrayFind(ra, "time-at-completed"))
1047 ippAddInteger(client->response, IPP_TAG_JOB,
1048 job->completed ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
1049 "time-at-completed", (int)(job->completed - client->printer->start_time));
1050
1051 if (!ra || cupsArrayFind(ra, "time-at-processing"))
1052 ippAddInteger(client->response, IPP_TAG_JOB,
1053 job->processing ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
1054 "time-at-processing", (int)(job->processing - client->printer->start_time));
1055}
1056
1057
1058/*
1059 * 'create_client()' - Accept a new network connection and create a client
1060 * object.
1061 */
1062
1063static ippeve_client_t * /* O - Client */
1064create_client(ippeve_printer_t *printer, /* I - Printer */
1065 int sock) /* I - Listen socket */
1066{
1067 ippeve_client_t *client; /* Client */
1068
1069
1070 if ((client = calloc(1, sizeof(ippeve_client_t))) == NULL)
1071 {
1072 perror("Unable to allocate memory for client");
1073 return (NULL);
1074 }
1075
1076 client->printer = printer;
1077
1078 /*
1079 * Accept the client and get the remote address...
1080 */
1081
1082 if ((client->http = httpAcceptConnection(sock, 1)) == NULL)
1083 {
1084 perror("Unable to accept client connection");
1085
1086 free(client);
1087
1088 return (NULL);
1089 }
1090
1091 httpGetHostname(client->http, client->hostname, sizeof(client->hostname));
1092
1093 if (Verbosity)
1094 fprintf(stderr, "Accepted connection from %s\n", client->hostname);
1095
1096 return (client);
1097}
1098
1099
1100/*
1101 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
1102 * request.
1103 */
1104
1105static ippeve_job_t * /* O - Job */
1106create_job(ippeve_client_t *client) /* I - Client */
1107{
1108 ippeve_job_t *job; /* Job */
1109 ipp_attribute_t *attr; /* Job attribute */
1110 char uri[1024], /* job-uri value */
1111 uuid[64]; /* job-uuid value */
1112
1113
1114 _cupsRWLockWrite(&(client->printer->rwlock));
1115 if (client->printer->active_job &&
1116 client->printer->active_job->state < IPP_JSTATE_CANCELED)
1117 {
1118 /*
1119 * Only accept a single job at a time...
1120 */
1121
1122 _cupsRWUnlock(&(client->printer->rwlock));
1123 return (NULL);
1124 }
1125
1126 /*
1127 * Allocate and initialize the job object...
1128 */
1129
1130 if ((job = calloc(1, sizeof(ippeve_job_t))) == NULL)
1131 {
1132 perror("Unable to allocate memory for job");
1133 return (NULL);
1134 }
1135
1136 job->printer = client->printer;
1137 job->attrs = ippNew();
1138 job->state = IPP_JSTATE_HELD;
1139 job->fd = -1;
1140
1141 /*
1142 * Copy all of the job attributes...
1143 */
1144
1145 copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
1146
1147 /*
1148 * Get the requesting-user-name, document format, and priority...
1149 */
1150
1151 if ((attr = ippFindAttribute(client->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
1152 job->username = ippGetString(attr, 0, NULL);
1153 else
1154 job->username = "anonymous";
1155
1156 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name", NULL, job->username);
1157
1158 if (ippGetOperation(client->request) != IPP_OP_CREATE_JOB)
1159 {
1160 if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
1161 job->format = ippGetString(attr, 0, NULL);
1162 else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
1163 job->format = ippGetString(attr, 0, NULL);
1164 else
1165 job->format = "application/octet-stream";
1166 }
1167
1168 if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_INTEGER)) != NULL)
1169 job->impressions = ippGetInteger(attr, 0);
1170
1171 if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_NAME)) != NULL)
1172 job->name = ippGetString(attr, 0, NULL);
1173
1174 /*
1175 * Add job description attributes and add to the jobs array...
1176 */
1177
1178 job->id = client->printer->next_job_id ++;
1179
1180 snprintf(uri, sizeof(uri), "%s/%d", client->printer->uri, job->id);
1181 httpAssembleUUID(client->printer->hostname, client->printer->port, client->printer->name, job->id, uuid, sizeof(uuid));
1182
1183 ippAddDate(job->attrs, IPP_TAG_JOB, "date-time-at-creation", ippTimeToDate(time(&job->created)));
1184 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1185 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, uri);
1186 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL, uuid);
1187 if ((attr = ippFindAttribute(client->request, "printer-uri", IPP_TAG_URI)) != NULL)
1188 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, ippGetString(attr, 0, NULL));
1189 else
1190 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, client->printer->uri);
1191 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", (int)(job->created - client->printer->start_time));
1192
1193 cupsArrayAdd(client->printer->jobs, job);
1194 client->printer->active_job = job;
1195
1196 _cupsRWUnlock(&(client->printer->rwlock));
1197
1198 return (job);
1199}
1200
1201
1202/*
1203 * 'create_job_file()' - Create a file for the document in a job.
1204 */
1205
1206static int /* O - File descriptor or -1 on error */
1207create_job_file(
1208 ippeve_job_t *job, /* I - Job */
1209 char *fname, /* I - Filename buffer */
1210 size_t fnamesize, /* I - Size of filename buffer */
1211 const char *directory, /* I - Directory to store in */
1212 const char *ext) /* I - Extension (`NULL` for default) */
1213{
1214 char name[256], /* "Safe" filename */
1215 *nameptr; /* Pointer into filename */
1216 const char *job_name; /* job-name value */
1217
1218
1219 /*
1220 * Make a name from the job-name attribute...
1221 */
1222
1223 if ((job_name = ippGetString(ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME), 0, NULL)) == NULL)
1224 job_name = "untitled";
1225
1226 for (nameptr = name; *job_name && nameptr < (name + sizeof(name) - 1); job_name ++)
1227 {
1228 if (isalnum(*job_name & 255) || *job_name == '-')
1229 {
1230 *nameptr++ = (char)tolower(*job_name & 255);
1231 }
1232 else
1233 {
1234 *nameptr++ = '_';
1235
1236 while (job_name[1] && !isalnum(job_name[1] & 255) && job_name[1] != '-')
1237 job_name ++;
1238 }
1239 }
1240
1241 *nameptr = '\0';
1242
1243 /*
1244 * Figure out the extension...
1245 */
1246
1247 if (!ext)
1248 {
1249 if (!strcasecmp(job->format, "image/jpeg"))
1250 ext = "jpg";
1251 else if (!strcasecmp(job->format, "image/png"))
1252 ext = "png";
1253 else if (!strcasecmp(job->format, "image/pwg-raster"))
1254 ext = "pwg";
1255 else if (!strcasecmp(job->format, "image/urf"))
1256 ext = "urf";
1257 else if (!strcasecmp(job->format, "application/pdf"))
1258 ext = "pdf";
1259 else if (!strcasecmp(job->format, "application/postscript"))
1260 ext = "ps";
1261 else if (!strcasecmp(job->format, "application/vnd.hp-pcl"))
1262 ext = "pcl";
1263 else
1264 ext = "dat";
1265 }
1266
1267 /*
1268 * Create a filename with the job-id, job-name, and document-format (extension)...
1269 */
1270
1271 snprintf(fname, fnamesize, "%s/%d-%s.%s", directory, job->id, name, ext);
1272
1273 return (open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0666));
1274}
1275
1276
1277/*
1278 * 'create_listener()' - Create a listener socket.
1279 */
1280
1281static int /* O - Listener socket or -1 on error */
1282create_listener(const char *name, /* I - Host name (`NULL` for any address) */
1283 int port, /* I - Port number */
1284 int family) /* I - Address family */
1285{
1286 int sock; /* Listener socket */
1287 http_addrlist_t *addrlist; /* Listen address */
1288 char service[255]; /* Service port */
1289
1290
1291 snprintf(service, sizeof(service), "%d", port);
1292 if ((addrlist = httpAddrGetList(name, family, service)) == NULL)
1293 return (-1);
1294
1295 sock = httpAddrListen(&(addrlist->addr), port);
1296
1297 httpAddrFreeList(addrlist);
1298
1299 return (sock);
1300}
1301
1302
1303/*
1304 * 'create_media_col()' - Create a media-col value.
1305 */
1306
1307static ipp_t * /* O - media-col collection */
1308create_media_col(const char *media, /* I - Media name */
1309 const char *source, /* I - Media source, if any */
1310 const char *type, /* I - Media type, if any */
1311 int width, /* I - x-dimension in 2540ths */
1312 int length, /* I - y-dimension in 2540ths */
1313 int bottom, /* I - Bottom margin in 2540ths */
1314 int left, /* I - Left margin in 2540ths */
1315 int right, /* I - Right margin in 2540ths */
1316 int top) /* I - Top margin in 2540ths */
1317{
1318 ipp_t *media_col = ippNew(), /* media-col value */
1319 *media_size = create_media_size(width, length);
1320 /* media-size value */
1321 char media_key[256]; /* media-key value */
1322 const char *media_key_suffix = ""; /* media-key suffix */
1323
1324
1325 if (bottom == 0 && left == 0 && right == 0 && top == 0)
1326 media_key_suffix = "_borderless";
1327
1328 if (type && source)
1329 snprintf(media_key, sizeof(media_key), "%s_%s_%s%s", media, source, type, media_key_suffix);
1330 else if (type)
1331 snprintf(media_key, sizeof(media_key), "%s__%s%s", media, type, media_key_suffix);
1332 else if (source)
1333 snprintf(media_key, sizeof(media_key), "%s_%s%s", media, source, media_key_suffix);
1334 else
1335 snprintf(media_key, sizeof(media_key), "%s%s", media, media_key_suffix);
1336
1337 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-key", NULL, media_key);
1338 ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
1339 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-size-name", NULL, media);
1340 if (bottom >= 0)
1341 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin", bottom);
1342 if (left >= 0)
1343 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin", left);
1344 if (right >= 0)
1345 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin", right);
1346 if (top >= 0)
1347 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin", top);
1348 if (source)
1349 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source", NULL, source);
1350 if (type)
1351 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type", NULL, type);
1352
1353 ippDelete(media_size);
1354
1355 return (media_col);
1356}
1357
1358
1359/*
1360 * 'create_media_size()' - Create a media-size value.
1361 */
1362
1363static ipp_t * /* O - media-col collection */
1364create_media_size(int width, /* I - x-dimension in 2540ths */
1365 int length) /* I - y-dimension in 2540ths */
1366{
1367 ipp_t *media_size = ippNew(); /* media-size value */
1368
1369
1370 ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension", width);
1371 ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension", length);
1372
1373 return (media_size);
1374}
1375
1376
1377/*
1378 * 'create_printer()' - Create, register, and listen for connections to a
1379 * printer object.
1380 */
1381
1382static ippeve_printer_t * /* O - Printer */
1383create_printer(
1384 const char *servername, /* I - Server hostname (NULL for default) */
1385 int serverport, /* I - Server port */
1386 const char *name, /* I - printer-name */
1387 const char *location, /* I - printer-location */
1388 const char *icon, /* I - printer-icons */
1389 cups_array_t *docformats, /* I - document-format-supported */
1390 const char *subtypes, /* I - Bonjour service subtype(s) */
1391 const char *directory, /* I - Spool directory */
1392 const char *command, /* I - Command to run on job files, if any */
1393 const char *device_uri, /* I - Output device, if any */
1394 const char *output_format, /* I - Output format, if any */
1395 ipp_t *attrs) /* I - Capability attributes */
1396{
1397 ippeve_printer_t *printer; /* Printer */
1398 int i; /* Looping var */
1399#ifndef _WIN32
1400 char path[1024]; /* Full path to command */
1401#endif /* !_WIN32 */
1402 char uri[1024], /* Printer URI */
1403#ifdef HAVE_SSL
1404 securi[1024], /* Secure printer URI */
1405 *uris[2], /* All URIs */
1406#endif /* HAVE_SSL */
1407 icons[1024], /* printer-icons URI */
1408 adminurl[1024], /* printer-more-info URI */
1409 supplyurl[1024],/* printer-supply-info-uri URI */
1410 uuid[128]; /* printer-uuid */
1411 int k_supported; /* Maximum file size supported */
1412 int num_formats; /* Number of supported document formats */
1413 const char *formats[100], /* Supported document formats */
1414 *format; /* Current format */
1415 int num_sup_attrs; /* Number of supported attributes */
1416 const char *sup_attrs[100];/* Supported attributes */
1417 char xxx_supported[256];
1418 /* Name of -supported attribute */
1419 _cups_globals_t *cg = _cupsGlobals();
1420 /* Global path values */
1421#ifdef HAVE_STATVFS
1422 struct statvfs spoolinfo; /* FS info for spool directory */
1423 double spoolsize; /* FS size */
1424#elif defined(HAVE_STATFS)
1425 struct statfs spoolinfo; /* FS info for spool directory */
1426 double spoolsize; /* FS size */
1427#endif /* HAVE_STATVFS */
1428 static const char * const versions[] =/* ipp-versions-supported values */
1429 {
1430 "1.1",
1431 "2.0"
1432 };
1433 static const char * const features[] =/* ipp-features-supported values */
1434 {
1435 "ipp-everywhere"
1436 };
1437 static const int ops[] = /* operations-supported values */
1438 {
1439 IPP_OP_PRINT_JOB,
1440 IPP_OP_PRINT_URI,
1441 IPP_OP_VALIDATE_JOB,
1442 IPP_OP_CREATE_JOB,
1443 IPP_OP_SEND_DOCUMENT,
1444 IPP_OP_SEND_URI,
1445 IPP_OP_CANCEL_JOB,
1446 IPP_OP_GET_JOB_ATTRIBUTES,
1447 IPP_OP_GET_JOBS,
1448 IPP_OP_GET_PRINTER_ATTRIBUTES,
1449 IPP_OP_CANCEL_MY_JOBS,
1450 IPP_OP_CLOSE_JOB,
1451 IPP_OP_IDENTIFY_PRINTER
1452 };
1453 static const char * const charsets[] =/* charset-supported values */
1454 {
1455 "us-ascii",
1456 "utf-8"
1457 };
1458 static const char * const compressions[] =/* compression-supported values */
1459 {
1460#ifdef HAVE_LIBZ
1461 "deflate",
1462 "gzip",
1463#endif /* HAVE_LIBZ */
1464 "none"
1465 };
1466 static const char * const identify_actions[] =
1467 {
1468 "display",
1469 "sound"
1470 };
1471 static const char * const job_creation[] =
1472 { /* job-creation-attributes-supported values */
1473 "copies",
1474 "document-access",
1475 "document-charset",
1476 "document-format",
1477 "document-message",
1478 "document-metadata",
1479 "document-name",
1480 "document-natural-language",
1481 "document-password",
1482 "finishings",
1483 "finishings-col",
1484 "ipp-attribute-fidelity",
1485 "job-account-id",
1486 "job-account-type",
1487 "job-accouunting-sheets",
1488 "job-accounting-user-id",
1489 "job-authorization-uri",
1490 "job-error-action",
1491 "job-error-sheet",
1492 "job-hold-until",
1493 "job-hold-until-time",
1494 "job-mandatory-attributes",
1495 "job-message-to-operator",
1496 "job-name",
1497 "job-pages-per-set",
1498 "job-password",
1499 "job-password-encryption",
1500 "job-phone-number",
1501 "job-priority",
1502 "job-recipient-name",
1503 "job-resource-ids",
1504 "job-sheet-message",
1505 "job-sheets",
1506 "job-sheets-col",
1507 "media",
1508 "media-col",
1509 "multiple-document-handling",
1510 "number-up",
1511 "orientation-requested",
1512 "output-bin",
1513 "output-device",
1514 "overrides",
1515 "page-delivery",
1516 "page-ranges",
1517 "presentation-direction-number-up",
1518 "print-color-mode",
1519 "print-content-optimize",
1520 "print-quality",
1521 "print-rendering-intent",
1522 "print-scaling",
1523 "printer-resolution",
1524 "proof-print",
1525 "separator-sheets",
1526 "sides",
1527 "x-image-position",
1528 "x-image-shift",
1529 "x-side1-image-shift",
1530 "x-side2-image-shift",
1531 "y-image-position",
1532 "y-image-shift",
1533 "y-side1-image-shift",
1534 "y-side2-image-shift"
1535 };
1536 static const char * const media_col_supported[] =
1537 { /* media-col-supported values */
1538 "media-bottom-margin",
1539 "media-left-margin",
1540 "media-right-margin",
1541 "media-size",
1542 "media-size-name",
1543 "media-source",
1544 "media-top-margin",
1545 "media-type"
1546 };
1547 static const char * const multiple_document_handling[] =
1548 { /* multiple-document-handling-supported values */
1549 "separate-documents-uncollated-copies",
1550 "separate-documents-collated-copies"
1551 };
1552 static const char * const reference_uri_schemes_supported[] =
1553 { /* reference-uri-schemes-supported */
1554 "file",
1555 "ftp",
1556 "http"
1557#ifdef HAVE_SSL
1558 , "https"
1559#endif /* HAVE_SSL */
1560 };
1561#ifdef HAVE_SSL
1562 static const char * const uri_authentication_supported[] =
1563 { /* uri-authentication-supported values */
1564 "none",
1565 "none"
1566 };
1567 static const char * const uri_authentication_basic[] =
1568 { /* uri-authentication-supported values with authentication */
1569 "basic",
1570 "basic"
1571 };
1572 static const char * const uri_security_supported[] =
1573 { /* uri-security-supported values */
1574 "none",
1575 "tls"
1576 };
1577#endif /* HAVE_SSL */
1578 static const char * const which_jobs[] =
1579 { /* which-jobs-supported values */
1580 "completed",
1581 "not-completed",
1582 "aborted",
1583 "all",
1584 "canceled",
1585 "pending",
1586 "pending-held",
1587 "processing",
1588 "processing-stopped"
1589 };
1590
1591
1592#ifndef _WIN32
1593 /*
1594 * If a command was specified, make sure it exists and is executable...
1595 */
1596
1597 if (command)
1598 {
1599 if (*command == '/' || !strncmp(command, "./", 2))
1600 {
1601 if (access(command, X_OK))
1602 {
1603 _cupsLangPrintf(stderr, _("Unable to execute command \"%s\": %s"), command, strerror(errno));
1604 return (NULL);
1605 }
1606 }
1607 else
1608 {
1609 snprintf(path, sizeof(path), "%s/command/%s", cg->cups_serverbin, command);
1610
1611 if (access(command, X_OK))
1612 {
1613 _cupsLangPrintf(stderr, _("Unable to execute command \"%s\": %s"), command, strerror(errno));
1614 return (NULL);
1615 }
1616
1617 command = path;
1618 }
1619 }
1620#endif /* !_WIN32 */
1621
1622 /*
1623 * Allocate memory for the printer...
1624 */
1625
1626 if ((printer = calloc(1, sizeof(ippeve_printer_t))) == NULL)
1627 {
1628 _cupsLangPrintError(NULL, _("Unable to allocate memory for printer"));
1629 return (NULL);
1630 }
1631
1632 printer->ipv4 = -1;
1633 printer->ipv6 = -1;
1634 printer->name = strdup(name);
1635 printer->dnssd_name = strdup(name);
1636 printer->command = command ? strdup(command) : NULL;
1637 printer->device_uri = device_uri ? strdup(device_uri) : NULL;
1638 printer->output_format = output_format ? strdup(output_format) : NULL;
1639 printer->directory = strdup(directory);
1640 printer->icon = icon ? strdup(icon) : NULL;
1641 printer->port = serverport;
1642 printer->start_time = time(NULL);
1643 printer->config_time = printer->start_time;
1644 printer->state = IPP_PSTATE_IDLE;
1645 printer->state_reasons = IPPEVE_PREASON_NONE;
1646 printer->state_time = printer->start_time;
1647 printer->jobs = cupsArrayNew((cups_array_func_t)compare_jobs, NULL);
1648 printer->next_job_id = 1;
1649
1650 if (servername)
1651 {
1652 printer->hostname = strdup(servername);
1653 }
1654 else
1655 {
1656 char temp[1024]; /* Temporary string */
1657
1658 printer->hostname = strdup(httpGetHostname(NULL, temp, sizeof(temp)));
1659 }
1660
1661 _cupsRWInit(&(printer->rwlock));
1662
1663 /*
1664 * Create the listener sockets...
1665 */
1666
1667 if ((printer->ipv4 = create_listener(servername, printer->port, AF_INET)) < 0)
1668 {
1669 perror("Unable to create IPv4 listener");
1670 goto bad_printer;
1671 }
1672
1673 if ((printer->ipv6 = create_listener(servername, printer->port, AF_INET6)) < 0)
1674 {
1675 perror("Unable to create IPv6 listener");
1676 goto bad_printer;
1677 }
1678
1679 /*
1680 * Prepare URI values for the printer attributes...
1681 */
1682
1683 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, printer->hostname, printer->port, "/ipp/print");
1684 printer->uri = strdup(uri);
1685 printer->urilen = strlen(uri);
1686
1687#ifdef HAVE_SSL
1688 httpAssembleURI(HTTP_URI_CODING_ALL, securi, sizeof(securi), "ipps", NULL, printer->hostname, printer->port, "/ipp/print");
1689#endif /* HAVE_SSL */
1690
1691 httpAssembleURI(HTTP_URI_CODING_ALL, icons, sizeof(icons), WEB_SCHEME, NULL, printer->hostname, printer->port, "/icon.png");
1692 httpAssembleURI(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), WEB_SCHEME, NULL, printer->hostname, printer->port, "/");
1693 httpAssembleURI(HTTP_URI_CODING_ALL, supplyurl, sizeof(supplyurl), WEB_SCHEME, NULL, printer->hostname, printer->port, "/supplies");
1694 httpAssembleUUID(printer->hostname, serverport, name, 0, uuid, sizeof(uuid));
1695
1696 if (Verbosity)
1697 {
1698 fprintf(stderr, "printer-more-info=\"%s\"\n", adminurl);
1699 fprintf(stderr, "printer-supply-info-uri=\"%s\"\n", supplyurl);
1700#ifdef HAVE_SSL
1701 fprintf(stderr, "printer-uri=\"%s\",\"%s\"\n", uri, securi);
1702#else
1703 fprintf(stderr, "printer-uri=\"%s\"\n", uri);
1704#endif /* HAVE_SSL */
1705 }
1706
1707 /*
1708 * Get the maximum spool size based on the size of the filesystem used for
1709 * the spool directory. If the host OS doesn't support the statfs call
1710 * or the filesystem is larger than 2TiB, always report INT_MAX.
1711 */
1712
1713#ifdef HAVE_STATVFS
1714 if (statvfs(printer->directory, &spoolinfo))
1715 k_supported = INT_MAX;
1716 else if ((spoolsize = (double)spoolinfo.f_frsize *
1717 spoolinfo.f_blocks / 1024) > INT_MAX)
1718 k_supported = INT_MAX;
1719 else
1720 k_supported = (int)spoolsize;
1721
1722#elif defined(HAVE_STATFS)
1723 if (statfs(printer->directory, &spoolinfo))
1724 k_supported = INT_MAX;
1725 else if ((spoolsize = (double)spoolinfo.f_bsize *
1726 spoolinfo.f_blocks / 1024) > INT_MAX)
1727 k_supported = INT_MAX;
1728 else
1729 k_supported = (int)spoolsize;
1730
1731#else
1732 k_supported = INT_MAX;
1733#endif /* HAVE_STATVFS */
1734
1735 /*
1736 * Assemble the final list of document formats...
1737 */
1738
1739 if (!cupsArrayFind(docformats, (void *)"application/octet-stream"))
1740 cupsArrayAdd(docformats, (void *)"application/octet-stream");
1741
1742 for (num_formats = 0, format = (const char *)cupsArrayFirst(docformats); format && num_formats < (int)(sizeof(formats) / sizeof(formats[0])); format = (const char *)cupsArrayNext(docformats))
1743 formats[num_formats ++] = format;
1744
1745 /*
1746 * Get the list of attributes that can be used when creating a job...
1747 */
1748
1749 num_sup_attrs = 0;
1750 sup_attrs[num_sup_attrs ++] = "document-access";
1751 sup_attrs[num_sup_attrs ++] = "document-charset";
1752 sup_attrs[num_sup_attrs ++] = "document-format";
1753 sup_attrs[num_sup_attrs ++] = "document-message";
1754 sup_attrs[num_sup_attrs ++] = "document-metadata";
1755 sup_attrs[num_sup_attrs ++] = "document-name";
1756 sup_attrs[num_sup_attrs ++] = "document-natural-language";
1757 sup_attrs[num_sup_attrs ++] = "ipp-attribute-fidelity";
1758 sup_attrs[num_sup_attrs ++] = "job-name";
1759 sup_attrs[num_sup_attrs ++] = "job-priority";
1760
1761 for (i = 0; i < (int)(sizeof(job_creation) / sizeof(job_creation[0])) && num_sup_attrs < (int)(sizeof(sup_attrs) / sizeof(sup_attrs[0])); i ++)
1762 {
1763 snprintf(xxx_supported, sizeof(xxx_supported), "%s-supported", job_creation[i]);
1764 if (ippFindAttribute(attrs, xxx_supported, IPP_TAG_ZERO))
1765 sup_attrs[num_sup_attrs ++] = job_creation[i];
1766 }
1767
1768 /*
1769 * Fill out the rest of the printer attributes.
1770 */
1771
1772 printer->attrs = attrs;
1773
1774 /* charset-configured */
1775 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-configured", NULL, "utf-8");
1776
1777 /* charset-supported */
1778 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-supported", sizeof(charsets) / sizeof(charsets[0]), NULL, charsets);
1779
1780 /* compression-supported */
1781 if (!ippFindAttribute(printer->attrs, "compression-supported", IPP_TAG_ZERO))
1782 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "compression-supported", (int)(sizeof(compressions) / sizeof(compressions[0])), NULL, compressions);
1783
1784 /* document-format-default */
1785 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_MIMETYPE), "document-format-default", NULL, "application/octet-stream");
1786
1787 /* document-format-supported */
1788 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE, "document-format-supported", num_formats, NULL, formats);
1789
1790 /* generated-natural-language-supported */
1791 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "generated-natural-language-supported", NULL, "en");
1792
1793 /* identify-actions-default */
1794 ippAddString (printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-default", NULL, "sound");
1795
1796 /* identify-actions-supported */
1797 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-supported", sizeof(identify_actions) / sizeof(identify_actions[0]), NULL, identify_actions);
1798
1799 /* ipp-features-supported */
1800 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-features-supported", sizeof(features) / sizeof(features[0]), NULL, features);
1801
1802 /* ipp-versions-supported */
1803 if (MaxVersion == 11)
1804 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", NULL, "1.1");
1805 else
1806 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", (int)(sizeof(versions) / sizeof(versions[0])), NULL, versions);
1807
1808 /* job-creation-attributes-supported */
1809 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-creation-attributes-supported", num_sup_attrs, NULL, sup_attrs);
1810
1811 /* job-ids-supported */
1812 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-ids-supported", 1);
1813
1814 /* job-k-octets-supported */
1815 ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0, k_supported);
1816
1817 /* job-priority-default */
1818 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-default", 50);
1819
1820 /* job-priority-supported */
1821 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-supported", 1);
1822
1823 /* job-sheets-default */
1824 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-default", NULL, "none");
1825
1826 /* job-sheets-supported */
1827 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-supported", NULL, "none");
1828
1829 /* media-col-supported */
1830 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-col-supported", (int)(sizeof(media_col_supported) / sizeof(media_col_supported[0])), NULL, media_col_supported);
1831
1832 /* multiple-document-handling-supported */
1833 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "multiple-document-handling-supported", sizeof(multiple_document_handling) / sizeof(multiple_document_handling[0]), NULL, multiple_document_handling);
1834
1835 /* multiple-document-jobs-supported */
1836 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "multiple-document-jobs-supported", 0);
1837
1838 /* multiple-operation-time-out */
1839 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "multiple-operation-time-out", 60);
1840
1841 /* multiple-operation-time-out-action */
1842 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "multiple-operation-time-out-action", NULL, "abort-job");
1843
1844 /* natural-language-configured */
1845 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "natural-language-configured", NULL, "en");
1846
1847 /* operations-supported */
1848 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
1849
1850 /* pdl-override-supported */
1851 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pdl-override-supported", NULL, "attempted");
1852
1853 /* preferred-attributes-supported */
1854 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "preferred-attributes-supported", 0);
1855
1856 /* printer-get-attributes-supported */
1857 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-get-attributes-supported", NULL, "document-format");
1858
1859 /* printer-geo-location */
1860 ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_UNKNOWN, "printer-geo-location");
1861
1862 /* printer-icons */
1863 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons", NULL, icons);
1864
1865 /* printer-is-accepting-jobs */
1866 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
1867
1868 /* printer-info */
1869 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL, name);
1870
1871 /* printer-location */
1872 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", NULL, location);
1873
1874 /* printer-more-info */
1875 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info", NULL, adminurl);
1876
1877 /* printer-name */
1878 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL, name);
1879
1880 /* printer-organization */
1881 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organization", NULL, "");
1882
1883 /* printer-organizational-unit */
1884 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organizational-unit", NULL, "");
1885
1886 /* printer-supply-info-uri */
1887 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-supply-info-uri", NULL, supplyurl);
1888
1889 /* printer-uri-supported */
1890#ifdef HAVE_SSL
1891 uris[0] = uri;
1892 uris[1] = securi;
1893
1894 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", 2, NULL, (const char **)uris);
1895
1896#else
1897 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri);
1898#endif /* HAVE_SSL */
1899
1900 /* printer-uuid */
1901 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uuid", NULL, uuid);
1902
1903 /* reference-uri-scheme-supported */
1904 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_URISCHEME), "reference-uri-schemes-supported", (int)(sizeof(reference_uri_schemes_supported) / sizeof(reference_uri_schemes_supported[0])), NULL, reference_uri_schemes_supported);
1905
1906 /* uri-authentication-supported */
1907#ifdef HAVE_SSL
1908 if (PAMService)
1909 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", 2, NULL, uri_authentication_basic);
1910 else
1911 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", 2, NULL, uri_authentication_supported);
1912#else
1913 if (PAMService)
1914 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", NULL, "basic");
1915 else
1916 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", NULL, "none");
1917#endif /* HAVE_SSL */
1918
1919 /* uri-security-supported */
1920#ifdef HAVE_SSL
1921 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", 2, NULL, uri_security_supported);
1922#else
1923 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", NULL, "none");
1924#endif /* HAVE_SSL */
1925
1926 /* which-jobs-supported */
1927 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "which-jobs-supported", sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs);
1928
1929 debug_attributes("Printer", printer->attrs, 0);
1930
1931 /*
1932 * Register the printer with Bonjour...
1933 */
1934
1935 if (!register_printer(printer, subtypes))
1936 goto bad_printer;
1937
1938 /*
1939 * Return it!
1940 */
1941
1942 return (printer);
1943
1944
1945 /*
1946 * If we get here we were unable to create the printer...
1947 */
1948
1949 bad_printer:
1950
1951 delete_printer(printer);
1952
1953 return (NULL);
1954}
1955
1956
1957/*
1958 * 'debug_attributes()' - Print attributes in a request or response.
1959 */
1960
1961static void
1962debug_attributes(const char *title, /* I - Title */
1963 ipp_t *ipp, /* I - Request/response */
1964 int type) /* I - 0 = object, 1 = request, 2 = response */
1965{
1966 ipp_tag_t group_tag; /* Current group */
1967 ipp_attribute_t *attr; /* Current attribute */
1968 char buffer[2048]; /* String buffer for value */
1969 int major, minor; /* Version */
1970
1971
1972 if (Verbosity <= 1)
1973 return;
1974
1975 fprintf(stderr, "%s:\n", title);
1976 major = ippGetVersion(ipp, &minor);
1977 fprintf(stderr, " version=%d.%d\n", major, minor);
1978 if (type == 1)
1979 fprintf(stderr, " operation-id=%s(%04x)\n",
1980 ippOpString(ippGetOperation(ipp)), ippGetOperation(ipp));
1981 else if (type == 2)
1982 fprintf(stderr, " status-code=%s(%04x)\n",
1983 ippErrorString(ippGetStatusCode(ipp)), ippGetStatusCode(ipp));
1984 fprintf(stderr, " request-id=%d\n\n", ippGetRequestId(ipp));
1985
1986 for (attr = ippFirstAttribute(ipp), group_tag = IPP_TAG_ZERO;
1987 attr;
1988 attr = ippNextAttribute(ipp))
1989 {
1990 if (ippGetGroupTag(attr) != group_tag)
1991 {
1992 group_tag = ippGetGroupTag(attr);
1993 fprintf(stderr, " %s\n", ippTagString(group_tag));
1994 }
1995
1996 if (ippGetName(attr))
1997 {
1998 ippAttributeString(attr, buffer, sizeof(buffer));
1999 fprintf(stderr, " %s (%s%s) %s\n", ippGetName(attr),
2000 ippGetCount(attr) > 1 ? "1setOf " : "",
2001 ippTagString(ippGetValueTag(attr)), buffer);
2002 }
2003 }
2004}
2005
2006
2007/*
2008 * 'delete_client()' - Close the socket and free all memory used by a client
2009 * object.
2010 */
2011
2012static void
2013delete_client(ippeve_client_t *client) /* I - Client */
2014{
2015 if (Verbosity)
2016 fprintf(stderr, "Closing connection from %s\n", client->hostname);
2017
2018 /*
2019 * Flush pending writes before closing...
2020 */
2021
2022 httpFlushWrite(client->http);
2023
2024 /*
2025 * Free memory...
2026 */
2027
2028 httpClose(client->http);
2029
2030 ippDelete(client->request);
2031 ippDelete(client->response);
2032
2033 free(client);
2034}
2035
2036
2037/*
2038 * 'delete_job()' - Remove from the printer and free all memory used by a job
2039 * object.
2040 */
2041
2042static void
2043delete_job(ippeve_job_t *job) /* I - Job */
2044{
2045 if (Verbosity)
2046 fprintf(stderr, "[Job %d] Removing job from history.\n", job->id);
2047
2048 ippDelete(job->attrs);
2049
2050 if (job->message)
2051 free(job->message);
2052
2053 if (job->filename)
2054 {
2055 if (!KeepFiles)
2056 unlink(job->filename);
2057
2058 free(job->filename);
2059 }
2060
2061 free(job);
2062}
2063
2064
2065/*
2066 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2067 * used by a printer object.
2068 */
2069
2070static void
2071delete_printer(ippeve_printer_t *printer) /* I - Printer */
2072{
2073 if (printer->ipv4 >= 0)
2074 close(printer->ipv4);
2075
2076 if (printer->ipv6 >= 0)
2077 close(printer->ipv6);
2078
2079#if HAVE_DNSSD
2080 if (printer->printer_ref)
2081 DNSServiceRefDeallocate(printer->printer_ref);
2082 if (printer->ipp_ref)
2083 DNSServiceRefDeallocate(printer->ipp_ref);
2084 if (printer->ipps_ref)
2085 DNSServiceRefDeallocate(printer->ipps_ref);
2086 if (printer->http_ref)
2087 DNSServiceRefDeallocate(printer->http_ref);
2088#elif defined(HAVE_AVAHI)
2089 avahi_threaded_poll_lock(DNSSDMaster);
2090
2091 if (printer->printer_ref)
2092 avahi_entry_group_free(printer->printer_ref);
2093 if (printer->ipp_ref)
2094 avahi_entry_group_free(printer->ipp_ref);
2095 if (printer->ipps_ref)
2096 avahi_entry_group_free(printer->ipps_ref);
2097 if (printer->http_ref)
2098 avahi_entry_group_free(printer->http_ref);
2099
2100 avahi_threaded_poll_unlock(DNSSDMaster);
2101#endif /* HAVE_DNSSD */
2102
2103 if (printer->dnssd_name)
2104 free(printer->dnssd_name);
2105 if (printer->name)
2106 free(printer->name);
2107 if (printer->icon)
2108 free(printer->icon);
2109 if (printer->command)
2110 free(printer->command);
2111 if (printer->device_uri)
2112 free(printer->device_uri);
2113#if !CUPS_LITE
2114 if (printer->ppdfile)
2115 free(printer->ppdfile);
2116#endif /* !CUPS_LITE */
2117 if (printer->directory)
2118 free(printer->directory);
2119 if (printer->hostname)
2120 free(printer->hostname);
2121 if (printer->uri)
2122 free(printer->uri);
2123
2124 ippDelete(printer->attrs);
2125 cupsArrayDelete(printer->jobs);
2126
2127 free(printer);
2128}
2129
2130
2131#ifdef HAVE_DNSSD
2132/*
2133 * 'dnssd_callback()' - Handle Bonjour registration events.
2134 */
2135
2136static void DNSSD_API
2137dnssd_callback(
2138 DNSServiceRef sdRef, /* I - Service reference */
2139 DNSServiceFlags flags, /* I - Status flags */
2140 DNSServiceErrorType errorCode, /* I - Error, if any */
2141 const char *name, /* I - Service name */
2142 const char *regtype, /* I - Service type */
2143 const char *domain, /* I - Domain for service */
2144 ippeve_printer_t *printer) /* I - Printer */
2145{
2146 (void)sdRef;
2147 (void)flags;
2148 (void)domain;
2149
2150 if (errorCode)
2151 {
2152 fprintf(stderr, "DNSServiceRegister for %s failed with error %d.\n", regtype, (int)errorCode);
2153 return;
2154 }
2155 else if (strcasecmp(name, printer->dnssd_name))
2156 {
2157 if (Verbosity)
2158 fprintf(stderr, "Now using DNS-SD service name \"%s\".\n", name);
2159
2160 /* No lock needed since only the main thread accesses/changes this */
2161 free(printer->dnssd_name);
2162 printer->dnssd_name = strdup(name);
2163 }
2164}
2165
2166
2167#elif defined(HAVE_AVAHI)
2168/*
2169 * 'dnssd_callback()' - Handle Bonjour registration events.
2170 */
2171
2172static void
2173dnssd_callback(
2174 AvahiEntryGroup *srv, /* I - Service */
2175 AvahiEntryGroupState state, /* I - Registration state */
2176 void *context) /* I - Printer */
2177{
2178 (void)srv;
2179 (void)state;
2180 (void)context;
2181}
2182
2183
2184/*
2185 * 'dnssd_client_cb()' - Client callback for Avahi.
2186 *
2187 * Called whenever the client or server state changes...
2188 */
2189
2190static void
2191dnssd_client_cb(
2192 AvahiClient *c, /* I - Client */
2193 AvahiClientState state, /* I - Current state */
2194 void *userdata) /* I - User data (unused) */
2195{
2196 (void)userdata;
2197
2198 if (!c)
2199 return;
2200
2201 switch (state)
2202 {
2203 default :
2204 fprintf(stderr, "Ignored Avahi state %d.\n", state);
2205 break;
2206
2207 case AVAHI_CLIENT_FAILURE:
2208 if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
2209 {
2210 fputs("Avahi server crashed, exiting.\n", stderr);
2211 exit(1);
2212 }
2213 break;
2214 }
2215}
2216#endif /* HAVE_DNSSD */
2217
2218
2219/*
2220 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2221 */
2222
2223static void
2224dnssd_init(void)
2225{
2226#ifdef HAVE_DNSSD
2227 if (DNSServiceCreateConnection(&DNSSDMaster) != kDNSServiceErr_NoError)
2228 {
2229 fputs("Error: Unable to initialize Bonjour.\n", stderr);
2230 exit(1);
2231 }
2232
2233#elif defined(HAVE_AVAHI)
2234 int error; /* Error code, if any */
2235
2236 if ((DNSSDMaster = avahi_threaded_poll_new()) == NULL)
2237 {
2238 fputs("Error: Unable to initialize Bonjour.\n", stderr);
2239 exit(1);
2240 }
2241
2242 if ((DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssd_client_cb, NULL, &error)) == NULL)
2243 {
2244 fputs("Error: Unable to initialize Bonjour.\n", stderr);
2245 exit(1);
2246 }
2247
2248 avahi_threaded_poll_start(DNSSDMaster);
2249#endif /* HAVE_DNSSD */
2250}
2251
2252
2253/*
2254 * 'filter_cb()' - Filter printer attributes based on the requested array.
2255 */
2256
2257static int /* O - 1 to copy, 0 to ignore */
2258filter_cb(ippeve_filter_t *filter, /* I - Filter parameters */
2259 ipp_t *dst, /* I - Destination (unused) */
2260 ipp_attribute_t *attr) /* I - Source attribute */
2261{
2262 /*
2263 * Filter attributes as needed...
2264 */
2265
2266#ifndef _WIN32 /* Avoid MS compiler bug */
2267 (void)dst;
2268#endif /* !_WIN32 */
2269
2270 ipp_tag_t group = ippGetGroupTag(attr);
2271 const char *name = ippGetName(attr);
2272
2273 if ((filter->group_tag != IPP_TAG_ZERO && group != filter->group_tag && group != IPP_TAG_ZERO) || !name || (!strcmp(name, "media-col-database") && !cupsArrayFind(filter->ra, (void *)name)))
2274 return (0);
2275
2276 return (!filter->ra || cupsArrayFind(filter->ra, (void *)name) != NULL);
2277}
2278
2279
2280/*
2281 * 'find_job()' - Find a job specified in a request.
2282 */
2283
2284static ippeve_job_t * /* O - Job or NULL */
2285find_job(ippeve_client_t *client) /* I - Client */
2286{
2287 ipp_attribute_t *attr; /* job-id or job-uri attribute */
2288 ippeve_job_t key, /* Job search key */
2289 *job; /* Matching job, if any */
2290
2291
2292 if ((attr = ippFindAttribute(client->request, "job-uri", IPP_TAG_URI)) != NULL)
2293 {
2294 const char *uri = ippGetString(attr, 0, NULL);
2295
2296 if (!strncmp(uri, client->printer->uri, client->printer->urilen) &&
2297 uri[client->printer->urilen] == '/')
2298 key.id = atoi(uri + client->printer->urilen + 1);
2299 else
2300 return (NULL);
2301 }
2302 else if ((attr = ippFindAttribute(client->request, "job-id", IPP_TAG_INTEGER)) != NULL)
2303 key.id = ippGetInteger(attr, 0);
2304
2305 _cupsRWLockRead(&(client->printer->rwlock));
2306 job = (ippeve_job_t *)cupsArrayFind(client->printer->jobs, &key);
2307 _cupsRWUnlock(&(client->printer->rwlock));
2308
2309 return (job);
2310}
2311
2312
2313/*
2314 * 'finish_document()' - Finish receiving a document file and start processing.
2315 */
2316
2317static void
2318finish_document_data(
2319 ippeve_client_t *client, /* I - Client */
2320 ippeve_job_t *job) /* I - Job */
2321{
2322 char filename[1024], /* Filename buffer */
2323 buffer[4096]; /* Copy buffer */
2324 ssize_t bytes; /* Bytes read */
2325 cups_array_t *ra; /* Attributes to send in response */
2326 _cups_thread_t t; /* Thread */
2327
2328
2329 /*
2330 * Create a file for the request data...
2331 *
2332 * TODO: Update code to support piping large raster data to the print command.
2333 */
2334
2335 if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0)
2336 {
2337 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno));
2338
2339 goto abort_job;
2340 }
2341
2342 if (Verbosity)
2343 fprintf(stderr, "Created job file \"%s\", format \"%s\".\n", filename, job->format);
2344
2345 while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
2346 {
2347 if (write(job->fd, buffer, (size_t)bytes) < bytes)
2348 {
2349 int error = errno; /* Write error */
2350
2351 close(job->fd);
2352 job->fd = -1;
2353
2354 unlink(filename);
2355
2356 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2357
2358 goto abort_job;
2359 }
2360 }
2361
2362 if (bytes < 0)
2363 {
2364 /*
2365 * Got an error while reading the print data, so abort this job.
2366 */
2367
2368 close(job->fd);
2369 job->fd = -1;
2370
2371 unlink(filename);
2372
2373 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to read print file.");
2374
2375 goto abort_job;
2376 }
2377
2378 if (close(job->fd))
2379 {
2380 int error = errno; /* Write error */
2381
2382 job->fd = -1;
2383
2384 unlink(filename);
2385
2386 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2387
2388 goto abort_job;
2389 }
2390
2391 job->fd = -1;
2392 job->filename = strdup(filename);
2393 job->state = IPP_JSTATE_PENDING;
2394
2395 /*
2396 * Process the job...
2397 */
2398
2399 t = _cupsThreadCreate((_cups_thread_func_t)process_job, job);
2400
2401 if (t)
2402 {
2403 _cupsThreadDetach(t);
2404 }
2405 else
2406 {
2407 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
2408 goto abort_job;
2409 }
2410
2411 /*
2412 * Return the job info...
2413 */
2414
2415 respond_ipp(client, IPP_STATUS_OK, NULL);
2416
2417 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2418 cupsArrayAdd(ra, "job-id");
2419 cupsArrayAdd(ra, "job-state");
2420 cupsArrayAdd(ra, "job-state-message");
2421 cupsArrayAdd(ra, "job-state-reasons");
2422 cupsArrayAdd(ra, "job-uri");
2423
2424 copy_job_attributes(client, job, ra);
2425 cupsArrayDelete(ra);
2426 return;
2427
2428 /*
2429 * If we get here we had to abort the job...
2430 */
2431
2432 abort_job:
2433
2434 job->state = IPP_JSTATE_ABORTED;
2435 job->completed = time(NULL);
2436
2437 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2438 cupsArrayAdd(ra, "job-id");
2439 cupsArrayAdd(ra, "job-state");
2440 cupsArrayAdd(ra, "job-state-reasons");
2441 cupsArrayAdd(ra, "job-uri");
2442
2443 copy_job_attributes(client, job, ra);
2444 cupsArrayDelete(ra);
2445}
2446
2447
2448/*
2449 * 'finish_uri()' - Finish fetching a document URI and start processing.
2450 */
2451
2452static void
2453finish_document_uri(
2454 ippeve_client_t *client, /* I - Client */
2455 ippeve_job_t *job) /* I - Job */
2456{
2457 ipp_attribute_t *uri; /* document-uri */
2458 char scheme[256], /* URI scheme */
2459 userpass[256], /* Username and password info */
2460 hostname[256], /* Hostname */
2461 resource[1024]; /* Resource path */
2462 int port; /* Port number */
2463 http_uri_status_t uri_status; /* URI decode status */
2464 http_encryption_t encryption; /* Encryption to use, if any */
2465 http_t *http; /* Connection for http/https URIs */
2466 http_status_t status; /* Access status for http/https URIs */
2467 int infile; /* Input file for local file URIs */
2468 char filename[1024], /* Filename buffer */
2469 buffer[4096]; /* Copy buffer */
2470 ssize_t bytes; /* Bytes read */
2471 ipp_attribute_t *attr; /* Current attribute */
2472 cups_array_t *ra; /* Attributes to send in response */
2473
2474
2475 /*
2476 * Do we have a file to print?
2477 */
2478
2479 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
2480 {
2481 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Unexpected document data following request.");
2482
2483 goto abort_job;
2484 }
2485
2486 /*
2487 * Do we have a document URI?
2488 */
2489
2490 if ((uri = ippFindAttribute(client->request, "document-uri", IPP_TAG_URI)) == NULL)
2491 {
2492 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
2493
2494 goto abort_job;
2495 }
2496
2497 if (ippGetCount(uri) != 1)
2498 {
2499 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Too many document-uri values.");
2500
2501 goto abort_job;
2502 }
2503
2504 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
2505 scheme, sizeof(scheme), userpass,
2506 sizeof(userpass), hostname, sizeof(hostname),
2507 &port, resource, sizeof(resource));
2508 if (uri_status < HTTP_URI_STATUS_OK)
2509 {
2510 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s", httpURIStatusString(uri_status));
2511
2512 goto abort_job;
2513 }
2514
2515 if (strcmp(scheme, "file") &&
2516#ifdef HAVE_SSL
2517 strcmp(scheme, "https") &&
2518#endif /* HAVE_SSL */
2519 strcmp(scheme, "http"))
2520 {
2521 respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME, "URI scheme \"%s\" not supported.", scheme);
2522
2523 goto abort_job;
2524 }
2525
2526 if (!strcmp(scheme, "file") && access(resource, R_OK))
2527 {
2528 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno));
2529
2530 goto abort_job;
2531 }
2532
2533 /*
2534 * Get the document format for the job...
2535 */
2536
2537 _cupsRWLockWrite(&(client->printer->rwlock));
2538
2539 if ((attr = ippFindAttribute(job->attrs, "document-format", IPP_TAG_MIMETYPE)) != NULL)
2540 job->format = ippGetString(attr, 0, NULL);
2541 else
2542 job->format = "application/octet-stream";
2543
2544 /*
2545 * Create a file for the request data...
2546 */
2547
2548 if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0)
2549 {
2550 _cupsRWUnlock(&(client->printer->rwlock));
2551
2552 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno));
2553
2554 goto abort_job;
2555 }
2556
2557 _cupsRWUnlock(&(client->printer->rwlock));
2558
2559 if (!strcmp(scheme, "file"))
2560 {
2561 if ((infile = open(resource, O_RDONLY)) < 0)
2562 {
2563 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno));
2564
2565 goto abort_job;
2566 }
2567
2568 do
2569 {
2570 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
2571 (errno == EAGAIN || errno == EINTR))
2572 {
2573 bytes = 1;
2574 }
2575 else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes)
2576 {
2577 int error = errno; /* Write error */
2578
2579 close(job->fd);
2580 job->fd = -1;
2581
2582 unlink(filename);
2583 close(infile);
2584
2585 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2586
2587 goto abort_job;
2588 }
2589 }
2590 while (bytes > 0);
2591
2592 close(infile);
2593 }
2594 else
2595 {
2596#ifdef HAVE_SSL
2597 if (port == 443 || !strcmp(scheme, "https"))
2598 encryption = HTTP_ENCRYPTION_ALWAYS;
2599 else
2600#endif /* HAVE_SSL */
2601 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
2602
2603 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL)
2604 {
2605 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to connect to %s: %s", hostname, cupsLastErrorString());
2606
2607 close(job->fd);
2608 job->fd = -1;
2609
2610 unlink(filename);
2611
2612 goto abort_job;
2613 }
2614
2615 httpClearFields(http);
2616 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
2617 if (httpGet(http, resource))
2618 {
2619 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", strerror(errno));
2620
2621 close(job->fd);
2622 job->fd = -1;
2623
2624 unlink(filename);
2625 httpClose(http);
2626
2627 goto abort_job;
2628 }
2629
2630 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
2631
2632 if (status != HTTP_STATUS_OK)
2633 {
2634 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", httpStatus(status));
2635
2636 close(job->fd);
2637 job->fd = -1;
2638
2639 unlink(filename);
2640 httpClose(http);
2641
2642 goto abort_job;
2643 }
2644
2645 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
2646 {
2647 if (write(job->fd, buffer, (size_t)bytes) < bytes)
2648 {
2649 int error = errno; /* Write error */
2650
2651 close(job->fd);
2652 job->fd = -1;
2653
2654 unlink(filename);
2655 httpClose(http);
2656
2657 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
2658 "Unable to write print file: %s", strerror(error));
2659
2660 goto abort_job;
2661 }
2662 }
2663
2664 httpClose(http);
2665 }
2666
2667 if (close(job->fd))
2668 {
2669 int error = errno; /* Write error */
2670
2671 job->fd = -1;
2672
2673 unlink(filename);
2674
2675 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2676
2677 goto abort_job;
2678 }
2679
2680 _cupsRWLockWrite(&(client->printer->rwlock));
2681
2682 job->fd = -1;
2683 job->filename = strdup(filename);
2684 job->state = IPP_JSTATE_PENDING;
2685
2686 _cupsRWUnlock(&(client->printer->rwlock));
2687
2688 /*
2689 * Process the job...
2690 */
2691
2692 process_job(job);
2693
2694 /*
2695 * Return the job info...
2696 */
2697
2698 respond_ipp(client, IPP_STATUS_OK, NULL);
2699
2700 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2701 cupsArrayAdd(ra, "job-id");
2702 cupsArrayAdd(ra, "job-state");
2703 cupsArrayAdd(ra, "job-state-reasons");
2704 cupsArrayAdd(ra, "job-uri");
2705
2706 copy_job_attributes(client, job, ra);
2707 cupsArrayDelete(ra);
2708 return;
2709
2710 /*
2711 * If we get here we had to abort the job...
2712 */
2713
2714 abort_job:
2715
2716 job->state = IPP_JSTATE_ABORTED;
2717 job->completed = time(NULL);
2718
2719 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2720 cupsArrayAdd(ra, "job-id");
2721 cupsArrayAdd(ra, "job-state");
2722 cupsArrayAdd(ra, "job-state-reasons");
2723 cupsArrayAdd(ra, "job-uri");
2724
2725 copy_job_attributes(client, job, ra);
2726 cupsArrayDelete(ra);
2727}
2728
2729
2730/*
2731 * 'html_escape()' - Write a HTML-safe string.
2732 */
2733
2734static void
2735html_escape(ippeve_client_t *client, /* I - Client */
2736 const char *s, /* I - String to write */
2737 size_t slen) /* I - Number of characters to write */
2738{
2739 const char *start, /* Start of segment */
2740 *end; /* End of string */
2741
2742
2743 start = s;
2744 end = s + (slen > 0 ? slen : strlen(s));
2745
2746 while (*s && s < end)
2747 {
2748 if (*s == '&' || *s == '<')
2749 {
2750 if (s > start)
2751 httpWrite2(client->http, start, (size_t)(s - start));
2752
2753 if (*s == '&')
2754 httpWrite2(client->http, "&amp;", 5);
2755 else
2756 httpWrite2(client->http, "&lt;", 4);
2757
2758 start = s + 1;
2759 }
2760
2761 s ++;
2762 }
2763
2764 if (s > start)
2765 httpWrite2(client->http, start, (size_t)(s - start));
2766}
2767
2768
2769/*
2770 * 'html_footer()' - Show the web interface footer.
2771 *
2772 * This function also writes the trailing 0-length chunk.
2773 */
2774
2775static void
2776html_footer(ippeve_client_t *client) /* I - Client */
2777{
2778 html_printf(client,
2779 "</div>\n"
2780 "</body>\n"
2781 "</html>\n");
2782 httpWrite2(client->http, "", 0);
2783}
2784
2785
2786/*
2787 * 'html_header()' - Show the web interface header and title.
2788 */
2789
2790static void
2791html_header(ippeve_client_t *client, /* I - Client */
2792 const char *title, /* I - Title */
2793 int refresh) /* I - Refresh timer, if any */
2794{
2795 html_printf(client,
2796 "<!doctype html>\n"
2797 "<html>\n"
2798 "<head>\n"
2799 "<title>%s</title>\n"
2800 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2801 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2802 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n", title);
2803 if (refresh > 0)
2804 html_printf(client, "<meta http-equiv=\"refresh\" content=\"%d\">\n", refresh);
2805 html_printf(client,
2806 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2807 "<style>\n"
2808 "body { font-family: sans-serif; margin: 0; }\n"
2809 "div.body { padding: 0px 10px 10px; }\n"
2810 "span.badge { background: #090; border-radius: 5px; color: #fff; padding: 5px 10px; }\n"
2811 "span.bar { box-shadow: 0px 1px 5px #333; font-size: 75%%; }\n"
2812 "table.form { border-collapse: collapse; margin-left: auto; margin-right: auto; margin-top: 10px; width: auto; }\n"
2813 "table.form td, table.form th { padding: 5px 2px; }\n"
2814 "table.form td.meter { border-right: solid 1px #ccc; padding: 0px; width: 400px; }\n"
2815 "table.form th { text-align: right; }\n"
2816 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2817 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2818 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2819 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2820 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2821 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2822 "table.nav td { margin: 0; text-align: center; }\n"
2823 "td.nav a, td.nav a:active, td.nav a:hover, td.nav a:hover:link, td.nav a:hover:link:visited, td.nav a:link, td.nav a:link:visited, td.nav a:visited { background: inherit; color: inherit; font-size: 80%%; text-decoration: none; }\n"
2824 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2825 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2826 "td.nav:hover { background: #666; color: #fff; }\n"
2827 "td.nav:active { background: #000; color: #ff0; }\n"
2828 "</style>\n"
2829 "</head>\n"
2830 "<body>\n"
2831 "<table class=\"nav\"><tr>"
2832 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2833 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2834 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2835 "</tr></table>\n"
2836 "<div class=\"body\">\n", !strcmp(client->uri, "/") ? " sel" : "", !strcmp(client->uri, "/supplies") ? " sel" : "", !strcmp(client->uri, "/media") ? " sel" : "");
2837}
2838
2839
2840/*
2841 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2842 */
2843
2844static void
2845html_printf(ippeve_client_t *client, /* I - Client */
2846 const char *format, /* I - Printf-style format string */
2847 ...) /* I - Additional arguments as needed */
2848{
2849 va_list ap; /* Pointer to arguments */
2850 const char *start; /* Start of string */
2851 char size, /* Size character (h, l, L) */
2852 type; /* Format type character */
2853 int width, /* Width of field */
2854 prec; /* Number of characters of precision */
2855 char tformat[100], /* Temporary format string for sprintf() */
2856 *tptr, /* Pointer into temporary format */
2857 temp[1024]; /* Buffer for formatted numbers */
2858 char *s; /* Pointer to string */
2859
2860
2861 /*
2862 * Loop through the format string, formatting as needed...
2863 */
2864
2865 va_start(ap, format);
2866 start = format;
2867
2868 while (*format)
2869 {
2870 if (*format == '%')
2871 {
2872 if (format > start)
2873 httpWrite2(client->http, start, (size_t)(format - start));
2874
2875 tptr = tformat;
2876 *tptr++ = *format++;
2877
2878 if (*format == '%')
2879 {
2880 httpWrite2(client->http, "%", 1);
2881 format ++;
2882 start = format;
2883 continue;
2884 }
2885 else if (strchr(" -+#\'", *format))
2886 *tptr++ = *format++;
2887
2888 if (*format == '*')
2889 {
2890 /*
2891 * Get width from argument...
2892 */
2893
2894 format ++;
2895 width = va_arg(ap, int);
2896
2897 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
2898 tptr += strlen(tptr);
2899 }
2900 else
2901 {
2902 width = 0;
2903
2904 while (isdigit(*format & 255))
2905 {
2906 if (tptr < (tformat + sizeof(tformat) - 1))
2907 *tptr++ = *format;
2908
2909 width = width * 10 + *format++ - '0';
2910 }
2911 }
2912
2913 if (*format == '.')
2914 {
2915 if (tptr < (tformat + sizeof(tformat) - 1))
2916 *tptr++ = *format;
2917
2918 format ++;
2919
2920 if (*format == '*')
2921 {
2922 /*
2923 * Get precision from argument...
2924 */
2925
2926 format ++;
2927 prec = va_arg(ap, int);
2928
2929 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
2930 tptr += strlen(tptr);
2931 }
2932 else
2933 {
2934 prec = 0;
2935
2936 while (isdigit(*format & 255))
2937 {
2938 if (tptr < (tformat + sizeof(tformat) - 1))
2939 *tptr++ = *format;
2940
2941 prec = prec * 10 + *format++ - '0';
2942 }
2943 }
2944 }
2945
2946 if (*format == 'l' && format[1] == 'l')
2947 {
2948 size = 'L';
2949
2950 if (tptr < (tformat + sizeof(tformat) - 2))
2951 {
2952 *tptr++ = 'l';
2953 *tptr++ = 'l';
2954 }
2955
2956 format += 2;
2957 }
2958 else if (*format == 'h' || *format == 'l' || *format == 'L')
2959 {
2960 if (tptr < (tformat + sizeof(tformat) - 1))
2961 *tptr++ = *format;
2962
2963 size = *format++;
2964 }
2965 else
2966 size = 0;
2967
2968
2969 if (!*format)
2970 {
2971 start = format;
2972 break;
2973 }
2974
2975 if (tptr < (tformat + sizeof(tformat) - 1))
2976 *tptr++ = *format;
2977
2978 type = *format++;
2979 *tptr = '\0';
2980 start = format;
2981
2982 switch (type)
2983 {
2984 case 'E' : /* Floating point formats */
2985 case 'G' :
2986 case 'e' :
2987 case 'f' :
2988 case 'g' :
2989 if ((size_t)(width + 2) > sizeof(temp))
2990 break;
2991
2992 sprintf(temp, tformat, va_arg(ap, double));
2993
2994 httpWrite2(client->http, temp, strlen(temp));
2995 break;
2996
2997 case 'B' : /* Integer formats */
2998 case 'X' :
2999 case 'b' :
3000 case 'd' :
3001 case 'i' :
3002 case 'o' :
3003 case 'u' :
3004 case 'x' :
3005 if ((size_t)(width + 2) > sizeof(temp))
3006 break;
3007
3008# ifdef HAVE_LONG_LONG
3009 if (size == 'L')
3010 sprintf(temp, tformat, va_arg(ap, long long));
3011 else
3012# endif /* HAVE_LONG_LONG */
3013 if (size == 'l')
3014 sprintf(temp, tformat, va_arg(ap, long));
3015 else
3016 sprintf(temp, tformat, va_arg(ap, int));
3017
3018 httpWrite2(client->http, temp, strlen(temp));
3019 break;
3020
3021 case 'p' : /* Pointer value */
3022 if ((size_t)(width + 2) > sizeof(temp))
3023 break;
3024
3025 sprintf(temp, tformat, va_arg(ap, void *));
3026
3027 httpWrite2(client->http, temp, strlen(temp));
3028 break;
3029
3030 case 'c' : /* Character or character array */
3031 if (width <= 1)
3032 {
3033 temp[0] = (char)va_arg(ap, int);
3034 temp[1] = '\0';
3035 html_escape(client, temp, 1);
3036 }
3037 else
3038 html_escape(client, va_arg(ap, char *), (size_t)width);
3039 break;
3040
3041 case 's' : /* String */
3042 if ((s = va_arg(ap, char *)) == NULL)
3043 s = "(null)";
3044
3045 html_escape(client, s, strlen(s));
3046 break;
3047 }
3048 }
3049 else
3050 format ++;
3051 }
3052
3053 if (format > start)
3054 httpWrite2(client->http, start, (size_t)(format - start));
3055
3056 va_end(ap);
3057}
3058
3059
3060/*
3061 * 'ipp_cancel_job()' - Cancel a job.
3062 */
3063
3064static void
3065ipp_cancel_job(ippeve_client_t *client) /* I - Client */
3066{
3067 ippeve_job_t *job; /* Job information */
3068
3069
3070 /*
3071 * Get the job...
3072 */
3073
3074 if ((job = find_job(client)) == NULL)
3075 {
3076 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3077 return;
3078 }
3079
3080 /*
3081 * See if the job is already completed, canceled, or aborted; if so,
3082 * we can't cancel...
3083 */
3084
3085 switch (job->state)
3086 {
3087 case IPP_JSTATE_CANCELED :
3088 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3089 "Job #%d is already canceled - can\'t cancel.", job->id);
3090 break;
3091
3092 case IPP_JSTATE_ABORTED :
3093 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3094 "Job #%d is already aborted - can\'t cancel.", job->id);
3095 break;
3096
3097 case IPP_JSTATE_COMPLETED :
3098 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3099 "Job #%d is already completed - can\'t cancel.", job->id);
3100 break;
3101
3102 default :
3103 /*
3104 * Cancel the job...
3105 */
3106
3107 _cupsRWLockWrite(&(client->printer->rwlock));
3108
3109 if (job->state == IPP_JSTATE_PROCESSING ||
3110 (job->state == IPP_JSTATE_HELD && job->fd >= 0))
3111 job->cancel = 1;
3112 else
3113 {
3114 job->state = IPP_JSTATE_CANCELED;
3115 job->completed = time(NULL);
3116 }
3117
3118 _cupsRWUnlock(&(client->printer->rwlock));
3119
3120 respond_ipp(client, IPP_STATUS_OK, NULL);
3121 break;
3122 }
3123}
3124
3125
3126/*
3127 * 'ipp_close_job()' - Close an open job.
3128 */
3129
3130static void
3131ipp_close_job(ippeve_client_t *client) /* I - Client */
3132{
3133 ippeve_job_t *job; /* Job information */
3134
3135
3136 /*
3137 * Get the job...
3138 */
3139
3140 if ((job = find_job(client)) == NULL)
3141 {
3142 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3143 return;
3144 }
3145
3146 /*
3147 * See if the job is already completed, canceled, or aborted; if so,
3148 * we can't cancel...
3149 */
3150
3151 switch (job->state)
3152 {
3153 case IPP_JSTATE_CANCELED :
3154 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3155 "Job #%d is canceled - can\'t close.", job->id);
3156 break;
3157
3158 case IPP_JSTATE_ABORTED :
3159 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3160 "Job #%d is aborted - can\'t close.", job->id);
3161 break;
3162
3163 case IPP_JSTATE_COMPLETED :
3164 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3165 "Job #%d is completed - can\'t close.", job->id);
3166 break;
3167
3168 case IPP_JSTATE_PROCESSING :
3169 case IPP_JSTATE_STOPPED :
3170 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3171 "Job #%d is already closed.", job->id);
3172 break;
3173
3174 default :
3175 respond_ipp(client, IPP_STATUS_OK, NULL);
3176 break;
3177 }
3178}
3179
3180
3181/*
3182 * 'ipp_create_job()' - Create a job object.
3183 */
3184
3185static void
3186ipp_create_job(ippeve_client_t *client) /* I - Client */
3187{
3188 ippeve_job_t *job; /* New job */
3189 cups_array_t *ra; /* Attributes to send in response */
3190
3191
3192 /*
3193 * Validate print job attributes...
3194 */
3195
3196 if (!valid_job_attributes(client))
3197 {
3198 httpFlush(client->http);
3199 return;
3200 }
3201
3202 /*
3203 * Do we have a file to print?
3204 */
3205
3206 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
3207 {
3208 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3209 "Unexpected document data following request.");
3210 return;
3211 }
3212
3213 /*
3214 * Create the job...
3215 */
3216
3217 if ((job = create_job(client)) == NULL)
3218 {
3219 respond_ipp(client, IPP_STATUS_ERROR_BUSY,
3220 "Currently printing another job.");
3221 return;
3222 }
3223
3224 /*
3225 * Return the job info...
3226 */
3227
3228 respond_ipp(client, IPP_STATUS_OK, NULL);
3229
3230 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3231 cupsArrayAdd(ra, "job-id");
3232 cupsArrayAdd(ra, "job-state");
3233 cupsArrayAdd(ra, "job-state-message");
3234 cupsArrayAdd(ra, "job-state-reasons");
3235 cupsArrayAdd(ra, "job-uri");
3236
3237 copy_job_attributes(client, job, ra);
3238 cupsArrayDelete(ra);
3239}
3240
3241
3242/*
3243 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3244 */
3245
3246static void
3247ipp_get_job_attributes(
3248 ippeve_client_t *client) /* I - Client */
3249{
3250 ippeve_job_t *job; /* Job */
3251 cups_array_t *ra; /* requested-attributes */
3252
3253
3254 if ((job = find_job(client)) == NULL)
3255 {
3256 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job not found.");
3257 return;
3258 }
3259
3260 respond_ipp(client, IPP_STATUS_OK, NULL);
3261
3262 ra = ippCreateRequestedArray(client->request);
3263 copy_job_attributes(client, job, ra);
3264 cupsArrayDelete(ra);
3265}
3266
3267
3268/*
3269 * 'ipp_get_jobs()' - Get a list of job objects.
3270 */
3271
3272static void
3273ipp_get_jobs(ippeve_client_t *client) /* I - Client */
3274{
3275 ipp_attribute_t *attr; /* Current attribute */
3276 const char *which_jobs = NULL;
3277 /* which-jobs values */
3278 int job_comparison; /* Job comparison */
3279 ipp_jstate_t job_state; /* job-state value */
3280 int first_job_id, /* First job ID */
3281 limit, /* Maximum number of jobs to return */
3282 count; /* Number of jobs that match */
3283 const char *username; /* Username */
3284 ippeve_job_t *job; /* Current job pointer */
3285 cups_array_t *ra; /* Requested attributes array */
3286
3287
3288 /*
3289 * See if the "which-jobs" attribute have been specified...
3290 */
3291
3292 if ((attr = ippFindAttribute(client->request, "which-jobs",
3293 IPP_TAG_KEYWORD)) != NULL)
3294 {
3295 which_jobs = ippGetString(attr, 0, NULL);
3296 fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->hostname, which_jobs);
3297 }
3298
3299 if (!which_jobs || !strcmp(which_jobs, "not-completed"))
3300 {
3301 job_comparison = -1;
3302 job_state = IPP_JSTATE_STOPPED;
3303 }
3304 else if (!strcmp(which_jobs, "completed"))
3305 {
3306 job_comparison = 1;
3307 job_state = IPP_JSTATE_CANCELED;
3308 }
3309 else if (!strcmp(which_jobs, "aborted"))
3310 {
3311 job_comparison = 0;
3312 job_state = IPP_JSTATE_ABORTED;
3313 }
3314 else if (!strcmp(which_jobs, "all"))
3315 {
3316 job_comparison = 1;
3317 job_state = IPP_JSTATE_PENDING;
3318 }
3319 else if (!strcmp(which_jobs, "canceled"))
3320 {
3321 job_comparison = 0;
3322 job_state = IPP_JSTATE_CANCELED;
3323 }
3324 else if (!strcmp(which_jobs, "pending"))
3325 {
3326 job_comparison = 0;
3327 job_state = IPP_JSTATE_PENDING;
3328 }
3329 else if (!strcmp(which_jobs, "pending-held"))
3330 {
3331 job_comparison = 0;
3332 job_state = IPP_JSTATE_HELD;
3333 }
3334 else if (!strcmp(which_jobs, "processing"))
3335 {
3336 job_comparison = 0;
3337 job_state = IPP_JSTATE_PROCESSING;
3338 }
3339 else if (!strcmp(which_jobs, "processing-stopped"))
3340 {
3341 job_comparison = 0;
3342 job_state = IPP_JSTATE_STOPPED;
3343 }
3344 else
3345 {
3346 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
3347 "The which-jobs value \"%s\" is not supported.", which_jobs);
3348 ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
3349 "which-jobs", NULL, which_jobs);
3350 return;
3351 }
3352
3353 /*
3354 * See if they want to limit the number of jobs reported...
3355 */
3356
3357 if ((attr = ippFindAttribute(client->request, "limit",
3358 IPP_TAG_INTEGER)) != NULL)
3359 {
3360 limit = ippGetInteger(attr, 0);
3361
3362 fprintf(stderr, "%s Get-Jobs limit=%d", client->hostname, limit);
3363 }
3364 else
3365 limit = 0;
3366
3367 if ((attr = ippFindAttribute(client->request, "first-job-id",
3368 IPP_TAG_INTEGER)) != NULL)
3369 {
3370 first_job_id = ippGetInteger(attr, 0);
3371
3372 fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->hostname, first_job_id);
3373 }
3374 else
3375 first_job_id = 1;
3376
3377 /*
3378 * See if we only want to see jobs for a specific user...
3379 */
3380
3381 username = NULL;
3382
3383 if ((attr = ippFindAttribute(client->request, "my-jobs",
3384 IPP_TAG_BOOLEAN)) != NULL)
3385 {
3386 int my_jobs = ippGetBoolean(attr, 0);
3387
3388 fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->hostname, my_jobs ? "true" : "false");
3389
3390 if (my_jobs)
3391 {
3392 if ((attr = ippFindAttribute(client->request, "requesting-user-name",
3393 IPP_TAG_NAME)) == NULL)
3394 {
3395 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3396 "Need requesting-user-name with my-jobs.");
3397 return;
3398 }
3399
3400 username = ippGetString(attr, 0, NULL);
3401
3402 fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n", client->hostname, username);
3403 }
3404 }
3405
3406 /*
3407 * OK, build a list of jobs for this printer...
3408 */
3409
3410 ra = ippCreateRequestedArray(client->request);
3411
3412 respond_ipp(client, IPP_STATUS_OK, NULL);
3413
3414 _cupsRWLockRead(&(client->printer->rwlock));
3415
3416 for (count = 0, job = (ippeve_job_t *)cupsArrayFirst(client->printer->jobs);
3417 (limit <= 0 || count < limit) && job;
3418 job = (ippeve_job_t *)cupsArrayNext(client->printer->jobs))
3419 {
3420 /*
3421 * Filter out jobs that don't match...
3422 */
3423
3424 if ((job_comparison < 0 && job->state > job_state) ||
3425 (job_comparison == 0 && job->state != job_state) ||
3426 (job_comparison > 0 && job->state < job_state) ||
3427 job->id < first_job_id ||
3428 (username && job->username &&
3429 strcasecmp(username, job->username)))
3430 continue;
3431
3432 if (count > 0)
3433 ippAddSeparator(client->response);
3434
3435 count ++;
3436 copy_job_attributes(client, job, ra);
3437 }
3438
3439 cupsArrayDelete(ra);
3440
3441 _cupsRWUnlock(&(client->printer->rwlock));
3442}
3443
3444
3445/*
3446 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3447 */
3448
3449static void
3450ipp_get_printer_attributes(
3451 ippeve_client_t *client) /* I - Client */
3452{
3453 cups_array_t *ra; /* Requested attributes array */
3454 ippeve_printer_t *printer; /* Printer */
3455
3456
3457 /*
3458 * Send the attributes...
3459 */
3460
3461 ra = ippCreateRequestedArray(client->request);
3462 printer = client->printer;
3463
3464 respond_ipp(client, IPP_STATUS_OK, NULL);
3465
3466 _cupsRWLockRead(&(printer->rwlock));
3467
3468 copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
3469 IPP_TAG_CUPS_CONST);
3470
3471 if (!ra || cupsArrayFind(ra, "printer-config-change-date-time"))
3472 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-config-change-date-time", ippTimeToDate(printer->config_time));
3473
3474 if (!ra || cupsArrayFind(ra, "printer-config-change-time"))
3475 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-config-change-time", (int)(printer->config_time - printer->start_time));
3476
3477 if (!ra || cupsArrayFind(ra, "printer-current-time"))
3478 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(time(NULL)));
3479
3480
3481 if (!ra || cupsArrayFind(ra, "printer-state"))
3482 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state);
3483
3484 if (!ra || cupsArrayFind(ra, "printer-state-change-date-time"))
3485 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-state-change-date-time", ippTimeToDate(printer->state_time));
3486
3487 if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
3488 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-state-change-time", (int)(printer->state_time - printer->start_time));
3489
3490 if (!ra || cupsArrayFind(ra, "printer-state-message"))
3491 {
3492 static const char * const messages[] = { "Idle.", "Printing.", "Stopped." };
3493
3494 ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-state-message", NULL, messages[printer->state - IPP_PSTATE_IDLE]);
3495 }
3496
3497 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
3498 {
3499 if (printer->state_reasons == IPPEVE_PREASON_NONE)
3500 {
3501 ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-state-reasons", NULL, "none");
3502 }
3503 else
3504 {
3505 ipp_attribute_t *attr = NULL; /* printer-state-reasons */
3506 ippeve_preason_t bit; /* Reason bit */
3507 int i; /* Looping var */
3508 char reason[32]; /* Reason string */
3509
3510 for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
3511 {
3512 if (printer->state_reasons & bit)
3513 {
3514 snprintf(reason, sizeof(reason), "%s-%s", ippeve_preason_strings[i], printer->state == IPP_PSTATE_IDLE ? "report" : printer->state == IPP_PSTATE_PROCESSING ? "warning" : "error");
3515 if (attr)
3516 ippSetString(client->response, &attr, ippGetCount(attr), reason);
3517 else
3518 attr = ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "printer-state-reasons", NULL, reason);
3519 }
3520 }
3521 }
3522 }
3523
3524 if (!ra || cupsArrayFind(ra, "printer-up-time"))
3525 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-up-time", (int)(time(NULL) - printer->start_time));
3526
3527 if (!ra || cupsArrayFind(ra, "queued-job-count"))
3528 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "queued-job-count", printer->active_job && printer->active_job->state < IPP_JSTATE_CANCELED);
3529
3530 _cupsRWUnlock(&(printer->rwlock));
3531
3532 cupsArrayDelete(ra);
3533}
3534
3535
3536/*
3537 * 'ipp_identify_printer()' - Beep or display a message.
3538 */
3539
3540static void
3541ipp_identify_printer(
3542 ippeve_client_t *client) /* I - Client */
3543{
3544 ipp_attribute_t *actions, /* identify-actions */
3545 *message; /* message */
3546
3547
3548 actions = ippFindAttribute(client->request, "identify-actions", IPP_TAG_KEYWORD);
3549 message = ippFindAttribute(client->request, "message", IPP_TAG_TEXT);
3550
3551 if (!actions || ippContainsString(actions, "sound"))
3552 {
3553 putchar(0x07);
3554 fflush(stdout);
3555 }
3556
3557 if (ippContainsString(actions, "display"))
3558 printf("IDENTIFY from %s: %s\n", client->hostname, message ? ippGetString(message, 0, NULL) : "No message supplied");
3559
3560 respond_ipp(client, IPP_STATUS_OK, NULL);
3561}
3562
3563
3564/*
3565 * 'ipp_print_job()' - Create a job object with an attached document.
3566 */
3567
3568static void
3569ipp_print_job(ippeve_client_t *client) /* I - Client */
3570{
3571 ippeve_job_t *job; /* New job */
3572
3573
3574 /*
3575 * Validate print job attributes...
3576 */
3577
3578 if (!valid_job_attributes(client))
3579 {
3580 httpFlush(client->http);
3581 return;
3582 }
3583
3584 /*
3585 * Do we have a file to print?
3586 */
3587
3588 if (httpGetState(client->http) == HTTP_STATE_POST_SEND)
3589 {
3590 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No file in request.");
3591 return;
3592 }
3593
3594 /*
3595 * Create the job...
3596 */
3597
3598 if ((job = create_job(client)) == NULL)
3599 {
3600 respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job.");
3601 return;
3602 }
3603
3604 /*
3605 * Then finish getting the document data and process things...
3606 */
3607
3608 finish_document_data(client, job);
3609}
3610
3611
3612/*
3613 * 'ipp_print_uri()' - Create a job object with a referenced document.
3614 */
3615
3616static void
3617ipp_print_uri(ippeve_client_t *client) /* I - Client */
3618{
3619 ippeve_job_t *job; /* New job */
3620
3621
3622 /*
3623 * Validate print job attributes...
3624 */
3625
3626 if (!valid_job_attributes(client))
3627 {
3628 httpFlush(client->http);
3629 return;
3630 }
3631
3632 /*
3633 * Create the job...
3634 */
3635
3636 if ((job = create_job(client)) == NULL)
3637 {
3638 respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job.");
3639 return;
3640 }
3641
3642 /*
3643 * Then finish getting the document data and process things...
3644 */
3645
3646 finish_document_uri(client, job);
3647}
3648
3649
3650/*
3651 * 'ipp_send_document()' - Add an attached document to a job object created with
3652 * Create-Job.
3653 */
3654
3655static void
3656ipp_send_document(
3657 ippeve_client_t *client) /* I - Client */
3658{
3659 ippeve_job_t *job; /* Job information */
3660 ipp_attribute_t *attr; /* Current attribute */
3661
3662
3663 /*
3664 * Get the job...
3665 */
3666
3667 if ((job = find_job(client)) == NULL)
3668 {
3669 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3670 httpFlush(client->http);
3671 return;
3672 }
3673
3674 /*
3675 * See if we already have a document for this job or the job has already
3676 * in a non-pending state...
3677 */
3678
3679 if (job->state > IPP_JSTATE_HELD)
3680 {
3681 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state.");
3682 httpFlush(client->http);
3683 return;
3684 }
3685 else if (job->filename || job->fd >= 0)
3686 {
3687 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported.");
3688 httpFlush(client->http);
3689 return;
3690 }
3691
3692 /*
3693 * Make sure we have the "last-document" operation attribute...
3694 */
3695
3696 if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL)
3697 {
3698 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute.");
3699 httpFlush(client->http);
3700 return;
3701 }
3702 else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION)
3703 {
3704 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group.");
3705 httpFlush(client->http);
3706 return;
3707 }
3708 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 || !ippGetBoolean(attr, 0))
3709 {
3710 respond_unsupported(client, attr);
3711 httpFlush(client->http);
3712 return;
3713 }
3714
3715 /*
3716 * Validate document attributes...
3717 */
3718
3719 if (!valid_doc_attributes(client))
3720 {
3721 httpFlush(client->http);
3722 return;
3723 }
3724
3725 /*
3726 * Then finish getting the document data and process things...
3727 */
3728
3729 _cupsRWLockWrite(&(client->printer->rwlock));
3730
3731 copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
3732
3733 if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
3734 job->format = ippGetString(attr, 0, NULL);
3735 else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
3736 job->format = ippGetString(attr, 0, NULL);
3737 else
3738 job->format = "application/octet-stream";
3739
3740 _cupsRWUnlock(&(client->printer->rwlock));
3741
3742 finish_document_data(client, job);
3743}
3744
3745
3746/*
3747 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3748 * Create-Job.
3749 */
3750
3751static void
3752ipp_send_uri(ippeve_client_t *client) /* I - Client */
3753{
3754 ippeve_job_t *job; /* Job information */
3755 ipp_attribute_t *attr; /* Current attribute */
3756
3757
3758 /*
3759 * Get the job...
3760 */
3761
3762 if ((job = find_job(client)) == NULL)
3763 {
3764 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3765 httpFlush(client->http);
3766 return;
3767 }
3768
3769 /*
3770 * See if we already have a document for this job or the job has already
3771 * in a non-pending state...
3772 */
3773
3774 if (job->state > IPP_JSTATE_HELD)
3775 {
3776 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state.");
3777 httpFlush(client->http);
3778 return;
3779 }
3780 else if (job->filename || job->fd >= 0)
3781 {
3782 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported.");
3783 httpFlush(client->http);
3784 return;
3785 }
3786
3787 if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL)
3788 {
3789 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute.");
3790 httpFlush(client->http);
3791 return;
3792 }
3793 else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION)
3794 {
3795 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group.");
3796 httpFlush(client->http);
3797 return;
3798 }
3799 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 || !ippGetBoolean(attr, 0))
3800 {
3801 respond_unsupported(client, attr);
3802 httpFlush(client->http);
3803 return;
3804 }
3805
3806 /*
3807 * Validate document attributes...
3808 */
3809
3810 if (!valid_doc_attributes(client))
3811 {
3812 httpFlush(client->http);
3813 return;
3814 }
3815
3816 /*
3817 * Then finish getting the document data and process things...
3818 */
3819
3820 _cupsRWLockWrite(&(client->printer->rwlock));
3821
3822 copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
3823
3824 if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
3825 job->format = ippGetString(attr, 0, NULL);
3826 else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
3827 job->format = ippGetString(attr, 0, NULL);
3828 else
3829 job->format = "application/octet-stream";
3830
3831 _cupsRWUnlock(&(client->printer->rwlock));
3832
3833 finish_document_uri(client, job);
3834}
3835
3836
3837/*
3838 * 'ipp_validate_job()' - Validate job creation attributes.
3839 */
3840
3841static void
3842ipp_validate_job(ippeve_client_t *client) /* I - Client */
3843{
3844 if (valid_job_attributes(client))
3845 respond_ipp(client, IPP_STATUS_OK, NULL);
3846}
3847
3848
3849/*
3850 * 'ippserver_attr_cb()' - Determine whether an attribute should be loaded.
3851 */
3852
3853static int /* O - 1 to use, 0 to ignore */
3854ippserver_attr_cb(
3855 _ipp_file_t *f, /* I - IPP file */
3856 void *user_data, /* I - User data pointer (unused) */
3857 const char *attr) /* I - Attribute name */
3858{
3859 int i, /* Current element */
3860 result; /* Result of comparison */
3861 static const char * const ignored[] =
3862 { /* Ignored attributes */
3863 "attributes-charset",
3864 "attributes-natural-language",
3865 "charset-configured",
3866 "charset-supported",
3867 "device-service-count",
3868 "device-uuid",
3869 "document-format-varying-attributes",
3870 "generated-natural-language-supported",
3871 "identify-actions-default",
3872 "identify-actions-supported",
3873 "ipp-features-supported",
3874 "ipp-versions-supproted",
3875 "ippget-event-life",
3876 "job-hold-until-supported",
3877 "job-hold-until-time-supported",
3878 "job-ids-supported",
3879 "job-k-octets-supported",
3880 "job-settable-attributes-supported",
3881 "multiple-document-jobs-supported",
3882 "multiple-operation-time-out",
3883 "multiple-operation-time-out-action",
3884 "natural-language-configured",
3885 "notify-attributes-supported",
3886 "notify-events-default",
3887 "notify-events-supported",
3888 "notify-lease-duration-default",
3889 "notify-lease-duration-supported",
3890 "notify-max-events-supported",
3891 "notify-pull-method-supported",
3892 "operations-supported",
3893 "printer-alert",
3894 "printer-alert-description",
3895 "printer-camera-image-uri",
3896 "printer-charge-info",
3897 "printer-charge-info-uri",
3898 "printer-config-change-date-time",
3899 "printer-config-change-time",
3900 "printer-current-time",
3901 "printer-detailed-status-messages",
3902 "printer-dns-sd-name",
3903 "printer-fax-log-uri",
3904 "printer-get-attributes-supported",
3905 "printer-icons",
3906 "printer-id",
3907 "printer-info",
3908 "printer-is-accepting-jobs",
3909 "printer-message-date-time",
3910 "printer-message-from-operator",
3911 "printer-message-time",
3912 "printer-more-info",
3913 "printer-service-type",
3914 "printer-settable-attributes-supported",
3915 "printer-state",
3916 "printer-state-message",
3917 "printer-state-reasons",
3918 "printer-static-resource-directory-uri",
3919 "printer-static-resource-k-octets-free",
3920 "printer-static-resource-k-octets-supported",
3921 "printer-strings-languages-supported",
3922 "printer-strings-uri",
3923 "printer-supply-info-uri",
3924 "printer-up-time",
3925 "printer-uri-supported",
3926 "printer-xri-supported",
3927 "queued-job-count",
3928 "reference-uri-scheme-supported",
3929 "uri-authentication-supported",
3930 "uri-security-supported",
3931 "which-jobs-supported",
3932 "xri-authentication-supported",
3933 "xri-security-supported",
3934 "xri-uri-scheme-supported"
3935 };
3936
3937
3938 (void)f;
3939 (void)user_data;
3940
3941 for (i = 0, result = 1; i < (int)(sizeof(ignored) / sizeof(ignored[0])); i ++)
3942 {
3943 if ((result = strcmp(attr, ignored[i])) <= 0)
3944 break;
3945 }
3946
3947 return (result != 0);
3948}
3949
3950
3951/*
3952 * 'ippserver_error_cb()' - Log an error message.
3953 */
3954
3955static int /* O - 1 to continue, 0 to stop */
3956ippserver_error_cb(
3957 _ipp_file_t *f, /* I - IPP file data */
3958 void *user_data, /* I - User data pointer (unused) */
3959 const char *error) /* I - Error message */
3960{
3961 (void)f;
3962 (void)user_data;
3963
3964 _cupsLangPrintf(stderr, "%s\n", error);
3965
3966 return (1);
3967}
3968
3969
3970/*
3971 * 'ippserver_token_cb()' - Process ippserver-specific config file tokens.
3972 */
3973
3974static int /* O - 1 to continue, 0 to stop */
3975ippserver_token_cb(
3976 _ipp_file_t *f, /* I - IPP file data */
3977 _ipp_vars_t *vars, /* I - IPP variables */
3978 void *user_data, /* I - User data pointer (unused) */
3979 const char *token) /* I - Current token */
3980{
3981 (void)vars;
3982 (void)user_data;
3983
3984 if (!token)
3985 {
3986 /*
3987 * NULL token means do the initial setup - create an empty IPP message and
3988 * return...
3989 */
3990
3991 f->attrs = ippNew();
3992 f->group_tag = IPP_TAG_PRINTER;
3993 }
3994 else
3995 {
3996 _cupsLangPrintf(stderr, _("Unknown directive \"%s\" on line %d of \"%s\" ignored."), token, f->linenum, f->filename);
3997 }
3998
3999 return (1);
4000}
4001
4002
4003/*
4004 * 'load_ippserver_attributes()' - Load IPP attributes from an ippserver file.
4005 */
4006
4007static ipp_t * /* O - IPP attributes or `NULL` on error */
4008load_ippserver_attributes(
4009 const char *servername, /* I - Server name or `NULL` for default */
4010 int serverport, /* I - Server port number */
4011 const char *filename, /* I - ippserver attribute filename */
4012 cups_array_t *docformats) /* I - document-format-supported values */
4013{
4014 ipp_t *attrs; /* IPP attributes */
4015 _ipp_vars_t vars; /* IPP variables */
4016 char temp[256]; /* Temporary string */
4017
4018
4019 (void)docformats; /* for now */
4020
4021 /*
4022 * Setup callbacks and variables for the printer configuration file...
4023 *
4024 * The following additional variables are supported:
4025 *
4026 * - SERVERNAME: The host name of the server.
4027 * - SERVERPORT: The default port of the server.
4028 */
4029
4030 _ippVarsInit(&vars, (_ipp_fattr_cb_t)ippserver_attr_cb, (_ipp_ferror_cb_t)ippserver_error_cb, (_ipp_ftoken_cb_t)ippserver_token_cb);
4031
4032 if (servername)
4033 {
4034 _ippVarsSet(&vars, "SERVERNAME", servername);
4035 }
4036 else
4037 {
4038 httpGetHostname(NULL, temp, sizeof(temp));
4039 _ippVarsSet(&vars, "SERVERNAME", temp);
4040 }
4041
4042 snprintf(temp, sizeof(temp), "%d", serverport);
4043 _ippVarsSet(&vars, "SERVERPORT", temp);
4044
4045 /*
4046 * Load attributes and values for the printer...
4047 */
4048
4049 attrs = _ippFileParse(&vars, filename, NULL);
4050
4051 /*
4052 * Free memory and return...
4053 */
4054
4055 _ippVarsDeinit(&vars);
4056
4057 return (attrs);
4058}
4059
4060
4061/*
4062 * 'load_legacy_attributes()' - Load IPP attributes using the old ippserver
4063 * options.
4064 */
4065
4066static ipp_t * /* O - IPP attributes or `NULL` on error */
4067load_legacy_attributes(
4068 const char *make, /* I - Manufacturer name */
4069 const char *model, /* I - Model name */
4070 int ppm, /* I - pages-per-minute */
4071 int ppm_color, /* I - pages-per-minute-color */
4072 int duplex, /* I - Duplex support? */
4073 cups_array_t *docformats) /* I - document-format-supported values */
4074{
4075 int i; /* Looping var */
4076 ipp_t *attrs, /* IPP attributes */
4077 *col; /* Collection value */
4078 ipp_attribute_t *attr; /* Current attribute */
4079 char device_id[1024],/* printer-device-id */
4080 *ptr, /* Pointer into device ID */
4081 make_model[128];/* printer-make-and-model */
4082 const char *format, /* Current document format */
4083 *prefix; /* Prefix for device ID */
4084 int num_media; /* Number of media */
4085 const char * const *media; /* List of media */
4086 int num_ready; /* Number of loaded media */
4087 const char * const *ready; /* List of loaded media */
4088 pwg_media_t *pwg; /* PWG media size information */
4089 static const char * const media_supported[] =
4090 { /* media-supported values */
4091 "na_letter_8.5x11in", /* Letter */
4092 "na_legal_8.5x14in", /* Legal */
4093 "iso_a4_210x297mm", /* A4 */
4094 "na_number-10_4.125x9.5in", /* #10 Envelope */
4095 "iso_dl_110x220mm" /* DL Envelope */
4096 };
4097 static const char * const media_supported_color[] =
4098 { /* media-supported values */
4099 "na_letter_8.5x11in", /* Letter */
4100 "na_legal_8.5x14in", /* Legal */
4101 "iso_a4_210x297mm", /* A4 */
4102 "na_number-10_4.125x9.5in", /* #10 Envelope */
4103 "iso_dl_110x220mm", /* DL Envelope */
4104 "na_index-3x5_3x5in", /* Photo 3x5 */
4105 "oe_photo-l_3.5x5in", /* Photo L */
4106 "na_index-4x6_4x6in", /* Photo 4x6 */
4107 "iso_a6_105x148mm", /* A6 */
4108 "na_5x7_5x7in" /* Photo 5x7 aka 2L */
4109 "iso_a5_148x210mm", /* A5 */
4110 };
4111 static const char * const media_ready[] =
4112 { /* media-ready values */
4113 "na_letter_8.5x11in", /* Letter */
4114 "na_number-10_4.125x9.5in" /* #10 */
4115 };
4116 static const char * const media_ready_color[] =
4117 { /* media-ready values */
4118 "na_letter_8.5x11in", /* Letter */
4119 "na_index-4x6_4x6in" /* Photo 4x6 */
4120 };
4121 static const char * const media_source_supported[] =
4122 { /* media-source-supported values */
4123 "auto",
4124 "main",
4125 "manual",
4126 "by-pass-tray" /* AKA multi-purpose tray */
4127 };
4128 static const char * const media_source_supported_color[] =
4129 { /* media-source-supported values */
4130 "auto",
4131 "main",
4132 "photo"
4133 };
4134 static const char * const media_type_supported[] =
4135 { /* media-type-supported values */
4136 "auto",
4137 "cardstock",
4138 "envelope",
4139 "labels",
4140 "other",
4141 "stationery",
4142 "stationery-letterhead",
4143 "transparency"
4144 };
4145 static const char * const media_type_supported_color[] =
4146 { /* media-type-supported values */
4147 "auto",
4148 "cardstock",
4149 "envelope",
4150 "labels",
4151 "other",
4152 "stationery",
4153 "stationery-letterhead",
4154 "transparency",
4155 "photographic-glossy",
4156 "photographic-high-gloss",
4157 "photographic-matte",
4158 "photographic-satin",
4159 "photographic-semi-gloss"
4160 };
4161 static const int media_bottom_margin_supported[] =
4162 { /* media-bottom-margin-supported values */
4163 635 /* 1/4" */
4164 };
4165 static const int media_bottom_margin_supported_color[] =
4166 { /* media-bottom/top-margin-supported values */
4167 0, /* Borderless */
4168 1168 /* 0.46" (common HP inkjet bottom margin) */
4169 };
4170 static const int media_lr_margin_supported[] =
4171 { /* media-left/right-margin-supported values */
4172 340, /* 3.4mm (historical HP PCL A4 margin) */
4173 635 /* 1/4" */
4174 };
4175 static const int media_lr_margin_supported_color[] =
4176 { /* media-left/right-margin-supported values */
4177 0, /* Borderless */
4178 340, /* 3.4mm (historical HP PCL A4 margin) */
4179 635 /* 1/4" */
4180 };
4181 static const int media_top_margin_supported[] =
4182 { /* media-top-margin-supported values */
4183 635 /* 1/4" */
4184 };
4185 static const int media_top_margin_supported_color[] =
4186 { /* media-top/top-margin-supported values */
4187 0, /* Borderless */
4188 102 /* 0.04" (common HP inkjet top margin */
4189 };
4190 static const int orientation_requested_supported[4] =
4191 { /* orientation-requested-supported values */
4192 IPP_ORIENT_PORTRAIT,
4193 IPP_ORIENT_LANDSCAPE,
4194 IPP_ORIENT_REVERSE_LANDSCAPE,
4195 IPP_ORIENT_REVERSE_PORTRAIT
4196 };
4197 static const char * const overrides_supported[] =
4198 { /* overrides-supported values */
4199 "document-numbers",
4200 "media",
4201 "media-col",
4202 "orientation-requested",
4203 "pages"
4204 };
4205 static const char * const print_color_mode_supported[] =
4206 { /* print-color-mode-supported values */
4207 "monochrome"
4208 };
4209 static const char * const print_color_mode_supported_color[] =
4210 { /* print-color-mode-supported values */
4211 "auto",
4212 "color",
4213 "monochrome"
4214 };
4215 static const int print_quality_supported[] =
4216 { /* print-quality-supported values */
4217 IPP_QUALITY_DRAFT,
4218 IPP_QUALITY_NORMAL,
4219 IPP_QUALITY_HIGH
4220 };
4221 static const char * const printer_input_tray[] =
4222 { /* printer-input-tray values */
4223 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4224 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=100;status=0;name=main",
4225 "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=manual",
4226 "type=sheetFeedAutoNonRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=by-pass-tray"
4227 };
4228 static const char * const printer_input_tray_color[] =
4229 { /* printer-input-tray values */
4230 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4231 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=-2;status=0;name=main",
4232 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=photo"
4233 };
4234 static const char * const printer_supply[] =
4235 { /* printer-supply values */
4236 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4237 "maxcapacity=100;level=25;colorantname=unknown;",
4238 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4239 "maxcapacity=100;level=75;colorantname=black;"
4240 };
4241 static const char * const printer_supply_color[] =
4242 { /* printer-supply values */
4243 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4244 "maxcapacity=100;level=25;colorantname=unknown;",
4245 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4246 "maxcapacity=100;level=75;colorantname=black;",
4247 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4248 "maxcapacity=100;level=50;colorantname=cyan;",
4249 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4250 "maxcapacity=100;level=33;colorantname=magenta;",
4251 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4252 "maxcapacity=100;level=67;colorantname=yellow;"
4253 };
4254 static const char * const printer_supply_description[] =
4255 { /* printer-supply-description values */
4256 "Toner Waste Tank",
4257 "Black Toner"
4258 };
4259 static const char * const printer_supply_description_color[] =
4260 { /* printer-supply-description values */
4261 "Ink Waste Tank",
4262 "Black Ink",
4263 "Cyan Ink",
4264 "Magenta Ink",
4265 "Yellow Ink"
4266 };
4267 static const int pwg_raster_document_resolution_supported[] =
4268 {
4269 300,
4270 600
4271 };
4272 static const char * const pwg_raster_document_type_supported[] =
4273 {
4274 "black_1",
4275 "sgray_8"
4276 };
4277 static const char * const pwg_raster_document_type_supported_color[] =
4278 {
4279 "black_1",
4280 "sgray_8",
4281 "srgb_8",
4282 "srgb_16"
4283 };
4284 static const char * const sides_supported[] =
4285 { /* sides-supported values */
4286 "one-sided",
4287 "two-sided-long-edge",
4288 "two-sided-short-edge"
4289 };
4290 static const char * const urf_supported[] =
4291 { /* urf-supported values */
4292 "CP1",
4293 "IS1-4-5-19",
4294 "MT1-2-3-4-5-6",
4295 "RS600",
4296 "V1.4",
4297 "W8"
4298 };
4299 static const char * const urf_supported_color[] =
4300 { /* urf-supported values */
4301 "CP1",
4302 "IS1-4-5-7-19",
4303 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4304 "RS600",
4305 "SRGB24",
4306 "V1.4",
4307 "W8"
4308 };
4309 static const char * const urf_supported_color_duplex[] =
4310 { /* urf-supported values */
4311 "CP1",
4312 "IS1-4-5-7-19",
4313 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4314 "RS600",
4315 "SRGB24",
4316 "V1.4",
4317 "W8",
4318 "DM3"
4319 };
4320 static const char * const urf_supported_duplex[] =
4321 { /* urf-supported values */
4322 "CP1",
4323 "IS1-4-5-19",
4324 "MT1-2-3-4-5-6",
4325 "RS600",
4326 "V1.4",
4327 "W8",
4328 "DM1"
4329 };
4330
4331
4332 attrs = ippNew();
4333
4334 if (ppm_color > 0)
4335 {
4336 num_media = (int)(sizeof(media_supported_color) / sizeof(media_supported_color[0]));
4337 media = media_supported_color;
4338 num_ready = (int)(sizeof(media_ready_color) / sizeof(media_ready_color[0]));
4339 ready = media_ready_color;
4340 }
4341 else
4342 {
4343 num_media = (int)(sizeof(media_supported) / sizeof(media_supported[0]));
4344 media = media_supported;
4345 num_ready = (int)(sizeof(media_ready) / sizeof(media_ready[0]));
4346 ready = media_ready;
4347 }
4348
4349 /* color-supported */
4350 ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", ppm_color > 0);
4351
4352 /* copies-default */
4353 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
4354
4355 /* copies-supported */
4356 ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, (cupsArrayFind(docformats, (void *)"application/pdf") != NULL || cupsArrayFind(docformats, (void *)"image/jpeg") != NULL) ? 999 : 1);
4357
4358 /* document-password-supported */
4359 if (cupsArrayFind(docformats, (void *)"application/pdf"))
4360 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 1023);
4361
4362 /* finishings-default */
4363 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
4364
4365 /* finishings-supported */
4366 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", IPP_FINISHINGS_NONE);
4367
4368 /* media-bottom-margin-supported */
4369 if (ppm_color > 0)
4370 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", (int)(sizeof(media_bottom_margin_supported) / sizeof(media_bottom_margin_supported[0])), media_bottom_margin_supported);
4371 else
4372 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", (int)(sizeof(media_bottom_margin_supported_color) / sizeof(media_bottom_margin_supported_color[0])), media_bottom_margin_supported_color);
4373
4374 /* media-col-database and media-col-default */
4375 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", num_media, NULL);
4376 for (i = 0; i < num_media; i ++)
4377 {
4378 int bottom, left, /* media-xxx-margins */
4379 right, top;
4380 const char *source; /* media-source, if any */
4381
4382 pwg = pwgMediaForPWG(media[i]);
4383
4384 if (pwg->width < 21000 && pwg->length < 21000)
4385 {
4386 source = "photo"; /* Photo size media from photo tray */
4387 bottom = /* Borderless margins */
4388 left =
4389 right =
4390 top = 0;
4391 }
4392 else if (pwg->width < 21000)
4393 {
4394 source = "by-pass-tray"; /* Envelopes from multi-purpose tray */
4395 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4396 left = /* Left/right margins are standard */
4397 right = media_lr_margin_supported[1];
4398 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4399 }
4400 else if (pwg->width == 21000)
4401 {
4402 source = NULL; /* A4 from any tray */
4403 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4404 left = /* Left/right margins are reduced */
4405 right = media_lr_margin_supported[0];
4406 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4407 }
4408 else
4409 {
4410 source = NULL; /* Other size media from any tray */
4411 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4412 left = /* Left/right margins are standard */
4413 right = media_lr_margin_supported[1];
4414 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4415 }
4416
4417 col = create_media_col(media[i], source, NULL, pwg->width, pwg->length, bottom, left, right, top);
4418 ippSetCollection(attrs, &attr, i, col);
4419
4420 ippDelete(col);
4421 }
4422
4423 /* media-col-default */
4424 pwg = pwgMediaForPWG(ready[0]);
4425
4426 if (pwg->width == 21000)
4427 col = create_media_col(ready[0], "main", "stationery", pwg->width, pwg->length, ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0], media_lr_margin_supported[0], media_lr_margin_supported[0], ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]);
4428 else
4429 col = create_media_col(ready[0], "main", "stationery", pwg->width, pwg->length, ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0], media_lr_margin_supported[1], media_lr_margin_supported[1], ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]);
4430
4431 ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col);
4432
4433 ippDelete(col);
4434
4435 /* media-col-ready */
4436 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-ready", num_ready, NULL);
4437 for (i = 0; i < num_ready; i ++)
4438 {
4439 int bottom, left, /* media-xxx-margins */
4440 right, top;
4441 const char *source, /* media-source */
4442 *type; /* media-type */
4443
4444 pwg = pwgMediaForPWG(ready[i]);
4445
4446 if (pwg->width < 21000 && pwg->length < 21000)
4447 {
4448 source = "photo"; /* Photo size media from photo tray */
4449 type = "photographic-glossy"; /* Glossy photo paper */
4450 bottom = /* Borderless margins */
4451 left =
4452 right =
4453 top = 0;
4454 }
4455 else if (pwg->width < 21000)
4456 {
4457 source = "by-pass-tray"; /* Envelopes from multi-purpose tray */
4458 type = "envelope"; /* Envelope */
4459 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4460 left = /* Left/right margins are standard */
4461 right = media_lr_margin_supported[1];
4462 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4463 }
4464 else if (pwg->width == 21000)
4465 {
4466 source = "main"; /* A4 from main tray */
4467 type = "stationery"; /* Plain paper */
4468 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4469 left = /* Left/right margins are reduced */
4470 right = media_lr_margin_supported[0];
4471 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4472 }
4473 else
4474 {
4475 source = "main"; /* A4 from main tray */
4476 type = "stationery"; /* Plain paper */
4477 bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4478 left = /* Left/right margins are standard */
4479 right = media_lr_margin_supported[1];
4480 top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4481 }
4482
4483 col = create_media_col(ready[i], source, type, pwg->width, pwg->length, bottom, left, right, top);
4484 ippSetCollection(attrs, &attr, i, col);
4485 ippDelete(col);
4486 }
4487
4488 /* media-default */
4489 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-default", NULL, media[0]);
4490
4491 /* media-left/right-margin-supported */
4492 if (ppm_color > 0)
4493 {
4494 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", (int)(sizeof(media_lr_margin_supported_color) / sizeof(media_lr_margin_supported_color[0])), media_lr_margin_supported_color);
4495 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", (int)(sizeof(media_lr_margin_supported_color) / sizeof(media_lr_margin_supported_color[0])), media_lr_margin_supported_color);
4496 }
4497 else
4498 {
4499 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", (int)(sizeof(media_lr_margin_supported) / sizeof(media_lr_margin_supported[0])), media_lr_margin_supported);
4500 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", (int)(sizeof(media_lr_margin_supported) / sizeof(media_lr_margin_supported[0])), media_lr_margin_supported);
4501 }
4502
4503 /* media-ready */
4504 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", num_ready, NULL, ready);
4505
4506 /* media-supported */
4507 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-supported", num_media, NULL, media);
4508
4509 /* media-size-supported */
4510 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", num_media, NULL);
4511 for (i = 0; i < num_media; i ++)
4512 {
4513 pwg = pwgMediaForPWG(media[i]);
4514 col = create_media_size(pwg->width, pwg->length);
4515
4516 ippSetCollection(attrs, &attr, i, col);
4517 ippDelete(col);
4518 }
4519
4520 /* media-source-supported */
4521 if (ppm_color > 0)
4522 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", (int)(sizeof(media_source_supported_color) / sizeof(media_source_supported_color[0])), NULL, media_source_supported_color);
4523 else
4524 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", (int)(sizeof(media_source_supported) / sizeof(media_source_supported[0])), NULL, media_source_supported);
4525
4526 /* media-top-margin-supported */
4527 if (ppm_color > 0)
4528 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", (int)(sizeof(media_top_margin_supported) / sizeof(media_top_margin_supported[0])), media_top_margin_supported);
4529 else
4530 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", (int)(sizeof(media_top_margin_supported_color) / sizeof(media_top_margin_supported_color[0])), media_top_margin_supported_color);
4531
4532 /* media-type-supported */
4533 if (ppm_color > 0)
4534 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", (int)(sizeof(media_type_supported_color) / sizeof(media_type_supported_color[0])), NULL, media_type_supported_color);
4535 else
4536 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", (int)(sizeof(media_type_supported) / sizeof(media_type_supported[0])), NULL, media_type_supported);
4537
4538 /* orientation-requested-default */
4539 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", IPP_ORIENT_PORTRAIT);
4540
4541 /* orientation-requested-supported */
4542 if (cupsArrayFind(docformats, (void *)"application/pdf") || cupsArrayFind(docformats, (void *)"image/jpeg"))
4543 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported);
4544 else
4545 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", IPP_ORIENT_PORTRAIT);
4546
4547 /* output-bin-default */
4548 if (ppm_color > 0)
4549 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-up");
4550 else
4551 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
4552
4553 /* output-bin-supported */
4554 if (ppm_color > 0)
4555 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-up");
4556 else
4557 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
4558
4559 /* overrides-supported */
4560 if (cupsArrayFind(docformats, (void *)"application/pdf"))
4561 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "overrides-supported", (int)(sizeof(overrides_supported) / sizeof(overrides_supported[0])), NULL, overrides_supported);
4562
4563 /* page-ranges-supported */
4564 ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", cupsArrayFind(docformats, (void *)"application/pdf") != NULL);
4565
4566 /* pages-per-minute */
4567 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppm);
4568
4569 /* pages-per-minute-color */
4570 if (ppm_color > 0)
4571 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppm_color);
4572
4573 /* print-color-mode-default */
4574 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, ppm_color > 0 ? "auto" : "monochrome");
4575
4576 /* print-color-mode-supported */
4577 if (ppm_color > 0)
4578 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported_color) / sizeof(print_color_mode_supported_color[0])), NULL, print_color_mode_supported_color);
4579 else
4580 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported) / sizeof(print_color_mode_supported[0])), NULL, print_color_mode_supported);
4581
4582 /* print-content-optimize-default */
4583 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
4584
4585 /* print-content-optimize-supported */
4586 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
4587
4588 /* print-quality-default */
4589 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
4590
4591 /* print-quality-supported */
4592 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
4593
4594 /* print-rendering-intent-default */
4595 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
4596
4597 /* print-rendering-intent-supported */
4598 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
4599
4600 /* printer-device-id */
4601 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make, model);
4602 ptr = device_id + strlen(device_id);
4603 prefix = "CMD:";
4604 for (format = (const char *)cupsArrayFirst(docformats); format; format = (const char *)cupsArrayNext(docformats))
4605 {
4606 if (!strcasecmp(format, "application/pdf"))
4607 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPDF", prefix);
4608 else if (!strcasecmp(format, "application/postscript"))
4609 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPS", prefix);
4610 else if (!strcasecmp(format, "application/vnd.hp-PCL"))
4611 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPCL", prefix);
4612 else if (!strcasecmp(format, "image/jpeg"))
4613 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sJPEG", prefix);
4614 else if (!strcasecmp(format, "image/png"))
4615 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPNG", prefix);
4616 else if (!strcasecmp(format, "image/pwg-raster"))
4617 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPWG", prefix);
4618 else if (!strcasecmp(format, "image/urf"))
4619 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sURF", prefix);
4620 else
4621 continue;
4622
4623 ptr += strlen(ptr);
4624 prefix = ",";
4625 }
4626 if (ptr < (device_id + sizeof(device_id) - 1))
4627 {
4628 *ptr++ = ';';
4629 *ptr = '\0';
4630 }
4631 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id);
4632
4633 /* printer-input-tray */
4634 if (ppm_color > 0)
4635 {
4636 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray_color[0], (int)strlen(printer_input_tray_color[0]));
4637 for (i = 1; i < (int)(sizeof(printer_input_tray_color) / sizeof(printer_input_tray_color[0])); i ++)
4638 ippSetOctetString(attrs, &attr, i, printer_input_tray_color[i], (int)strlen(printer_input_tray_color[i]));
4639 }
4640 else
4641 {
4642 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray[0], (int)strlen(printer_input_tray[0]));
4643 for (i = 1; i < (int)(sizeof(printer_input_tray) / sizeof(printer_input_tray[0])); i ++)
4644 ippSetOctetString(attrs, &attr, i, printer_input_tray[i], (int)strlen(printer_input_tray[i]));
4645 }
4646
4647 /* printer-make-and-model */
4648 snprintf(make_model, sizeof(make_model), "%s %s", make, model);
4649 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, make_model);
4650
4651 /* printer-resolution-default */
4652 ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, 600, 600);
4653
4654 /* printer-resolution-supported */
4655 ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600);
4656
4657 /* printer-supply and printer-supply-description */
4658 if (ppm_color > 0)
4659 {
4660 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], (int)strlen(printer_supply_color[0]));
4661 for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++)
4662 ippSetOctetString(attrs, &attr, i, printer_supply_color[i], (int)strlen(printer_supply_color[i]));
4663
4664 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description_color) / sizeof(printer_supply_description_color[0])), NULL, printer_supply_description_color);
4665 }
4666 else
4667 {
4668 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], (int)strlen(printer_supply[0]));
4669 for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++)
4670 ippSetOctetString(attrs, &attr, i, printer_supply[i], (int)strlen(printer_supply[i]));
4671
4672 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description) / sizeof(printer_supply_description[0])), NULL, printer_supply_description);
4673 }
4674
4675 /* pwg-raster-document-xxx-supported */
4676 if (cupsArrayFind(docformats, (void *)"image/pwg-raster"))
4677 {
4678 ippAddResolutions(attrs, IPP_TAG_PRINTER, "pwg-raster-document-resolution-supported", (int)(sizeof(pwg_raster_document_resolution_supported) / sizeof(pwg_raster_document_resolution_supported[0])), IPP_RES_PER_INCH, pwg_raster_document_resolution_supported, pwg_raster_document_resolution_supported);
4679
4680 if (ppm_color > 0 && duplex)
4681 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "rotated");
4682 else if (duplex)
4683 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal");
4684
4685 if (ppm_color > 0)
4686 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported_color) / sizeof(pwg_raster_document_type_supported_color[0])), NULL, pwg_raster_document_type_supported_color);
4687 else
4688 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported) / sizeof(pwg_raster_document_type_supported[0])), NULL, pwg_raster_document_type_supported);
4689 }
4690
4691 /* sides-default */
4692 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
4693
4694 /* sides-supported */
4695 if (duplex)
4696 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported);
4697 else
4698 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided");
4699
4700 /* urf-supported */
4701 if (cupsArrayFind(docformats, (void *)"image/urf"))
4702 {
4703 if (ppm_color > 0)
4704 {
4705 if (duplex)
4706 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_color_duplex) / sizeof(urf_supported_color_duplex[0])), NULL, urf_supported_color_duplex);
4707 else
4708 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_color) / sizeof(urf_supported_color[0])), NULL, urf_supported_color);
4709 }
4710 else if (duplex)
4711 {
4712 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_duplex) / sizeof(urf_supported_duplex[0])), NULL, urf_supported_duplex);
4713 }
4714 else
4715 {
4716 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported) / sizeof(urf_supported[0])), NULL, urf_supported);
4717 }
4718 }
4719
4720 return (attrs);
4721}
4722
4723
4724#if !CUPS_LITE
4725/*
4726 * 'load_ppd_attributes()' - Load IPP attributes from a PPD file.
4727 */
4728
4729static ipp_t * /* O - IPP attributes or `NULL` on error */
4730load_ppd_attributes(
4731 const char *ppdfile, /* I - PPD filename */
4732 cups_array_t *docformats) /* I - document-format-supported values */
4733{
4734 int i, j; /* Looping vars */
4735 ipp_t *attrs; /* Attributes */
4736 ipp_attribute_t *attr; /* Current attribute */
4737 ipp_t *col; /* Current collection value */
4738 ppd_file_t *ppd; /* PPD data */
4739 ppd_attr_t *ppd_attr; /* PPD attribute */
4740 ppd_choice_t *ppd_choice; /* PPD choice */
4741 ppd_size_t *ppd_size; /* Default PPD size */
4742 pwg_size_t *pwg_size, /* Current PWG size */
4743 *default_size = NULL; /* Default PWG size */
4744 const char *default_source = NULL, /* Default media source */
4745 *default_type = NULL; /* Default media type */
4746 pwg_map_t *pwg_map; /* Mapping from PWG to PPD keywords */
4747 _ppd_cache_t *pc; /* PPD cache */
4748 _pwg_finishings_t *finishings; /* Current finishings value */
4749 const char *template; /* Current finishings-template value */
4750 int num_margins; /* Number of media-xxx-margin-supported values */
4751 int margins[10]; /* media-xxx-margin-supported values */
4752 int xres, /* Default horizontal resolution */
4753 yres; /* Default vertical resolution */
4754 int num_urf; /* Number of urf-supported values */
4755 const char *urf[10]; /* urf-supported values */
4756 char urf_rs[32]; /* RS value */
4757 static const int orientation_requested_supported[4] =
4758 { /* orientation-requested-supported values */
4759 IPP_ORIENT_PORTRAIT,
4760 IPP_ORIENT_LANDSCAPE,
4761 IPP_ORIENT_REVERSE_LANDSCAPE,
4762 IPP_ORIENT_REVERSE_PORTRAIT
4763 };
4764 static const char * const overrides_supported[] =
4765 { /* overrides-supported */
4766 "document-numbers",
4767 "media",
4768 "media-col",
4769 "orientation-requested",
4770 "pages"
4771 };
4772 static const char * const print_color_mode_supported[] =
4773 { /* print-color-mode-supported values */
4774 "monochrome"
4775 };
4776 static const char * const print_color_mode_supported_color[] =
4777 { /* print-color-mode-supported values */
4778 "auto",
4779 "color",
4780 "monochrome"
4781 };
4782 static const int print_quality_supported[] =
4783 { /* print-quality-supported values */
4784 IPP_QUALITY_DRAFT,
4785 IPP_QUALITY_NORMAL,
4786 IPP_QUALITY_HIGH
4787 };
4788 static const char * const printer_supply[] =
4789 { /* printer-supply values */
4790 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4791 "maxcapacity=100;level=25;colorantname=unknown;",
4792 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4793 "maxcapacity=100;level=75;colorantname=black;"
4794 };
4795 static const char * const printer_supply_color[] =
4796 { /* printer-supply values */
4797 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4798 "maxcapacity=100;level=25;colorantname=unknown;",
4799 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4800 "maxcapacity=100;level=75;colorantname=black;",
4801 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4802 "maxcapacity=100;level=50;colorantname=cyan;",
4803 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4804 "maxcapacity=100;level=33;colorantname=magenta;",
4805 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4806 "maxcapacity=100;level=67;colorantname=yellow;"
4807 };
4808 static const char * const printer_supply_description[] =
4809 { /* printer-supply-description values */
4810 "Toner Waste Tank",
4811 "Black Toner"
4812 };
4813 static const char * const printer_supply_description_color[] =
4814 { /* printer-supply-description values */
4815 "Ink Waste Tank",
4816 "Black Ink",
4817 "Cyan Ink",
4818 "Magenta Ink",
4819 "Yellow Ink"
4820 };
4821 static const char * const pwg_raster_document_type_supported[] =
4822 {
4823 "black_1",
4824 "sgray_8"
4825 };
4826 static const char * const pwg_raster_document_type_supported_color[] =
4827 {
4828 "black_1",
4829 "sgray_8",
4830 "srgb_8",
4831 "srgb_16"
4832 };
4833 static const char * const sides_supported[] =
4834 { /* sides-supported values */
4835 "one-sided",
4836 "two-sided-long-edge",
4837 "two-sided-short-edge"
4838 };
4839
4840
4841 /*
4842 * Open the PPD file...
4843 */
4844
4845 if ((ppd = ppdOpenFile(ppdfile)) == NULL)
4846 {
4847 ppd_status_t status; /* Load error */
4848
4849 status = ppdLastError(&i);
4850 _cupsLangPrintf(stderr, _("ippeveprinter: Unable to open \"%s\": %s on line %d."), ppdfile, ppdErrorString(status), i);
4851 return (NULL);
4852 }
4853
4854 ppdMarkDefaults(ppd);
4855
4856 pc = _ppdCacheCreateWithPPD(ppd);
4857
4858 if ((ppd_size = ppdPageSize(ppd, NULL)) != NULL)
4859 {
4860 /*
4861 * Look up default size...
4862 */
4863
4864 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
4865 {
4866 if (!strcmp(pwg_size->map.ppd, ppd_size->name))
4867 {
4868 default_size = pwg_size;
4869 break;
4870 }
4871 }
4872 }
4873
4874 if (!default_size)
4875 {
4876 /*
4877 * Default to A4 or Letter...
4878 */
4879
4880 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
4881 {
4882 if (!strcmp(pwg_size->map.ppd, "Letter") || !strcmp(pwg_size->map.ppd, "A4"))
4883 {
4884 default_size = pwg_size;
4885 break;
4886 }
4887 }
4888
4889 if (!default_size)
4890 default_size = pc->sizes; /* Last resort: first size */
4891 }
4892
4893 if ((ppd_choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
4894 default_source = _ppdCacheGetSource(pc, ppd_choice->choice);
4895
4896 if ((ppd_choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL)
4897 default_source = _ppdCacheGetType(pc, ppd_choice->choice);
4898
4899 if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL)
4900 {
4901 /*
4902 * Use the PPD-defined default resolution...
4903 */
4904
4905 if ((i = sscanf(ppd_attr->value, "%dx%d", &xres, &yres)) == 1)
4906 yres = xres;
4907 else if (i < 0)
4908 xres = yres = 300;
4909 }
4910 else
4911 {
4912 /*
4913 * Use default of 300dpi...
4914 */
4915
4916 xres = yres = 300;
4917 }
4918
4919 snprintf(urf_rs, sizeof(urf_rs), "RS%d", yres < xres ? yres : xres);
4920
4921 num_urf = 0;
4922 urf[num_urf ++] = "V1.4";
4923 urf[num_urf ++] = "CP1";
4924 urf[num_urf ++] = urf_rs;
4925 urf[num_urf ++] = "W8";
4926 if (pc->sides_2sided_long)
4927 urf[num_urf ++] = "DM1";
4928 if (ppd->color_device)
4929 urf[num_urf ++] = "SRGB24";
4930
4931 /*
4932 * PostScript printers accept PDF via one of the CUPS PDF to PostScript
4933 * filters, along with PostScript (of course) and JPEG...
4934 */
4935
4936 cupsArrayAdd(docformats, "application/pdf");
4937 cupsArrayAdd(docformats, "application/postscript");
4938 cupsArrayAdd(docformats, "image/jpeg");
4939
4940 /*
4941 * Create the attributes...
4942 */
4943
4944 attrs = ippNew();
4945
4946 /* color-supported */
4947 ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", (char)ppd->color_device);
4948
4949 /* copies-default */
4950 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
4951
4952 /* copies-supported */
4953 ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999);
4954
4955 /* document-password-supported */
4956 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 127);
4957
4958 /* finishing-template-supported */
4959 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template-supported", cupsArrayCount(pc->templates) + 1, NULL, NULL);
4960 ippSetString(attrs, &attr, 0, "none");
4961 for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
4962 ippSetString(attrs, &attr, i, template);
4963
4964 /* finishings-col-database */
4965 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-database", cupsArrayCount(pc->templates) + 1, NULL);
4966
4967 col = ippNew();
4968 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
4969 ippSetCollection(attrs, &attr, 0, col);
4970 ippDelete(col);
4971
4972 for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
4973 {
4974 col = ippNew();
4975 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template);
4976 ippSetCollection(attrs, &attr, i, col);
4977 ippDelete(col);
4978 }
4979
4980 /* finishings-col-default */
4981 col = ippNew();
4982 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
4983 ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-default", col);
4984 ippDelete(col);
4985
4986 /* finishings-col-ready */
4987 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-ready", cupsArrayCount(pc->templates) + 1, NULL);
4988
4989 col = ippNew();
4990 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
4991 ippSetCollection(attrs, &attr, 0, col);
4992 ippDelete(col);
4993
4994 for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
4995 {
4996 col = ippNew();
4997 ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template);
4998 ippSetCollection(attrs, &attr, i, col);
4999 ippDelete(col);
5000 }
5001
5002 /* finishings-col-supported */
5003 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishings-col-supported", NULL, "finishing-template");
5004
5005 /* finishings-default */
5006 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
5007
5008 /* finishings-ready */
5009 attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-ready", cupsArrayCount(pc->finishings) + 1, NULL);
5010 ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
5011 for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
5012 ippSetInteger(attrs, &attr, i, (int)finishings->value);
5013
5014 /* finishings-supported */
5015 attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", cupsArrayCount(pc->finishings) + 1, NULL);
5016 ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
5017 for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
5018 ippSetInteger(attrs, &attr, i, (int)finishings->value);
5019
5020 /* media-bottom-margin-supported */
5021 for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
5022 {
5023 for (j = 0; j < num_margins; j ++)
5024 {
5025 if (margins[j] == pwg_size->bottom)
5026 break;
5027 }
5028
5029 if (j >= num_margins)
5030 margins[num_margins ++] = pwg_size->bottom;
5031 }
5032
5033 for (i = 0; i < (num_margins - 1); i ++)
5034 {
5035 for (j = i + 1; j < num_margins; j ++)
5036 {
5037 if (margins[i] > margins[j])
5038 {
5039 int mtemp = margins[i];
5040
5041 margins[i] = margins[j];
5042 margins[j] = mtemp;
5043 }
5044 }
5045 }
5046
5047 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", num_margins, margins);
5048
5049 /* media-col-database */
5050 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", pc->num_sizes, NULL);
5051 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
5052 {
5053 col = create_media_col(pwg_size->map.pwg, NULL, NULL, pwg_size->width, pwg_size->length, pwg_size->bottom, pwg_size->left, pwg_size->right, pwg_size->top);
5054 ippSetCollection(attrs, &attr, i, col);
5055 ippDelete(col);
5056 }
5057
5058 /* media-col-default */
5059 col = create_media_col(default_size->map.pwg, default_source, default_type, default_size->width, default_size->length, default_size->bottom, default_size->left, default_size->right, default_size->top);
5060 ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col);
5061 ippDelete(col);
5062
5063 /* media-col-ready */
5064 col = create_media_col(default_size->map.pwg, default_source, default_type, default_size->width, default_size->length, default_size->bottom, default_size->left, default_size->right, default_size->top);
5065 ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-ready", col);
5066 ippDelete(col);
5067
5068 /* media-default */
5069 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default", NULL, default_size->map.pwg);
5070
5071 /* media-left-margin-supported */
5072 for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
5073 {
5074 for (j = 0; j < num_margins; j ++)
5075 {
5076 if (margins[j] == pwg_size->left)
5077 break;
5078 }
5079
5080 if (j >= num_margins)
5081 margins[num_margins ++] = pwg_size->left;
5082 }
5083
5084 for (i = 0; i < (num_margins - 1); i ++)
5085 {
5086 for (j = i + 1; j < num_margins; j ++)
5087 {
5088 if (margins[i] > margins[j])
5089 {
5090 int mtemp = margins[i];
5091
5092 margins[i] = margins[j];
5093 margins[j] = mtemp;
5094 }
5095 }
5096 }
5097
5098 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", num_margins, margins);
5099
5100 /* media-ready */
5101 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, default_size->map.pwg);
5102
5103 /* media-right-margin-supported */
5104 for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
5105 {
5106 for (j = 0; j < num_margins; j ++)
5107 {
5108 if (margins[j] == pwg_size->right)
5109 break;
5110 }
5111
5112 if (j >= num_margins)
5113 margins[num_margins ++] = pwg_size->right;
5114 }
5115
5116 for (i = 0; i < (num_margins - 1); i ++)
5117 {
5118 for (j = i + 1; j < num_margins; j ++)
5119 {
5120 if (margins[i] > margins[j])
5121 {
5122 int mtemp = margins[i];
5123
5124 margins[i] = margins[j];
5125 margins[j] = mtemp;
5126 }
5127 }
5128 }
5129
5130 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", num_margins, margins);
5131
5132 /* media-supported */
5133 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-supported", pc->num_sizes, NULL, NULL);
5134 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
5135 ippSetString(attrs, &attr, i, pwg_size->map.pwg);
5136
5137 /* media-size-supported */
5138 attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", pc->num_sizes, NULL);
5139 for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
5140 {
5141 col = create_media_size(pwg_size->width, pwg_size->length);
5142 ippSetCollection(attrs, &attr, i, col);
5143 ippDelete(col);
5144 }
5145
5146 /* media-source-supported */
5147 if (pc->num_sources > 0)
5148 {
5149 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source-supported", pc->num_sources, NULL, NULL);
5150 for (i = 0, pwg_map = pc->sources; i < pc->num_sources; i ++, pwg_map ++)
5151 ippSetString(attrs, &attr, i, pwg_map->pwg);
5152 }
5153 else
5154 {
5155 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", NULL, "auto");
5156 }
5157
5158 /* media-top-margin-supported */
5159 for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
5160 {
5161 for (j = 0; j < num_margins; j ++)
5162 {
5163 if (margins[j] == pwg_size->top)
5164 break;
5165 }
5166
5167 if (j >= num_margins)
5168 margins[num_margins ++] = pwg_size->top;
5169 }
5170
5171 for (i = 0; i < (num_margins - 1); i ++)
5172 {
5173 for (j = i + 1; j < num_margins; j ++)
5174 {
5175 if (margins[i] > margins[j])
5176 {
5177 int mtemp = margins[i];
5178
5179 margins[i] = margins[j];
5180 margins[j] = mtemp;
5181 }
5182 }
5183 }
5184
5185 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", num_margins, margins);
5186
5187 /* media-type-supported */
5188 if (pc->num_types > 0)
5189 {
5190 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type-supported", pc->num_types, NULL, NULL);
5191 for (i = 0, pwg_map = pc->types; i < pc->num_types; i ++, pwg_map ++)
5192 ippSetString(attrs, &attr, i, pwg_map->pwg);
5193 }
5194 else
5195 {
5196 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", NULL, "auto");
5197 }
5198
5199 /* orientation-requested-default */
5200 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", IPP_ORIENT_PORTRAIT);
5201
5202 /* orientation-requested-supported */
5203 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported);
5204
5205 /* output-bin-default */
5206 if (pc->num_bins > 0)
5207 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-default", NULL, pc->bins->pwg);
5208 else
5209 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
5210
5211 /* output-bin-supported */
5212 if (pc->num_bins > 0)
5213 {
5214 attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-supported", pc->num_bins, NULL, NULL);
5215 for (i = 0, pwg_map = pc->bins; i < pc->num_bins; i ++, pwg_map ++)
5216 ippSetString(attrs, &attr, i, pwg_map->pwg);
5217 }
5218 else
5219 {
5220 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
5221 }
5222
5223 /* overrides-supported */
5224 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "overrides-supported", (int)(sizeof(overrides_supported) / sizeof(overrides_supported[0])), NULL, overrides_supported);
5225
5226 /* page-ranges-supported */
5227 ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1);
5228
5229 /* pages-per-minute */
5230 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppd->throughput);
5231
5232 /* pages-per-minute-color */
5233 if (ppd->color_device)
5234 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppd->throughput);
5235
5236 /* print-color-mode-default */
5237 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, ppd->color_device ? "auto" : "monochrome");
5238
5239 /* print-color-mode-supported */
5240 if (ppd->color_device)
5241 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported_color) / sizeof(print_color_mode_supported_color[0])), NULL, print_color_mode_supported_color);
5242 else
5243 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported) / sizeof(print_color_mode_supported[0])), NULL, print_color_mode_supported);
5244
5245 /* print-content-optimize-default */
5246 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
5247
5248 /* print-content-optimize-supported */
5249 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
5250
5251 /* print-quality-default */
5252 ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
5253
5254 /* print-quality-supported */
5255 ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
5256
5257 /* print-rendering-intent-default */
5258 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
5259
5260 /* print-rendering-intent-supported */
5261 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
5262
5263 /* printer-device-id */
5264 if ((ppd_attr = ppdFindAttr(ppd, "1284DeviceId", NULL)) != NULL)
5265 {
5266 /*
5267 * Use the device ID string from the PPD...
5268 */
5269
5270 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, ppd_attr->value);
5271 }
5272 else
5273 {
5274 /*
5275 * Synthesize a device ID string...
5276 */
5277
5278 char device_id[1024]; /* Device ID string */
5279
5280 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;CMD:PS;", ppd->manufacturer, ppd->modelname);
5281
5282 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id);
5283 }
5284
5285 /* printer-input-tray */
5286 if (pc->num_sources > 0)
5287 {
5288 for (i = 0, attr = NULL; i < pc->num_sources; i ++)
5289 {
5290 char input_tray[1024]; /* printer-input-tray value */
5291
5292 if (!strcmp(pc->sources[i].pwg, "manual") || strstr(pc->sources[i].pwg, "-man") != NULL)
5293 snprintf(input_tray, sizeof(input_tray), "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=%s", pc->sources[i].pwg);
5294 else
5295 snprintf(input_tray, sizeof(input_tray), "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=125;status=0;name=%s", pc->sources[i].pwg);
5296
5297 if (attr)
5298 ippSetOctetString(attrs, &attr, i, input_tray, (int)strlen(input_tray));
5299 else
5300 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", input_tray, (int)strlen(input_tray));
5301 }
5302 }
5303 else
5304 {
5305 static const char *printer_input_tray = "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto";
5306
5307 ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray, (int)strlen(printer_input_tray));
5308 }
5309
5310 /* printer-make-and-model */
5311 ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, ppd->nickname);
5312
5313 /* printer-resolution-default */
5314 ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, xres, yres);
5315
5316 /* printer-resolution-supported */
5317 ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, xres, yres);
5318
5319 /* printer-supply and printer-supply-description */
5320 if (ppd->color_device)
5321 {
5322 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], (int)strlen(printer_supply_color[0]));
5323 for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++)
5324 ippSetOctetString(attrs, &attr, i, printer_supply_color[i], (int)strlen(printer_supply_color[i]));
5325
5326 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description_color) / sizeof(printer_supply_description_color[0])), NULL, printer_supply_description_color);
5327 }
5328 else
5329 {
5330 attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], (int)strlen(printer_supply[0]));
5331 for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++)
5332 ippSetOctetString(attrs, &attr, i, printer_supply[i], (int)strlen(printer_supply[i]));
5333
5334 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description) / sizeof(printer_supply_description[0])), NULL, printer_supply_description);
5335 }
5336
5337 /* pwg-raster-document-xxx-supported */
5338 if (cupsArrayFind(docformats, (void *)"image/pwg-raster"))
5339 {
5340 ippAddResolution(attrs, IPP_TAG_PRINTER, "pwg-raster-document-resolution-supported", IPP_RES_PER_INCH, xres, yres);
5341
5342 if (pc->sides_2sided_long)
5343 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal");
5344
5345 if (ppd->color_device)
5346 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported_color) / sizeof(pwg_raster_document_type_supported_color[0])), NULL, pwg_raster_document_type_supported_color);
5347 else
5348 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported) / sizeof(pwg_raster_document_type_supported[0])), NULL, pwg_raster_document_type_supported);
5349 }
5350
5351 /* sides-default */
5352 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
5353
5354 /* sides-supported */
5355 if (pc->sides_2sided_long)
5356 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported);
5357 else
5358 ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided");
5359
5360 /* urf-supported */
5361 if (cupsArrayFind(docformats, (void *)"image/urf"))
5362 ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", num_urf, NULL, urf);
5363
5364 /*
5365 * Free the PPD file and return the attributes...
5366 */
5367
5368 _ppdCacheDestroy(pc);
5369
5370 ppdClose(ppd);
5371
5372 return (attrs);
5373}
5374#endif /* !CUPS_LITE */
5375
5376
5377#if HAVE_LIBPAM
5378/*
5379 * 'pam_func()' - PAM conversation function.
5380 */
5381
5382static int /* O - Success or failure */
5383pam_func(
5384 int num_msg, /* I - Number of messages */
5385 const struct pam_message **msg, /* I - Messages */
5386 struct pam_response **resp, /* O - Responses */
5387 void *appdata_ptr)
5388 /* I - Pointer to connection */
5389{
5390 int i; /* Looping var */
5391 struct pam_response *replies; /* Replies */
5392 ippeve_authdata_t *data; /* Pointer to auth data */
5393
5394
5395 /*
5396 * Allocate memory for the responses...
5397 */
5398
5399 if ((replies = malloc(sizeof(struct pam_response) * (size_t)num_msg)) == NULL)
5400 return (PAM_CONV_ERR);
5401
5402 /*
5403 * Answer all of the messages...
5404 */
5405
5406 data = (ippeve_authdata_t *)appdata_ptr;
5407
5408 for (i = 0; i < num_msg; i ++)
5409 {
5410 switch (msg[i]->msg_style)
5411 {
5412 case PAM_PROMPT_ECHO_ON:
5413 replies[i].resp_retcode = PAM_SUCCESS;
5414 replies[i].resp = strdup(data->username);
5415 break;
5416
5417 case PAM_PROMPT_ECHO_OFF:
5418 replies[i].resp_retcode = PAM_SUCCESS;
5419 replies[i].resp = strdup(data->password);
5420 break;
5421
5422 case PAM_TEXT_INFO:
5423 replies[i].resp_retcode = PAM_SUCCESS;
5424 replies[i].resp = NULL;
5425 break;
5426
5427 case PAM_ERROR_MSG:
5428 replies[i].resp_retcode = PAM_SUCCESS;
5429 replies[i].resp = NULL;
5430 break;
5431
5432 default:
5433 free(replies);
5434 return (PAM_CONV_ERR);
5435 }
5436 }
5437
5438 /*
5439 * Return the responses back to PAM...
5440 */
5441
5442 *resp = replies;
5443
5444 return (PAM_SUCCESS);
5445}
5446#endif /* HAVE_LIBPAM */
5447
5448
5449/*
5450 * 'parse_options()' - Parse URL options into CUPS options.
5451 *
5452 * The client->options string is destroyed by this function.
5453 */
5454
5455static int /* O - Number of options */
5456parse_options(ippeve_client_t *client, /* I - Client */
5457 cups_option_t **options)/* O - Options */
5458{
5459 char *name, /* Name */
5460 *value, /* Value */
5461 *next; /* Next name=value pair */
5462 int num_options = 0; /* Number of options */
5463
5464
5465 *options = NULL;
5466
5467 for (name = client->options; name && *name; name = next)
5468 {
5469 if ((value = strchr(name, '=')) == NULL)
5470 break;
5471
5472 *value++ = '\0';
5473 if ((next = strchr(value, '&')) != NULL)
5474 *next++ = '\0';
5475
5476 num_options = cupsAddOption(name, value, num_options, options);
5477 }
5478
5479 return (num_options);
5480}
5481
5482
5483/*
5484 * 'process_attr_message()' - Process an ATTR: message from a command.
5485 */
5486
5487static void
5488process_attr_message(
5489 ippeve_job_t *job, /* I - Job */
5490 char *message) /* I - Message */
5491{
5492 int i, /* Looping var */
5493 num_options = 0; /* Number of name=value pairs */
5494 cups_option_t *options = NULL, /* name=value pairs from message */
5495 *option; /* Current option */
5496 ipp_attribute_t *attr; /* Current attribute */
5497
5498
5499 /*
5500 * Grab attributes from the message line...
5501 */
5502
5503 num_options = cupsParseOptions(message + 5, num_options, &options);
5504
5505 /*
5506 * Loop through the options and record them in the printer or job objects...
5507 */
5508
5509 for (i = num_options, option = options; i > 0; i --, option ++)
5510 {
5511 if (!strcmp(option->name, "job-impressions"))
5512 {
5513 /*
5514 * Update job-impressions attribute...
5515 */
5516
5517 job->impressions = atoi(option->value);
5518 }
5519 else if (!strcmp(option->name, "job-impressions-completed"))
5520 {
5521 /*
5522 * Update job-impressions-completed attribute...
5523 */
5524
5525 job->impcompleted = atoi(option->value);
5526 }
5527 else if (!strncmp(option->name, "marker-", 7) || !strcmp(option->name, "printer-alert") || !strcmp(option->name, "printer-alert-description") || !strcmp(option->name, "printer-supply") || !strcmp(option->name, "printer-supply-description"))
5528 {
5529 /*
5530 * Update Printer Status attribute...
5531 */
5532
5533 _cupsRWLockWrite(&job->printer->rwlock);
5534
5535 if ((attr = ippFindAttribute(job->printer->attrs, option->name, IPP_TAG_ZERO)) != NULL)
5536 ippDeleteAttribute(job->printer->attrs, attr);
5537
5538 cupsEncodeOption(job->printer->attrs, IPP_TAG_PRINTER, option->name, option->value);
5539
5540 _cupsRWUnlock(&job->printer->rwlock);
5541 }
5542 else
5543 {
5544 /*
5545 * Something else that isn't currently supported...
5546 */
5547
5548 fprintf(stderr, "[Job %d] Ignoring update of attribute \"%s\" with value \"%s\".\n", job->id, option->name, option->value);
5549 }
5550 }
5551
5552 cupsFreeOptions(num_options, options);
5553}
5554
5555
5556/*
5557 * 'process_client()' - Process client requests on a thread.
5558 */
5559
5560static void * /* O - Exit status */
5561process_client(ippeve_client_t *client) /* I - Client */
5562{
5563 /*
5564 * Loop until we are out of requests or timeout (30 seconds)...
5565 */
5566
5567#ifdef HAVE_SSL
5568 int first_time = 1; /* First time request? */
5569#endif /* HAVE_SSL */
5570
5571 while (httpWait(client->http, 30000))
5572 {
5573#ifdef HAVE_SSL
5574 if (first_time)
5575 {
5576 /*
5577 * See if we need to negotiate a TLS connection...
5578 */
5579
5580 char buf[1]; /* First byte from client */
5581
5582 if (recv(httpGetFd(client->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0])))
5583 {
5584 fprintf(stderr, "%s Starting HTTPS session.\n", client->hostname);
5585
5586 if (httpEncryption(client->http, HTTP_ENCRYPTION_ALWAYS))
5587 {
5588 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
5589 break;
5590 }
5591
5592 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
5593 }
5594
5595 first_time = 0;
5596 }
5597#endif /* HAVE_SSL */
5598
5599 if (!process_http(client))
5600 break;
5601 }
5602
5603 /*
5604 * Close the conection to the client and return...
5605 */
5606
5607 delete_client(client);
5608
5609 return (NULL);
5610}
5611
5612
5613/*
5614 * 'process_http()' - Process a HTTP request.
5615 */
5616
5617int /* O - 1 on success, 0 on failure */
5618process_http(ippeve_client_t *client) /* I - Client connection */
5619{
5620 char uri[1024]; /* URI */
5621 http_state_t http_state; /* HTTP state */
5622 http_status_t http_status; /* HTTP status */
5623 ipp_state_t ipp_state; /* State of IPP transfer */
5624 char scheme[32], /* Method/scheme */
5625 userpass[128], /* Username:password */
5626 hostname[HTTP_MAX_HOST];
5627 /* Hostname */
5628 int port; /* Port number */
5629 static const char * const http_states[] =
5630 { /* Strings for logging HTTP method */
5631 "WAITING",
5632 "OPTIONS",
5633 "GET",
5634 "GET_SEND",
5635 "HEAD",
5636 "POST",
5637 "POST_RECV",
5638 "POST_SEND",
5639 "PUT",
5640 "PUT_RECV",
5641 "DELETE",
5642 "TRACE",
5643 "CONNECT",
5644 "STATUS",
5645 "UNKNOWN_METHOD",
5646 "UNKNOWN_VERSION"
5647 };
5648
5649
5650 /*
5651 * Clear state variables...
5652 */
5653
5654 client->username[0] = '\0';
5655
5656 ippDelete(client->request);
5657 ippDelete(client->response);
5658
5659 client->request = NULL;
5660 client->response = NULL;
5661 client->operation = HTTP_STATE_WAITING;
5662
5663 /*
5664 * Read a request from the connection...
5665 */
5666
5667 while ((http_state = httpReadRequest(client->http, uri,
5668 sizeof(uri))) == HTTP_STATE_WAITING)
5669 usleep(1);
5670
5671 /*
5672 * Parse the request line...
5673 */
5674
5675 if (http_state == HTTP_STATE_ERROR)
5676 {
5677 if (httpError(client->http) == EPIPE)
5678 fprintf(stderr, "%s Client closed connection.\n", client->hostname);
5679 else
5680 fprintf(stderr, "%s Bad request line (%s).\n", client->hostname, strerror(httpError(client->http)));
5681
5682 return (0);
5683 }
5684 else if (http_state == HTTP_STATE_UNKNOWN_METHOD)
5685 {
5686 fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname);
5687 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5688 return (0);
5689 }
5690 else if (http_state == HTTP_STATE_UNKNOWN_VERSION)
5691 {
5692 fprintf(stderr, "%s Bad HTTP version.\n", client->hostname);
5693 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5694 return (0);
5695 }
5696
5697 fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state], uri);
5698
5699 /*
5700 * Separate the URI into its components...
5701 */
5702
5703 if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
5704 userpass, sizeof(userpass),
5705 hostname, sizeof(hostname), &port,
5706 client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK &&
5707 (http_state != HTTP_STATE_OPTIONS || strcmp(uri, "*")))
5708 {
5709 fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri);
5710 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5711 return (0);
5712 }
5713
5714 if ((client->options = strchr(client->uri, '?')) != NULL)
5715 *(client->options)++ = '\0';
5716
5717 /*
5718 * Process the request...
5719 */
5720
5721 client->start = time(NULL);
5722 client->operation = httpGetState(client->http);
5723
5724 /*
5725 * Parse incoming parameters until the status changes...
5726 */
5727
5728 while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE);
5729
5730 if (http_status != HTTP_STATUS_OK)
5731 {
5732 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5733 return (0);
5734 }
5735
5736 if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] &&
5737 httpGetVersion(client->http) >= HTTP_VERSION_1_1)
5738 {
5739 /*
5740 * HTTP/1.1 and higher require the "Host:" field...
5741 */
5742
5743 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5744 return (0);
5745 }
5746
5747 /*
5748 * Handle HTTP Upgrade...
5749 */
5750
5751 if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION), "Upgrade"))
5752 {
5753#ifdef HAVE_SSL
5754 if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http))
5755 {
5756 if (!respond_http(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0))
5757 return (0);
5758
5759 fprintf(stderr, "%s Upgrading to encrypted connection.\n", client->hostname);
5760
5761 if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED))
5762 {
5763 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
5764 return (0);
5765 }
5766
5767 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
5768 }
5769 else
5770#endif /* HAVE_SSL */
5771
5772 if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
5773 return (0);
5774 }
5775
5776 /*
5777 * Handle new transfers...
5778 */
5779
5780 switch (client->operation)
5781 {
5782 case HTTP_STATE_OPTIONS :
5783 /*
5784 * Do OPTIONS command...
5785 */
5786
5787 return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
5788
5789 case HTTP_STATE_HEAD :
5790 if (!strcmp(client->uri, "/icon.png"))
5791 return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
5792 else if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies"))
5793 return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
5794 else
5795 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
5796
5797 case HTTP_STATE_GET :
5798 if (!strcmp(client->uri, "/icon.png"))
5799 {
5800 /*
5801 * Send PNG icon file.
5802 */
5803
5804 if (client->printer->icon)
5805 {
5806 int fd; /* Icon file */
5807 struct stat fileinfo; /* Icon file information */
5808 char buffer[4096]; /* Copy buffer */
5809 ssize_t bytes; /* Bytes */
5810
5811 fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon);
5812
5813 if (!stat(client->printer->icon, &fileinfo) && (fd = open(client->printer->icon, O_RDONLY)) >= 0)
5814 {
5815 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
5816 {
5817 close(fd);
5818 return (0);
5819 }
5820
5821 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
5822 httpWrite2(client->http, buffer, (size_t)bytes);
5823
5824 httpFlushWrite(client->http);
5825
5826 close(fd);
5827 }
5828 else
5829 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
5830 }
5831 else
5832 {
5833 fputs("Icon file is internal printer.png.\n", stderr);
5834
5835 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_png)))
5836 return (0);
5837
5838 httpWrite2(client->http, (const char *)printer_png, sizeof(printer_png));
5839 httpFlushWrite(client->http);
5840 }
5841 }
5842 else
5843 {
5844 /*
5845 * Authenticate if needed...
5846 */
5847
5848 if ((http_status = authenticate_request(client)) != HTTP_STATUS_CONTINUE)
5849 {
5850 return (respond_http(client, http_status, NULL, NULL, 0));
5851 }
5852
5853 if (!strcmp(client->uri, "/"))
5854 {
5855 /*
5856 * Show web status page...
5857 */
5858
5859 return (show_status(client));
5860 }
5861 else if (!strcmp(client->uri, "/media"))
5862 {
5863 /*
5864 * Show web media page...
5865 */
5866
5867 return (show_media(client));
5868 }
5869 else if (!strcmp(client->uri, "/supplies"))
5870 {
5871 /*
5872 * Show web supplies page...
5873 */
5874
5875 return (show_supplies(client));
5876 }
5877 else
5878 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
5879 }
5880 break;
5881
5882 case HTTP_STATE_POST :
5883 if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
5884 "application/ipp"))
5885 {
5886 /*
5887 * Not an IPP request...
5888 */
5889
5890 return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
5891 }
5892
5893 /*
5894 * Read the IPP request...
5895 */
5896
5897 client->request = ippNew();
5898
5899 while ((ipp_state = ippRead(client->http,
5900 client->request)) != IPP_STATE_DATA)
5901 {
5902 if (ipp_state == IPP_STATE_ERROR)
5903 {
5904 fprintf(stderr, "%s IPP read error (%s).\n", client->hostname, cupsLastErrorString());
5905 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5906 return (0);
5907 }
5908 }
5909
5910 /*
5911 * Now that we have the IPP request, process the request...
5912 */
5913
5914 return (process_ipp(client));
5915
5916 default :
5917 break; /* Anti-compiler-warning-code */
5918 }
5919
5920 return (1);
5921}
5922
5923
5924/*
5925 * 'process_ipp()' - Process an IPP request.
5926 */
5927
5928static int /* O - 1 on success, 0 on error */
5929process_ipp(ippeve_client_t *client) /* I - Client */
5930{
5931 ipp_tag_t group; /* Current group tag */
5932 ipp_attribute_t *attr; /* Current attribute */
5933 ipp_attribute_t *charset; /* Character set attribute */
5934 ipp_attribute_t *language; /* Language attribute */
5935 ipp_attribute_t *uri; /* Printer URI attribute */
5936 int major, minor; /* Version number */
5937 const char *name; /* Name of attribute */
5938 http_status_t status; /* Authentication status */
5939
5940
5941 debug_attributes("Request", client->request, 1);
5942
5943 /*
5944 * First build an empty response message for this request...
5945 */
5946
5947 client->operation_id = ippGetOperation(client->request);
5948 client->response = ippNewResponse(client->request);
5949
5950 /*
5951 * Then validate the request header and required attributes...
5952 */
5953
5954 major = ippGetVersion(client->request, &minor);
5955
5956 if (major < 1 || major > 2)
5957 {
5958 /*
5959 * Return an error, since we only support IPP 1.x and 2.x.
5960 */
5961
5962 respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, "Bad request version number %d.%d.", major, minor);
5963 }
5964 else if ((major * 10 + minor) > MaxVersion)
5965 {
5966 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
5967 httpFlush(client->http); /* Flush trailing (junk) data */
5968
5969 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5970 return (0);
5971 }
5972 else if (ippGetRequestId(client->request) <= 0)
5973 {
5974 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.", ippGetRequestId(client->request));
5975 }
5976 else if (!ippFirstAttribute(client->request))
5977 {
5978 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No attributes in request.");
5979 }
5980 else
5981 {
5982 /*
5983 * Make sure that the attributes are provided in the correct order and
5984 * don't repeat groups...
5985 */
5986
5987 for (attr = ippFirstAttribute(client->request),
5988 group = ippGetGroupTag(attr);
5989 attr;
5990 attr = ippNextAttribute(client->request))
5991 {
5992 if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
5993 {
5994 /*
5995 * Out of order; return an error...
5996 */
5997
5998 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5999 "Attribute groups are out of order (%x < %x).",
6000 ippGetGroupTag(attr), group);
6001 break;
6002 }
6003 else
6004 group = ippGetGroupTag(attr);
6005 }
6006
6007 if (!attr)
6008 {
6009 /*
6010 * Then make sure that the first three attributes are:
6011 *
6012 * attributes-charset
6013 * attributes-natural-language
6014 * printer-uri/job-uri
6015 */
6016
6017 attr = ippFirstAttribute(client->request);
6018 name = ippGetName(attr);
6019 if (attr && name && !strcmp(name, "attributes-charset") &&
6020 ippGetValueTag(attr) == IPP_TAG_CHARSET)
6021 charset = attr;
6022 else
6023 charset = NULL;
6024
6025 attr = ippNextAttribute(client->request);
6026 name = ippGetName(attr);
6027
6028 if (attr && name && !strcmp(name, "attributes-natural-language") &&
6029 ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
6030 language = attr;
6031 else
6032 language = NULL;
6033
6034 if ((attr = ippFindAttribute(client->request, "printer-uri",
6035 IPP_TAG_URI)) != NULL)
6036 uri = attr;
6037 else if ((attr = ippFindAttribute(client->request, "job-uri",
6038 IPP_TAG_URI)) != NULL)
6039 uri = attr;
6040 else
6041 uri = NULL;
6042
6043 if (charset &&
6044 strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
6045 strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
6046 {
6047 /*
6048 * Bad character set...
6049 */
6050
6051 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6052 "Unsupported character set \"%s\".",
6053 ippGetString(charset, 0, NULL));
6054 }
6055 else if (!charset || !language || !uri)
6056 {
6057 /*
6058 * Return an error, since attributes-charset,
6059 * attributes-natural-language, and printer-uri/job-uri are required
6060 * for all operations.
6061 */
6062
6063 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6064 "Missing required attributes.");
6065 }
6066 else
6067 {
6068 char scheme[32], /* URI scheme */
6069 userpass[32], /* Username/password in URI */
6070 host[256], /* Host name in URI */
6071 resource[256]; /* Resource path in URI */
6072 int port; /* Port number in URI */
6073
6074 name = ippGetName(uri);
6075
6076 if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
6077 scheme, sizeof(scheme),
6078 userpass, sizeof(userpass),
6079 host, sizeof(host), &port,
6080 resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
6081 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
6082 "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
6083 else if ((!strcmp(name, "job-uri") &&
6084 strncmp(resource, "/ipp/print/", 11)) ||
6085 (!strcmp(name, "printer-uri") &&
6086 strcmp(resource, "/ipp/print")))
6087 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
6088 name, ippGetString(uri, 0, NULL));
6089 else if (client->operation_id != IPP_OP_GET_PRINTER_ATTRIBUTES && (status = authenticate_request(client)) != HTTP_STATUS_CONTINUE)
6090 {
6091 httpFlush(client->http);
6092
6093 return (respond_http(client, status, NULL, NULL, 0));
6094 }
6095 else
6096 {
6097 /*
6098 * Handle HTTP Expect...
6099 */
6100
6101 if (httpGetExpect(client->http))
6102 {
6103 if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
6104 {
6105 /*
6106 * Send 100-continue header...
6107 */
6108
6109 if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
6110 return (0);
6111 }
6112 else
6113 {
6114 /*
6115 * Send 417-expectation-failed header...
6116 */
6117
6118 if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
6119 return (0);
6120
6121 httpFlush(client->http);
6122 return (1);
6123 }
6124 }
6125
6126 /*
6127 * Try processing the operation...
6128 */
6129
6130 switch (client->operation_id)
6131 {
6132 case IPP_OP_PRINT_JOB :
6133 ipp_print_job(client);
6134 break;
6135
6136 case IPP_OP_PRINT_URI :
6137 ipp_print_uri(client);
6138 break;
6139
6140 case IPP_OP_VALIDATE_JOB :
6141 ipp_validate_job(client);
6142 break;
6143
6144 case IPP_OP_CREATE_JOB :
6145 ipp_create_job(client);
6146 break;
6147
6148 case IPP_OP_SEND_DOCUMENT :
6149 ipp_send_document(client);
6150 break;
6151
6152 case IPP_OP_SEND_URI :
6153 ipp_send_uri(client);
6154 break;
6155
6156 case IPP_OP_CANCEL_JOB :
6157 ipp_cancel_job(client);
6158 break;
6159
6160 case IPP_OP_GET_JOB_ATTRIBUTES :
6161 ipp_get_job_attributes(client);
6162 break;
6163
6164 case IPP_OP_GET_JOBS :
6165 ipp_get_jobs(client);
6166 break;
6167
6168 case IPP_OP_GET_PRINTER_ATTRIBUTES :
6169 ipp_get_printer_attributes(client);
6170 break;
6171
6172 case IPP_OP_CLOSE_JOB :
6173 ipp_close_job(client);
6174 break;
6175
6176 case IPP_OP_IDENTIFY_PRINTER :
6177 ipp_identify_printer(client);
6178 break;
6179
6180 default :
6181 respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
6182 "Operation not supported.");
6183 break;
6184 }
6185 }
6186 }
6187 }
6188 }
6189
6190 /*
6191 * Send the HTTP header and return...
6192 */
6193
6194 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
6195 httpFlush(client->http); /* Flush trailing (junk) data */
6196
6197 return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
6198 ippLength(client->response)));
6199}
6200
6201
6202/*
6203 * 'process_job()' - Process a print job.
6204 */
6205
6206static void * /* O - Thread exit status */
6207process_job(ippeve_job_t *job) /* I - Job */
6208{
6209 job->state = IPP_JSTATE_PROCESSING;
6210 job->printer->state = IPP_PSTATE_PROCESSING;
6211 job->processing = time(NULL);
6212
6213 while (job->printer->state_reasons & IPPEVE_PREASON_MEDIA_EMPTY)
6214 {
6215 job->printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
6216
6217 sleep(1);
6218 }
6219
6220 job->printer->state_reasons &= (ippeve_preason_t)~IPPEVE_PREASON_MEDIA_NEEDED;
6221
6222 if (job->printer->command)
6223 {
6224 /*
6225 * Execute a command with the job spool file and wait for it to complete...
6226 */
6227
6228 int pid, /* Process ID */
6229 status; /* Exit status */
6230 struct timeval start, /* Start time */
6231 end; /* End time */
6232 char *myargv[3], /* Command-line arguments */
6233 *myenvp[400]; /* Environment variables */
6234 int myenvc; /* Number of environment variables */
6235 ipp_attribute_t *attr; /* Job attribute */
6236 char val[1280], /* IPP_NAME=value */
6237 *valptr; /* Pointer into string */
6238#ifndef _WIN32
6239 int mystdout = -1; /* File for stdout */
6240 int mypipe[2]; /* Pipe for stderr */
6241 char line[2048], /* Line from stderr */
6242 *ptr, /* Pointer into line */
6243 *endptr; /* End of line */
6244 ssize_t bytes; /* Bytes read */
6245#endif /* !_WIN32 */
6246
6247 fprintf(stderr, "[Job %d] Running command \"%s %s\".\n", job->id, job->printer->command, job->filename);
6248 gettimeofday(&start, NULL);
6249
6250 /*
6251 * Setup the command-line arguments...
6252 */
6253
6254 myargv[0] = job->printer->command;
6255 myargv[1] = job->filename;
6256 myargv[2] = NULL;
6257
6258 /*
6259 * Copy the current environment, then add environment variables for every
6260 * Job attribute and Printer -default attributes...
6261 */
6262
6263 for (myenvc = 0; environ[myenvc] && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); myenvc ++)
6264 myenvp[myenvc] = strdup(environ[myenvc]);
6265
6266 if (myenvc > (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 32))
6267 {
6268 fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id);
6269 job->state = IPP_JSTATE_ABORTED;
6270 goto error;
6271 }
6272
6273 snprintf(val, sizeof(val), "CONTENT_TYPE=%s", job->format);
6274 myenvp[myenvc ++] = strdup(val);
6275
6276 if (job->printer->device_uri)
6277 {
6278 snprintf(val, sizeof(val), "DEVICE_URI=%s", job->printer->device_uri);
6279 myenvp[myenvc ++] = strdup(val);
6280 }
6281
6282 if (job->printer->output_format)
6283 {
6284 snprintf(val, sizeof(val), "OUTPUT_TYPE=%s", job->printer->output_format);
6285 myenvp[myenvc ++] = strdup(val);
6286 }
6287
6288#if !CUPS_LITE
6289 if (job->printer->ppdfile)
6290 {
6291 snprintf(val, sizeof(val), "PPD=%s", job->printer->ppdfile);
6292 myenvp[myenvc++] = strdup(val);
6293 }
6294#endif /* !CUPS_LITE */
6295
6296 for (attr = ippFirstAttribute(job->printer->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->printer->attrs))
6297 {
6298 /*
6299 * Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and
6300 * "pwg-xxx" to "IPP_PWG_XXX", then add the value(s) from the attribute.
6301 */
6302
6303 const char *name = ippGetName(attr),
6304 /* Attribute name */
6305 *suffix = strstr(name, "-default");
6306 /* Suffix on attribute name */
6307
6308 if (strncmp(name, "pwg-", 4) && (!suffix || suffix[8]))
6309 continue;
6310
6311 valptr = val;
6312 *valptr++ = 'I';
6313 *valptr++ = 'P';
6314 *valptr++ = 'P';
6315 *valptr++ = '_';
6316 while (*name && valptr < (val + sizeof(val) - 2))
6317 {
6318 if (*name == '-')
6319 *valptr++ = '_';
6320 else
6321 *valptr++ = (char)toupper(*name & 255);
6322
6323 name ++;
6324 }
6325 *valptr++ = '=';
6326 ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
6327
6328 myenvp[myenvc++] = strdup(val);
6329 }
6330
6331 for (attr = ippFirstAttribute(job->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->attrs))
6332 {
6333 /*
6334 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6335 * value(s) from the attribute.
6336 */
6337
6338 const char *name = ippGetName(attr);
6339 /* Attribute name */
6340
6341 if (!name)
6342 continue;
6343
6344 valptr = val;
6345 *valptr++ = 'I';
6346 *valptr++ = 'P';
6347 *valptr++ = 'P';
6348 *valptr++ = '_';
6349 while (*name && valptr < (val + sizeof(val) - 2))
6350 {
6351 if (*name == '-')
6352 *valptr++ = '_';
6353 else
6354 *valptr++ = (char)toupper(*name & 255);
6355
6356 name ++;
6357 }
6358 *valptr++ = '=';
6359 ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
6360
6361 myenvp[myenvc++] = strdup(val);
6362 }
6363
6364 if (attr)
6365 {
6366 fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id);
6367 job->state = IPP_JSTATE_ABORTED;
6368 goto error;
6369 }
6370
6371 myenvp[myenvc] = NULL;
6372
6373 /*
6374 * Now run the program...
6375 */
6376
6377#ifdef _WIN32
6378 status = _spawnvpe(_P_WAIT, job->printer->command, myargv, myenvp);
6379
6380#else
6381 if (job->printer->device_uri)
6382 {
6383 char scheme[32], /* URI scheme */
6384 userpass[256], /* username:password (unused) */
6385 host[256], /* Hostname or IP address */
6386 resource[256]; /* Resource path */
6387 int port; /* Port number */
6388
6389
6390 if (httpSeparateURI(HTTP_URI_CODING_ALL, job->printer->device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
6391 {
6392 fprintf(stderr, "[Job %d] Bad device URI \"%s\".\n", job->id, job->printer->device_uri);
6393 }
6394 else if (!strcmp(scheme, "file"))
6395 {
6396 struct stat fileinfo; /* See if this is a file or directory... */
6397
6398 if (stat(resource, &fileinfo))
6399 {
6400 if (errno == ENOENT)
6401 {
6402 if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC, 0666)) >= 0)
6403 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
6404 else
6405 fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno));
6406 }
6407 else
6408 fprintf(stderr, "[Job %d] Unable to access \"%s\": %s\n", job->id, resource, strerror(errno));
6409 }
6410 else if (S_ISDIR(fileinfo.st_mode))
6411 {
6412 if ((mystdout = create_job_file(job, line, sizeof(line), resource, "prn")) >= 0)
6413 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line);
6414 else
6415 fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, line, strerror(errno));
6416 }
6417 else if (!S_ISREG(fileinfo.st_mode))
6418 {
6419 if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC, 0666)) >= 0)
6420 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
6421 else
6422 fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno));
6423 }
6424 else if ((mystdout = open(resource, O_WRONLY)) >= 0)
6425 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
6426 else
6427 fprintf(stderr, "[Job %d] Unable to open \"%s\": %s\n", job->id, resource, strerror(errno));
6428 }
6429 else if (!strcmp(scheme, "socket"))
6430 {
6431 http_addrlist_t *addrlist; /* List of addresses */
6432 char service[32]; /* Service number */
6433
6434 snprintf(service, sizeof(service), "%d", port);
6435
6436 if ((addrlist = httpAddrGetList(host, AF_UNSPEC, service)) == NULL)
6437 fprintf(stderr, "[Job %d] Unable to find \"%s\": %s\n", job->id, host, cupsLastErrorString());
6438 else if (!httpAddrConnect2(addrlist, &mystdout, 30000, &(job->cancel)))
6439 fprintf(stderr, "[Job %d] Unable to connect to \"%s\": %s\n", job->id, host, cupsLastErrorString());
6440
6441 httpAddrFreeList(addrlist);
6442 }
6443 else
6444 {
6445 fprintf(stderr, "[Job %d] Unsupported device URI scheme \"%s\".\n", job->id, scheme);
6446 }
6447 }
6448 else if ((mystdout = create_job_file(job, line, sizeof(line), job->printer->directory, "prn")) >= 0)
6449 {
6450 fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line);
6451 }
6452
6453 if (mystdout < 0)
6454 mystdout = open("/dev/null", O_WRONLY);
6455
6456 if (pipe(mypipe))
6457 {
6458 fprintf(stderr, "[Job %d] Unable to create pipe for stderr: %s\n", job->id, strerror(errno));
6459 mypipe[0] = mypipe[1] = -1;
6460 }
6461
6462 if ((pid = fork()) == 0)
6463 {
6464 /*
6465 * Child comes here...
6466 */
6467
6468 close(1);
6469 dup2(mystdout, 1);
6470 close(mystdout);
6471
6472 close(2);
6473 dup2(mypipe[1], 2);
6474 close(mypipe[0]);
6475 close(mypipe[1]);
6476
6477 execve(job->printer->command, myargv, myenvp);
6478 exit(errno);
6479 }
6480 else if (pid < 0)
6481 {
6482 /*
6483 * Unable to fork process...
6484 */
6485
6486 fprintf(stderr, "[Job %d] Unable to start job processing command: %s\n", job->id, strerror(errno));
6487 status = -1;
6488
6489 close(mystdout);
6490 close(mypipe[0]);
6491 close(mypipe[1]);
6492
6493 /*
6494 * Free memory used for environment...
6495 */
6496
6497 while (myenvc > 0)
6498 free(myenvp[-- myenvc]);
6499 }
6500 else
6501 {
6502 /*
6503 * Free memory used for environment...
6504 */
6505
6506 while (myenvc > 0)
6507 free(myenvp[-- myenvc]);
6508
6509 /*
6510 * Close the output file in the parent process...
6511 */
6512
6513 close(mystdout);
6514
6515 /*
6516 * If the pipe exists, read from it until EOF...
6517 */
6518
6519 if (mypipe[0] >= 0)
6520 {
6521 close(mypipe[1]);
6522
6523 endptr = line;
6524 while ((bytes = read(mypipe[0], endptr, sizeof(line) - (size_t)(endptr - line) - 1)) > 0)
6525 {
6526 endptr += bytes;
6527 *endptr = '\0';
6528
6529 while ((ptr = strchr(line, '\n')) != NULL)
6530 {
6531 int level = 3; /* Message log level */
6532
6533 *ptr++ = '\0';
6534
6535 if (!strncmp(line, "ATTR:", 5))
6536 {
6537 /*
6538 * Process job/printer attribute updates.
6539 */
6540
6541 process_attr_message(job, line);
6542 }
6543 else if (!strncmp(line, "DEBUG:", 6))
6544 {
6545 /*
6546 * Debug message...
6547 */
6548
6549 level = 2;
6550 }
6551 else if (!strncmp(line, "ERROR:", 6))
6552 {
6553 /*
6554 * Error message...
6555 */
6556
6557 level = 0;
6558 job->message = strdup(line + 6);
6559 job->msglevel = 0;
6560 }
6561 else if (!strncmp(line, "INFO:", 5))
6562 {
6563 /*
6564 * Informational/progress message...
6565 */
6566
6567 level = 1;
6568 if (job->msglevel)
6569 {
6570 job->message = strdup(line + 5);
6571 job->msglevel = 1;
6572 }
6573 }
6574 else if (!strncmp(line, "STATE:", 6))
6575 {
6576 /*
6577 * Process printer-state-reasons keywords.
6578 */
6579
6580 process_state_message(job, line);
6581 }
6582
6583 if (Verbosity >= level)
6584 fprintf(stderr, "[Job %d] Command - %s\n", job->id, line);
6585
6586 bytes = ptr - line;
6587 if (ptr < endptr)
6588 memmove(line, ptr, (size_t)(endptr - ptr));
6589 endptr -= bytes;
6590 *endptr = '\0';
6591 }
6592 }
6593
6594 close(mypipe[0]);
6595 }
6596
6597 /*
6598 * Wait for child to complete...
6599 */
6600
6601# ifdef HAVE_WAITPID
6602 while (waitpid(pid, &status, 0) < 0);
6603# else
6604 while (wait(&status) < 0);
6605# endif /* HAVE_WAITPID */
6606 }
6607#endif /* _WIN32 */
6608
6609 if (status)
6610 {
6611#ifndef _WIN32
6612 if (WIFEXITED(status))
6613#endif /* !_WIN32 */
6614 fprintf(stderr, "[Job %d] Command \"%s\" exited with status %d.\n", job->id, job->printer->command, WEXITSTATUS(status));
6615#ifndef _WIN32
6616 else
6617 fprintf(stderr, "[Job %d] Command \"%s\" terminated with signal %d.\n", job->id, job->printer->command, WTERMSIG(status));
6618#endif /* !_WIN32 */
6619 job->state = IPP_JSTATE_ABORTED;
6620 }
6621 else if (status < 0)
6622 job->state = IPP_JSTATE_ABORTED;
6623 else
6624 fprintf(stderr, "[Job %d] Command \"%s\" completed successfully.\n", job->id, job->printer->command);
6625
6626 /*
6627 * Report the total processing time...
6628 */
6629
6630 gettimeofday(&end, NULL);
6631
6632 fprintf(stderr, "[Job %d] Processing time was %.3f seconds.\n", job->id, end.tv_sec - start.tv_sec + 0.000001 * (end.tv_usec - start.tv_usec));
6633 }
6634 else
6635 {
6636 /*
6637 * Sleep for a random amount of time to simulate job processing.
6638 */
6639
6640 sleep((unsigned)(5 + (CUPS_RAND() % 11)));
6641 }
6642
6643 if (job->cancel)
6644 job->state = IPP_JSTATE_CANCELED;
6645 else if (job->state == IPP_JSTATE_PROCESSING)
6646 job->state = IPP_JSTATE_COMPLETED;
6647
6648 error:
6649
6650 job->completed = time(NULL);
6651 job->printer->state = IPP_PSTATE_IDLE;
6652 job->printer->active_job = NULL;
6653
6654 return (NULL);
6655}
6656
6657
6658/*
6659 * 'process_state_message()' - Process a STATE: message from a command.
6660 */
6661
6662static void
6663process_state_message(
6664 ippeve_job_t *job, /* I - Job */
6665 char *message) /* I - Message */
6666{
6667 int i; /* Looping var */
6668 ippeve_preason_t state_reasons, /* printer-state-reasons values */
6669 bit; /* Current reason bit */
6670 char *ptr, /* Pointer into message */
6671 *next; /* Next keyword in message */
6672 int remove; /* Non-zero if we are removing keywords */
6673
6674
6675 /*
6676 * Skip leading "STATE:" and any whitespace...
6677 */
6678
6679 for (message += 6; *message; message ++)
6680 if (*message != ' ' && *message != '\t')
6681 break;
6682
6683 /*
6684 * Support the following forms of message:
6685 *
6686 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6687 *
6688 * "-keyword[,keyword,...]" to remove keywords.
6689 *
6690 * "+keyword[,keyword,...]" to add keywords.
6691 *
6692 * Keywords may or may not have a suffix (-report, -warning, -error) per
6693 * RFC 8011.
6694 */
6695
6696 if (*message == '-')
6697 {
6698 remove = 1;
6699 state_reasons = job->printer->state_reasons;
6700 message ++;
6701 }
6702 else if (*message == '+')
6703 {
6704 remove = 0;
6705 state_reasons = job->printer->state_reasons;
6706 message ++;
6707 }
6708 else
6709 {
6710 remove = 0;
6711 state_reasons = IPPEVE_PREASON_NONE;
6712 }
6713
6714 while (*message)
6715 {
6716 if ((next = strchr(message, ',')) != NULL)
6717 *next++ = '\0';
6718
6719 if ((ptr = strstr(message, "-error")) != NULL)
6720 *ptr = '\0';
6721 else if ((ptr = strstr(message, "-report")) != NULL)
6722 *ptr = '\0';
6723 else if ((ptr = strstr(message, "-warning")) != NULL)
6724 *ptr = '\0';
6725
6726 for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
6727 {
6728 if (!strcmp(message, ippeve_preason_strings[i]))
6729 {
6730 if (remove)
6731 state_reasons &= ~bit;
6732 else
6733 state_reasons |= bit;
6734 }
6735 }
6736
6737 if (next)
6738 message = next;
6739 else
6740 break;
6741 }
6742
6743 job->printer->state_reasons = state_reasons;
6744}
6745
6746
6747/*
6748 * 'register_printer()' - Register a printer object via Bonjour.
6749 */
6750
6751static int /* O - 1 on success, 0 on error */
6752register_printer(
6753 ippeve_printer_t *printer, /* I - Printer */
6754 const char *subtypes) /* I - Service subtype(s) */
6755{
6756#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6757 ippeve_txt_t ipp_txt; /* Bonjour IPP TXT record */
6758 int i, /* Looping var */
6759 count; /* Number of values */
6760 ipp_attribute_t *color_supported,
6761 *document_format_supported,
6762 *printer_location,
6763 *printer_make_and_model,
6764 *printer_more_info,
6765 *printer_uuid,
6766 *sides_supported,
6767 *urf_supported; /* Printer attributes */
6768 const char *value; /* Value string */
6769 char formats[252], /* List of supported formats */
6770 urf[252], /* List of supported URF values */
6771 *ptr; /* Pointer into string */
6772
6773
6774 if (!strcmp(subtypes, "off"))
6775 return (1);
6776
6777 color_supported = ippFindAttribute(printer->attrs, "color-supported", IPP_TAG_BOOLEAN);
6778 document_format_supported = ippFindAttribute(printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE);
6779 printer_location = ippFindAttribute(printer->attrs, "printer-location", IPP_TAG_TEXT);
6780 printer_make_and_model = ippFindAttribute(printer->attrs, "printer-make-and-model", IPP_TAG_TEXT);
6781 printer_more_info = ippFindAttribute(printer->attrs, "printer-more-info", IPP_TAG_URI);
6782 printer_uuid = ippFindAttribute(printer->attrs, "printer-uuid", IPP_TAG_URI);
6783 sides_supported = ippFindAttribute(printer->attrs, "sides-supported", IPP_TAG_KEYWORD);
6784 urf_supported = ippFindAttribute(printer->attrs, "urf-supported", IPP_TAG_KEYWORD);
6785
6786 for (i = 0, count = ippGetCount(document_format_supported), ptr = formats; i < count; i ++)
6787 {
6788 value = ippGetString(document_format_supported, i, NULL);
6789
6790 if (!strcasecmp(value, "application/octet-stream"))
6791 continue;
6792
6793 if (ptr > formats && ptr < (formats + sizeof(formats) - 1))
6794 *ptr++ = ',';
6795
6796 strlcpy(ptr, value, sizeof(formats) - (size_t)(ptr - formats));
6797 ptr += strlen(ptr);
6798
6799 if (ptr >= (formats + sizeof(formats) - 1))
6800 break;
6801 }
6802
6803 urf[0] = '\0';
6804 for (i = 0, count = ippGetCount(urf_supported), ptr = urf; i < count; i ++)
6805 {
6806 value = ippGetString(urf_supported, i, NULL);
6807
6808 if (ptr > urf && ptr < (urf + sizeof(urf) - 1))
6809 *ptr++ = ',';
6810
6811 strlcpy(ptr, value, sizeof(urf) - (size_t)(ptr - urf));
6812 ptr += strlen(ptr);
6813
6814 if (ptr >= (urf + sizeof(urf) - 1))
6815 break;
6816 }
6817
6818#endif /* HAVE_DNSSD || HAVE_AVAHI */
6819#ifdef HAVE_DNSSD
6820 DNSServiceErrorType error; /* Error from Bonjour */
6821 char regtype[256]; /* Bonjour service type */
6822 uint32_t interface; /* Interface index */
6823
6824
6825 /*
6826 * Build the TXT record for IPP...
6827 */
6828
6829 TXTRecordCreate(&ipp_txt, 1024, NULL);
6830 TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print");
6831 if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
6832 TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(value), value);
6833 if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL)
6834 TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(value), value);
6835 if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
6836 TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(value), value);
6837 TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats), formats);
6838 TXTRecordSetValue(&ipp_txt, "Color", 1, ippGetBoolean(color_supported, 0) ? "T" : "F");
6839 TXTRecordSetValue(&ipp_txt, "Duplex", 1, ippGetCount(sides_supported) > 1 ? "T" : "F");
6840 if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
6841 TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(value) - 9, value + 9);
6842# ifdef HAVE_SSL
6843 TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2");
6844# endif /* HAVE_SSL */
6845 if (urf[0])
6846 TXTRecordSetValue(&ipp_txt, "URF", (uint8_t)strlen(urf), urf);
6847 TXTRecordSetValue(&ipp_txt, "txtvers", 1, "1");
6848 TXTRecordSetValue(&ipp_txt, "qtotal", 1, "1");
6849
6850 /*
6851 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6852 * defend our service name but not actually support LPD...
6853 */
6854
6855 interface = !strcmp(printer->hostname, "localhost") ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
6856
6857 printer->printer_ref = DNSSDMaster;
6858
6859 if ((error = DNSServiceRegister(&(printer->printer_ref), kDNSServiceFlagsShareConnection, interface, printer->dnssd_name, "_printer._tcp", NULL /* domain */, NULL /* host */, 0 /* port */, 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
6860 {
6861 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_printer._tcp", error);
6862 return (0);
6863 }
6864
6865 /*
6866 * Then register the _ipp._tcp (IPP) service type with the real port number to
6867 * advertise our IPP printer...
6868 */
6869
6870 printer->ipp_ref = DNSSDMaster;
6871
6872 if (subtypes && *subtypes)
6873 snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtypes);
6874 else
6875 strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
6876
6877 if ((error = DNSServiceRegister(&(printer->ipp_ref), kDNSServiceFlagsShareConnection, interface, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
6878 {
6879 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
6880 return (0);
6881 }
6882
6883# ifdef HAVE_SSL
6884 /*
6885 * Then register the _ipps._tcp (IPP) service type with the real port number to
6886 * advertise our IPPS printer...
6887 */
6888
6889 printer->ipps_ref = DNSSDMaster;
6890
6891 if (subtypes && *subtypes)
6892 snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtypes);
6893 else
6894 strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
6895
6896 if ((error = DNSServiceRegister(&(printer->ipps_ref), kDNSServiceFlagsShareConnection, interface, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
6897 {
6898 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
6899 return (0);
6900 }
6901# endif /* HAVE_SSL */
6902
6903 /*
6904 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6905 * real port number to advertise our IPP printer...
6906 */
6907
6908 printer->http_ref = DNSSDMaster;
6909
6910 if ((error = DNSServiceRegister(&(printer->http_ref), kDNSServiceFlagsShareConnection, interface, printer->dnssd_name, "_http._tcp,_printer", NULL /* domain */, NULL /* host */, htons(printer->port), 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
6911 {
6912 _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_http._tcp,_printer", error);
6913 return (0);
6914 }
6915
6916 TXTRecordDeallocate(&ipp_txt);
6917
6918#elif defined(HAVE_AVAHI)
6919 char temp[256]; /* Subtype service string */
6920
6921 /*
6922 * Create the TXT record...
6923 */
6924
6925 ipp_txt = NULL;
6926 ipp_txt = avahi_string_list_add_printf(ipp_txt, "rp=ipp/print");
6927 if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
6928 ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s", value);
6929 if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL)
6930 ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", value);
6931 if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
6932 ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", value);
6933 ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
6934 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", ippGetBoolean(color_supported, 0) ? "T" : "F");
6935 ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", ippGetCount(sides_supported) > 1 ? "T" : "F");
6936 if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
6937 ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", value + 9);
6938# ifdef HAVE_SSL
6939 ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2");
6940# endif /* HAVE_SSL */
6941 if (urf[0])
6942 ipp_txt = avahi_string_list_add_printf(ipp_txt, "URF=%s", urf);
6943 ipp_txt = avahi_string_list_add_printf(ipp_txt, "txtvers=1");
6944 ipp_txt = avahi_string_list_add_printf(ipp_txt, "qtotal=1");
6945
6946 /*
6947 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6948 */
6949
6950 avahi_threaded_poll_lock(DNSSDMaster);
6951
6952 printer->ipp_ref = avahi_entry_group_new(DNSSDClient, dnssd_callback, NULL);
6953
6954 avahi_entry_group_add_service_strlst(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_printer._tcp", NULL, NULL, 0, NULL);
6955
6956 /*
6957 * Then register the _ipp._tcp (IPP)...
6958 */
6959
6960 avahi_entry_group_add_service_strlst(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, NULL, printer->port, ipp_txt);
6961 if (subtypes && *subtypes)
6962 {
6963 char *temptypes = strdup(subtypes), *start, *end;
6964
6965 for (start = temptypes; *start; start = end)
6966 {
6967 if ((end = strchr(start, ',')) != NULL)
6968 *end++ = '\0';
6969 else
6970 end = start + strlen(start);
6971
6972 snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", start);
6973 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp);
6974 }
6975
6976 free(temptypes);
6977 }
6978
6979#ifdef HAVE_SSL
6980 /*
6981 * _ipps._tcp (IPPS) for secure printing...
6982 */
6983
6984 avahi_entry_group_add_service_strlst(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, NULL, printer->port, ipp_txt);
6985 if (subtypes && *subtypes)
6986 {
6987 char *temptypes = strdup(subtypes), *start, *end;
6988
6989 for (start = temptypes; *start; start = end)
6990 {
6991 if ((end = strchr(start, ',')) != NULL)
6992 *end++ = '\0';
6993 else
6994 end = start + strlen(start);
6995
6996 snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", start);
6997 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp);
6998 }
6999
7000 free(temptypes);
7001 }
7002#endif /* HAVE_SSL */
7003
7004 /*
7005 * Finally _http.tcp (HTTP) for the web interface...
7006 */
7007
7008 avahi_entry_group_add_service_strlst(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_http._tcp", NULL, NULL, printer->port, NULL);
7009 avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_http._tcp", NULL, "_printer._sub._http._tcp");
7010
7011 /*
7012 * Commit it...
7013 */
7014
7015 avahi_entry_group_commit(printer->ipp_ref);
7016 avahi_threaded_poll_unlock(DNSSDMaster);
7017
7018 avahi_string_list_free(ipp_txt);
7019#endif /* HAVE_DNSSD */
7020
7021 return (1);
7022}
7023
7024
7025/*
7026 * 'respond_http()' - Send a HTTP response.
7027 */
7028
7029int /* O - 1 on success, 0 on failure */
7030respond_http(
7031 ippeve_client_t *client, /* I - Client */
7032 http_status_t code, /* I - HTTP status of response */
7033 const char *content_encoding, /* I - Content-Encoding of response */
7034 const char *type, /* I - MIME media type of response */
7035 size_t length) /* I - Length of response */
7036{
7037 char message[1024]; /* Text message */
7038
7039
7040 fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
7041
7042 if (code == HTTP_STATUS_CONTINUE)
7043 {
7044 /*
7045 * 100-continue doesn't send any headers...
7046 */
7047
7048 return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
7049 }
7050
7051 /*
7052 * Format an error message...
7053 */
7054
7055 if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
7056 {
7057 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
7058
7059 type = "text/plain";
7060 length = strlen(message);
7061 }
7062 else
7063 message[0] = '\0';
7064
7065 /*
7066 * Send the HTTP response header...
7067 */
7068
7069 httpClearFields(client->http);
7070
7071 if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
7072 client->operation == HTTP_STATE_OPTIONS)
7073 httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
7074
7075 if (code == HTTP_STATUS_UNAUTHORIZED)
7076 {
7077 char value[256]; /* WWW-Authenticate value */
7078
7079 snprintf(value, sizeof(value), "Basic realm=\"%s\"", PAMService);
7080 httpSetField(client->http, HTTP_FIELD_WWW_AUTHENTICATE, value);
7081 }
7082
7083 if (type)
7084 {
7085 if (!strcmp(type, "text/html"))
7086 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
7087 "text/html; charset=utf-8");
7088 else
7089 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
7090
7091 if (content_encoding)
7092 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
7093 }
7094
7095 httpSetLength(client->http, length);
7096
7097 if (httpWriteResponse(client->http, code) < 0)
7098 return (0);
7099
7100 /*
7101 * Send the response data...
7102 */
7103
7104 if (message[0])
7105 {
7106 /*
7107 * Send a plain text message.
7108 */
7109
7110 if (httpPrintf(client->http, "%s", message) < 0)
7111 return (0);
7112
7113 if (httpWrite2(client->http, "", 0) < 0)
7114 return (0);
7115 }
7116 else if (client->response)
7117 {
7118 /*
7119 * Send an IPP response...
7120 */
7121
7122 debug_attributes("Response", client->response, 2);
7123
7124 ippSetState(client->response, IPP_STATE_IDLE);
7125
7126 if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
7127 return (0);
7128 }
7129
7130 return (1);
7131}
7132
7133
7134/*
7135 * 'respond_ipp()' - Send an IPP response.
7136 */
7137
7138static void
7139respond_ipp(ippeve_client_t *client, /* I - Client */
7140 ipp_status_t status, /* I - status-code */
7141 const char *message, /* I - printf-style status-message */
7142 ...) /* I - Additional args as needed */
7143{
7144 const char *formatted = NULL; /* Formatted message */
7145
7146
7147 ippSetStatusCode(client->response, status);
7148
7149 if (message)
7150 {
7151 va_list ap; /* Pointer to additional args */
7152 ipp_attribute_t *attr; /* New status-message attribute */
7153
7154 va_start(ap, message);
7155 if ((attr = ippFindAttribute(client->response, "status-message", IPP_TAG_TEXT)) != NULL)
7156 ippSetStringfv(client->response, &attr, 0, message, ap);
7157 else
7158 attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, "status-message", NULL, message, ap);
7159 va_end(ap);
7160
7161 formatted = ippGetString(attr, 0, NULL);
7162 }
7163
7164 if (formatted)
7165 fprintf(stderr, "%s %s %s (%s)\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status), formatted);
7166 else
7167 fprintf(stderr, "%s %s %s\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status));
7168}
7169
7170
7171/*
7172 * 'respond_unsupported()' - Respond with an unsupported attribute.
7173 */
7174
7175static void
7176respond_unsupported(
7177 ippeve_client_t *client, /* I - Client */
7178 ipp_attribute_t *attr) /* I - Atribute */
7179{
7180 ipp_attribute_t *temp; /* Copy of attribute */
7181
7182
7183 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Unsupported %s %s%s value.", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)));
7184
7185 temp = ippCopyAttribute(client->response, attr, 0);
7186 ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
7187}
7188
7189
7190/*
7191 * 'run_printer()' - Run the printer service.
7192 */
7193
7194static void
7195run_printer(ippeve_printer_t *printer) /* I - Printer */
7196{
7197 int num_fds; /* Number of file descriptors */
7198 struct pollfd polldata[3]; /* poll() data */
7199 int timeout; /* Timeout for poll() */
7200 ippeve_client_t *client; /* New client */
7201
7202
7203 /*
7204 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
7205 */
7206
7207 polldata[0].fd = printer->ipv4;
7208 polldata[0].events = POLLIN;
7209
7210 polldata[1].fd = printer->ipv6;
7211 polldata[1].events = POLLIN;
7212
7213 num_fds = 2;
7214
7215#ifdef HAVE_DNSSD
7216 polldata[num_fds ].fd = DNSServiceRefSockFD(DNSSDMaster);
7217 polldata[num_fds ++].events = POLLIN;
7218#endif /* HAVE_DNSSD */
7219
7220 /*
7221 * Loop until we are killed or have a hard error...
7222 */
7223
7224 for (;;)
7225 {
7226 if (cupsArrayCount(printer->jobs))
7227 timeout = 10;
7228 else
7229 timeout = -1;
7230
7231 if (poll(polldata, (nfds_t)num_fds, timeout) < 0 && errno != EINTR)
7232 {
7233 perror("poll() failed");
7234 break;
7235 }
7236
7237 if (polldata[0].revents & POLLIN)
7238 {
7239 if ((client = create_client(printer, printer->ipv4)) != NULL)
7240 {
7241 _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
7242
7243 if (t)
7244 {
7245 _cupsThreadDetach(t);
7246 }
7247 else
7248 {
7249 perror("Unable to create client thread");
7250 delete_client(client);
7251 }
7252 }
7253 }
7254
7255 if (polldata[1].revents & POLLIN)
7256 {
7257 if ((client = create_client(printer, printer->ipv6)) != NULL)
7258 {
7259 _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
7260
7261 if (t)
7262 {
7263 _cupsThreadDetach(t);
7264 }
7265 else
7266 {
7267 perror("Unable to create client thread");
7268 delete_client(client);
7269 }
7270 }
7271 }
7272
7273#ifdef HAVE_DNSSD
7274 if (polldata[2].revents & POLLIN)
7275 DNSServiceProcessResult(DNSSDMaster);
7276#endif /* HAVE_DNSSD */
7277
7278 /*
7279 * Clean out old jobs...
7280 */
7281
7282 clean_jobs(printer);
7283 }
7284}
7285
7286
7287/*
7288 * 'show_media()' - Show media load state.
7289 */
7290
7291static int /* O - 1 on success, 0 on failure */
7292show_media(ippeve_client_t *client) /* I - Client connection */
7293{
7294 ippeve_printer_t *printer = client->printer;
7295 /* Printer */
7296 int i, j, /* Looping vars */
7297 num_ready, /* Number of ready media */
7298 num_sizes, /* Number of media sizes */
7299 num_sources, /* Number of media sources */
7300 num_types; /* Number of media types */
7301 ipp_attribute_t *media_col_ready,/* media-col-ready attribute */
7302 *media_ready, /* media-ready attribute */
7303 *media_sizes, /* media-supported attribute */
7304 *media_sources, /* media-source-supported attribute */
7305 *media_types, /* media-type-supported attribute */
7306 *input_tray; /* printer-input-tray attribute */
7307 ipp_t *media_col; /* media-col value */
7308 const char *media_size, /* media value */
7309 *media_source, /* media-source value */
7310 *media_type, /* media-type value */
7311 *ready_size, /* media-col-ready media-size[-name] value */
7312 *ready_source, /* media-col-ready media-source value */
7313 *ready_tray, /* printer-input-tray value */
7314 *ready_type; /* media-col-ready media-type value */
7315 char tray_str[1024], /* printer-input-tray string value */
7316 *tray_ptr; /* Pointer into value */
7317 int tray_len; /* Length of printer-input-tray value */
7318 int ready_sheets; /* printer-input-tray sheets value */
7319 int num_options = 0;/* Number of form options */
7320 cups_option_t *options = NULL;/* Form options */
7321 static const int sheets[] = /* Number of sheets */
7322 {
7323 250,
7324 125,
7325 50,
7326 25,
7327 5,
7328 0,
7329 -2
7330 };
7331
7332
7333 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7334 return (0);
7335
7336 html_header(client, printer->name, 0);
7337
7338 if ((media_col_ready = ippFindAttribute(printer->attrs, "media-col-ready", IPP_TAG_BEGIN_COLLECTION)) == NULL)
7339 {
7340 html_printf(client, "<p>Error: No media-col-ready defined for printer.</p>\n");
7341 html_footer(client);
7342 return (1);
7343 }
7344
7345 media_ready = ippFindAttribute(printer->attrs, "media-ready", IPP_TAG_ZERO);
7346
7347 if ((media_sizes = ippFindAttribute(printer->attrs, "media-supported", IPP_TAG_ZERO)) == NULL)
7348 {
7349 html_printf(client, "<p>Error: No media-supported defined for printer.</p>\n");
7350 html_footer(client);
7351 return (1);
7352 }
7353
7354 if ((media_sources = ippFindAttribute(printer->attrs, "media-source-supported", IPP_TAG_ZERO)) == NULL)
7355 {
7356 html_printf(client, "<p>Error: No media-source-supported defined for printer.</p>\n");
7357 html_footer(client);
7358 return (1);
7359 }
7360
7361 if ((media_types = ippFindAttribute(printer->attrs, "media-type-supported", IPP_TAG_ZERO)) == NULL)
7362 {
7363 html_printf(client, "<p>Error: No media-type-supported defined for printer.</p>\n");
7364 html_footer(client);
7365 return (1);
7366 }
7367
7368 if ((input_tray = ippFindAttribute(printer->attrs, "printer-input-tray", IPP_TAG_STRING)) == NULL)
7369 {
7370 html_printf(client, "<p>Error: No printer-input-tray defined for printer.</p>\n");
7371 html_footer(client);
7372 return (1);
7373 }
7374
7375 num_ready = ippGetCount(media_col_ready);
7376 num_sizes = ippGetCount(media_sizes);
7377 num_sources = ippGetCount(media_sources);
7378 num_types = ippGetCount(media_types);
7379
7380 if (num_sources != ippGetCount(input_tray))
7381 {
7382 html_printf(client, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n");
7383 html_footer(client);
7384 return (1);
7385 }
7386
7387 /*
7388 * Process form data if present...
7389 */
7390
7391 if (printer->web_forms)
7392 num_options = parse_options(client, &options);
7393
7394 if (num_options > 0)
7395 {
7396 /*
7397 * WARNING: A real printer/server implementation MUST NOT implement
7398 * media updates via a GET request - GET requests are supposed to be
7399 * idempotent (without side-effects) and we obviously are not
7400 * authenticating access here. This form is provided solely to
7401 * enable testing and development!
7402 */
7403
7404 char name[255]; /* Form name */
7405 const char *val; /* Form value */
7406 pwg_media_t *media; /* Media info */
7407
7408 _cupsRWLockWrite(&printer->rwlock);
7409
7410 ippDeleteAttribute(printer->attrs, media_col_ready);
7411 media_col_ready = NULL;
7412
7413 if (media_ready)
7414 {
7415 ippDeleteAttribute(printer->attrs, media_ready);
7416 media_ready = NULL;
7417 }
7418
7419 printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MEDIA_LOW | IPPEVE_PREASON_MEDIA_EMPTY | IPPEVE_PREASON_MEDIA_NEEDED);
7420
7421 for (i = 0; i < num_sources; i ++)
7422 {
7423 media_source = ippGetString(media_sources, i, NULL);
7424
7425 if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
7426 continue;
7427
7428 snprintf(name, sizeof(name), "size%d", i);
7429 if ((media_size = cupsGetOption(name, num_options, options)) != NULL && (media = pwgMediaForPWG(media_size)) != NULL)
7430 {
7431 snprintf(name, sizeof(name), "type%d", i);
7432 if ((media_type = cupsGetOption(name, num_options, options)) != NULL && !*media_type)
7433 media_type = NULL;
7434
7435 if (media_ready)
7436 ippSetString(printer->attrs, &media_ready, ippGetCount(media_ready), media_size);
7437 else
7438 media_ready = ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, media_size);
7439
7440 media_col = create_media_col(media_size, media_source, media_type, media->width, media->length, -1, -1, -1, -1);
7441
7442 if (media_col_ready)
7443 ippSetCollection(printer->attrs, &media_col_ready, ippGetCount(media_col_ready), media_col);
7444 else
7445 media_col_ready = ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-ready", media_col);
7446 ippDelete(media_col);
7447 }
7448 else
7449 media = NULL;
7450
7451 snprintf(name, sizeof(name), "level%d", i);
7452 if ((val = cupsGetOption(name, num_options, options)) != NULL)
7453 ready_sheets = atoi(val);
7454 else
7455 ready_sheets = 0;
7456
7457 snprintf(tray_str, sizeof(tray_str), "type=sheetFeedAuto%sRemovableTray;mediafeed=%d;mediaxfeed=%d;maxcapacity=%d;level=%d;status=0;name=%s;", !strcmp(media_source, "by-pass-tray") ? "Non" : "", media ? media->length : 0, media ? media->width : 0, strcmp(media_source, "by-pass-tray") ? 250 : 25, ready_sheets, media_source);
7458
7459 ippSetOctetString(printer->attrs, &input_tray, i, tray_str, (int)strlen(tray_str));
7460
7461 if (ready_sheets == 0)
7462 {
7463 printer->state_reasons |= IPPEVE_PREASON_MEDIA_EMPTY;
7464 if (printer->active_job)
7465 printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
7466 }
7467 else if (ready_sheets < 25 && ready_sheets > 0)
7468 printer->state_reasons |= IPPEVE_PREASON_MEDIA_LOW;
7469 }
7470
7471 if (!media_col_ready)
7472 media_col_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-col-ready");
7473
7474 if (!media_ready)
7475 media_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-ready");
7476
7477 _cupsRWUnlock(&printer->rwlock);
7478 }
7479
7480 if (printer->web_forms)
7481 html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
7482
7483 html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
7484 for (i = 0; i < num_sources; i ++)
7485 {
7486 media_source = ippGetString(media_sources, i, NULL);
7487
7488 if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
7489 continue;
7490
7491 for (j = 0, ready_size = NULL, ready_type = NULL; j < num_ready; j ++)
7492 {
7493 media_col = ippGetCollection(media_col_ready, j);
7494 ready_size = ippGetString(ippFindAttribute(media_col, "media-size-name", IPP_TAG_ZERO), 0, NULL);
7495 ready_source = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL);
7496 ready_type = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL);
7497
7498 if (ready_source && !strcmp(ready_source, media_source))
7499 break;
7500
7501 ready_source = NULL;
7502 ready_size = NULL;
7503 ready_type = NULL;
7504 }
7505
7506 html_printf(client, "<tr><th>%s:</th>", media_source);
7507
7508 /*
7509 * Media size...
7510 */
7511
7512 if (printer->web_forms)
7513 {
7514 html_printf(client, "<td><select name=\"size%d\"><option value=\"\">None</option>", i);
7515 for (j = 0; j < num_sizes; j ++)
7516 {
7517 media_size = ippGetString(media_sizes, j, NULL);
7518
7519 html_printf(client, "<option%s>%s</option>", (ready_size && !strcmp(ready_size, media_size)) ? " selected" : "", media_size);
7520 }
7521 html_printf(client, "</select>");
7522 }
7523 else
7524 html_printf(client, "<td>%s", ready_size);
7525
7526 /*
7527 * Media type...
7528 */
7529
7530 if (printer->web_forms)
7531 {
7532 html_printf(client, " <select name=\"type%d\"><option value=\"\">None</option>", i);
7533 for (j = 0; j < num_types; j ++)
7534 {
7535 media_type = ippGetString(media_types, j, NULL);
7536
7537 html_printf(client, "<option%s>%s</option>", (ready_type && !strcmp(ready_type, media_type)) ? " selected" : "", media_type);
7538 }
7539 html_printf(client, "</select>");
7540 }
7541 else if (ready_type)
7542 html_printf(client, ", %s", ready_type);
7543
7544 /*
7545 * Level/sheets loaded...
7546 */
7547
7548 if ((ready_tray = ippGetOctetString(input_tray, i, &tray_len)) != NULL)
7549 {
7550 if (tray_len > (int)(sizeof(tray_str) - 1))
7551 tray_len = (int)sizeof(tray_str) - 1;
7552 memcpy(tray_str, ready_tray, (size_t)tray_len);
7553 tray_str[tray_len] = '\0';
7554
7555 if ((tray_ptr = strstr(tray_str, "level=")) != NULL)
7556 ready_sheets = atoi(tray_ptr + 6);
7557 else
7558 ready_sheets = 0;
7559 }
7560 else
7561 ready_sheets = 0;
7562
7563 if (printer->web_forms)
7564 {
7565 html_printf(client, " <select name=\"level%d\">", i);
7566 for (j = 0; j < (int)(sizeof(sheets) / sizeof(sheets[0])); j ++)
7567 {
7568 if (!strcmp(media_source, "by-pass-tray") && sheets[j] > 25)
7569 continue;
7570
7571 if (sheets[j] < 0)
7572 html_printf(client, "<option value=\"%d\"%s>Unknown</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "");
7573 else
7574 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "", sheets[j]);
7575 }
7576 html_printf(client, "</select></td></tr>\n");
7577 }
7578 else if (ready_sheets == 1)
7579 html_printf(client, ", 1 sheet</td></tr>\n");
7580 else if (ready_sheets > 0)
7581 html_printf(client, ", %d sheets</td></tr>\n", ready_sheets);
7582 else
7583 html_printf(client, "</td></tr>\n");
7584 }
7585
7586 if (printer->web_forms)
7587 {
7588 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\">");
7589 if (num_options > 0)
7590 html_printf(client, " <span class=\"badge\" id=\"status\">Media updated.</span>\n");
7591 html_printf(client, "</td></tr></table></form>\n");
7592
7593 if (num_options > 0)
7594 html_printf(client, "<script>\n"
7595 "setTimeout(hide_status, 3000);\n"
7596 "function hide_status() {\n"
7597 " var status = document.getElementById('status');\n"
7598 " status.style.display = 'none';\n"
7599 "}\n"
7600 "</script>\n");
7601 }
7602 else
7603 html_printf(client, "</table>\n");
7604
7605 html_footer(client);
7606
7607 return (1);
7608}
7609
7610
7611/*
7612 * 'show_status()' - Show printer/system state.
7613 */
7614
7615static int /* O - 1 on success, 0 on failure */
7616show_status(ippeve_client_t *client) /* I - Client connection */
7617{
7618 ippeve_printer_t *printer = client->printer;
7619 /* Printer */
7620 ippeve_job_t *job; /* Current job */
7621 int i; /* Looping var */
7622 ippeve_preason_t reason; /* Current reason */
7623 static const char * const reasons[] = /* Reason strings */
7624 {
7625 "Other",
7626 "Cover Open",
7627 "Input Tray Missing",
7628 "Marker Supply Empty",
7629 "Marker Supply Low",
7630 "Marker Waste Almost Full",
7631 "Marker Waste Full",
7632 "Media Empty",
7633 "Media Jam",
7634 "Media Low",
7635 "Media Needed",
7636 "Moving to Paused",
7637 "Paused",
7638 "Spool Area Full",
7639 "Toner Empty",
7640 "Toner Low"
7641 };
7642 static const char * const state_colors[] =
7643 { /* State colors */
7644 "#0C0", /* Idle */
7645 "#EE0", /* Processing */
7646 "#C00" /* Stopped */
7647 };
7648
7649
7650 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7651 return (0);
7652
7653 html_header(client, printer->name, printer->state == IPP_PSTATE_PROCESSING ? 5 : 15);
7654 html_printf(client, "<h1><img style=\"background: %s; border-radius: 10px; float: left; margin-right: 10px; padding: 10px;\" src=\"/icon.png\" width=\"64\" height=\"64\">%s Jobs</h1>\n", state_colors[printer->state - IPP_PSTATE_IDLE], printer->name);
7655 html_printf(client, "<p>%s, %d job(s).", printer->state == IPP_PSTATE_IDLE ? "Idle" : printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(printer->jobs));
7656 for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
7657 if (printer->state_reasons & reason)
7658 html_printf(client, "\n<br>&nbsp;&nbsp;&nbsp;&nbsp;%s", reasons[i]);
7659 html_printf(client, "</p>\n");
7660
7661 if (cupsArrayCount(printer->jobs) > 0)
7662 {
7663 _cupsRWLockRead(&(printer->rwlock));
7664
7665 html_printf(client, "<table class=\"striped\" summary=\"Jobs\"><thead><tr><th>Job #</th><th>Name</th><th>Owner</th><th>Status</th></tr></thead><tbody>\n");
7666 for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs); job; job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
7667 {
7668 char when[256], /* When job queued/started/finished */
7669 hhmmss[64]; /* Time HH:MM:SS */
7670
7671 switch (job->state)
7672 {
7673 case IPP_JSTATE_PENDING :
7674 case IPP_JSTATE_HELD :
7675 snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss)));
7676 break;
7677 case IPP_JSTATE_PROCESSING :
7678 case IPP_JSTATE_STOPPED :
7679 snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss)));
7680 break;
7681 case IPP_JSTATE_ABORTED :
7682 snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7683 break;
7684 case IPP_JSTATE_CANCELED :
7685 snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7686 break;
7687 case IPP_JSTATE_COMPLETED :
7688 snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
7689 break;
7690 }
7691
7692 html_printf(client, "<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", job->id, job->name, job->username, when);
7693 }
7694 html_printf(client, "</tbody></table>\n");
7695
7696 _cupsRWUnlock(&(printer->rwlock));
7697 }
7698
7699 html_footer(client);
7700
7701 return (1);
7702}
7703
7704
7705/*
7706 * 'show_supplies()' - Show printer supplies.
7707 */
7708
7709static int /* O - 1 on success, 0 on failure */
7710show_supplies(
7711 ippeve_client_t *client) /* I - Client connection */
7712{
7713 ippeve_printer_t *printer = client->printer;
7714 /* Printer */
7715 int i, /* Looping var */
7716 num_supply; /* Number of supplies */
7717 ipp_attribute_t *supply, /* printer-supply attribute */
7718 *supply_desc; /* printer-supply-description attribute */
7719 int num_options = 0; /* Number of form options */
7720 cups_option_t *options = NULL; /* Form options */
7721 int supply_len, /* Length of supply value */
7722 level; /* Supply level */
7723 const char *supply_value; /* Supply value */
7724 char supply_text[1024], /* Supply string */
7725 *supply_ptr; /* Pointer into supply string */
7726 static const char * const printer_supply[] =
7727 { /* printer-supply values */
7728 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
7729 "maxcapacity=100;level=%d;colorantname=unknown;",
7730 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
7731 "maxcapacity=100;level=%d;colorantname=black;",
7732 "index=3;class=supplyThatIsConsumed;type=toner;unit=percent;"
7733 "maxcapacity=100;level=%d;colorantname=cyan;",
7734 "index=4;class=supplyThatIsConsumed;type=toner;unit=percent;"
7735 "maxcapacity=100;level=%d;colorantname=magenta;",
7736 "index=5;class=supplyThatIsConsumed;type=toner;unit=percent;"
7737 "maxcapacity=100;level=%d;colorantname=yellow;"
7738 };
7739 static const char * const backgrounds[] =
7740 { /* Background colors for the supply-level bars */
7741 "#777 linear-gradient(#333,#777)",
7742 "#000 linear-gradient(#666,#000)",
7743 "#0FF linear-gradient(#6FF,#0FF)",
7744 "#F0F linear-gradient(#F6F,#F0F)",
7745 "#CC0 linear-gradient(#EE6,#EE0)"
7746 };
7747 static const char * const colors[] = /* Text colors for the supply-level bars */
7748 {
7749 "#fff",
7750 "#fff",
7751 "#000",
7752 "#000",
7753 "#000"
7754 };
7755
7756
7757 if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7758 return (0);
7759
7760 html_header(client, printer->name, 0);
7761
7762 if ((supply = ippFindAttribute(printer->attrs, "printer-supply", IPP_TAG_STRING)) == NULL)
7763 {
7764 html_printf(client, "<p>Error: No printer-supply defined for printer.</p>\n");
7765 html_footer(client);
7766 return (1);
7767 }
7768
7769 num_supply = ippGetCount(supply);
7770
7771 if ((supply_desc = ippFindAttribute(printer->attrs, "printer-supply-description", IPP_TAG_TEXT)) == NULL)
7772 {
7773 html_printf(client, "<p>Error: No printer-supply-description defined for printer.</p>\n");
7774 html_footer(client);
7775 return (1);
7776 }
7777
7778 if (num_supply != ippGetCount(supply_desc))
7779 {
7780 html_printf(client, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n");
7781 html_footer(client);
7782 return (1);
7783 }
7784
7785 if (printer->web_forms)
7786 num_options = parse_options(client, &options);
7787
7788 if (num_options > 0)
7789 {
7790 /*
7791 * WARNING: A real printer/server implementation MUST NOT implement
7792 * supply updates via a GET request - GET requests are supposed to be
7793 * idempotent (without side-effects) and we obviously are not
7794 * authenticating access here. This form is provided solely to
7795 * enable testing and development!
7796 */
7797
7798 char name[64]; /* Form field */
7799 const char *val; /* Form value */
7800
7801 _cupsRWLockWrite(&printer->rwlock);
7802
7803 ippDeleteAttribute(printer->attrs, supply);
7804 supply = NULL;
7805
7806 printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MARKER_SUPPLY_EMPTY | IPPEVE_PREASON_MARKER_SUPPLY_LOW | IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL | IPPEVE_PREASON_MARKER_WASTE_FULL | IPPEVE_PREASON_TONER_EMPTY | IPPEVE_PREASON_TONER_LOW);
7807
7808 for (i = 0; i < num_supply; i ++)
7809 {
7810 snprintf(name, sizeof(name), "supply%d", i);
7811 if ((val = cupsGetOption(name, num_options, options)) != NULL)
7812 {
7813 level = atoi(val); /* New level */
7814
7815 snprintf(supply_text, sizeof(supply_text), printer_supply[i], level);
7816 if (supply)
7817 ippSetOctetString(printer->attrs, &supply, ippGetCount(supply), supply_text, (int)strlen(supply_text));
7818 else
7819 supply = ippAddOctetString(printer->attrs, IPP_TAG_PRINTER, "printer-supply", supply_text, (int)strlen(supply_text));
7820
7821 if (i == 0)
7822 {
7823 if (level == 100)
7824 printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_FULL;
7825 else if (level > 90)
7826 printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL;
7827 }
7828 else
7829 {
7830 if (level == 0)
7831 printer->state_reasons |= IPPEVE_PREASON_TONER_EMPTY;
7832 else if (level < 10)
7833 printer->state_reasons |= IPPEVE_PREASON_TONER_LOW;
7834 }
7835 }
7836 }
7837
7838 _cupsRWUnlock(&printer->rwlock);
7839 }
7840
7841 if (printer->web_forms)
7842 html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
7843
7844 html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
7845 for (i = 0; i < num_supply; i ++)
7846 {
7847 supply_value = ippGetOctetString(supply, i, &supply_len);
7848 if (supply_len > (int)(sizeof(supply_text) - 1))
7849 supply_len = (int)sizeof(supply_text) - 1;
7850
7851 memcpy(supply_text, supply_value, (size_t)supply_len);
7852 supply_text[supply_len] = '\0';
7853
7854 if ((supply_ptr = strstr(supply_text, "level=")) != NULL)
7855 level = atoi(supply_ptr + 6);
7856 else
7857 level = 50;
7858
7859 if (printer->web_forms)
7860 html_printf(client, "<tr><th>%s:</th><td><input name=\"supply%d\" size=\"3\" value=\"%d\"></td>", ippGetString(supply_desc, i, NULL), i, level);
7861 else
7862 html_printf(client, "<tr><th>%s:</th>", ippGetString(supply_desc, i, NULL));
7863
7864 if (level < 10)
7865 html_printf(client, "<td class=\"meter\"><span class=\"bar\" style=\"background: %s; padding: 5px %dpx;\"></span>&nbsp;%d%%</td></tr>\n", backgrounds[i], level * 2, level);
7866 else
7867 html_printf(client, "<td class=\"meter\"><span class=\"bar\" style=\"background: %s; color: %s; padding: 5px %dpx;\">%d%%</span></td></tr>\n", backgrounds[i], colors[i], level * 2, level);
7868 }
7869
7870 if (printer->web_forms)
7871 {
7872 html_printf(client, "<tr><td></td><td colspan=\"2\"><input type=\"submit\" value=\"Update Supplies\">");
7873 if (num_options > 0)
7874 html_printf(client, " <span class=\"badge\" id=\"status\">Supplies updated.</span>\n");
7875 html_printf(client, "</td></tr>\n</table>\n</form>\n");
7876
7877 if (num_options > 0)
7878 html_printf(client, "<script>\n"
7879 "setTimeout(hide_status, 3000);\n"
7880 "function hide_status() {\n"
7881 " var status = document.getElementById('status');\n"
7882 " status.style.display = 'none';\n"
7883 "}\n"
7884 "</script>\n");
7885 }
7886 else
7887 html_printf(client, "</table>\n");
7888
7889 html_footer(client);
7890
7891 return (1);
7892}
7893
7894
7895/*
7896 * 'time_string()' - Return the local time in hours, minutes, and seconds.
7897 */
7898
7899static char *
7900time_string(time_t tv, /* I - Time value */
7901 char *buffer, /* I - Buffer */
7902 size_t bufsize) /* I - Size of buffer */
7903{
7904 struct tm date; /* Local time and date */
7905
7906 localtime_r(&tv, &date);
7907
7908 strftime(buffer, bufsize, "%X", &date);
7909
7910 return (buffer);
7911}
7912
7913
7914/*
7915 * 'usage()' - Show program usage.
7916 */
7917
7918static void
7919usage(int status) /* O - Exit status */
7920{
7921 _cupsLangPuts(stdout, _("Usage: ippeveprinter [options] \"name\""));
7922 _cupsLangPuts(stdout, _("Options:"));
7923 _cupsLangPuts(stdout, _("--help Show program help"));
7924 _cupsLangPuts(stdout, _("--no-web-forms Disable web forms for media and supplies"));
7925 _cupsLangPuts(stdout, _("--pam-service service Use the named PAM service"));
7926 _cupsLangPuts(stdout, _("--version Show program version"));
7927 _cupsLangPuts(stdout, _("-2 Set 2-sided printing support (default=1-sided)"));
7928 _cupsLangPuts(stdout, _("-A Enable authentication"));
7929 _cupsLangPuts(stdout, _("-D device-uri Set the device URI for the printer"));
7930 _cupsLangPuts(stdout, _("-F output-type/subtype Set the output format for the printer"));
7931#ifdef HAVE_SSL
7932 _cupsLangPuts(stdout, _("-K keypath Set location of server X.509 certificates and keys."));
7933#endif /* HAVE_SSL */
7934 _cupsLangPuts(stdout, _("-M manufacturer Set manufacturer name (default=Test)"));
7935 _cupsLangPuts(stdout, _("-P filename.ppd Load printer attributes from PPD file"));
7936 _cupsLangPuts(stdout, _("-V version Set default IPP version"));
7937 _cupsLangPuts(stdout, _("-a filename.conf Load printer attributes from conf file"));
7938 _cupsLangPuts(stdout, _("-c command Set print command"));
7939 _cupsLangPuts(stdout, _("-d spool-directory Set spool directory"));
7940 _cupsLangPuts(stdout, _("-f type/subtype[,...] Set supported file types"));
7941 _cupsLangPuts(stdout, _("-i iconfile.png Set icon file"));
7942 _cupsLangPuts(stdout, _("-k Keep job spool files"));
7943 _cupsLangPuts(stdout, _("-l location Set location of printer"));
7944 _cupsLangPuts(stdout, _("-m model Set model name (default=Printer)"));
7945 _cupsLangPuts(stdout, _("-n hostname Set hostname for printer"));
7946 _cupsLangPuts(stdout, _("-p port Set port number for printer"));
7947 _cupsLangPuts(stdout, _("-r subtype,[subtype] Set DNS-SD service subtype"));
7948 _cupsLangPuts(stdout, _("-s speed[,color-speed] Set speed in pages per minute"));
7949 _cupsLangPuts(stdout, _("-v Be verbose"));
7950
7951 exit(status);
7952}
7953
7954
7955/*
7956 * 'valid_doc_attributes()' - Determine whether the document attributes are
7957 * valid.
7958 *
7959 * When one or more document attributes are invalid, this function adds a
7960 * suitable response and attributes to the unsupported group.
7961 */
7962
7963static int /* O - 1 if valid, 0 if not */
7964valid_doc_attributes(
7965 ippeve_client_t *client) /* I - Client */
7966{
7967 int valid = 1; /* Valid attributes? */
7968 ipp_op_t op = ippGetOperation(client->request);
7969 /* IPP operation */
7970 const char *op_name = ippOpString(op);
7971 /* IPP operation name */
7972 ipp_attribute_t *attr, /* Current attribute */
7973 *supported; /* xxx-supported attribute */
7974 const char *compression = NULL,
7975 /* compression value */
7976 *format = NULL; /* document-format value */
7977
7978
7979 /*
7980 * Check operation attributes...
7981 */
7982
7983 if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
7984 {
7985 /*
7986 * If compression is specified, only accept a supported value in a Print-Job
7987 * or Send-Document request...
7988 */
7989
7990 compression = ippGetString(attr, 0, NULL);
7991 supported = ippFindAttribute(client->printer->attrs,
7992 "compression-supported", IPP_TAG_KEYWORD);
7993
7994 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
7995 ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
7996 (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
7997 op != IPP_OP_VALIDATE_JOB) ||
7998 !ippContainsString(supported, compression))
7999 {
8000 respond_unsupported(client, attr);
8001 valid = 0;
8002 }
8003 else
8004 {
8005 fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression);
8006
8007 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression);
8008
8009 if (strcmp(compression, "none"))
8010 {
8011 if (Verbosity)
8012 fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression);
8013 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
8014 }
8015 }
8016 }
8017
8018 /*
8019 * Is it a format we support?
8020 */
8021
8022 if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
8023 {
8024 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
8025 ippGetGroupTag(attr) != IPP_TAG_OPERATION)
8026 {
8027 respond_unsupported(client, attr);
8028 valid = 0;
8029 }
8030 else
8031 {
8032 format = ippGetString(attr, 0, NULL);
8033
8034 fprintf(stderr, "%s %s document-format=\"%s\"\n", client->hostname, op_name, format);
8035
8036 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format);
8037 }
8038 }
8039 else
8040 {
8041 format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL);
8042 if (!format)
8043 format = "application/octet-stream"; /* Should never happen */
8044
8045 attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
8046 }
8047
8048 if (format && !strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
8049 {
8050 /*
8051 * Auto-type the file using the first 8 bytes of the file...
8052 */
8053
8054 unsigned char header[8]; /* First 8 bytes of file */
8055
8056 memset(header, 0, sizeof(header));
8057 httpPeek(client->http, (char *)header, sizeof(header));
8058
8059 if (!memcmp(header, "%PDF", 4))
8060 format = "application/pdf";
8061 else if (!memcmp(header, "%!", 2))
8062 format = "application/postscript";
8063 else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
8064 format = "image/jpeg";
8065 else if (!memcmp(header, "\211PNG", 4))
8066 format = "image/png";
8067 else if (!memcmp(header, "RAS2", 4))
8068 format = "image/pwg-raster";
8069 else if (!memcmp(header, "UNIRAST", 8))
8070 format = "image/urf";
8071 else
8072 format = NULL;
8073
8074 if (format)
8075 {
8076 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n", client->hostname, op_name, format);
8077
8078 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
8079 }
8080 }
8081
8082 if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
8083 {
8084 respond_unsupported(client, attr);
8085 valid = 0;
8086 }
8087
8088 /*
8089 * document-name
8090 */
8091
8092 if ((attr = ippFindAttribute(client->request, "document-name", IPP_TAG_NAME)) != NULL)
8093 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
8094
8095 return (valid);
8096}
8097
8098
8099/*
8100 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
8101 *
8102 * When one or more job attributes are invalid, this function adds a suitable
8103 * response and attributes to the unsupported group.
8104 */
8105
8106static int /* O - 1 if valid, 0 if not */
8107valid_job_attributes(
8108 ippeve_client_t *client) /* I - Client */
8109{
8110 int i, /* Looping var */
8111 count, /* Number of values */
8112 valid = 1; /* Valid attributes? */
8113 ipp_attribute_t *attr, /* Current attribute */
8114 *supported; /* xxx-supported attribute */
8115
8116
8117 /*
8118 * Check operation attributes...
8119 */
8120
8121 valid = valid_doc_attributes(client);
8122
8123 /*
8124 * Check the various job template attributes...
8125 */
8126
8127 if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL)
8128 {
8129 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
8130 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
8131 {
8132 respond_unsupported(client, attr);
8133 valid = 0;
8134 }
8135 }
8136
8137 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
8138 {
8139 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
8140 {
8141 respond_unsupported(client, attr);
8142 valid = 0;
8143 }
8144 }
8145
8146 if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
8147 {
8148 if (ippGetCount(attr) != 1 ||
8149 (ippGetValueTag(attr) != IPP_TAG_NAME &&
8150 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8151 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
8152 strcmp(ippGetString(attr, 0, NULL), "no-hold"))
8153 {
8154 respond_unsupported(client, attr);
8155 valid = 0;
8156 }
8157 }
8158
8159 if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL)
8160 {
8161 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0)
8162 {
8163 respond_unsupported(client, attr);
8164 valid = 0;
8165 }
8166 }
8167
8168 if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL)
8169 {
8170 if (ippGetCount(attr) != 1 ||
8171 (ippGetValueTag(attr) != IPP_TAG_NAME &&
8172 ippGetValueTag(attr) != IPP_TAG_NAMELANG))
8173 {
8174 respond_unsupported(client, attr);
8175 valid = 0;
8176 }
8177
8178 ippSetGroupTag(client->request, &attr, IPP_TAG_JOB);
8179 }
8180 else
8181 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
8182
8183 if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL)
8184 {
8185 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
8186 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
8187 {
8188 respond_unsupported(client, attr);
8189 valid = 0;
8190 }
8191 }
8192
8193 if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
8194 {
8195 if (ippGetCount(attr) != 1 ||
8196 (ippGetValueTag(attr) != IPP_TAG_NAME &&
8197 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8198 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
8199 strcmp(ippGetString(attr, 0, NULL), "none"))
8200 {
8201 respond_unsupported(client, attr);
8202 valid = 0;
8203 }
8204 }
8205
8206 if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
8207 {
8208 if (ippGetCount(attr) != 1 ||
8209 (ippGetValueTag(attr) != IPP_TAG_NAME &&
8210 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8211 ippGetValueTag(attr) != IPP_TAG_KEYWORD))
8212 {
8213 respond_unsupported(client, attr);
8214 valid = 0;
8215 }
8216 else
8217 {
8218 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
8219
8220 if (!ippContainsString(supported, ippGetString(attr, 0, NULL)))
8221 {
8222 respond_unsupported(client, attr);
8223 valid = 0;
8224 }
8225 }
8226 }
8227
8228 if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL)
8229 {
8230 ipp_t *col, /* media-col collection */
8231 *size; /* media-size collection */
8232 ipp_attribute_t *member, /* Member attribute */
8233 *x_dim, /* x-dimension */
8234 *y_dim; /* y-dimension */
8235 int x_value, /* y-dimension value */
8236 y_value; /* x-dimension value */
8237
8238 if (ippGetCount(attr) != 1 ||
8239 ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
8240 {
8241 respond_unsupported(client, attr);
8242 valid = 0;
8243 }
8244
8245 col = ippGetCollection(attr, 0);
8246
8247 if ((member = ippFindAttribute(col, "media-size-name", IPP_TAG_ZERO)) != NULL)
8248 {
8249 if (ippGetCount(member) != 1 ||
8250 (ippGetValueTag(member) != IPP_TAG_NAME &&
8251 ippGetValueTag(member) != IPP_TAG_NAMELANG &&
8252 ippGetValueTag(member) != IPP_TAG_KEYWORD))
8253 {
8254 respond_unsupported(client, attr);
8255 valid = 0;
8256 }
8257 else
8258 {
8259 supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
8260
8261 if (!ippContainsString(supported, ippGetString(member, 0, NULL)))
8262 {
8263 respond_unsupported(client, attr);
8264 valid = 0;
8265 }
8266 }
8267 }
8268 else if ((member = ippFindAttribute(col, "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
8269 {
8270 if (ippGetCount(member) != 1)
8271 {
8272 respond_unsupported(client, attr);
8273 valid = 0;
8274 }
8275 else
8276 {
8277 size = ippGetCollection(member, 0);
8278
8279 if ((x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(x_dim) != 1 ||
8280 (y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(y_dim) != 1)
8281 {
8282 respond_unsupported(client, attr);
8283 valid = 0;
8284 }
8285 else
8286 {
8287 x_value = ippGetInteger(x_dim, 0);
8288 y_value = ippGetInteger(y_dim, 0);
8289 supported = ippFindAttribute(client->printer->attrs, "media-size-supported", IPP_TAG_BEGIN_COLLECTION);
8290 count = ippGetCount(supported);
8291
8292 for (i = 0; i < count ; i ++)
8293 {
8294 size = ippGetCollection(supported, i);
8295 x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_ZERO);
8296 y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_ZERO);
8297
8298 if (ippContainsInteger(x_dim, x_value) && ippContainsInteger(y_dim, y_value))
8299 break;
8300 }
8301
8302 if (i >= count)
8303 {
8304 respond_unsupported(client, attr);
8305 valid = 0;
8306 }
8307 }
8308 }
8309 }
8310 }
8311
8312 if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL)
8313 {
8314 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
8315 (strcmp(ippGetString(attr, 0, NULL),
8316 "separate-documents-uncollated-copies") &&
8317 strcmp(ippGetString(attr, 0, NULL),
8318 "separate-documents-collated-copies")))
8319 {
8320 respond_unsupported(client, attr);
8321 valid = 0;
8322 }
8323 }
8324
8325 if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL)
8326 {
8327 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
8328 ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
8329 ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
8330 {
8331 respond_unsupported(client, attr);
8332 valid = 0;
8333 }
8334 }
8335
8336 if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
8337 {
8338 if (ippGetValueTag(attr) != IPP_TAG_RANGE)
8339 {
8340 respond_unsupported(client, attr);
8341 valid = 0;
8342 }
8343 }
8344
8345 if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL)
8346 {
8347 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
8348 ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
8349 ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
8350 {
8351 respond_unsupported(client, attr);
8352 valid = 0;
8353 }
8354 }
8355
8356 if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
8357 {
8358 supported = ippFindAttribute(client->printer->attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION);
8359
8360 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION ||
8361 !supported)
8362 {
8363 respond_unsupported(client, attr);
8364 valid = 0;
8365 }
8366 else
8367 {
8368 int xdpi, /* Horizontal resolution for job template attribute */
8369 ydpi, /* Vertical resolution for job template attribute */
8370 sydpi; /* Vertical resolution for supported value */
8371 ipp_res_t units, /* Units for job template attribute */
8372 sunits; /* Units for supported value */
8373
8374 xdpi = ippGetResolution(attr, 0, &ydpi, &units);
8375 count = ippGetCount(supported);
8376
8377 for (i = 0; i < count; i ++)
8378 {
8379 if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits)
8380 break;
8381 }
8382
8383 if (i >= count)
8384 {
8385 respond_unsupported(client, attr);
8386 valid = 0;
8387 }
8388 }
8389 }
8390
8391 if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
8392 {
8393 const char *sides = ippGetString(attr, 0, NULL);
8394 /* "sides" value... */
8395
8396 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
8397 {
8398 respond_unsupported(client, attr);
8399 valid = 0;
8400 }
8401 else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
8402 {
8403 if (!ippContainsString(supported, sides))
8404 {
8405 respond_unsupported(client, attr);
8406 valid = 0;
8407 }
8408 }
8409 else if (strcmp(sides, "one-sided"))
8410 {
8411 respond_unsupported(client, attr);
8412 valid = 0;
8413 }
8414 }
8415
8416 return (valid);
8417}