2 * Sample IPP Everywhere server for CUPS.
4 * Copyright 2010-2017 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
10 * Disable private and deprecated stuff so we can verify that the public API
11 * is sufficient to implement a server.
14 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
15 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
19 * Include necessary headers...
22 #include <config.h> /* CUPS configuration header */
23 #include <cups/cups.h> /* Public API */
24 #include <cups/string-private.h> /* CUPS string functions */
25 #include <cups/thread-private.h> /* For multithreading functions */
38 # define WEXITSTATUS(s) (s)
39 # include <winsock2.h>
43 extern char **environ
;
45 # include <sys/fcntl.h>
46 # include <sys/wait.h>
52 #elif defined(HAVE_AVAHI)
53 # include <avahi-client/client.h>
54 # include <avahi-client/publish.h>
55 # include <avahi-common/error.h>
56 # include <avahi-common/thread-watch.h>
57 #endif /* HAVE_DNSSD */
58 #ifdef HAVE_SYS_MOUNT_H
59 # include <sys/mount.h>
60 #endif /* HAVE_SYS_MOUNT_H */
61 #ifdef HAVE_SYS_STATFS_H
62 # include <sys/statfs.h>
63 #endif /* HAVE_SYS_STATFS_H */
64 #ifdef HAVE_SYS_STATVFS_H
65 # include <sys/statvfs.h>
66 #endif /* HAVE_SYS_STATVFS_H */
69 #endif /* HAVE_SYS_VFS_H */
76 enum _ipp_preason_e
/* printer-state-reasons bit values */
78 _IPP_PREASON_NONE
= 0x0000, /* none */
79 _IPP_PREASON_OTHER
= 0x0001, /* other */
80 _IPP_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
81 _IPP_PREASON_INPUT_TRAY_MISSING
= 0x0004,
82 /* input-tray-missing */
83 _IPP_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
84 /* marker-supply-empty */
85 _IPP_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
86 /* marker-supply-low */
87 _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
88 /* marker-waste-almost-full */
89 _IPP_PREASON_MARKER_WASTE_FULL
= 0x0040,
90 /* marker-waste-full */
91 _IPP_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
92 _IPP_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
93 _IPP_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
94 _IPP_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
95 _IPP_PREASON_MOVING_TO_PAUSED
= 0x0800,
96 /* moving-to-paused */
97 _IPP_PREASON_PAUSED
= 0x1000, /* paused */
98 _IPP_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
99 _IPP_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
100 _IPP_PREASON_TONER_LOW
= 0x8000 /* toner-low */
102 typedef unsigned int _ipp_preason_t
; /* Bitfield for printer-state-reasons */
103 static const char * const _ipp_preason_strings
[] =
104 { /* Strings for each bit */
105 /* "none" is implied for no bits set */
108 "input-tray-missing",
109 "marker-supply-empty",
111 "marker-waste-almost-full",
124 typedef enum _ipp_media_class_e
126 _IPP_GENERAL
, /* General-purpose size */
127 _IPP_PHOTO_ONLY
, /* Photo-only size */
128 _IPP_ENV_ONLY
/* Envelope-only size */
129 } _ipp_media_class_t
;
131 typedef enum _ipp_media_size_e
133 _IPP_MEDIA_SIZE_NONE
= -1,
138 _IPP_MEDIA_SIZE_LEGAL
,
139 _IPP_MEDIA_SIZE_LETTER
,
140 _IPP_MEDIA_SIZE_COM10
,
146 static const char * const media_supported
[] =
147 { /* media-supported values */
148 "iso_a4_210x297mm", /* A4 */
149 "iso_a5_148x210mm", /* A5 */
150 "iso_a6_105x148mm", /* A6 */
151 "iso_dl_110x220mm", /* DL */
152 "na_legal_8.5x14in", /* Legal */
153 "na_letter_8.5x11in", /* Letter */
154 "na_number-10_4.125x9.5in", /* #10 */
155 "na_index-3x5_3x5in", /* 3x5 */
156 "oe_photo-l_3.5x5in", /* L */
157 "na_index-4x6_4x6in", /* 4x6 */
158 "na_5x7_5x7in" /* 5x7 aka 2L */
160 static const int media_col_sizes
[][3] =
161 { /* media-col-database sizes */
162 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
163 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
164 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
165 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
166 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
167 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
168 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
169 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
170 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
171 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
172 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
175 typedef enum _ipp_media_source_e
177 _IPP_MEDIA_SOURCE_NONE
= -1,
178 _IPP_MEDIA_SOURCE_AUTO
,
179 _IPP_MEDIA_SOURCE_MAIN
,
180 _IPP_MEDIA_SOURCE_MANUAL
,
181 _IPP_MEDIA_SOURCE_ENVELOPE
,
182 _IPP_MEDIA_SOURCE_PHOTO
183 } _ipp_media_source_t
;
184 static const char * const media_source_supported
[] =
185 /* media-source-supported values */
194 typedef enum _ipp_media_type_e
196 _IPP_MEDIA_TYPE_NONE
= -1,
197 _IPP_MEDIA_TYPE_AUTO
,
198 _IPP_MEDIA_TYPE_CARDSTOCK
,
199 _IPP_MEDIA_TYPE_ENVELOPE
,
200 _IPP_MEDIA_TYPE_LABELS
,
201 _IPP_MEDIA_TYPE_OTHER
,
202 _IPP_MEDIA_TYPE_GLOSSY
,
203 _IPP_MEDIA_TYPE_HIGH_GLOSS
,
204 _IPP_MEDIA_TYPE_MATTE
,
205 _IPP_MEDIA_TYPE_SATIN
,
206 _IPP_MEDIA_TYPE_SEMI_GLOSS
,
207 _IPP_MEDIA_TYPE_STATIONERY
,
208 _IPP_MEDIA_TYPE_LETTERHEAD
,
209 _IPP_MEDIA_TYPE_TRANSPARENCY
211 static const char * const media_type_supported
[] =
212 /* media-type-supported values */
219 "photographic-glossy",
220 "photographic-high-gloss",
221 "photographic-matte",
222 "photographic-satin",
223 "photographic-semi-gloss",
225 "stationery-letterhead",
229 typedef enum _ipp_supply_e
231 _IPP_SUPPLY_CYAN
, /* Cyan Toner */
232 _IPP_SUPPLY_MAGENTA
, /* Magenta Toner */
233 _IPP_SUPPLY_YELLOW
, /* Yellow Toner */
234 _IPP_SUPPLY_BLACK
, /* Black Toner */
235 _IPP_SUPPLY_WASTE
/* Waste Toner */
237 static const char * const printer_supplies
[] =
238 { /* printer-supply-description values */
247 * URL scheme for web resources...
251 # define WEB_SCHEME "https"
253 # define WEB_SCHEME "http"
254 #endif /* HAVE_SSL */
262 typedef DNSServiceRef _ipp_srv_t
; /* Service reference */
263 typedef TXTRecordRef _ipp_txt_t
; /* TXT record */
265 #elif defined(HAVE_AVAHI)
266 typedef AvahiEntryGroup
*_ipp_srv_t
; /* Service reference */
267 typedef AvahiStringList
*_ipp_txt_t
; /* TXT record */
270 typedef void *_ipp_srv_t
; /* Service reference */
271 typedef void *_ipp_txt_t
; /* TXT record */
272 #endif /* HAVE_DNSSD */
274 typedef struct _ipp_filter_s
/**** Attribute filter ****/
276 cups_array_t
*ra
; /* Requested attributes */
277 ipp_tag_t group_tag
; /* Group to copy */
280 typedef struct _ipp_job_s _ipp_job_t
;
282 typedef struct _ipp_printer_s
/**** Printer data ****/
284 int ipv4
, /* IPv4 listener */
285 ipv6
; /* IPv6 listener */
286 _ipp_srv_t ipp_ref
, /* Bonjour IPP service */
287 ipps_ref
, /* Bonjour IPPS service */
288 http_ref
, /* Bonjour HTTP service */
289 printer_ref
; /* Bonjour LPD service */
290 char *dnssd_name
, /* printer-dnssd-name */
291 *name
, /* printer-name */
292 *icon
, /* Icon filename */
293 *directory
, /* Spool directory */
294 *hostname
, /* Hostname */
295 *uri
, /* printer-uri-supported */
296 *command
; /* Command to run with job file */
298 size_t urilen
; /* Length of printer URI */
299 ipp_t
*attrs
; /* Static attributes */
300 time_t start_time
; /* Startup time */
301 time_t config_time
; /* printer-config-change-time */
302 ipp_pstate_t state
; /* printer-state value */
303 _ipp_preason_t state_reasons
; /* printer-state-reasons values */
304 time_t state_time
; /* printer-state-change-time */
305 cups_array_t
*jobs
; /* Jobs */
306 _ipp_job_t
*active_job
; /* Current active/pending job */
307 int next_job_id
; /* Next job-id value */
308 _cups_rwlock_t rwlock
; /* Printer lock */
309 _ipp_media_size_t main_size
; /* Ready media */
310 _ipp_media_type_t main_type
;
312 _ipp_media_size_t envelope_size
;
314 _ipp_media_size_t photo_size
;
315 _ipp_media_type_t photo_type
;
317 int supplies
[5]; /* Supply levels (0-100) */
320 struct _ipp_job_s
/**** Job data ****/
323 const char *name
, /* job-name */
324 *username
, /* job-originating-user-name */
325 *format
; /* document-format */
326 ipp_jstate_t state
; /* job-state value */
327 time_t created
, /* time-at-creation value */
328 processing
, /* time-at-processing value */
329 completed
; /* time-at-completed value */
330 int impressions
, /* job-impressions value */
331 impcompleted
; /* job-impressions-completed value */
332 ipp_t
*attrs
; /* Static attributes */
333 int cancel
; /* Non-zero when job canceled */
334 char *filename
; /* Print file name */
335 int fd
; /* Print file descriptor */
336 _ipp_printer_t
*printer
; /* Printer */
339 typedef struct _ipp_client_s
/**** Client data ****/
341 http_t
*http
; /* HTTP connection */
342 ipp_t
*request
, /* IPP request */
343 *response
; /* IPP response */
344 time_t start
; /* Request start time */
345 http_state_t operation
; /* Request operation */
346 ipp_op_t operation_id
; /* IPP operation-id */
347 char uri
[1024], /* Request URI */
348 *options
; /* URI options */
349 http_addr_t addr
; /* Client address */
350 char hostname
[256]; /* Client hostname */
351 _ipp_printer_t
*printer
; /* Printer */
352 _ipp_job_t
*job
; /* Current job, if any */
360 static void clean_jobs(_ipp_printer_t
*printer
);
361 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
362 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
363 ipp_tag_t group_tag
, int quickcopy
);
364 static void copy_job_attributes(_ipp_client_t
*client
,
365 _ipp_job_t
*job
, cups_array_t
*ra
);
366 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
367 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
368 static int create_listener(int family
, int port
);
369 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
370 static ipp_t
*create_media_size(int width
, int length
);
371 static _ipp_printer_t
*create_printer(const char *servername
,
372 const char *name
, const char *location
,
373 const char *make
, const char *model
,
375 const char *docformats
, int ppm
,
376 int ppm_color
, int duplex
, int port
,
377 int pin
, const char *subtype
,
378 const char *directory
,
380 const char *attrfile
);
381 static void debug_attributes(const char *title
, ipp_t
*ipp
,
383 static void delete_client(_ipp_client_t
*client
);
384 static void delete_job(_ipp_job_t
*job
);
385 static void delete_printer(_ipp_printer_t
*printer
);
387 static void DNSSD_API
dnssd_callback(DNSServiceRef sdRef
,
388 DNSServiceFlags flags
,
389 DNSServiceErrorType errorCode
,
393 _ipp_printer_t
*printer
);
394 #elif defined(HAVE_AVAHI)
395 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
396 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
397 #endif /* HAVE_DNSSD */
398 static void dnssd_init(void);
399 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
400 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
401 static ipp_t
*get_collection(FILE *fp
, const char *filename
, int *linenum
);
402 static char *get_token(FILE *fp
, char *buf
, int buflen
, int *linenum
);
403 static void html_escape(_ipp_client_t
*client
, const char *s
,
405 static void html_footer(_ipp_client_t
*client
);
406 static void html_header(_ipp_client_t
*client
, const char *title
);
407 static void html_printf(_ipp_client_t
*client
, const char *format
,
408 ...) __attribute__((__format__(__printf__
,
410 static void ipp_cancel_job(_ipp_client_t
*client
);
411 static void ipp_close_job(_ipp_client_t
*client
);
412 static void ipp_create_job(_ipp_client_t
*client
);
413 static void ipp_get_job_attributes(_ipp_client_t
*client
);
414 static void ipp_get_jobs(_ipp_client_t
*client
);
415 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
416 static void ipp_identify_printer(_ipp_client_t
*client
);
417 static void ipp_print_job(_ipp_client_t
*client
);
418 static void ipp_print_uri(_ipp_client_t
*client
);
419 static void ipp_send_document(_ipp_client_t
*client
);
420 static void ipp_send_uri(_ipp_client_t
*client
);
421 static void ipp_validate_job(_ipp_client_t
*client
);
422 static void load_attributes(const char *filename
, ipp_t
*attrs
);
423 static int parse_options(_ipp_client_t
*client
, cups_option_t
**options
);
424 static void process_attr_message(_ipp_job_t
*job
, char *message
);
425 static void *process_client(_ipp_client_t
*client
);
426 static int process_http(_ipp_client_t
*client
);
427 static int process_ipp(_ipp_client_t
*client
);
428 static void *process_job(_ipp_job_t
*job
);
429 static void process_state_message(_ipp_job_t
*job
, char *message
);
430 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
);
431 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
432 const char *content_coding
,
433 const char *type
, size_t length
);
434 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
435 const char *message
, ...)
436 __attribute__ ((__format__ (__printf__
, 3, 4)));
437 static void respond_unsupported(_ipp_client_t
*client
,
438 ipp_attribute_t
*attr
);
439 static void run_printer(_ipp_printer_t
*printer
);
440 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
441 static void usage(int status
) __attribute__((noreturn
));
442 static int valid_doc_attributes(_ipp_client_t
*client
);
443 static int valid_job_attributes(_ipp_client_t
*client
);
451 static DNSServiceRef DNSSDMaster
= NULL
;
452 #elif defined(HAVE_AVAHI)
453 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
454 static AvahiClient
*DNSSDClient
= NULL
;
455 #endif /* HAVE_DNSSD */
457 static int KeepFiles
= 0,
462 * 'main()' - Main entry to the sample server.
465 int /* O - Exit status */
466 main(int argc
, /* I - Number of command-line args */
467 char *argv
[]) /* I - Command-line arguments */
469 int i
; /* Looping var */
470 const char *opt
, /* Current option character */
471 *attrfile
= NULL
, /* Attributes file */
472 *command
= NULL
, /* Command to run with job files */
473 *servername
= NULL
, /* Server host name */
474 *name
= NULL
, /* Printer name */
475 *location
= "", /* Location of printer */
476 *make
= "Test", /* Manufacturer */
477 *model
= "Printer", /* Model */
478 *icon
= "printer.png", /* Icon file */
479 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
480 /* Supported formats */
482 const char *keypath
= NULL
; /* Keychain path */
483 #endif /* HAVE_SSL */
484 const char *subtype
= "_print"; /* Bonjour service subtype */
485 int port
= 0, /* Port number (0 = auto) */
486 duplex
= 0, /* Duplex mode */
487 ppm
= 10, /* Pages per minute for mono */
488 ppm_color
= 0, /* Pages per minute for color */
489 pin
= 0; /* PIN printing mode? */
490 char directory
[1024] = "", /* Spool directory */
491 hostname
[1024]; /* Auto-detected hostname */
492 _ipp_printer_t
*printer
; /* Printer object */
496 * Parse command-line arguments...
499 for (i
= 1; i
< argc
; i
++)
500 if (argv
[i
][0] == '-')
502 for (opt
= argv
[i
] + 1; *opt
; opt
++)
506 case '2' : /* -2 (enable 2-sided printing) */
511 case 'K' : /* -K keypath */
517 #endif /* HAVE_SSL */
519 case 'M' : /* -M manufacturer */
526 case 'P' : /* -P (PIN printing mode) */
530 case 'a' : /* -a attributes-file */
538 case 'c' : /* -c command */
546 case 'd' : /* -d spool-directory */
550 strlcpy(directory
, argv
[i
], sizeof(directory
));
553 case 'f' : /* -f type/subtype[,...] */
560 case 'h' : /* -h (show help) */
563 case 'i' : /* -i icon.png */
570 case 'k' : /* -k (keep files) */
574 case 'l' : /* -l location */
581 case 'm' : /* -m model */
588 case 'n' : /* -n hostname */
592 servername
= argv
[i
];
595 case 'p' : /* -p port */
597 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
599 port
= atoi(argv
[i
]);
602 case 'r' : /* -r subtype */
609 case 's' : /* -s speed[,color-speed] */
613 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
617 case 'v' : /* -v (be verbose) */
621 default : /* Unknown */
622 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
633 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
641 * Apply defaults as needed...
645 servername
= httpGetHostname(NULL
, hostname
, sizeof(hostname
));
651 * Windows is almost always used as a single user system, so use a default
652 * port number of 8631.
659 * Use 8000 + UID mod 1000 for the default port number...
662 port
= 8000 + ((int)getuid() % 1000);
665 fprintf(stderr
, "Listening on port %d.\n", port
);
670 const char *tmpdir
; /* Temporary directory */
673 if ((tmpdir
= getenv("TEMP")) == NULL
)
675 #elif defined(__APPLE__) && !TARGET_OS_IOS
676 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
677 tmpdir
= "/private/tmp";
679 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
683 snprintf(directory
, sizeof(directory
), "%s/ippserver.%d", tmpdir
, (int)getpid());
685 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
687 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
688 directory
, strerror(errno
));
693 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
697 cupsSetServerCredentials(keypath
, servername
, 1);
698 #endif /* HAVE_SSL */
701 * Initialize Bonjour...
707 * Create the printer...
710 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
711 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
712 subtype
, directory
, command
, attrfile
)) == NULL
)
716 * Run the print service...
719 run_printer(printer
);
722 * Destroy the printer and exit...
725 delete_printer(printer
);
732 * 'clean_jobs()' - Clean out old (completed) jobs.
736 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
738 _ipp_job_t
*job
; /* Current job */
739 time_t cleantime
; /* Clean time */
742 if (cupsArrayCount(printer
->jobs
) == 0)
745 cleantime
= time(NULL
) - 60;
747 _cupsRWLockWrite(&(printer
->rwlock
));
748 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
750 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
751 if (job
->completed
&& job
->completed
< cleantime
)
753 cupsArrayRemove(printer
->jobs
, job
);
758 _cupsRWUnlock(&(printer
->rwlock
));
763 * 'compare_jobs()' - Compare two jobs.
766 static int /* O - Result of comparison */
767 compare_jobs(_ipp_job_t
*a
, /* I - First job */
768 _ipp_job_t
*b
) /* I - Second job */
770 return (b
->id
- a
->id
);
775 * 'copy_attributes()' - Copy attributes from one request to another.
779 copy_attributes(ipp_t
*to
, /* I - Destination request */
780 ipp_t
*from
, /* I - Source request */
781 cups_array_t
*ra
, /* I - Requested attributes */
782 ipp_tag_t group_tag
, /* I - Group to copy */
783 int quickcopy
) /* I - Do a quick copy? */
785 _ipp_filter_t filter
; /* Filter data */
789 filter
.group_tag
= group_tag
;
791 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
796 * 'copy_job_attrs()' - Copy job attributes to the response.
801 _ipp_client_t
*client
, /* I - Client */
802 _ipp_job_t
*job
, /* I - Job */
803 cups_array_t
*ra
) /* I - requested-attributes */
805 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
807 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
810 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
812 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
815 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
818 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
820 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
823 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
824 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
826 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
827 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
829 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
830 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
832 if (!ra
|| cupsArrayFind(ra
, "job-state"))
833 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
834 "job-state", job
->state
);
836 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
840 case IPP_JSTATE_PENDING
:
841 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
844 case IPP_JSTATE_HELD
:
846 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
847 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
848 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
850 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
853 case IPP_JSTATE_PROCESSING
:
855 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
857 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
860 case IPP_JSTATE_STOPPED
:
861 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
864 case IPP_JSTATE_CANCELED
:
865 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
868 case IPP_JSTATE_ABORTED
:
869 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
872 case IPP_JSTATE_COMPLETED
:
873 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
878 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
882 case IPP_JSTATE_PENDING
:
883 ippAddString(client
->response
, IPP_TAG_JOB
,
884 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
888 case IPP_JSTATE_HELD
:
890 ippAddString(client
->response
, IPP_TAG_JOB
,
891 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
892 "job-state-reasons", NULL
, "job-incoming");
893 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
894 ippAddString(client
->response
, IPP_TAG_JOB
,
895 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
896 "job-state-reasons", NULL
, "job-hold-until-specified");
898 ippAddString(client
->response
, IPP_TAG_JOB
,
899 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
900 "job-state-reasons", NULL
, "job-data-insufficient");
903 case IPP_JSTATE_PROCESSING
:
905 ippAddString(client
->response
, IPP_TAG_JOB
,
906 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
907 "job-state-reasons", NULL
, "processing-to-stop-point");
909 ippAddString(client
->response
, IPP_TAG_JOB
,
910 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
911 "job-state-reasons", NULL
, "job-printing");
914 case IPP_JSTATE_STOPPED
:
915 ippAddString(client
->response
, IPP_TAG_JOB
,
916 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
917 NULL
, "job-stopped");
920 case IPP_JSTATE_CANCELED
:
921 ippAddString(client
->response
, IPP_TAG_JOB
,
922 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
923 NULL
, "job-canceled-by-user");
926 case IPP_JSTATE_ABORTED
:
927 ippAddString(client
->response
, IPP_TAG_JOB
,
928 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
929 NULL
, "aborted-by-system");
932 case IPP_JSTATE_COMPLETED
:
933 ippAddString(client
->response
, IPP_TAG_JOB
,
934 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
935 NULL
, "job-completed-successfully");
940 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
941 ippAddInteger(client
->response
, IPP_TAG_JOB
,
942 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
943 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
945 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
946 ippAddInteger(client
->response
, IPP_TAG_JOB
,
947 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
948 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
953 * 'create_client()' - Accept a new network connection and create a client
957 static _ipp_client_t
* /* O - Client */
958 create_client(_ipp_printer_t
*printer
, /* I - Printer */
959 int sock
) /* I - Listen socket */
961 _ipp_client_t
*client
; /* Client */
964 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
966 perror("Unable to allocate memory for client");
970 client
->printer
= printer
;
973 * Accept the client and get the remote address...
976 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
978 perror("Unable to accept client connection");
985 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
988 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
995 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
999 static _ipp_job_t
* /* O - Job */
1000 create_job(_ipp_client_t
*client
) /* I - Client */
1002 _ipp_job_t
*job
; /* Job */
1003 ipp_attribute_t
*attr
; /* Job attribute */
1004 char uri
[1024], /* job-uri value */
1005 uuid
[64]; /* job-uuid value */
1008 _cupsRWLockWrite(&(client
->printer
->rwlock
));
1009 if (client
->printer
->active_job
&&
1010 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
1013 * Only accept a single job at a time...
1016 _cupsRWUnlock(&(client
->printer
->rwlock
));
1021 * Allocate and initialize the job object...
1024 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
1026 perror("Unable to allocate memory for job");
1030 job
->printer
= client
->printer
;
1031 job
->attrs
= ippNew();
1032 job
->state
= IPP_JSTATE_HELD
;
1036 * Copy all of the job attributes...
1039 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
1042 * Get the requesting-user-name, document format, and priority...
1045 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1046 job
->username
= ippGetString(attr
, 0, NULL
);
1048 job
->username
= "anonymous";
1050 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1052 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1054 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1055 job
->format
= ippGetString(attr
, 0, NULL
);
1056 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1057 job
->format
= ippGetString(attr
, 0, NULL
);
1059 job
->format
= "application/octet-stream";
1062 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1063 job
->impressions
= ippGetInteger(attr
, 0);
1065 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1066 job
->name
= ippGetString(attr
, 0, NULL
);
1069 * Add job description attributes and add to the jobs array...
1072 job
->id
= client
->printer
->next_job_id
++;
1074 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1075 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1077 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1078 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1079 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1080 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1081 if ((attr
= ippFindAttribute(client
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
1082 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, ippGetString(attr
, 0, NULL
));
1084 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1085 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1087 cupsArrayAdd(client
->printer
->jobs
, job
);
1088 client
->printer
->active_job
= job
;
1090 _cupsRWUnlock(&(client
->printer
->rwlock
));
1097 * 'create_job_filename()' - Create the filename for a document in a job.
1100 static void create_job_filename(
1101 _ipp_printer_t
*printer
, /* I - Printer */
1102 _ipp_job_t
*job
, /* I - Job */
1103 char *fname
, /* I - Filename buffer */
1104 size_t fnamesize
) /* I - Size of filename buffer */
1106 char name
[256], /* "Safe" filename */
1107 *nameptr
; /* Pointer into filename */
1108 const char *ext
, /* Filename extension */
1109 *job_name
; /* job-name value */
1110 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1114 * Make a name from the job-name attribute...
1117 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1118 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1120 job_name
= "untitled";
1122 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1123 if (isalnum(*job_name
& 255) || *job_name
== '-')
1124 *nameptr
++ = (char)tolower(*job_name
& 255);
1131 * Figure out the extension...
1134 if (!strcasecmp(job
->format
, "image/jpeg"))
1136 else if (!strcasecmp(job
->format
, "image/png"))
1138 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1140 else if (!strcasecmp(job
->format
, "image/urf"))
1142 else if (!strcasecmp(job
->format
, "application/pdf"))
1144 else if (!strcasecmp(job
->format
, "application/postscript"))
1150 * Create a filename with the job-id, job-name, and document-format (extension)...
1153 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1158 * 'create_listener()' - Create a listener socket.
1161 static int /* O - Listener socket or -1 on error */
1162 create_listener(int family
, /* I - Address family */
1163 int port
) /* I - Port number */
1165 int sock
; /* Listener socket */
1166 http_addrlist_t
*addrlist
; /* Listen address */
1167 char service
[255]; /* Service port */
1170 snprintf(service
, sizeof(service
), "%d", port
);
1171 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1174 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1176 httpAddrFreeList(addrlist
);
1183 * 'create_media_col()' - Create a media-col value.
1186 static ipp_t
* /* O - media-col collection */
1187 create_media_col(const char *media
, /* I - Media name */
1188 const char *source
, /* I - Media source */
1189 const char *type
, /* I - Media type */
1190 int width
, /* I - x-dimension in 2540ths */
1191 int length
, /* I - y-dimension in 2540ths */
1192 int margins
) /* I - Value for margins */
1194 ipp_t
*media_col
= ippNew(), /* media-col value */
1195 *media_size
= create_media_size(width
, length
);
1196 /* media-size value */
1197 char media_key
[256]; /* media-key value */
1201 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1203 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1205 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1207 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1209 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1211 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1212 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1213 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1214 "media-bottom-margin", margins
);
1215 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1216 "media-left-margin", margins
);
1217 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1218 "media-right-margin", margins
);
1219 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1220 "media-top-margin", margins
);
1222 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1224 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1226 ippDelete(media_size
);
1233 * 'create_media_size()' - Create a media-size value.
1236 static ipp_t
* /* O - media-col collection */
1237 create_media_size(int width
, /* I - x-dimension in 2540ths */
1238 int length
) /* I - y-dimension in 2540ths */
1240 ipp_t
*media_size
= ippNew(); /* media-size value */
1243 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1245 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1248 return (media_size
);
1253 * 'create_printer()' - Create, register, and listen for connections to a
1257 static _ipp_printer_t
* /* O - Printer */
1258 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1259 const char *name
, /* I - printer-name */
1260 const char *location
, /* I - printer-location */
1261 const char *make
, /* I - printer-make-and-model */
1262 const char *model
, /* I - printer-make-and-model */
1263 const char *icon
, /* I - printer-icons */
1264 const char *docformats
, /* I - document-format-supported */
1265 int ppm
, /* I - Pages per minute in grayscale */
1266 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1267 int duplex
, /* I - 1 = duplex, 0 = simplex */
1268 int port
, /* I - Port for listeners or 0 for auto */
1269 int pin
, /* I - Require PIN printing */
1270 const char *subtype
, /* I - Bonjour service subtype */
1271 const char *directory
, /* I - Spool directory */
1272 const char *command
, /* I - Command to run on job files */
1273 const char *attrfile
) /* I - Attributes file */
1275 int i
, j
; /* Looping vars */
1276 _ipp_printer_t
*printer
; /* Printer */
1278 char path
[1024]; /* Full path to command */
1280 char uri
[1024], /* Printer URI */
1282 securi
[1024], /* Secure printer URI */
1283 *uris
[2], /* All URIs */
1284 #endif /* HAVE_SSL */
1285 icons
[1024], /* printer-icons URI */
1286 adminurl
[1024], /* printer-more-info URI */
1287 supplyurl
[1024],/* printer-supply-info-uri URI */
1288 device_id
[1024],/* printer-device-id */
1289 make_model
[128],/* printer-make-and-model */
1290 uuid
[128]; /* printer-uuid */
1291 int num_formats
; /* Number of document-format-supported values */
1292 char *defformat
, /* document-format-default value */
1293 *formats
[100], /* document-format-supported values */
1294 *ptr
; /* Pointer into string */
1295 const char *prefix
; /* Prefix string */
1296 int num_database
; /* Number of database values */
1297 ipp_attribute_t
*media_col_database
,
1298 /* media-col-database value */
1299 *media_size_supported
;
1300 /* media-size-supported value */
1301 ipp_t
*media_col_default
;
1302 /* media-col-default value */
1303 int media_col_index
;/* Current media-col-database value */
1304 int k_supported
; /* Maximum file size supported */
1306 struct statvfs spoolinfo
; /* FS info for spool directory */
1307 double spoolsize
; /* FS size */
1308 #elif defined(HAVE_STATFS)
1309 struct statfs spoolinfo
; /* FS info for spool directory */
1310 double spoolsize
; /* FS size */
1311 #endif /* HAVE_STATVFS */
1312 static const int orients
[4] = /* orientation-requested-supported values */
1314 IPP_ORIENT_PORTRAIT
,
1315 IPP_ORIENT_LANDSCAPE
,
1316 IPP_ORIENT_REVERSE_LANDSCAPE
,
1317 IPP_ORIENT_REVERSE_PORTRAIT
1319 static const char * const versions
[] =/* ipp-versions-supported values */
1325 static const char * const features
[] =/* ipp-features-supported values */
1329 static const int ops
[] = /* operations-supported values */
1333 IPP_OP_VALIDATE_JOB
,
1335 IPP_OP_SEND_DOCUMENT
,
1338 IPP_OP_GET_JOB_ATTRIBUTES
,
1340 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1341 IPP_OP_CANCEL_MY_JOBS
,
1343 IPP_OP_IDENTIFY_PRINTER
1345 static const char * const charsets
[] =/* charset-supported values */
1350 static const char * const compressions
[] =/* compression-supported values */
1355 #endif /* HAVE_LIBZ */
1358 static const char * const identify_actions
[] =
1363 static const char * const job_creation
[] =
1364 { /* job-creation-attributes-supported values */
1366 "ipp-attribute-fidelity",
1368 "job-accounting-user-id",
1374 "multiple-document-handling",
1375 "orientation-requested",
1379 static const char * const media_col_supported
[] =
1380 { /* media-col-supported values */
1381 "media-bottom-margin",
1382 "media-left-margin",
1383 "media-right-margin",
1389 static const int media_xxx_margin_supported
[] =
1390 { /* media-xxx-margin-supported values */
1394 static const char * const multiple_document_handling
[] =
1395 { /* multiple-document-handling-supported values */
1396 "separate-documents-uncollated-copies",
1397 "separate-documents-collated-copies"
1399 static const char * const overrides
[] =
1400 { /* overrides-supported */
1404 static const char * const print_color_mode_supported
[] =
1405 { /* print-color-mode-supported values */
1410 static const int print_quality_supported
[] =
1411 { /* print-quality-supported values */
1416 static const int pwg_raster_document_resolution_supported
[] =
1421 static const char * const pwg_raster_document_type_supported
[] =
1429 static const char * const reference_uri_schemes_supported
[] =
1430 { /* reference-uri-schemes-supported */
1436 #endif /* HAVE_SSL */
1438 static const char * const sides_supported
[] =
1439 { /* sides-supported values */
1441 "two-sided-long-edge",
1442 "two-sided-short-edge"
1444 static const char * const urf_supported
[] =
1445 { /* urf-supported values */
1448 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1456 static const char * const uri_authentication_supported
[] =
1457 { /* uri-authentication-supported values */
1461 static const char * const uri_security_supported
[] =
1462 { /* uri-security-supported values */
1466 #endif /* HAVE_SSL */
1467 static const char * const which_jobs
[] =
1468 { /* which-jobs-supported values */
1477 "processing-stopped"
1483 * If a command was specified, make sure it exists and is executable...
1488 if (*command
== '/' || !strncmp(command
, "./", 2))
1490 if (access(command
, X_OK
))
1492 fprintf(stderr
, "ippserver: Unable to execute command \"%s\": %s\n", command
, strerror(errno
));
1498 if (!cupsFileFind(command
, getenv("PATH"), 1, path
, sizeof(path
)))
1500 fprintf(stderr
, "ippserver: Unable to find command \"%s\".\n", command
);
1510 * Allocate memory for the printer...
1513 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1515 perror("ippserver: Unable to allocate memory for printer");
1521 printer
->name
= strdup(name
);
1522 printer
->dnssd_name
= strdup(printer
->name
);
1523 printer
->command
= command
? strdup(command
) : NULL
;
1524 printer
->directory
= strdup(directory
);
1525 printer
->hostname
= strdup(servername
);
1526 printer
->port
= port
;
1527 printer
->start_time
= time(NULL
);
1528 printer
->config_time
= printer
->start_time
;
1529 printer
->state
= IPP_PSTATE_IDLE
;
1530 printer
->state_reasons
= _IPP_PREASON_NONE
;
1531 printer
->state_time
= printer
->start_time
;
1532 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1533 printer
->next_job_id
= 1;
1535 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1536 printer
->uri
= strdup(uri
);
1537 printer
->urilen
= strlen(uri
);
1540 httpAssembleURI(HTTP_URI_CODING_ALL
, securi
, sizeof(securi
), "ipps", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1541 #endif /* HAVE_SSL */
1544 printer
->icon
= strdup(icon
);
1546 printer
->main_size
= _IPP_MEDIA_SIZE_A4
;
1547 printer
->main_type
= _IPP_MEDIA_TYPE_STATIONERY
;
1548 printer
->main_level
= 500;
1550 printer
->envelope_size
= _IPP_MEDIA_SIZE_NONE
;
1551 printer
->envelope_level
= 0;
1553 printer
->photo_size
= _IPP_MEDIA_SIZE_NONE
;
1554 printer
->photo_type
= _IPP_MEDIA_TYPE_NONE
;
1555 printer
->photo_level
= 0;
1557 printer
->supplies
[_IPP_SUPPLY_CYAN
] = 100;
1558 printer
->supplies
[_IPP_SUPPLY_MAGENTA
] = 100;
1559 printer
->supplies
[_IPP_SUPPLY_YELLOW
] = 100;
1560 printer
->supplies
[_IPP_SUPPLY_BLACK
] = 100;
1561 printer
->supplies
[_IPP_SUPPLY_WASTE
] = 0;
1563 _cupsRWInit(&(printer
->rwlock
));
1566 * Create the listener sockets...
1569 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1571 perror("Unable to create IPv4 listener");
1575 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1577 perror("Unable to create IPv6 listener");
1582 * Prepare values for the printer attributes...
1585 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/icon.png");
1586 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/");
1587 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/supplies");
1591 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1592 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1593 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1596 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1599 formats
[0] = strdup(docformats
);
1600 defformat
= formats
[0];
1601 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1604 formats
[num_formats
++] = ptr
;
1606 if (!strcasecmp(ptr
, "application/octet-stream"))
1610 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1611 ptr
= device_id
+ strlen(device_id
);
1613 for (i
= 0; i
< num_formats
; i
++)
1615 if (!strcasecmp(formats
[i
], "application/pdf"))
1616 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1617 else if (!strcasecmp(formats
[i
], "application/postscript"))
1618 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1619 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1620 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1621 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1622 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1623 else if (!strcasecmp(formats
[i
], "image/png"))
1624 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1625 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1626 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1631 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
1638 * Get the maximum spool size based on the size of the filesystem used for
1639 * the spool directory. If the host OS doesn't support the statfs call
1640 * or the filesystem is larger than 2TiB, always report INT_MAX.
1644 if (statvfs(printer
->directory
, &spoolinfo
))
1645 k_supported
= INT_MAX
;
1646 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1647 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1648 k_supported
= INT_MAX
;
1650 k_supported
= (int)spoolsize
;
1652 #elif defined(HAVE_STATFS)
1653 if (statfs(printer
->directory
, &spoolinfo
))
1654 k_supported
= INT_MAX
;
1655 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1656 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1657 k_supported
= INT_MAX
;
1659 k_supported
= (int)spoolsize
;
1662 k_supported
= INT_MAX
;
1663 #endif /* HAVE_STATVFS */
1666 * Create the printer attributes. This list of attributes is sorted to improve
1667 * performance when the client provides a requested-attributes attribute...
1670 printer
->attrs
= ippNew();
1673 load_attributes(attrfile
, printer
->attrs
);
1675 /* charset-configured */
1676 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-configured", NULL
, "utf-8");
1678 /* charset-supported */
1679 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]), NULL
, charsets
);
1681 /* color-supported */
1682 if (!ippFindAttribute(printer
->attrs
, "color-supported", IPP_TAG_ZERO
))
1683 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
1685 /* compression-supported */
1686 if (!ippFindAttribute(printer
->attrs
, "compression-supported", IPP_TAG_ZERO
))
1687 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "compression-supported", (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
, compressions
);
1689 /* copies-default */
1690 if (!ippFindAttribute(printer
->attrs
, "copies-default", IPP_TAG_ZERO
))
1691 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
1693 /* copies-supported */
1694 if (!ippFindAttribute(printer
->attrs
, "copies-supported", IPP_TAG_ZERO
))
1695 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1697 /* document-format-default */
1698 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1699 "document-format-default", NULL
, defformat
);
1701 /* document-format-supported */
1702 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1703 "document-format-supported", num_formats
, NULL
,
1704 (const char * const *)formats
);
1706 /* document-password-supported */
1707 if (!ippFindAttribute(printer
->attrs
, "document-password-supported", IPP_TAG_ZERO
))
1708 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1710 /* finishings-default */
1711 if (!ippFindAttribute(printer
->attrs
, "finishings-default", IPP_TAG_ZERO
))
1712 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
1714 /* finishings-supported */
1715 if (!ippFindAttribute(printer
->attrs
, "finishings-supported", IPP_TAG_ZERO
))
1716 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
1718 /* generated-natural-language-supported */
1719 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "generated-natural-language-supported", NULL
, "en");
1721 /* identify-actions-default */
1722 if (!ippFindAttribute(printer
->attrs
, "identify-actions-default", IPP_TAG_ZERO
))
1723 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1725 /* identify-actions-supported */
1726 if (!ippFindAttribute(printer
->attrs
, "identify-actions-supported", IPP_TAG_ZERO
))
1727 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
);
1729 /* ipp-features-supported */
1730 if (!ippFindAttribute(printer
->attrs
, "ipp-features-supported", IPP_TAG_ZERO
))
1731 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1733 /* ipp-versions-supported */
1734 if (!ippFindAttribute(printer
->attrs
, "ipp-versions-supported", IPP_TAG_ZERO
))
1735 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1737 /* job-account-id-default */
1738 if (!ippFindAttribute(printer
->attrs
, "job-account-id-default", IPP_TAG_ZERO
))
1739 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1741 /* job-account-id-supported */
1742 if (!ippFindAttribute(printer
->attrs
, "job-account-id-supported", IPP_TAG_ZERO
))
1743 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1745 /* job-accounting-user-id-default */
1746 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-default", IPP_TAG_ZERO
))
1747 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1749 /* job-accounting-user-id-supported */
1750 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-supported", IPP_TAG_ZERO
))
1751 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1753 /* job-creation-attributes-supported */
1754 if (!ippFindAttribute(printer
->attrs
, "job-creation-attributes-supported", IPP_TAG_ZERO
))
1755 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
);
1757 /* job-ids-supported */
1758 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1760 /* job-k-octets-supported */
1761 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1764 /* job-password-supported */
1765 if (!ippFindAttribute(printer
->attrs
, "job-password-supported", IPP_TAG_ZERO
))
1766 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-password-supported", 4);
1768 /* job-priority-default */
1769 if (!ippFindAttribute(printer
->attrs
, "job-priority-default", IPP_TAG_ZERO
))
1770 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-default", 50);
1772 /* job-priority-supported */
1773 if (!ippFindAttribute(printer
->attrs
, "job-priority-supported", IPP_TAG_ZERO
))
1774 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-supported", 100);
1776 /* job-sheets-default */
1777 if (!ippFindAttribute(printer
->attrs
, "job-sheets-default", IPP_TAG_ZERO
))
1778 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-default", NULL
, "none");
1780 /* job-sheets-supported */
1781 if (!ippFindAttribute(printer
->attrs
, "job-sheets-supported", IPP_TAG_ZERO
))
1782 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-supported", NULL
, "none");
1784 /* media-bottom-margin-supported */
1785 if (!ippFindAttribute(printer
->attrs
, "media-bottom-margin-supported", IPP_TAG_ZERO
))
1786 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
);
1788 /* media-col-database */
1789 if (!ippFindAttribute(printer
->attrs
, "media-col-database", IPP_TAG_ZERO
))
1791 for (num_database
= 0, i
= 0;
1792 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1795 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1796 num_database
+= 3; /* auto + manual + envelope */
1797 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1798 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1800 num_database
+= 2; /* Regular + borderless */
1803 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-database", num_database
, NULL
);
1804 for (media_col_index
= 0, i
= 0;
1805 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1808 switch (media_col_sizes
[i
][2])
1812 * Regular + borderless for the general class; no source/type
1816 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]));
1817 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]));
1820 case _IPP_ENV_ONLY
:
1822 * Regular margins for "auto", "manual", and "envelope" sources.
1825 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]));
1826 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]));
1827 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]));
1829 case _IPP_PHOTO_ONLY
:
1831 * Photos have specific media types and can only be printed via
1832 * the auto, manual, and photo sources...
1836 j
< (int)(sizeof(media_type_supported
) /
1837 sizeof(media_type_supported
[0]));
1840 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1843 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]));
1844 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]));
1845 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]));
1852 /* media-col-default */
1853 if (!ippFindAttribute(printer
->attrs
, "media-col-default", IPP_TAG_ZERO
))
1855 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]);
1857 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1859 ippDelete(media_col_default
);
1862 /* media-col-supported */
1863 if (!ippFindAttribute(printer
->attrs
, "media-col-supported", IPP_TAG_ZERO
))
1864 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
);
1867 if (!ippFindAttribute(printer
->attrs
, "media-default", IPP_TAG_ZERO
))
1868 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media_supported
[0]);
1870 /* media-left-margin-supported */
1871 if (!ippFindAttribute(printer
->attrs
, "media-left-margin-supported", IPP_TAG_ZERO
))
1872 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
);
1874 /* media-right-margin-supported */
1875 if (!ippFindAttribute(printer
->attrs
, "media-right-margin-supported", IPP_TAG_ZERO
))
1876 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
);
1878 /* media-supported */
1879 if (!ippFindAttribute(printer
->attrs
, "media-supported", IPP_TAG_ZERO
))
1880 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
);
1882 /* media-size-supported */
1883 if (!ippFindAttribute(printer
->attrs
, "media-size-supported", IPP_TAG_ZERO
))
1885 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-size-supported", (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0])), NULL
);
1888 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1891 ipp_t
*size
= create_media_size(media_col_sizes
[i
][0], media_col_sizes
[i
][1]);
1893 ippSetCollection(printer
->attrs
, &media_size_supported
, i
, size
);
1898 /* media-source-supported */
1899 if (!ippFindAttribute(printer
->attrs
, "media-source-supported", IPP_TAG_ZERO
))
1900 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
);
1902 /* media-top-margin-supported */
1903 if (!ippFindAttribute(printer
->attrs
, "media-top-margin-supported", IPP_TAG_ZERO
))
1904 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
);
1906 /* media-type-supported */
1907 if (!ippFindAttribute(printer
->attrs
, "media-type-supported", IPP_TAG_ZERO
))
1908 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
);
1910 /* multiple-document-handling-supported */
1911 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
);
1913 /* multiple-document-jobs-supported */
1914 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1916 /* multiple-operation-time-out */
1917 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1919 /* multiple-operation-time-out-action */
1920 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1922 /* natural-language-configured */
1923 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1924 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1925 "natural-language-configured", NULL
, "en");
1927 /* number-up-default */
1928 if (!ippFindAttribute(printer
->attrs
, "number-up-default", IPP_TAG_ZERO
))
1929 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-default", 1);
1931 /* number-up-supported */
1932 if (!ippFindAttribute(printer
->attrs
, "number-up-supported", IPP_TAG_ZERO
))
1933 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-supported", 1);
1935 /* operations-supported */
1936 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1938 /* orientation-requested-default */
1939 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-default", IPP_TAG_ZERO
))
1940 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "orientation-requested-default", 0);
1942 /* orientation-requested-supported */
1943 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-supported", IPP_TAG_ZERO
))
1944 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", 4, orients
);
1946 /* output-bin-default */
1947 if (!ippFindAttribute(printer
->attrs
, "output-bin-default", IPP_TAG_ZERO
))
1948 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
1950 /* output-bin-supported */
1951 if (!ippFindAttribute(printer
->attrs
, "output-bin-supported", IPP_TAG_ZERO
))
1952 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
1954 /* overrides-supported */
1955 if (!ippFindAttribute(printer
->attrs
, "overrides-supported", IPP_TAG_ZERO
))
1956 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1958 /* page-ranges-supported */
1959 if (!ippFindAttribute(printer
->attrs
, "page-ranges-supported", IPP_TAG_ZERO
))
1960 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1962 /* pages-per-minute */
1963 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1964 "pages-per-minute", ppm
);
1966 /* pages-per-minute-color */
1968 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1969 "pages-per-minute-color", ppm_color
);
1971 /* pdl-override-supported */
1972 if (!ippFindAttribute(printer
->attrs
, "pdl-override-supported", IPP_TAG_ZERO
))
1973 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
1975 /* preferred-attributes-supported */
1976 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "preferred-attributes-supported", 0);
1978 /* print-color-mode-default */
1979 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-default", IPP_TAG_ZERO
))
1980 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1982 /* print-color-mode-supported */
1983 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-supported", IPP_TAG_ZERO
))
1984 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
);
1986 /* print-content-optimize-default */
1987 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-default", IPP_TAG_ZERO
))
1988 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1990 /* print-content-optimize-supported */
1991 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-supported", IPP_TAG_ZERO
))
1992 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
1994 /* print-rendering-intent-default */
1995 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-default", IPP_TAG_ZERO
))
1996 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
1998 /* print-rendering-intent-supported */
1999 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-supported", IPP_TAG_ZERO
))
2000 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
2002 /* print-quality-default */
2003 if (!ippFindAttribute(printer
->attrs
, "print-quality-default", IPP_TAG_ZERO
))
2004 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
2006 /* print-quality-supported */
2007 if (!ippFindAttribute(printer
->attrs
, "print-quality-supported", IPP_TAG_ZERO
))
2008 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
);
2010 /* printer-device-id */
2011 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2012 "printer-device-id", NULL
, device_id
);
2014 /* printer-get-attributes-supported */
2015 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
2017 /* printer-geo-location */
2018 if (!ippFindAttribute(printer
->attrs
, "printer-geo-location", IPP_TAG_ZERO
))
2019 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
2022 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
2023 "printer-icons", NULL
, icons
);
2025 /* printer-is-accepting-jobs */
2026 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
2029 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
2031 /* printer-location */
2032 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2033 "printer-location", NULL
, location
);
2035 /* printer-make-and-model */
2036 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2037 "printer-make-and-model", NULL
, make_model
);
2039 /* printer-mandatory-job-attributes */
2040 if (pin
&& !ippFindAttribute(printer
->attrs
, "", IPP_TAG_ZERO
))
2042 static const char * const names
[] =
2045 "job-accounting-user-id",
2049 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2050 "printer-mandatory-job-attributes",
2051 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
2054 /* printer-more-info */
2055 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
2058 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
2060 /* printer-organization */
2061 if (!ippFindAttribute(printer
->attrs
, "printer-organization", IPP_TAG_ZERO
))
2062 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
2064 /* printer-organizational-unit */
2065 if (!ippFindAttribute(printer
->attrs
, "printer-organizational-unit", IPP_TAG_ZERO
))
2066 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
2068 /* printer-resolution-default */
2069 if (!ippFindAttribute(printer
->attrs
, "printer-resolution-default", IPP_TAG_ZERO
))
2070 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
2072 /* printer-resolution-supported */
2073 if (!ippFindAttribute(printer
->attrs
, "printer-resolutions-supported", IPP_TAG_ZERO
))
2074 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
2076 /* printer-supply-description */
2077 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
);
2079 /* printer-supply-info-uri */
2080 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
2082 /* printer-uri-supported */
2087 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", 2, NULL
, (const char **)uris
);
2090 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
2091 #endif /* HAVE_SSL */
2094 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
2095 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
2097 /* pwg-raster-document-xxx-supported */
2098 for (i
= 0; i
< num_formats
; i
++)
2099 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
2102 if (i
< num_formats
)
2104 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-resolution-supported", IPP_TAG_ZERO
))
2105 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
);
2106 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-sheet-back", IPP_TAG_ZERO
))
2107 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "pwg-raster-document-sheet-back", NULL
, "normal");
2108 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-type-supported", IPP_TAG_ZERO
))
2109 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
);
2112 /* reference-uri-scheme-supported */
2113 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
);
2116 if (!ippFindAttribute(printer
->attrs
, "sides-default", IPP_TAG_ZERO
))
2117 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
2119 /* sides-supported */
2120 if (!ippFindAttribute(printer
->attrs
, "sides-supported", IPP_TAG_ZERO
))
2121 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
2124 for (i
= 0; i
< num_formats
; i
++)
2125 if (!strcasecmp(formats
[i
], "image/urf"))
2128 if (i
< num_formats
&& !ippFindAttribute(printer
->attrs
, "urf-supported", IPP_TAG_ZERO
))
2129 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2131 /* uri-authentication-supported */
2133 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_supported
);
2135 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "none");
2136 #endif /* HAVE_SSL */
2138 /* uri-security-supported */
2140 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", 2, NULL
, uri_security_supported
);
2142 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", NULL
, "none");
2143 #endif /* HAVE_SSL */
2145 /* which-jobs-supported */
2146 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
);
2150 debug_attributes("Printer", printer
->attrs
, 0);
2153 * Register the printer with Bonjour...
2156 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2167 * If we get here we were unable to create the printer...
2172 delete_printer(printer
);
2178 * 'debug_attributes()' - Print attributes in a request or response.
2182 debug_attributes(const char *title
, /* I - Title */
2183 ipp_t
*ipp
, /* I - Request/response */
2184 int type
) /* I - 0 = object, 1 = request, 2 = response */
2186 ipp_tag_t group_tag
; /* Current group */
2187 ipp_attribute_t
*attr
; /* Current attribute */
2188 char buffer
[2048]; /* String buffer for value */
2189 int major
, minor
; /* Version */
2195 fprintf(stderr
, "%s:\n", title
);
2196 major
= ippGetVersion(ipp
, &minor
);
2197 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2199 fprintf(stderr
, " operation-id=%s(%04x)\n",
2200 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2202 fprintf(stderr
, " status-code=%s(%04x)\n",
2203 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2204 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2206 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2208 attr
= ippNextAttribute(ipp
))
2210 if (ippGetGroupTag(attr
) != group_tag
)
2212 group_tag
= ippGetGroupTag(attr
);
2213 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2216 if (ippGetName(attr
))
2218 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2219 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2220 ippGetCount(attr
) > 1 ? "1setOf " : "",
2221 ippTagString(ippGetValueTag(attr
)), buffer
);
2228 * 'delete_client()' - Close the socket and free all memory used by a client
2233 delete_client(_ipp_client_t
*client
) /* I - Client */
2236 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2239 * Flush pending writes before closing...
2242 httpFlushWrite(client
->http
);
2248 httpClose(client
->http
);
2250 ippDelete(client
->request
);
2251 ippDelete(client
->response
);
2258 * 'delete_job()' - Remove from the printer and free all memory used by a job
2263 delete_job(_ipp_job_t
*job
) /* I - Job */
2266 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2268 ippDelete(job
->attrs
);
2273 unlink(job
->filename
);
2275 free(job
->filename
);
2283 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2284 * used by a printer object.
2288 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2290 if (printer
->ipv4
>= 0)
2291 close(printer
->ipv4
);
2293 if (printer
->ipv6
>= 0)
2294 close(printer
->ipv6
);
2297 if (printer
->printer_ref
)
2298 DNSServiceRefDeallocate(printer
->printer_ref
);
2299 if (printer
->ipp_ref
)
2300 DNSServiceRefDeallocate(printer
->ipp_ref
);
2301 if (printer
->ipps_ref
)
2302 DNSServiceRefDeallocate(printer
->ipps_ref
);
2303 if (printer
->http_ref
)
2304 DNSServiceRefDeallocate(printer
->http_ref
);
2305 #elif defined(HAVE_AVAHI)
2306 avahi_threaded_poll_lock(DNSSDMaster
);
2308 if (printer
->printer_ref
)
2309 avahi_entry_group_free(printer
->printer_ref
);
2310 if (printer
->ipp_ref
)
2311 avahi_entry_group_free(printer
->ipp_ref
);
2312 if (printer
->ipps_ref
)
2313 avahi_entry_group_free(printer
->ipps_ref
);
2314 if (printer
->http_ref
)
2315 avahi_entry_group_free(printer
->http_ref
);
2317 avahi_threaded_poll_unlock(DNSSDMaster
);
2318 #endif /* HAVE_DNSSD */
2320 if (printer
->dnssd_name
)
2321 free(printer
->dnssd_name
);
2323 free(printer
->name
);
2325 free(printer
->icon
);
2326 if (printer
->command
)
2327 free(printer
->command
);
2328 if (printer
->directory
)
2329 free(printer
->directory
);
2330 if (printer
->hostname
)
2331 free(printer
->hostname
);
2335 ippDelete(printer
->attrs
);
2336 cupsArrayDelete(printer
->jobs
);
2344 * 'dnssd_callback()' - Handle Bonjour registration events.
2347 static void DNSSD_API
2349 DNSServiceRef sdRef
, /* I - Service reference */
2350 DNSServiceFlags flags
, /* I - Status flags */
2351 DNSServiceErrorType errorCode
, /* I - Error, if any */
2352 const char *name
, /* I - Service name */
2353 const char *regtype
, /* I - Service type */
2354 const char *domain
, /* I - Domain for service */
2355 _ipp_printer_t
*printer
) /* I - Printer */
2363 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2364 regtype
, (int)errorCode
);
2367 else if (strcasecmp(name
, printer
->dnssd_name
))
2370 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2372 /* No lock needed since only the main thread accesses/changes this */
2373 free(printer
->dnssd_name
);
2374 printer
->dnssd_name
= strdup(name
);
2379 #elif defined(HAVE_AVAHI)
2381 * 'dnssd_callback()' - Handle Bonjour registration events.
2386 AvahiEntryGroup
*srv
, /* I - Service */
2387 AvahiEntryGroupState state
, /* I - Registration state */
2388 void *context
) /* I - Printer */
2397 * 'dnssd_client_cb()' - Client callback for Avahi.
2399 * Called whenever the client or server state changes...
2404 AvahiClient
*c
, /* I - Client */
2405 AvahiClientState state
, /* I - Current state */
2406 void *userdata
) /* I - User data (unused) */
2416 fprintf(stderr
, "Ignore Avahi state %d.\n", state
);
2419 case AVAHI_CLIENT_FAILURE
:
2420 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
2422 fputs("Avahi server crashed, exiting.\n", stderr
);
2428 #endif /* HAVE_DNSSD */
2432 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2439 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2441 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2445 #elif defined(HAVE_AVAHI)
2446 int error
; /* Error code, if any */
2448 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2450 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2454 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2456 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2460 avahi_threaded_poll_start(DNSSDMaster
);
2461 #endif /* HAVE_DNSSD */
2466 * 'filter_cb()' - Filter printer attributes based on the requested array.
2469 static int /* O - 1 to copy, 0 to ignore */
2470 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2471 ipp_t
*dst
, /* I - Destination (unused) */
2472 ipp_attribute_t
*attr
) /* I - Source attribute */
2475 * Filter attributes as needed...
2478 #ifndef WIN32 /* Avoid MS compiler bug */
2482 ipp_tag_t group
= ippGetGroupTag(attr
);
2483 const char *name
= ippGetName(attr
);
2485 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
)))
2488 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2493 * 'find_job()' - Find a job specified in a request.
2496 static _ipp_job_t
* /* O - Job or NULL */
2497 find_job(_ipp_client_t
*client
) /* I - Client */
2499 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2500 _ipp_job_t key
, /* Job search key */
2501 *job
; /* Matching job, if any */
2504 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2506 const char *uri
= ippGetString(attr
, 0, NULL
);
2508 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2509 uri
[client
->printer
->urilen
] == '/')
2510 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2514 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2515 key
.id
= ippGetInteger(attr
, 0);
2517 _cupsRWLockRead(&(client
->printer
->rwlock
));
2518 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2519 _cupsRWUnlock(&(client
->printer
->rwlock
));
2526 * 'get_collection()' - Get a collection value from a file.
2529 static ipp_t
* /* O - Collection value */
2530 get_collection(FILE *fp
, /* I - File to read from */
2531 const char *filename
, /* I - Attributes filename */
2532 int *linenum
) /* IO - Line number */
2534 char token
[1024], /* Token from file */
2535 attr
[128]; /* Attribute name */
2536 ipp_tag_t value
; /* Current value type */
2537 ipp_t
*col
= ippNew(); /* Collection value */
2538 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
2541 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
2543 if (!strcmp(token
, "}"))
2545 else if (!strcmp(token
, "{") && lastcol
)
2548 * Another collection value
2551 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2552 /* Collection value */
2555 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
2559 else if (!_cups_strcasecmp(token
, "MEMBER"))
2567 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2569 fprintf(stderr
, "ippserver: Missing MEMBER value tag on line %d of \"%s\".\n", *linenum
, filename
);
2573 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
2575 fprintf(stderr
, "ippserver: Bad MEMBER value tag \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2579 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
2581 fprintf(stderr
, "ippserver: Missing MEMBER name on line %d of \"%s\".\n", *linenum
, filename
);
2585 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2587 fprintf(stderr
, "ippserver: Missing MEMBER value on line %d of \"%s\".\n", *linenum
, filename
);
2593 case IPP_TAG_BOOLEAN
:
2594 if (!_cups_strcasecmp(token
, "true"))
2595 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
2597 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
2600 case IPP_TAG_INTEGER
:
2602 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
2605 case IPP_TAG_RESOLUTION
:
2607 int xres
, /* X resolution */
2608 yres
; /* Y resolution */
2609 char units
[6]; /* Units */
2611 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
2612 (_cups_strcasecmp(units
, "dpi") &&
2613 _cups_strcasecmp(units
, "dpc") &&
2614 _cups_strcasecmp(units
, "dpcm") &&
2615 _cups_strcasecmp(units
, "other")))
2617 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2621 if (!_cups_strcasecmp(units
, "dpi"))
2622 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
2623 else if (!_cups_strcasecmp(units
, "dpc") ||
2624 !_cups_strcasecmp(units
, "dpcm"))
2625 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
2627 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
2631 case IPP_TAG_RANGE
:
2633 int lowers
[4], /* Lower value */
2634 uppers
[4], /* Upper values */
2635 num_vals
; /* Number of values */
2638 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
2639 lowers
+ 0, uppers
+ 0,
2640 lowers
+ 1, uppers
+ 1,
2641 lowers
+ 2, uppers
+ 2,
2642 lowers
+ 3, uppers
+ 3);
2644 if ((num_vals
& 1) || num_vals
== 0)
2646 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2650 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
2655 case IPP_TAG_BEGIN_COLLECTION
:
2656 if (!strcmp(token
, "{"))
2658 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2659 /* Collection value */
2663 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
2671 fprintf(stderr
, "ippserver: Bad collection value on line %d of \"%s\".\n", *linenum
, filename
);
2675 case IPP_TAG_STRING
:
2676 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
2680 if (!strchr(token
, ','))
2681 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
2685 * Multiple string values...
2688 int num_values
; /* Number of values */
2689 char *values
[100], /* Values */
2690 *ptr
; /* Pointer to next value */
2696 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
2699 values
[num_values
] = ptr
;
2701 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
2705 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
2706 NULL
, (const char **)values
);
2716 * If we get here there was a parse error; free memory and return.
2728 * 'get_token()' - Get a token from a file.
2731 static char * /* O - Token from file or NULL on EOF */
2732 get_token(FILE *fp
, /* I - File to read from */
2733 char *buf
, /* I - Buffer to read into */
2734 int buflen
, /* I - Length of buffer */
2735 int *linenum
) /* IO - Current line number */
2737 int ch
, /* Character from file */
2738 quote
; /* Quoting character */
2739 char *bufptr
, /* Pointer into buffer */
2740 *bufend
; /* End of buffer */
2746 * Skip whitespace...
2749 while (isspace(ch
= getc(fp
)))
2761 else if (ch
== '\'' || ch
== '\"')
2764 * Quoted text or regular expression...
2769 bufend
= buf
+ buflen
- 1;
2771 while ((ch
= getc(fp
)) != EOF
)
2776 * Escape next character...
2779 if (bufptr
< bufend
)
2780 *bufptr
++ = (char)ch
;
2782 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
2783 *bufptr
++ = (char)ch
;
2785 else if (ch
== quote
)
2787 else if (bufptr
< bufend
)
2788 *bufptr
++ = (char)ch
;
2801 while ((ch
= getc(fp
)) != EOF
)
2807 else if (ch
== '{' || ch
== '}' || ch
== ',')
2817 * Whitespace delimited text...
2823 bufend
= buf
+ buflen
- 1;
2825 while ((ch
= getc(fp
)) != EOF
)
2826 if (isspace(ch
) || ch
== '#')
2828 else if (bufptr
< bufend
)
2829 *bufptr
++ = (char)ch
;
2833 else if (ch
== '\n')
2845 * 'html_escape()' - Write a HTML-safe string.
2849 html_escape(_ipp_client_t
*client
, /* I - Client */
2850 const char *s
, /* I - String to write */
2851 size_t slen
) /* I - Number of characters to write */
2853 const char *start
, /* Start of segment */
2854 *end
; /* End of string */
2858 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2860 while (*s
&& s
< end
)
2862 if (*s
== '&' || *s
== '<')
2865 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2868 httpWrite2(client
->http
, "&", 5);
2870 httpWrite2(client
->http
, "<", 4);
2879 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2884 * 'html_footer()' - Show the web interface footer.
2886 * This function also writes the trailing 0-length chunk.
2890 html_footer(_ipp_client_t
*client
) /* I - Client */
2896 httpWrite2(client
->http
, "", 0);
2901 * 'html_header()' - Show the web interface header and title.
2905 html_header(_ipp_client_t
*client
, /* I - Client */
2906 const char *title
) /* I - Title */
2912 "<title>%s</title>\n"
2913 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2914 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2915 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2916 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2918 "body { font-family: sans-serif; margin: 0; }\n"
2919 "div.body { padding: 0px 10px 10px; }\n"
2920 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2921 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2922 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2923 "table.form th { text-align: right; }\n"
2924 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2925 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2926 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2927 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2928 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2929 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2930 "table.nav td { margin: 0; text-align: center; }\n"
2931 "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"
2932 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2933 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2934 "td.nav:hover { background: #666; color: #fff; }\n"
2935 "td.nav:active { background: #000; color: #ff0; }\n"
2939 "<table class=\"nav\"><tr>"
2940 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2941 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2942 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2944 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2949 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2953 html_printf(_ipp_client_t
*client
, /* I - Client */
2954 const char *format
, /* I - Printf-style format string */
2955 ...) /* I - Additional arguments as needed */
2957 va_list ap
; /* Pointer to arguments */
2958 const char *start
; /* Start of string */
2959 char size
, /* Size character (h, l, L) */
2960 type
; /* Format type character */
2961 int width
, /* Width of field */
2962 prec
; /* Number of characters of precision */
2963 char tformat
[100], /* Temporary format string for sprintf() */
2964 *tptr
, /* Pointer into temporary format */
2965 temp
[1024]; /* Buffer for formatted numbers */
2966 char *s
; /* Pointer to string */
2970 * Loop through the format string, formatting as needed...
2973 va_start(ap
, format
);
2981 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2984 *tptr
++ = *format
++;
2988 httpWrite2(client
->http
, "%", 1);
2993 else if (strchr(" -+#\'", *format
))
2994 *tptr
++ = *format
++;
2999 * Get width from argument...
3003 width
= va_arg(ap
, int);
3005 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
3006 tptr
+= strlen(tptr
);
3012 while (isdigit(*format
& 255))
3014 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3017 width
= width
* 10 + *format
++ - '0';
3023 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3031 * Get precision from argument...
3035 prec
= va_arg(ap
, int);
3037 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
3038 tptr
+= strlen(tptr
);
3044 while (isdigit(*format
& 255))
3046 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3049 prec
= prec
* 10 + *format
++ - '0';
3054 if (*format
== 'l' && format
[1] == 'l')
3058 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
3066 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
3068 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3083 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3092 case 'E' : /* Floating point formats */
3097 if ((size_t)(width
+ 2) > sizeof(temp
))
3100 sprintf(temp
, tformat
, va_arg(ap
, double));
3102 httpWrite2(client
->http
, temp
, strlen(temp
));
3105 case 'B' : /* Integer formats */
3113 if ((size_t)(width
+ 2) > sizeof(temp
))
3116 # ifdef HAVE_LONG_LONG
3118 sprintf(temp
, tformat
, va_arg(ap
, long long));
3120 # endif /* HAVE_LONG_LONG */
3122 sprintf(temp
, tformat
, va_arg(ap
, long));
3124 sprintf(temp
, tformat
, va_arg(ap
, int));
3126 httpWrite2(client
->http
, temp
, strlen(temp
));
3129 case 'p' : /* Pointer value */
3130 if ((size_t)(width
+ 2) > sizeof(temp
))
3133 sprintf(temp
, tformat
, va_arg(ap
, void *));
3135 httpWrite2(client
->http
, temp
, strlen(temp
));
3138 case 'c' : /* Character or character array */
3141 temp
[0] = (char)va_arg(ap
, int);
3143 html_escape(client
, temp
, 1);
3146 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
3149 case 's' : /* String */
3150 if ((s
= va_arg(ap
, char *)) == NULL
)
3153 html_escape(client
, s
, strlen(s
));
3162 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
3169 * 'ipp_cancel_job()' - Cancel a job.
3173 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
3175 _ipp_job_t
*job
; /* Job information */
3182 if ((job
= find_job(client
)) == NULL
)
3184 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3189 * See if the job is already completed, canceled, or aborted; if so,
3190 * we can't cancel...
3195 case IPP_JSTATE_CANCELED
:
3196 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3197 "Job #%d is already canceled - can\'t cancel.", job
->id
);
3200 case IPP_JSTATE_ABORTED
:
3201 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3202 "Job #%d is already aborted - can\'t cancel.", job
->id
);
3205 case IPP_JSTATE_COMPLETED
:
3206 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3207 "Job #%d is already completed - can\'t cancel.", job
->id
);
3215 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3217 if (job
->state
== IPP_JSTATE_PROCESSING
||
3218 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
3222 job
->state
= IPP_JSTATE_CANCELED
;
3223 job
->completed
= time(NULL
);
3226 _cupsRWUnlock(&(client
->printer
->rwlock
));
3228 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3235 * 'ipp_close_job()' - Close an open job.
3239 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
3241 _ipp_job_t
*job
; /* Job information */
3248 if ((job
= find_job(client
)) == NULL
)
3250 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3255 * See if the job is already completed, canceled, or aborted; if so,
3256 * we can't cancel...
3261 case IPP_JSTATE_CANCELED
:
3262 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3263 "Job #%d is canceled - can\'t close.", job
->id
);
3266 case IPP_JSTATE_ABORTED
:
3267 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3268 "Job #%d is aborted - can\'t close.", job
->id
);
3271 case IPP_JSTATE_COMPLETED
:
3272 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3273 "Job #%d is completed - can\'t close.", job
->id
);
3276 case IPP_JSTATE_PROCESSING
:
3277 case IPP_JSTATE_STOPPED
:
3278 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3279 "Job #%d is already closed.", job
->id
);
3283 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3290 * 'ipp_create_job()' - Create a job object.
3294 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
3296 _ipp_job_t
*job
; /* New job */
3297 cups_array_t
*ra
; /* Attributes to send in response */
3301 * Validate print job attributes...
3304 if (!valid_job_attributes(client
))
3306 httpFlush(client
->http
);
3311 * Do we have a file to print?
3314 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3316 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3317 "Unexpected document data following request.");
3325 if ((job
= create_job(client
)) == NULL
)
3327 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3328 "Currently printing another job.");
3333 * Return the job info...
3336 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3338 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3339 cupsArrayAdd(ra
, "job-id");
3340 cupsArrayAdd(ra
, "job-state");
3341 cupsArrayAdd(ra
, "job-state-message");
3342 cupsArrayAdd(ra
, "job-state-reasons");
3343 cupsArrayAdd(ra
, "job-uri");
3345 copy_job_attributes(client
, job
, ra
);
3346 cupsArrayDelete(ra
);
3351 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3355 ipp_get_job_attributes(
3356 _ipp_client_t
*client
) /* I - Client */
3358 _ipp_job_t
*job
; /* Job */
3359 cups_array_t
*ra
; /* requested-attributes */
3362 if ((job
= find_job(client
)) == NULL
)
3364 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3368 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3370 ra
= ippCreateRequestedArray(client
->request
);
3371 copy_job_attributes(client
, job
, ra
);
3372 cupsArrayDelete(ra
);
3377 * 'ipp_get_jobs()' - Get a list of job objects.
3381 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
3383 ipp_attribute_t
*attr
; /* Current attribute */
3384 const char *which_jobs
= NULL
;
3385 /* which-jobs values */
3386 int job_comparison
; /* Job comparison */
3387 ipp_jstate_t job_state
; /* job-state value */
3388 int first_job_id
, /* First job ID */
3389 limit
, /* Maximum number of jobs to return */
3390 count
; /* Number of jobs that match */
3391 const char *username
; /* Username */
3392 _ipp_job_t
*job
; /* Current job pointer */
3393 cups_array_t
*ra
; /* Requested attributes array */
3397 * See if the "which-jobs" attribute have been specified...
3400 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3401 IPP_TAG_KEYWORD
)) != NULL
)
3403 which_jobs
= ippGetString(attr
, 0, NULL
);
3404 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3407 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3409 job_comparison
= -1;
3410 job_state
= IPP_JSTATE_STOPPED
;
3412 else if (!strcmp(which_jobs
, "completed"))
3415 job_state
= IPP_JSTATE_CANCELED
;
3417 else if (!strcmp(which_jobs
, "aborted"))
3420 job_state
= IPP_JSTATE_ABORTED
;
3422 else if (!strcmp(which_jobs
, "all"))
3425 job_state
= IPP_JSTATE_PENDING
;
3427 else if (!strcmp(which_jobs
, "canceled"))
3430 job_state
= IPP_JSTATE_CANCELED
;
3432 else if (!strcmp(which_jobs
, "pending"))
3435 job_state
= IPP_JSTATE_PENDING
;
3437 else if (!strcmp(which_jobs
, "pending-held"))
3440 job_state
= IPP_JSTATE_HELD
;
3442 else if (!strcmp(which_jobs
, "processing"))
3445 job_state
= IPP_JSTATE_PROCESSING
;
3447 else if (!strcmp(which_jobs
, "processing-stopped"))
3450 job_state
= IPP_JSTATE_STOPPED
;
3454 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3455 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3456 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3457 "which-jobs", NULL
, which_jobs
);
3462 * See if they want to limit the number of jobs reported...
3465 if ((attr
= ippFindAttribute(client
->request
, "limit",
3466 IPP_TAG_INTEGER
)) != NULL
)
3468 limit
= ippGetInteger(attr
, 0);
3470 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3475 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3476 IPP_TAG_INTEGER
)) != NULL
)
3478 first_job_id
= ippGetInteger(attr
, 0);
3480 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3487 * See if we only want to see jobs for a specific user...
3492 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3493 IPP_TAG_BOOLEAN
)) != NULL
)
3495 int my_jobs
= ippGetBoolean(attr
, 0);
3497 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3498 my_jobs
? "true" : "false");
3502 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3503 IPP_TAG_NAME
)) == NULL
)
3505 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3506 "Need requesting-user-name with my-jobs.");
3510 username
= ippGetString(attr
, 0, NULL
);
3512 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3513 client
->hostname
, username
);
3518 * OK, build a list of jobs for this printer...
3521 ra
= ippCreateRequestedArray(client
->request
);
3523 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3525 _cupsRWLockRead(&(client
->printer
->rwlock
));
3527 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3528 (limit
<= 0 || count
< limit
) && job
;
3529 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3532 * Filter out jobs that don't match...
3535 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3536 (job_comparison
== 0 && job
->state
!= job_state
) ||
3537 (job_comparison
> 0 && job
->state
< job_state
) ||
3538 job
->id
< first_job_id
||
3539 (username
&& job
->username
&&
3540 strcasecmp(username
, job
->username
)))
3544 ippAddSeparator(client
->response
);
3547 copy_job_attributes(client
, job
, ra
);
3550 cupsArrayDelete(ra
);
3552 _cupsRWUnlock(&(client
->printer
->rwlock
));
3557 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3561 ipp_get_printer_attributes(
3562 _ipp_client_t
*client
) /* I - Client */
3564 cups_array_t
*ra
; /* Requested attributes array */
3565 _ipp_printer_t
*printer
; /* Printer */
3569 * Send the attributes...
3572 ra
= ippCreateRequestedArray(client
->request
);
3573 printer
= client
->printer
;
3575 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3577 _cupsRWLockRead(&(printer
->rwlock
));
3579 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3580 IPP_TAG_CUPS_CONST
);
3582 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3584 int i
, /* Looping var */
3585 num_ready
= 0; /* Number of ready media */
3586 ipp_t
*ready
[3]; /* Ready media */
3588 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3590 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
3591 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);
3593 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);
3595 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3596 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);
3597 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3599 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
3600 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);
3602 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);
3607 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3608 for (i
= 0; i
< num_ready
; i
++)
3609 ippDelete(ready
[i
]);
3612 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3615 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3617 int num_ready
= 0; /* Number of ready media */
3618 const char *ready
[3]; /* Ready media */
3620 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3621 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3623 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3624 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3626 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3627 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3630 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3632 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3635 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3636 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3638 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3639 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3641 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3642 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3645 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3646 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3647 "printer-state", printer
->state
);
3649 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3650 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3652 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3653 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3655 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3657 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3659 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3662 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3664 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3665 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3666 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3667 "printer-state-reasons", NULL
, "none");
3670 ipp_attribute_t
*attr
= NULL
; /* printer-state-reasons */
3671 _ipp_preason_t bit
; /* Reason bit */
3672 int i
; /* Looping var */
3673 char reason
[32]; /* Reason string */
3675 for (i
= 0, bit
= 1; i
< (int)(sizeof(_ipp_preason_strings
) / sizeof(_ipp_preason_strings
[0])); i
++, bit
*= 2)
3677 if (printer
->state_reasons
& bit
)
3679 snprintf(reason
, sizeof(reason
), "%s-%s", _ipp_preason_strings
[i
], printer
->state
== IPP_PSTATE_IDLE
? "report" : printer
->state
== IPP_PSTATE_PROCESSING
? "warning" : "error");
3681 ippSetString(client
->response
, &attr
, ippGetCount(attr
), reason
);
3683 attr
= ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
, reason
);
3689 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3691 int i
; /* Looping var */
3692 char buffer
[256]; /* Supply value buffer */
3693 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3694 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3696 for (i
= 0; i
< 5; i
++)
3698 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
]);
3701 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3703 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3707 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3708 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3710 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3711 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3712 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3714 _cupsRWUnlock(&(printer
->rwlock
));
3716 cupsArrayDelete(ra
);
3721 * 'ipp_identify_printer()' - Beep or display a message.
3725 ipp_identify_printer(
3726 _ipp_client_t
*client
) /* I - Client */
3728 ipp_attribute_t
*actions
, /* identify-actions */
3729 *message
; /* message */
3732 actions
= ippFindAttribute(client
->request
, "identify-actions", IPP_TAG_KEYWORD
);
3733 message
= ippFindAttribute(client
->request
, "message", IPP_TAG_TEXT
);
3735 if (!actions
|| ippContainsString(actions
, "sound"))
3741 if (ippContainsString(actions
, "display"))
3742 printf("IDENTIFY from %s: %s\n", client
->hostname
, message
? ippGetString(message
, 0, NULL
) : "No message supplied");
3744 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3749 * 'ipp_print_job()' - Create a job object with an attached document.
3753 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3755 _ipp_job_t
*job
; /* New job */
3756 char filename
[1024], /* Filename buffer */
3757 buffer
[4096]; /* Copy buffer */
3758 ssize_t bytes
; /* Bytes read */
3759 cups_array_t
*ra
; /* Attributes to send in response */
3760 _cups_thread_t t
; /* Thread */
3764 * Validate print job attributes...
3767 if (!valid_job_attributes(client
))
3769 httpFlush(client
->http
);
3774 * Do we have a file to print?
3777 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3779 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3787 if ((job
= create_job(client
)) == NULL
)
3789 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3790 "Currently printing another job.");
3795 * Create a file for the request data...
3798 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3801 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3803 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3805 job
->state
= IPP_JSTATE_ABORTED
;
3807 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3808 "Unable to create print file: %s", strerror(errno
));
3812 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3814 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3816 int error
= errno
; /* Write error */
3818 job
->state
= IPP_JSTATE_ABORTED
;
3825 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3826 "Unable to write print file: %s", strerror(error
));
3834 * Got an error while reading the print data, so abort this job.
3837 job
->state
= IPP_JSTATE_ABORTED
;
3844 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3845 "Unable to read print file.");
3851 int error
= errno
; /* Write error */
3853 job
->state
= IPP_JSTATE_ABORTED
;
3858 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3859 "Unable to write print file: %s", strerror(error
));
3864 job
->filename
= strdup(filename
);
3865 job
->state
= IPP_JSTATE_PENDING
;
3868 * Process the job...
3871 t
= _cupsThreadCreate((_cups_thread_func_t
)process_job
, job
);
3875 _cupsThreadDetach(t
);
3879 job
->state
= IPP_JSTATE_ABORTED
;
3880 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3885 * Return the job info...
3888 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3890 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3891 cupsArrayAdd(ra
, "job-id");
3892 cupsArrayAdd(ra
, "job-state");
3893 cupsArrayAdd(ra
, "job-state-message");
3894 cupsArrayAdd(ra
, "job-state-reasons");
3895 cupsArrayAdd(ra
, "job-uri");
3897 copy_job_attributes(client
, job
, ra
);
3898 cupsArrayDelete(ra
);
3903 * 'ipp_print_uri()' - Create a job object with a referenced document.
3907 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3909 _ipp_job_t
*job
; /* New job */
3910 ipp_attribute_t
*uri
; /* document-uri */
3911 char scheme
[256], /* URI scheme */
3912 userpass
[256], /* Username and password info */
3913 hostname
[256], /* Hostname */
3914 resource
[1024]; /* Resource path */
3915 int port
; /* Port number */
3916 http_uri_status_t uri_status
; /* URI decode status */
3917 http_encryption_t encryption
; /* Encryption to use, if any */
3918 http_t
*http
; /* Connection for http/https URIs */
3919 http_status_t status
; /* Access status for http/https URIs */
3920 int infile
; /* Input file for local file URIs */
3921 char filename
[1024], /* Filename buffer */
3922 buffer
[4096]; /* Copy buffer */
3923 ssize_t bytes
; /* Bytes read */
3924 cups_array_t
*ra
; /* Attributes to send in response */
3925 static const char * const uri_status_strings
[] =
3926 { /* URI decode errors */
3928 "Bad arguments to function.",
3929 "Bad resource in URI.",
3930 "Bad port number in URI.",
3931 "Bad hostname in URI.",
3932 "Bad username in URI.",
3933 "Bad scheme in URI.",
3939 * Validate print job attributes...
3942 if (!valid_job_attributes(client
))
3944 httpFlush(client
->http
);
3949 * Do we have a file to print?
3952 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3954 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3955 "Unexpected document data following request.");
3960 * Do we have a document URI?
3963 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3964 IPP_TAG_URI
)) == NULL
)
3966 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3970 if (ippGetCount(uri
) != 1)
3972 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3973 "Too many document-uri values.");
3977 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3978 scheme
, sizeof(scheme
), userpass
,
3979 sizeof(userpass
), hostname
, sizeof(hostname
),
3980 &port
, resource
, sizeof(resource
));
3981 if (uri_status
< HTTP_URI_STATUS_OK
)
3983 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3984 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3988 if (strcmp(scheme
, "file") &&
3990 strcmp(scheme
, "https") &&
3991 #endif /* HAVE_SSL */
3992 strcmp(scheme
, "http"))
3994 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3995 "URI scheme \"%s\" not supported.", scheme
);
3999 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4001 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4002 "Unable to access URI: %s", strerror(errno
));
4010 if ((job
= create_job(client
)) == NULL
)
4012 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
4013 "Currently printing another job.");
4018 * Create a file for the request data...
4021 if (!strcasecmp(job
->format
, "image/jpeg"))
4022 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4023 client
->printer
->directory
, job
->id
);
4024 else if (!strcasecmp(job
->format
, "image/png"))
4025 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4026 client
->printer
->directory
, job
->id
);
4027 else if (!strcasecmp(job
->format
, "application/pdf"))
4028 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4029 client
->printer
->directory
, job
->id
);
4030 else if (!strcasecmp(job
->format
, "application/postscript"))
4031 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4032 client
->printer
->directory
, job
->id
);
4034 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4035 client
->printer
->directory
, job
->id
);
4037 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
4039 job
->state
= IPP_JSTATE_ABORTED
;
4041 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4042 "Unable to create print file: %s", strerror(errno
));
4046 if (!strcmp(scheme
, "file"))
4048 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4050 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4051 "Unable to access URI: %s", strerror(errno
));
4057 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4058 (errno
== EAGAIN
|| errno
== EINTR
))
4060 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4062 int error
= errno
; /* Write error */
4064 job
->state
= IPP_JSTATE_ABORTED
;
4072 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4073 "Unable to write print file: %s", strerror(error
));
4084 if (port
== 443 || !strcmp(scheme
, "https"))
4085 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4087 #endif /* HAVE_SSL */
4088 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4090 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4091 1, 30000, NULL
)) == NULL
)
4093 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4094 "Unable to connect to %s: %s", hostname
,
4095 cupsLastErrorString());
4096 job
->state
= IPP_JSTATE_ABORTED
;
4105 httpClearFields(http
);
4106 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4107 if (httpGet(http
, resource
))
4109 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4110 "Unable to GET URI: %s", strerror(errno
));
4112 job
->state
= IPP_JSTATE_ABORTED
;
4122 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4124 if (status
!= HTTP_STATUS_OK
)
4126 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4127 "Unable to GET URI: %s", httpStatus(status
));
4129 job
->state
= IPP_JSTATE_ABORTED
;
4139 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4141 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4143 int error
= errno
; /* Write error */
4145 job
->state
= IPP_JSTATE_ABORTED
;
4153 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4154 "Unable to write print file: %s", strerror(error
));
4164 int error
= errno
; /* Write error */
4166 job
->state
= IPP_JSTATE_ABORTED
;
4171 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4172 "Unable to write print file: %s", strerror(error
));
4177 job
->filename
= strdup(filename
);
4178 job
->state
= IPP_JSTATE_PENDING
;
4181 * Process the job...
4187 * Return the job info...
4190 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4192 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4193 cupsArrayAdd(ra
, "job-id");
4194 cupsArrayAdd(ra
, "job-state");
4195 cupsArrayAdd(ra
, "job-state-reasons");
4196 cupsArrayAdd(ra
, "job-uri");
4198 copy_job_attributes(client
, job
, ra
);
4199 cupsArrayDelete(ra
);
4204 * 'ipp_send_document()' - Add an attached document to a job object created with
4209 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
4211 _ipp_job_t
*job
; /* Job information */
4212 char filename
[1024], /* Filename buffer */
4213 buffer
[4096]; /* Copy buffer */
4214 ssize_t bytes
; /* Bytes read */
4215 ipp_attribute_t
*attr
; /* Current attribute */
4216 cups_array_t
*ra
; /* Attributes to send in response */
4223 if ((job
= find_job(client
)) == NULL
)
4225 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4226 httpFlush(client
->http
);
4231 * See if we already have a document for this job or the job has already
4232 * in a non-pending state...
4235 if (job
->state
> IPP_JSTATE_HELD
)
4237 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4238 "Job is not in a pending state.");
4239 httpFlush(client
->http
);
4242 else if (job
->filename
|| job
->fd
>= 0)
4244 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4245 "Multiple document jobs are not supported.");
4246 httpFlush(client
->http
);
4250 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4251 IPP_TAG_ZERO
)) == NULL
)
4253 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4254 "Missing required last-document attribute.");
4255 httpFlush(client
->http
);
4258 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4259 !ippGetBoolean(attr
, 0))
4261 respond_unsupported(client
, attr
);
4262 httpFlush(client
->http
);
4267 * Validate document attributes...
4270 if (!valid_doc_attributes(client
))
4272 httpFlush(client
->http
);
4276 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
4279 * Get the document format for the job...
4282 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4284 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
4285 job
->format
= ippGetString(attr
, 0, NULL
);
4286 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
4287 job
->format
= ippGetString(attr
, 0, NULL
);
4289 job
->format
= "application/octet-stream";
4292 * Create a file for the request data...
4295 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
4298 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
4300 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4302 _cupsRWUnlock(&(client
->printer
->rwlock
));
4306 job
->state
= IPP_JSTATE_ABORTED
;
4308 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4309 "Unable to create print file: %s", strerror(errno
));
4313 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
4315 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4317 int error
= errno
; /* Write error */
4319 job
->state
= IPP_JSTATE_ABORTED
;
4326 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4327 "Unable to write print file: %s", strerror(error
));
4335 * Got an error while reading the print data, so abort this job.
4338 job
->state
= IPP_JSTATE_ABORTED
;
4345 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4346 "Unable to read print file.");
4352 int error
= errno
; /* Write error */
4354 job
->state
= IPP_JSTATE_ABORTED
;
4359 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4360 "Unable to write print file: %s", strerror(error
));
4364 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4367 job
->filename
= strdup(filename
);
4368 job
->state
= IPP_JSTATE_PENDING
;
4370 _cupsRWUnlock(&(client
->printer
->rwlock
));
4373 * Process the job...
4379 * Return the job info...
4382 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4384 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4385 cupsArrayAdd(ra
, "job-id");
4386 cupsArrayAdd(ra
, "job-state");
4387 cupsArrayAdd(ra
, "job-state-reasons");
4388 cupsArrayAdd(ra
, "job-uri");
4390 copy_job_attributes(client
, job
, ra
);
4391 cupsArrayDelete(ra
);
4396 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4401 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
4403 _ipp_job_t
*job
; /* Job information */
4404 ipp_attribute_t
*uri
; /* document-uri */
4405 char scheme
[256], /* URI scheme */
4406 userpass
[256], /* Username and password info */
4407 hostname
[256], /* Hostname */
4408 resource
[1024]; /* Resource path */
4409 int port
; /* Port number */
4410 http_uri_status_t uri_status
; /* URI decode status */
4411 http_encryption_t encryption
; /* Encryption to use, if any */
4412 http_t
*http
; /* Connection for http/https URIs */
4413 http_status_t status
; /* Access status for http/https URIs */
4414 int infile
; /* Input file for local file URIs */
4415 char filename
[1024], /* Filename buffer */
4416 buffer
[4096]; /* Copy buffer */
4417 ssize_t bytes
; /* Bytes read */
4418 ipp_attribute_t
*attr
; /* Current attribute */
4419 cups_array_t
*ra
; /* Attributes to send in response */
4420 static const char * const uri_status_strings
[] =
4421 { /* URI decode errors */
4423 "Bad arguments to function.",
4424 "Bad resource in URI.",
4425 "Bad port number in URI.",
4426 "Bad hostname in URI.",
4427 "Bad username in URI.",
4428 "Bad scheme in URI.",
4437 if ((job
= find_job(client
)) == NULL
)
4439 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4440 httpFlush(client
->http
);
4445 * See if we already have a document for this job or the job has already
4446 * in a non-pending state...
4449 if (job
->state
> IPP_JSTATE_HELD
)
4451 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4452 "Job is not in a pending state.");
4453 httpFlush(client
->http
);
4456 else if (job
->filename
|| job
->fd
>= 0)
4458 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4459 "Multiple document jobs are not supported.");
4460 httpFlush(client
->http
);
4464 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4465 IPP_TAG_ZERO
)) == NULL
)
4467 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4468 "Missing required last-document attribute.");
4469 httpFlush(client
->http
);
4472 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4473 !ippGetBoolean(attr
, 0))
4475 respond_unsupported(client
, attr
);
4476 httpFlush(client
->http
);
4481 * Validate document attributes...
4484 if (!valid_doc_attributes(client
))
4486 httpFlush(client
->http
);
4491 * Do we have a file to print?
4494 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4496 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4497 "Unexpected document data following request.");
4502 * Do we have a document URI?
4505 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4506 IPP_TAG_URI
)) == NULL
)
4508 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4512 if (ippGetCount(uri
) != 1)
4514 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4515 "Too many document-uri values.");
4519 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4520 scheme
, sizeof(scheme
), userpass
,
4521 sizeof(userpass
), hostname
, sizeof(hostname
),
4522 &port
, resource
, sizeof(resource
));
4523 if (uri_status
< HTTP_URI_STATUS_OK
)
4525 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4526 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4530 if (strcmp(scheme
, "file") &&
4532 strcmp(scheme
, "https") &&
4533 #endif /* HAVE_SSL */
4534 strcmp(scheme
, "http"))
4536 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4537 "URI scheme \"%s\" not supported.", scheme
);
4541 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4543 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4544 "Unable to access URI: %s", strerror(errno
));
4549 * Get the document format for the job...
4552 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4554 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4555 IPP_TAG_MIMETYPE
)) != NULL
)
4556 job
->format
= ippGetString(attr
, 0, NULL
);
4558 job
->format
= "application/octet-stream";
4561 * Create a file for the request data...
4564 if (!strcasecmp(job
->format
, "image/jpeg"))
4565 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4566 client
->printer
->directory
, job
->id
);
4567 else if (!strcasecmp(job
->format
, "image/png"))
4568 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4569 client
->printer
->directory
, job
->id
);
4570 else if (!strcasecmp(job
->format
, "application/pdf"))
4571 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4572 client
->printer
->directory
, job
->id
);
4573 else if (!strcasecmp(job
->format
, "application/postscript"))
4574 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4575 client
->printer
->directory
, job
->id
);
4577 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4578 client
->printer
->directory
, job
->id
);
4580 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4582 _cupsRWUnlock(&(client
->printer
->rwlock
));
4586 job
->state
= IPP_JSTATE_ABORTED
;
4588 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4589 "Unable to create print file: %s", strerror(errno
));
4593 if (!strcmp(scheme
, "file"))
4595 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4597 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4598 "Unable to access URI: %s", strerror(errno
));
4604 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4605 (errno
== EAGAIN
|| errno
== EINTR
))
4607 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4609 int error
= errno
; /* Write error */
4611 job
->state
= IPP_JSTATE_ABORTED
;
4619 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4620 "Unable to write print file: %s", strerror(error
));
4631 if (port
== 443 || !strcmp(scheme
, "https"))
4632 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4634 #endif /* HAVE_SSL */
4635 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4637 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4638 1, 30000, NULL
)) == NULL
)
4640 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4641 "Unable to connect to %s: %s", hostname
,
4642 cupsLastErrorString());
4643 job
->state
= IPP_JSTATE_ABORTED
;
4652 httpClearFields(http
);
4653 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4654 if (httpGet(http
, resource
))
4656 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4657 "Unable to GET URI: %s", strerror(errno
));
4659 job
->state
= IPP_JSTATE_ABORTED
;
4669 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4671 if (status
!= HTTP_STATUS_OK
)
4673 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4674 "Unable to GET URI: %s", httpStatus(status
));
4676 job
->state
= IPP_JSTATE_ABORTED
;
4686 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4688 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4690 int error
= errno
; /* Write error */
4692 job
->state
= IPP_JSTATE_ABORTED
;
4700 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4701 "Unable to write print file: %s", strerror(error
));
4711 int error
= errno
; /* Write error */
4713 job
->state
= IPP_JSTATE_ABORTED
;
4718 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4719 "Unable to write print file: %s", strerror(error
));
4723 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4726 job
->filename
= strdup(filename
);
4727 job
->state
= IPP_JSTATE_PENDING
;
4729 _cupsRWUnlock(&(client
->printer
->rwlock
));
4732 * Process the job...
4738 * Return the job info...
4741 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4743 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4744 cupsArrayAdd(ra
, "job-id");
4745 cupsArrayAdd(ra
, "job-state");
4746 cupsArrayAdd(ra
, "job-state-reasons");
4747 cupsArrayAdd(ra
, "job-uri");
4749 copy_job_attributes(client
, job
, ra
);
4750 cupsArrayDelete(ra
);
4755 * 'ipp_validate_job()' - Validate job creation attributes.
4759 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4761 if (valid_job_attributes(client
))
4762 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4767 * 'load_attributes()' - Load printer attributes from a file.
4769 * Syntax is based on ipptool format:
4771 * ATTR value-tag name value
4775 load_attributes(const char *filename
, /* I - File to load */
4776 ipp_t
*attrs
) /* I - Printer attributes */
4778 int linenum
= 0; /* Current line number */
4779 FILE *fp
= NULL
; /* Test file */
4780 char attr
[128], /* Attribute name */
4781 token
[1024], /* Token from file */
4782 *tokenptr
; /* Pointer into token */
4783 ipp_tag_t value
; /* Current value type */
4784 ipp_attribute_t
*attrptr
, /* Attribute pointer */
4785 *lastcol
= NULL
; /* Last collection attribute */
4788 if ((fp
= fopen(filename
, "r")) == NULL
)
4790 fprintf(stderr
, "ippserver: Unable to open \"%s\": %s\n", filename
, strerror(errno
));
4794 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
4796 if (!_cups_strcasecmp(token
, "ATTR"))
4802 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4804 fprintf(stderr
, "ippserver: Missing ATTR value tag on line %d of \"%s\".\n", linenum
, filename
);
4808 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
4810 fprintf(stderr
, "ippserver: Bad ATTR value tag \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4814 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
4816 fprintf(stderr
, "ippserver: Missing ATTR name on line %d of \"%s\".\n", linenum
, filename
);
4820 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4822 fprintf(stderr
, "ippserver: Missing ATTR value on line %d of \"%s\".\n", linenum
, filename
);
4830 case IPP_TAG_BOOLEAN
:
4831 if (!_cups_strcasecmp(token
, "true"))
4832 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, 1);
4834 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, (char)atoi(token
));
4837 case IPP_TAG_INTEGER
:
4839 if (!strchr(token
, ','))
4840 attrptr
= ippAddInteger(attrs
, IPP_TAG_PRINTER
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
4843 int values
[100], /* Values */
4844 num_values
= 1; /* Number of values */
4846 values
[0] = (int)strtol(token
, &tokenptr
, 10);
4847 while (tokenptr
&& *tokenptr
&&
4848 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
4850 if (*tokenptr
== ',')
4852 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
4855 values
[num_values
] = (int)strtol(tokenptr
, &tokenptr
, 0);
4859 attrptr
= ippAddIntegers(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, values
);
4862 if (!tokenptr
|| *tokenptr
)
4864 fprintf(stderr
, "ippserver: Bad %s value \"%s\" on line %d of \"%s\".\n", ippTagString(value
), token
, linenum
, filename
);
4869 case IPP_TAG_RESOLUTION
:
4871 int xres
, /* X resolution */
4872 yres
; /* Y resolution */
4873 ipp_res_t units
; /* Units */
4874 char *start
, /* Start of value */
4875 *ptr
, /* Pointer into value */
4876 *next
= NULL
; /* Next value */
4878 for (start
= token
; start
; start
= next
)
4880 xres
= yres
= (int)strtol(start
, (char **)&ptr
, 10);
4881 if (ptr
> start
&& xres
> 0)
4884 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
4887 if (ptr
&& (next
= strchr(ptr
, ',')) != NULL
)
4890 if (ptr
<= start
|| xres
<= 0 || yres
<= 0 || !ptr
||
4891 (_cups_strcasecmp(ptr
, "dpi") &&
4892 _cups_strcasecmp(ptr
, "dpc") &&
4893 _cups_strcasecmp(ptr
, "dpcm") &&
4894 _cups_strcasecmp(ptr
, "other")))
4896 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4900 if (!_cups_strcasecmp(ptr
, "dpc") || !_cups_strcasecmp(ptr
, "dpcm"))
4901 units
= IPP_RES_PER_CM
;
4903 units
= IPP_RES_PER_INCH
;
4906 ippSetResolution(attrs
, &attrptr
, ippGetCount(attrptr
), units
, xres
, yres
);
4908 attrptr
= ippAddResolution(attrs
, IPP_TAG_PRINTER
, attr
, units
, xres
, yres
);
4913 case IPP_TAG_RANGE
:
4915 int lowers
[4], /* Lower value */
4916 uppers
[4], /* Upper values */
4917 num_vals
; /* Number of values */
4920 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
4921 lowers
+ 0, uppers
+ 0,
4922 lowers
+ 1, uppers
+ 1,
4923 lowers
+ 2, uppers
+ 2,
4924 lowers
+ 3, uppers
+ 3);
4926 if ((num_vals
& 1) || num_vals
== 0)
4928 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", token
, linenum
, filename
);
4932 attrptr
= ippAddRanges(attrs
, IPP_TAG_PRINTER
, attr
, num_vals
/ 2, lowers
,
4937 case IPP_TAG_BEGIN_COLLECTION
:
4938 if (!strcmp(token
, "{"))
4940 ipp_t
*col
= get_collection(fp
, filename
, &linenum
);
4941 /* Collection value */
4945 attrptr
= lastcol
= ippAddCollection(attrs
, IPP_TAG_PRINTER
, attr
, col
);
4953 fprintf(stderr
, "ippserver: Bad ATTR collection value on line %d of \"%s\".\n", linenum
, filename
);
4959 ipp_t
*col
; /* Collection value */
4960 long pos
= ftell(fp
); /* Save position of file */
4962 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4965 if (strcmp(token
, ","))
4967 fseek(fp
, pos
, SEEK_SET
);
4971 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
4973 fprintf(stderr
, "ippserver: Unexpected \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4977 if ((col
= get_collection(fp
, filename
, &linenum
)) == NULL
)
4980 ippSetCollection(attrs
, &attrptr
, ippGetCount(attrptr
), col
);
4982 while (!strcmp(token
, "{"));
4985 case IPP_TAG_STRING
:
4986 attrptr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, attr
, token
, (int)strlen(token
));
4990 fprintf(stderr
, "ippserver: Unsupported ATTR value tag %s on line %d of \"%s\".\n", ippTagString(value
), linenum
, filename
);
4993 case IPP_TAG_TEXTLANG
:
4994 case IPP_TAG_NAMELANG
:
4997 case IPP_TAG_KEYWORD
:
4999 case IPP_TAG_URISCHEME
:
5000 case IPP_TAG_CHARSET
:
5001 case IPP_TAG_LANGUAGE
:
5002 case IPP_TAG_MIMETYPE
:
5003 if (!strchr(token
, ','))
5004 attrptr
= ippAddString(attrs
, IPP_TAG_PRINTER
, value
, attr
, NULL
, token
);
5008 * Multiple string values...
5011 int num_values
; /* Number of values */
5012 char *values
[100], /* Values */
5013 *ptr
; /* Pointer to next value */
5019 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
5021 if (ptr
> token
&& ptr
[-1] == '\\')
5022 _cups_strcpy(ptr
- 1, ptr
);
5026 values
[num_values
] = ptr
;
5028 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
5033 attrptr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, NULL
, (const char **)values
);
5040 fprintf(stderr
, "ippserver: Unable to add attribute on line %d of \"%s\": %s\n", linenum
, filename
, cupsLastErrorString());
5046 fprintf(stderr
, "ippserver: Unknown directive \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
5056 * 'parse_options()' - Parse URL options into CUPS options.
5058 * The client->options string is destroyed by this function.
5061 static int /* O - Number of options */
5062 parse_options(_ipp_client_t
*client
, /* I - Client */
5063 cups_option_t
**options
) /* O - Options */
5065 char *name
, /* Name */
5067 *next
; /* Next name=value pair */
5068 int num_options
= 0; /* Number of options */
5073 for (name
= client
->options
; name
&& *name
; name
= next
)
5075 if ((value
= strchr(name
, '=')) == NULL
)
5079 if ((next
= strchr(value
, '&')) != NULL
)
5082 num_options
= cupsAddOption(name
, value
, num_options
, options
);
5085 return (num_options
);
5090 * 'process_attr_message()' - Process an ATTR: message from a command.
5094 process_attr_message(
5095 _ipp_job_t
*job
, /* I - Job */
5096 char *message
) /* I - Message */
5104 * 'process_client()' - Process client requests on a thread.
5107 static void * /* O - Exit status */
5108 process_client(_ipp_client_t
*client
) /* I - Client */
5111 * Loop until we are out of requests or timeout (30 seconds)...
5115 int first_time
= 1; /* First time request? */
5116 #endif /* HAVE_SSL */
5118 while (httpWait(client
->http
, 30000))
5124 * See if we need to negotiate a TLS connection...
5127 char buf
[1]; /* First byte from client */
5129 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
5131 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
5133 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
5135 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5139 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5144 #endif /* HAVE_SSL */
5146 if (!process_http(client
))
5151 * Close the conection to the client and return...
5154 delete_client(client
);
5161 * 'process_http()' - Process a HTTP request.
5164 int /* O - 1 on success, 0 on failure */
5165 process_http(_ipp_client_t
*client
) /* I - Client connection */
5167 char uri
[1024]; /* URI */
5168 http_state_t http_state
; /* HTTP state */
5169 http_status_t http_status
; /* HTTP status */
5170 ipp_state_t ipp_state
; /* State of IPP transfer */
5171 char scheme
[32], /* Method/scheme */
5172 userpass
[128], /* Username:password */
5173 hostname
[HTTP_MAX_HOST
];
5175 int port
; /* Port number */
5176 const char *encoding
; /* Content-Encoding value */
5177 static const char * const http_states
[] =
5178 { /* Strings for logging HTTP method */
5199 * Clear state variables...
5202 ippDelete(client
->request
);
5203 ippDelete(client
->response
);
5205 client
->request
= NULL
;
5206 client
->response
= NULL
;
5207 client
->operation
= HTTP_STATE_WAITING
;
5210 * Read a request from the connection...
5213 while ((http_state
= httpReadRequest(client
->http
, uri
,
5214 sizeof(uri
))) == HTTP_STATE_WAITING
)
5218 * Parse the request line...
5221 if (http_state
== HTTP_STATE_ERROR
)
5223 if (httpError(client
->http
) == EPIPE
)
5224 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
5226 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
5227 strerror(httpError(client
->http
)));
5231 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
5233 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
5234 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5237 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
5239 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
5240 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5244 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
5248 * Separate the URI into its components...
5251 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
5252 userpass
, sizeof(userpass
),
5253 hostname
, sizeof(hostname
), &port
,
5254 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
5255 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
5257 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
5258 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5262 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
5263 *(client
->options
)++ = '\0';
5266 * Process the request...
5269 client
->start
= time(NULL
);
5270 client
->operation
= httpGetState(client
->http
);
5273 * Parse incoming parameters until the status changes...
5276 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
5278 if (http_status
!= HTTP_STATUS_OK
)
5280 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5284 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
5285 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
5288 * HTTP/1.1 and higher require the "Host:" field...
5291 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5296 * Handle HTTP Upgrade...
5299 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
5303 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
5305 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
5308 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
5310 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
5312 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5316 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5319 #endif /* HAVE_SSL */
5321 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
5326 * Handle HTTP Expect...
5329 if (httpGetExpect(client
->http
) &&
5330 (client
->operation
== HTTP_STATE_POST
||
5331 client
->operation
== HTTP_STATE_PUT
))
5333 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
5336 * Send 100-continue header...
5339 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
5345 * Send 417-expectation-failed header...
5348 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
5354 * Handle new transfers...
5357 encoding
= httpGetContentEncoding(client
->http
);
5359 switch (client
->operation
)
5361 case HTTP_STATE_OPTIONS
:
5363 * Do OPTIONS command...
5366 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
5368 case HTTP_STATE_HEAD
:
5369 if (!strcmp(client
->uri
, "/icon.png"))
5370 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
5371 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
5372 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
5374 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5376 case HTTP_STATE_GET
:
5377 if (!strcmp(client
->uri
, "/icon.png"))
5380 * Send PNG icon file.
5383 int fd
; /* Icon file */
5384 struct stat fileinfo
; /* Icon file information */
5385 char buffer
[4096]; /* Copy buffer */
5386 ssize_t bytes
; /* Bytes */
5388 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5390 if (!stat(client
->printer
->icon
, &fileinfo
) &&
5391 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5393 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
5394 (size_t)fileinfo
.st_size
))
5400 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5401 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5403 httpFlushWrite(client
->http
);
5408 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5410 else if (!strcmp(client
->uri
, "/"))
5413 * Show web status page...
5416 _ipp_job_t
*job
; /* Current job */
5417 int i
; /* Looping var */
5418 _ipp_preason_t reason
; /* Current reason */
5419 static const char * const reasons
[] =
5420 { /* Reason strings */
5423 "Input Tray Missing",
5424 "Marker Supply Empty",
5425 "Marker Supply Low",
5426 "Marker Waste Almost Full",
5427 "Marker Waste Full",
5439 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5442 html_header(client
, client
->printer
->name
);
5444 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
5445 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
5446 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
5447 if (client
->printer
->state_reasons
& reason
)
5448 html_printf(client
, "\n<br> %s", reasons
[i
]);
5449 html_printf(client
, "</p>\n");
5451 if (cupsArrayCount(client
->printer
->jobs
) > 0)
5453 _cupsRWLockRead(&(client
->printer
->rwlock
));
5455 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");
5456 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
5458 char when
[256], /* When job queued/started/finished */
5459 hhmmss
[64]; /* Time HH:MM:SS */
5463 case IPP_JSTATE_PENDING
:
5464 case IPP_JSTATE_HELD
:
5465 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
5467 case IPP_JSTATE_PROCESSING
:
5468 case IPP_JSTATE_STOPPED
:
5469 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
5471 case IPP_JSTATE_ABORTED
:
5472 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5474 case IPP_JSTATE_CANCELED
:
5475 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5477 case IPP_JSTATE_COMPLETED
:
5478 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5482 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
);
5484 html_printf(client
, "</tbody></table>\n");
5486 _cupsRWUnlock(&(client
->printer
->rwlock
));
5488 html_footer(client
);
5492 else if (!strcmp(client
->uri
, "/media"))
5495 * Show web media page...
5498 int i
, /* Looping var */
5499 num_options
; /* Number of form options */
5500 cups_option_t
*options
; /* Form options */
5501 static const char * const sizes
[] =
5502 { /* Size strings */
5515 static const char * const types
[] =
5532 static const int sheets
[] = /* Number of sheets */
5541 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5544 html_header(client
, client
->printer
->name
);
5546 if ((num_options
= parse_options(client
, &options
)) > 0)
5549 * WARNING: A real printer/server implementation MUST NOT implement
5550 * media updates via a GET request - GET requests are supposed to be
5551 * idempotent (without side-effects) and we obviously are not
5552 * authenticating access here. This form is provided solely to
5553 * enable testing and development!
5556 const char *val
; /* Form value */
5558 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
5559 client
->printer
->main_size
= atoi(val
);
5560 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
5561 client
->printer
->main_type
= atoi(val
);
5562 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
5563 client
->printer
->main_level
= atoi(val
);
5565 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
5566 client
->printer
->envelope_size
= atoi(val
);
5567 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
5568 client
->printer
->envelope_level
= atoi(val
);
5570 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
5571 client
->printer
->photo_size
= atoi(val
);
5572 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
5573 client
->printer
->photo_type
= atoi(val
);
5574 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
5575 client
->printer
->photo_level
= atoi(val
);
5577 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))
5578 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
5580 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
5582 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
))
5584 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
5585 if (client
->printer
->active_job
)
5586 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5589 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
5591 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
5594 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
5596 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
5597 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
5598 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5599 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
5600 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
5601 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
5602 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5603 if (!strstr(types
[i
], "Photo"))
5604 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
5605 html_printf(client
, "</select> <select name=\"main_level\">");
5606 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5607 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
5608 html_printf(client
, "</select></td></tr>\n");
5611 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
5612 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5613 if (strstr(sizes
[i
], "Envelope"))
5614 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
5615 html_printf(client
, "</select> <select name=\"envelope_level\">");
5616 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5617 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
5618 html_printf(client
, "</select></td></tr>\n");
5621 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
5622 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5623 if (strstr(sizes
[i
], "Photo"))
5624 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
5625 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
5626 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5627 if (strstr(types
[i
], "Photo"))
5628 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
5629 html_printf(client
, "</select> <select name=\"photo_level\">");
5630 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5631 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
5632 html_printf(client
, "</select></td></tr>\n");
5634 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5635 html_footer(client
);
5639 else if (!strcmp(client
->uri
, "/supplies"))
5642 * Show web supplies page...
5645 int i
, j
, /* Looping vars */
5646 num_options
; /* Number of form options */
5647 cups_option_t
*options
; /* Form options */
5648 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5650 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5653 html_header(client
, client
->printer
->name
);
5655 if ((num_options
= parse_options(client
, &options
)) > 0)
5658 * WARNING: A real printer/server implementation MUST NOT implement
5659 * supply updates via a GET request - GET requests are supposed to be
5660 * idempotent (without side-effects) and we obviously are not
5661 * authenticating access here. This form is provided solely to
5662 * enable testing and development!
5665 char name
[64]; /* Form field */
5666 const char *val
; /* Form value */
5668 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
);
5670 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5672 snprintf(name
, sizeof(name
), "supply_%d", i
);
5673 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5675 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5681 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
5682 else if (level
< 10)
5683 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
5688 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
5689 else if (level
> 90)
5690 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
5695 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5698 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5700 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5701 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5703 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5704 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5705 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5706 html_printf(client
, "</select></td></tr>\n");
5708 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5709 html_footer(client
);
5714 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5717 case HTTP_STATE_POST
:
5718 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5722 * Not an IPP request...
5725 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5729 * Read the IPP request...
5732 client
->request
= ippNew();
5734 while ((ipp_state
= ippRead(client
->http
,
5735 client
->request
)) != IPP_STATE_DATA
)
5737 if (ipp_state
== IPP_STATE_ERROR
)
5739 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5740 cupsLastErrorString());
5741 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5747 * Now that we have the IPP request, process the request...
5750 return (process_ipp(client
));
5753 break; /* Anti-compiler-warning-code */
5761 * 'process_ipp()' - Process an IPP request.
5764 static int /* O - 1 on success, 0 on error */
5765 process_ipp(_ipp_client_t
*client
) /* I - Client */
5767 ipp_tag_t group
; /* Current group tag */
5768 ipp_attribute_t
*attr
; /* Current attribute */
5769 ipp_attribute_t
*charset
; /* Character set attribute */
5770 ipp_attribute_t
*language
; /* Language attribute */
5771 ipp_attribute_t
*uri
; /* Printer URI attribute */
5772 int major
, minor
; /* Version number */
5773 const char *name
; /* Name of attribute */
5776 debug_attributes("Request", client
->request
, 1);
5779 * First build an empty response message for this request...
5782 client
->operation_id
= ippGetOperation(client
->request
);
5783 client
->response
= ippNewResponse(client
->request
);
5786 * Then validate the request header and required attributes...
5789 major
= ippGetVersion(client
->request
, &minor
);
5791 if (major
< 1 || major
> 2)
5794 * Return an error, since we only support IPP 1.x and 2.x.
5797 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
5798 "Bad request version number %d.%d.", major
, minor
);
5800 else if (ippGetRequestId(client
->request
) <= 0)
5801 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
5802 ippGetRequestId(client
->request
));
5803 else if (!ippFirstAttribute(client
->request
))
5804 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5805 "No attributes in request.");
5809 * Make sure that the attributes are provided in the correct order and
5810 * don't repeat groups...
5813 for (attr
= ippFirstAttribute(client
->request
),
5814 group
= ippGetGroupTag(attr
);
5816 attr
= ippNextAttribute(client
->request
))
5818 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5821 * Out of order; return an error...
5824 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5825 "Attribute groups are out of order (%x < %x).",
5826 ippGetGroupTag(attr
), group
);
5830 group
= ippGetGroupTag(attr
);
5836 * Then make sure that the first three attributes are:
5838 * attributes-charset
5839 * attributes-natural-language
5840 * printer-uri/job-uri
5843 attr
= ippFirstAttribute(client
->request
);
5844 name
= ippGetName(attr
);
5845 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5846 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5851 attr
= ippNextAttribute(client
->request
);
5852 name
= ippGetName(attr
);
5854 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5855 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5860 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5861 IPP_TAG_URI
)) != NULL
)
5863 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5864 IPP_TAG_URI
)) != NULL
)
5870 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5871 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5874 * Bad character set...
5877 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5878 "Unsupported character set \"%s\".",
5879 ippGetString(charset
, 0, NULL
));
5881 else if (!charset
|| !language
|| !uri
)
5884 * Return an error, since attributes-charset,
5885 * attributes-natural-language, and printer-uri/job-uri are required
5886 * for all operations.
5889 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5890 "Missing required attributes.");
5894 char scheme
[32], /* URI scheme */
5895 userpass
[32], /* Username/password in URI */
5896 host
[256], /* Host name in URI */
5897 resource
[256]; /* Resource path in URI */
5898 int port
; /* Port number in URI */
5900 name
= ippGetName(uri
);
5902 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5903 scheme
, sizeof(scheme
),
5904 userpass
, sizeof(userpass
),
5905 host
, sizeof(host
), &port
,
5906 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5907 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5908 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5909 else if ((!strcmp(name
, "job-uri") &&
5910 strncmp(resource
, "/ipp/print/", 11)) ||
5911 (!strcmp(name
, "printer-uri") &&
5912 strcmp(resource
, "/ipp/print")))
5913 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5914 name
, ippGetString(uri
, 0, NULL
));
5918 * Try processing the operation...
5921 switch (ippGetOperation(client
->request
))
5923 case IPP_OP_PRINT_JOB
:
5924 ipp_print_job(client
);
5927 case IPP_OP_PRINT_URI
:
5928 ipp_print_uri(client
);
5931 case IPP_OP_VALIDATE_JOB
:
5932 ipp_validate_job(client
);
5935 case IPP_OP_CREATE_JOB
:
5936 ipp_create_job(client
);
5939 case IPP_OP_SEND_DOCUMENT
:
5940 ipp_send_document(client
);
5943 case IPP_OP_SEND_URI
:
5944 ipp_send_uri(client
);
5947 case IPP_OP_CANCEL_JOB
:
5948 ipp_cancel_job(client
);
5951 case IPP_OP_GET_JOB_ATTRIBUTES
:
5952 ipp_get_job_attributes(client
);
5955 case IPP_OP_GET_JOBS
:
5956 ipp_get_jobs(client
);
5959 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5960 ipp_get_printer_attributes(client
);
5963 case IPP_OP_CLOSE_JOB
:
5964 ipp_close_job(client
);
5967 case IPP_OP_IDENTIFY_PRINTER
:
5968 ipp_identify_printer(client
);
5972 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5973 "Operation not supported.");
5982 * Send the HTTP header and return...
5985 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5986 httpFlush(client
->http
); /* Flush trailing (junk) data */
5988 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5989 ippLength(client
->response
)));
5994 * 'process_job()' - Process a print job.
5997 static void * /* O - Thread exit status */
5998 process_job(_ipp_job_t
*job
) /* I - Job */
6000 job
->state
= IPP_JSTATE_PROCESSING
;
6001 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
6002 job
->processing
= time(NULL
);
6004 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
6006 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
6011 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
6013 if (job
->printer
->command
)
6016 * Execute a command with the job spool file and wait for it to complete...
6019 int pid
, /* Process ID */
6020 status
; /* Exit status */
6021 time_t start
, /* Start time */
6023 char *myargv
[3], /* Command-line arguments */
6024 *myenvp
[200]; /* Environment variables */
6025 int myenvc
; /* Number of environment variables */
6026 ipp_attribute_t
*attr
; /* Job attribute */
6027 char val
[1280], /* IPP_NAME=value */
6028 *valptr
; /* Pointer into string */
6030 int mypipe
[2]; /* Pipe for stderr */
6031 char line
[2048], /* Line from stderr */
6032 *ptr
, /* Pointer into line */
6033 *endptr
; /* End of line */
6034 ssize_t bytes
; /* Bytes read */
6037 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
6042 * Setup the command-line arguments...
6045 myargv
[0] = job
->printer
->command
;
6046 myargv
[1] = job
->filename
;
6050 * Copy the current environment, then add ENV variables for every Job
6054 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
6055 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
6057 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
6060 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6061 * value(s) from the attribute.
6064 const char *name
= ippGetName(attr
);
6073 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6078 *valptr
++ = (char)toupper(*name
& 255);
6083 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6085 myenvp
[myenvc
++] = strdup(val
);
6087 myenvp
[myenvc
] = NULL
;
6090 * Now run the program...
6094 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
6099 perror("Unable to create pipe for stderr");
6100 mypipe
[0] = mypipe
[1] = -1;
6103 if ((pid
= fork()) == 0)
6106 * Child comes here...
6114 execve(job
->printer
->command
, myargv
, myenvp
);
6120 * Unable to fork process...
6123 perror("Unable to start job processing command");
6130 * Free memory used for environment...
6134 free(myenvp
[-- myenvc
]);
6139 * Free memory used for environment...
6143 free(myenvp
[-- myenvc
]);
6146 * If the pipe exists, read from it until EOF...
6154 while ((bytes
= read(mypipe
[0], endptr
, sizeof(line
) - (size_t)(endptr
- line
) - 1)) > 0)
6159 while ((ptr
= strchr(line
, '\n')) != NULL
)
6163 if (!strncmp(line
, "STATE:", 6))
6166 * Process printer-state-reasons keywords.
6169 process_state_message(job
, line
);
6171 else if (!strncmp(line
, "ATTR:", 5))
6174 * Process printer attribute update.
6177 process_attr_message(job
, line
);
6179 else if (Verbosity
> 1)
6180 fprintf(stderr
, "%s: %s\n", job
->printer
->command
, line
);
6184 memmove(line
, ptr
, (size_t)(endptr
- ptr
));
6194 * Wait for child to complete...
6197 # ifdef HAVE_WAITPID
6198 while (waitpid(pid
, &status
, 0) < 0);
6200 while (wait(&status
) < 0);
6201 # endif /* HAVE_WAITPID */
6208 if (WIFEXITED(status
))
6210 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
6211 job
->printer
->command
, WEXITSTATUS(status
));
6214 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
6215 job
->printer
->command
, WTERMSIG(status
));
6217 job
->state
= IPP_JSTATE_ABORTED
;
6219 else if (status
< 0)
6220 job
->state
= IPP_JSTATE_ABORTED
;
6222 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
6223 job
->printer
->command
);
6226 * Make sure processing takes at least 5 seconds...
6230 if ((end
- start
) < 5)
6236 * Sleep for a random amount of time to simulate job processing.
6239 sleep((unsigned)(5 + (rand() % 11)));
6243 job
->state
= IPP_JSTATE_CANCELED
;
6244 else if (job
->state
== IPP_JSTATE_PROCESSING
)
6245 job
->state
= IPP_JSTATE_COMPLETED
;
6247 job
->completed
= time(NULL
);
6248 job
->printer
->state
= IPP_PSTATE_IDLE
;
6249 job
->printer
->active_job
= NULL
;
6256 * 'process_state_message()' - Process a STATE: message from a command.
6260 process_state_message(
6261 _ipp_job_t
*job
, /* I - Job */
6262 char *message
) /* I - Message */
6264 int i
; /* Looping var */
6265 _ipp_preason_t state_reasons
, /* printer-state-reasons values */
6266 bit
; /* Current reason bit */
6267 char *ptr
, /* Pointer into message */
6268 *next
; /* Next keyword in message */
6269 int remove
; /* Non-zero if we are removing keywords */
6273 * Skip leading "STATE:" and any whitespace...
6276 for (message
+= 6; *message
; message
++)
6277 if (*message
!= ' ' && *message
!= '\t')
6281 * Support the following forms of message:
6283 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6285 * "-keyword[,keyword,...]" to remove keywords.
6287 * "+keyword[,keyword,...]" to add keywords.
6289 * Keywords may or may not have a suffix (-report, -warning, -error) per
6293 if (*message
== '-')
6296 state_reasons
= job
->printer
->state_reasons
;
6299 else if (*message
== '+')
6302 state_reasons
= job
->printer
->state_reasons
;
6308 state_reasons
= _IPP_PREASON_NONE
;
6313 if ((next
= strchr(message
, ',')) != NULL
)
6316 if ((ptr
= strstr(message
, "-error")) != NULL
)
6318 else if ((ptr
= strstr(message
, "-report")) != NULL
)
6320 else if ((ptr
= strstr(message
, "-warning")) != NULL
)
6323 for (i
= 0, bit
= 1; i
< (int)(sizeof(_ipp_preason_strings
) / sizeof(_ipp_preason_strings
[0])); i
++, bit
*= 2)
6325 if (!strcmp(message
, _ipp_preason_strings
[i
]))
6328 state_reasons
&= ~bit
;
6330 state_reasons
|= bit
;
6340 job
->printer
->state_reasons
= state_reasons
;
6345 * 'register_printer()' - Register a printer object via Bonjour.
6348 static int /* O - 1 on success, 0 on error */
6350 _ipp_printer_t
*printer
, /* I - Printer */
6351 const char *location
, /* I - Location */
6352 const char *make
, /* I - Manufacturer */
6353 const char *model
, /* I - Model name */
6354 const char *formats
, /* I - Supported formats */
6355 const char *adminurl
, /* I - Web interface URL */
6356 const char *uuid
, /* I - Printer UUID */
6357 int color
, /* I - 1 = color, 0 = monochrome */
6358 int duplex
, /* I - 1 = duplex, 0 = simplex */
6359 const char *subtype
) /* I - Service subtype */
6361 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6362 _ipp_txt_t ipp_txt
; /* Bonjour IPP TXT record */
6363 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6365 DNSServiceErrorType error
; /* Error from Bonjour */
6366 char make_model
[256],/* Make and model together */
6367 product
[256], /* Product string */
6368 regtype
[256]; /* Bonjour service type */
6372 * Build the TXT record for IPP...
6375 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
6376 snprintf(product
, sizeof(product
), "(%s)", model
);
6378 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
6379 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
6380 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(make_model
),
6382 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(adminurl
),
6385 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(location
),
6387 TXTRecordSetValue(&ipp_txt
, "product", (uint8_t)strlen(product
),
6389 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
),
6391 TXTRecordSetValue(&ipp_txt
, "Color", 1, color
? "T" : "F");
6392 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, duplex
? "T" : "F");
6393 TXTRecordSetValue(&ipp_txt
, "usb_MFG", (uint8_t)strlen(make
),
6395 TXTRecordSetValue(&ipp_txt
, "usb_MDL", (uint8_t)strlen(model
),
6397 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(uuid
), uuid
);
6399 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
6400 # endif /* HAVE_SSL */
6401 if (strstr(formats
, "image/urf"))
6402 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");
6404 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
6405 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
6408 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6409 * defend our service name but not actually support LPD...
6412 printer
->printer_ref
= DNSSDMaster
;
6414 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
6415 kDNSServiceFlagsShareConnection
,
6416 0 /* interfaceIndex */, printer
->dnssd_name
,
6417 "_printer._tcp", NULL
/* domain */,
6418 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
6419 NULL
/* txtRecord */,
6420 (DNSServiceRegisterReply
)dnssd_callback
,
6421 printer
)) != kDNSServiceErr_NoError
)
6423 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
6424 printer
->dnssd_name
, error
);
6429 * Then register the _ipp._tcp (IPP) service type with the real port number to
6430 * advertise our IPP printer...
6433 printer
->ipp_ref
= DNSSDMaster
;
6435 if (subtype
&& *subtype
)
6436 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
6438 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
6440 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
6441 kDNSServiceFlagsShareConnection
,
6442 0 /* interfaceIndex */, printer
->dnssd_name
,
6443 regtype
, NULL
/* domain */,
6444 NULL
/* host */, htons(printer
->port
),
6445 TXTRecordGetLength(&ipp_txt
),
6446 TXTRecordGetBytesPtr(&ipp_txt
),
6447 (DNSServiceRegisterReply
)dnssd_callback
,
6448 printer
)) != kDNSServiceErr_NoError
)
6450 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6451 printer
->dnssd_name
, regtype
, error
);
6457 * Then register the _ipps._tcp (IPP) service type with the real port number to
6458 * advertise our IPPS printer...
6461 printer
->ipps_ref
= DNSSDMaster
;
6463 if (subtype
&& *subtype
)
6464 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
6466 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
6468 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
6469 kDNSServiceFlagsShareConnection
,
6470 0 /* interfaceIndex */, printer
->dnssd_name
,
6471 regtype
, NULL
/* domain */,
6472 NULL
/* host */, htons(printer
->port
),
6473 TXTRecordGetLength(&ipp_txt
),
6474 TXTRecordGetBytesPtr(&ipp_txt
),
6475 (DNSServiceRegisterReply
)dnssd_callback
,
6476 printer
)) != kDNSServiceErr_NoError
)
6478 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6479 printer
->dnssd_name
, regtype
, error
);
6482 # endif /* HAVE_SSL */
6485 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6486 * real port number to advertise our IPP printer...
6489 printer
->http_ref
= DNSSDMaster
;
6491 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
6492 kDNSServiceFlagsShareConnection
,
6493 0 /* interfaceIndex */, printer
->dnssd_name
,
6494 "_http._tcp,_printer", NULL
/* domain */,
6495 NULL
/* host */, htons(printer
->port
),
6496 0 /* txtLen */, NULL
, /* txtRecord */
6497 (DNSServiceRegisterReply
)dnssd_callback
,
6498 printer
)) != kDNSServiceErr_NoError
)
6500 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6501 printer
->dnssd_name
, regtype
, error
);
6505 TXTRecordDeallocate(&ipp_txt
);
6507 #elif defined(HAVE_AVAHI)
6508 char temp
[256]; /* Subtype service string */
6511 * Create the TXT record...
6515 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
6516 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s %s", make
, model
);
6517 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", adminurl
);
6519 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", location
);
6520 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "product=(%s)", model
);
6521 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
6522 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", color
? "T" : "F");
6523 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", duplex
? "T" : "F");
6524 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MFG=%s", make
);
6525 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MDL=%s", model
);
6526 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", uuid
);
6528 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
6529 # endif /* HAVE_SSL */
6532 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6535 avahi_threaded_poll_lock(DNSSDMaster
);
6537 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
6539 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
);
6542 * Then register the _ipp._tcp (IPP)...
6545 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
);
6546 if (subtype
&& *subtype
)
6548 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
6549 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
6554 * _ipps._tcp (IPPS) for secure printing...
6557 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
);
6558 if (subtype
&& *subtype
)
6560 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
6561 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
6563 #endif /* HAVE_SSL */
6566 * Finally _http.tcp (HTTP) for the web interface...
6569 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
);
6570 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");
6576 avahi_entry_group_commit(printer
->ipp_ref
);
6577 avahi_threaded_poll_unlock(DNSSDMaster
);
6579 avahi_string_list_free(ipp_txt
);
6580 #endif /* HAVE_DNSSD */
6587 * 'respond_http()' - Send a HTTP response.
6590 int /* O - 1 on success, 0 on failure */
6592 _ipp_client_t
*client
, /* I - Client */
6593 http_status_t code
, /* I - HTTP status of response */
6594 const char *content_encoding
, /* I - Content-Encoding of response */
6595 const char *type
, /* I - MIME media type of response */
6596 size_t length
) /* I - Length of response */
6598 char message
[1024]; /* Text message */
6601 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
6603 if (code
== HTTP_STATUS_CONTINUE
)
6606 * 100-continue doesn't send any headers...
6609 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
6613 * Format an error message...
6616 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
6618 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
6620 type
= "text/plain";
6621 length
= strlen(message
);
6627 * Send the HTTP response header...
6630 httpClearFields(client
->http
);
6632 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
6633 client
->operation
== HTTP_STATE_OPTIONS
)
6634 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
6638 if (!strcmp(type
, "text/html"))
6639 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
6640 "text/html; charset=utf-8");
6642 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
6644 if (content_encoding
)
6645 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
6648 httpSetLength(client
->http
, length
);
6650 if (httpWriteResponse(client
->http
, code
) < 0)
6654 * Send the response data...
6660 * Send a plain text message.
6663 if (httpPrintf(client
->http
, "%s", message
) < 0)
6666 if (httpWrite2(client
->http
, "", 0) < 0)
6669 else if (client
->response
)
6672 * Send an IPP response...
6675 debug_attributes("Response", client
->response
, 2);
6677 ippSetState(client
->response
, IPP_STATE_IDLE
);
6679 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6688 * 'respond_ipp()' - Send an IPP response.
6692 respond_ipp(_ipp_client_t
*client
, /* I - Client */
6693 ipp_status_t status
, /* I - status-code */
6694 const char *message
, /* I - printf-style status-message */
6695 ...) /* I - Additional args as needed */
6697 const char *formatted
= NULL
; /* Formatted message */
6700 ippSetStatusCode(client
->response
, status
);
6704 va_list ap
; /* Pointer to additional args */
6705 ipp_attribute_t
*attr
; /* New status-message attribute */
6707 va_start(ap
, message
);
6708 if ((attr
= ippFindAttribute(client
->response
, "status-message",
6709 IPP_TAG_TEXT
)) != NULL
)
6710 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6712 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
6713 "status-message", NULL
, message
, ap
);
6716 formatted
= ippGetString(attr
, 0, NULL
);
6720 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
6721 ippOpString(client
->operation_id
), ippErrorString(status
),
6724 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
6725 ippOpString(client
->operation_id
), ippErrorString(status
));
6730 * 'respond_unsupported()' - Respond with an unsupported attribute.
6734 respond_unsupported(
6735 _ipp_client_t
*client
, /* I - Client */
6736 ipp_attribute_t
*attr
) /* I - Atribute */
6738 ipp_attribute_t
*temp
; /* Copy of attribute */
6741 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6742 "Unsupported %s %s%s value.", ippGetName(attr
),
6743 ippGetCount(attr
) > 1 ? "1setOf " : "",
6744 ippTagString(ippGetValueTag(attr
)));
6746 temp
= ippCopyAttribute(client
->response
, attr
, 0);
6747 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
6752 * 'run_printer()' - Run the printer service.
6756 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
6758 int num_fds
; /* Number of file descriptors */
6759 struct pollfd polldata
[3]; /* poll() data */
6760 int timeout
; /* Timeout for poll() */
6761 _ipp_client_t
*client
; /* New client */
6765 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6768 polldata
[0].fd
= printer
->ipv4
;
6769 polldata
[0].events
= POLLIN
;
6771 polldata
[1].fd
= printer
->ipv6
;
6772 polldata
[1].events
= POLLIN
;
6777 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
6778 polldata
[num_fds
++].events
= POLLIN
;
6779 #endif /* HAVE_DNSSD */
6782 * Loop until we are killed or have a hard error...
6787 if (cupsArrayCount(printer
->jobs
))
6792 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
6794 perror("poll() failed");
6798 if (polldata
[0].revents
& POLLIN
)
6800 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6802 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6806 _cupsThreadDetach(t
);
6810 perror("Unable to create client thread");
6811 delete_client(client
);
6816 if (polldata
[1].revents
& POLLIN
)
6818 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6820 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6824 _cupsThreadDetach(t
);
6828 perror("Unable to create client thread");
6829 delete_client(client
);
6835 if (polldata
[2].revents
& POLLIN
)
6836 DNSServiceProcessResult(DNSSDMaster
);
6837 #endif /* HAVE_DNSSD */
6840 * Clean out old jobs...
6843 clean_jobs(printer
);
6849 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6853 time_string(time_t tv
, /* I - Time value */
6854 char *buffer
, /* I - Buffer */
6855 size_t bufsize
) /* I - Size of buffer */
6857 struct tm
*curtime
= localtime(&tv
);
6860 strftime(buffer
, bufsize
, "%X", curtime
);
6866 * 'usage()' - Show program usage.
6870 usage(int status
) /* O - Exit status */
6874 puts(CUPS_SVERSION
" - Copyright 2010-2015 by Apple Inc. All rights "
6879 puts("Usage: ippserver [options] \"name\"");
6882 puts("-2 Supports 2-sided printing (default=1-sided)");
6883 puts("-M manufacturer Manufacturer name (default=Test)");
6884 puts("-P PIN printing mode");
6885 puts("-a attributes-file Load printer attributes from file");
6886 puts("-c command Run command for every print job");
6887 printf("-d spool-directory Spool directory "
6888 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6889 puts("-f type/subtype[,...] List of supported types "
6890 "(default=application/pdf,image/jpeg)");
6891 puts("-h Show program help");
6892 puts("-i iconfile.png PNG icon file (default=printer.png)");
6893 puts("-k Keep job spool files");
6894 puts("-l location Location of printer (default=empty string)");
6895 puts("-m model Model name (default=Printer)");
6896 puts("-n hostname Hostname for printer");
6897 puts("-p port Port number (default=auto)");
6898 puts("-r subtype Bonjour service subtype (default=_print)");
6899 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6900 puts("-v[vvv] Be (very) verbose");
6907 * 'valid_doc_attributes()' - Determine whether the document attributes are
6910 * When one or more document attributes are invalid, this function adds a
6911 * suitable response and attributes to the unsupported group.
6914 static int /* O - 1 if valid, 0 if not */
6915 valid_doc_attributes(
6916 _ipp_client_t
*client
) /* I - Client */
6918 int valid
= 1; /* Valid attributes? */
6919 ipp_op_t op
= ippGetOperation(client
->request
);
6921 const char *op_name
= ippOpString(op
);
6922 /* IPP operation name */
6923 ipp_attribute_t
*attr
, /* Current attribute */
6924 *supported
; /* xxx-supported attribute */
6925 const char *compression
= NULL
,
6926 /* compression value */
6927 *format
= NULL
; /* document-format value */
6931 * Check operation attributes...
6934 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6937 * If compression is specified, only accept a supported value in a Print-Job
6938 * or Send-Document request...
6941 compression
= ippGetString(attr
, 0, NULL
);
6942 supported
= ippFindAttribute(client
->printer
->attrs
,
6943 "compression-supported", IPP_TAG_KEYWORD
);
6945 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6946 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6947 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6948 op
!= IPP_OP_VALIDATE_JOB
) ||
6949 !ippContainsString(supported
, compression
))
6951 respond_unsupported(client
, attr
);
6956 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6958 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6960 if (strcmp(compression
, "none"))
6963 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6964 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
6970 * Is it a format we support?
6973 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
6975 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
6976 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
6978 respond_unsupported(client
, attr
);
6983 format
= ippGetString(attr
, 0, NULL
);
6985 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
6986 client
->hostname
, op_name
, format
);
6988 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
6993 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
6995 format
= "application/octet-stream"; /* Should never happen */
6997 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
7000 if (format
&& !strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
7003 * Auto-type the file using the first 8 bytes of the file...
7006 unsigned char header
[8]; /* First 8 bytes of file */
7008 memset(header
, 0, sizeof(header
));
7009 httpPeek(client
->http
, (char *)header
, sizeof(header
));
7011 if (!memcmp(header
, "%PDF", 4))
7012 format
= "application/pdf";
7013 else if (!memcmp(header
, "%!", 2))
7014 format
= "application/postscript";
7015 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
7016 format
= "image/jpeg";
7017 else if (!memcmp(header
, "\211PNG", 4))
7018 format
= "image/png";
7019 else if (!memcmp(header
, "RAS2", 4))
7020 format
= "image/pwg-raster";
7021 else if (!memcmp(header
, "UNIRAST", 8))
7022 format
= "image/urf";
7028 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
7029 client
->hostname
, op_name
, format
);
7031 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
7035 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
7037 respond_unsupported(client
, attr
);
7045 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
7046 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
7053 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
7055 * When one or more job attributes are invalid, this function adds a suitable
7056 * response and attributes to the unsupported group.
7059 static int /* O - 1 if valid, 0 if not */
7060 valid_job_attributes(
7061 _ipp_client_t
*client
) /* I - Client */
7063 int i
, /* Looping var */
7064 count
, /* Number of values */
7065 valid
= 1; /* Valid attributes? */
7066 ipp_attribute_t
*attr
, /* Current attribute */
7067 *supported
; /* xxx-supported attribute */
7071 * Check operation attributes...
7074 valid
= valid_doc_attributes(client
);
7077 * Check the various job template attributes...
7080 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
7082 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7083 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
7085 respond_unsupported(client
, attr
);
7090 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
7092 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
7094 respond_unsupported(client
, attr
);
7099 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
7101 if (ippGetCount(attr
) != 1 ||
7102 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7103 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7104 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7105 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
7107 respond_unsupported(client
, attr
);
7112 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
7114 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
7116 respond_unsupported(client
, attr
);
7121 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
7123 if (ippGetCount(attr
) != 1 ||
7124 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7125 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
7127 respond_unsupported(client
, attr
);
7131 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
7134 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
7136 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
7138 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7139 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
7141 respond_unsupported(client
, attr
);
7146 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
7148 if (ippGetCount(attr
) != 1 ||
7149 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7150 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7151 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7152 strcmp(ippGetString(attr
, 0, NULL
), "none"))
7154 respond_unsupported(client
, attr
);
7159 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
7161 if (ippGetCount(attr
) != 1 ||
7162 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7163 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7164 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
7166 respond_unsupported(client
, attr
);
7171 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7173 if (!ippContainsString(supported
, ippGetString(attr
, 0, NULL
)))
7175 respond_unsupported(client
, attr
);
7181 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
7183 ipp_t
*col
, /* media-col collection */
7184 *size
; /* media-size collection */
7185 ipp_attribute_t
*member
, /* Member attribute */
7186 *x_dim
, /* x-dimension */
7187 *y_dim
; /* y-dimension */
7188 int x_value
, /* y-dimension value */
7189 y_value
; /* x-dimension value */
7191 if (ippGetCount(attr
) != 1 ||
7192 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
7194 respond_unsupported(client
, attr
);
7198 col
= ippGetCollection(attr
, 0);
7200 if ((member
= ippFindAttribute(col
, "media-size-name", IPP_TAG_ZERO
)) != NULL
)
7202 if (ippGetCount(member
) != 1 ||
7203 (ippGetValueTag(member
) != IPP_TAG_NAME
&&
7204 ippGetValueTag(member
) != IPP_TAG_NAMELANG
&&
7205 ippGetValueTag(member
) != IPP_TAG_KEYWORD
))
7207 respond_unsupported(client
, attr
);
7212 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7214 if (!ippContainsString(supported
, ippGetString(member
, 0, NULL
)))
7216 respond_unsupported(client
, attr
);
7221 else if ((member
= ippFindAttribute(col
, "media-size", IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
7223 if (ippGetCount(member
) != 1)
7225 respond_unsupported(client
, attr
);
7230 size
= ippGetCollection(member
, 0);
7232 if ((x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(x_dim
) != 1 ||
7233 (y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(y_dim
) != 1)
7235 respond_unsupported(client
, attr
);
7240 x_value
= ippGetInteger(x_dim
, 0);
7241 y_value
= ippGetInteger(y_dim
, 0);
7242 supported
= ippFindAttribute(client
->printer
->attrs
, "media-size-supported", IPP_TAG_BEGIN_COLLECTION
);
7243 count
= ippGetCount(supported
);
7245 for (i
= 0; i
< count
; i
++)
7247 size
= ippGetCollection(supported
, i
);
7248 x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_ZERO
);
7249 y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_ZERO
);
7251 if (ippContainsInteger(x_dim
, x_value
) && ippContainsInteger(y_dim
, y_value
))
7257 respond_unsupported(client
, attr
);
7265 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
7267 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7268 (strcmp(ippGetString(attr
, 0, NULL
),
7269 "separate-documents-uncollated-copies") &&
7270 strcmp(ippGetString(attr
, 0, NULL
),
7271 "separate-documents-collated-copies")))
7273 respond_unsupported(client
, attr
);
7278 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
7280 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7281 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
7282 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
7284 respond_unsupported(client
, attr
);
7289 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
7291 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
7293 respond_unsupported(client
, attr
);
7298 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
7300 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7301 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
7302 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
7304 respond_unsupported(client
, attr
);
7309 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
7311 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
7313 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
7316 respond_unsupported(client
, attr
);
7321 int xdpi
, /* Horizontal resolution for job template attribute */
7322 ydpi
, /* Vertical resolution for job template attribute */
7323 sydpi
; /* Vertical resolution for supported value */
7324 ipp_res_t units
, /* Units for job template attribute */
7325 sunits
; /* Units for supported value */
7327 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
7328 count
= ippGetCount(supported
);
7330 for (i
= 0; i
< count
; i
++)
7332 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
7338 respond_unsupported(client
, attr
);
7344 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
7346 const char *sides
= ippGetString(attr
, 0, NULL
);
7347 /* "sides" value... */
7349 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
7351 respond_unsupported(client
, attr
);
7354 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
7356 if (!ippContainsString(supported
, sides
))
7358 respond_unsupported(client
, attr
);
7362 else if (strcmp(sides
, "one-sided"))
7364 respond_unsupported(client
, attr
);