2 * IPP Everywhere printer application for CUPS.
4 * Copyright © 2010-2019 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
11 * Disable deprecated stuff so we can verify that the public API is sufficient
12 * to implement a server.
15 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
19 * Include necessary headers...
22 #include <config.h> /* CUPS configuration header */
23 #include <cups/cups.h> /* Public API */
24 #include <cups/string-private.h> /* CUPS string functions */
25 #include <cups/thread-private.h> /* For multithreading functions */
38 # define WEXITSTATUS(s) (s)
39 # include <winsock2.h>
43 extern char **environ
;
45 # include <sys/fcntl.h>
46 # include <sys/wait.h>
52 #elif defined(HAVE_AVAHI)
53 # include <avahi-client/client.h>
54 # include <avahi-client/publish.h>
55 # include <avahi-common/error.h>
56 # include <avahi-common/thread-watch.h>
57 #endif /* HAVE_DNSSD */
58 #ifdef HAVE_SYS_MOUNT_H
59 # include <sys/mount.h>
60 #endif /* HAVE_SYS_MOUNT_H */
61 #ifdef HAVE_SYS_STATFS_H
62 # include <sys/statfs.h>
63 #endif /* HAVE_SYS_STATFS_H */
64 #ifdef HAVE_SYS_STATVFS_H
65 # include <sys/statvfs.h>
66 #endif /* HAVE_SYS_STATVFS_H */
69 #endif /* HAVE_SYS_VFS_H */
76 enum ippeve_preason_e
/* printer-state-reasons bit values */
78 IPPEVE_PREASON_NONE
= 0x0000, /* none */
79 IPPEVE_PREASON_OTHER
= 0x0001, /* other */
80 IPPEVE_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
81 IPPEVE_PREASON_INPUT_TRAY_MISSING
= 0x0004,
82 /* input-tray-missing */
83 IPPEVE_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
84 /* marker-supply-empty */
85 IPPEVE_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
86 /* marker-supply-low */
87 IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
88 /* marker-waste-almost-full */
89 IPPEVE_PREASON_MARKER_WASTE_FULL
= 0x0040,
90 /* marker-waste-full */
91 IPPEVE_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
92 IPPEVE_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
93 IPPEVE_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
94 IPPEVE_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
95 IPPEVE_PREASON_MOVING_TO_PAUSED
= 0x0800,
96 /* moving-to-paused */
97 IPPEVE_PREASON_PAUSED
= 0x1000, /* paused */
98 IPPEVE_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
99 IPPEVE_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
100 IPPEVE_PREASON_TONER_LOW
= 0x8000 /* toner-low */
102 typedef unsigned int ippeve_preason_t
; /* Bitfield for printer-state-reasons */
103 static const char * const ippeve_preason_strings
[] =
104 { /* Strings for each bit */
105 /* "none" is implied for no bits set */
108 "input-tray-missing",
109 "marker-supply-empty",
111 "marker-waste-almost-full",
124 typedef enum ippeve_media_class_e
126 IPPEVE_GENERAL
, /* General-purpose size */
127 IPPEVE_PHOTO_ONLY
, /* Photo-only size */
128 IPPEVE_ENV_ONLY
/* Envelope-only size */
129 } ippeve_media_class_t
;
131 typedef enum ippeve_media_size_e
133 IPPEVE_MEDIA_SIZE_NONE
= -1,
134 IPPEVE_MEDIA_SIZE_A4
,
135 IPPEVE_MEDIA_SIZE_A5
,
136 IPPEVE_MEDIA_SIZE_A6
,
137 IPPEVE_MEDIA_SIZE_DL
,
138 IPPEVE_MEDIA_SIZE_LEGAL
,
139 IPPEVE_MEDIA_SIZE_LETTER
,
140 IPPEVE_MEDIA_SIZE_COM10
,
141 IPPEVE_MEDIA_SIZE_3x5
,
143 IPPEVE_MEDIA_SIZE_4x6
,
144 IPPEVE_MEDIA_SIZE_5x7
145 } ippeve_media_size_t
;
146 static const char * const media_supported
[] =
147 { /* media-supported values */
148 "iso_a4_210x297mm", /* A4 */
149 "iso_a5_148x210mm", /* A5 */
150 "iso_a6_105x148mm", /* A6 */
151 "iso_dl_110x220mm", /* DL */
152 "na_legal_8.5x14in", /* Legal */
153 "na_letter_8.5x11in", /* Letter */
154 "na_number-10_4.125x9.5in", /* #10 */
155 "na_index-3x5_3x5in", /* 3x5 */
156 "oe_photo-l_3.5x5in", /* L */
157 "na_index-4x6_4x6in", /* 4x6 */
158 "na_5x7_5x7in" /* 5x7 aka 2L */
160 static const int media_col_sizes
[][3] =
161 { /* media-col-database sizes */
162 { 21000, 29700, IPPEVE_GENERAL
}, /* A4 */
163 { 14800, 21000, IPPEVE_PHOTO_ONLY
}, /* A5 */
164 { 10500, 14800, IPPEVE_PHOTO_ONLY
}, /* A6 */
165 { 11000, 22000, IPPEVE_ENV_ONLY
}, /* DL */
166 { 21590, 35560, IPPEVE_GENERAL
}, /* Legal */
167 { 21590, 27940, IPPEVE_GENERAL
}, /* Letter */
168 { 10477, 24130, IPPEVE_ENV_ONLY
}, /* #10 */
169 { 7630, 12700, IPPEVE_PHOTO_ONLY
}, /* 3x5 */
170 { 8890, 12700, IPPEVE_PHOTO_ONLY
}, /* L */
171 { 10160, 15240, IPPEVE_PHOTO_ONLY
}, /* 4x6 */
172 { 12700, 17780, IPPEVE_PHOTO_ONLY
} /* 5x7 aka 2L */
175 typedef enum ippeve_media_source_e
177 IPPEVE_MEDIA_SOURCE_NONE
= -1,
178 IPPEVE_MEDIA_SOURCE_AUTO
,
179 IPPEVE_MEDIA_SOURCE_MAIN
,
180 IPPEVE_MEDIA_SOURCE_MANUAL
,
181 IPPEVE_MEDIA_SOURCE_ENVELOPE
,
182 IPPEVE_MEDIA_SOURCE_PHOTO
183 } ippeve_media_source_t
;
184 static const char * const media_source_supported
[] =
185 /* media-source-supported values */
194 typedef enum ippeve_media_type_e
196 IPPEVE_MEDIA_TYPE_NONE
= -1,
197 IPPEVE_MEDIA_TYPE_AUTO
,
198 IPPEVE_MEDIA_TYPE_CARDSTOCK
,
199 IPPEVE_MEDIA_TYPE_ENVELOPE
,
200 IPPEVE_MEDIA_TYPE_LABELS
,
201 IPPEVE_MEDIA_TYPE_OTHER
,
202 IPPEVE_MEDIA_TYPE_GLOSSY
,
203 IPPEVE_MEDIA_TYPE_HIGH_GLOSS
,
204 IPPEVE_MEDIA_TYPE_MATTE
,
205 IPPEVE_MEDIA_TYPE_SATIN
,
206 IPPEVE_MEDIA_TYPE_SEMI_GLOSS
,
207 IPPEVE_MEDIA_TYPE_STATIONERY
,
208 IPPEVE_MEDIA_TYPE_LETTERHEAD
,
209 IPPEVE_MEDIA_TYPE_TRANSPARENCY
210 } ippeve_media_type_t
;
211 static const char * const media_type_supported
[] =
212 /* media-type-supported values */
219 "photographic-glossy",
220 "photographic-high-gloss",
221 "photographic-matte",
222 "photographic-satin",
223 "photographic-semi-gloss",
225 "stationery-letterhead",
229 typedef enum ippeve_supply_e
231 IPPEVE_SUPPLY_CYAN
, /* Cyan Toner */
232 IPPEVE_SUPPLY_MAGENTA
, /* Magenta Toner */
233 IPPEVE_SUPPLY_YELLOW
, /* Yellow Toner */
234 IPPEVE_SUPPLY_BLACK
, /* Black Toner */
235 IPPEVE_SUPPLY_WASTE
/* Waste Toner */
237 static const char * const printer_supplies
[] =
238 { /* printer-supply-description values */
247 * URL scheme for web resources...
251 # define WEB_SCHEME "https"
253 # define WEB_SCHEME "http"
254 #endif /* HAVE_SSL */
262 typedef DNSServiceRef ippeve_srv_t
; /* Service reference */
263 typedef TXTRecordRef ippeve_txt_t
; /* TXT record */
265 #elif defined(HAVE_AVAHI)
266 typedef AvahiEntryGroup
*_ipp_srv_t
; /* Service reference */
267 typedef AvahiStringList
*_ipp_txt_t
; /* TXT record */
270 typedef void *_ipp_srv_t
; /* Service reference */
271 typedef void *_ipp_txt_t
; /* TXT record */
272 #endif /* HAVE_DNSSD */
274 typedef struct ippeve_filter_s
/**** Attribute filter ****/
276 cups_array_t
*ra
; /* Requested attributes */
277 ipp_tag_t group_tag
; /* Group to copy */
280 typedef struct ippeve_job_s ippeve_job_t
;
282 typedef struct ippeve_printer_s
/**** Printer data ****/
284 int ipv4
, /* IPv4 listener */
285 ipv6
; /* IPv6 listener */
286 ippeve_srv_t ipp_ref
, /* Bonjour IPP service */
287 ipps_ref
, /* Bonjour IPPS service */
288 http_ref
, /* Bonjour HTTP service */
289 printer_ref
; /* Bonjour LPD service */
290 char *dnssd_name
, /* printer-dnssd-name */
291 *name
, /* printer-name */
292 *icon
, /* Icon filename */
293 *directory
, /* Spool directory */
294 *hostname
, /* Hostname */
295 *uri
, /* printer-uri-supported */
296 *command
; /* Command to run with job file */
298 size_t urilen
; /* Length of printer URI */
299 ipp_t
*attrs
; /* Static attributes */
300 time_t start_time
; /* Startup time */
301 time_t config_time
; /* printer-config-change-time */
302 ipp_pstate_t state
; /* printer-state value */
303 ippeve_preason_t state_reasons
; /* printer-state-reasons values */
304 time_t state_time
; /* printer-state-change-time */
305 cups_array_t
*jobs
; /* Jobs */
306 ippeve_job_t
*active_job
; /* Current active/pending job */
307 int next_job_id
; /* Next job-id value */
308 _cups_rwlock_t rwlock
; /* Printer lock */
309 ippeve_media_size_t main_size
; /* Ready media */
310 ippeve_media_type_t main_type
;
312 ippeve_media_size_t envelope_size
;
314 ippeve_media_size_t photo_size
;
315 ippeve_media_type_t photo_type
;
317 int supplies
[5]; /* Supply levels (0-100) */
320 struct ippeve_job_s
/**** Job data ****/
323 const char *name
, /* job-name */
324 *username
, /* job-originating-user-name */
325 *format
; /* document-format */
326 ipp_jstate_t state
; /* job-state value */
327 time_t created
, /* time-at-creation value */
328 processing
, /* time-at-processing value */
329 completed
; /* time-at-completed value */
330 int impressions
, /* job-impressions value */
331 impcompleted
; /* job-impressions-completed value */
332 ipp_t
*attrs
; /* Static attributes */
333 int cancel
; /* Non-zero when job canceled */
334 char *filename
; /* Print file name */
335 int fd
; /* Print file descriptor */
336 ippeve_printer_t
*printer
; /* Printer */
339 typedef struct ippeve_client_s
/**** Client data ****/
341 http_t
*http
; /* HTTP connection */
342 ipp_t
*request
, /* IPP request */
343 *response
; /* IPP response */
344 time_t start
; /* Request start time */
345 http_state_t operation
; /* Request operation */
346 ipp_op_t operation_id
; /* IPP operation-id */
347 char uri
[1024], /* Request URI */
348 *options
; /* URI options */
349 http_addr_t addr
; /* Client address */
350 char hostname
[256]; /* Client hostname */
351 ippeve_printer_t
*printer
; /* Printer */
352 ippeve_job_t
*job
; /* Current job, if any */
360 static void clean_jobs(ippeve_printer_t
*printer
);
361 static int compare_jobs(ippeve_job_t
*a
, ippeve_job_t
*b
);
362 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
363 ipp_tag_t group_tag
, int quickcopy
);
364 static void copy_job_attributes(ippeve_client_t
*client
,
365 ippeve_job_t
*job
, cups_array_t
*ra
);
366 static ippeve_client_t
*create_client(ippeve_printer_t
*printer
, int sock
);
367 static ippeve_job_t
*create_job(ippeve_client_t
*client
);
368 static int create_listener(int family
, int port
);
369 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
370 static ipp_t
*create_media_size(int width
, int length
);
371 static ippeve_printer_t
*create_printer(const char *servername
,
372 const char *name
, const char *location
,
373 const char *make
, const char *model
,
375 const char *docformats
, int ppm
,
376 int ppm_color
, int duplex
, int port
,
377 int pin
, const char *subtype
,
378 const char *directory
,
380 const char *attrfile
);
381 static void debug_attributes(const char *title
, ipp_t
*ipp
,
383 static void delete_client(ippeve_client_t
*client
);
384 static void delete_job(ippeve_job_t
*job
);
385 static void delete_printer(ippeve_printer_t
*printer
);
387 static void DNSSD_API
dnssd_callback(DNSServiceRef sdRef
,
388 DNSServiceFlags flags
,
389 DNSServiceErrorType errorCode
,
393 ippeve_printer_t
*printer
);
394 #elif defined(HAVE_AVAHI)
395 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
396 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
397 #endif /* HAVE_DNSSD */
398 static void dnssd_init(void);
399 static int filter_cb(ippeve_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
400 static ippeve_job_t
*find_job(ippeve_client_t
*client
);
401 static ipp_t
*get_collection(FILE *fp
, const char *filename
, int *linenum
);
402 static char *get_token(FILE *fp
, char *buf
, int buflen
, int *linenum
);
403 static void html_escape(ippeve_client_t
*client
, const char *s
,
405 static void html_footer(ippeve_client_t
*client
);
406 static void html_header(ippeve_client_t
*client
, const char *title
);
407 static void html_printf(ippeve_client_t
*client
, const char *format
,
408 ...) _CUPS_FORMAT(2, 3);
409 static void ipp_cancel_job(ippeve_client_t
*client
);
410 static void ipp_close_job(ippeve_client_t
*client
);
411 static void ipp_create_job(ippeve_client_t
*client
);
412 static void ipp_get_job_attributes(ippeve_client_t
*client
);
413 static void ipp_get_jobs(ippeve_client_t
*client
);
414 static void ipp_get_printer_attributes(ippeve_client_t
*client
);
415 static void ipp_identify_printer(ippeve_client_t
*client
);
416 static void ipp_print_job(ippeve_client_t
*client
);
417 static void ipp_print_uri(ippeve_client_t
*client
);
418 static void ipp_send_document(ippeve_client_t
*client
);
419 static void ipp_send_uri(ippeve_client_t
*client
);
420 static void ipp_validate_job(ippeve_client_t
*client
);
421 static void load_attributes(const char *filename
, ipp_t
*attrs
);
422 static int parse_options(ippeve_client_t
*client
, cups_option_t
**options
);
423 static void process_attr_message(ippeve_job_t
*job
, char *message
);
424 static void *process_client(ippeve_client_t
*client
);
425 static int process_http(ippeve_client_t
*client
);
426 static int process_ipp(ippeve_client_t
*client
);
427 static void *process_job(ippeve_job_t
*job
);
428 static void process_state_message(ippeve_job_t
*job
, char *message
);
429 static int register_printer(ippeve_printer_t
*printer
, const char *location
, const char *make
, const char *model
, const char *formats
, const char *adminurl
, const char *uuid
, int color
, int duplex
, const char *regtype
);
430 static int respond_http(ippeve_client_t
*client
, http_status_t code
,
431 const char *content_coding
,
432 const char *type
, size_t length
);
433 static void respond_ipp(ippeve_client_t
*client
, ipp_status_t status
,
434 const char *message
, ...) _CUPS_FORMAT(3, 4);
435 static void respond_unsupported(ippeve_client_t
*client
,
436 ipp_attribute_t
*attr
);
437 static void run_printer(ippeve_printer_t
*printer
);
438 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
439 static void usage(int status
) _CUPS_NORETURN
;
440 static int valid_doc_attributes(ippeve_client_t
*client
);
441 static int valid_job_attributes(ippeve_client_t
*client
);
449 static DNSServiceRef DNSSDMaster
= NULL
;
450 #elif defined(HAVE_AVAHI)
451 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
452 static AvahiClient
*DNSSDClient
= NULL
;
453 #endif /* HAVE_DNSSD */
455 static int KeepFiles
= 0,
461 * 'main()' - Main entry to the sample server.
464 int /* O - Exit status */
465 main(int argc
, /* I - Number of command-line args */
466 char *argv
[]) /* I - Command-line arguments */
468 int i
; /* Looping var */
469 const char *opt
, /* Current option character */
470 *attrfile
= NULL
, /* Attributes file */
471 *command
= NULL
, /* Command to run with job files */
472 *servername
= NULL
, /* Server host name */
473 *name
= NULL
, /* Printer name */
474 *location
= "", /* Location of printer */
475 *make
= "Test", /* Manufacturer */
476 *model
= "Printer", /* Model */
477 *icon
= "printer.png", /* Icon file */
478 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
479 /* Supported formats */
481 const char *keypath
= NULL
; /* Keychain path */
482 #endif /* HAVE_SSL */
483 const char *subtype
= "_print"; /* Bonjour service subtype */
484 int port
= 0, /* Port number (0 = auto) */
485 duplex
= 0, /* Duplex mode */
486 ppm
= 10, /* Pages per minute for mono */
487 ppm_color
= 0, /* Pages per minute for color */
488 pin
= 0; /* PIN printing mode? */
489 char directory
[1024] = "", /* Spool directory */
490 hostname
[1024]; /* Auto-detected hostname */
491 ippeve_printer_t
*printer
; /* Printer object */
495 * Parse command-line arguments...
498 for (i
= 1; i
< argc
; i
++)
499 if (argv
[i
][0] == '-')
501 for (opt
= argv
[i
] + 1; *opt
; opt
++)
505 case '2' : /* -2 (enable 2-sided printing) */
510 case 'K' : /* -K keypath */
516 #endif /* HAVE_SSL */
518 case 'M' : /* -M manufacturer */
525 case 'P' : /* -P (PIN printing mode) */
529 case 'V' : /* -V max-version */
534 if (!strcmp(argv
[i
], "2.2"))
536 else if (!strcmp(argv
[i
], "2.1"))
538 else if (!strcmp(argv
[i
], "2.0"))
540 else if (!strcmp(argv
[i
], "1.1"))
546 case 'a' : /* -a attributes-file */
554 case 'c' : /* -c command */
562 case 'd' : /* -d spool-directory */
566 strlcpy(directory
, argv
[i
], sizeof(directory
));
569 case 'f' : /* -f type/subtype[,...] */
576 case 'h' : /* -h (show help) */
579 case 'i' : /* -i icon.png */
586 case 'k' : /* -k (keep files) */
590 case 'l' : /* -l location */
597 case 'm' : /* -m model */
604 case 'n' : /* -n hostname */
608 servername
= argv
[i
];
611 case 'p' : /* -p port */
613 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
615 port
= atoi(argv
[i
]);
618 case 'r' : /* -r subtype */
625 case 's' : /* -s speed[,color-speed] */
629 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
633 case 'v' : /* -v (be verbose) */
637 default : /* Unknown */
638 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
649 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
657 * Apply defaults as needed...
661 servername
= httpGetHostname(NULL
, hostname
, sizeof(hostname
));
667 * Windows is almost always used as a single user system, so use a default
668 * port number of 8631.
675 * Use 8000 + UID mod 1000 for the default port number...
678 port
= 8000 + ((int)getuid() % 1000);
681 fprintf(stderr
, "Listening on port %d.\n", port
);
686 const char *tmpdir
; /* Temporary directory */
689 if ((tmpdir
= getenv("TEMP")) == NULL
)
691 #elif defined(__APPLE__) && !TARGET_OS_IOS
692 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
693 tmpdir
= "/private/tmp";
695 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
699 snprintf(directory
, sizeof(directory
), "%s/ippserver.%d", tmpdir
, (int)getpid());
701 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
703 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
704 directory
, strerror(errno
));
709 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
713 cupsSetServerCredentials(keypath
, servername
, 1);
714 #endif /* HAVE_SSL */
717 * Initialize Bonjour...
723 * Create the printer...
726 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
727 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
728 subtype
, directory
, command
, attrfile
)) == NULL
)
732 * Run the print service...
735 run_printer(printer
);
738 * Destroy the printer and exit...
741 delete_printer(printer
);
748 * 'clean_jobs()' - Clean out old (completed) jobs.
752 clean_jobs(ippeve_printer_t
*printer
) /* I - Printer */
754 ippeve_job_t
*job
; /* Current job */
755 time_t cleantime
; /* Clean time */
758 if (cupsArrayCount(printer
->jobs
) == 0)
761 cleantime
= time(NULL
) - 60;
763 _cupsRWLockWrite(&(printer
->rwlock
));
764 for (job
= (ippeve_job_t
*)cupsArrayFirst(printer
->jobs
);
766 job
= (ippeve_job_t
*)cupsArrayNext(printer
->jobs
))
767 if (job
->completed
&& job
->completed
< cleantime
)
769 cupsArrayRemove(printer
->jobs
, job
);
774 _cupsRWUnlock(&(printer
->rwlock
));
779 * 'compare_jobs()' - Compare two jobs.
782 static int /* O - Result of comparison */
783 compare_jobs(ippeve_job_t
*a
, /* I - First job */
784 ippeve_job_t
*b
) /* I - Second job */
786 return (b
->id
- a
->id
);
791 * 'copy_attributes()' - Copy attributes from one request to another.
795 copy_attributes(ipp_t
*to
, /* I - Destination request */
796 ipp_t
*from
, /* I - Source request */
797 cups_array_t
*ra
, /* I - Requested attributes */
798 ipp_tag_t group_tag
, /* I - Group to copy */
799 int quickcopy
) /* I - Do a quick copy? */
801 ippeve_filter_t filter
; /* Filter data */
805 filter
.group_tag
= group_tag
;
807 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
812 * 'copy_job_attrs()' - Copy job attributes to the response.
817 ippeve_client_t
*client
, /* I - Client */
818 ippeve_job_t
*job
, /* I - Job */
819 cups_array_t
*ra
) /* I - requested-attributes */
821 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
823 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
826 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
828 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
831 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
834 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
836 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
839 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
840 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
842 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
843 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
845 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
846 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
848 if (!ra
|| cupsArrayFind(ra
, "job-state"))
849 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
850 "job-state", job
->state
);
852 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
856 case IPP_JSTATE_PENDING
:
857 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
860 case IPP_JSTATE_HELD
:
862 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
863 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
864 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
866 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
869 case IPP_JSTATE_PROCESSING
:
871 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
873 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
876 case IPP_JSTATE_STOPPED
:
877 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
880 case IPP_JSTATE_CANCELED
:
881 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
884 case IPP_JSTATE_ABORTED
:
885 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
888 case IPP_JSTATE_COMPLETED
:
889 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
894 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
898 case IPP_JSTATE_PENDING
:
899 ippAddString(client
->response
, IPP_TAG_JOB
,
900 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
904 case IPP_JSTATE_HELD
:
906 ippAddString(client
->response
, IPP_TAG_JOB
,
907 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
908 "job-state-reasons", NULL
, "job-incoming");
909 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
910 ippAddString(client
->response
, IPP_TAG_JOB
,
911 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
912 "job-state-reasons", NULL
, "job-hold-until-specified");
914 ippAddString(client
->response
, IPP_TAG_JOB
,
915 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
916 "job-state-reasons", NULL
, "job-data-insufficient");
919 case IPP_JSTATE_PROCESSING
:
921 ippAddString(client
->response
, IPP_TAG_JOB
,
922 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
923 "job-state-reasons", NULL
, "processing-to-stop-point");
925 ippAddString(client
->response
, IPP_TAG_JOB
,
926 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
927 "job-state-reasons", NULL
, "job-printing");
930 case IPP_JSTATE_STOPPED
:
931 ippAddString(client
->response
, IPP_TAG_JOB
,
932 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
933 NULL
, "job-stopped");
936 case IPP_JSTATE_CANCELED
:
937 ippAddString(client
->response
, IPP_TAG_JOB
,
938 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
939 NULL
, "job-canceled-by-user");
942 case IPP_JSTATE_ABORTED
:
943 ippAddString(client
->response
, IPP_TAG_JOB
,
944 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
945 NULL
, "aborted-by-system");
948 case IPP_JSTATE_COMPLETED
:
949 ippAddString(client
->response
, IPP_TAG_JOB
,
950 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
951 NULL
, "job-completed-successfully");
956 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
957 ippAddInteger(client
->response
, IPP_TAG_JOB
,
958 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
959 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
961 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
962 ippAddInteger(client
->response
, IPP_TAG_JOB
,
963 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
964 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
969 * 'create_client()' - Accept a new network connection and create a client
973 static ippeve_client_t
* /* O - Client */
974 create_client(ippeve_printer_t
*printer
, /* I - Printer */
975 int sock
) /* I - Listen socket */
977 ippeve_client_t
*client
; /* Client */
980 if ((client
= calloc(1, sizeof(ippeve_client_t
))) == NULL
)
982 perror("Unable to allocate memory for client");
986 client
->printer
= printer
;
989 * Accept the client and get the remote address...
992 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
994 perror("Unable to accept client connection");
1001 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
1004 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
1011 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
1015 static ippeve_job_t
* /* O - Job */
1016 create_job(ippeve_client_t
*client
) /* I - Client */
1018 ippeve_job_t
*job
; /* Job */
1019 ipp_attribute_t
*attr
; /* Job attribute */
1020 char uri
[1024], /* job-uri value */
1021 uuid
[64]; /* job-uuid value */
1024 _cupsRWLockWrite(&(client
->printer
->rwlock
));
1025 if (client
->printer
->active_job
&&
1026 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
1029 * Only accept a single job at a time...
1032 _cupsRWUnlock(&(client
->printer
->rwlock
));
1037 * Allocate and initialize the job object...
1040 if ((job
= calloc(1, sizeof(ippeve_job_t
))) == NULL
)
1042 perror("Unable to allocate memory for job");
1046 job
->printer
= client
->printer
;
1047 job
->attrs
= ippNew();
1048 job
->state
= IPP_JSTATE_HELD
;
1052 * Copy all of the job attributes...
1055 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
1058 * Get the requesting-user-name, document format, and priority...
1061 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1062 job
->username
= ippGetString(attr
, 0, NULL
);
1064 job
->username
= "anonymous";
1066 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1068 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1070 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1071 job
->format
= ippGetString(attr
, 0, NULL
);
1072 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1073 job
->format
= ippGetString(attr
, 0, NULL
);
1075 job
->format
= "application/octet-stream";
1078 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1079 job
->impressions
= ippGetInteger(attr
, 0);
1081 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1082 job
->name
= ippGetString(attr
, 0, NULL
);
1085 * Add job description attributes and add to the jobs array...
1088 job
->id
= client
->printer
->next_job_id
++;
1090 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1091 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1093 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1094 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1095 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1096 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1097 if ((attr
= ippFindAttribute(client
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
1098 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, ippGetString(attr
, 0, NULL
));
1100 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1101 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1103 cupsArrayAdd(client
->printer
->jobs
, job
);
1104 client
->printer
->active_job
= job
;
1106 _cupsRWUnlock(&(client
->printer
->rwlock
));
1113 * 'create_job_filename()' - Create the filename for a document in a job.
1116 static void create_job_filename(
1117 ippeve_printer_t
*printer
, /* I - Printer */
1118 ippeve_job_t
*job
, /* I - Job */
1119 char *fname
, /* I - Filename buffer */
1120 size_t fnamesize
) /* I - Size of filename buffer */
1122 char name
[256], /* "Safe" filename */
1123 *nameptr
; /* Pointer into filename */
1124 const char *ext
, /* Filename extension */
1125 *job_name
; /* job-name value */
1126 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1130 * Make a name from the job-name attribute...
1133 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1134 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1136 job_name
= "untitled";
1138 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1139 if (isalnum(*job_name
& 255) || *job_name
== '-')
1140 *nameptr
++ = (char)tolower(*job_name
& 255);
1147 * Figure out the extension...
1150 if (!strcasecmp(job
->format
, "image/jpeg"))
1152 else if (!strcasecmp(job
->format
, "image/png"))
1154 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1156 else if (!strcasecmp(job
->format
, "image/urf"))
1158 else if (!strcasecmp(job
->format
, "application/pdf"))
1160 else if (!strcasecmp(job
->format
, "application/postscript"))
1166 * Create a filename with the job-id, job-name, and document-format (extension)...
1169 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1174 * 'create_listener()' - Create a listener socket.
1177 static int /* O - Listener socket or -1 on error */
1178 create_listener(int family
, /* I - Address family */
1179 int port
) /* I - Port number */
1181 int sock
; /* Listener socket */
1182 http_addrlist_t
*addrlist
; /* Listen address */
1183 char service
[255]; /* Service port */
1186 snprintf(service
, sizeof(service
), "%d", port
);
1187 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1190 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1192 httpAddrFreeList(addrlist
);
1199 * 'create_media_col()' - Create a media-col value.
1202 static ipp_t
* /* O - media-col collection */
1203 create_media_col(const char *media
, /* I - Media name */
1204 const char *source
, /* I - Media source */
1205 const char *type
, /* I - Media type */
1206 int width
, /* I - x-dimension in 2540ths */
1207 int length
, /* I - y-dimension in 2540ths */
1208 int margins
) /* I - Value for margins */
1210 ipp_t
*media_col
= ippNew(), /* media-col value */
1211 *media_size
= create_media_size(width
, length
);
1212 /* media-size value */
1213 char media_key
[256]; /* media-key value */
1217 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1219 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1221 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1223 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1225 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1227 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1228 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1229 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1230 "media-bottom-margin", margins
);
1231 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1232 "media-left-margin", margins
);
1233 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1234 "media-right-margin", margins
);
1235 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1236 "media-top-margin", margins
);
1238 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1240 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1242 ippDelete(media_size
);
1249 * 'create_media_size()' - Create a media-size value.
1252 static ipp_t
* /* O - media-col collection */
1253 create_media_size(int width
, /* I - x-dimension in 2540ths */
1254 int length
) /* I - y-dimension in 2540ths */
1256 ipp_t
*media_size
= ippNew(); /* media-size value */
1259 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1261 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1264 return (media_size
);
1269 * 'create_printer()' - Create, register, and listen for connections to a
1273 static ippeve_printer_t
* /* O - Printer */
1274 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1275 const char *name
, /* I - printer-name */
1276 const char *location
, /* I - printer-location */
1277 const char *make
, /* I - printer-make-and-model */
1278 const char *model
, /* I - printer-make-and-model */
1279 const char *icon
, /* I - printer-icons */
1280 const char *docformats
, /* I - document-format-supported */
1281 int ppm
, /* I - Pages per minute in grayscale */
1282 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1283 int duplex
, /* I - 1 = duplex, 0 = simplex */
1284 int port
, /* I - Port for listeners or 0 for auto */
1285 int pin
, /* I - Require PIN printing */
1286 const char *subtype
, /* I - Bonjour service subtype */
1287 const char *directory
, /* I - Spool directory */
1288 const char *command
, /* I - Command to run on job files */
1289 const char *attrfile
) /* I - Attributes file */
1291 int i
, j
; /* Looping vars */
1292 ippeve_printer_t
*printer
; /* Printer */
1294 char path
[1024]; /* Full path to command */
1295 #endif /* !_WIN32 */
1296 char uri
[1024], /* Printer URI */
1298 securi
[1024], /* Secure printer URI */
1299 *uris
[2], /* All URIs */
1300 #endif /* HAVE_SSL */
1301 icons
[1024], /* printer-icons URI */
1302 adminurl
[1024], /* printer-more-info URI */
1303 supplyurl
[1024],/* printer-supply-info-uri URI */
1304 device_id
[1024],/* printer-device-id */
1305 make_model
[128],/* printer-make-and-model */
1306 uuid
[128]; /* printer-uuid */
1307 int num_formats
; /* Number of document-format-supported values */
1308 char *defformat
, /* document-format-default value */
1309 *formats
[100], /* document-format-supported values */
1310 *ptr
; /* Pointer into string */
1311 const char *prefix
; /* Prefix string */
1312 int num_database
; /* Number of database values */
1313 ipp_attribute_t
*media_col_database
,
1314 /* media-col-database value */
1315 *media_size_supported
;
1316 /* media-size-supported value */
1317 ipp_t
*media_col_default
;
1318 /* media-col-default value */
1319 int media_col_index
;/* Current media-col-database value */
1320 int k_supported
; /* Maximum file size supported */
1322 struct statvfs spoolinfo
; /* FS info for spool directory */
1323 double spoolsize
; /* FS size */
1324 #elif defined(HAVE_STATFS)
1325 struct statfs spoolinfo
; /* FS info for spool directory */
1326 double spoolsize
; /* FS size */
1327 #endif /* HAVE_STATVFS */
1328 static const int orients
[4] = /* orientation-requested-supported values */
1330 IPP_ORIENT_PORTRAIT
,
1331 IPP_ORIENT_LANDSCAPE
,
1332 IPP_ORIENT_REVERSE_LANDSCAPE
,
1333 IPP_ORIENT_REVERSE_PORTRAIT
1335 static const char * const versions
[] =/* ipp-versions-supported values */
1342 static const char * const features
[] =/* ipp-features-supported values */
1346 static const int ops
[] = /* operations-supported values */
1350 IPP_OP_VALIDATE_JOB
,
1352 IPP_OP_SEND_DOCUMENT
,
1355 IPP_OP_GET_JOB_ATTRIBUTES
,
1357 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1358 IPP_OP_CANCEL_MY_JOBS
,
1360 IPP_OP_IDENTIFY_PRINTER
1362 static const char * const charsets
[] =/* charset-supported values */
1367 static const char * const compressions
[] =/* compression-supported values */
1372 #endif /* HAVE_LIBZ */
1375 static const char * const identify_actions
[] =
1380 static const char * const job_creation
[] =
1381 { /* job-creation-attributes-supported values */
1383 "ipp-attribute-fidelity",
1385 "job-accounting-user-id",
1391 "multiple-document-handling",
1392 "orientation-requested",
1396 static const char * const media_col_supported
[] =
1397 { /* media-col-supported values */
1398 "media-bottom-margin",
1399 "media-left-margin",
1400 "media-right-margin",
1406 static const int media_xxx_margin_supported
[] =
1407 { /* media-xxx-margin-supported values */
1411 static const char * const multiple_document_handling
[] =
1412 { /* multiple-document-handling-supported values */
1413 "separate-documents-uncollated-copies",
1414 "separate-documents-collated-copies"
1416 static const char * const overrides
[] =
1417 { /* overrides-supported */
1421 static const char * const print_color_mode_supported
[] =
1422 { /* print-color-mode-supported values */
1427 static const int print_quality_supported
[] =
1428 { /* print-quality-supported values */
1433 static const int pwg_raster_document_resolution_supported
[] =
1438 static const char * const pwg_raster_document_type_supported
[] =
1446 static const char * const reference_uri_schemes_supported
[] =
1447 { /* reference-uri-schemes-supported */
1453 #endif /* HAVE_SSL */
1455 static const char * const sides_supported
[] =
1456 { /* sides-supported values */
1458 "two-sided-long-edge",
1459 "two-sided-short-edge"
1461 static const char * const urf_supported
[] =
1462 { /* urf-supported values */
1465 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1473 static const char * const uri_authentication_supported
[] =
1474 { /* uri-authentication-supported values */
1478 static const char * const uri_security_supported
[] =
1479 { /* uri-security-supported values */
1483 #endif /* HAVE_SSL */
1484 static const char * const which_jobs
[] =
1485 { /* which-jobs-supported values */
1494 "processing-stopped"
1500 * If a command was specified, make sure it exists and is executable...
1505 if (*command
== '/' || !strncmp(command
, "./", 2))
1507 if (access(command
, X_OK
))
1509 fprintf(stderr
, "ippserver: Unable to execute command \"%s\": %s\n", command
, strerror(errno
));
1515 if (!cupsFileFind(command
, getenv("PATH"), 1, path
, sizeof(path
)))
1517 fprintf(stderr
, "ippserver: Unable to find command \"%s\".\n", command
);
1524 #endif /* !_WIN32 */
1527 * Allocate memory for the printer...
1530 if ((printer
= calloc(1, sizeof(ippeve_printer_t
))) == NULL
)
1532 perror("ippserver: Unable to allocate memory for printer");
1538 printer
->name
= strdup(name
);
1539 printer
->dnssd_name
= strdup(printer
->name
);
1540 printer
->command
= command
? strdup(command
) : NULL
;
1541 printer
->directory
= strdup(directory
);
1542 printer
->hostname
= strdup(servername
);
1543 printer
->port
= port
;
1544 printer
->start_time
= time(NULL
);
1545 printer
->config_time
= printer
->start_time
;
1546 printer
->state
= IPP_PSTATE_IDLE
;
1547 printer
->state_reasons
= IPPEVE_PREASON_NONE
;
1548 printer
->state_time
= printer
->start_time
;
1549 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1550 printer
->next_job_id
= 1;
1552 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1553 printer
->uri
= strdup(uri
);
1554 printer
->urilen
= strlen(uri
);
1557 httpAssembleURI(HTTP_URI_CODING_ALL
, securi
, sizeof(securi
), "ipps", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1558 #endif /* HAVE_SSL */
1561 printer
->icon
= strdup(icon
);
1563 printer
->main_size
= IPPEVE_MEDIA_SIZE_A4
;
1564 printer
->main_type
= IPPEVE_MEDIA_TYPE_STATIONERY
;
1565 printer
->main_level
= 500;
1567 printer
->envelope_size
= IPPEVE_MEDIA_SIZE_NONE
;
1568 printer
->envelope_level
= 0;
1570 printer
->photo_size
= IPPEVE_MEDIA_SIZE_NONE
;
1571 printer
->photo_type
= IPPEVE_MEDIA_TYPE_NONE
;
1572 printer
->photo_level
= 0;
1574 printer
->supplies
[IPPEVE_SUPPLY_CYAN
] = 100;
1575 printer
->supplies
[IPPEVE_SUPPLY_MAGENTA
] = 100;
1576 printer
->supplies
[IPPEVE_SUPPLY_YELLOW
] = 100;
1577 printer
->supplies
[IPPEVE_SUPPLY_BLACK
] = 100;
1578 printer
->supplies
[IPPEVE_SUPPLY_WASTE
] = 0;
1580 _cupsRWInit(&(printer
->rwlock
));
1583 * Create the listener sockets...
1586 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1588 perror("Unable to create IPv4 listener");
1592 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1594 perror("Unable to create IPv6 listener");
1599 * Prepare values for the printer attributes...
1602 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/icon.png");
1603 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/");
1604 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/supplies");
1608 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1609 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1610 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1613 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1616 formats
[0] = strdup(docformats
);
1617 defformat
= formats
[0];
1618 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1621 formats
[num_formats
++] = ptr
;
1623 if (!strcasecmp(ptr
, "application/octet-stream"))
1627 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1628 ptr
= device_id
+ strlen(device_id
);
1630 for (i
= 0; i
< num_formats
; i
++)
1632 if (!strcasecmp(formats
[i
], "application/pdf"))
1633 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1634 else if (!strcasecmp(formats
[i
], "application/postscript"))
1635 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1636 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1637 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1638 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1639 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1640 else if (!strcasecmp(formats
[i
], "image/png"))
1641 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1642 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1643 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1648 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
1655 * Get the maximum spool size based on the size of the filesystem used for
1656 * the spool directory. If the host OS doesn't support the statfs call
1657 * or the filesystem is larger than 2TiB, always report INT_MAX.
1661 if (statvfs(printer
->directory
, &spoolinfo
))
1662 k_supported
= INT_MAX
;
1663 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1664 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1665 k_supported
= INT_MAX
;
1667 k_supported
= (int)spoolsize
;
1669 #elif defined(HAVE_STATFS)
1670 if (statfs(printer
->directory
, &spoolinfo
))
1671 k_supported
= INT_MAX
;
1672 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1673 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1674 k_supported
= INT_MAX
;
1676 k_supported
= (int)spoolsize
;
1679 k_supported
= INT_MAX
;
1680 #endif /* HAVE_STATVFS */
1683 * Create the printer attributes. This list of attributes is sorted to improve
1684 * performance when the client provides a requested-attributes attribute...
1687 printer
->attrs
= ippNew();
1690 load_attributes(attrfile
, printer
->attrs
);
1692 /* charset-configured */
1693 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-configured", NULL
, "utf-8");
1695 /* charset-supported */
1696 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]), NULL
, charsets
);
1698 /* color-supported */
1699 if (!ippFindAttribute(printer
->attrs
, "color-supported", IPP_TAG_ZERO
))
1700 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
1702 /* compression-supported */
1703 if (!ippFindAttribute(printer
->attrs
, "compression-supported", IPP_TAG_ZERO
))
1704 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "compression-supported", (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
, compressions
);
1706 /* copies-default */
1707 if (!ippFindAttribute(printer
->attrs
, "copies-default", IPP_TAG_ZERO
))
1708 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
1710 /* copies-supported */
1711 if (!ippFindAttribute(printer
->attrs
, "copies-supported", IPP_TAG_ZERO
))
1712 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1714 /* document-format-default */
1715 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1716 "document-format-default", NULL
, defformat
);
1718 /* document-format-supported */
1719 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1720 "document-format-supported", num_formats
, NULL
,
1721 (const char * const *)formats
);
1723 /* document-password-supported */
1724 if (!ippFindAttribute(printer
->attrs
, "document-password-supported", IPP_TAG_ZERO
))
1725 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1727 /* finishings-default */
1728 if (!ippFindAttribute(printer
->attrs
, "finishings-default", IPP_TAG_ZERO
))
1729 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
1731 /* finishings-supported */
1732 if (!ippFindAttribute(printer
->attrs
, "finishings-supported", IPP_TAG_ZERO
))
1733 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
1735 /* generated-natural-language-supported */
1736 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "generated-natural-language-supported", NULL
, "en");
1738 /* identify-actions-default */
1739 if (!ippFindAttribute(printer
->attrs
, "identify-actions-default", IPP_TAG_ZERO
))
1740 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1742 /* identify-actions-supported */
1743 if (!ippFindAttribute(printer
->attrs
, "identify-actions-supported", IPP_TAG_ZERO
))
1744 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
);
1746 /* ipp-features-supported */
1747 if (!ippFindAttribute(printer
->attrs
, "ipp-features-supported", IPP_TAG_ZERO
))
1748 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1750 /* ipp-versions-supported */
1751 if (!ippFindAttribute(printer
->attrs
, "ipp-versions-supported", IPP_TAG_ZERO
))
1753 int num_versions
= MaxVersion
== 11 ? 1 : MaxVersion
== 20 ? 2 : MaxVersion
== 21 ? 3 : 4;
1754 /* Number of supported versions */
1756 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", num_versions
, NULL
, versions
);
1759 /* job-account-id-default */
1760 if (!ippFindAttribute(printer
->attrs
, "job-account-id-default", IPP_TAG_ZERO
))
1761 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1763 /* job-account-id-supported */
1764 if (!ippFindAttribute(printer
->attrs
, "job-account-id-supported", IPP_TAG_ZERO
))
1765 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1767 /* job-accounting-user-id-default */
1768 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-default", IPP_TAG_ZERO
))
1769 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1771 /* job-accounting-user-id-supported */
1772 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-supported", IPP_TAG_ZERO
))
1773 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1775 /* job-creation-attributes-supported */
1776 if (!ippFindAttribute(printer
->attrs
, "job-creation-attributes-supported", IPP_TAG_ZERO
))
1777 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-creation-attributes-supported", sizeof(job_creation
) / sizeof(job_creation
[0]), NULL
, job_creation
);
1779 /* job-ids-supported */
1780 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1782 /* job-k-octets-supported */
1783 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1786 /* job-password-supported */
1787 if (!ippFindAttribute(printer
->attrs
, "job-password-supported", IPP_TAG_ZERO
))
1788 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-password-supported", 4);
1790 /* job-priority-default */
1791 if (!ippFindAttribute(printer
->attrs
, "job-priority-default", IPP_TAG_ZERO
))
1792 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-default", 50);
1794 /* job-priority-supported */
1795 if (!ippFindAttribute(printer
->attrs
, "job-priority-supported", IPP_TAG_ZERO
))
1796 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-supported", 100);
1798 /* job-sheets-default */
1799 if (!ippFindAttribute(printer
->attrs
, "job-sheets-default", IPP_TAG_ZERO
))
1800 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-default", NULL
, "none");
1802 /* job-sheets-supported */
1803 if (!ippFindAttribute(printer
->attrs
, "job-sheets-supported", IPP_TAG_ZERO
))
1804 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-supported", NULL
, "none");
1806 /* media-bottom-margin-supported */
1807 if (!ippFindAttribute(printer
->attrs
, "media-bottom-margin-supported", IPP_TAG_ZERO
))
1808 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-bottom-margin-supported", (int)(sizeof(media_xxx_margin_supported
) / sizeof(media_xxx_margin_supported
[0])), media_xxx_margin_supported
);
1810 /* media-col-database */
1811 if (!ippFindAttribute(printer
->attrs
, "media-col-database", IPP_TAG_ZERO
))
1813 for (num_database
= 0, i
= 0;
1814 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1817 if (media_col_sizes
[i
][2] == IPPEVE_ENV_ONLY
)
1818 num_database
+= 3; /* auto + manual + envelope */
1819 else if (media_col_sizes
[i
][2] == IPPEVE_PHOTO_ONLY
)
1820 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1822 num_database
+= 2; /* Regular + borderless */
1825 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-database", num_database
, NULL
);
1826 for (media_col_index
= 0, i
= 0;
1827 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1830 switch (media_col_sizes
[i
][2])
1832 case IPPEVE_GENERAL
:
1834 * Regular + borderless for the general class; no source/type
1838 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], NULL
, NULL
, media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[1]));
1839 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], NULL
, NULL
, media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[0]));
1842 case IPPEVE_ENV_ONLY
:
1844 * Regular margins for "auto", "manual", and "envelope" sources.
1847 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "auto", "envelope", media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[1]));
1848 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "manual", "envelope", media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[1]));
1849 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "envelope", "envelope", media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[1]));
1851 case IPPEVE_PHOTO_ONLY
:
1853 * Photos have specific media types and can only be printed via
1854 * the auto, manual, and photo sources...
1858 j
< (int)(sizeof(media_type_supported
) /
1859 sizeof(media_type_supported
[0]));
1862 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1865 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "auto", media_type_supported
[j
], media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[0]));
1866 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "manual", media_type_supported
[j
], media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[0]));
1867 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "photo", media_type_supported
[j
], media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[0]));
1874 /* media-col-default */
1875 if (!ippFindAttribute(printer
->attrs
, "media-col-default", IPP_TAG_ZERO
))
1877 media_col_default
= create_media_col(media_supported
[0], media_source_supported
[0], media_type_supported
[0], media_col_sizes
[0][0], media_col_sizes
[0][1],media_xxx_margin_supported
[1]);
1879 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1881 ippDelete(media_col_default
);
1884 /* media-col-supported */
1885 if (!ippFindAttribute(printer
->attrs
, "media-col-supported", IPP_TAG_ZERO
))
1886 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
);
1889 if (!ippFindAttribute(printer
->attrs
, "media-default", IPP_TAG_ZERO
))
1890 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media_supported
[0]);
1892 /* media-left-margin-supported */
1893 if (!ippFindAttribute(printer
->attrs
, "media-left-margin-supported", IPP_TAG_ZERO
))
1894 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-left-margin-supported", (int)(sizeof(media_xxx_margin_supported
) / sizeof(media_xxx_margin_supported
[0])), media_xxx_margin_supported
);
1896 /* media-right-margin-supported */
1897 if (!ippFindAttribute(printer
->attrs
, "media-right-margin-supported", IPP_TAG_ZERO
))
1898 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-right-margin-supported", (int)(sizeof(media_xxx_margin_supported
) / sizeof(media_xxx_margin_supported
[0])), media_xxx_margin_supported
);
1900 /* media-supported */
1901 if (!ippFindAttribute(printer
->attrs
, "media-supported", IPP_TAG_ZERO
))
1902 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-supported", (int)(sizeof(media_supported
) / sizeof(media_supported
[0])), NULL
, media_supported
);
1904 /* media-size-supported */
1905 if (!ippFindAttribute(printer
->attrs
, "media-size-supported", IPP_TAG_ZERO
))
1907 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-size-supported", (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0])), NULL
);
1910 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1913 ipp_t
*size
= create_media_size(media_col_sizes
[i
][0], media_col_sizes
[i
][1]);
1915 ippSetCollection(printer
->attrs
, &media_size_supported
, i
, size
);
1920 /* media-source-supported */
1921 if (!ippFindAttribute(printer
->attrs
, "media-source-supported", IPP_TAG_ZERO
))
1922 ippAddStrings(printer
->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
);
1924 /* media-top-margin-supported */
1925 if (!ippFindAttribute(printer
->attrs
, "media-top-margin-supported", IPP_TAG_ZERO
))
1926 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-top-margin-supported", (int)(sizeof(media_xxx_margin_supported
) / sizeof(media_xxx_margin_supported
[0])), media_xxx_margin_supported
);
1928 /* media-type-supported */
1929 if (!ippFindAttribute(printer
->attrs
, "media-type-supported", IPP_TAG_ZERO
))
1930 ippAddStrings(printer
->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
);
1932 /* multiple-document-handling-supported */
1933 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
);
1935 /* multiple-document-jobs-supported */
1936 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1938 /* multiple-operation-time-out */
1939 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1941 /* multiple-operation-time-out-action */
1942 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1944 /* natural-language-configured */
1945 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1946 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1947 "natural-language-configured", NULL
, "en");
1949 /* number-up-default */
1950 if (!ippFindAttribute(printer
->attrs
, "number-up-default", IPP_TAG_ZERO
))
1951 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-default", 1);
1953 /* number-up-supported */
1954 if (!ippFindAttribute(printer
->attrs
, "number-up-supported", IPP_TAG_ZERO
))
1955 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-supported", 1);
1957 /* operations-supported */
1958 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1960 /* orientation-requested-default */
1961 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-default", IPP_TAG_ZERO
))
1962 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "orientation-requested-default", 0);
1964 /* orientation-requested-supported */
1965 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-supported", IPP_TAG_ZERO
))
1966 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", 4, orients
);
1968 /* output-bin-default */
1969 if (!ippFindAttribute(printer
->attrs
, "output-bin-default", IPP_TAG_ZERO
))
1970 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
1972 /* output-bin-supported */
1973 if (!ippFindAttribute(printer
->attrs
, "output-bin-supported", IPP_TAG_ZERO
))
1974 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
1976 /* overrides-supported */
1977 if (!ippFindAttribute(printer
->attrs
, "overrides-supported", IPP_TAG_ZERO
))
1978 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1980 /* page-ranges-supported */
1981 if (!ippFindAttribute(printer
->attrs
, "page-ranges-supported", IPP_TAG_ZERO
))
1982 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1984 /* pages-per-minute */
1985 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1986 "pages-per-minute", ppm
);
1988 /* pages-per-minute-color */
1990 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1991 "pages-per-minute-color", ppm_color
);
1993 /* pdl-override-supported */
1994 if (!ippFindAttribute(printer
->attrs
, "pdl-override-supported", IPP_TAG_ZERO
))
1995 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
1997 /* preferred-attributes-supported */
1998 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "preferred-attributes-supported", 0);
2000 /* print-color-mode-default */
2001 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-default", IPP_TAG_ZERO
))
2002 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
2004 /* print-color-mode-supported */
2005 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-supported", IPP_TAG_ZERO
))
2006 ippAddStrings(printer
->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
);
2008 /* print-content-optimize-default */
2009 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-default", IPP_TAG_ZERO
))
2010 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
2012 /* print-content-optimize-supported */
2013 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-supported", IPP_TAG_ZERO
))
2014 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
2016 /* print-rendering-intent-default */
2017 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-default", IPP_TAG_ZERO
))
2018 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
2020 /* print-rendering-intent-supported */
2021 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-supported", IPP_TAG_ZERO
))
2022 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
2024 /* print-quality-default */
2025 if (!ippFindAttribute(printer
->attrs
, "print-quality-default", IPP_TAG_ZERO
))
2026 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
2028 /* print-quality-supported */
2029 if (!ippFindAttribute(printer
->attrs
, "print-quality-supported", IPP_TAG_ZERO
))
2030 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-supported", (int)(sizeof(print_quality_supported
) / sizeof(print_quality_supported
[0])), print_quality_supported
);
2032 /* printer-device-id */
2033 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2034 "printer-device-id", NULL
, device_id
);
2036 /* printer-get-attributes-supported */
2037 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
2039 /* printer-geo-location */
2040 if (!ippFindAttribute(printer
->attrs
, "printer-geo-location", IPP_TAG_ZERO
))
2041 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
2044 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
2045 "printer-icons", NULL
, icons
);
2047 /* printer-is-accepting-jobs */
2048 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
2051 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
2053 /* printer-location */
2054 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2055 "printer-location", NULL
, location
);
2057 /* printer-make-and-model */
2058 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2059 "printer-make-and-model", NULL
, make_model
);
2061 /* printer-mandatory-job-attributes */
2062 if (pin
&& !ippFindAttribute(printer
->attrs
, "", IPP_TAG_ZERO
))
2064 static const char * const names
[] =
2067 "job-accounting-user-id",
2071 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2072 "printer-mandatory-job-attributes",
2073 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
2076 /* printer-more-info */
2077 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
2080 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
2082 /* printer-organization */
2083 if (!ippFindAttribute(printer
->attrs
, "printer-organization", IPP_TAG_ZERO
))
2084 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
2086 /* printer-organizational-unit */
2087 if (!ippFindAttribute(printer
->attrs
, "printer-organizational-unit", IPP_TAG_ZERO
))
2088 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
2090 /* printer-resolution-default */
2091 if (!ippFindAttribute(printer
->attrs
, "printer-resolution-default", IPP_TAG_ZERO
))
2092 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
2094 /* printer-resolution-supported */
2095 if (!ippFindAttribute(printer
->attrs
, "printer-resolutions-supported", IPP_TAG_ZERO
))
2096 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
2098 /* printer-supply-description */
2099 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-supply-description", (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])), NULL
, printer_supplies
);
2101 /* printer-supply-info-uri */
2102 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
2104 /* printer-uri-supported */
2109 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", 2, NULL
, (const char **)uris
);
2112 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
2113 #endif /* HAVE_SSL */
2116 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
2117 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
2119 /* pwg-raster-document-xxx-supported */
2120 for (i
= 0; i
< num_formats
; i
++)
2121 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
2124 if (i
< num_formats
)
2126 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-resolution-supported", IPP_TAG_ZERO
))
2127 ippAddResolutions(printer
->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
);
2128 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-sheet-back", IPP_TAG_ZERO
))
2129 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "pwg-raster-document-sheet-back", NULL
, "normal");
2130 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-type-supported", IPP_TAG_ZERO
))
2131 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, 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
);
2134 /* reference-uri-scheme-supported */
2135 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
);
2138 if (!ippFindAttribute(printer
->attrs
, "sides-default", IPP_TAG_ZERO
))
2139 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
2141 /* sides-supported */
2142 if (!ippFindAttribute(printer
->attrs
, "sides-supported", IPP_TAG_ZERO
))
2143 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
2146 for (i
= 0; i
< num_formats
; i
++)
2147 if (!strcasecmp(formats
[i
], "image/urf"))
2150 if (i
< num_formats
&& !ippFindAttribute(printer
->attrs
, "urf-supported", IPP_TAG_ZERO
))
2151 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2153 /* uri-authentication-supported */
2155 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_supported
);
2157 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "none");
2158 #endif /* HAVE_SSL */
2160 /* uri-security-supported */
2162 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", 2, NULL
, uri_security_supported
);
2164 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", NULL
, "none");
2165 #endif /* HAVE_SSL */
2167 /* which-jobs-supported */
2168 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
);
2172 debug_attributes("Printer", printer
->attrs
, 0);
2175 * Register the printer with Bonjour...
2178 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2189 * If we get here we were unable to create the printer...
2194 delete_printer(printer
);
2200 * 'debug_attributes()' - Print attributes in a request or response.
2204 debug_attributes(const char *title
, /* I - Title */
2205 ipp_t
*ipp
, /* I - Request/response */
2206 int type
) /* I - 0 = object, 1 = request, 2 = response */
2208 ipp_tag_t group_tag
; /* Current group */
2209 ipp_attribute_t
*attr
; /* Current attribute */
2210 char buffer
[2048]; /* String buffer for value */
2211 int major
, minor
; /* Version */
2217 fprintf(stderr
, "%s:\n", title
);
2218 major
= ippGetVersion(ipp
, &minor
);
2219 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2221 fprintf(stderr
, " operation-id=%s(%04x)\n",
2222 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2224 fprintf(stderr
, " status-code=%s(%04x)\n",
2225 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2226 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2228 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2230 attr
= ippNextAttribute(ipp
))
2232 if (ippGetGroupTag(attr
) != group_tag
)
2234 group_tag
= ippGetGroupTag(attr
);
2235 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2238 if (ippGetName(attr
))
2240 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2241 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2242 ippGetCount(attr
) > 1 ? "1setOf " : "",
2243 ippTagString(ippGetValueTag(attr
)), buffer
);
2250 * 'delete_client()' - Close the socket and free all memory used by a client
2255 delete_client(ippeve_client_t
*client
) /* I - Client */
2258 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2261 * Flush pending writes before closing...
2264 httpFlushWrite(client
->http
);
2270 httpClose(client
->http
);
2272 ippDelete(client
->request
);
2273 ippDelete(client
->response
);
2280 * 'delete_job()' - Remove from the printer and free all memory used by a job
2285 delete_job(ippeve_job_t
*job
) /* I - Job */
2288 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2290 ippDelete(job
->attrs
);
2295 unlink(job
->filename
);
2297 free(job
->filename
);
2305 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2306 * used by a printer object.
2310 delete_printer(ippeve_printer_t
*printer
) /* I - Printer */
2312 if (printer
->ipv4
>= 0)
2313 close(printer
->ipv4
);
2315 if (printer
->ipv6
>= 0)
2316 close(printer
->ipv6
);
2319 if (printer
->printer_ref
)
2320 DNSServiceRefDeallocate(printer
->printer_ref
);
2321 if (printer
->ipp_ref
)
2322 DNSServiceRefDeallocate(printer
->ipp_ref
);
2323 if (printer
->ipps_ref
)
2324 DNSServiceRefDeallocate(printer
->ipps_ref
);
2325 if (printer
->http_ref
)
2326 DNSServiceRefDeallocate(printer
->http_ref
);
2327 #elif defined(HAVE_AVAHI)
2328 avahi_threaded_poll_lock(DNSSDMaster
);
2330 if (printer
->printer_ref
)
2331 avahi_entry_group_free(printer
->printer_ref
);
2332 if (printer
->ipp_ref
)
2333 avahi_entry_group_free(printer
->ipp_ref
);
2334 if (printer
->ipps_ref
)
2335 avahi_entry_group_free(printer
->ipps_ref
);
2336 if (printer
->http_ref
)
2337 avahi_entry_group_free(printer
->http_ref
);
2339 avahi_threaded_poll_unlock(DNSSDMaster
);
2340 #endif /* HAVE_DNSSD */
2342 if (printer
->dnssd_name
)
2343 free(printer
->dnssd_name
);
2345 free(printer
->name
);
2347 free(printer
->icon
);
2348 if (printer
->command
)
2349 free(printer
->command
);
2350 if (printer
->directory
)
2351 free(printer
->directory
);
2352 if (printer
->hostname
)
2353 free(printer
->hostname
);
2357 ippDelete(printer
->attrs
);
2358 cupsArrayDelete(printer
->jobs
);
2366 * 'dnssd_callback()' - Handle Bonjour registration events.
2369 static void DNSSD_API
2371 DNSServiceRef sdRef
, /* I - Service reference */
2372 DNSServiceFlags flags
, /* I - Status flags */
2373 DNSServiceErrorType errorCode
, /* I - Error, if any */
2374 const char *name
, /* I - Service name */
2375 const char *regtype
, /* I - Service type */
2376 const char *domain
, /* I - Domain for service */
2377 ippeve_printer_t
*printer
) /* I - Printer */
2385 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2386 regtype
, (int)errorCode
);
2389 else if (strcasecmp(name
, printer
->dnssd_name
))
2392 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2394 /* No lock needed since only the main thread accesses/changes this */
2395 free(printer
->dnssd_name
);
2396 printer
->dnssd_name
= strdup(name
);
2401 #elif defined(HAVE_AVAHI)
2403 * 'dnssd_callback()' - Handle Bonjour registration events.
2408 AvahiEntryGroup
*srv
, /* I - Service */
2409 AvahiEntryGroupState state
, /* I - Registration state */
2410 void *context
) /* I - Printer */
2419 * 'dnssd_client_cb()' - Client callback for Avahi.
2421 * Called whenever the client or server state changes...
2426 AvahiClient
*c
, /* I - Client */
2427 AvahiClientState state
, /* I - Current state */
2428 void *userdata
) /* I - User data (unused) */
2438 fprintf(stderr
, "Ignore Avahi state %d.\n", state
);
2441 case AVAHI_CLIENT_FAILURE
:
2442 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
2444 fputs("Avahi server crashed, exiting.\n", stderr
);
2450 #endif /* HAVE_DNSSD */
2454 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2461 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2463 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2467 #elif defined(HAVE_AVAHI)
2468 int error
; /* Error code, if any */
2470 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2472 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2476 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2478 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2482 avahi_threaded_poll_start(DNSSDMaster
);
2483 #endif /* HAVE_DNSSD */
2488 * 'filter_cb()' - Filter printer attributes based on the requested array.
2491 static int /* O - 1 to copy, 0 to ignore */
2492 filter_cb(ippeve_filter_t
*filter
, /* I - Filter parameters */
2493 ipp_t
*dst
, /* I - Destination (unused) */
2494 ipp_attribute_t
*attr
) /* I - Source attribute */
2497 * Filter attributes as needed...
2500 #ifndef _WIN32 /* Avoid MS compiler bug */
2502 #endif /* !_WIN32 */
2504 ipp_tag_t group
= ippGetGroupTag(attr
);
2505 const char *name
= ippGetName(attr
);
2507 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
)))
2510 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2515 * 'find_job()' - Find a job specified in a request.
2518 static ippeve_job_t
* /* O - Job or NULL */
2519 find_job(ippeve_client_t
*client
) /* I - Client */
2521 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2522 ippeve_job_t key
, /* Job search key */
2523 *job
; /* Matching job, if any */
2526 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2528 const char *uri
= ippGetString(attr
, 0, NULL
);
2530 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2531 uri
[client
->printer
->urilen
] == '/')
2532 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2536 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2537 key
.id
= ippGetInteger(attr
, 0);
2539 _cupsRWLockRead(&(client
->printer
->rwlock
));
2540 job
= (ippeve_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2541 _cupsRWUnlock(&(client
->printer
->rwlock
));
2548 * 'get_collection()' - Get a collection value from a file.
2551 static ipp_t
* /* O - Collection value */
2552 get_collection(FILE *fp
, /* I - File to read from */
2553 const char *filename
, /* I - Attributes filename */
2554 int *linenum
) /* IO - Line number */
2556 char token
[1024], /* Token from file */
2557 attr
[128]; /* Attribute name */
2558 ipp_tag_t value
; /* Current value type */
2559 ipp_t
*col
= ippNew(); /* Collection value */
2560 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
2563 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
2565 if (!strcmp(token
, "}"))
2567 else if (!strcmp(token
, "{") && lastcol
)
2570 * Another collection value
2573 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2574 /* Collection value */
2577 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
2581 else if (!_cups_strcasecmp(token
, "MEMBER"))
2589 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2591 fprintf(stderr
, "ippserver: Missing MEMBER value tag on line %d of \"%s\".\n", *linenum
, filename
);
2595 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
2597 fprintf(stderr
, "ippserver: Bad MEMBER value tag \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2601 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
2603 fprintf(stderr
, "ippserver: Missing MEMBER name on line %d of \"%s\".\n", *linenum
, filename
);
2607 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2609 fprintf(stderr
, "ippserver: Missing MEMBER value on line %d of \"%s\".\n", *linenum
, filename
);
2615 case IPP_TAG_BOOLEAN
:
2616 if (!_cups_strcasecmp(token
, "true"))
2617 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
2619 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
2622 case IPP_TAG_INTEGER
:
2624 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
2627 case IPP_TAG_RESOLUTION
:
2629 int xres
, /* X resolution */
2630 yres
; /* Y resolution */
2631 char units
[6]; /* Units */
2633 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
2634 (_cups_strcasecmp(units
, "dpi") &&
2635 _cups_strcasecmp(units
, "dpc") &&
2636 _cups_strcasecmp(units
, "dpcm") &&
2637 _cups_strcasecmp(units
, "other")))
2639 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2643 if (!_cups_strcasecmp(units
, "dpi"))
2644 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
2645 else if (!_cups_strcasecmp(units
, "dpc") ||
2646 !_cups_strcasecmp(units
, "dpcm"))
2647 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
2649 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
2653 case IPP_TAG_RANGE
:
2655 int lowers
[4], /* Lower value */
2656 uppers
[4], /* Upper values */
2657 num_vals
; /* Number of values */
2660 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
2661 lowers
+ 0, uppers
+ 0,
2662 lowers
+ 1, uppers
+ 1,
2663 lowers
+ 2, uppers
+ 2,
2664 lowers
+ 3, uppers
+ 3);
2666 if ((num_vals
& 1) || num_vals
== 0)
2668 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2672 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
2677 case IPP_TAG_BEGIN_COLLECTION
:
2678 if (!strcmp(token
, "{"))
2680 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2681 /* Collection value */
2685 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
2693 fprintf(stderr
, "ippserver: Bad collection value on line %d of \"%s\".\n", *linenum
, filename
);
2697 case IPP_TAG_STRING
:
2698 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
2702 if (!strchr(token
, ','))
2703 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
2707 * Multiple string values...
2710 int num_values
; /* Number of values */
2711 char *values
[100], /* Values */
2712 *ptr
; /* Pointer to next value */
2718 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
2721 values
[num_values
] = ptr
;
2723 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
2727 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
2728 NULL
, (const char **)values
);
2738 * If we get here there was a parse error; free memory and return.
2750 * 'get_token()' - Get a token from a file.
2753 static char * /* O - Token from file or NULL on EOF */
2754 get_token(FILE *fp
, /* I - File to read from */
2755 char *buf
, /* I - Buffer to read into */
2756 int buflen
, /* I - Length of buffer */
2757 int *linenum
) /* IO - Current line number */
2759 int ch
, /* Character from file */
2760 quote
; /* Quoting character */
2761 char *bufptr
, /* Pointer into buffer */
2762 *bufend
; /* End of buffer */
2768 * Skip whitespace...
2771 while (isspace(ch
= getc(fp
)))
2783 else if (ch
== '\'' || ch
== '\"')
2786 * Quoted text or regular expression...
2791 bufend
= buf
+ buflen
- 1;
2793 while ((ch
= getc(fp
)) != EOF
)
2798 * Escape next character...
2801 if (bufptr
< bufend
)
2802 *bufptr
++ = (char)ch
;
2804 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
2805 *bufptr
++ = (char)ch
;
2807 else if (ch
== quote
)
2809 else if (bufptr
< bufend
)
2810 *bufptr
++ = (char)ch
;
2823 while ((ch
= getc(fp
)) != EOF
)
2829 else if (ch
== '{' || ch
== '}' || ch
== ',')
2839 * Whitespace delimited text...
2845 bufend
= buf
+ buflen
- 1;
2847 while ((ch
= getc(fp
)) != EOF
)
2848 if (isspace(ch
) || ch
== '#')
2850 else if (bufptr
< bufend
)
2851 *bufptr
++ = (char)ch
;
2855 else if (ch
== '\n')
2867 * 'html_escape()' - Write a HTML-safe string.
2871 html_escape(ippeve_client_t
*client
, /* I - Client */
2872 const char *s
, /* I - String to write */
2873 size_t slen
) /* I - Number of characters to write */
2875 const char *start
, /* Start of segment */
2876 *end
; /* End of string */
2880 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2882 while (*s
&& s
< end
)
2884 if (*s
== '&' || *s
== '<')
2887 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2890 httpWrite2(client
->http
, "&", 5);
2892 httpWrite2(client
->http
, "<", 4);
2901 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2906 * 'html_footer()' - Show the web interface footer.
2908 * This function also writes the trailing 0-length chunk.
2912 html_footer(ippeve_client_t
*client
) /* I - Client */
2918 httpWrite2(client
->http
, "", 0);
2923 * 'html_header()' - Show the web interface header and title.
2927 html_header(ippeve_client_t
*client
, /* I - Client */
2928 const char *title
) /* I - Title */
2934 "<title>%s</title>\n"
2935 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2936 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2937 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2938 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2940 "body { font-family: sans-serif; margin: 0; }\n"
2941 "div.body { padding: 0px 10px 10px; }\n"
2942 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2943 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2944 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2945 "table.form th { text-align: right; }\n"
2946 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2947 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2948 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2949 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2950 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2951 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2952 "table.nav td { margin: 0; text-align: center; }\n"
2953 "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"
2954 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2955 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2956 "td.nav:hover { background: #666; color: #fff; }\n"
2957 "td.nav:active { background: #000; color: #ff0; }\n"
2961 "<table class=\"nav\"><tr>"
2962 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2963 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2964 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2966 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2971 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2975 html_printf(ippeve_client_t
*client
, /* I - Client */
2976 const char *format
, /* I - Printf-style format string */
2977 ...) /* I - Additional arguments as needed */
2979 va_list ap
; /* Pointer to arguments */
2980 const char *start
; /* Start of string */
2981 char size
, /* Size character (h, l, L) */
2982 type
; /* Format type character */
2983 int width
, /* Width of field */
2984 prec
; /* Number of characters of precision */
2985 char tformat
[100], /* Temporary format string for sprintf() */
2986 *tptr
, /* Pointer into temporary format */
2987 temp
[1024]; /* Buffer for formatted numbers */
2988 char *s
; /* Pointer to string */
2992 * Loop through the format string, formatting as needed...
2995 va_start(ap
, format
);
3003 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
3006 *tptr
++ = *format
++;
3010 httpWrite2(client
->http
, "%", 1);
3015 else if (strchr(" -+#\'", *format
))
3016 *tptr
++ = *format
++;
3021 * Get width from argument...
3025 width
= va_arg(ap
, int);
3027 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
3028 tptr
+= strlen(tptr
);
3034 while (isdigit(*format
& 255))
3036 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3039 width
= width
* 10 + *format
++ - '0';
3045 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3053 * Get precision from argument...
3057 prec
= va_arg(ap
, int);
3059 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
3060 tptr
+= strlen(tptr
);
3066 while (isdigit(*format
& 255))
3068 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3071 prec
= prec
* 10 + *format
++ - '0';
3076 if (*format
== 'l' && format
[1] == 'l')
3080 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
3088 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
3090 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3105 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3114 case 'E' : /* Floating point formats */
3119 if ((size_t)(width
+ 2) > sizeof(temp
))
3122 sprintf(temp
, tformat
, va_arg(ap
, double));
3124 httpWrite2(client
->http
, temp
, strlen(temp
));
3127 case 'B' : /* Integer formats */
3135 if ((size_t)(width
+ 2) > sizeof(temp
))
3138 # ifdef HAVE_LONG_LONG
3140 sprintf(temp
, tformat
, va_arg(ap
, long long));
3142 # endif /* HAVE_LONG_LONG */
3144 sprintf(temp
, tformat
, va_arg(ap
, long));
3146 sprintf(temp
, tformat
, va_arg(ap
, int));
3148 httpWrite2(client
->http
, temp
, strlen(temp
));
3151 case 'p' : /* Pointer value */
3152 if ((size_t)(width
+ 2) > sizeof(temp
))
3155 sprintf(temp
, tformat
, va_arg(ap
, void *));
3157 httpWrite2(client
->http
, temp
, strlen(temp
));
3160 case 'c' : /* Character or character array */
3163 temp
[0] = (char)va_arg(ap
, int);
3165 html_escape(client
, temp
, 1);
3168 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
3171 case 's' : /* String */
3172 if ((s
= va_arg(ap
, char *)) == NULL
)
3175 html_escape(client
, s
, strlen(s
));
3184 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
3191 * 'ipp_cancel_job()' - Cancel a job.
3195 ipp_cancel_job(ippeve_client_t
*client
) /* I - Client */
3197 ippeve_job_t
*job
; /* Job information */
3204 if ((job
= find_job(client
)) == NULL
)
3206 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3211 * See if the job is already completed, canceled, or aborted; if so,
3212 * we can't cancel...
3217 case IPP_JSTATE_CANCELED
:
3218 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3219 "Job #%d is already canceled - can\'t cancel.", job
->id
);
3222 case IPP_JSTATE_ABORTED
:
3223 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3224 "Job #%d is already aborted - can\'t cancel.", job
->id
);
3227 case IPP_JSTATE_COMPLETED
:
3228 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3229 "Job #%d is already completed - can\'t cancel.", job
->id
);
3237 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3239 if (job
->state
== IPP_JSTATE_PROCESSING
||
3240 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
3244 job
->state
= IPP_JSTATE_CANCELED
;
3245 job
->completed
= time(NULL
);
3248 _cupsRWUnlock(&(client
->printer
->rwlock
));
3250 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3257 * 'ipp_close_job()' - Close an open job.
3261 ipp_close_job(ippeve_client_t
*client
) /* I - Client */
3263 ippeve_job_t
*job
; /* Job information */
3270 if ((job
= find_job(client
)) == NULL
)
3272 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3277 * See if the job is already completed, canceled, or aborted; if so,
3278 * we can't cancel...
3283 case IPP_JSTATE_CANCELED
:
3284 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3285 "Job #%d is canceled - can\'t close.", job
->id
);
3288 case IPP_JSTATE_ABORTED
:
3289 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3290 "Job #%d is aborted - can\'t close.", job
->id
);
3293 case IPP_JSTATE_COMPLETED
:
3294 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3295 "Job #%d is completed - can\'t close.", job
->id
);
3298 case IPP_JSTATE_PROCESSING
:
3299 case IPP_JSTATE_STOPPED
:
3300 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3301 "Job #%d is already closed.", job
->id
);
3305 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3312 * 'ipp_create_job()' - Create a job object.
3316 ipp_create_job(ippeve_client_t
*client
) /* I - Client */
3318 ippeve_job_t
*job
; /* New job */
3319 cups_array_t
*ra
; /* Attributes to send in response */
3323 * Validate print job attributes...
3326 if (!valid_job_attributes(client
))
3328 httpFlush(client
->http
);
3333 * Do we have a file to print?
3336 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3338 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3339 "Unexpected document data following request.");
3347 if ((job
= create_job(client
)) == NULL
)
3349 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3350 "Currently printing another job.");
3355 * Return the job info...
3358 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3360 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3361 cupsArrayAdd(ra
, "job-id");
3362 cupsArrayAdd(ra
, "job-state");
3363 cupsArrayAdd(ra
, "job-state-message");
3364 cupsArrayAdd(ra
, "job-state-reasons");
3365 cupsArrayAdd(ra
, "job-uri");
3367 copy_job_attributes(client
, job
, ra
);
3368 cupsArrayDelete(ra
);
3373 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3377 ipp_get_job_attributes(
3378 ippeve_client_t
*client
) /* I - Client */
3380 ippeve_job_t
*job
; /* Job */
3381 cups_array_t
*ra
; /* requested-attributes */
3384 if ((job
= find_job(client
)) == NULL
)
3386 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3390 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3392 ra
= ippCreateRequestedArray(client
->request
);
3393 copy_job_attributes(client
, job
, ra
);
3394 cupsArrayDelete(ra
);
3399 * 'ipp_get_jobs()' - Get a list of job objects.
3403 ipp_get_jobs(ippeve_client_t
*client
) /* I - Client */
3405 ipp_attribute_t
*attr
; /* Current attribute */
3406 const char *which_jobs
= NULL
;
3407 /* which-jobs values */
3408 int job_comparison
; /* Job comparison */
3409 ipp_jstate_t job_state
; /* job-state value */
3410 int first_job_id
, /* First job ID */
3411 limit
, /* Maximum number of jobs to return */
3412 count
; /* Number of jobs that match */
3413 const char *username
; /* Username */
3414 ippeve_job_t
*job
; /* Current job pointer */
3415 cups_array_t
*ra
; /* Requested attributes array */
3419 * See if the "which-jobs" attribute have been specified...
3422 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3423 IPP_TAG_KEYWORD
)) != NULL
)
3425 which_jobs
= ippGetString(attr
, 0, NULL
);
3426 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3429 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3431 job_comparison
= -1;
3432 job_state
= IPP_JSTATE_STOPPED
;
3434 else if (!strcmp(which_jobs
, "completed"))
3437 job_state
= IPP_JSTATE_CANCELED
;
3439 else if (!strcmp(which_jobs
, "aborted"))
3442 job_state
= IPP_JSTATE_ABORTED
;
3444 else if (!strcmp(which_jobs
, "all"))
3447 job_state
= IPP_JSTATE_PENDING
;
3449 else if (!strcmp(which_jobs
, "canceled"))
3452 job_state
= IPP_JSTATE_CANCELED
;
3454 else if (!strcmp(which_jobs
, "pending"))
3457 job_state
= IPP_JSTATE_PENDING
;
3459 else if (!strcmp(which_jobs
, "pending-held"))
3462 job_state
= IPP_JSTATE_HELD
;
3464 else if (!strcmp(which_jobs
, "processing"))
3467 job_state
= IPP_JSTATE_PROCESSING
;
3469 else if (!strcmp(which_jobs
, "processing-stopped"))
3472 job_state
= IPP_JSTATE_STOPPED
;
3476 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3477 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3478 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3479 "which-jobs", NULL
, which_jobs
);
3484 * See if they want to limit the number of jobs reported...
3487 if ((attr
= ippFindAttribute(client
->request
, "limit",
3488 IPP_TAG_INTEGER
)) != NULL
)
3490 limit
= ippGetInteger(attr
, 0);
3492 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3497 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3498 IPP_TAG_INTEGER
)) != NULL
)
3500 first_job_id
= ippGetInteger(attr
, 0);
3502 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3509 * See if we only want to see jobs for a specific user...
3514 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3515 IPP_TAG_BOOLEAN
)) != NULL
)
3517 int my_jobs
= ippGetBoolean(attr
, 0);
3519 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3520 my_jobs
? "true" : "false");
3524 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3525 IPP_TAG_NAME
)) == NULL
)
3527 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3528 "Need requesting-user-name with my-jobs.");
3532 username
= ippGetString(attr
, 0, NULL
);
3534 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3535 client
->hostname
, username
);
3540 * OK, build a list of jobs for this printer...
3543 ra
= ippCreateRequestedArray(client
->request
);
3545 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3547 _cupsRWLockRead(&(client
->printer
->rwlock
));
3549 for (count
= 0, job
= (ippeve_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3550 (limit
<= 0 || count
< limit
) && job
;
3551 job
= (ippeve_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3554 * Filter out jobs that don't match...
3557 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3558 (job_comparison
== 0 && job
->state
!= job_state
) ||
3559 (job_comparison
> 0 && job
->state
< job_state
) ||
3560 job
->id
< first_job_id
||
3561 (username
&& job
->username
&&
3562 strcasecmp(username
, job
->username
)))
3566 ippAddSeparator(client
->response
);
3569 copy_job_attributes(client
, job
, ra
);
3572 cupsArrayDelete(ra
);
3574 _cupsRWUnlock(&(client
->printer
->rwlock
));
3579 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3583 ipp_get_printer_attributes(
3584 ippeve_client_t
*client
) /* I - Client */
3586 cups_array_t
*ra
; /* Requested attributes array */
3587 ippeve_printer_t
*printer
; /* Printer */
3591 * Send the attributes...
3594 ra
= ippCreateRequestedArray(client
->request
);
3595 printer
= client
->printer
;
3597 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3599 _cupsRWLockRead(&(printer
->rwlock
));
3601 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3602 IPP_TAG_CUPS_CONST
);
3604 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3606 int i
, /* Looping var */
3607 num_ready
= 0; /* Number of ready media */
3608 ipp_t
*ready
[3]; /* Ready media */
3610 if (printer
->main_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3612 if (printer
->main_type
!= IPPEVE_MEDIA_TYPE_NONE
)
3613 ready
[num_ready
++] = create_media_col(media_supported
[printer
->main_size
], "main", media_type_supported
[printer
->main_type
], media_col_sizes
[printer
->main_size
][0], media_col_sizes
[printer
->main_size
][1], 635);
3615 ready
[num_ready
++] = create_media_col(media_supported
[printer
->main_size
], "main", NULL
, media_col_sizes
[printer
->main_size
][0], media_col_sizes
[printer
->main_size
][1], 635);
3617 if (printer
->envelope_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3618 ready
[num_ready
++] = create_media_col(media_supported
[printer
->envelope_size
], "envelope", NULL
, media_col_sizes
[printer
->envelope_size
][0], media_col_sizes
[printer
->envelope_size
][1], 635);
3619 if (printer
->photo_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3621 if (printer
->photo_type
!= IPPEVE_MEDIA_TYPE_NONE
)
3622 ready
[num_ready
++] = create_media_col(media_supported
[printer
->photo_size
], "photo", media_type_supported
[printer
->photo_type
], media_col_sizes
[printer
->photo_size
][0], media_col_sizes
[printer
->photo_size
][1], 0);
3624 ready
[num_ready
++] = create_media_col(media_supported
[printer
->photo_size
], "photo", NULL
, media_col_sizes
[printer
->photo_size
][0], media_col_sizes
[printer
->photo_size
][1], 0);
3629 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3630 for (i
= 0; i
< num_ready
; i
++)
3631 ippDelete(ready
[i
]);
3634 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3637 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3639 int num_ready
= 0; /* Number of ready media */
3640 const char *ready
[3]; /* Ready media */
3642 if (printer
->main_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3643 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3645 if (printer
->envelope_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3646 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3648 if (printer
->photo_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3649 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3652 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3654 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3657 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3658 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3660 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3661 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3663 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3664 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3667 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3668 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3669 "printer-state", printer
->state
);
3671 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3672 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3674 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3675 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3677 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3679 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3681 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3684 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3686 if (printer
->state_reasons
== IPPEVE_PREASON_NONE
)
3687 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3688 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3689 "printer-state-reasons", NULL
, "none");
3692 ipp_attribute_t
*attr
= NULL
; /* printer-state-reasons */
3693 ippeve_preason_t bit
; /* Reason bit */
3694 int i
; /* Looping var */
3695 char reason
[32]; /* Reason string */
3697 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
3699 if (printer
->state_reasons
& bit
)
3701 snprintf(reason
, sizeof(reason
), "%s-%s", ippeve_preason_strings
[i
], printer
->state
== IPP_PSTATE_IDLE
? "report" : printer
->state
== IPP_PSTATE_PROCESSING
? "warning" : "error");
3703 ippSetString(client
->response
, &attr
, ippGetCount(attr
), reason
);
3705 attr
= ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
, reason
);
3711 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3713 int i
; /* Looping var */
3714 char buffer
[256]; /* Supply value buffer */
3715 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3716 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3718 for (i
= 0; i
< 5; i
++)
3720 snprintf(buffer
, sizeof(buffer
), "index=%d;class=%s;type=%s;unit=percent;maxcapacity=100;level=%d;colorantname=%s;", i
+ 1, i
< 4 ? "supplyThatIsConsumed" : "receptacleThatIsFilled", i
< 4 ? "toner" : "wasteToner", printer
->supplies
[i
], colorants
[i
]);
3723 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3725 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3729 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3730 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3732 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3733 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3734 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3736 _cupsRWUnlock(&(printer
->rwlock
));
3738 cupsArrayDelete(ra
);
3743 * 'ipp_identify_printer()' - Beep or display a message.
3747 ipp_identify_printer(
3748 ippeve_client_t
*client
) /* I - Client */
3750 ipp_attribute_t
*actions
, /* identify-actions */
3751 *message
; /* message */
3754 actions
= ippFindAttribute(client
->request
, "identify-actions", IPP_TAG_KEYWORD
);
3755 message
= ippFindAttribute(client
->request
, "message", IPP_TAG_TEXT
);
3757 if (!actions
|| ippContainsString(actions
, "sound"))
3763 if (ippContainsString(actions
, "display"))
3764 printf("IDENTIFY from %s: %s\n", client
->hostname
, message
? ippGetString(message
, 0, NULL
) : "No message supplied");
3766 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3771 * 'ipp_print_job()' - Create a job object with an attached document.
3775 ipp_print_job(ippeve_client_t
*client
) /* I - Client */
3777 ippeve_job_t
*job
; /* New job */
3778 char filename
[1024], /* Filename buffer */
3779 buffer
[4096]; /* Copy buffer */
3780 ssize_t bytes
; /* Bytes read */
3781 cups_array_t
*ra
; /* Attributes to send in response */
3782 _cups_thread_t t
; /* Thread */
3786 * Validate print job attributes...
3789 if (!valid_job_attributes(client
))
3791 httpFlush(client
->http
);
3796 * Do we have a file to print?
3799 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3801 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3809 if ((job
= create_job(client
)) == NULL
)
3811 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3812 "Currently printing another job.");
3817 * Create a file for the request data...
3820 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3823 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3825 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3827 job
->state
= IPP_JSTATE_ABORTED
;
3829 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3830 "Unable to create print file: %s", strerror(errno
));
3834 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3836 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3838 int error
= errno
; /* Write error */
3840 job
->state
= IPP_JSTATE_ABORTED
;
3847 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3848 "Unable to write print file: %s", strerror(error
));
3856 * Got an error while reading the print data, so abort this job.
3859 job
->state
= IPP_JSTATE_ABORTED
;
3866 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3867 "Unable to read print file.");
3873 int error
= errno
; /* Write error */
3875 job
->state
= IPP_JSTATE_ABORTED
;
3880 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3881 "Unable to write print file: %s", strerror(error
));
3886 job
->filename
= strdup(filename
);
3887 job
->state
= IPP_JSTATE_PENDING
;
3890 * Process the job...
3893 t
= _cupsThreadCreate((_cups_thread_func_t
)process_job
, job
);
3897 _cupsThreadDetach(t
);
3901 job
->state
= IPP_JSTATE_ABORTED
;
3902 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3907 * Return the job info...
3910 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3912 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3913 cupsArrayAdd(ra
, "job-id");
3914 cupsArrayAdd(ra
, "job-state");
3915 cupsArrayAdd(ra
, "job-state-message");
3916 cupsArrayAdd(ra
, "job-state-reasons");
3917 cupsArrayAdd(ra
, "job-uri");
3919 copy_job_attributes(client
, job
, ra
);
3920 cupsArrayDelete(ra
);
3925 * 'ipp_print_uri()' - Create a job object with a referenced document.
3929 ipp_print_uri(ippeve_client_t
*client
) /* I - Client */
3931 ippeve_job_t
*job
; /* New job */
3932 ipp_attribute_t
*uri
; /* document-uri */
3933 char scheme
[256], /* URI scheme */
3934 userpass
[256], /* Username and password info */
3935 hostname
[256], /* Hostname */
3936 resource
[1024]; /* Resource path */
3937 int port
; /* Port number */
3938 http_uri_status_t uri_status
; /* URI decode status */
3939 http_encryption_t encryption
; /* Encryption to use, if any */
3940 http_t
*http
; /* Connection for http/https URIs */
3941 http_status_t status
; /* Access status for http/https URIs */
3942 int infile
; /* Input file for local file URIs */
3943 char filename
[1024], /* Filename buffer */
3944 buffer
[4096]; /* Copy buffer */
3945 ssize_t bytes
; /* Bytes read */
3946 cups_array_t
*ra
; /* Attributes to send in response */
3947 static const char * const uri_status_strings
[] =
3948 { /* URI decode errors */
3950 "Bad arguments to function.",
3951 "Bad resource in URI.",
3952 "Bad port number in URI.",
3953 "Bad hostname in URI.",
3954 "Bad username in URI.",
3955 "Bad scheme in URI.",
3961 * Validate print job attributes...
3964 if (!valid_job_attributes(client
))
3966 httpFlush(client
->http
);
3971 * Do we have a file to print?
3974 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3976 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3977 "Unexpected document data following request.");
3982 * Do we have a document URI?
3985 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3986 IPP_TAG_URI
)) == NULL
)
3988 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3992 if (ippGetCount(uri
) != 1)
3994 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3995 "Too many document-uri values.");
3999 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4000 scheme
, sizeof(scheme
), userpass
,
4001 sizeof(userpass
), hostname
, sizeof(hostname
),
4002 &port
, resource
, sizeof(resource
));
4003 if (uri_status
< HTTP_URI_STATUS_OK
)
4005 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4006 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4010 if (strcmp(scheme
, "file") &&
4012 strcmp(scheme
, "https") &&
4013 #endif /* HAVE_SSL */
4014 strcmp(scheme
, "http"))
4016 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4017 "URI scheme \"%s\" not supported.", scheme
);
4021 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4023 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4024 "Unable to access URI: %s", strerror(errno
));
4032 if ((job
= create_job(client
)) == NULL
)
4034 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
4035 "Currently printing another job.");
4040 * Create a file for the request data...
4043 if (!strcasecmp(job
->format
, "image/jpeg"))
4044 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4045 client
->printer
->directory
, job
->id
);
4046 else if (!strcasecmp(job
->format
, "image/png"))
4047 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4048 client
->printer
->directory
, job
->id
);
4049 else if (!strcasecmp(job
->format
, "application/pdf"))
4050 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4051 client
->printer
->directory
, job
->id
);
4052 else if (!strcasecmp(job
->format
, "application/postscript"))
4053 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4054 client
->printer
->directory
, job
->id
);
4056 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4057 client
->printer
->directory
, job
->id
);
4059 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
4061 job
->state
= IPP_JSTATE_ABORTED
;
4063 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4064 "Unable to create print file: %s", strerror(errno
));
4068 if (!strcmp(scheme
, "file"))
4070 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4072 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4073 "Unable to access URI: %s", strerror(errno
));
4079 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4080 (errno
== EAGAIN
|| errno
== EINTR
))
4082 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4084 int error
= errno
; /* Write error */
4086 job
->state
= IPP_JSTATE_ABORTED
;
4094 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4095 "Unable to write print file: %s", strerror(error
));
4106 if (port
== 443 || !strcmp(scheme
, "https"))
4107 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4109 #endif /* HAVE_SSL */
4110 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4112 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4113 1, 30000, NULL
)) == NULL
)
4115 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4116 "Unable to connect to %s: %s", hostname
,
4117 cupsLastErrorString());
4118 job
->state
= IPP_JSTATE_ABORTED
;
4127 httpClearFields(http
);
4128 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4129 if (httpGet(http
, resource
))
4131 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4132 "Unable to GET URI: %s", strerror(errno
));
4134 job
->state
= IPP_JSTATE_ABORTED
;
4144 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4146 if (status
!= HTTP_STATUS_OK
)
4148 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4149 "Unable to GET URI: %s", httpStatus(status
));
4151 job
->state
= IPP_JSTATE_ABORTED
;
4161 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4163 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4165 int error
= errno
; /* Write error */
4167 job
->state
= IPP_JSTATE_ABORTED
;
4175 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4176 "Unable to write print file: %s", strerror(error
));
4186 int error
= errno
; /* Write error */
4188 job
->state
= IPP_JSTATE_ABORTED
;
4193 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4194 "Unable to write print file: %s", strerror(error
));
4199 job
->filename
= strdup(filename
);
4200 job
->state
= IPP_JSTATE_PENDING
;
4203 * Process the job...
4209 * Return the job info...
4212 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4214 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4215 cupsArrayAdd(ra
, "job-id");
4216 cupsArrayAdd(ra
, "job-state");
4217 cupsArrayAdd(ra
, "job-state-reasons");
4218 cupsArrayAdd(ra
, "job-uri");
4220 copy_job_attributes(client
, job
, ra
);
4221 cupsArrayDelete(ra
);
4226 * 'ipp_send_document()' - Add an attached document to a job object created with
4231 ipp_send_document(ippeve_client_t
*client
)/* I - Client */
4233 ippeve_job_t
*job
; /* Job information */
4234 char filename
[1024], /* Filename buffer */
4235 buffer
[4096]; /* Copy buffer */
4236 ssize_t bytes
; /* Bytes read */
4237 ipp_attribute_t
*attr
; /* Current attribute */
4238 cups_array_t
*ra
; /* Attributes to send in response */
4245 if ((job
= find_job(client
)) == NULL
)
4247 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4248 httpFlush(client
->http
);
4253 * See if we already have a document for this job or the job has already
4254 * in a non-pending state...
4257 if (job
->state
> IPP_JSTATE_HELD
)
4259 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4260 "Job is not in a pending state.");
4261 httpFlush(client
->http
);
4264 else if (job
->filename
|| job
->fd
>= 0)
4266 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4267 "Multiple document jobs are not supported.");
4268 httpFlush(client
->http
);
4272 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4273 IPP_TAG_ZERO
)) == NULL
)
4275 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4276 "Missing required last-document attribute.");
4277 httpFlush(client
->http
);
4280 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4281 !ippGetBoolean(attr
, 0))
4283 respond_unsupported(client
, attr
);
4284 httpFlush(client
->http
);
4289 * Validate document attributes...
4292 if (!valid_doc_attributes(client
))
4294 httpFlush(client
->http
);
4298 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
4301 * Get the document format for the job...
4304 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4306 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
4307 job
->format
= ippGetString(attr
, 0, NULL
);
4308 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
4309 job
->format
= ippGetString(attr
, 0, NULL
);
4311 job
->format
= "application/octet-stream";
4314 * Create a file for the request data...
4317 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
4320 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
4322 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4324 _cupsRWUnlock(&(client
->printer
->rwlock
));
4328 job
->state
= IPP_JSTATE_ABORTED
;
4330 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4331 "Unable to create print file: %s", strerror(errno
));
4335 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
4337 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4339 int error
= errno
; /* Write error */
4341 job
->state
= IPP_JSTATE_ABORTED
;
4348 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4349 "Unable to write print file: %s", strerror(error
));
4357 * Got an error while reading the print data, so abort this job.
4360 job
->state
= IPP_JSTATE_ABORTED
;
4367 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4368 "Unable to read print file.");
4374 int error
= errno
; /* Write error */
4376 job
->state
= IPP_JSTATE_ABORTED
;
4381 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4382 "Unable to write print file: %s", strerror(error
));
4386 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4389 job
->filename
= strdup(filename
);
4390 job
->state
= IPP_JSTATE_PENDING
;
4392 _cupsRWUnlock(&(client
->printer
->rwlock
));
4395 * Process the job...
4401 * Return the job info...
4404 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4406 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4407 cupsArrayAdd(ra
, "job-id");
4408 cupsArrayAdd(ra
, "job-state");
4409 cupsArrayAdd(ra
, "job-state-reasons");
4410 cupsArrayAdd(ra
, "job-uri");
4412 copy_job_attributes(client
, job
, ra
);
4413 cupsArrayDelete(ra
);
4418 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4423 ipp_send_uri(ippeve_client_t
*client
) /* I - Client */
4425 ippeve_job_t
*job
; /* Job information */
4426 ipp_attribute_t
*uri
; /* document-uri */
4427 char scheme
[256], /* URI scheme */
4428 userpass
[256], /* Username and password info */
4429 hostname
[256], /* Hostname */
4430 resource
[1024]; /* Resource path */
4431 int port
; /* Port number */
4432 http_uri_status_t uri_status
; /* URI decode status */
4433 http_encryption_t encryption
; /* Encryption to use, if any */
4434 http_t
*http
; /* Connection for http/https URIs */
4435 http_status_t status
; /* Access status for http/https URIs */
4436 int infile
; /* Input file for local file URIs */
4437 char filename
[1024], /* Filename buffer */
4438 buffer
[4096]; /* Copy buffer */
4439 ssize_t bytes
; /* Bytes read */
4440 ipp_attribute_t
*attr
; /* Current attribute */
4441 cups_array_t
*ra
; /* Attributes to send in response */
4442 static const char * const uri_status_strings
[] =
4443 { /* URI decode errors */
4445 "Bad arguments to function.",
4446 "Bad resource in URI.",
4447 "Bad port number in URI.",
4448 "Bad hostname in URI.",
4449 "Bad username in URI.",
4450 "Bad scheme in URI.",
4459 if ((job
= find_job(client
)) == NULL
)
4461 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4462 httpFlush(client
->http
);
4467 * See if we already have a document for this job or the job has already
4468 * in a non-pending state...
4471 if (job
->state
> IPP_JSTATE_HELD
)
4473 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4474 "Job is not in a pending state.");
4475 httpFlush(client
->http
);
4478 else if (job
->filename
|| job
->fd
>= 0)
4480 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4481 "Multiple document jobs are not supported.");
4482 httpFlush(client
->http
);
4486 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4487 IPP_TAG_ZERO
)) == NULL
)
4489 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4490 "Missing required last-document attribute.");
4491 httpFlush(client
->http
);
4494 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4495 !ippGetBoolean(attr
, 0))
4497 respond_unsupported(client
, attr
);
4498 httpFlush(client
->http
);
4503 * Validate document attributes...
4506 if (!valid_doc_attributes(client
))
4508 httpFlush(client
->http
);
4513 * Do we have a file to print?
4516 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4518 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4519 "Unexpected document data following request.");
4524 * Do we have a document URI?
4527 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4528 IPP_TAG_URI
)) == NULL
)
4530 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4534 if (ippGetCount(uri
) != 1)
4536 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4537 "Too many document-uri values.");
4541 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4542 scheme
, sizeof(scheme
), userpass
,
4543 sizeof(userpass
), hostname
, sizeof(hostname
),
4544 &port
, resource
, sizeof(resource
));
4545 if (uri_status
< HTTP_URI_STATUS_OK
)
4547 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4548 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4552 if (strcmp(scheme
, "file") &&
4554 strcmp(scheme
, "https") &&
4555 #endif /* HAVE_SSL */
4556 strcmp(scheme
, "http"))
4558 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4559 "URI scheme \"%s\" not supported.", scheme
);
4563 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4565 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4566 "Unable to access URI: %s", strerror(errno
));
4571 * Get the document format for the job...
4574 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4576 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4577 IPP_TAG_MIMETYPE
)) != NULL
)
4578 job
->format
= ippGetString(attr
, 0, NULL
);
4580 job
->format
= "application/octet-stream";
4583 * Create a file for the request data...
4586 if (!strcasecmp(job
->format
, "image/jpeg"))
4587 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4588 client
->printer
->directory
, job
->id
);
4589 else if (!strcasecmp(job
->format
, "image/png"))
4590 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4591 client
->printer
->directory
, job
->id
);
4592 else if (!strcasecmp(job
->format
, "application/pdf"))
4593 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4594 client
->printer
->directory
, job
->id
);
4595 else if (!strcasecmp(job
->format
, "application/postscript"))
4596 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4597 client
->printer
->directory
, job
->id
);
4599 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4600 client
->printer
->directory
, job
->id
);
4602 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4604 _cupsRWUnlock(&(client
->printer
->rwlock
));
4608 job
->state
= IPP_JSTATE_ABORTED
;
4610 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4611 "Unable to create print file: %s", strerror(errno
));
4615 if (!strcmp(scheme
, "file"))
4617 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4619 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4620 "Unable to access URI: %s", strerror(errno
));
4626 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4627 (errno
== EAGAIN
|| errno
== EINTR
))
4629 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4631 int error
= errno
; /* Write error */
4633 job
->state
= IPP_JSTATE_ABORTED
;
4641 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4642 "Unable to write print file: %s", strerror(error
));
4653 if (port
== 443 || !strcmp(scheme
, "https"))
4654 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4656 #endif /* HAVE_SSL */
4657 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4659 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4660 1, 30000, NULL
)) == NULL
)
4662 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4663 "Unable to connect to %s: %s", hostname
,
4664 cupsLastErrorString());
4665 job
->state
= IPP_JSTATE_ABORTED
;
4674 httpClearFields(http
);
4675 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4676 if (httpGet(http
, resource
))
4678 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4679 "Unable to GET URI: %s", strerror(errno
));
4681 job
->state
= IPP_JSTATE_ABORTED
;
4691 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4693 if (status
!= HTTP_STATUS_OK
)
4695 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4696 "Unable to GET URI: %s", httpStatus(status
));
4698 job
->state
= IPP_JSTATE_ABORTED
;
4708 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4710 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4712 int error
= errno
; /* Write error */
4714 job
->state
= IPP_JSTATE_ABORTED
;
4722 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4723 "Unable to write print file: %s", strerror(error
));
4733 int error
= errno
; /* Write error */
4735 job
->state
= IPP_JSTATE_ABORTED
;
4740 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4741 "Unable to write print file: %s", strerror(error
));
4745 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4748 job
->filename
= strdup(filename
);
4749 job
->state
= IPP_JSTATE_PENDING
;
4751 _cupsRWUnlock(&(client
->printer
->rwlock
));
4754 * Process the job...
4760 * Return the job info...
4763 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4765 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4766 cupsArrayAdd(ra
, "job-id");
4767 cupsArrayAdd(ra
, "job-state");
4768 cupsArrayAdd(ra
, "job-state-reasons");
4769 cupsArrayAdd(ra
, "job-uri");
4771 copy_job_attributes(client
, job
, ra
);
4772 cupsArrayDelete(ra
);
4777 * 'ipp_validate_job()' - Validate job creation attributes.
4781 ipp_validate_job(ippeve_client_t
*client
) /* I - Client */
4783 if (valid_job_attributes(client
))
4784 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4789 * 'load_attributes()' - Load printer attributes from a file.
4791 * Syntax is based on ipptool format:
4793 * ATTR value-tag name value
4797 load_attributes(const char *filename
, /* I - File to load */
4798 ipp_t
*attrs
) /* I - Printer attributes */
4800 int linenum
= 0; /* Current line number */
4801 FILE *fp
= NULL
; /* Test file */
4802 char attr
[128], /* Attribute name */
4803 token
[1024], /* Token from file */
4804 *tokenptr
; /* Pointer into token */
4805 ipp_tag_t value
; /* Current value type */
4806 ipp_attribute_t
*attrptr
, /* Attribute pointer */
4807 *lastcol
= NULL
; /* Last collection attribute */
4810 if ((fp
= fopen(filename
, "r")) == NULL
)
4812 fprintf(stderr
, "ippserver: Unable to open \"%s\": %s\n", filename
, strerror(errno
));
4816 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
4818 if (!_cups_strcasecmp(token
, "ATTR"))
4824 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4826 fprintf(stderr
, "ippserver: Missing ATTR value tag on line %d of \"%s\".\n", linenum
, filename
);
4830 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
4832 fprintf(stderr
, "ippserver: Bad ATTR value tag \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4836 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
4838 fprintf(stderr
, "ippserver: Missing ATTR name on line %d of \"%s\".\n", linenum
, filename
);
4842 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4844 fprintf(stderr
, "ippserver: Missing ATTR value on line %d of \"%s\".\n", linenum
, filename
);
4852 case IPP_TAG_BOOLEAN
:
4853 if (!_cups_strcasecmp(token
, "true"))
4854 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, 1);
4856 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, (char)atoi(token
));
4859 case IPP_TAG_INTEGER
:
4861 if (!strchr(token
, ','))
4862 attrptr
= ippAddInteger(attrs
, IPP_TAG_PRINTER
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
4865 int values
[100], /* Values */
4866 num_values
= 1; /* Number of values */
4868 values
[0] = (int)strtol(token
, &tokenptr
, 10);
4869 while (tokenptr
&& *tokenptr
&&
4870 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
4872 if (*tokenptr
== ',')
4874 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
4877 values
[num_values
] = (int)strtol(tokenptr
, &tokenptr
, 0);
4881 attrptr
= ippAddIntegers(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, values
);
4884 if (!tokenptr
|| *tokenptr
)
4886 fprintf(stderr
, "ippserver: Bad %s value \"%s\" on line %d of \"%s\".\n", ippTagString(value
), token
, linenum
, filename
);
4891 case IPP_TAG_RESOLUTION
:
4893 int xres
, /* X resolution */
4894 yres
; /* Y resolution */
4895 ipp_res_t units
; /* Units */
4896 char *start
, /* Start of value */
4897 *ptr
, /* Pointer into value */
4898 *next
= NULL
; /* Next value */
4900 for (start
= token
; start
; start
= next
)
4902 xres
= yres
= (int)strtol(start
, (char **)&ptr
, 10);
4903 if (ptr
> start
&& xres
> 0)
4906 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
4909 if (ptr
&& (next
= strchr(ptr
, ',')) != NULL
)
4912 if (ptr
<= start
|| xres
<= 0 || yres
<= 0 || !ptr
||
4913 (_cups_strcasecmp(ptr
, "dpi") &&
4914 _cups_strcasecmp(ptr
, "dpc") &&
4915 _cups_strcasecmp(ptr
, "dpcm") &&
4916 _cups_strcasecmp(ptr
, "other")))
4918 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4922 if (!_cups_strcasecmp(ptr
, "dpc") || !_cups_strcasecmp(ptr
, "dpcm"))
4923 units
= IPP_RES_PER_CM
;
4925 units
= IPP_RES_PER_INCH
;
4928 ippSetResolution(attrs
, &attrptr
, ippGetCount(attrptr
), units
, xres
, yres
);
4930 attrptr
= ippAddResolution(attrs
, IPP_TAG_PRINTER
, attr
, units
, xres
, yres
);
4935 case IPP_TAG_RANGE
:
4937 int lowers
[4], /* Lower value */
4938 uppers
[4], /* Upper values */
4939 num_vals
; /* Number of values */
4942 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
4943 lowers
+ 0, uppers
+ 0,
4944 lowers
+ 1, uppers
+ 1,
4945 lowers
+ 2, uppers
+ 2,
4946 lowers
+ 3, uppers
+ 3);
4948 if ((num_vals
& 1) || num_vals
== 0)
4950 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", token
, linenum
, filename
);
4954 attrptr
= ippAddRanges(attrs
, IPP_TAG_PRINTER
, attr
, num_vals
/ 2, lowers
,
4959 case IPP_TAG_BEGIN_COLLECTION
:
4960 if (!strcmp(token
, "{"))
4962 ipp_t
*col
= get_collection(fp
, filename
, &linenum
);
4963 /* Collection value */
4967 attrptr
= lastcol
= ippAddCollection(attrs
, IPP_TAG_PRINTER
, attr
, col
);
4975 fprintf(stderr
, "ippserver: Bad ATTR collection value on line %d of \"%s\".\n", linenum
, filename
);
4981 ipp_t
*col
; /* Collection value */
4982 long pos
= ftell(fp
); /* Save position of file */
4984 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4987 if (strcmp(token
, ","))
4989 fseek(fp
, pos
, SEEK_SET
);
4993 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
4995 fprintf(stderr
, "ippserver: Unexpected \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4999 if ((col
= get_collection(fp
, filename
, &linenum
)) == NULL
)
5002 ippSetCollection(attrs
, &attrptr
, ippGetCount(attrptr
), col
);
5004 while (!strcmp(token
, "{"));
5007 case IPP_TAG_STRING
:
5008 attrptr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, attr
, token
, (int)strlen(token
));
5012 fprintf(stderr
, "ippserver: Unsupported ATTR value tag %s on line %d of \"%s\".\n", ippTagString(value
), linenum
, filename
);
5015 case IPP_TAG_TEXTLANG
:
5016 case IPP_TAG_NAMELANG
:
5019 case IPP_TAG_KEYWORD
:
5021 case IPP_TAG_URISCHEME
:
5022 case IPP_TAG_CHARSET
:
5023 case IPP_TAG_LANGUAGE
:
5024 case IPP_TAG_MIMETYPE
:
5025 if (!strchr(token
, ','))
5026 attrptr
= ippAddString(attrs
, IPP_TAG_PRINTER
, value
, attr
, NULL
, token
);
5030 * Multiple string values...
5033 int num_values
; /* Number of values */
5034 char *values
[100], /* Values */
5035 *ptr
; /* Pointer to next value */
5041 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
5043 if (ptr
> token
&& ptr
[-1] == '\\')
5044 _cups_strcpy(ptr
- 1, ptr
);
5048 values
[num_values
] = ptr
;
5050 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
5055 attrptr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, NULL
, (const char **)values
);
5062 fprintf(stderr
, "ippserver: Unable to add attribute on line %d of \"%s\": %s\n", linenum
, filename
, cupsLastErrorString());
5068 fprintf(stderr
, "ippserver: Unknown directive \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
5078 * 'parse_options()' - Parse URL options into CUPS options.
5080 * The client->options string is destroyed by this function.
5083 static int /* O - Number of options */
5084 parse_options(ippeve_client_t
*client
, /* I - Client */
5085 cups_option_t
**options
) /* O - Options */
5087 char *name
, /* Name */
5089 *next
; /* Next name=value pair */
5090 int num_options
= 0; /* Number of options */
5095 for (name
= client
->options
; name
&& *name
; name
= next
)
5097 if ((value
= strchr(name
, '=')) == NULL
)
5101 if ((next
= strchr(value
, '&')) != NULL
)
5104 num_options
= cupsAddOption(name
, value
, num_options
, options
);
5107 return (num_options
);
5112 * 'process_attr_message()' - Process an ATTR: message from a command.
5116 process_attr_message(
5117 ippeve_job_t
*job
, /* I - Job */
5118 char *message
) /* I - Message */
5126 * 'process_client()' - Process client requests on a thread.
5129 static void * /* O - Exit status */
5130 process_client(ippeve_client_t
*client
) /* I - Client */
5133 * Loop until we are out of requests or timeout (30 seconds)...
5137 int first_time
= 1; /* First time request? */
5138 #endif /* HAVE_SSL */
5140 while (httpWait(client
->http
, 30000))
5146 * See if we need to negotiate a TLS connection...
5149 char buf
[1]; /* First byte from client */
5151 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
5153 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
5155 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
5157 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5161 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5166 #endif /* HAVE_SSL */
5168 if (!process_http(client
))
5173 * Close the conection to the client and return...
5176 delete_client(client
);
5183 * 'process_http()' - Process a HTTP request.
5186 int /* O - 1 on success, 0 on failure */
5187 process_http(ippeve_client_t
*client
) /* I - Client connection */
5189 char uri
[1024]; /* URI */
5190 http_state_t http_state
; /* HTTP state */
5191 http_status_t http_status
; /* HTTP status */
5192 ipp_state_t ipp_state
; /* State of IPP transfer */
5193 char scheme
[32], /* Method/scheme */
5194 userpass
[128], /* Username:password */
5195 hostname
[HTTP_MAX_HOST
];
5197 int port
; /* Port number */
5198 const char *encoding
; /* Content-Encoding value */
5199 static const char * const http_states
[] =
5200 { /* Strings for logging HTTP method */
5221 * Clear state variables...
5224 ippDelete(client
->request
);
5225 ippDelete(client
->response
);
5227 client
->request
= NULL
;
5228 client
->response
= NULL
;
5229 client
->operation
= HTTP_STATE_WAITING
;
5232 * Read a request from the connection...
5235 while ((http_state
= httpReadRequest(client
->http
, uri
,
5236 sizeof(uri
))) == HTTP_STATE_WAITING
)
5240 * Parse the request line...
5243 if (http_state
== HTTP_STATE_ERROR
)
5245 if (httpError(client
->http
) == EPIPE
)
5246 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
5248 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
5249 strerror(httpError(client
->http
)));
5253 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
5255 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
5256 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5259 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
5261 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
5262 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5266 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
5270 * Separate the URI into its components...
5273 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
5274 userpass
, sizeof(userpass
),
5275 hostname
, sizeof(hostname
), &port
,
5276 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
5277 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
5279 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
5280 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5284 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
5285 *(client
->options
)++ = '\0';
5288 * Process the request...
5291 client
->start
= time(NULL
);
5292 client
->operation
= httpGetState(client
->http
);
5295 * Parse incoming parameters until the status changes...
5298 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
5300 if (http_status
!= HTTP_STATUS_OK
)
5302 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5306 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
5307 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
5310 * HTTP/1.1 and higher require the "Host:" field...
5313 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5318 * Handle HTTP Upgrade...
5321 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
5325 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
5327 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
5330 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
5332 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
5334 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5338 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5341 #endif /* HAVE_SSL */
5343 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
5348 * Handle HTTP Expect...
5351 if (httpGetExpect(client
->http
) &&
5352 (client
->operation
== HTTP_STATE_POST
||
5353 client
->operation
== HTTP_STATE_PUT
))
5355 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
5358 * Send 100-continue header...
5361 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
5367 * Send 417-expectation-failed header...
5370 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
5376 * Handle new transfers...
5379 encoding
= httpGetContentEncoding(client
->http
);
5381 switch (client
->operation
)
5383 case HTTP_STATE_OPTIONS
:
5385 * Do OPTIONS command...
5388 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
5390 case HTTP_STATE_HEAD
:
5391 if (!strcmp(client
->uri
, "/icon.png"))
5392 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
5393 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
5394 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
5396 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5398 case HTTP_STATE_GET
:
5399 if (!strcmp(client
->uri
, "/icon.png"))
5402 * Send PNG icon file.
5405 int fd
; /* Icon file */
5406 struct stat fileinfo
; /* Icon file information */
5407 char buffer
[4096]; /* Copy buffer */
5408 ssize_t bytes
; /* Bytes */
5410 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5412 if (!stat(client
->printer
->icon
, &fileinfo
) &&
5413 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5415 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
5416 (size_t)fileinfo
.st_size
))
5422 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5423 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5425 httpFlushWrite(client
->http
);
5430 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5432 else if (!strcmp(client
->uri
, "/"))
5435 * Show web status page...
5438 ippeve_job_t
*job
; /* Current job */
5439 int i
; /* Looping var */
5440 ippeve_preason_t reason
; /* Current reason */
5441 static const char * const reasons
[] =
5442 { /* Reason strings */
5445 "Input Tray Missing",
5446 "Marker Supply Empty",
5447 "Marker Supply Low",
5448 "Marker Waste Almost Full",
5449 "Marker Waste Full",
5461 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5464 html_header(client
, client
->printer
->name
);
5466 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
5467 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
5468 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
5469 if (client
->printer
->state_reasons
& reason
)
5470 html_printf(client
, "\n<br> %s", reasons
[i
]);
5471 html_printf(client
, "</p>\n");
5473 if (cupsArrayCount(client
->printer
->jobs
) > 0)
5475 _cupsRWLockRead(&(client
->printer
->rwlock
));
5477 html_printf(client
, "<table class=\"striped\" summary=\"Jobs\"><thead><tr><th>Job #</th><th>Name</th><th>Owner</th><th>When</th></tr></thead><tbody>\n");
5478 for (job
= (ippeve_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (ippeve_job_t
*)cupsArrayNext(client
->printer
->jobs
))
5480 char when
[256], /* When job queued/started/finished */
5481 hhmmss
[64]; /* Time HH:MM:SS */
5485 case IPP_JSTATE_PENDING
:
5486 case IPP_JSTATE_HELD
:
5487 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
5489 case IPP_JSTATE_PROCESSING
:
5490 case IPP_JSTATE_STOPPED
:
5491 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
5493 case IPP_JSTATE_ABORTED
:
5494 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5496 case IPP_JSTATE_CANCELED
:
5497 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5499 case IPP_JSTATE_COMPLETED
:
5500 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5504 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
);
5506 html_printf(client
, "</tbody></table>\n");
5508 _cupsRWUnlock(&(client
->printer
->rwlock
));
5510 html_footer(client
);
5514 else if (!strcmp(client
->uri
, "/media"))
5517 * Show web media page...
5520 int i
, /* Looping var */
5521 num_options
; /* Number of form options */
5522 cups_option_t
*options
; /* Form options */
5523 static const char * const sizes
[] =
5524 { /* Size strings */
5537 static const char * const types
[] =
5554 static const int sheets
[] = /* Number of sheets */
5563 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5566 html_header(client
, client
->printer
->name
);
5568 if ((num_options
= parse_options(client
, &options
)) > 0)
5571 * WARNING: A real printer/server implementation MUST NOT implement
5572 * media updates via a GET request - GET requests are supposed to be
5573 * idempotent (without side-effects) and we obviously are not
5574 * authenticating access here. This form is provided solely to
5575 * enable testing and development!
5578 const char *val
; /* Form value */
5580 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
5581 client
->printer
->main_size
= atoi(val
);
5582 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
5583 client
->printer
->main_type
= atoi(val
);
5584 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
5585 client
->printer
->main_level
= atoi(val
);
5587 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
5588 client
->printer
->envelope_size
= atoi(val
);
5589 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
5590 client
->printer
->envelope_level
= atoi(val
);
5592 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
5593 client
->printer
->photo_size
= atoi(val
);
5594 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
5595 client
->printer
->photo_type
= atoi(val
);
5596 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
5597 client
->printer
->photo_level
= atoi(val
);
5599 if ((client
->printer
->main_level
< 100 && client
->printer
->main_level
> 0) || (client
->printer
->envelope_level
< 25 && client
->printer
->envelope_level
> 0) || (client
->printer
->photo_level
< 25 && client
->printer
->photo_level
> 0))
5600 client
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_LOW
;
5602 client
->printer
->state_reasons
&= (ippeve_preason_t
)~IPPEVE_PREASON_MEDIA_LOW
;
5604 if ((client
->printer
->main_level
== 0 && client
->printer
->main_size
> IPPEVE_MEDIA_SIZE_NONE
) || (client
->printer
->envelope_level
== 0 && client
->printer
->envelope_size
> IPPEVE_MEDIA_SIZE_NONE
) || (client
->printer
->photo_level
== 0 && client
->printer
->photo_size
> IPPEVE_MEDIA_SIZE_NONE
))
5606 client
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_EMPTY
;
5607 if (client
->printer
->active_job
)
5608 client
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
5611 client
->printer
->state_reasons
&= (ippeve_preason_t
)~(IPPEVE_PREASON_MEDIA_EMPTY
| IPPEVE_PREASON_MEDIA_NEEDED
);
5613 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
5616 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
5618 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
5619 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
5620 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5621 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
5622 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
5623 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
5624 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5625 if (!strstr(types
[i
], "Photo"))
5626 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
5627 html_printf(client
, "</select> <select name=\"main_level\">");
5628 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5629 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
5630 html_printf(client
, "</select></td></tr>\n");
5633 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
5634 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5635 if (strstr(sizes
[i
], "Envelope"))
5636 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
5637 html_printf(client
, "</select> <select name=\"envelope_level\">");
5638 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5639 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
5640 html_printf(client
, "</select></td></tr>\n");
5643 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
5644 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5645 if (strstr(sizes
[i
], "Photo"))
5646 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
5647 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
5648 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5649 if (strstr(types
[i
], "Photo"))
5650 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
5651 html_printf(client
, "</select> <select name=\"photo_level\">");
5652 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5653 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
5654 html_printf(client
, "</select></td></tr>\n");
5656 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5657 html_footer(client
);
5661 else if (!strcmp(client
->uri
, "/supplies"))
5664 * Show web supplies page...
5667 int i
, j
, /* Looping vars */
5668 num_options
; /* Number of form options */
5669 cups_option_t
*options
; /* Form options */
5670 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5672 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5675 html_header(client
, client
->printer
->name
);
5677 if ((num_options
= parse_options(client
, &options
)) > 0)
5680 * WARNING: A real printer/server implementation MUST NOT implement
5681 * supply updates via a GET request - GET requests are supposed to be
5682 * idempotent (without side-effects) and we obviously are not
5683 * authenticating access here. This form is provided solely to
5684 * enable testing and development!
5687 char name
[64]; /* Form field */
5688 const char *val
; /* Form value */
5690 client
->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
);
5692 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5694 snprintf(name
, sizeof(name
), "supply_%d", i
);
5695 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5697 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5703 client
->printer
->state_reasons
|= IPPEVE_PREASON_TONER_EMPTY
;
5704 else if (level
< 10)
5705 client
->printer
->state_reasons
|= IPPEVE_PREASON_TONER_LOW
;
5710 client
->printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_FULL
;
5711 else if (level
> 90)
5712 client
->printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
;
5717 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5720 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5722 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5723 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5725 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5726 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5727 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5728 html_printf(client
, "</select></td></tr>\n");
5730 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5731 html_footer(client
);
5736 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5739 case HTTP_STATE_POST
:
5740 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5744 * Not an IPP request...
5747 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5751 * Read the IPP request...
5754 client
->request
= ippNew();
5756 while ((ipp_state
= ippRead(client
->http
,
5757 client
->request
)) != IPP_STATE_DATA
)
5759 if (ipp_state
== IPP_STATE_ERROR
)
5761 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5762 cupsLastErrorString());
5763 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5769 * Now that we have the IPP request, process the request...
5772 return (process_ipp(client
));
5775 break; /* Anti-compiler-warning-code */
5783 * 'process_ipp()' - Process an IPP request.
5786 static int /* O - 1 on success, 0 on error */
5787 process_ipp(ippeve_client_t
*client
) /* I - Client */
5789 ipp_tag_t group
; /* Current group tag */
5790 ipp_attribute_t
*attr
; /* Current attribute */
5791 ipp_attribute_t
*charset
; /* Character set attribute */
5792 ipp_attribute_t
*language
; /* Language attribute */
5793 ipp_attribute_t
*uri
; /* Printer URI attribute */
5794 int major
, minor
; /* Version number */
5795 const char *name
; /* Name of attribute */
5798 debug_attributes("Request", client
->request
, 1);
5801 * First build an empty response message for this request...
5804 client
->operation_id
= ippGetOperation(client
->request
);
5805 client
->response
= ippNewResponse(client
->request
);
5808 * Then validate the request header and required attributes...
5811 major
= ippGetVersion(client
->request
, &minor
);
5813 if (major
< 1 || major
> 2)
5816 * Return an error, since we only support IPP 1.x and 2.x.
5819 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
, "Bad request version number %d.%d.", major
, minor
);
5821 else if ((major
* 10 + minor
) > MaxVersion
)
5823 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5824 httpFlush(client
->http
); /* Flush trailing (junk) data */
5826 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5829 else if (ippGetRequestId(client
->request
) <= 0)
5831 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.", ippGetRequestId(client
->request
));
5833 else if (!ippFirstAttribute(client
->request
))
5835 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No attributes in request.");
5840 * Make sure that the attributes are provided in the correct order and
5841 * don't repeat groups...
5844 for (attr
= ippFirstAttribute(client
->request
),
5845 group
= ippGetGroupTag(attr
);
5847 attr
= ippNextAttribute(client
->request
))
5849 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5852 * Out of order; return an error...
5855 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5856 "Attribute groups are out of order (%x < %x).",
5857 ippGetGroupTag(attr
), group
);
5861 group
= ippGetGroupTag(attr
);
5867 * Then make sure that the first three attributes are:
5869 * attributes-charset
5870 * attributes-natural-language
5871 * printer-uri/job-uri
5874 attr
= ippFirstAttribute(client
->request
);
5875 name
= ippGetName(attr
);
5876 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5877 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5882 attr
= ippNextAttribute(client
->request
);
5883 name
= ippGetName(attr
);
5885 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5886 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5891 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5892 IPP_TAG_URI
)) != NULL
)
5894 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5895 IPP_TAG_URI
)) != NULL
)
5901 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5902 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5905 * Bad character set...
5908 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5909 "Unsupported character set \"%s\".",
5910 ippGetString(charset
, 0, NULL
));
5912 else if (!charset
|| !language
|| !uri
)
5915 * Return an error, since attributes-charset,
5916 * attributes-natural-language, and printer-uri/job-uri are required
5917 * for all operations.
5920 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5921 "Missing required attributes.");
5925 char scheme
[32], /* URI scheme */
5926 userpass
[32], /* Username/password in URI */
5927 host
[256], /* Host name in URI */
5928 resource
[256]; /* Resource path in URI */
5929 int port
; /* Port number in URI */
5931 name
= ippGetName(uri
);
5933 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5934 scheme
, sizeof(scheme
),
5935 userpass
, sizeof(userpass
),
5936 host
, sizeof(host
), &port
,
5937 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5938 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5939 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5940 else if ((!strcmp(name
, "job-uri") &&
5941 strncmp(resource
, "/ipp/print/", 11)) ||
5942 (!strcmp(name
, "printer-uri") &&
5943 strcmp(resource
, "/ipp/print")))
5944 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5945 name
, ippGetString(uri
, 0, NULL
));
5949 * Try processing the operation...
5952 switch (ippGetOperation(client
->request
))
5954 case IPP_OP_PRINT_JOB
:
5955 ipp_print_job(client
);
5958 case IPP_OP_PRINT_URI
:
5959 ipp_print_uri(client
);
5962 case IPP_OP_VALIDATE_JOB
:
5963 ipp_validate_job(client
);
5966 case IPP_OP_CREATE_JOB
:
5967 ipp_create_job(client
);
5970 case IPP_OP_SEND_DOCUMENT
:
5971 ipp_send_document(client
);
5974 case IPP_OP_SEND_URI
:
5975 ipp_send_uri(client
);
5978 case IPP_OP_CANCEL_JOB
:
5979 ipp_cancel_job(client
);
5982 case IPP_OP_GET_JOB_ATTRIBUTES
:
5983 ipp_get_job_attributes(client
);
5986 case IPP_OP_GET_JOBS
:
5987 ipp_get_jobs(client
);
5990 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5991 ipp_get_printer_attributes(client
);
5994 case IPP_OP_CLOSE_JOB
:
5995 ipp_close_job(client
);
5998 case IPP_OP_IDENTIFY_PRINTER
:
5999 ipp_identify_printer(client
);
6003 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
6004 "Operation not supported.");
6013 * Send the HTTP header and return...
6016 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
6017 httpFlush(client
->http
); /* Flush trailing (junk) data */
6019 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
6020 ippLength(client
->response
)));
6025 * 'process_job()' - Process a print job.
6028 static void * /* O - Thread exit status */
6029 process_job(ippeve_job_t
*job
) /* I - Job */
6031 job
->state
= IPP_JSTATE_PROCESSING
;
6032 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
6033 job
->processing
= time(NULL
);
6035 while (job
->printer
->state_reasons
& IPPEVE_PREASON_MEDIA_EMPTY
)
6037 job
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
6042 job
->printer
->state_reasons
&= (ippeve_preason_t
)~IPPEVE_PREASON_MEDIA_NEEDED
;
6044 if (job
->printer
->command
)
6047 * Execute a command with the job spool file and wait for it to complete...
6050 int pid
, /* Process ID */
6051 status
; /* Exit status */
6052 time_t start
, /* Start time */
6054 char *myargv
[3], /* Command-line arguments */
6055 *myenvp
[200]; /* Environment variables */
6056 int myenvc
; /* Number of environment variables */
6057 ipp_attribute_t
*attr
; /* Job attribute */
6058 char val
[1280], /* IPP_NAME=value */
6059 *valptr
; /* Pointer into string */
6061 int mypipe
[2]; /* Pipe for stderr */
6062 char line
[2048], /* Line from stderr */
6063 *ptr
, /* Pointer into line */
6064 *endptr
; /* End of line */
6065 ssize_t bytes
; /* Bytes read */
6066 #endif /* !_WIN32 */
6068 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
6073 * Setup the command-line arguments...
6076 myargv
[0] = job
->printer
->command
;
6077 myargv
[1] = job
->filename
;
6081 * Copy the current environment, then add ENV variables for every Job
6085 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
6086 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
6088 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
6091 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6092 * value(s) from the attribute.
6095 const char *name
= ippGetName(attr
);
6104 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6109 *valptr
++ = (char)toupper(*name
& 255);
6114 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6116 myenvp
[myenvc
++] = strdup(val
);
6118 myenvp
[myenvc
] = NULL
;
6121 * Now run the program...
6125 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
6130 perror("Unable to create pipe for stderr");
6131 mypipe
[0] = mypipe
[1] = -1;
6134 if ((pid
= fork()) == 0)
6137 * Child comes here...
6145 execve(job
->printer
->command
, myargv
, myenvp
);
6151 * Unable to fork process...
6154 perror("Unable to start job processing command");
6161 * Free memory used for environment...
6165 free(myenvp
[-- myenvc
]);
6170 * Free memory used for environment...
6174 free(myenvp
[-- myenvc
]);
6177 * If the pipe exists, read from it until EOF...
6185 while ((bytes
= read(mypipe
[0], endptr
, sizeof(line
) - (size_t)(endptr
- line
) - 1)) > 0)
6190 while ((ptr
= strchr(line
, '\n')) != NULL
)
6194 if (!strncmp(line
, "STATE:", 6))
6197 * Process printer-state-reasons keywords.
6200 process_state_message(job
, line
);
6202 else if (!strncmp(line
, "ATTR:", 5))
6205 * Process printer attribute update.
6208 process_attr_message(job
, line
);
6210 else if (Verbosity
> 1)
6211 fprintf(stderr
, "%s: %s\n", job
->printer
->command
, line
);
6215 memmove(line
, ptr
, (size_t)(endptr
- ptr
));
6225 * Wait for child to complete...
6228 # ifdef HAVE_WAITPID
6229 while (waitpid(pid
, &status
, 0) < 0);
6231 while (wait(&status
) < 0);
6232 # endif /* HAVE_WAITPID */
6239 if (WIFEXITED(status
))
6240 #endif /* !_WIN32 */
6241 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
6242 job
->printer
->command
, WEXITSTATUS(status
));
6245 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
6246 job
->printer
->command
, WTERMSIG(status
));
6247 #endif /* !_WIN32 */
6248 job
->state
= IPP_JSTATE_ABORTED
;
6250 else if (status
< 0)
6251 job
->state
= IPP_JSTATE_ABORTED
;
6253 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
6254 job
->printer
->command
);
6257 * Make sure processing takes at least 5 seconds...
6261 if ((end
- start
) < 5)
6267 * Sleep for a random amount of time to simulate job processing.
6270 sleep((unsigned)(5 + (rand() % 11)));
6274 job
->state
= IPP_JSTATE_CANCELED
;
6275 else if (job
->state
== IPP_JSTATE_PROCESSING
)
6276 job
->state
= IPP_JSTATE_COMPLETED
;
6278 job
->completed
= time(NULL
);
6279 job
->printer
->state
= IPP_PSTATE_IDLE
;
6280 job
->printer
->active_job
= NULL
;
6287 * 'process_state_message()' - Process a STATE: message from a command.
6291 process_state_message(
6292 ippeve_job_t
*job
, /* I - Job */
6293 char *message
) /* I - Message */
6295 int i
; /* Looping var */
6296 ippeve_preason_t state_reasons
, /* printer-state-reasons values */
6297 bit
; /* Current reason bit */
6298 char *ptr
, /* Pointer into message */
6299 *next
; /* Next keyword in message */
6300 int remove
; /* Non-zero if we are removing keywords */
6304 * Skip leading "STATE:" and any whitespace...
6307 for (message
+= 6; *message
; message
++)
6308 if (*message
!= ' ' && *message
!= '\t')
6312 * Support the following forms of message:
6314 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6316 * "-keyword[,keyword,...]" to remove keywords.
6318 * "+keyword[,keyword,...]" to add keywords.
6320 * Keywords may or may not have a suffix (-report, -warning, -error) per
6324 if (*message
== '-')
6327 state_reasons
= job
->printer
->state_reasons
;
6330 else if (*message
== '+')
6333 state_reasons
= job
->printer
->state_reasons
;
6339 state_reasons
= IPPEVE_PREASON_NONE
;
6344 if ((next
= strchr(message
, ',')) != NULL
)
6347 if ((ptr
= strstr(message
, "-error")) != NULL
)
6349 else if ((ptr
= strstr(message
, "-report")) != NULL
)
6351 else if ((ptr
= strstr(message
, "-warning")) != NULL
)
6354 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
6356 if (!strcmp(message
, ippeve_preason_strings
[i
]))
6359 state_reasons
&= ~bit
;
6361 state_reasons
|= bit
;
6371 job
->printer
->state_reasons
= state_reasons
;
6376 * 'register_printer()' - Register a printer object via Bonjour.
6379 static int /* O - 1 on success, 0 on error */
6381 ippeve_printer_t
*printer
, /* I - Printer */
6382 const char *location
, /* I - Location */
6383 const char *make
, /* I - Manufacturer */
6384 const char *model
, /* I - Model name */
6385 const char *formats
, /* I - Supported formats */
6386 const char *adminurl
, /* I - Web interface URL */
6387 const char *uuid
, /* I - Printer UUID */
6388 int color
, /* I - 1 = color, 0 = monochrome */
6389 int duplex
, /* I - 1 = duplex, 0 = simplex */
6390 const char *subtype
) /* I - Service subtype */
6392 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6393 ippeve_txt_t ipp_txt
; /* Bonjour IPP TXT record */
6394 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6396 DNSServiceErrorType error
; /* Error from Bonjour */
6397 char make_model
[256],/* Make and model together */
6398 product
[256], /* Product string */
6399 regtype
[256]; /* Bonjour service type */
6403 * Build the TXT record for IPP...
6406 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
6407 snprintf(product
, sizeof(product
), "(%s)", model
);
6409 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
6410 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
6411 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(make_model
),
6413 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(adminurl
),
6416 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(location
),
6418 TXTRecordSetValue(&ipp_txt
, "product", (uint8_t)strlen(product
),
6420 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
),
6422 TXTRecordSetValue(&ipp_txt
, "Color", 1, color
? "T" : "F");
6423 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, duplex
? "T" : "F");
6424 TXTRecordSetValue(&ipp_txt
, "usb_MFG", (uint8_t)strlen(make
),
6426 TXTRecordSetValue(&ipp_txt
, "usb_MDL", (uint8_t)strlen(model
),
6428 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(uuid
), uuid
);
6430 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
6431 # endif /* HAVE_SSL */
6432 if (strstr(formats
, "image/urf"))
6433 TXTRecordSetValue(&ipp_txt
, "URF", 66, "CP1,IS1-5-7,MT1-2-3-4-5-6-8-9-10-11-12-13,RS300,SRGB24,V1.4,W8,DM1");
6435 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
6436 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
6439 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6440 * defend our service name but not actually support LPD...
6443 printer
->printer_ref
= DNSSDMaster
;
6445 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
6446 kDNSServiceFlagsShareConnection
,
6447 0 /* interfaceIndex */, printer
->dnssd_name
,
6448 "_printer._tcp", NULL
/* domain */,
6449 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
6450 NULL
/* txtRecord */,
6451 (DNSServiceRegisterReply
)dnssd_callback
,
6452 printer
)) != kDNSServiceErr_NoError
)
6454 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
6455 printer
->dnssd_name
, error
);
6460 * Then register the ippeve._tcp (IPP) service type with the real port number to
6461 * advertise our IPP printer...
6464 printer
->ipp_ref
= DNSSDMaster
;
6466 if (subtype
&& *subtype
)
6467 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
6469 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
6471 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
6472 kDNSServiceFlagsShareConnection
,
6473 0 /* interfaceIndex */, printer
->dnssd_name
,
6474 regtype
, NULL
/* domain */,
6475 NULL
/* host */, htons(printer
->port
),
6476 TXTRecordGetLength(&ipp_txt
),
6477 TXTRecordGetBytesPtr(&ipp_txt
),
6478 (DNSServiceRegisterReply
)dnssd_callback
,
6479 printer
)) != kDNSServiceErr_NoError
)
6481 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6482 printer
->dnssd_name
, regtype
, error
);
6488 * Then register the ippeves._tcp (IPP) service type with the real port number to
6489 * advertise our IPPS printer...
6492 printer
->ipps_ref
= DNSSDMaster
;
6494 if (subtype
&& *subtype
)
6495 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
6497 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
6499 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
6500 kDNSServiceFlagsShareConnection
,
6501 0 /* interfaceIndex */, printer
->dnssd_name
,
6502 regtype
, NULL
/* domain */,
6503 NULL
/* host */, htons(printer
->port
),
6504 TXTRecordGetLength(&ipp_txt
),
6505 TXTRecordGetBytesPtr(&ipp_txt
),
6506 (DNSServiceRegisterReply
)dnssd_callback
,
6507 printer
)) != kDNSServiceErr_NoError
)
6509 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6510 printer
->dnssd_name
, regtype
, error
);
6513 # endif /* HAVE_SSL */
6516 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6517 * real port number to advertise our IPP printer...
6520 printer
->http_ref
= DNSSDMaster
;
6522 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
6523 kDNSServiceFlagsShareConnection
,
6524 0 /* interfaceIndex */, printer
->dnssd_name
,
6525 "_http._tcp,_printer", NULL
/* domain */,
6526 NULL
/* host */, htons(printer
->port
),
6527 0 /* txtLen */, NULL
, /* txtRecord */
6528 (DNSServiceRegisterReply
)dnssd_callback
,
6529 printer
)) != kDNSServiceErr_NoError
)
6531 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6532 printer
->dnssd_name
, regtype
, error
);
6536 TXTRecordDeallocate(&ipp_txt
);
6538 #elif defined(HAVE_AVAHI)
6539 char temp
[256]; /* Subtype service string */
6542 * Create the TXT record...
6546 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
6547 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s %s", make
, model
);
6548 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", adminurl
);
6550 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", location
);
6551 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "product=(%s)", model
);
6552 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
6553 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", color
? "T" : "F");
6554 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", duplex
? "T" : "F");
6555 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MFG=%s", make
);
6556 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MDL=%s", model
);
6557 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", uuid
);
6559 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
6560 # endif /* HAVE_SSL */
6563 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6566 avahi_threaded_poll_lock(DNSSDMaster
);
6568 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
6570 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
);
6573 * Then register the ippeve._tcp (IPP)...
6576 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
);
6577 if (subtype
&& *subtype
)
6579 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
6580 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
6585 * ippeves._tcp (IPPS) for secure printing...
6588 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
);
6589 if (subtype
&& *subtype
)
6591 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
6592 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
6594 #endif /* HAVE_SSL */
6597 * Finally _http.tcp (HTTP) for the web interface...
6600 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
);
6601 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");
6607 avahi_entry_group_commit(printer
->ipp_ref
);
6608 avahi_threaded_poll_unlock(DNSSDMaster
);
6610 avahi_string_list_free(ipp_txt
);
6611 #endif /* HAVE_DNSSD */
6618 * 'respond_http()' - Send a HTTP response.
6621 int /* O - 1 on success, 0 on failure */
6623 ippeve_client_t
*client
, /* I - Client */
6624 http_status_t code
, /* I - HTTP status of response */
6625 const char *content_encoding
, /* I - Content-Encoding of response */
6626 const char *type
, /* I - MIME media type of response */
6627 size_t length
) /* I - Length of response */
6629 char message
[1024]; /* Text message */
6632 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
6634 if (code
== HTTP_STATUS_CONTINUE
)
6637 * 100-continue doesn't send any headers...
6640 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
6644 * Format an error message...
6647 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
6649 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
6651 type
= "text/plain";
6652 length
= strlen(message
);
6658 * Send the HTTP response header...
6661 httpClearFields(client
->http
);
6663 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
6664 client
->operation
== HTTP_STATE_OPTIONS
)
6665 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
6669 if (!strcmp(type
, "text/html"))
6670 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
6671 "text/html; charset=utf-8");
6673 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
6675 if (content_encoding
)
6676 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
6679 httpSetLength(client
->http
, length
);
6681 if (httpWriteResponse(client
->http
, code
) < 0)
6685 * Send the response data...
6691 * Send a plain text message.
6694 if (httpPrintf(client
->http
, "%s", message
) < 0)
6697 if (httpWrite2(client
->http
, "", 0) < 0)
6700 else if (client
->response
)
6703 * Send an IPP response...
6706 debug_attributes("Response", client
->response
, 2);
6708 ippSetState(client
->response
, IPP_STATE_IDLE
);
6710 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6719 * 'respond_ipp()' - Send an IPP response.
6723 respond_ipp(ippeve_client_t
*client
, /* I - Client */
6724 ipp_status_t status
, /* I - status-code */
6725 const char *message
, /* I - printf-style status-message */
6726 ...) /* I - Additional args as needed */
6728 const char *formatted
= NULL
; /* Formatted message */
6731 ippSetStatusCode(client
->response
, status
);
6735 va_list ap
; /* Pointer to additional args */
6736 ipp_attribute_t
*attr
; /* New status-message attribute */
6738 va_start(ap
, message
);
6739 if ((attr
= ippFindAttribute(client
->response
, "status-message",
6740 IPP_TAG_TEXT
)) != NULL
)
6741 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6743 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
6744 "status-message", NULL
, message
, ap
);
6747 formatted
= ippGetString(attr
, 0, NULL
);
6751 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
6752 ippOpString(client
->operation_id
), ippErrorString(status
),
6755 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
6756 ippOpString(client
->operation_id
), ippErrorString(status
));
6761 * 'respond_unsupported()' - Respond with an unsupported attribute.
6765 respond_unsupported(
6766 ippeve_client_t
*client
, /* I - Client */
6767 ipp_attribute_t
*attr
) /* I - Atribute */
6769 ipp_attribute_t
*temp
; /* Copy of attribute */
6772 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6773 "Unsupported %s %s%s value.", ippGetName(attr
),
6774 ippGetCount(attr
) > 1 ? "1setOf " : "",
6775 ippTagString(ippGetValueTag(attr
)));
6777 temp
= ippCopyAttribute(client
->response
, attr
, 0);
6778 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
6783 * 'run_printer()' - Run the printer service.
6787 run_printer(ippeve_printer_t
*printer
) /* I - Printer */
6789 int num_fds
; /* Number of file descriptors */
6790 struct pollfd polldata
[3]; /* poll() data */
6791 int timeout
; /* Timeout for poll() */
6792 ippeve_client_t
*client
; /* New client */
6796 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6799 polldata
[0].fd
= printer
->ipv4
;
6800 polldata
[0].events
= POLLIN
;
6802 polldata
[1].fd
= printer
->ipv6
;
6803 polldata
[1].events
= POLLIN
;
6808 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
6809 polldata
[num_fds
++].events
= POLLIN
;
6810 #endif /* HAVE_DNSSD */
6813 * Loop until we are killed or have a hard error...
6818 if (cupsArrayCount(printer
->jobs
))
6823 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
6825 perror("poll() failed");
6829 if (polldata
[0].revents
& POLLIN
)
6831 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6833 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6837 _cupsThreadDetach(t
);
6841 perror("Unable to create client thread");
6842 delete_client(client
);
6847 if (polldata
[1].revents
& POLLIN
)
6849 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6851 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6855 _cupsThreadDetach(t
);
6859 perror("Unable to create client thread");
6860 delete_client(client
);
6866 if (polldata
[2].revents
& POLLIN
)
6867 DNSServiceProcessResult(DNSSDMaster
);
6868 #endif /* HAVE_DNSSD */
6871 * Clean out old jobs...
6874 clean_jobs(printer
);
6880 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6884 time_string(time_t tv
, /* I - Time value */
6885 char *buffer
, /* I - Buffer */
6886 size_t bufsize
) /* I - Size of buffer */
6888 struct tm
*curtime
= localtime(&tv
);
6891 strftime(buffer
, bufsize
, "%X", curtime
);
6897 * 'usage()' - Show program usage.
6901 usage(int status
) /* O - Exit status */
6905 puts(CUPS_SVERSION
" - Copyright (c) 2010-2018 by Apple Inc. All rights reserved.");
6909 puts("Usage: ippserver [options] \"name\"");
6912 puts("-2 Supports 2-sided printing (default=1-sided)");
6913 puts("-M manufacturer Manufacturer name (default=Test)");
6914 puts("-P PIN printing mode");
6915 puts("-V max-version Set maximum supported IPP version");
6916 puts("-a attributes-file Load printer attributes from file");
6917 puts("-c command Run command for every print job");
6918 printf("-d spool-directory Spool directory "
6919 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6920 puts("-f type/subtype[,...] List of supported types "
6921 "(default=application/pdf,image/jpeg)");
6922 puts("-h Show program help");
6923 puts("-i iconfile.png PNG icon file (default=printer.png)");
6924 puts("-k Keep job spool files");
6925 puts("-l location Location of printer (default=empty string)");
6926 puts("-m model Model name (default=Printer)");
6927 puts("-n hostname Hostname for printer");
6928 puts("-p port Port number (default=auto)");
6929 puts("-r subtype Bonjour service subtype (default=_print)");
6930 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6931 puts("-v[vvv] Be (very) verbose");
6938 * 'valid_doc_attributes()' - Determine whether the document attributes are
6941 * When one or more document attributes are invalid, this function adds a
6942 * suitable response and attributes to the unsupported group.
6945 static int /* O - 1 if valid, 0 if not */
6946 valid_doc_attributes(
6947 ippeve_client_t
*client
) /* I - Client */
6949 int valid
= 1; /* Valid attributes? */
6950 ipp_op_t op
= ippGetOperation(client
->request
);
6952 const char *op_name
= ippOpString(op
);
6953 /* IPP operation name */
6954 ipp_attribute_t
*attr
, /* Current attribute */
6955 *supported
; /* xxx-supported attribute */
6956 const char *compression
= NULL
,
6957 /* compression value */
6958 *format
= NULL
; /* document-format value */
6962 * Check operation attributes...
6965 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6968 * If compression is specified, only accept a supported value in a Print-Job
6969 * or Send-Document request...
6972 compression
= ippGetString(attr
, 0, NULL
);
6973 supported
= ippFindAttribute(client
->printer
->attrs
,
6974 "compression-supported", IPP_TAG_KEYWORD
);
6976 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6977 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6978 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6979 op
!= IPP_OP_VALIDATE_JOB
) ||
6980 !ippContainsString(supported
, compression
))
6982 respond_unsupported(client
, attr
);
6987 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6989 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6991 if (strcmp(compression
, "none"))
6994 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6995 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
7001 * Is it a format we support?
7004 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
7006 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
7007 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
7009 respond_unsupported(client
, attr
);
7014 format
= ippGetString(attr
, 0, NULL
);
7016 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
7017 client
->hostname
, op_name
, format
);
7019 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
7024 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
7026 format
= "application/octet-stream"; /* Should never happen */
7028 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
7031 if (format
&& !strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
7034 * Auto-type the file using the first 8 bytes of the file...
7037 unsigned char header
[8]; /* First 8 bytes of file */
7039 memset(header
, 0, sizeof(header
));
7040 httpPeek(client
->http
, (char *)header
, sizeof(header
));
7042 if (!memcmp(header
, "%PDF", 4))
7043 format
= "application/pdf";
7044 else if (!memcmp(header
, "%!", 2))
7045 format
= "application/postscript";
7046 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
7047 format
= "image/jpeg";
7048 else if (!memcmp(header
, "\211PNG", 4))
7049 format
= "image/png";
7050 else if (!memcmp(header
, "RAS2", 4))
7051 format
= "image/pwg-raster";
7052 else if (!memcmp(header
, "UNIRAST", 8))
7053 format
= "image/urf";
7059 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
7060 client
->hostname
, op_name
, format
);
7062 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
7066 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
7068 respond_unsupported(client
, attr
);
7076 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
7077 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
7084 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
7086 * When one or more job attributes are invalid, this function adds a suitable
7087 * response and attributes to the unsupported group.
7090 static int /* O - 1 if valid, 0 if not */
7091 valid_job_attributes(
7092 ippeve_client_t
*client
) /* I - Client */
7094 int i
, /* Looping var */
7095 count
, /* Number of values */
7096 valid
= 1; /* Valid attributes? */
7097 ipp_attribute_t
*attr
, /* Current attribute */
7098 *supported
; /* xxx-supported attribute */
7102 * Check operation attributes...
7105 valid
= valid_doc_attributes(client
);
7108 * Check the various job template attributes...
7111 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
7113 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7114 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
7116 respond_unsupported(client
, attr
);
7121 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
7123 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
7125 respond_unsupported(client
, attr
);
7130 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
7132 if (ippGetCount(attr
) != 1 ||
7133 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7134 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7135 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7136 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
7138 respond_unsupported(client
, attr
);
7143 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
7145 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
7147 respond_unsupported(client
, attr
);
7152 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
7154 if (ippGetCount(attr
) != 1 ||
7155 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7156 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
7158 respond_unsupported(client
, attr
);
7162 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
7165 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
7167 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
7169 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7170 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
7172 respond_unsupported(client
, attr
);
7177 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
7179 if (ippGetCount(attr
) != 1 ||
7180 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7181 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7182 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7183 strcmp(ippGetString(attr
, 0, NULL
), "none"))
7185 respond_unsupported(client
, attr
);
7190 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
7192 if (ippGetCount(attr
) != 1 ||
7193 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7194 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7195 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
7197 respond_unsupported(client
, attr
);
7202 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7204 if (!ippContainsString(supported
, ippGetString(attr
, 0, NULL
)))
7206 respond_unsupported(client
, attr
);
7212 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
7214 ipp_t
*col
, /* media-col collection */
7215 *size
; /* media-size collection */
7216 ipp_attribute_t
*member
, /* Member attribute */
7217 *x_dim
, /* x-dimension */
7218 *y_dim
; /* y-dimension */
7219 int x_value
, /* y-dimension value */
7220 y_value
; /* x-dimension value */
7222 if (ippGetCount(attr
) != 1 ||
7223 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
7225 respond_unsupported(client
, attr
);
7229 col
= ippGetCollection(attr
, 0);
7231 if ((member
= ippFindAttribute(col
, "media-size-name", IPP_TAG_ZERO
)) != NULL
)
7233 if (ippGetCount(member
) != 1 ||
7234 (ippGetValueTag(member
) != IPP_TAG_NAME
&&
7235 ippGetValueTag(member
) != IPP_TAG_NAMELANG
&&
7236 ippGetValueTag(member
) != IPP_TAG_KEYWORD
))
7238 respond_unsupported(client
, attr
);
7243 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7245 if (!ippContainsString(supported
, ippGetString(member
, 0, NULL
)))
7247 respond_unsupported(client
, attr
);
7252 else if ((member
= ippFindAttribute(col
, "media-size", IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
7254 if (ippGetCount(member
) != 1)
7256 respond_unsupported(client
, attr
);
7261 size
= ippGetCollection(member
, 0);
7263 if ((x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(x_dim
) != 1 ||
7264 (y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(y_dim
) != 1)
7266 respond_unsupported(client
, attr
);
7271 x_value
= ippGetInteger(x_dim
, 0);
7272 y_value
= ippGetInteger(y_dim
, 0);
7273 supported
= ippFindAttribute(client
->printer
->attrs
, "media-size-supported", IPP_TAG_BEGIN_COLLECTION
);
7274 count
= ippGetCount(supported
);
7276 for (i
= 0; i
< count
; i
++)
7278 size
= ippGetCollection(supported
, i
);
7279 x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_ZERO
);
7280 y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_ZERO
);
7282 if (ippContainsInteger(x_dim
, x_value
) && ippContainsInteger(y_dim
, y_value
))
7288 respond_unsupported(client
, attr
);
7296 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
7298 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7299 (strcmp(ippGetString(attr
, 0, NULL
),
7300 "separate-documents-uncollated-copies") &&
7301 strcmp(ippGetString(attr
, 0, NULL
),
7302 "separate-documents-collated-copies")))
7304 respond_unsupported(client
, attr
);
7309 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
7311 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7312 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
7313 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
7315 respond_unsupported(client
, attr
);
7320 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
7322 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
7324 respond_unsupported(client
, attr
);
7329 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
7331 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7332 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
7333 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
7335 respond_unsupported(client
, attr
);
7340 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
7342 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
7344 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
7347 respond_unsupported(client
, attr
);
7352 int xdpi
, /* Horizontal resolution for job template attribute */
7353 ydpi
, /* Vertical resolution for job template attribute */
7354 sydpi
; /* Vertical resolution for supported value */
7355 ipp_res_t units
, /* Units for job template attribute */
7356 sunits
; /* Units for supported value */
7358 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
7359 count
= ippGetCount(supported
);
7361 for (i
= 0; i
< count
; i
++)
7363 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
7369 respond_unsupported(client
, attr
);
7375 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
7377 const char *sides
= ippGetString(attr
, 0, NULL
);
7378 /* "sides" value... */
7380 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
7382 respond_unsupported(client
, attr
);
7385 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
7387 if (!ippContainsString(supported
, sides
))
7389 respond_unsupported(client
, attr
);
7393 else if (strcmp(sides
, "one-sided"))
7395 respond_unsupported(client
, attr
);