2 * Sample IPP Everywhere server for CUPS.
4 * Copyright © 2010-2018 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
11 * Disable private and deprecated stuff so we can verify that the public API
12 * is sufficient to implement a server.
15 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
16 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
20 * Include necessary headers...
23 #include <config.h> /* CUPS configuration header */
24 #include <cups/cups.h> /* Public API */
25 #include <cups/string-private.h> /* CUPS string functions */
26 #include <cups/thread-private.h> /* For multithreading functions */
39 # define WEXITSTATUS(s) (s)
40 # include <winsock2.h>
44 extern char **environ
;
46 # include <sys/fcntl.h>
47 # include <sys/wait.h>
53 #elif defined(HAVE_AVAHI)
54 # include <avahi-client/client.h>
55 # include <avahi-client/publish.h>
56 # include <avahi-common/error.h>
57 # include <avahi-common/thread-watch.h>
58 #endif /* HAVE_DNSSD */
59 #ifdef HAVE_SYS_MOUNT_H
60 # include <sys/mount.h>
61 #endif /* HAVE_SYS_MOUNT_H */
62 #ifdef HAVE_SYS_STATFS_H
63 # include <sys/statfs.h>
64 #endif /* HAVE_SYS_STATFS_H */
65 #ifdef HAVE_SYS_STATVFS_H
66 # include <sys/statvfs.h>
67 #endif /* HAVE_SYS_STATVFS_H */
70 #endif /* HAVE_SYS_VFS_H */
77 enum _ipp_preason_e
/* printer-state-reasons bit values */
79 _IPP_PREASON_NONE
= 0x0000, /* none */
80 _IPP_PREASON_OTHER
= 0x0001, /* other */
81 _IPP_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
82 _IPP_PREASON_INPUT_TRAY_MISSING
= 0x0004,
83 /* input-tray-missing */
84 _IPP_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
85 /* marker-supply-empty */
86 _IPP_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
87 /* marker-supply-low */
88 _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
89 /* marker-waste-almost-full */
90 _IPP_PREASON_MARKER_WASTE_FULL
= 0x0040,
91 /* marker-waste-full */
92 _IPP_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
93 _IPP_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
94 _IPP_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
95 _IPP_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
96 _IPP_PREASON_MOVING_TO_PAUSED
= 0x0800,
97 /* moving-to-paused */
98 _IPP_PREASON_PAUSED
= 0x1000, /* paused */
99 _IPP_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
100 _IPP_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
101 _IPP_PREASON_TONER_LOW
= 0x8000 /* toner-low */
103 typedef unsigned int _ipp_preason_t
; /* Bitfield for printer-state-reasons */
104 static const char * const _ipp_preason_strings
[] =
105 { /* Strings for each bit */
106 /* "none" is implied for no bits set */
109 "input-tray-missing",
110 "marker-supply-empty",
112 "marker-waste-almost-full",
125 typedef enum _ipp_media_class_e
127 _IPP_GENERAL
, /* General-purpose size */
128 _IPP_PHOTO_ONLY
, /* Photo-only size */
129 _IPP_ENV_ONLY
/* Envelope-only size */
130 } _ipp_media_class_t
;
132 typedef enum _ipp_media_size_e
134 _IPP_MEDIA_SIZE_NONE
= -1,
139 _IPP_MEDIA_SIZE_LEGAL
,
140 _IPP_MEDIA_SIZE_LETTER
,
141 _IPP_MEDIA_SIZE_COM10
,
147 static const char * const media_supported
[] =
148 { /* media-supported values */
149 "iso_a4_210x297mm", /* A4 */
150 "iso_a5_148x210mm", /* A5 */
151 "iso_a6_105x148mm", /* A6 */
152 "iso_dl_110x220mm", /* DL */
153 "na_legal_8.5x14in", /* Legal */
154 "na_letter_8.5x11in", /* Letter */
155 "na_number-10_4.125x9.5in", /* #10 */
156 "na_index-3x5_3x5in", /* 3x5 */
157 "oe_photo-l_3.5x5in", /* L */
158 "na_index-4x6_4x6in", /* 4x6 */
159 "na_5x7_5x7in" /* 5x7 aka 2L */
161 static const int media_col_sizes
[][3] =
162 { /* media-col-database sizes */
163 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
164 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
165 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
166 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
167 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
168 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
169 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
170 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
171 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
172 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
173 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
176 typedef enum _ipp_media_source_e
178 _IPP_MEDIA_SOURCE_NONE
= -1,
179 _IPP_MEDIA_SOURCE_AUTO
,
180 _IPP_MEDIA_SOURCE_MAIN
,
181 _IPP_MEDIA_SOURCE_MANUAL
,
182 _IPP_MEDIA_SOURCE_ENVELOPE
,
183 _IPP_MEDIA_SOURCE_PHOTO
184 } _ipp_media_source_t
;
185 static const char * const media_source_supported
[] =
186 /* media-source-supported values */
195 typedef enum _ipp_media_type_e
197 _IPP_MEDIA_TYPE_NONE
= -1,
198 _IPP_MEDIA_TYPE_AUTO
,
199 _IPP_MEDIA_TYPE_CARDSTOCK
,
200 _IPP_MEDIA_TYPE_ENVELOPE
,
201 _IPP_MEDIA_TYPE_LABELS
,
202 _IPP_MEDIA_TYPE_OTHER
,
203 _IPP_MEDIA_TYPE_GLOSSY
,
204 _IPP_MEDIA_TYPE_HIGH_GLOSS
,
205 _IPP_MEDIA_TYPE_MATTE
,
206 _IPP_MEDIA_TYPE_SATIN
,
207 _IPP_MEDIA_TYPE_SEMI_GLOSS
,
208 _IPP_MEDIA_TYPE_STATIONERY
,
209 _IPP_MEDIA_TYPE_LETTERHEAD
,
210 _IPP_MEDIA_TYPE_TRANSPARENCY
212 static const char * const media_type_supported
[] =
213 /* media-type-supported values */
220 "photographic-glossy",
221 "photographic-high-gloss",
222 "photographic-matte",
223 "photographic-satin",
224 "photographic-semi-gloss",
226 "stationery-letterhead",
230 typedef enum _ipp_supply_e
232 _IPP_SUPPLY_CYAN
, /* Cyan Toner */
233 _IPP_SUPPLY_MAGENTA
, /* Magenta Toner */
234 _IPP_SUPPLY_YELLOW
, /* Yellow Toner */
235 _IPP_SUPPLY_BLACK
, /* Black Toner */
236 _IPP_SUPPLY_WASTE
/* Waste Toner */
238 static const char * const printer_supplies
[] =
239 { /* printer-supply-description values */
248 * URL scheme for web resources...
252 # define WEB_SCHEME "https"
254 # define WEB_SCHEME "http"
255 #endif /* HAVE_SSL */
263 typedef DNSServiceRef _ipp_srv_t
; /* Service reference */
264 typedef TXTRecordRef _ipp_txt_t
; /* TXT record */
266 #elif defined(HAVE_AVAHI)
267 typedef AvahiEntryGroup
*_ipp_srv_t
; /* Service reference */
268 typedef AvahiStringList
*_ipp_txt_t
; /* TXT record */
271 typedef void *_ipp_srv_t
; /* Service reference */
272 typedef void *_ipp_txt_t
; /* TXT record */
273 #endif /* HAVE_DNSSD */
275 typedef struct _ipp_filter_s
/**** Attribute filter ****/
277 cups_array_t
*ra
; /* Requested attributes */
278 ipp_tag_t group_tag
; /* Group to copy */
281 typedef struct _ipp_job_s _ipp_job_t
;
283 typedef struct _ipp_printer_s
/**** Printer data ****/
285 int ipv4
, /* IPv4 listener */
286 ipv6
; /* IPv6 listener */
287 _ipp_srv_t ipp_ref
, /* Bonjour IPP service */
288 ipps_ref
, /* Bonjour IPPS service */
289 http_ref
, /* Bonjour HTTP service */
290 printer_ref
; /* Bonjour LPD service */
291 char *dnssd_name
, /* printer-dnssd-name */
292 *name
, /* printer-name */
293 *icon
, /* Icon filename */
294 *directory
, /* Spool directory */
295 *hostname
, /* Hostname */
296 *uri
, /* printer-uri-supported */
297 *command
; /* Command to run with job file */
299 size_t urilen
; /* Length of printer URI */
300 ipp_t
*attrs
; /* Static attributes */
301 time_t start_time
; /* Startup time */
302 time_t config_time
; /* printer-config-change-time */
303 ipp_pstate_t state
; /* printer-state value */
304 _ipp_preason_t state_reasons
; /* printer-state-reasons values */
305 time_t state_time
; /* printer-state-change-time */
306 cups_array_t
*jobs
; /* Jobs */
307 _ipp_job_t
*active_job
; /* Current active/pending job */
308 int next_job_id
; /* Next job-id value */
309 _cups_rwlock_t rwlock
; /* Printer lock */
310 _ipp_media_size_t main_size
; /* Ready media */
311 _ipp_media_type_t main_type
;
313 _ipp_media_size_t envelope_size
;
315 _ipp_media_size_t photo_size
;
316 _ipp_media_type_t photo_type
;
318 int supplies
[5]; /* Supply levels (0-100) */
321 struct _ipp_job_s
/**** Job data ****/
324 const char *name
, /* job-name */
325 *username
, /* job-originating-user-name */
326 *format
; /* document-format */
327 ipp_jstate_t state
; /* job-state value */
328 time_t created
, /* time-at-creation value */
329 processing
, /* time-at-processing value */
330 completed
; /* time-at-completed value */
331 int impressions
, /* job-impressions value */
332 impcompleted
; /* job-impressions-completed value */
333 ipp_t
*attrs
; /* Static attributes */
334 int cancel
; /* Non-zero when job canceled */
335 char *filename
; /* Print file name */
336 int fd
; /* Print file descriptor */
337 _ipp_printer_t
*printer
; /* Printer */
340 typedef struct _ipp_client_s
/**** Client data ****/
342 http_t
*http
; /* HTTP connection */
343 ipp_t
*request
, /* IPP request */
344 *response
; /* IPP response */
345 time_t start
; /* Request start time */
346 http_state_t operation
; /* Request operation */
347 ipp_op_t operation_id
; /* IPP operation-id */
348 char uri
[1024], /* Request URI */
349 *options
; /* URI options */
350 http_addr_t addr
; /* Client address */
351 char hostname
[256]; /* Client hostname */
352 _ipp_printer_t
*printer
; /* Printer */
353 _ipp_job_t
*job
; /* Current job, if any */
361 static void clean_jobs(_ipp_printer_t
*printer
);
362 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
363 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
364 ipp_tag_t group_tag
, int quickcopy
);
365 static void copy_job_attributes(_ipp_client_t
*client
,
366 _ipp_job_t
*job
, cups_array_t
*ra
);
367 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
368 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
369 static int create_listener(int family
, int port
);
370 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
371 static ipp_t
*create_media_size(int width
, int length
);
372 static _ipp_printer_t
*create_printer(const char *servername
,
373 const char *name
, const char *location
,
374 const char *make
, const char *model
,
376 const char *docformats
, int ppm
,
377 int ppm_color
, int duplex
, int port
,
378 int pin
, const char *subtype
,
379 const char *directory
,
381 const char *attrfile
);
382 static void debug_attributes(const char *title
, ipp_t
*ipp
,
384 static void delete_client(_ipp_client_t
*client
);
385 static void delete_job(_ipp_job_t
*job
);
386 static void delete_printer(_ipp_printer_t
*printer
);
388 static void DNSSD_API
dnssd_callback(DNSServiceRef sdRef
,
389 DNSServiceFlags flags
,
390 DNSServiceErrorType errorCode
,
394 _ipp_printer_t
*printer
);
395 #elif defined(HAVE_AVAHI)
396 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
397 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
398 #endif /* HAVE_DNSSD */
399 static void dnssd_init(void);
400 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
401 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
402 static ipp_t
*get_collection(FILE *fp
, const char *filename
, int *linenum
);
403 static char *get_token(FILE *fp
, char *buf
, int buflen
, int *linenum
);
404 static void html_escape(_ipp_client_t
*client
, const char *s
,
406 static void html_footer(_ipp_client_t
*client
);
407 static void html_header(_ipp_client_t
*client
, const char *title
);
408 static void html_printf(_ipp_client_t
*client
, const char *format
,
409 ...) __attribute__((__format__(__printf__
,
411 static void ipp_cancel_job(_ipp_client_t
*client
);
412 static void ipp_close_job(_ipp_client_t
*client
);
413 static void ipp_create_job(_ipp_client_t
*client
);
414 static void ipp_get_job_attributes(_ipp_client_t
*client
);
415 static void ipp_get_jobs(_ipp_client_t
*client
);
416 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
417 static void ipp_identify_printer(_ipp_client_t
*client
);
418 static void ipp_print_job(_ipp_client_t
*client
);
419 static void ipp_print_uri(_ipp_client_t
*client
);
420 static void ipp_send_document(_ipp_client_t
*client
);
421 static void ipp_send_uri(_ipp_client_t
*client
);
422 static void ipp_validate_job(_ipp_client_t
*client
);
423 static void load_attributes(const char *filename
, ipp_t
*attrs
);
424 static int parse_options(_ipp_client_t
*client
, cups_option_t
**options
);
425 static void process_attr_message(_ipp_job_t
*job
, char *message
);
426 static void *process_client(_ipp_client_t
*client
);
427 static int process_http(_ipp_client_t
*client
);
428 static int process_ipp(_ipp_client_t
*client
);
429 static void *process_job(_ipp_job_t
*job
);
430 static void process_state_message(_ipp_job_t
*job
, char *message
);
431 static int register_printer(_ipp_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
);
432 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
433 const char *content_coding
,
434 const char *type
, size_t length
);
435 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
436 const char *message
, ...)
437 __attribute__ ((__format__ (__printf__
, 3, 4)));
438 static void respond_unsupported(_ipp_client_t
*client
,
439 ipp_attribute_t
*attr
);
440 static void run_printer(_ipp_printer_t
*printer
);
441 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
442 static void usage(int status
) __attribute__((noreturn
));
443 static int valid_doc_attributes(_ipp_client_t
*client
);
444 static int valid_job_attributes(_ipp_client_t
*client
);
452 static DNSServiceRef DNSSDMaster
= NULL
;
453 #elif defined(HAVE_AVAHI)
454 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
455 static AvahiClient
*DNSSDClient
= NULL
;
456 #endif /* HAVE_DNSSD */
458 static int KeepFiles
= 0,
464 * 'main()' - Main entry to the sample server.
467 int /* O - Exit status */
468 main(int argc
, /* I - Number of command-line args */
469 char *argv
[]) /* I - Command-line arguments */
471 int i
; /* Looping var */
472 const char *opt
, /* Current option character */
473 *attrfile
= NULL
, /* Attributes file */
474 *command
= NULL
, /* Command to run with job files */
475 *servername
= NULL
, /* Server host name */
476 *name
= NULL
, /* Printer name */
477 *location
= "", /* Location of printer */
478 *make
= "Test", /* Manufacturer */
479 *model
= "Printer", /* Model */
480 *icon
= "printer.png", /* Icon file */
481 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
482 /* Supported formats */
484 const char *keypath
= NULL
; /* Keychain path */
485 #endif /* HAVE_SSL */
486 const char *subtype
= "_print"; /* Bonjour service subtype */
487 int port
= 0, /* Port number (0 = auto) */
488 duplex
= 0, /* Duplex mode */
489 ppm
= 10, /* Pages per minute for mono */
490 ppm_color
= 0, /* Pages per minute for color */
491 pin
= 0; /* PIN printing mode? */
492 char directory
[1024] = "", /* Spool directory */
493 hostname
[1024]; /* Auto-detected hostname */
494 _ipp_printer_t
*printer
; /* Printer object */
498 * Parse command-line arguments...
501 for (i
= 1; i
< argc
; i
++)
502 if (argv
[i
][0] == '-')
504 for (opt
= argv
[i
] + 1; *opt
; opt
++)
508 case '2' : /* -2 (enable 2-sided printing) */
513 case 'K' : /* -K keypath */
519 #endif /* HAVE_SSL */
521 case 'M' : /* -M manufacturer */
528 case 'P' : /* -P (PIN printing mode) */
532 case 'V' : /* -V max-version */
537 if (!strcmp(argv
[i
], "2.2"))
539 else if (!strcmp(argv
[i
], "2.1"))
541 else if (!strcmp(argv
[i
], "2.0"))
543 else if (!strcmp(argv
[i
], "1.1"))
549 case 'a' : /* -a attributes-file */
557 case 'c' : /* -c command */
565 case 'd' : /* -d spool-directory */
569 strlcpy(directory
, argv
[i
], sizeof(directory
));
572 case 'f' : /* -f type/subtype[,...] */
579 case 'h' : /* -h (show help) */
582 case 'i' : /* -i icon.png */
589 case 'k' : /* -k (keep files) */
593 case 'l' : /* -l location */
600 case 'm' : /* -m model */
607 case 'n' : /* -n hostname */
611 servername
= argv
[i
];
614 case 'p' : /* -p port */
616 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
618 port
= atoi(argv
[i
]);
621 case 'r' : /* -r subtype */
628 case 's' : /* -s speed[,color-speed] */
632 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
636 case 'v' : /* -v (be verbose) */
640 default : /* Unknown */
641 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
652 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
660 * Apply defaults as needed...
664 servername
= httpGetHostname(NULL
, hostname
, sizeof(hostname
));
670 * Windows is almost always used as a single user system, so use a default
671 * port number of 8631.
678 * Use 8000 + UID mod 1000 for the default port number...
681 port
= 8000 + ((int)getuid() % 1000);
684 fprintf(stderr
, "Listening on port %d.\n", port
);
689 const char *tmpdir
; /* Temporary directory */
692 if ((tmpdir
= getenv("TEMP")) == NULL
)
694 #elif defined(__APPLE__) && !TARGET_OS_IOS
695 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
696 tmpdir
= "/private/tmp";
698 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
702 snprintf(directory
, sizeof(directory
), "%s/ippserver.%d", tmpdir
, (int)getpid());
704 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
706 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
707 directory
, strerror(errno
));
712 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
716 cupsSetServerCredentials(keypath
, servername
, 1);
717 #endif /* HAVE_SSL */
720 * Initialize Bonjour...
726 * Create the printer...
729 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
730 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
731 subtype
, directory
, command
, attrfile
)) == NULL
)
735 * Run the print service...
738 run_printer(printer
);
741 * Destroy the printer and exit...
744 delete_printer(printer
);
751 * 'clean_jobs()' - Clean out old (completed) jobs.
755 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
757 _ipp_job_t
*job
; /* Current job */
758 time_t cleantime
; /* Clean time */
761 if (cupsArrayCount(printer
->jobs
) == 0)
764 cleantime
= time(NULL
) - 60;
766 _cupsRWLockWrite(&(printer
->rwlock
));
767 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
769 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
770 if (job
->completed
&& job
->completed
< cleantime
)
772 cupsArrayRemove(printer
->jobs
, job
);
777 _cupsRWUnlock(&(printer
->rwlock
));
782 * 'compare_jobs()' - Compare two jobs.
785 static int /* O - Result of comparison */
786 compare_jobs(_ipp_job_t
*a
, /* I - First job */
787 _ipp_job_t
*b
) /* I - Second job */
789 return (b
->id
- a
->id
);
794 * 'copy_attributes()' - Copy attributes from one request to another.
798 copy_attributes(ipp_t
*to
, /* I - Destination request */
799 ipp_t
*from
, /* I - Source request */
800 cups_array_t
*ra
, /* I - Requested attributes */
801 ipp_tag_t group_tag
, /* I - Group to copy */
802 int quickcopy
) /* I - Do a quick copy? */
804 _ipp_filter_t filter
; /* Filter data */
808 filter
.group_tag
= group_tag
;
810 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
815 * 'copy_job_attrs()' - Copy job attributes to the response.
820 _ipp_client_t
*client
, /* I - Client */
821 _ipp_job_t
*job
, /* I - Job */
822 cups_array_t
*ra
) /* I - requested-attributes */
824 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
826 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
829 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
831 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
834 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
837 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
839 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
842 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
843 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
845 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
846 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
848 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
849 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
851 if (!ra
|| cupsArrayFind(ra
, "job-state"))
852 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
853 "job-state", job
->state
);
855 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
859 case IPP_JSTATE_PENDING
:
860 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
863 case IPP_JSTATE_HELD
:
865 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
866 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
867 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
869 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
872 case IPP_JSTATE_PROCESSING
:
874 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
876 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
879 case IPP_JSTATE_STOPPED
:
880 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
883 case IPP_JSTATE_CANCELED
:
884 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
887 case IPP_JSTATE_ABORTED
:
888 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
891 case IPP_JSTATE_COMPLETED
:
892 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
897 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
901 case IPP_JSTATE_PENDING
:
902 ippAddString(client
->response
, IPP_TAG_JOB
,
903 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
907 case IPP_JSTATE_HELD
:
909 ippAddString(client
->response
, IPP_TAG_JOB
,
910 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
911 "job-state-reasons", NULL
, "job-incoming");
912 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
913 ippAddString(client
->response
, IPP_TAG_JOB
,
914 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
915 "job-state-reasons", NULL
, "job-hold-until-specified");
917 ippAddString(client
->response
, IPP_TAG_JOB
,
918 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
919 "job-state-reasons", NULL
, "job-data-insufficient");
922 case IPP_JSTATE_PROCESSING
:
924 ippAddString(client
->response
, IPP_TAG_JOB
,
925 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
926 "job-state-reasons", NULL
, "processing-to-stop-point");
928 ippAddString(client
->response
, IPP_TAG_JOB
,
929 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
930 "job-state-reasons", NULL
, "job-printing");
933 case IPP_JSTATE_STOPPED
:
934 ippAddString(client
->response
, IPP_TAG_JOB
,
935 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
936 NULL
, "job-stopped");
939 case IPP_JSTATE_CANCELED
:
940 ippAddString(client
->response
, IPP_TAG_JOB
,
941 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
942 NULL
, "job-canceled-by-user");
945 case IPP_JSTATE_ABORTED
:
946 ippAddString(client
->response
, IPP_TAG_JOB
,
947 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
948 NULL
, "aborted-by-system");
951 case IPP_JSTATE_COMPLETED
:
952 ippAddString(client
->response
, IPP_TAG_JOB
,
953 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
954 NULL
, "job-completed-successfully");
959 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
960 ippAddInteger(client
->response
, IPP_TAG_JOB
,
961 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
962 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
964 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
965 ippAddInteger(client
->response
, IPP_TAG_JOB
,
966 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
967 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
972 * 'create_client()' - Accept a new network connection and create a client
976 static _ipp_client_t
* /* O - Client */
977 create_client(_ipp_printer_t
*printer
, /* I - Printer */
978 int sock
) /* I - Listen socket */
980 _ipp_client_t
*client
; /* Client */
983 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
985 perror("Unable to allocate memory for client");
989 client
->printer
= printer
;
992 * Accept the client and get the remote address...
995 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
997 perror("Unable to accept client connection");
1004 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
1007 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
1014 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
1018 static _ipp_job_t
* /* O - Job */
1019 create_job(_ipp_client_t
*client
) /* I - Client */
1021 _ipp_job_t
*job
; /* Job */
1022 ipp_attribute_t
*attr
; /* Job attribute */
1023 char uri
[1024], /* job-uri value */
1024 uuid
[64]; /* job-uuid value */
1027 _cupsRWLockWrite(&(client
->printer
->rwlock
));
1028 if (client
->printer
->active_job
&&
1029 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
1032 * Only accept a single job at a time...
1035 _cupsRWUnlock(&(client
->printer
->rwlock
));
1040 * Allocate and initialize the job object...
1043 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
1045 perror("Unable to allocate memory for job");
1049 job
->printer
= client
->printer
;
1050 job
->attrs
= ippNew();
1051 job
->state
= IPP_JSTATE_HELD
;
1055 * Copy all of the job attributes...
1058 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
1061 * Get the requesting-user-name, document format, and priority...
1064 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1065 job
->username
= ippGetString(attr
, 0, NULL
);
1067 job
->username
= "anonymous";
1069 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1071 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1073 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1074 job
->format
= ippGetString(attr
, 0, NULL
);
1075 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1076 job
->format
= ippGetString(attr
, 0, NULL
);
1078 job
->format
= "application/octet-stream";
1081 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1082 job
->impressions
= ippGetInteger(attr
, 0);
1084 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1085 job
->name
= ippGetString(attr
, 0, NULL
);
1088 * Add job description attributes and add to the jobs array...
1091 job
->id
= client
->printer
->next_job_id
++;
1093 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1094 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1096 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1097 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1098 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1099 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1100 if ((attr
= ippFindAttribute(client
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
1101 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, ippGetString(attr
, 0, NULL
));
1103 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1104 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1106 cupsArrayAdd(client
->printer
->jobs
, job
);
1107 client
->printer
->active_job
= job
;
1109 _cupsRWUnlock(&(client
->printer
->rwlock
));
1116 * 'create_job_filename()' - Create the filename for a document in a job.
1119 static void create_job_filename(
1120 _ipp_printer_t
*printer
, /* I - Printer */
1121 _ipp_job_t
*job
, /* I - Job */
1122 char *fname
, /* I - Filename buffer */
1123 size_t fnamesize
) /* I - Size of filename buffer */
1125 char name
[256], /* "Safe" filename */
1126 *nameptr
; /* Pointer into filename */
1127 const char *ext
, /* Filename extension */
1128 *job_name
; /* job-name value */
1129 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1133 * Make a name from the job-name attribute...
1136 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1137 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1139 job_name
= "untitled";
1141 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1142 if (isalnum(*job_name
& 255) || *job_name
== '-')
1143 *nameptr
++ = (char)tolower(*job_name
& 255);
1150 * Figure out the extension...
1153 if (!strcasecmp(job
->format
, "image/jpeg"))
1155 else if (!strcasecmp(job
->format
, "image/png"))
1157 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1159 else if (!strcasecmp(job
->format
, "image/urf"))
1161 else if (!strcasecmp(job
->format
, "application/pdf"))
1163 else if (!strcasecmp(job
->format
, "application/postscript"))
1169 * Create a filename with the job-id, job-name, and document-format (extension)...
1172 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1177 * 'create_listener()' - Create a listener socket.
1180 static int /* O - Listener socket or -1 on error */
1181 create_listener(int family
, /* I - Address family */
1182 int port
) /* I - Port number */
1184 int sock
; /* Listener socket */
1185 http_addrlist_t
*addrlist
; /* Listen address */
1186 char service
[255]; /* Service port */
1189 snprintf(service
, sizeof(service
), "%d", port
);
1190 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1193 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1195 httpAddrFreeList(addrlist
);
1202 * 'create_media_col()' - Create a media-col value.
1205 static ipp_t
* /* O - media-col collection */
1206 create_media_col(const char *media
, /* I - Media name */
1207 const char *source
, /* I - Media source */
1208 const char *type
, /* I - Media type */
1209 int width
, /* I - x-dimension in 2540ths */
1210 int length
, /* I - y-dimension in 2540ths */
1211 int margins
) /* I - Value for margins */
1213 ipp_t
*media_col
= ippNew(), /* media-col value */
1214 *media_size
= create_media_size(width
, length
);
1215 /* media-size value */
1216 char media_key
[256]; /* media-key value */
1220 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1222 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1224 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1226 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1228 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1230 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1231 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1232 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1233 "media-bottom-margin", margins
);
1234 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1235 "media-left-margin", margins
);
1236 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1237 "media-right-margin", margins
);
1238 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1239 "media-top-margin", margins
);
1241 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1243 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1245 ippDelete(media_size
);
1252 * 'create_media_size()' - Create a media-size value.
1255 static ipp_t
* /* O - media-col collection */
1256 create_media_size(int width
, /* I - x-dimension in 2540ths */
1257 int length
) /* I - y-dimension in 2540ths */
1259 ipp_t
*media_size
= ippNew(); /* media-size value */
1262 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1264 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1267 return (media_size
);
1272 * 'create_printer()' - Create, register, and listen for connections to a
1276 static _ipp_printer_t
* /* O - Printer */
1277 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1278 const char *name
, /* I - printer-name */
1279 const char *location
, /* I - printer-location */
1280 const char *make
, /* I - printer-make-and-model */
1281 const char *model
, /* I - printer-make-and-model */
1282 const char *icon
, /* I - printer-icons */
1283 const char *docformats
, /* I - document-format-supported */
1284 int ppm
, /* I - Pages per minute in grayscale */
1285 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1286 int duplex
, /* I - 1 = duplex, 0 = simplex */
1287 int port
, /* I - Port for listeners or 0 for auto */
1288 int pin
, /* I - Require PIN printing */
1289 const char *subtype
, /* I - Bonjour service subtype */
1290 const char *directory
, /* I - Spool directory */
1291 const char *command
, /* I - Command to run on job files */
1292 const char *attrfile
) /* I - Attributes file */
1294 int i
, j
; /* Looping vars */
1295 _ipp_printer_t
*printer
; /* Printer */
1297 char path
[1024]; /* Full path to command */
1299 char uri
[1024], /* Printer URI */
1301 securi
[1024], /* Secure printer URI */
1302 *uris
[2], /* All URIs */
1303 #endif /* HAVE_SSL */
1304 icons
[1024], /* printer-icons URI */
1305 adminurl
[1024], /* printer-more-info URI */
1306 supplyurl
[1024],/* printer-supply-info-uri URI */
1307 device_id
[1024],/* printer-device-id */
1308 make_model
[128],/* printer-make-and-model */
1309 uuid
[128]; /* printer-uuid */
1310 int num_formats
; /* Number of document-format-supported values */
1311 char *defformat
, /* document-format-default value */
1312 *formats
[100], /* document-format-supported values */
1313 *ptr
; /* Pointer into string */
1314 const char *prefix
; /* Prefix string */
1315 int num_database
; /* Number of database values */
1316 ipp_attribute_t
*media_col_database
,
1317 /* media-col-database value */
1318 *media_size_supported
;
1319 /* media-size-supported value */
1320 ipp_t
*media_col_default
;
1321 /* media-col-default value */
1322 int media_col_index
;/* Current media-col-database value */
1323 int k_supported
; /* Maximum file size supported */
1325 struct statvfs spoolinfo
; /* FS info for spool directory */
1326 double spoolsize
; /* FS size */
1327 #elif defined(HAVE_STATFS)
1328 struct statfs spoolinfo
; /* FS info for spool directory */
1329 double spoolsize
; /* FS size */
1330 #endif /* HAVE_STATVFS */
1331 static const int orients
[4] = /* orientation-requested-supported values */
1333 IPP_ORIENT_PORTRAIT
,
1334 IPP_ORIENT_LANDSCAPE
,
1335 IPP_ORIENT_REVERSE_LANDSCAPE
,
1336 IPP_ORIENT_REVERSE_PORTRAIT
1338 static const char * const versions
[] =/* ipp-versions-supported values */
1345 static const char * const features
[] =/* ipp-features-supported values */
1349 static const int ops
[] = /* operations-supported values */
1353 IPP_OP_VALIDATE_JOB
,
1355 IPP_OP_SEND_DOCUMENT
,
1358 IPP_OP_GET_JOB_ATTRIBUTES
,
1360 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1361 IPP_OP_CANCEL_MY_JOBS
,
1363 IPP_OP_IDENTIFY_PRINTER
1365 static const char * const charsets
[] =/* charset-supported values */
1370 static const char * const compressions
[] =/* compression-supported values */
1375 #endif /* HAVE_LIBZ */
1378 static const char * const identify_actions
[] =
1383 static const char * const job_creation
[] =
1384 { /* job-creation-attributes-supported values */
1386 "ipp-attribute-fidelity",
1388 "job-accounting-user-id",
1394 "multiple-document-handling",
1395 "orientation-requested",
1399 static const char * const media_col_supported
[] =
1400 { /* media-col-supported values */
1401 "media-bottom-margin",
1402 "media-left-margin",
1403 "media-right-margin",
1409 static const int media_xxx_margin_supported
[] =
1410 { /* media-xxx-margin-supported values */
1414 static const char * const multiple_document_handling
[] =
1415 { /* multiple-document-handling-supported values */
1416 "separate-documents-uncollated-copies",
1417 "separate-documents-collated-copies"
1419 static const char * const overrides
[] =
1420 { /* overrides-supported */
1424 static const char * const print_color_mode_supported
[] =
1425 { /* print-color-mode-supported values */
1430 static const int print_quality_supported
[] =
1431 { /* print-quality-supported values */
1436 static const int pwg_raster_document_resolution_supported
[] =
1441 static const char * const pwg_raster_document_type_supported
[] =
1449 static const char * const reference_uri_schemes_supported
[] =
1450 { /* reference-uri-schemes-supported */
1456 #endif /* HAVE_SSL */
1458 static const char * const sides_supported
[] =
1459 { /* sides-supported values */
1461 "two-sided-long-edge",
1462 "two-sided-short-edge"
1464 static const char * const urf_supported
[] =
1465 { /* urf-supported values */
1468 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1476 static const char * const uri_authentication_supported
[] =
1477 { /* uri-authentication-supported values */
1481 static const char * const uri_security_supported
[] =
1482 { /* uri-security-supported values */
1486 #endif /* HAVE_SSL */
1487 static const char * const which_jobs
[] =
1488 { /* which-jobs-supported values */
1497 "processing-stopped"
1503 * If a command was specified, make sure it exists and is executable...
1508 if (*command
== '/' || !strncmp(command
, "./", 2))
1510 if (access(command
, X_OK
))
1512 fprintf(stderr
, "ippserver: Unable to execute command \"%s\": %s\n", command
, strerror(errno
));
1518 if (!cupsFileFind(command
, getenv("PATH"), 1, path
, sizeof(path
)))
1520 fprintf(stderr
, "ippserver: Unable to find command \"%s\".\n", command
);
1530 * Allocate memory for the printer...
1533 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1535 perror("ippserver: Unable to allocate memory for printer");
1541 printer
->name
= strdup(name
);
1542 printer
->dnssd_name
= strdup(printer
->name
);
1543 printer
->command
= command
? strdup(command
) : NULL
;
1544 printer
->directory
= strdup(directory
);
1545 printer
->hostname
= strdup(servername
);
1546 printer
->port
= port
;
1547 printer
->start_time
= time(NULL
);
1548 printer
->config_time
= printer
->start_time
;
1549 printer
->state
= IPP_PSTATE_IDLE
;
1550 printer
->state_reasons
= _IPP_PREASON_NONE
;
1551 printer
->state_time
= printer
->start_time
;
1552 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1553 printer
->next_job_id
= 1;
1555 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1556 printer
->uri
= strdup(uri
);
1557 printer
->urilen
= strlen(uri
);
1560 httpAssembleURI(HTTP_URI_CODING_ALL
, securi
, sizeof(securi
), "ipps", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1561 #endif /* HAVE_SSL */
1564 printer
->icon
= strdup(icon
);
1566 printer
->main_size
= _IPP_MEDIA_SIZE_A4
;
1567 printer
->main_type
= _IPP_MEDIA_TYPE_STATIONERY
;
1568 printer
->main_level
= 500;
1570 printer
->envelope_size
= _IPP_MEDIA_SIZE_NONE
;
1571 printer
->envelope_level
= 0;
1573 printer
->photo_size
= _IPP_MEDIA_SIZE_NONE
;
1574 printer
->photo_type
= _IPP_MEDIA_TYPE_NONE
;
1575 printer
->photo_level
= 0;
1577 printer
->supplies
[_IPP_SUPPLY_CYAN
] = 100;
1578 printer
->supplies
[_IPP_SUPPLY_MAGENTA
] = 100;
1579 printer
->supplies
[_IPP_SUPPLY_YELLOW
] = 100;
1580 printer
->supplies
[_IPP_SUPPLY_BLACK
] = 100;
1581 printer
->supplies
[_IPP_SUPPLY_WASTE
] = 0;
1583 _cupsRWInit(&(printer
->rwlock
));
1586 * Create the listener sockets...
1589 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1591 perror("Unable to create IPv4 listener");
1595 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1597 perror("Unable to create IPv6 listener");
1602 * Prepare values for the printer attributes...
1605 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/icon.png");
1606 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/");
1607 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/supplies");
1611 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1612 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1613 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1616 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1619 formats
[0] = strdup(docformats
);
1620 defformat
= formats
[0];
1621 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1624 formats
[num_formats
++] = ptr
;
1626 if (!strcasecmp(ptr
, "application/octet-stream"))
1630 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1631 ptr
= device_id
+ strlen(device_id
);
1633 for (i
= 0; i
< num_formats
; i
++)
1635 if (!strcasecmp(formats
[i
], "application/pdf"))
1636 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1637 else if (!strcasecmp(formats
[i
], "application/postscript"))
1638 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1639 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1640 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1641 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1642 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1643 else if (!strcasecmp(formats
[i
], "image/png"))
1644 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1645 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1646 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1651 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
1658 * Get the maximum spool size based on the size of the filesystem used for
1659 * the spool directory. If the host OS doesn't support the statfs call
1660 * or the filesystem is larger than 2TiB, always report INT_MAX.
1664 if (statvfs(printer
->directory
, &spoolinfo
))
1665 k_supported
= INT_MAX
;
1666 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1667 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1668 k_supported
= INT_MAX
;
1670 k_supported
= (int)spoolsize
;
1672 #elif defined(HAVE_STATFS)
1673 if (statfs(printer
->directory
, &spoolinfo
))
1674 k_supported
= INT_MAX
;
1675 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1676 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1677 k_supported
= INT_MAX
;
1679 k_supported
= (int)spoolsize
;
1682 k_supported
= INT_MAX
;
1683 #endif /* HAVE_STATVFS */
1686 * Create the printer attributes. This list of attributes is sorted to improve
1687 * performance when the client provides a requested-attributes attribute...
1690 printer
->attrs
= ippNew();
1693 load_attributes(attrfile
, printer
->attrs
);
1695 /* charset-configured */
1696 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-configured", NULL
, "utf-8");
1698 /* charset-supported */
1699 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]), NULL
, charsets
);
1701 /* color-supported */
1702 if (!ippFindAttribute(printer
->attrs
, "color-supported", IPP_TAG_ZERO
))
1703 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
1705 /* compression-supported */
1706 if (!ippFindAttribute(printer
->attrs
, "compression-supported", IPP_TAG_ZERO
))
1707 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "compression-supported", (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
, compressions
);
1709 /* copies-default */
1710 if (!ippFindAttribute(printer
->attrs
, "copies-default", IPP_TAG_ZERO
))
1711 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
1713 /* copies-supported */
1714 if (!ippFindAttribute(printer
->attrs
, "copies-supported", IPP_TAG_ZERO
))
1715 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1717 /* document-format-default */
1718 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1719 "document-format-default", NULL
, defformat
);
1721 /* document-format-supported */
1722 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1723 "document-format-supported", num_formats
, NULL
,
1724 (const char * const *)formats
);
1726 /* document-password-supported */
1727 if (!ippFindAttribute(printer
->attrs
, "document-password-supported", IPP_TAG_ZERO
))
1728 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1730 /* finishings-default */
1731 if (!ippFindAttribute(printer
->attrs
, "finishings-default", IPP_TAG_ZERO
))
1732 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
1734 /* finishings-supported */
1735 if (!ippFindAttribute(printer
->attrs
, "finishings-supported", IPP_TAG_ZERO
))
1736 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
1738 /* generated-natural-language-supported */
1739 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "generated-natural-language-supported", NULL
, "en");
1741 /* identify-actions-default */
1742 if (!ippFindAttribute(printer
->attrs
, "identify-actions-default", IPP_TAG_ZERO
))
1743 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1745 /* identify-actions-supported */
1746 if (!ippFindAttribute(printer
->attrs
, "identify-actions-supported", IPP_TAG_ZERO
))
1747 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
);
1749 /* ipp-features-supported */
1750 if (!ippFindAttribute(printer
->attrs
, "ipp-features-supported", IPP_TAG_ZERO
))
1751 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1753 /* ipp-versions-supported */
1754 if (!ippFindAttribute(printer
->attrs
, "ipp-versions-supported", IPP_TAG_ZERO
))
1756 int num_versions
= MaxVersion
== 11 ? 1 : MaxVersion
== 20 ? 2 : MaxVersion
== 21 ? 3 : 4;
1757 /* Number of supported versions */
1759 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", num_versions
, NULL
, versions
);
1762 /* job-account-id-default */
1763 if (!ippFindAttribute(printer
->attrs
, "job-account-id-default", IPP_TAG_ZERO
))
1764 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1766 /* job-account-id-supported */
1767 if (!ippFindAttribute(printer
->attrs
, "job-account-id-supported", IPP_TAG_ZERO
))
1768 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1770 /* job-accounting-user-id-default */
1771 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-default", IPP_TAG_ZERO
))
1772 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1774 /* job-accounting-user-id-supported */
1775 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-supported", IPP_TAG_ZERO
))
1776 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1778 /* job-creation-attributes-supported */
1779 if (!ippFindAttribute(printer
->attrs
, "job-creation-attributes-supported", IPP_TAG_ZERO
))
1780 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
);
1782 /* job-ids-supported */
1783 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1785 /* job-k-octets-supported */
1786 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1789 /* job-password-supported */
1790 if (!ippFindAttribute(printer
->attrs
, "job-password-supported", IPP_TAG_ZERO
))
1791 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-password-supported", 4);
1793 /* job-priority-default */
1794 if (!ippFindAttribute(printer
->attrs
, "job-priority-default", IPP_TAG_ZERO
))
1795 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-default", 50);
1797 /* job-priority-supported */
1798 if (!ippFindAttribute(printer
->attrs
, "job-priority-supported", IPP_TAG_ZERO
))
1799 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-supported", 100);
1801 /* job-sheets-default */
1802 if (!ippFindAttribute(printer
->attrs
, "job-sheets-default", IPP_TAG_ZERO
))
1803 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-default", NULL
, "none");
1805 /* job-sheets-supported */
1806 if (!ippFindAttribute(printer
->attrs
, "job-sheets-supported", IPP_TAG_ZERO
))
1807 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-supported", NULL
, "none");
1809 /* media-bottom-margin-supported */
1810 if (!ippFindAttribute(printer
->attrs
, "media-bottom-margin-supported", IPP_TAG_ZERO
))
1811 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
);
1813 /* media-col-database */
1814 if (!ippFindAttribute(printer
->attrs
, "media-col-database", IPP_TAG_ZERO
))
1816 for (num_database
= 0, i
= 0;
1817 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1820 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1821 num_database
+= 3; /* auto + manual + envelope */
1822 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1823 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1825 num_database
+= 2; /* Regular + borderless */
1828 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-database", num_database
, NULL
);
1829 for (media_col_index
= 0, i
= 0;
1830 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1833 switch (media_col_sizes
[i
][2])
1837 * Regular + borderless for the general class; no source/type
1841 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]));
1842 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]));
1845 case _IPP_ENV_ONLY
:
1847 * Regular margins for "auto", "manual", and "envelope" sources.
1850 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]));
1851 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]));
1852 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]));
1854 case _IPP_PHOTO_ONLY
:
1856 * Photos have specific media types and can only be printed via
1857 * the auto, manual, and photo sources...
1861 j
< (int)(sizeof(media_type_supported
) /
1862 sizeof(media_type_supported
[0]));
1865 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1868 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]));
1869 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]));
1870 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]));
1877 /* media-col-default */
1878 if (!ippFindAttribute(printer
->attrs
, "media-col-default", IPP_TAG_ZERO
))
1880 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]);
1882 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1884 ippDelete(media_col_default
);
1887 /* media-col-supported */
1888 if (!ippFindAttribute(printer
->attrs
, "media-col-supported", IPP_TAG_ZERO
))
1889 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
);
1892 if (!ippFindAttribute(printer
->attrs
, "media-default", IPP_TAG_ZERO
))
1893 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media_supported
[0]);
1895 /* media-left-margin-supported */
1896 if (!ippFindAttribute(printer
->attrs
, "media-left-margin-supported", IPP_TAG_ZERO
))
1897 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
);
1899 /* media-right-margin-supported */
1900 if (!ippFindAttribute(printer
->attrs
, "media-right-margin-supported", IPP_TAG_ZERO
))
1901 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
);
1903 /* media-supported */
1904 if (!ippFindAttribute(printer
->attrs
, "media-supported", IPP_TAG_ZERO
))
1905 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
);
1907 /* media-size-supported */
1908 if (!ippFindAttribute(printer
->attrs
, "media-size-supported", IPP_TAG_ZERO
))
1910 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-size-supported", (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0])), NULL
);
1913 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1916 ipp_t
*size
= create_media_size(media_col_sizes
[i
][0], media_col_sizes
[i
][1]);
1918 ippSetCollection(printer
->attrs
, &media_size_supported
, i
, size
);
1923 /* media-source-supported */
1924 if (!ippFindAttribute(printer
->attrs
, "media-source-supported", IPP_TAG_ZERO
))
1925 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
);
1927 /* media-top-margin-supported */
1928 if (!ippFindAttribute(printer
->attrs
, "media-top-margin-supported", IPP_TAG_ZERO
))
1929 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
);
1931 /* media-type-supported */
1932 if (!ippFindAttribute(printer
->attrs
, "media-type-supported", IPP_TAG_ZERO
))
1933 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
);
1935 /* multiple-document-handling-supported */
1936 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
);
1938 /* multiple-document-jobs-supported */
1939 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1941 /* multiple-operation-time-out */
1942 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1944 /* multiple-operation-time-out-action */
1945 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1947 /* natural-language-configured */
1948 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1949 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1950 "natural-language-configured", NULL
, "en");
1952 /* number-up-default */
1953 if (!ippFindAttribute(printer
->attrs
, "number-up-default", IPP_TAG_ZERO
))
1954 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-default", 1);
1956 /* number-up-supported */
1957 if (!ippFindAttribute(printer
->attrs
, "number-up-supported", IPP_TAG_ZERO
))
1958 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-supported", 1);
1960 /* operations-supported */
1961 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1963 /* orientation-requested-default */
1964 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-default", IPP_TAG_ZERO
))
1965 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "orientation-requested-default", 0);
1967 /* orientation-requested-supported */
1968 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-supported", IPP_TAG_ZERO
))
1969 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", 4, orients
);
1971 /* output-bin-default */
1972 if (!ippFindAttribute(printer
->attrs
, "output-bin-default", IPP_TAG_ZERO
))
1973 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
1975 /* output-bin-supported */
1976 if (!ippFindAttribute(printer
->attrs
, "output-bin-supported", IPP_TAG_ZERO
))
1977 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
1979 /* overrides-supported */
1980 if (!ippFindAttribute(printer
->attrs
, "overrides-supported", IPP_TAG_ZERO
))
1981 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1983 /* page-ranges-supported */
1984 if (!ippFindAttribute(printer
->attrs
, "page-ranges-supported", IPP_TAG_ZERO
))
1985 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1987 /* pages-per-minute */
1988 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1989 "pages-per-minute", ppm
);
1991 /* pages-per-minute-color */
1993 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1994 "pages-per-minute-color", ppm_color
);
1996 /* pdl-override-supported */
1997 if (!ippFindAttribute(printer
->attrs
, "pdl-override-supported", IPP_TAG_ZERO
))
1998 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
2000 /* preferred-attributes-supported */
2001 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "preferred-attributes-supported", 0);
2003 /* print-color-mode-default */
2004 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-default", IPP_TAG_ZERO
))
2005 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
2007 /* print-color-mode-supported */
2008 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-supported", IPP_TAG_ZERO
))
2009 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
);
2011 /* print-content-optimize-default */
2012 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-default", IPP_TAG_ZERO
))
2013 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
2015 /* print-content-optimize-supported */
2016 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-supported", IPP_TAG_ZERO
))
2017 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
2019 /* print-rendering-intent-default */
2020 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-default", IPP_TAG_ZERO
))
2021 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
2023 /* print-rendering-intent-supported */
2024 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-supported", IPP_TAG_ZERO
))
2025 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
2027 /* print-quality-default */
2028 if (!ippFindAttribute(printer
->attrs
, "print-quality-default", IPP_TAG_ZERO
))
2029 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
2031 /* print-quality-supported */
2032 if (!ippFindAttribute(printer
->attrs
, "print-quality-supported", IPP_TAG_ZERO
))
2033 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
);
2035 /* printer-device-id */
2036 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2037 "printer-device-id", NULL
, device_id
);
2039 /* printer-get-attributes-supported */
2040 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
2042 /* printer-geo-location */
2043 if (!ippFindAttribute(printer
->attrs
, "printer-geo-location", IPP_TAG_ZERO
))
2044 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
2047 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
2048 "printer-icons", NULL
, icons
);
2050 /* printer-is-accepting-jobs */
2051 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
2054 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
2056 /* printer-location */
2057 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2058 "printer-location", NULL
, location
);
2060 /* printer-make-and-model */
2061 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2062 "printer-make-and-model", NULL
, make_model
);
2064 /* printer-mandatory-job-attributes */
2065 if (pin
&& !ippFindAttribute(printer
->attrs
, "", IPP_TAG_ZERO
))
2067 static const char * const names
[] =
2070 "job-accounting-user-id",
2074 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2075 "printer-mandatory-job-attributes",
2076 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
2079 /* printer-more-info */
2080 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
2083 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
2085 /* printer-organization */
2086 if (!ippFindAttribute(printer
->attrs
, "printer-organization", IPP_TAG_ZERO
))
2087 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
2089 /* printer-organizational-unit */
2090 if (!ippFindAttribute(printer
->attrs
, "printer-organizational-unit", IPP_TAG_ZERO
))
2091 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
2093 /* printer-resolution-default */
2094 if (!ippFindAttribute(printer
->attrs
, "printer-resolution-default", IPP_TAG_ZERO
))
2095 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
2097 /* printer-resolution-supported */
2098 if (!ippFindAttribute(printer
->attrs
, "printer-resolutions-supported", IPP_TAG_ZERO
))
2099 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
2101 /* printer-supply-description */
2102 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
);
2104 /* printer-supply-info-uri */
2105 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
2107 /* printer-uri-supported */
2112 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", 2, NULL
, (const char **)uris
);
2115 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
2116 #endif /* HAVE_SSL */
2119 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
2120 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
2122 /* pwg-raster-document-xxx-supported */
2123 for (i
= 0; i
< num_formats
; i
++)
2124 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
2127 if (i
< num_formats
)
2129 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-resolution-supported", IPP_TAG_ZERO
))
2130 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
);
2131 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-sheet-back", IPP_TAG_ZERO
))
2132 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "pwg-raster-document-sheet-back", NULL
, "normal");
2133 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-type-supported", IPP_TAG_ZERO
))
2134 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
);
2137 /* reference-uri-scheme-supported */
2138 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
);
2141 if (!ippFindAttribute(printer
->attrs
, "sides-default", IPP_TAG_ZERO
))
2142 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
2144 /* sides-supported */
2145 if (!ippFindAttribute(printer
->attrs
, "sides-supported", IPP_TAG_ZERO
))
2146 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
2149 for (i
= 0; i
< num_formats
; i
++)
2150 if (!strcasecmp(formats
[i
], "image/urf"))
2153 if (i
< num_formats
&& !ippFindAttribute(printer
->attrs
, "urf-supported", IPP_TAG_ZERO
))
2154 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2156 /* uri-authentication-supported */
2158 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_supported
);
2160 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "none");
2161 #endif /* HAVE_SSL */
2163 /* uri-security-supported */
2165 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", 2, NULL
, uri_security_supported
);
2167 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", NULL
, "none");
2168 #endif /* HAVE_SSL */
2170 /* which-jobs-supported */
2171 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
);
2175 debug_attributes("Printer", printer
->attrs
, 0);
2178 * Register the printer with Bonjour...
2181 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2192 * If we get here we were unable to create the printer...
2197 delete_printer(printer
);
2203 * 'debug_attributes()' - Print attributes in a request or response.
2207 debug_attributes(const char *title
, /* I - Title */
2208 ipp_t
*ipp
, /* I - Request/response */
2209 int type
) /* I - 0 = object, 1 = request, 2 = response */
2211 ipp_tag_t group_tag
; /* Current group */
2212 ipp_attribute_t
*attr
; /* Current attribute */
2213 char buffer
[2048]; /* String buffer for value */
2214 int major
, minor
; /* Version */
2220 fprintf(stderr
, "%s:\n", title
);
2221 major
= ippGetVersion(ipp
, &minor
);
2222 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2224 fprintf(stderr
, " operation-id=%s(%04x)\n",
2225 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2227 fprintf(stderr
, " status-code=%s(%04x)\n",
2228 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2229 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2231 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2233 attr
= ippNextAttribute(ipp
))
2235 if (ippGetGroupTag(attr
) != group_tag
)
2237 group_tag
= ippGetGroupTag(attr
);
2238 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2241 if (ippGetName(attr
))
2243 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2244 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2245 ippGetCount(attr
) > 1 ? "1setOf " : "",
2246 ippTagString(ippGetValueTag(attr
)), buffer
);
2253 * 'delete_client()' - Close the socket and free all memory used by a client
2258 delete_client(_ipp_client_t
*client
) /* I - Client */
2261 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2264 * Flush pending writes before closing...
2267 httpFlushWrite(client
->http
);
2273 httpClose(client
->http
);
2275 ippDelete(client
->request
);
2276 ippDelete(client
->response
);
2283 * 'delete_job()' - Remove from the printer and free all memory used by a job
2288 delete_job(_ipp_job_t
*job
) /* I - Job */
2291 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2293 ippDelete(job
->attrs
);
2298 unlink(job
->filename
);
2300 free(job
->filename
);
2308 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2309 * used by a printer object.
2313 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2315 if (printer
->ipv4
>= 0)
2316 close(printer
->ipv4
);
2318 if (printer
->ipv6
>= 0)
2319 close(printer
->ipv6
);
2322 if (printer
->printer_ref
)
2323 DNSServiceRefDeallocate(printer
->printer_ref
);
2324 if (printer
->ipp_ref
)
2325 DNSServiceRefDeallocate(printer
->ipp_ref
);
2326 if (printer
->ipps_ref
)
2327 DNSServiceRefDeallocate(printer
->ipps_ref
);
2328 if (printer
->http_ref
)
2329 DNSServiceRefDeallocate(printer
->http_ref
);
2330 #elif defined(HAVE_AVAHI)
2331 avahi_threaded_poll_lock(DNSSDMaster
);
2333 if (printer
->printer_ref
)
2334 avahi_entry_group_free(printer
->printer_ref
);
2335 if (printer
->ipp_ref
)
2336 avahi_entry_group_free(printer
->ipp_ref
);
2337 if (printer
->ipps_ref
)
2338 avahi_entry_group_free(printer
->ipps_ref
);
2339 if (printer
->http_ref
)
2340 avahi_entry_group_free(printer
->http_ref
);
2342 avahi_threaded_poll_unlock(DNSSDMaster
);
2343 #endif /* HAVE_DNSSD */
2345 if (printer
->dnssd_name
)
2346 free(printer
->dnssd_name
);
2348 free(printer
->name
);
2350 free(printer
->icon
);
2351 if (printer
->command
)
2352 free(printer
->command
);
2353 if (printer
->directory
)
2354 free(printer
->directory
);
2355 if (printer
->hostname
)
2356 free(printer
->hostname
);
2360 ippDelete(printer
->attrs
);
2361 cupsArrayDelete(printer
->jobs
);
2369 * 'dnssd_callback()' - Handle Bonjour registration events.
2372 static void DNSSD_API
2374 DNSServiceRef sdRef
, /* I - Service reference */
2375 DNSServiceFlags flags
, /* I - Status flags */
2376 DNSServiceErrorType errorCode
, /* I - Error, if any */
2377 const char *name
, /* I - Service name */
2378 const char *regtype
, /* I - Service type */
2379 const char *domain
, /* I - Domain for service */
2380 _ipp_printer_t
*printer
) /* I - Printer */
2388 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2389 regtype
, (int)errorCode
);
2392 else if (strcasecmp(name
, printer
->dnssd_name
))
2395 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2397 /* No lock needed since only the main thread accesses/changes this */
2398 free(printer
->dnssd_name
);
2399 printer
->dnssd_name
= strdup(name
);
2404 #elif defined(HAVE_AVAHI)
2406 * 'dnssd_callback()' - Handle Bonjour registration events.
2411 AvahiEntryGroup
*srv
, /* I - Service */
2412 AvahiEntryGroupState state
, /* I - Registration state */
2413 void *context
) /* I - Printer */
2422 * 'dnssd_client_cb()' - Client callback for Avahi.
2424 * Called whenever the client or server state changes...
2429 AvahiClient
*c
, /* I - Client */
2430 AvahiClientState state
, /* I - Current state */
2431 void *userdata
) /* I - User data (unused) */
2441 fprintf(stderr
, "Ignore Avahi state %d.\n", state
);
2444 case AVAHI_CLIENT_FAILURE
:
2445 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
2447 fputs("Avahi server crashed, exiting.\n", stderr
);
2453 #endif /* HAVE_DNSSD */
2457 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2464 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2466 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2470 #elif defined(HAVE_AVAHI)
2471 int error
; /* Error code, if any */
2473 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2475 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2479 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2481 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2485 avahi_threaded_poll_start(DNSSDMaster
);
2486 #endif /* HAVE_DNSSD */
2491 * 'filter_cb()' - Filter printer attributes based on the requested array.
2494 static int /* O - 1 to copy, 0 to ignore */
2495 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2496 ipp_t
*dst
, /* I - Destination (unused) */
2497 ipp_attribute_t
*attr
) /* I - Source attribute */
2500 * Filter attributes as needed...
2503 #ifndef WIN32 /* Avoid MS compiler bug */
2507 ipp_tag_t group
= ippGetGroupTag(attr
);
2508 const char *name
= ippGetName(attr
);
2510 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
)))
2513 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2518 * 'find_job()' - Find a job specified in a request.
2521 static _ipp_job_t
* /* O - Job or NULL */
2522 find_job(_ipp_client_t
*client
) /* I - Client */
2524 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2525 _ipp_job_t key
, /* Job search key */
2526 *job
; /* Matching job, if any */
2529 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2531 const char *uri
= ippGetString(attr
, 0, NULL
);
2533 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2534 uri
[client
->printer
->urilen
] == '/')
2535 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2539 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2540 key
.id
= ippGetInteger(attr
, 0);
2542 _cupsRWLockRead(&(client
->printer
->rwlock
));
2543 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2544 _cupsRWUnlock(&(client
->printer
->rwlock
));
2551 * 'get_collection()' - Get a collection value from a file.
2554 static ipp_t
* /* O - Collection value */
2555 get_collection(FILE *fp
, /* I - File to read from */
2556 const char *filename
, /* I - Attributes filename */
2557 int *linenum
) /* IO - Line number */
2559 char token
[1024], /* Token from file */
2560 attr
[128]; /* Attribute name */
2561 ipp_tag_t value
; /* Current value type */
2562 ipp_t
*col
= ippNew(); /* Collection value */
2563 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
2566 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
2568 if (!strcmp(token
, "}"))
2570 else if (!strcmp(token
, "{") && lastcol
)
2573 * Another collection value
2576 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2577 /* Collection value */
2580 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
2584 else if (!_cups_strcasecmp(token
, "MEMBER"))
2592 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2594 fprintf(stderr
, "ippserver: Missing MEMBER value tag on line %d of \"%s\".\n", *linenum
, filename
);
2598 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
2600 fprintf(stderr
, "ippserver: Bad MEMBER value tag \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2604 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
2606 fprintf(stderr
, "ippserver: Missing MEMBER name on line %d of \"%s\".\n", *linenum
, filename
);
2610 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2612 fprintf(stderr
, "ippserver: Missing MEMBER value on line %d of \"%s\".\n", *linenum
, filename
);
2618 case IPP_TAG_BOOLEAN
:
2619 if (!_cups_strcasecmp(token
, "true"))
2620 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
2622 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
2625 case IPP_TAG_INTEGER
:
2627 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
2630 case IPP_TAG_RESOLUTION
:
2632 int xres
, /* X resolution */
2633 yres
; /* Y resolution */
2634 char units
[6]; /* Units */
2636 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
2637 (_cups_strcasecmp(units
, "dpi") &&
2638 _cups_strcasecmp(units
, "dpc") &&
2639 _cups_strcasecmp(units
, "dpcm") &&
2640 _cups_strcasecmp(units
, "other")))
2642 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2646 if (!_cups_strcasecmp(units
, "dpi"))
2647 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
2648 else if (!_cups_strcasecmp(units
, "dpc") ||
2649 !_cups_strcasecmp(units
, "dpcm"))
2650 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
2652 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
2656 case IPP_TAG_RANGE
:
2658 int lowers
[4], /* Lower value */
2659 uppers
[4], /* Upper values */
2660 num_vals
; /* Number of values */
2663 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
2664 lowers
+ 0, uppers
+ 0,
2665 lowers
+ 1, uppers
+ 1,
2666 lowers
+ 2, uppers
+ 2,
2667 lowers
+ 3, uppers
+ 3);
2669 if ((num_vals
& 1) || num_vals
== 0)
2671 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2675 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
2680 case IPP_TAG_BEGIN_COLLECTION
:
2681 if (!strcmp(token
, "{"))
2683 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2684 /* Collection value */
2688 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
2696 fprintf(stderr
, "ippserver: Bad collection value on line %d of \"%s\".\n", *linenum
, filename
);
2700 case IPP_TAG_STRING
:
2701 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
2705 if (!strchr(token
, ','))
2706 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
2710 * Multiple string values...
2713 int num_values
; /* Number of values */
2714 char *values
[100], /* Values */
2715 *ptr
; /* Pointer to next value */
2721 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
2724 values
[num_values
] = ptr
;
2726 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
2730 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
2731 NULL
, (const char **)values
);
2741 * If we get here there was a parse error; free memory and return.
2753 * 'get_token()' - Get a token from a file.
2756 static char * /* O - Token from file or NULL on EOF */
2757 get_token(FILE *fp
, /* I - File to read from */
2758 char *buf
, /* I - Buffer to read into */
2759 int buflen
, /* I - Length of buffer */
2760 int *linenum
) /* IO - Current line number */
2762 int ch
, /* Character from file */
2763 quote
; /* Quoting character */
2764 char *bufptr
, /* Pointer into buffer */
2765 *bufend
; /* End of buffer */
2771 * Skip whitespace...
2774 while (isspace(ch
= getc(fp
)))
2786 else if (ch
== '\'' || ch
== '\"')
2789 * Quoted text or regular expression...
2794 bufend
= buf
+ buflen
- 1;
2796 while ((ch
= getc(fp
)) != EOF
)
2801 * Escape next character...
2804 if (bufptr
< bufend
)
2805 *bufptr
++ = (char)ch
;
2807 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
2808 *bufptr
++ = (char)ch
;
2810 else if (ch
== quote
)
2812 else if (bufptr
< bufend
)
2813 *bufptr
++ = (char)ch
;
2826 while ((ch
= getc(fp
)) != EOF
)
2832 else if (ch
== '{' || ch
== '}' || ch
== ',')
2842 * Whitespace delimited text...
2848 bufend
= buf
+ buflen
- 1;
2850 while ((ch
= getc(fp
)) != EOF
)
2851 if (isspace(ch
) || ch
== '#')
2853 else if (bufptr
< bufend
)
2854 *bufptr
++ = (char)ch
;
2858 else if (ch
== '\n')
2870 * 'html_escape()' - Write a HTML-safe string.
2874 html_escape(_ipp_client_t
*client
, /* I - Client */
2875 const char *s
, /* I - String to write */
2876 size_t slen
) /* I - Number of characters to write */
2878 const char *start
, /* Start of segment */
2879 *end
; /* End of string */
2883 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2885 while (*s
&& s
< end
)
2887 if (*s
== '&' || *s
== '<')
2890 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2893 httpWrite2(client
->http
, "&", 5);
2895 httpWrite2(client
->http
, "<", 4);
2904 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2909 * 'html_footer()' - Show the web interface footer.
2911 * This function also writes the trailing 0-length chunk.
2915 html_footer(_ipp_client_t
*client
) /* I - Client */
2921 httpWrite2(client
->http
, "", 0);
2926 * 'html_header()' - Show the web interface header and title.
2930 html_header(_ipp_client_t
*client
, /* I - Client */
2931 const char *title
) /* I - Title */
2937 "<title>%s</title>\n"
2938 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2939 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2940 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2941 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2943 "body { font-family: sans-serif; margin: 0; }\n"
2944 "div.body { padding: 0px 10px 10px; }\n"
2945 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2946 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2947 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2948 "table.form th { text-align: right; }\n"
2949 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2950 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2951 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2952 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2953 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2954 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2955 "table.nav td { margin: 0; text-align: center; }\n"
2956 "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"
2957 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2958 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2959 "td.nav:hover { background: #666; color: #fff; }\n"
2960 "td.nav:active { background: #000; color: #ff0; }\n"
2964 "<table class=\"nav\"><tr>"
2965 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2966 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2967 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2969 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2974 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2978 html_printf(_ipp_client_t
*client
, /* I - Client */
2979 const char *format
, /* I - Printf-style format string */
2980 ...) /* I - Additional arguments as needed */
2982 va_list ap
; /* Pointer to arguments */
2983 const char *start
; /* Start of string */
2984 char size
, /* Size character (h, l, L) */
2985 type
; /* Format type character */
2986 int width
, /* Width of field */
2987 prec
; /* Number of characters of precision */
2988 char tformat
[100], /* Temporary format string for sprintf() */
2989 *tptr
, /* Pointer into temporary format */
2990 temp
[1024]; /* Buffer for formatted numbers */
2991 char *s
; /* Pointer to string */
2995 * Loop through the format string, formatting as needed...
2998 va_start(ap
, format
);
3006 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
3009 *tptr
++ = *format
++;
3013 httpWrite2(client
->http
, "%", 1);
3018 else if (strchr(" -+#\'", *format
))
3019 *tptr
++ = *format
++;
3024 * Get width from argument...
3028 width
= va_arg(ap
, int);
3030 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
3031 tptr
+= strlen(tptr
);
3037 while (isdigit(*format
& 255))
3039 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3042 width
= width
* 10 + *format
++ - '0';
3048 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3056 * Get precision from argument...
3060 prec
= va_arg(ap
, int);
3062 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
3063 tptr
+= strlen(tptr
);
3069 while (isdigit(*format
& 255))
3071 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3074 prec
= prec
* 10 + *format
++ - '0';
3079 if (*format
== 'l' && format
[1] == 'l')
3083 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
3091 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
3093 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3108 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3117 case 'E' : /* Floating point formats */
3122 if ((size_t)(width
+ 2) > sizeof(temp
))
3125 sprintf(temp
, tformat
, va_arg(ap
, double));
3127 httpWrite2(client
->http
, temp
, strlen(temp
));
3130 case 'B' : /* Integer formats */
3138 if ((size_t)(width
+ 2) > sizeof(temp
))
3141 # ifdef HAVE_LONG_LONG
3143 sprintf(temp
, tformat
, va_arg(ap
, long long));
3145 # endif /* HAVE_LONG_LONG */
3147 sprintf(temp
, tformat
, va_arg(ap
, long));
3149 sprintf(temp
, tformat
, va_arg(ap
, int));
3151 httpWrite2(client
->http
, temp
, strlen(temp
));
3154 case 'p' : /* Pointer value */
3155 if ((size_t)(width
+ 2) > sizeof(temp
))
3158 sprintf(temp
, tformat
, va_arg(ap
, void *));
3160 httpWrite2(client
->http
, temp
, strlen(temp
));
3163 case 'c' : /* Character or character array */
3166 temp
[0] = (char)va_arg(ap
, int);
3168 html_escape(client
, temp
, 1);
3171 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
3174 case 's' : /* String */
3175 if ((s
= va_arg(ap
, char *)) == NULL
)
3178 html_escape(client
, s
, strlen(s
));
3187 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
3194 * 'ipp_cancel_job()' - Cancel a job.
3198 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
3200 _ipp_job_t
*job
; /* Job information */
3207 if ((job
= find_job(client
)) == NULL
)
3209 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3214 * See if the job is already completed, canceled, or aborted; if so,
3215 * we can't cancel...
3220 case IPP_JSTATE_CANCELED
:
3221 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3222 "Job #%d is already canceled - can\'t cancel.", job
->id
);
3225 case IPP_JSTATE_ABORTED
:
3226 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3227 "Job #%d is already aborted - can\'t cancel.", job
->id
);
3230 case IPP_JSTATE_COMPLETED
:
3231 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3232 "Job #%d is already completed - can\'t cancel.", job
->id
);
3240 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3242 if (job
->state
== IPP_JSTATE_PROCESSING
||
3243 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
3247 job
->state
= IPP_JSTATE_CANCELED
;
3248 job
->completed
= time(NULL
);
3251 _cupsRWUnlock(&(client
->printer
->rwlock
));
3253 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3260 * 'ipp_close_job()' - Close an open job.
3264 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
3266 _ipp_job_t
*job
; /* Job information */
3273 if ((job
= find_job(client
)) == NULL
)
3275 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3280 * See if the job is already completed, canceled, or aborted; if so,
3281 * we can't cancel...
3286 case IPP_JSTATE_CANCELED
:
3287 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3288 "Job #%d is canceled - can\'t close.", job
->id
);
3291 case IPP_JSTATE_ABORTED
:
3292 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3293 "Job #%d is aborted - can\'t close.", job
->id
);
3296 case IPP_JSTATE_COMPLETED
:
3297 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3298 "Job #%d is completed - can\'t close.", job
->id
);
3301 case IPP_JSTATE_PROCESSING
:
3302 case IPP_JSTATE_STOPPED
:
3303 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3304 "Job #%d is already closed.", job
->id
);
3308 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3315 * 'ipp_create_job()' - Create a job object.
3319 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
3321 _ipp_job_t
*job
; /* New job */
3322 cups_array_t
*ra
; /* Attributes to send in response */
3326 * Validate print job attributes...
3329 if (!valid_job_attributes(client
))
3331 httpFlush(client
->http
);
3336 * Do we have a file to print?
3339 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3341 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3342 "Unexpected document data following request.");
3350 if ((job
= create_job(client
)) == NULL
)
3352 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3353 "Currently printing another job.");
3358 * Return the job info...
3361 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3363 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3364 cupsArrayAdd(ra
, "job-id");
3365 cupsArrayAdd(ra
, "job-state");
3366 cupsArrayAdd(ra
, "job-state-message");
3367 cupsArrayAdd(ra
, "job-state-reasons");
3368 cupsArrayAdd(ra
, "job-uri");
3370 copy_job_attributes(client
, job
, ra
);
3371 cupsArrayDelete(ra
);
3376 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3380 ipp_get_job_attributes(
3381 _ipp_client_t
*client
) /* I - Client */
3383 _ipp_job_t
*job
; /* Job */
3384 cups_array_t
*ra
; /* requested-attributes */
3387 if ((job
= find_job(client
)) == NULL
)
3389 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3393 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3395 ra
= ippCreateRequestedArray(client
->request
);
3396 copy_job_attributes(client
, job
, ra
);
3397 cupsArrayDelete(ra
);
3402 * 'ipp_get_jobs()' - Get a list of job objects.
3406 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
3408 ipp_attribute_t
*attr
; /* Current attribute */
3409 const char *which_jobs
= NULL
;
3410 /* which-jobs values */
3411 int job_comparison
; /* Job comparison */
3412 ipp_jstate_t job_state
; /* job-state value */
3413 int first_job_id
, /* First job ID */
3414 limit
, /* Maximum number of jobs to return */
3415 count
; /* Number of jobs that match */
3416 const char *username
; /* Username */
3417 _ipp_job_t
*job
; /* Current job pointer */
3418 cups_array_t
*ra
; /* Requested attributes array */
3422 * See if the "which-jobs" attribute have been specified...
3425 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3426 IPP_TAG_KEYWORD
)) != NULL
)
3428 which_jobs
= ippGetString(attr
, 0, NULL
);
3429 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3432 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3434 job_comparison
= -1;
3435 job_state
= IPP_JSTATE_STOPPED
;
3437 else if (!strcmp(which_jobs
, "completed"))
3440 job_state
= IPP_JSTATE_CANCELED
;
3442 else if (!strcmp(which_jobs
, "aborted"))
3445 job_state
= IPP_JSTATE_ABORTED
;
3447 else if (!strcmp(which_jobs
, "all"))
3450 job_state
= IPP_JSTATE_PENDING
;
3452 else if (!strcmp(which_jobs
, "canceled"))
3455 job_state
= IPP_JSTATE_CANCELED
;
3457 else if (!strcmp(which_jobs
, "pending"))
3460 job_state
= IPP_JSTATE_PENDING
;
3462 else if (!strcmp(which_jobs
, "pending-held"))
3465 job_state
= IPP_JSTATE_HELD
;
3467 else if (!strcmp(which_jobs
, "processing"))
3470 job_state
= IPP_JSTATE_PROCESSING
;
3472 else if (!strcmp(which_jobs
, "processing-stopped"))
3475 job_state
= IPP_JSTATE_STOPPED
;
3479 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3480 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3481 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3482 "which-jobs", NULL
, which_jobs
);
3487 * See if they want to limit the number of jobs reported...
3490 if ((attr
= ippFindAttribute(client
->request
, "limit",
3491 IPP_TAG_INTEGER
)) != NULL
)
3493 limit
= ippGetInteger(attr
, 0);
3495 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3500 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3501 IPP_TAG_INTEGER
)) != NULL
)
3503 first_job_id
= ippGetInteger(attr
, 0);
3505 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3512 * See if we only want to see jobs for a specific user...
3517 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3518 IPP_TAG_BOOLEAN
)) != NULL
)
3520 int my_jobs
= ippGetBoolean(attr
, 0);
3522 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3523 my_jobs
? "true" : "false");
3527 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3528 IPP_TAG_NAME
)) == NULL
)
3530 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3531 "Need requesting-user-name with my-jobs.");
3535 username
= ippGetString(attr
, 0, NULL
);
3537 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3538 client
->hostname
, username
);
3543 * OK, build a list of jobs for this printer...
3546 ra
= ippCreateRequestedArray(client
->request
);
3548 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3550 _cupsRWLockRead(&(client
->printer
->rwlock
));
3552 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3553 (limit
<= 0 || count
< limit
) && job
;
3554 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3557 * Filter out jobs that don't match...
3560 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3561 (job_comparison
== 0 && job
->state
!= job_state
) ||
3562 (job_comparison
> 0 && job
->state
< job_state
) ||
3563 job
->id
< first_job_id
||
3564 (username
&& job
->username
&&
3565 strcasecmp(username
, job
->username
)))
3569 ippAddSeparator(client
->response
);
3572 copy_job_attributes(client
, job
, ra
);
3575 cupsArrayDelete(ra
);
3577 _cupsRWUnlock(&(client
->printer
->rwlock
));
3582 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3586 ipp_get_printer_attributes(
3587 _ipp_client_t
*client
) /* I - Client */
3589 cups_array_t
*ra
; /* Requested attributes array */
3590 _ipp_printer_t
*printer
; /* Printer */
3594 * Send the attributes...
3597 ra
= ippCreateRequestedArray(client
->request
);
3598 printer
= client
->printer
;
3600 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3602 _cupsRWLockRead(&(printer
->rwlock
));
3604 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3605 IPP_TAG_CUPS_CONST
);
3607 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3609 int i
, /* Looping var */
3610 num_ready
= 0; /* Number of ready media */
3611 ipp_t
*ready
[3]; /* Ready media */
3613 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3615 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
3616 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);
3618 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);
3620 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3621 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);
3622 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3624 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
3625 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);
3627 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);
3632 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3633 for (i
= 0; i
< num_ready
; i
++)
3634 ippDelete(ready
[i
]);
3637 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3640 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3642 int num_ready
= 0; /* Number of ready media */
3643 const char *ready
[3]; /* Ready media */
3645 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3646 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3648 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3649 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3651 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3652 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3655 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3657 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3660 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3661 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3663 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3664 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3666 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3667 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3670 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3671 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3672 "printer-state", printer
->state
);
3674 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3675 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3677 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3678 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3680 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3682 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3684 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3687 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3689 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3690 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3691 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3692 "printer-state-reasons", NULL
, "none");
3695 ipp_attribute_t
*attr
= NULL
; /* printer-state-reasons */
3696 _ipp_preason_t bit
; /* Reason bit */
3697 int i
; /* Looping var */
3698 char reason
[32]; /* Reason string */
3700 for (i
= 0, bit
= 1; i
< (int)(sizeof(_ipp_preason_strings
) / sizeof(_ipp_preason_strings
[0])); i
++, bit
*= 2)
3702 if (printer
->state_reasons
& bit
)
3704 snprintf(reason
, sizeof(reason
), "%s-%s", _ipp_preason_strings
[i
], printer
->state
== IPP_PSTATE_IDLE
? "report" : printer
->state
== IPP_PSTATE_PROCESSING
? "warning" : "error");
3706 ippSetString(client
->response
, &attr
, ippGetCount(attr
), reason
);
3708 attr
= ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
, reason
);
3714 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3716 int i
; /* Looping var */
3717 char buffer
[256]; /* Supply value buffer */
3718 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3719 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3721 for (i
= 0; i
< 5; i
++)
3723 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
]);
3726 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3728 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3732 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3733 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3735 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3736 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3737 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3739 _cupsRWUnlock(&(printer
->rwlock
));
3741 cupsArrayDelete(ra
);
3746 * 'ipp_identify_printer()' - Beep or display a message.
3750 ipp_identify_printer(
3751 _ipp_client_t
*client
) /* I - Client */
3753 ipp_attribute_t
*actions
, /* identify-actions */
3754 *message
; /* message */
3757 actions
= ippFindAttribute(client
->request
, "identify-actions", IPP_TAG_KEYWORD
);
3758 message
= ippFindAttribute(client
->request
, "message", IPP_TAG_TEXT
);
3760 if (!actions
|| ippContainsString(actions
, "sound"))
3766 if (ippContainsString(actions
, "display"))
3767 printf("IDENTIFY from %s: %s\n", client
->hostname
, message
? ippGetString(message
, 0, NULL
) : "No message supplied");
3769 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3774 * 'ipp_print_job()' - Create a job object with an attached document.
3778 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3780 _ipp_job_t
*job
; /* New job */
3781 char filename
[1024], /* Filename buffer */
3782 buffer
[4096]; /* Copy buffer */
3783 ssize_t bytes
; /* Bytes read */
3784 cups_array_t
*ra
; /* Attributes to send in response */
3785 _cups_thread_t t
; /* Thread */
3789 * Validate print job attributes...
3792 if (!valid_job_attributes(client
))
3794 httpFlush(client
->http
);
3799 * Do we have a file to print?
3802 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3804 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3812 if ((job
= create_job(client
)) == NULL
)
3814 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3815 "Currently printing another job.");
3820 * Create a file for the request data...
3823 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3826 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3828 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3830 job
->state
= IPP_JSTATE_ABORTED
;
3832 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3833 "Unable to create print file: %s", strerror(errno
));
3837 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3839 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3841 int error
= errno
; /* Write error */
3843 job
->state
= IPP_JSTATE_ABORTED
;
3850 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3851 "Unable to write print file: %s", strerror(error
));
3859 * Got an error while reading the print data, so abort this job.
3862 job
->state
= IPP_JSTATE_ABORTED
;
3869 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3870 "Unable to read print file.");
3876 int error
= errno
; /* Write error */
3878 job
->state
= IPP_JSTATE_ABORTED
;
3883 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3884 "Unable to write print file: %s", strerror(error
));
3889 job
->filename
= strdup(filename
);
3890 job
->state
= IPP_JSTATE_PENDING
;
3893 * Process the job...
3896 t
= _cupsThreadCreate((_cups_thread_func_t
)process_job
, job
);
3900 _cupsThreadDetach(t
);
3904 job
->state
= IPP_JSTATE_ABORTED
;
3905 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3910 * Return the job info...
3913 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3915 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3916 cupsArrayAdd(ra
, "job-id");
3917 cupsArrayAdd(ra
, "job-state");
3918 cupsArrayAdd(ra
, "job-state-message");
3919 cupsArrayAdd(ra
, "job-state-reasons");
3920 cupsArrayAdd(ra
, "job-uri");
3922 copy_job_attributes(client
, job
, ra
);
3923 cupsArrayDelete(ra
);
3928 * 'ipp_print_uri()' - Create a job object with a referenced document.
3932 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3934 _ipp_job_t
*job
; /* New job */
3935 ipp_attribute_t
*uri
; /* document-uri */
3936 char scheme
[256], /* URI scheme */
3937 userpass
[256], /* Username and password info */
3938 hostname
[256], /* Hostname */
3939 resource
[1024]; /* Resource path */
3940 int port
; /* Port number */
3941 http_uri_status_t uri_status
; /* URI decode status */
3942 http_encryption_t encryption
; /* Encryption to use, if any */
3943 http_t
*http
; /* Connection for http/https URIs */
3944 http_status_t status
; /* Access status for http/https URIs */
3945 int infile
; /* Input file for local file URIs */
3946 char filename
[1024], /* Filename buffer */
3947 buffer
[4096]; /* Copy buffer */
3948 ssize_t bytes
; /* Bytes read */
3949 cups_array_t
*ra
; /* Attributes to send in response */
3950 static const char * const uri_status_strings
[] =
3951 { /* URI decode errors */
3953 "Bad arguments to function.",
3954 "Bad resource in URI.",
3955 "Bad port number in URI.",
3956 "Bad hostname in URI.",
3957 "Bad username in URI.",
3958 "Bad scheme in URI.",
3964 * Validate print job attributes...
3967 if (!valid_job_attributes(client
))
3969 httpFlush(client
->http
);
3974 * Do we have a file to print?
3977 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3979 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3980 "Unexpected document data following request.");
3985 * Do we have a document URI?
3988 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3989 IPP_TAG_URI
)) == NULL
)
3991 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3995 if (ippGetCount(uri
) != 1)
3997 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3998 "Too many document-uri values.");
4002 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4003 scheme
, sizeof(scheme
), userpass
,
4004 sizeof(userpass
), hostname
, sizeof(hostname
),
4005 &port
, resource
, sizeof(resource
));
4006 if (uri_status
< HTTP_URI_STATUS_OK
)
4008 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4009 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4013 if (strcmp(scheme
, "file") &&
4015 strcmp(scheme
, "https") &&
4016 #endif /* HAVE_SSL */
4017 strcmp(scheme
, "http"))
4019 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4020 "URI scheme \"%s\" not supported.", scheme
);
4024 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4026 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4027 "Unable to access URI: %s", strerror(errno
));
4035 if ((job
= create_job(client
)) == NULL
)
4037 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
4038 "Currently printing another job.");
4043 * Create a file for the request data...
4046 if (!strcasecmp(job
->format
, "image/jpeg"))
4047 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4048 client
->printer
->directory
, job
->id
);
4049 else if (!strcasecmp(job
->format
, "image/png"))
4050 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4051 client
->printer
->directory
, job
->id
);
4052 else if (!strcasecmp(job
->format
, "application/pdf"))
4053 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4054 client
->printer
->directory
, job
->id
);
4055 else if (!strcasecmp(job
->format
, "application/postscript"))
4056 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4057 client
->printer
->directory
, job
->id
);
4059 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4060 client
->printer
->directory
, job
->id
);
4062 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
4064 job
->state
= IPP_JSTATE_ABORTED
;
4066 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4067 "Unable to create print file: %s", strerror(errno
));
4071 if (!strcmp(scheme
, "file"))
4073 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4075 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4076 "Unable to access URI: %s", strerror(errno
));
4082 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4083 (errno
== EAGAIN
|| errno
== EINTR
))
4085 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4087 int error
= errno
; /* Write error */
4089 job
->state
= IPP_JSTATE_ABORTED
;
4097 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4098 "Unable to write print file: %s", strerror(error
));
4109 if (port
== 443 || !strcmp(scheme
, "https"))
4110 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4112 #endif /* HAVE_SSL */
4113 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4115 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4116 1, 30000, NULL
)) == NULL
)
4118 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4119 "Unable to connect to %s: %s", hostname
,
4120 cupsLastErrorString());
4121 job
->state
= IPP_JSTATE_ABORTED
;
4130 httpClearFields(http
);
4131 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4132 if (httpGet(http
, resource
))
4134 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4135 "Unable to GET URI: %s", strerror(errno
));
4137 job
->state
= IPP_JSTATE_ABORTED
;
4147 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4149 if (status
!= HTTP_STATUS_OK
)
4151 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4152 "Unable to GET URI: %s", httpStatus(status
));
4154 job
->state
= IPP_JSTATE_ABORTED
;
4164 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4166 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4168 int error
= errno
; /* Write error */
4170 job
->state
= IPP_JSTATE_ABORTED
;
4178 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4179 "Unable to write print file: %s", strerror(error
));
4189 int error
= errno
; /* Write error */
4191 job
->state
= IPP_JSTATE_ABORTED
;
4196 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4197 "Unable to write print file: %s", strerror(error
));
4202 job
->filename
= strdup(filename
);
4203 job
->state
= IPP_JSTATE_PENDING
;
4206 * Process the job...
4212 * Return the job info...
4215 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4217 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4218 cupsArrayAdd(ra
, "job-id");
4219 cupsArrayAdd(ra
, "job-state");
4220 cupsArrayAdd(ra
, "job-state-reasons");
4221 cupsArrayAdd(ra
, "job-uri");
4223 copy_job_attributes(client
, job
, ra
);
4224 cupsArrayDelete(ra
);
4229 * 'ipp_send_document()' - Add an attached document to a job object created with
4234 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
4236 _ipp_job_t
*job
; /* Job information */
4237 char filename
[1024], /* Filename buffer */
4238 buffer
[4096]; /* Copy buffer */
4239 ssize_t bytes
; /* Bytes read */
4240 ipp_attribute_t
*attr
; /* Current attribute */
4241 cups_array_t
*ra
; /* Attributes to send in response */
4248 if ((job
= find_job(client
)) == NULL
)
4250 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4251 httpFlush(client
->http
);
4256 * See if we already have a document for this job or the job has already
4257 * in a non-pending state...
4260 if (job
->state
> IPP_JSTATE_HELD
)
4262 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4263 "Job is not in a pending state.");
4264 httpFlush(client
->http
);
4267 else if (job
->filename
|| job
->fd
>= 0)
4269 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4270 "Multiple document jobs are not supported.");
4271 httpFlush(client
->http
);
4275 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4276 IPP_TAG_ZERO
)) == NULL
)
4278 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4279 "Missing required last-document attribute.");
4280 httpFlush(client
->http
);
4283 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4284 !ippGetBoolean(attr
, 0))
4286 respond_unsupported(client
, attr
);
4287 httpFlush(client
->http
);
4292 * Validate document attributes...
4295 if (!valid_doc_attributes(client
))
4297 httpFlush(client
->http
);
4301 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
4304 * Get the document format for the job...
4307 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4309 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
4310 job
->format
= ippGetString(attr
, 0, NULL
);
4311 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
4312 job
->format
= ippGetString(attr
, 0, NULL
);
4314 job
->format
= "application/octet-stream";
4317 * Create a file for the request data...
4320 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
4323 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
4325 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4327 _cupsRWUnlock(&(client
->printer
->rwlock
));
4331 job
->state
= IPP_JSTATE_ABORTED
;
4333 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4334 "Unable to create print file: %s", strerror(errno
));
4338 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
4340 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4342 int error
= errno
; /* Write error */
4344 job
->state
= IPP_JSTATE_ABORTED
;
4351 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4352 "Unable to write print file: %s", strerror(error
));
4360 * Got an error while reading the print data, so abort this job.
4363 job
->state
= IPP_JSTATE_ABORTED
;
4370 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4371 "Unable to read print file.");
4377 int error
= errno
; /* Write error */
4379 job
->state
= IPP_JSTATE_ABORTED
;
4384 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4385 "Unable to write print file: %s", strerror(error
));
4389 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4392 job
->filename
= strdup(filename
);
4393 job
->state
= IPP_JSTATE_PENDING
;
4395 _cupsRWUnlock(&(client
->printer
->rwlock
));
4398 * Process the job...
4404 * Return the job info...
4407 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4409 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4410 cupsArrayAdd(ra
, "job-id");
4411 cupsArrayAdd(ra
, "job-state");
4412 cupsArrayAdd(ra
, "job-state-reasons");
4413 cupsArrayAdd(ra
, "job-uri");
4415 copy_job_attributes(client
, job
, ra
);
4416 cupsArrayDelete(ra
);
4421 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4426 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
4428 _ipp_job_t
*job
; /* Job information */
4429 ipp_attribute_t
*uri
; /* document-uri */
4430 char scheme
[256], /* URI scheme */
4431 userpass
[256], /* Username and password info */
4432 hostname
[256], /* Hostname */
4433 resource
[1024]; /* Resource path */
4434 int port
; /* Port number */
4435 http_uri_status_t uri_status
; /* URI decode status */
4436 http_encryption_t encryption
; /* Encryption to use, if any */
4437 http_t
*http
; /* Connection for http/https URIs */
4438 http_status_t status
; /* Access status for http/https URIs */
4439 int infile
; /* Input file for local file URIs */
4440 char filename
[1024], /* Filename buffer */
4441 buffer
[4096]; /* Copy buffer */
4442 ssize_t bytes
; /* Bytes read */
4443 ipp_attribute_t
*attr
; /* Current attribute */
4444 cups_array_t
*ra
; /* Attributes to send in response */
4445 static const char * const uri_status_strings
[] =
4446 { /* URI decode errors */
4448 "Bad arguments to function.",
4449 "Bad resource in URI.",
4450 "Bad port number in URI.",
4451 "Bad hostname in URI.",
4452 "Bad username in URI.",
4453 "Bad scheme in URI.",
4462 if ((job
= find_job(client
)) == NULL
)
4464 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4465 httpFlush(client
->http
);
4470 * See if we already have a document for this job or the job has already
4471 * in a non-pending state...
4474 if (job
->state
> IPP_JSTATE_HELD
)
4476 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4477 "Job is not in a pending state.");
4478 httpFlush(client
->http
);
4481 else if (job
->filename
|| job
->fd
>= 0)
4483 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4484 "Multiple document jobs are not supported.");
4485 httpFlush(client
->http
);
4489 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4490 IPP_TAG_ZERO
)) == NULL
)
4492 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4493 "Missing required last-document attribute.");
4494 httpFlush(client
->http
);
4497 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4498 !ippGetBoolean(attr
, 0))
4500 respond_unsupported(client
, attr
);
4501 httpFlush(client
->http
);
4506 * Validate document attributes...
4509 if (!valid_doc_attributes(client
))
4511 httpFlush(client
->http
);
4516 * Do we have a file to print?
4519 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4521 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4522 "Unexpected document data following request.");
4527 * Do we have a document URI?
4530 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4531 IPP_TAG_URI
)) == NULL
)
4533 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4537 if (ippGetCount(uri
) != 1)
4539 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4540 "Too many document-uri values.");
4544 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4545 scheme
, sizeof(scheme
), userpass
,
4546 sizeof(userpass
), hostname
, sizeof(hostname
),
4547 &port
, resource
, sizeof(resource
));
4548 if (uri_status
< HTTP_URI_STATUS_OK
)
4550 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4551 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4555 if (strcmp(scheme
, "file") &&
4557 strcmp(scheme
, "https") &&
4558 #endif /* HAVE_SSL */
4559 strcmp(scheme
, "http"))
4561 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4562 "URI scheme \"%s\" not supported.", scheme
);
4566 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4568 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4569 "Unable to access URI: %s", strerror(errno
));
4574 * Get the document format for the job...
4577 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4579 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4580 IPP_TAG_MIMETYPE
)) != NULL
)
4581 job
->format
= ippGetString(attr
, 0, NULL
);
4583 job
->format
= "application/octet-stream";
4586 * Create a file for the request data...
4589 if (!strcasecmp(job
->format
, "image/jpeg"))
4590 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4591 client
->printer
->directory
, job
->id
);
4592 else if (!strcasecmp(job
->format
, "image/png"))
4593 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4594 client
->printer
->directory
, job
->id
);
4595 else if (!strcasecmp(job
->format
, "application/pdf"))
4596 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4597 client
->printer
->directory
, job
->id
);
4598 else if (!strcasecmp(job
->format
, "application/postscript"))
4599 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4600 client
->printer
->directory
, job
->id
);
4602 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4603 client
->printer
->directory
, job
->id
);
4605 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4607 _cupsRWUnlock(&(client
->printer
->rwlock
));
4611 job
->state
= IPP_JSTATE_ABORTED
;
4613 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4614 "Unable to create print file: %s", strerror(errno
));
4618 if (!strcmp(scheme
, "file"))
4620 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4622 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4623 "Unable to access URI: %s", strerror(errno
));
4629 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4630 (errno
== EAGAIN
|| errno
== EINTR
))
4632 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4634 int error
= errno
; /* Write error */
4636 job
->state
= IPP_JSTATE_ABORTED
;
4644 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4645 "Unable to write print file: %s", strerror(error
));
4656 if (port
== 443 || !strcmp(scheme
, "https"))
4657 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4659 #endif /* HAVE_SSL */
4660 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4662 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4663 1, 30000, NULL
)) == NULL
)
4665 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4666 "Unable to connect to %s: %s", hostname
,
4667 cupsLastErrorString());
4668 job
->state
= IPP_JSTATE_ABORTED
;
4677 httpClearFields(http
);
4678 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4679 if (httpGet(http
, resource
))
4681 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4682 "Unable to GET URI: %s", strerror(errno
));
4684 job
->state
= IPP_JSTATE_ABORTED
;
4694 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4696 if (status
!= HTTP_STATUS_OK
)
4698 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4699 "Unable to GET URI: %s", httpStatus(status
));
4701 job
->state
= IPP_JSTATE_ABORTED
;
4711 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4713 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4715 int error
= errno
; /* Write error */
4717 job
->state
= IPP_JSTATE_ABORTED
;
4725 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4726 "Unable to write print file: %s", strerror(error
));
4736 int error
= errno
; /* Write error */
4738 job
->state
= IPP_JSTATE_ABORTED
;
4743 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4744 "Unable to write print file: %s", strerror(error
));
4748 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4751 job
->filename
= strdup(filename
);
4752 job
->state
= IPP_JSTATE_PENDING
;
4754 _cupsRWUnlock(&(client
->printer
->rwlock
));
4757 * Process the job...
4763 * Return the job info...
4766 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4768 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4769 cupsArrayAdd(ra
, "job-id");
4770 cupsArrayAdd(ra
, "job-state");
4771 cupsArrayAdd(ra
, "job-state-reasons");
4772 cupsArrayAdd(ra
, "job-uri");
4774 copy_job_attributes(client
, job
, ra
);
4775 cupsArrayDelete(ra
);
4780 * 'ipp_validate_job()' - Validate job creation attributes.
4784 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4786 if (valid_job_attributes(client
))
4787 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4792 * 'load_attributes()' - Load printer attributes from a file.
4794 * Syntax is based on ipptool format:
4796 * ATTR value-tag name value
4800 load_attributes(const char *filename
, /* I - File to load */
4801 ipp_t
*attrs
) /* I - Printer attributes */
4803 int linenum
= 0; /* Current line number */
4804 FILE *fp
= NULL
; /* Test file */
4805 char attr
[128], /* Attribute name */
4806 token
[1024], /* Token from file */
4807 *tokenptr
; /* Pointer into token */
4808 ipp_tag_t value
; /* Current value type */
4809 ipp_attribute_t
*attrptr
, /* Attribute pointer */
4810 *lastcol
= NULL
; /* Last collection attribute */
4813 if ((fp
= fopen(filename
, "r")) == NULL
)
4815 fprintf(stderr
, "ippserver: Unable to open \"%s\": %s\n", filename
, strerror(errno
));
4819 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
4821 if (!_cups_strcasecmp(token
, "ATTR"))
4827 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4829 fprintf(stderr
, "ippserver: Missing ATTR value tag on line %d of \"%s\".\n", linenum
, filename
);
4833 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
4835 fprintf(stderr
, "ippserver: Bad ATTR value tag \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4839 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
4841 fprintf(stderr
, "ippserver: Missing ATTR name on line %d of \"%s\".\n", linenum
, filename
);
4845 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4847 fprintf(stderr
, "ippserver: Missing ATTR value on line %d of \"%s\".\n", linenum
, filename
);
4855 case IPP_TAG_BOOLEAN
:
4856 if (!_cups_strcasecmp(token
, "true"))
4857 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, 1);
4859 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, (char)atoi(token
));
4862 case IPP_TAG_INTEGER
:
4864 if (!strchr(token
, ','))
4865 attrptr
= ippAddInteger(attrs
, IPP_TAG_PRINTER
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
4868 int values
[100], /* Values */
4869 num_values
= 1; /* Number of values */
4871 values
[0] = (int)strtol(token
, &tokenptr
, 10);
4872 while (tokenptr
&& *tokenptr
&&
4873 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
4875 if (*tokenptr
== ',')
4877 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
4880 values
[num_values
] = (int)strtol(tokenptr
, &tokenptr
, 0);
4884 attrptr
= ippAddIntegers(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, values
);
4887 if (!tokenptr
|| *tokenptr
)
4889 fprintf(stderr
, "ippserver: Bad %s value \"%s\" on line %d of \"%s\".\n", ippTagString(value
), token
, linenum
, filename
);
4894 case IPP_TAG_RESOLUTION
:
4896 int xres
, /* X resolution */
4897 yres
; /* Y resolution */
4898 ipp_res_t units
; /* Units */
4899 char *start
, /* Start of value */
4900 *ptr
, /* Pointer into value */
4901 *next
= NULL
; /* Next value */
4903 for (start
= token
; start
; start
= next
)
4905 xres
= yres
= (int)strtol(start
, (char **)&ptr
, 10);
4906 if (ptr
> start
&& xres
> 0)
4909 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
4912 if (ptr
&& (next
= strchr(ptr
, ',')) != NULL
)
4915 if (ptr
<= start
|| xres
<= 0 || yres
<= 0 || !ptr
||
4916 (_cups_strcasecmp(ptr
, "dpi") &&
4917 _cups_strcasecmp(ptr
, "dpc") &&
4918 _cups_strcasecmp(ptr
, "dpcm") &&
4919 _cups_strcasecmp(ptr
, "other")))
4921 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4925 if (!_cups_strcasecmp(ptr
, "dpc") || !_cups_strcasecmp(ptr
, "dpcm"))
4926 units
= IPP_RES_PER_CM
;
4928 units
= IPP_RES_PER_INCH
;
4931 ippSetResolution(attrs
, &attrptr
, ippGetCount(attrptr
), units
, xres
, yres
);
4933 attrptr
= ippAddResolution(attrs
, IPP_TAG_PRINTER
, attr
, units
, xres
, yres
);
4938 case IPP_TAG_RANGE
:
4940 int lowers
[4], /* Lower value */
4941 uppers
[4], /* Upper values */
4942 num_vals
; /* Number of values */
4945 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
4946 lowers
+ 0, uppers
+ 0,
4947 lowers
+ 1, uppers
+ 1,
4948 lowers
+ 2, uppers
+ 2,
4949 lowers
+ 3, uppers
+ 3);
4951 if ((num_vals
& 1) || num_vals
== 0)
4953 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", token
, linenum
, filename
);
4957 attrptr
= ippAddRanges(attrs
, IPP_TAG_PRINTER
, attr
, num_vals
/ 2, lowers
,
4962 case IPP_TAG_BEGIN_COLLECTION
:
4963 if (!strcmp(token
, "{"))
4965 ipp_t
*col
= get_collection(fp
, filename
, &linenum
);
4966 /* Collection value */
4970 attrptr
= lastcol
= ippAddCollection(attrs
, IPP_TAG_PRINTER
, attr
, col
);
4978 fprintf(stderr
, "ippserver: Bad ATTR collection value on line %d of \"%s\".\n", linenum
, filename
);
4984 ipp_t
*col
; /* Collection value */
4985 long pos
= ftell(fp
); /* Save position of file */
4987 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4990 if (strcmp(token
, ","))
4992 fseek(fp
, pos
, SEEK_SET
);
4996 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
4998 fprintf(stderr
, "ippserver: Unexpected \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
5002 if ((col
= get_collection(fp
, filename
, &linenum
)) == NULL
)
5005 ippSetCollection(attrs
, &attrptr
, ippGetCount(attrptr
), col
);
5007 while (!strcmp(token
, "{"));
5010 case IPP_TAG_STRING
:
5011 attrptr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, attr
, token
, (int)strlen(token
));
5015 fprintf(stderr
, "ippserver: Unsupported ATTR value tag %s on line %d of \"%s\".\n", ippTagString(value
), linenum
, filename
);
5018 case IPP_TAG_TEXTLANG
:
5019 case IPP_TAG_NAMELANG
:
5022 case IPP_TAG_KEYWORD
:
5024 case IPP_TAG_URISCHEME
:
5025 case IPP_TAG_CHARSET
:
5026 case IPP_TAG_LANGUAGE
:
5027 case IPP_TAG_MIMETYPE
:
5028 if (!strchr(token
, ','))
5029 attrptr
= ippAddString(attrs
, IPP_TAG_PRINTER
, value
, attr
, NULL
, token
);
5033 * Multiple string values...
5036 int num_values
; /* Number of values */
5037 char *values
[100], /* Values */
5038 *ptr
; /* Pointer to next value */
5044 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
5046 if (ptr
> token
&& ptr
[-1] == '\\')
5047 _cups_strcpy(ptr
- 1, ptr
);
5051 values
[num_values
] = ptr
;
5053 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
5058 attrptr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, NULL
, (const char **)values
);
5065 fprintf(stderr
, "ippserver: Unable to add attribute on line %d of \"%s\": %s\n", linenum
, filename
, cupsLastErrorString());
5071 fprintf(stderr
, "ippserver: Unknown directive \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
5081 * 'parse_options()' - Parse URL options into CUPS options.
5083 * The client->options string is destroyed by this function.
5086 static int /* O - Number of options */
5087 parse_options(_ipp_client_t
*client
, /* I - Client */
5088 cups_option_t
**options
) /* O - Options */
5090 char *name
, /* Name */
5092 *next
; /* Next name=value pair */
5093 int num_options
= 0; /* Number of options */
5098 for (name
= client
->options
; name
&& *name
; name
= next
)
5100 if ((value
= strchr(name
, '=')) == NULL
)
5104 if ((next
= strchr(value
, '&')) != NULL
)
5107 num_options
= cupsAddOption(name
, value
, num_options
, options
);
5110 return (num_options
);
5115 * 'process_attr_message()' - Process an ATTR: message from a command.
5119 process_attr_message(
5120 _ipp_job_t
*job
, /* I - Job */
5121 char *message
) /* I - Message */
5129 * 'process_client()' - Process client requests on a thread.
5132 static void * /* O - Exit status */
5133 process_client(_ipp_client_t
*client
) /* I - Client */
5136 * Loop until we are out of requests or timeout (30 seconds)...
5140 int first_time
= 1; /* First time request? */
5141 #endif /* HAVE_SSL */
5143 while (httpWait(client
->http
, 30000))
5149 * See if we need to negotiate a TLS connection...
5152 char buf
[1]; /* First byte from client */
5154 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
5156 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
5158 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
5160 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5164 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5169 #endif /* HAVE_SSL */
5171 if (!process_http(client
))
5176 * Close the conection to the client and return...
5179 delete_client(client
);
5186 * 'process_http()' - Process a HTTP request.
5189 int /* O - 1 on success, 0 on failure */
5190 process_http(_ipp_client_t
*client
) /* I - Client connection */
5192 char uri
[1024]; /* URI */
5193 http_state_t http_state
; /* HTTP state */
5194 http_status_t http_status
; /* HTTP status */
5195 ipp_state_t ipp_state
; /* State of IPP transfer */
5196 char scheme
[32], /* Method/scheme */
5197 userpass
[128], /* Username:password */
5198 hostname
[HTTP_MAX_HOST
];
5200 int port
; /* Port number */
5201 const char *encoding
; /* Content-Encoding value */
5202 static const char * const http_states
[] =
5203 { /* Strings for logging HTTP method */
5224 * Clear state variables...
5227 ippDelete(client
->request
);
5228 ippDelete(client
->response
);
5230 client
->request
= NULL
;
5231 client
->response
= NULL
;
5232 client
->operation
= HTTP_STATE_WAITING
;
5235 * Read a request from the connection...
5238 while ((http_state
= httpReadRequest(client
->http
, uri
,
5239 sizeof(uri
))) == HTTP_STATE_WAITING
)
5243 * Parse the request line...
5246 if (http_state
== HTTP_STATE_ERROR
)
5248 if (httpError(client
->http
) == EPIPE
)
5249 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
5251 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
5252 strerror(httpError(client
->http
)));
5256 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
5258 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
5259 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5262 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
5264 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
5265 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5269 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
5273 * Separate the URI into its components...
5276 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
5277 userpass
, sizeof(userpass
),
5278 hostname
, sizeof(hostname
), &port
,
5279 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
5280 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
5282 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
5283 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5287 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
5288 *(client
->options
)++ = '\0';
5291 * Process the request...
5294 client
->start
= time(NULL
);
5295 client
->operation
= httpGetState(client
->http
);
5298 * Parse incoming parameters until the status changes...
5301 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
5303 if (http_status
!= HTTP_STATUS_OK
)
5305 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5309 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
5310 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
5313 * HTTP/1.1 and higher require the "Host:" field...
5316 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5321 * Handle HTTP Upgrade...
5324 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
5328 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
5330 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
5333 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
5335 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
5337 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5341 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5344 #endif /* HAVE_SSL */
5346 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
5351 * Handle HTTP Expect...
5354 if (httpGetExpect(client
->http
) &&
5355 (client
->operation
== HTTP_STATE_POST
||
5356 client
->operation
== HTTP_STATE_PUT
))
5358 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
5361 * Send 100-continue header...
5364 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
5370 * Send 417-expectation-failed header...
5373 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
5379 * Handle new transfers...
5382 encoding
= httpGetContentEncoding(client
->http
);
5384 switch (client
->operation
)
5386 case HTTP_STATE_OPTIONS
:
5388 * Do OPTIONS command...
5391 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
5393 case HTTP_STATE_HEAD
:
5394 if (!strcmp(client
->uri
, "/icon.png"))
5395 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
5396 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
5397 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
5399 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5401 case HTTP_STATE_GET
:
5402 if (!strcmp(client
->uri
, "/icon.png"))
5405 * Send PNG icon file.
5408 int fd
; /* Icon file */
5409 struct stat fileinfo
; /* Icon file information */
5410 char buffer
[4096]; /* Copy buffer */
5411 ssize_t bytes
; /* Bytes */
5413 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5415 if (!stat(client
->printer
->icon
, &fileinfo
) &&
5416 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5418 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
5419 (size_t)fileinfo
.st_size
))
5425 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5426 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5428 httpFlushWrite(client
->http
);
5433 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5435 else if (!strcmp(client
->uri
, "/"))
5438 * Show web status page...
5441 _ipp_job_t
*job
; /* Current job */
5442 int i
; /* Looping var */
5443 _ipp_preason_t reason
; /* Current reason */
5444 static const char * const reasons
[] =
5445 { /* Reason strings */
5448 "Input Tray Missing",
5449 "Marker Supply Empty",
5450 "Marker Supply Low",
5451 "Marker Waste Almost Full",
5452 "Marker Waste Full",
5464 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5467 html_header(client
, client
->printer
->name
);
5469 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
5470 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
5471 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
5472 if (client
->printer
->state_reasons
& reason
)
5473 html_printf(client
, "\n<br> %s", reasons
[i
]);
5474 html_printf(client
, "</p>\n");
5476 if (cupsArrayCount(client
->printer
->jobs
) > 0)
5478 _cupsRWLockRead(&(client
->printer
->rwlock
));
5480 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");
5481 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
5483 char when
[256], /* When job queued/started/finished */
5484 hhmmss
[64]; /* Time HH:MM:SS */
5488 case IPP_JSTATE_PENDING
:
5489 case IPP_JSTATE_HELD
:
5490 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
5492 case IPP_JSTATE_PROCESSING
:
5493 case IPP_JSTATE_STOPPED
:
5494 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
5496 case IPP_JSTATE_ABORTED
:
5497 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5499 case IPP_JSTATE_CANCELED
:
5500 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5502 case IPP_JSTATE_COMPLETED
:
5503 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5507 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
);
5509 html_printf(client
, "</tbody></table>\n");
5511 _cupsRWUnlock(&(client
->printer
->rwlock
));
5513 html_footer(client
);
5517 else if (!strcmp(client
->uri
, "/media"))
5520 * Show web media page...
5523 int i
, /* Looping var */
5524 num_options
; /* Number of form options */
5525 cups_option_t
*options
; /* Form options */
5526 static const char * const sizes
[] =
5527 { /* Size strings */
5540 static const char * const types
[] =
5557 static const int sheets
[] = /* Number of sheets */
5566 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5569 html_header(client
, client
->printer
->name
);
5571 if ((num_options
= parse_options(client
, &options
)) > 0)
5574 * WARNING: A real printer/server implementation MUST NOT implement
5575 * media updates via a GET request - GET requests are supposed to be
5576 * idempotent (without side-effects) and we obviously are not
5577 * authenticating access here. This form is provided solely to
5578 * enable testing and development!
5581 const char *val
; /* Form value */
5583 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
5584 client
->printer
->main_size
= atoi(val
);
5585 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
5586 client
->printer
->main_type
= atoi(val
);
5587 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
5588 client
->printer
->main_level
= atoi(val
);
5590 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
5591 client
->printer
->envelope_size
= atoi(val
);
5592 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
5593 client
->printer
->envelope_level
= atoi(val
);
5595 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
5596 client
->printer
->photo_size
= atoi(val
);
5597 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
5598 client
->printer
->photo_type
= atoi(val
);
5599 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
5600 client
->printer
->photo_level
= atoi(val
);
5602 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))
5603 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
5605 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
5607 if ((client
->printer
->main_level
== 0 && client
->printer
->main_size
> _IPP_MEDIA_SIZE_NONE
) || (client
->printer
->envelope_level
== 0 && client
->printer
->envelope_size
> _IPP_MEDIA_SIZE_NONE
) || (client
->printer
->photo_level
== 0 && client
->printer
->photo_size
> _IPP_MEDIA_SIZE_NONE
))
5609 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
5610 if (client
->printer
->active_job
)
5611 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5614 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
5616 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
5619 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
5621 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
5622 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
5623 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5624 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
5625 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
5626 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
5627 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5628 if (!strstr(types
[i
], "Photo"))
5629 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
5630 html_printf(client
, "</select> <select name=\"main_level\">");
5631 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5632 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
5633 html_printf(client
, "</select></td></tr>\n");
5636 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
5637 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5638 if (strstr(sizes
[i
], "Envelope"))
5639 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
5640 html_printf(client
, "</select> <select name=\"envelope_level\">");
5641 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5642 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
5643 html_printf(client
, "</select></td></tr>\n");
5646 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
5647 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5648 if (strstr(sizes
[i
], "Photo"))
5649 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
5650 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
5651 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5652 if (strstr(types
[i
], "Photo"))
5653 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
5654 html_printf(client
, "</select> <select name=\"photo_level\">");
5655 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5656 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
5657 html_printf(client
, "</select></td></tr>\n");
5659 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5660 html_footer(client
);
5664 else if (!strcmp(client
->uri
, "/supplies"))
5667 * Show web supplies page...
5670 int i
, j
, /* Looping vars */
5671 num_options
; /* Number of form options */
5672 cups_option_t
*options
; /* Form options */
5673 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5675 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5678 html_header(client
, client
->printer
->name
);
5680 if ((num_options
= parse_options(client
, &options
)) > 0)
5683 * WARNING: A real printer/server implementation MUST NOT implement
5684 * supply updates via a GET request - GET requests are supposed to be
5685 * idempotent (without side-effects) and we obviously are not
5686 * authenticating access here. This form is provided solely to
5687 * enable testing and development!
5690 char name
[64]; /* Form field */
5691 const char *val
; /* Form value */
5693 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MARKER_SUPPLY_EMPTY
| _IPP_PREASON_MARKER_SUPPLY_LOW
| _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
| _IPP_PREASON_MARKER_WASTE_FULL
| _IPP_PREASON_TONER_EMPTY
| _IPP_PREASON_TONER_LOW
);
5695 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5697 snprintf(name
, sizeof(name
), "supply_%d", i
);
5698 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5700 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5706 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
5707 else if (level
< 10)
5708 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
5713 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
5714 else if (level
> 90)
5715 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
5720 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5723 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5725 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5726 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5728 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5729 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5730 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5731 html_printf(client
, "</select></td></tr>\n");
5733 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5734 html_footer(client
);
5739 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5742 case HTTP_STATE_POST
:
5743 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5747 * Not an IPP request...
5750 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5754 * Read the IPP request...
5757 client
->request
= ippNew();
5759 while ((ipp_state
= ippRead(client
->http
,
5760 client
->request
)) != IPP_STATE_DATA
)
5762 if (ipp_state
== IPP_STATE_ERROR
)
5764 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5765 cupsLastErrorString());
5766 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5772 * Now that we have the IPP request, process the request...
5775 return (process_ipp(client
));
5778 break; /* Anti-compiler-warning-code */
5786 * 'process_ipp()' - Process an IPP request.
5789 static int /* O - 1 on success, 0 on error */
5790 process_ipp(_ipp_client_t
*client
) /* I - Client */
5792 ipp_tag_t group
; /* Current group tag */
5793 ipp_attribute_t
*attr
; /* Current attribute */
5794 ipp_attribute_t
*charset
; /* Character set attribute */
5795 ipp_attribute_t
*language
; /* Language attribute */
5796 ipp_attribute_t
*uri
; /* Printer URI attribute */
5797 int major
, minor
; /* Version number */
5798 const char *name
; /* Name of attribute */
5801 debug_attributes("Request", client
->request
, 1);
5804 * First build an empty response message for this request...
5807 client
->operation_id
= ippGetOperation(client
->request
);
5808 client
->response
= ippNewResponse(client
->request
);
5811 * Then validate the request header and required attributes...
5814 major
= ippGetVersion(client
->request
, &minor
);
5816 if (major
< 1 || major
> 2)
5819 * Return an error, since we only support IPP 1.x and 2.x.
5822 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
, "Bad request version number %d.%d.", major
, minor
);
5824 else if ((major
* 10 + minor
) > MaxVersion
)
5826 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5827 httpFlush(client
->http
); /* Flush trailing (junk) data */
5829 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5832 else if (ippGetRequestId(client
->request
) <= 0)
5834 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.", ippGetRequestId(client
->request
));
5836 else if (!ippFirstAttribute(client
->request
))
5838 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No attributes in request.");
5843 * Make sure that the attributes are provided in the correct order and
5844 * don't repeat groups...
5847 for (attr
= ippFirstAttribute(client
->request
),
5848 group
= ippGetGroupTag(attr
);
5850 attr
= ippNextAttribute(client
->request
))
5852 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5855 * Out of order; return an error...
5858 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5859 "Attribute groups are out of order (%x < %x).",
5860 ippGetGroupTag(attr
), group
);
5864 group
= ippGetGroupTag(attr
);
5870 * Then make sure that the first three attributes are:
5872 * attributes-charset
5873 * attributes-natural-language
5874 * printer-uri/job-uri
5877 attr
= ippFirstAttribute(client
->request
);
5878 name
= ippGetName(attr
);
5879 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5880 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5885 attr
= ippNextAttribute(client
->request
);
5886 name
= ippGetName(attr
);
5888 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5889 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5894 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5895 IPP_TAG_URI
)) != NULL
)
5897 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5898 IPP_TAG_URI
)) != NULL
)
5904 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5905 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5908 * Bad character set...
5911 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5912 "Unsupported character set \"%s\".",
5913 ippGetString(charset
, 0, NULL
));
5915 else if (!charset
|| !language
|| !uri
)
5918 * Return an error, since attributes-charset,
5919 * attributes-natural-language, and printer-uri/job-uri are required
5920 * for all operations.
5923 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5924 "Missing required attributes.");
5928 char scheme
[32], /* URI scheme */
5929 userpass
[32], /* Username/password in URI */
5930 host
[256], /* Host name in URI */
5931 resource
[256]; /* Resource path in URI */
5932 int port
; /* Port number in URI */
5934 name
= ippGetName(uri
);
5936 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5937 scheme
, sizeof(scheme
),
5938 userpass
, sizeof(userpass
),
5939 host
, sizeof(host
), &port
,
5940 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5941 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5942 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5943 else if ((!strcmp(name
, "job-uri") &&
5944 strncmp(resource
, "/ipp/print/", 11)) ||
5945 (!strcmp(name
, "printer-uri") &&
5946 strcmp(resource
, "/ipp/print")))
5947 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5948 name
, ippGetString(uri
, 0, NULL
));
5952 * Try processing the operation...
5955 switch (ippGetOperation(client
->request
))
5957 case IPP_OP_PRINT_JOB
:
5958 ipp_print_job(client
);
5961 case IPP_OP_PRINT_URI
:
5962 ipp_print_uri(client
);
5965 case IPP_OP_VALIDATE_JOB
:
5966 ipp_validate_job(client
);
5969 case IPP_OP_CREATE_JOB
:
5970 ipp_create_job(client
);
5973 case IPP_OP_SEND_DOCUMENT
:
5974 ipp_send_document(client
);
5977 case IPP_OP_SEND_URI
:
5978 ipp_send_uri(client
);
5981 case IPP_OP_CANCEL_JOB
:
5982 ipp_cancel_job(client
);
5985 case IPP_OP_GET_JOB_ATTRIBUTES
:
5986 ipp_get_job_attributes(client
);
5989 case IPP_OP_GET_JOBS
:
5990 ipp_get_jobs(client
);
5993 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5994 ipp_get_printer_attributes(client
);
5997 case IPP_OP_CLOSE_JOB
:
5998 ipp_close_job(client
);
6001 case IPP_OP_IDENTIFY_PRINTER
:
6002 ipp_identify_printer(client
);
6006 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
6007 "Operation not supported.");
6016 * Send the HTTP header and return...
6019 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
6020 httpFlush(client
->http
); /* Flush trailing (junk) data */
6022 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
6023 ippLength(client
->response
)));
6028 * 'process_job()' - Process a print job.
6031 static void * /* O - Thread exit status */
6032 process_job(_ipp_job_t
*job
) /* I - Job */
6034 job
->state
= IPP_JSTATE_PROCESSING
;
6035 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
6036 job
->processing
= time(NULL
);
6038 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
6040 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
6045 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
6047 if (job
->printer
->command
)
6050 * Execute a command with the job spool file and wait for it to complete...
6053 int pid
, /* Process ID */
6054 status
; /* Exit status */
6055 time_t start
, /* Start time */
6057 char *myargv
[3], /* Command-line arguments */
6058 *myenvp
[200]; /* Environment variables */
6059 int myenvc
; /* Number of environment variables */
6060 ipp_attribute_t
*attr
; /* Job attribute */
6061 char val
[1280], /* IPP_NAME=value */
6062 *valptr
; /* Pointer into string */
6064 int mypipe
[2]; /* Pipe for stderr */
6065 char line
[2048], /* Line from stderr */
6066 *ptr
, /* Pointer into line */
6067 *endptr
; /* End of line */
6068 ssize_t bytes
; /* Bytes read */
6071 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
6076 * Setup the command-line arguments...
6079 myargv
[0] = job
->printer
->command
;
6080 myargv
[1] = job
->filename
;
6084 * Copy the current environment, then add ENV variables for every Job
6088 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
6089 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
6091 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
6094 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6095 * value(s) from the attribute.
6098 const char *name
= ippGetName(attr
);
6107 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6112 *valptr
++ = (char)toupper(*name
& 255);
6117 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6119 myenvp
[myenvc
++] = strdup(val
);
6121 myenvp
[myenvc
] = NULL
;
6124 * Now run the program...
6128 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
6133 perror("Unable to create pipe for stderr");
6134 mypipe
[0] = mypipe
[1] = -1;
6137 if ((pid
= fork()) == 0)
6140 * Child comes here...
6148 execve(job
->printer
->command
, myargv
, myenvp
);
6154 * Unable to fork process...
6157 perror("Unable to start job processing command");
6164 * Free memory used for environment...
6168 free(myenvp
[-- myenvc
]);
6173 * Free memory used for environment...
6177 free(myenvp
[-- myenvc
]);
6180 * If the pipe exists, read from it until EOF...
6188 while ((bytes
= read(mypipe
[0], endptr
, sizeof(line
) - (size_t)(endptr
- line
) - 1)) > 0)
6193 while ((ptr
= strchr(line
, '\n')) != NULL
)
6197 if (!strncmp(line
, "STATE:", 6))
6200 * Process printer-state-reasons keywords.
6203 process_state_message(job
, line
);
6205 else if (!strncmp(line
, "ATTR:", 5))
6208 * Process printer attribute update.
6211 process_attr_message(job
, line
);
6213 else if (Verbosity
> 1)
6214 fprintf(stderr
, "%s: %s\n", job
->printer
->command
, line
);
6218 memmove(line
, ptr
, (size_t)(endptr
- ptr
));
6228 * Wait for child to complete...
6231 # ifdef HAVE_WAITPID
6232 while (waitpid(pid
, &status
, 0) < 0);
6234 while (wait(&status
) < 0);
6235 # endif /* HAVE_WAITPID */
6242 if (WIFEXITED(status
))
6244 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
6245 job
->printer
->command
, WEXITSTATUS(status
));
6248 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
6249 job
->printer
->command
, WTERMSIG(status
));
6251 job
->state
= IPP_JSTATE_ABORTED
;
6253 else if (status
< 0)
6254 job
->state
= IPP_JSTATE_ABORTED
;
6256 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
6257 job
->printer
->command
);
6260 * Make sure processing takes at least 5 seconds...
6264 if ((end
- start
) < 5)
6270 * Sleep for a random amount of time to simulate job processing.
6273 sleep((unsigned)(5 + (rand() % 11)));
6277 job
->state
= IPP_JSTATE_CANCELED
;
6278 else if (job
->state
== IPP_JSTATE_PROCESSING
)
6279 job
->state
= IPP_JSTATE_COMPLETED
;
6281 job
->completed
= time(NULL
);
6282 job
->printer
->state
= IPP_PSTATE_IDLE
;
6283 job
->printer
->active_job
= NULL
;
6290 * 'process_state_message()' - Process a STATE: message from a command.
6294 process_state_message(
6295 _ipp_job_t
*job
, /* I - Job */
6296 char *message
) /* I - Message */
6298 int i
; /* Looping var */
6299 _ipp_preason_t state_reasons
, /* printer-state-reasons values */
6300 bit
; /* Current reason bit */
6301 char *ptr
, /* Pointer into message */
6302 *next
; /* Next keyword in message */
6303 int remove
; /* Non-zero if we are removing keywords */
6307 * Skip leading "STATE:" and any whitespace...
6310 for (message
+= 6; *message
; message
++)
6311 if (*message
!= ' ' && *message
!= '\t')
6315 * Support the following forms of message:
6317 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6319 * "-keyword[,keyword,...]" to remove keywords.
6321 * "+keyword[,keyword,...]" to add keywords.
6323 * Keywords may or may not have a suffix (-report, -warning, -error) per
6327 if (*message
== '-')
6330 state_reasons
= job
->printer
->state_reasons
;
6333 else if (*message
== '+')
6336 state_reasons
= job
->printer
->state_reasons
;
6342 state_reasons
= _IPP_PREASON_NONE
;
6347 if ((next
= strchr(message
, ',')) != NULL
)
6350 if ((ptr
= strstr(message
, "-error")) != NULL
)
6352 else if ((ptr
= strstr(message
, "-report")) != NULL
)
6354 else if ((ptr
= strstr(message
, "-warning")) != NULL
)
6357 for (i
= 0, bit
= 1; i
< (int)(sizeof(_ipp_preason_strings
) / sizeof(_ipp_preason_strings
[0])); i
++, bit
*= 2)
6359 if (!strcmp(message
, _ipp_preason_strings
[i
]))
6362 state_reasons
&= ~bit
;
6364 state_reasons
|= bit
;
6374 job
->printer
->state_reasons
= state_reasons
;
6379 * 'register_printer()' - Register a printer object via Bonjour.
6382 static int /* O - 1 on success, 0 on error */
6384 _ipp_printer_t
*printer
, /* I - Printer */
6385 const char *location
, /* I - Location */
6386 const char *make
, /* I - Manufacturer */
6387 const char *model
, /* I - Model name */
6388 const char *formats
, /* I - Supported formats */
6389 const char *adminurl
, /* I - Web interface URL */
6390 const char *uuid
, /* I - Printer UUID */
6391 int color
, /* I - 1 = color, 0 = monochrome */
6392 int duplex
, /* I - 1 = duplex, 0 = simplex */
6393 const char *subtype
) /* I - Service subtype */
6395 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6396 _ipp_txt_t ipp_txt
; /* Bonjour IPP TXT record */
6397 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6399 DNSServiceErrorType error
; /* Error from Bonjour */
6400 char make_model
[256],/* Make and model together */
6401 product
[256], /* Product string */
6402 regtype
[256]; /* Bonjour service type */
6406 * Build the TXT record for IPP...
6409 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
6410 snprintf(product
, sizeof(product
), "(%s)", model
);
6412 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
6413 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
6414 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(make_model
),
6416 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(adminurl
),
6419 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(location
),
6421 TXTRecordSetValue(&ipp_txt
, "product", (uint8_t)strlen(product
),
6423 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
),
6425 TXTRecordSetValue(&ipp_txt
, "Color", 1, color
? "T" : "F");
6426 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, duplex
? "T" : "F");
6427 TXTRecordSetValue(&ipp_txt
, "usb_MFG", (uint8_t)strlen(make
),
6429 TXTRecordSetValue(&ipp_txt
, "usb_MDL", (uint8_t)strlen(model
),
6431 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(uuid
), uuid
);
6433 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
6434 # endif /* HAVE_SSL */
6435 if (strstr(formats
, "image/urf"))
6436 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");
6438 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
6439 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
6442 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6443 * defend our service name but not actually support LPD...
6446 printer
->printer_ref
= DNSSDMaster
;
6448 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
6449 kDNSServiceFlagsShareConnection
,
6450 0 /* interfaceIndex */, printer
->dnssd_name
,
6451 "_printer._tcp", NULL
/* domain */,
6452 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
6453 NULL
/* txtRecord */,
6454 (DNSServiceRegisterReply
)dnssd_callback
,
6455 printer
)) != kDNSServiceErr_NoError
)
6457 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
6458 printer
->dnssd_name
, error
);
6463 * Then register the _ipp._tcp (IPP) service type with the real port number to
6464 * advertise our IPP printer...
6467 printer
->ipp_ref
= DNSSDMaster
;
6469 if (subtype
&& *subtype
)
6470 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
6472 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
6474 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
6475 kDNSServiceFlagsShareConnection
,
6476 0 /* interfaceIndex */, printer
->dnssd_name
,
6477 regtype
, NULL
/* domain */,
6478 NULL
/* host */, htons(printer
->port
),
6479 TXTRecordGetLength(&ipp_txt
),
6480 TXTRecordGetBytesPtr(&ipp_txt
),
6481 (DNSServiceRegisterReply
)dnssd_callback
,
6482 printer
)) != kDNSServiceErr_NoError
)
6484 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6485 printer
->dnssd_name
, regtype
, error
);
6491 * Then register the _ipps._tcp (IPP) service type with the real port number to
6492 * advertise our IPPS printer...
6495 printer
->ipps_ref
= DNSSDMaster
;
6497 if (subtype
&& *subtype
)
6498 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
6500 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
6502 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
6503 kDNSServiceFlagsShareConnection
,
6504 0 /* interfaceIndex */, printer
->dnssd_name
,
6505 regtype
, NULL
/* domain */,
6506 NULL
/* host */, htons(printer
->port
),
6507 TXTRecordGetLength(&ipp_txt
),
6508 TXTRecordGetBytesPtr(&ipp_txt
),
6509 (DNSServiceRegisterReply
)dnssd_callback
,
6510 printer
)) != kDNSServiceErr_NoError
)
6512 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6513 printer
->dnssd_name
, regtype
, error
);
6516 # endif /* HAVE_SSL */
6519 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6520 * real port number to advertise our IPP printer...
6523 printer
->http_ref
= DNSSDMaster
;
6525 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
6526 kDNSServiceFlagsShareConnection
,
6527 0 /* interfaceIndex */, printer
->dnssd_name
,
6528 "_http._tcp,_printer", NULL
/* domain */,
6529 NULL
/* host */, htons(printer
->port
),
6530 0 /* txtLen */, NULL
, /* txtRecord */
6531 (DNSServiceRegisterReply
)dnssd_callback
,
6532 printer
)) != kDNSServiceErr_NoError
)
6534 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6535 printer
->dnssd_name
, regtype
, error
);
6539 TXTRecordDeallocate(&ipp_txt
);
6541 #elif defined(HAVE_AVAHI)
6542 char temp
[256]; /* Subtype service string */
6545 * Create the TXT record...
6549 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
6550 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s %s", make
, model
);
6551 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", adminurl
);
6553 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", location
);
6554 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "product=(%s)", model
);
6555 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
6556 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", color
? "T" : "F");
6557 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", duplex
? "T" : "F");
6558 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MFG=%s", make
);
6559 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MDL=%s", model
);
6560 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", uuid
);
6562 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
6563 # endif /* HAVE_SSL */
6566 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6569 avahi_threaded_poll_lock(DNSSDMaster
);
6571 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
6573 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
);
6576 * Then register the _ipp._tcp (IPP)...
6579 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
);
6580 if (subtype
&& *subtype
)
6582 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
6583 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
6588 * _ipps._tcp (IPPS) for secure printing...
6591 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
);
6592 if (subtype
&& *subtype
)
6594 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
6595 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
6597 #endif /* HAVE_SSL */
6600 * Finally _http.tcp (HTTP) for the web interface...
6603 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
);
6604 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");
6610 avahi_entry_group_commit(printer
->ipp_ref
);
6611 avahi_threaded_poll_unlock(DNSSDMaster
);
6613 avahi_string_list_free(ipp_txt
);
6614 #endif /* HAVE_DNSSD */
6621 * 'respond_http()' - Send a HTTP response.
6624 int /* O - 1 on success, 0 on failure */
6626 _ipp_client_t
*client
, /* I - Client */
6627 http_status_t code
, /* I - HTTP status of response */
6628 const char *content_encoding
, /* I - Content-Encoding of response */
6629 const char *type
, /* I - MIME media type of response */
6630 size_t length
) /* I - Length of response */
6632 char message
[1024]; /* Text message */
6635 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
6637 if (code
== HTTP_STATUS_CONTINUE
)
6640 * 100-continue doesn't send any headers...
6643 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
6647 * Format an error message...
6650 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
6652 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
6654 type
= "text/plain";
6655 length
= strlen(message
);
6661 * Send the HTTP response header...
6664 httpClearFields(client
->http
);
6666 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
6667 client
->operation
== HTTP_STATE_OPTIONS
)
6668 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
6672 if (!strcmp(type
, "text/html"))
6673 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
6674 "text/html; charset=utf-8");
6676 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
6678 if (content_encoding
)
6679 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
6682 httpSetLength(client
->http
, length
);
6684 if (httpWriteResponse(client
->http
, code
) < 0)
6688 * Send the response data...
6694 * Send a plain text message.
6697 if (httpPrintf(client
->http
, "%s", message
) < 0)
6700 if (httpWrite2(client
->http
, "", 0) < 0)
6703 else if (client
->response
)
6706 * Send an IPP response...
6709 debug_attributes("Response", client
->response
, 2);
6711 ippSetState(client
->response
, IPP_STATE_IDLE
);
6713 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6722 * 'respond_ipp()' - Send an IPP response.
6726 respond_ipp(_ipp_client_t
*client
, /* I - Client */
6727 ipp_status_t status
, /* I - status-code */
6728 const char *message
, /* I - printf-style status-message */
6729 ...) /* I - Additional args as needed */
6731 const char *formatted
= NULL
; /* Formatted message */
6734 ippSetStatusCode(client
->response
, status
);
6738 va_list ap
; /* Pointer to additional args */
6739 ipp_attribute_t
*attr
; /* New status-message attribute */
6741 va_start(ap
, message
);
6742 if ((attr
= ippFindAttribute(client
->response
, "status-message",
6743 IPP_TAG_TEXT
)) != NULL
)
6744 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6746 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
6747 "status-message", NULL
, message
, ap
);
6750 formatted
= ippGetString(attr
, 0, NULL
);
6754 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
6755 ippOpString(client
->operation_id
), ippErrorString(status
),
6758 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
6759 ippOpString(client
->operation_id
), ippErrorString(status
));
6764 * 'respond_unsupported()' - Respond with an unsupported attribute.
6768 respond_unsupported(
6769 _ipp_client_t
*client
, /* I - Client */
6770 ipp_attribute_t
*attr
) /* I - Atribute */
6772 ipp_attribute_t
*temp
; /* Copy of attribute */
6775 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6776 "Unsupported %s %s%s value.", ippGetName(attr
),
6777 ippGetCount(attr
) > 1 ? "1setOf " : "",
6778 ippTagString(ippGetValueTag(attr
)));
6780 temp
= ippCopyAttribute(client
->response
, attr
, 0);
6781 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
6786 * 'run_printer()' - Run the printer service.
6790 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
6792 int num_fds
; /* Number of file descriptors */
6793 struct pollfd polldata
[3]; /* poll() data */
6794 int timeout
; /* Timeout for poll() */
6795 _ipp_client_t
*client
; /* New client */
6799 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6802 polldata
[0].fd
= printer
->ipv4
;
6803 polldata
[0].events
= POLLIN
;
6805 polldata
[1].fd
= printer
->ipv6
;
6806 polldata
[1].events
= POLLIN
;
6811 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
6812 polldata
[num_fds
++].events
= POLLIN
;
6813 #endif /* HAVE_DNSSD */
6816 * Loop until we are killed or have a hard error...
6821 if (cupsArrayCount(printer
->jobs
))
6826 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
6828 perror("poll() failed");
6832 if (polldata
[0].revents
& POLLIN
)
6834 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6836 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6840 _cupsThreadDetach(t
);
6844 perror("Unable to create client thread");
6845 delete_client(client
);
6850 if (polldata
[1].revents
& POLLIN
)
6852 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6854 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6858 _cupsThreadDetach(t
);
6862 perror("Unable to create client thread");
6863 delete_client(client
);
6869 if (polldata
[2].revents
& POLLIN
)
6870 DNSServiceProcessResult(DNSSDMaster
);
6871 #endif /* HAVE_DNSSD */
6874 * Clean out old jobs...
6877 clean_jobs(printer
);
6883 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6887 time_string(time_t tv
, /* I - Time value */
6888 char *buffer
, /* I - Buffer */
6889 size_t bufsize
) /* I - Size of buffer */
6891 struct tm
*curtime
= localtime(&tv
);
6894 strftime(buffer
, bufsize
, "%X", curtime
);
6900 * 'usage()' - Show program usage.
6904 usage(int status
) /* O - Exit status */
6908 puts(CUPS_SVERSION
" - Copyright (c) 2010-2018 by Apple Inc. All rights reserved.");
6912 puts("Usage: ippserver [options] \"name\"");
6915 puts("-2 Supports 2-sided printing (default=1-sided)");
6916 puts("-M manufacturer Manufacturer name (default=Test)");
6917 puts("-P PIN printing mode");
6918 puts("-V max-version Set maximum supported IPP version");
6919 puts("-a attributes-file Load printer attributes from file");
6920 puts("-c command Run command for every print job");
6921 printf("-d spool-directory Spool directory "
6922 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6923 puts("-f type/subtype[,...] List of supported types "
6924 "(default=application/pdf,image/jpeg)");
6925 puts("-h Show program help");
6926 puts("-i iconfile.png PNG icon file (default=printer.png)");
6927 puts("-k Keep job spool files");
6928 puts("-l location Location of printer (default=empty string)");
6929 puts("-m model Model name (default=Printer)");
6930 puts("-n hostname Hostname for printer");
6931 puts("-p port Port number (default=auto)");
6932 puts("-r subtype Bonjour service subtype (default=_print)");
6933 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6934 puts("-v[vvv] Be (very) verbose");
6941 * 'valid_doc_attributes()' - Determine whether the document attributes are
6944 * When one or more document attributes are invalid, this function adds a
6945 * suitable response and attributes to the unsupported group.
6948 static int /* O - 1 if valid, 0 if not */
6949 valid_doc_attributes(
6950 _ipp_client_t
*client
) /* I - Client */
6952 int valid
= 1; /* Valid attributes? */
6953 ipp_op_t op
= ippGetOperation(client
->request
);
6955 const char *op_name
= ippOpString(op
);
6956 /* IPP operation name */
6957 ipp_attribute_t
*attr
, /* Current attribute */
6958 *supported
; /* xxx-supported attribute */
6959 const char *compression
= NULL
,
6960 /* compression value */
6961 *format
= NULL
; /* document-format value */
6965 * Check operation attributes...
6968 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6971 * If compression is specified, only accept a supported value in a Print-Job
6972 * or Send-Document request...
6975 compression
= ippGetString(attr
, 0, NULL
);
6976 supported
= ippFindAttribute(client
->printer
->attrs
,
6977 "compression-supported", IPP_TAG_KEYWORD
);
6979 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6980 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6981 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6982 op
!= IPP_OP_VALIDATE_JOB
) ||
6983 !ippContainsString(supported
, compression
))
6985 respond_unsupported(client
, attr
);
6990 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6992 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6994 if (strcmp(compression
, "none"))
6997 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6998 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
7004 * Is it a format we support?
7007 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
7009 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
7010 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
7012 respond_unsupported(client
, attr
);
7017 format
= ippGetString(attr
, 0, NULL
);
7019 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
7020 client
->hostname
, op_name
, format
);
7022 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
7027 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
7029 format
= "application/octet-stream"; /* Should never happen */
7031 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
7034 if (format
&& !strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
7037 * Auto-type the file using the first 8 bytes of the file...
7040 unsigned char header
[8]; /* First 8 bytes of file */
7042 memset(header
, 0, sizeof(header
));
7043 httpPeek(client
->http
, (char *)header
, sizeof(header
));
7045 if (!memcmp(header
, "%PDF", 4))
7046 format
= "application/pdf";
7047 else if (!memcmp(header
, "%!", 2))
7048 format
= "application/postscript";
7049 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
7050 format
= "image/jpeg";
7051 else if (!memcmp(header
, "\211PNG", 4))
7052 format
= "image/png";
7053 else if (!memcmp(header
, "RAS2", 4))
7054 format
= "image/pwg-raster";
7055 else if (!memcmp(header
, "UNIRAST", 8))
7056 format
= "image/urf";
7062 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
7063 client
->hostname
, op_name
, format
);
7065 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
7069 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
7071 respond_unsupported(client
, attr
);
7079 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
7080 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
7087 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
7089 * When one or more job attributes are invalid, this function adds a suitable
7090 * response and attributes to the unsupported group.
7093 static int /* O - 1 if valid, 0 if not */
7094 valid_job_attributes(
7095 _ipp_client_t
*client
) /* I - Client */
7097 int i
, /* Looping var */
7098 count
, /* Number of values */
7099 valid
= 1; /* Valid attributes? */
7100 ipp_attribute_t
*attr
, /* Current attribute */
7101 *supported
; /* xxx-supported attribute */
7105 * Check operation attributes...
7108 valid
= valid_doc_attributes(client
);
7111 * Check the various job template attributes...
7114 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
7116 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7117 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
7119 respond_unsupported(client
, attr
);
7124 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
7126 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
7128 respond_unsupported(client
, attr
);
7133 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
7135 if (ippGetCount(attr
) != 1 ||
7136 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7137 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7138 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7139 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
7141 respond_unsupported(client
, attr
);
7146 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
7148 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
7150 respond_unsupported(client
, attr
);
7155 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
7157 if (ippGetCount(attr
) != 1 ||
7158 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7159 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
7161 respond_unsupported(client
, attr
);
7165 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
7168 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
7170 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
7172 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7173 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
7175 respond_unsupported(client
, attr
);
7180 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
7182 if (ippGetCount(attr
) != 1 ||
7183 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7184 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7185 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7186 strcmp(ippGetString(attr
, 0, NULL
), "none"))
7188 respond_unsupported(client
, attr
);
7193 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
7195 if (ippGetCount(attr
) != 1 ||
7196 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7197 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7198 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
7200 respond_unsupported(client
, attr
);
7205 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7207 if (!ippContainsString(supported
, ippGetString(attr
, 0, NULL
)))
7209 respond_unsupported(client
, attr
);
7215 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
7217 ipp_t
*col
, /* media-col collection */
7218 *size
; /* media-size collection */
7219 ipp_attribute_t
*member
, /* Member attribute */
7220 *x_dim
, /* x-dimension */
7221 *y_dim
; /* y-dimension */
7222 int x_value
, /* y-dimension value */
7223 y_value
; /* x-dimension value */
7225 if (ippGetCount(attr
) != 1 ||
7226 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
7228 respond_unsupported(client
, attr
);
7232 col
= ippGetCollection(attr
, 0);
7234 if ((member
= ippFindAttribute(col
, "media-size-name", IPP_TAG_ZERO
)) != NULL
)
7236 if (ippGetCount(member
) != 1 ||
7237 (ippGetValueTag(member
) != IPP_TAG_NAME
&&
7238 ippGetValueTag(member
) != IPP_TAG_NAMELANG
&&
7239 ippGetValueTag(member
) != IPP_TAG_KEYWORD
))
7241 respond_unsupported(client
, attr
);
7246 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7248 if (!ippContainsString(supported
, ippGetString(member
, 0, NULL
)))
7250 respond_unsupported(client
, attr
);
7255 else if ((member
= ippFindAttribute(col
, "media-size", IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
7257 if (ippGetCount(member
) != 1)
7259 respond_unsupported(client
, attr
);
7264 size
= ippGetCollection(member
, 0);
7266 if ((x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(x_dim
) != 1 ||
7267 (y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(y_dim
) != 1)
7269 respond_unsupported(client
, attr
);
7274 x_value
= ippGetInteger(x_dim
, 0);
7275 y_value
= ippGetInteger(y_dim
, 0);
7276 supported
= ippFindAttribute(client
->printer
->attrs
, "media-size-supported", IPP_TAG_BEGIN_COLLECTION
);
7277 count
= ippGetCount(supported
);
7279 for (i
= 0; i
< count
; i
++)
7281 size
= ippGetCollection(supported
, i
);
7282 x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_ZERO
);
7283 y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_ZERO
);
7285 if (ippContainsInteger(x_dim
, x_value
) && ippContainsInteger(y_dim
, y_value
))
7291 respond_unsupported(client
, attr
);
7299 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
7301 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7302 (strcmp(ippGetString(attr
, 0, NULL
),
7303 "separate-documents-uncollated-copies") &&
7304 strcmp(ippGetString(attr
, 0, NULL
),
7305 "separate-documents-collated-copies")))
7307 respond_unsupported(client
, attr
);
7312 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
7314 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7315 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
7316 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
7318 respond_unsupported(client
, attr
);
7323 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
7325 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
7327 respond_unsupported(client
, attr
);
7332 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
7334 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7335 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
7336 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
7338 respond_unsupported(client
, attr
);
7343 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
7345 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
7347 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
7350 respond_unsupported(client
, attr
);
7355 int xdpi
, /* Horizontal resolution for job template attribute */
7356 ydpi
, /* Vertical resolution for job template attribute */
7357 sydpi
; /* Vertical resolution for supported value */
7358 ipp_res_t units
, /* Units for job template attribute */
7359 sunits
; /* Units for supported value */
7361 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
7362 count
= ippGetCount(supported
);
7364 for (i
= 0; i
< count
; i
++)
7366 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
7372 respond_unsupported(client
, attr
);
7378 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
7380 const char *sides
= ippGetString(attr
, 0, NULL
);
7381 /* "sides" value... */
7383 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
7385 respond_unsupported(client
, attr
);
7388 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
7390 if (!ippContainsString(supported
, sides
))
7392 respond_unsupported(client
, attr
);
7396 else if (strcmp(sides
, "one-sided"))
7398 respond_unsupported(client
, attr
);