4 * Sample IPP Everywhere server for CUPS.
6 * Copyright 2010-2015 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * Disable private and deprecated stuff so we can verify that the public API
19 * is sufficient to implement a server.
22 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
23 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
27 * Include necessary headers...
30 #include <config.h> /* CUPS configuration header */
31 #include <cups/cups.h> /* Public API */
32 #include <cups/string-private.h> /* CUPS string functions */
33 #include <cups/thread-private.h> /* For multithreading functions */
46 # define WEXITSTATUS(s) (s)
47 # include <winsock2.h>
51 extern char **environ
;
53 # include <sys/fcntl.h>
54 # include <sys/wait.h>
60 #elif defined(HAVE_AVAHI)
61 # include <avahi-client/client.h>
62 # include <avahi-client/publish.h>
63 # include <avahi-common/error.h>
64 # include <avahi-common/thread-watch.h>
65 #endif /* HAVE_DNSSD */
66 #ifdef HAVE_SYS_MOUNT_H
67 # include <sys/mount.h>
68 #endif /* HAVE_SYS_MOUNT_H */
69 #ifdef HAVE_SYS_STATFS_H
70 # include <sys/statfs.h>
71 #endif /* HAVE_SYS_STATFS_H */
72 #ifdef HAVE_SYS_STATVFS_H
73 # include <sys/statvfs.h>
74 #endif /* HAVE_SYS_STATVFS_H */
77 #endif /* HAVE_SYS_VFS_H */
84 enum _ipp_preason_e
/* printer-state-reasons bit values */
86 _IPP_PREASON_NONE
= 0x0000, /* none */
87 _IPP_PREASON_OTHER
= 0x0001, /* other */
88 _IPP_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
89 _IPP_PREASON_INPUT_TRAY_MISSING
= 0x0004,
90 /* input-tray-missing */
91 _IPP_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
92 /* marker-supply-empty */
93 _IPP_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
94 /* marker-supply-low */
95 _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
96 /* marker-waste-almost-full */
97 _IPP_PREASON_MARKER_WASTE_FULL
= 0x0040,
98 /* marker-waste-full */
99 _IPP_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
100 _IPP_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
101 _IPP_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
102 _IPP_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
103 _IPP_PREASON_MOVING_TO_PAUSED
= 0x0800,
104 /* moving-to-paused */
105 _IPP_PREASON_PAUSED
= 0x1000, /* paused */
106 _IPP_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
107 _IPP_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
108 _IPP_PREASON_TONER_LOW
= 0x8000 /* toner-low */
110 typedef unsigned int _ipp_preason_t
; /* Bitfield for printer-state-reasons */
111 static const char * const _ipp_preason_strings
[] =
112 { /* Strings for each bit */
113 /* "none" is implied for no bits set */
116 "input-tray-missing",
117 "marker-supply-empty",
119 "marker-waste-almost-full",
132 typedef enum _ipp_media_class_e
134 _IPP_GENERAL
, /* General-purpose size */
135 _IPP_PHOTO_ONLY
, /* Photo-only size */
136 _IPP_ENV_ONLY
/* Envelope-only size */
137 } _ipp_media_class_t
;
139 typedef enum _ipp_media_size_e
141 _IPP_MEDIA_SIZE_NONE
= -1,
146 _IPP_MEDIA_SIZE_LEGAL
,
147 _IPP_MEDIA_SIZE_LETTER
,
148 _IPP_MEDIA_SIZE_COM10
,
154 static const char * const media_supported
[] =
155 { /* media-supported values */
156 "iso_a4_210x297mm", /* A4 */
157 "iso_a5_148x210mm", /* A5 */
158 "iso_a6_105x148mm", /* A6 */
159 "iso_dl_110x220mm", /* DL */
160 "na_legal_8.5x14in", /* Legal */
161 "na_letter_8.5x11in", /* Letter */
162 "na_number-10_4.125x9.5in", /* #10 */
163 "na_index-3x5_3x5in", /* 3x5 */
164 "oe_photo-l_3.5x5in", /* L */
165 "na_index-4x6_4x6in", /* 4x6 */
166 "na_5x7_5x7in" /* 5x7 aka 2L */
168 static const int media_col_sizes
[][3] =
169 { /* media-col-database sizes */
170 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
171 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
172 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
173 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
174 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
175 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
176 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
177 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
178 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
179 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
180 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
183 typedef enum _ipp_media_source_e
185 _IPP_MEDIA_SOURCE_NONE
= -1,
186 _IPP_MEDIA_SOURCE_AUTO
,
187 _IPP_MEDIA_SOURCE_MAIN
,
188 _IPP_MEDIA_SOURCE_MANUAL
,
189 _IPP_MEDIA_SOURCE_ENVELOPE
,
190 _IPP_MEDIA_SOURCE_PHOTO
191 } _ipp_media_source_t
;
192 static const char * const media_source_supported
[] =
193 /* media-source-supported values */
202 typedef enum _ipp_media_type_e
204 _IPP_MEDIA_TYPE_NONE
= -1,
205 _IPP_MEDIA_TYPE_AUTO
,
206 _IPP_MEDIA_TYPE_CARDSTOCK
,
207 _IPP_MEDIA_TYPE_ENVELOPE
,
208 _IPP_MEDIA_TYPE_LABELS
,
209 _IPP_MEDIA_TYPE_OTHER
,
210 _IPP_MEDIA_TYPE_GLOSSY
,
211 _IPP_MEDIA_TYPE_HIGH_GLOSS
,
212 _IPP_MEDIA_TYPE_MATTE
,
213 _IPP_MEDIA_TYPE_SATIN
,
214 _IPP_MEDIA_TYPE_SEMI_GLOSS
,
215 _IPP_MEDIA_TYPE_STATIONERY
,
216 _IPP_MEDIA_TYPE_LETTERHEAD
,
217 _IPP_MEDIA_TYPE_TRANSPARENCY
219 static const char * const media_type_supported
[] =
220 /* media-type-supported values */
227 "photographic-glossy",
228 "photographic-high-gloss",
229 "photographic-matte",
230 "photographic-satin",
231 "photographic-semi-gloss",
233 "stationery-letterhead",
237 typedef enum _ipp_supply_e
239 _IPP_SUPPLY_CYAN
, /* Cyan Toner */
240 _IPP_SUPPLY_MAGENTA
, /* Magenta Toner */
241 _IPP_SUPPLY_YELLOW
, /* Yellow Toner */
242 _IPP_SUPPLY_BLACK
, /* Black Toner */
243 _IPP_SUPPLY_WASTE
/* Waste Toner */
245 static const char * const printer_supplies
[] =
246 { /* printer-supply-description values */
260 typedef DNSServiceRef _ipp_srv_t
; /* Service reference */
261 typedef TXTRecordRef _ipp_txt_t
; /* TXT record */
263 #elif defined(HAVE_AVAHI)
264 typedef AvahiEntryGroup
*_ipp_srv_t
; /* Service reference */
265 typedef AvahiStringList
*_ipp_txt_t
; /* TXT record */
268 typedef void *_ipp_srv_t
; /* Service reference */
269 typedef void *_ipp_txt_t
; /* TXT record */
270 #endif /* HAVE_DNSSD */
272 typedef struct _ipp_filter_s
/**** Attribute filter ****/
274 cups_array_t
*ra
; /* Requested attributes */
275 ipp_tag_t group_tag
; /* Group to copy */
278 typedef struct _ipp_job_s _ipp_job_t
;
280 typedef struct _ipp_printer_s
/**** Printer data ****/
282 int ipv4
, /* IPv4 listener */
283 ipv6
; /* IPv6 listener */
284 _ipp_srv_t ipp_ref
, /* Bonjour IPP service */
285 ipps_ref
, /* Bonjour IPPS service */
286 http_ref
, /* Bonjour HTTP service */
287 printer_ref
; /* Bonjour LPD service */
288 char *dnssd_name
, /* printer-dnssd-name */
289 *name
, /* printer-name */
290 *icon
, /* Icon filename */
291 *directory
, /* Spool directory */
292 *hostname
, /* Hostname */
293 *uri
, /* printer-uri-supported */
294 *command
; /* Command to run with job file */
296 size_t urilen
; /* Length of printer URI */
297 ipp_t
*attrs
; /* Static attributes */
298 time_t start_time
; /* Startup time */
299 time_t config_time
; /* printer-config-change-time */
300 ipp_pstate_t state
; /* printer-state value */
301 _ipp_preason_t state_reasons
; /* printer-state-reasons values */
302 time_t state_time
; /* printer-state-change-time */
303 cups_array_t
*jobs
; /* Jobs */
304 _ipp_job_t
*active_job
; /* Current active/pending job */
305 int next_job_id
; /* Next job-id value */
306 _cups_rwlock_t rwlock
; /* Printer lock */
307 _ipp_media_size_t main_size
; /* Ready media */
308 _ipp_media_type_t main_type
;
310 _ipp_media_size_t envelope_size
;
312 _ipp_media_size_t photo_size
;
313 _ipp_media_type_t photo_type
;
315 int supplies
[5]; /* Supply levels (0-100) */
318 struct _ipp_job_s
/**** Job data ****/
321 const char *name
, /* job-name */
322 *username
, /* job-originating-user-name */
323 *format
; /* document-format */
324 ipp_jstate_t state
; /* job-state value */
325 time_t created
, /* time-at-creation value */
326 processing
, /* time-at-processing value */
327 completed
; /* time-at-completed value */
328 int impressions
, /* job-impressions value */
329 impcompleted
; /* job-impressions-completed value */
330 ipp_t
*attrs
; /* Static attributes */
331 int cancel
; /* Non-zero when job canceled */
332 char *filename
; /* Print file name */
333 int fd
; /* Print file descriptor */
334 _ipp_printer_t
*printer
; /* Printer */
337 typedef struct _ipp_client_s
/**** Client data ****/
339 http_t
*http
; /* HTTP connection */
340 ipp_t
*request
, /* IPP request */
341 *response
; /* IPP response */
342 time_t start
; /* Request start time */
343 http_state_t operation
; /* Request operation */
344 ipp_op_t operation_id
; /* IPP operation-id */
345 char uri
[1024], /* Request URI */
346 *options
; /* URI options */
347 http_addr_t addr
; /* Client address */
348 char hostname
[256]; /* Client hostname */
349 _ipp_printer_t
*printer
; /* Printer */
350 _ipp_job_t
*job
; /* Current job, if any */
358 static void clean_jobs(_ipp_printer_t
*printer
);
359 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
360 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
361 ipp_tag_t group_tag
, int quickcopy
);
362 static void copy_job_attributes(_ipp_client_t
*client
,
363 _ipp_job_t
*job
, cups_array_t
*ra
);
364 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
365 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
366 static int create_listener(int family
, int port
);
367 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
368 static ipp_t
*create_media_size(int width
, int length
);
369 static _ipp_printer_t
*create_printer(const char *servername
,
370 const char *name
, const char *location
,
371 const char *make
, const char *model
,
373 const char *docformats
, int ppm
,
374 int ppm_color
, int duplex
, int port
,
375 int pin
, const char *subtype
,
376 const char *directory
,
378 const char *attrfile
);
379 static void debug_attributes(const char *title
, ipp_t
*ipp
,
381 static void delete_client(_ipp_client_t
*client
);
382 static void delete_job(_ipp_job_t
*job
);
383 static void delete_printer(_ipp_printer_t
*printer
);
385 static void DNSSD_API
dnssd_callback(DNSServiceRef sdRef
,
386 DNSServiceFlags flags
,
387 DNSServiceErrorType errorCode
,
391 _ipp_printer_t
*printer
);
392 #elif defined(HAVE_AVAHI)
393 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
394 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
395 #endif /* HAVE_DNSSD */
396 static void dnssd_init(void);
397 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
398 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
399 static ipp_t
*get_collection(FILE *fp
, const char *filename
, int *linenum
);
400 static char *get_token(FILE *fp
, char *buf
, int buflen
, int *linenum
);
401 static void html_escape(_ipp_client_t
*client
, const char *s
,
403 static void html_footer(_ipp_client_t
*client
);
404 static void html_header(_ipp_client_t
*client
, const char *title
);
405 static void html_printf(_ipp_client_t
*client
, const char *format
,
406 ...) __attribute__((__format__(__printf__
,
408 static void ipp_cancel_job(_ipp_client_t
*client
);
409 static void ipp_close_job(_ipp_client_t
*client
);
410 static void ipp_create_job(_ipp_client_t
*client
);
411 static void ipp_get_job_attributes(_ipp_client_t
*client
);
412 static void ipp_get_jobs(_ipp_client_t
*client
);
413 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
414 static void ipp_identify_printer(_ipp_client_t
*client
);
415 static void ipp_print_job(_ipp_client_t
*client
);
416 static void ipp_print_uri(_ipp_client_t
*client
);
417 static void ipp_send_document(_ipp_client_t
*client
);
418 static void ipp_send_uri(_ipp_client_t
*client
);
419 static void ipp_validate_job(_ipp_client_t
*client
);
420 static void load_attributes(const char *filename
, ipp_t
*attrs
);
421 static int parse_options(_ipp_client_t
*client
, cups_option_t
**options
);
422 static void process_attr_message(_ipp_job_t
*job
, char *message
);
423 static void *process_client(_ipp_client_t
*client
);
424 static int process_http(_ipp_client_t
*client
);
425 static int process_ipp(_ipp_client_t
*client
);
426 static void *process_job(_ipp_job_t
*job
);
427 static void process_state_message(_ipp_job_t
*job
, char *message
);
428 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
);
429 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
430 const char *content_coding
,
431 const char *type
, size_t length
);
432 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
433 const char *message
, ...)
434 __attribute__ ((__format__ (__printf__
, 3, 4)));
435 static void respond_unsupported(_ipp_client_t
*client
,
436 ipp_attribute_t
*attr
);
437 static void run_printer(_ipp_printer_t
*printer
);
438 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
439 static void usage(int status
) __attribute__((noreturn
));
440 static int valid_doc_attributes(_ipp_client_t
*client
);
441 static int valid_job_attributes(_ipp_client_t
*client
);
449 static DNSServiceRef DNSSDMaster
= NULL
;
450 #elif defined(HAVE_AVAHI)
451 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
452 static AvahiClient
*DNSSDClient
= NULL
;
453 #endif /* HAVE_DNSSD */
455 static int KeepFiles
= 0,
460 * 'main()' - Main entry to the sample server.
463 int /* O - Exit status */
464 main(int argc
, /* I - Number of command-line args */
465 char *argv
[]) /* I - Command-line arguments */
467 int i
; /* Looping var */
468 const char *opt
, /* Current option character */
469 *attrfile
= NULL
, /* Attributes file */
470 *command
= NULL
, /* Command to run with job files */
471 *servername
= NULL
, /* Server host name */
472 *name
= NULL
, /* Printer name */
473 *location
= "", /* Location of printer */
474 *make
= "Test", /* Manufacturer */
475 *model
= "Printer", /* Model */
476 *icon
= "printer.png", /* Icon file */
477 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
478 /* Supported formats */
480 const char *keypath
= NULL
; /* Keychain path */
481 #endif /* HAVE_SSL */
482 const char *subtype
= "_print"; /* Bonjour service subtype */
483 int port
= 0, /* Port number (0 = auto) */
484 duplex
= 0, /* Duplex mode */
485 ppm
= 10, /* Pages per minute for mono */
486 ppm_color
= 0, /* Pages per minute for color */
487 pin
= 0; /* PIN printing mode? */
488 char directory
[1024] = "", /* Spool directory */
489 hostname
[1024]; /* Auto-detected hostname */
490 _ipp_printer_t
*printer
; /* Printer object */
494 * Parse command-line arguments...
497 for (i
= 1; i
< argc
; i
++)
498 if (argv
[i
][0] == '-')
500 for (opt
= argv
[i
] + 1; *opt
; opt
++)
504 case '2' : /* -2 (enable 2-sided printing) */
509 case 'K' : /* -K keypath */
515 #endif /* HAVE_SSL */
517 case 'M' : /* -M manufacturer */
524 case 'P' : /* -P (PIN printing mode) */
528 case 'a' : /* -a attributes-file */
536 case 'c' : /* -c command */
544 case 'd' : /* -d spool-directory */
548 strlcpy(directory
, argv
[i
], sizeof(directory
));
551 case 'f' : /* -f type/subtype[,...] */
558 case 'h' : /* -h (show help) */
561 case 'i' : /* -i icon.png */
568 case 'k' : /* -k (keep files) */
572 case 'l' : /* -l location */
579 case 'm' : /* -m model */
586 case 'n' : /* -n hostname */
590 servername
= argv
[i
];
593 case 'p' : /* -p port */
595 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
597 port
= atoi(argv
[i
]);
600 case 'r' : /* -r subtype */
607 case 's' : /* -s speed[,color-speed] */
611 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
615 case 'v' : /* -v (be verbose) */
619 default : /* Unknown */
620 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
631 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
639 * Apply defaults as needed...
643 servername
= httpGetHostname(NULL
, hostname
, sizeof(hostname
));
649 * Windows is almost always used as a single user system, so use a default
650 * port number of 8631.
657 * Use 8000 + UID mod 1000 for the default port number...
660 port
= 8000 + ((int)getuid() % 1000);
663 fprintf(stderr
, "Listening on port %d.\n", port
);
668 const char *tmpdir
; /* Temporary directory */
671 if ((tmpdir
= getenv("TEMP")) == NULL
)
673 #elif defined(__APPLE__)
674 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
675 tmpdir
= "/private/tmp";
677 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
681 snprintf(directory
, sizeof(directory
), "%s/ippserver.%d", tmpdir
, (int)getpid());
683 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
685 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
686 directory
, strerror(errno
));
691 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
695 cupsSetServerCredentials(keypath
, servername
, 1);
696 #endif /* HAVE_SSL */
699 * Initialize Bonjour...
705 * Create the printer...
708 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
709 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
710 subtype
, directory
, command
, attrfile
)) == NULL
)
714 * Run the print service...
717 run_printer(printer
);
720 * Destroy the printer and exit...
723 delete_printer(printer
);
730 * 'clean_jobs()' - Clean out old (completed) jobs.
734 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
736 _ipp_job_t
*job
; /* Current job */
737 time_t cleantime
; /* Clean time */
740 if (cupsArrayCount(printer
->jobs
) == 0)
743 cleantime
= time(NULL
) - 60;
745 _cupsRWLockWrite(&(printer
->rwlock
));
746 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
748 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
749 if (job
->completed
&& job
->completed
< cleantime
)
751 cupsArrayRemove(printer
->jobs
, job
);
756 _cupsRWUnlock(&(printer
->rwlock
));
761 * 'compare_jobs()' - Compare two jobs.
764 static int /* O - Result of comparison */
765 compare_jobs(_ipp_job_t
*a
, /* I - First job */
766 _ipp_job_t
*b
) /* I - Second job */
768 return (b
->id
- a
->id
);
773 * 'copy_attributes()' - Copy attributes from one request to another.
777 copy_attributes(ipp_t
*to
, /* I - Destination request */
778 ipp_t
*from
, /* I - Source request */
779 cups_array_t
*ra
, /* I - Requested attributes */
780 ipp_tag_t group_tag
, /* I - Group to copy */
781 int quickcopy
) /* I - Do a quick copy? */
783 _ipp_filter_t filter
; /* Filter data */
787 filter
.group_tag
= group_tag
;
789 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
794 * 'copy_job_attrs()' - Copy job attributes to the response.
799 _ipp_client_t
*client
, /* I - Client */
800 _ipp_job_t
*job
, /* I - Job */
801 cups_array_t
*ra
) /* I - requested-attributes */
803 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
805 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
808 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
810 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
813 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
816 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
818 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
821 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
822 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
824 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
825 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
827 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
828 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
830 if (!ra
|| cupsArrayFind(ra
, "job-state"))
831 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
832 "job-state", job
->state
);
834 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
838 case IPP_JSTATE_PENDING
:
839 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
842 case IPP_JSTATE_HELD
:
844 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
845 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
846 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
848 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
851 case IPP_JSTATE_PROCESSING
:
853 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
855 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
858 case IPP_JSTATE_STOPPED
:
859 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
862 case IPP_JSTATE_CANCELED
:
863 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
866 case IPP_JSTATE_ABORTED
:
867 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
870 case IPP_JSTATE_COMPLETED
:
871 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
876 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
880 case IPP_JSTATE_PENDING
:
881 ippAddString(client
->response
, IPP_TAG_JOB
,
882 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
886 case IPP_JSTATE_HELD
:
888 ippAddString(client
->response
, IPP_TAG_JOB
,
889 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
890 "job-state-reasons", NULL
, "job-incoming");
891 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
892 ippAddString(client
->response
, IPP_TAG_JOB
,
893 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
894 "job-state-reasons", NULL
, "job-hold-until-specified");
896 ippAddString(client
->response
, IPP_TAG_JOB
,
897 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
898 "job-state-reasons", NULL
, "job-data-insufficient");
901 case IPP_JSTATE_PROCESSING
:
903 ippAddString(client
->response
, IPP_TAG_JOB
,
904 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
905 "job-state-reasons", NULL
, "processing-to-stop-point");
907 ippAddString(client
->response
, IPP_TAG_JOB
,
908 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
909 "job-state-reasons", NULL
, "job-printing");
912 case IPP_JSTATE_STOPPED
:
913 ippAddString(client
->response
, IPP_TAG_JOB
,
914 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
915 NULL
, "job-stopped");
918 case IPP_JSTATE_CANCELED
:
919 ippAddString(client
->response
, IPP_TAG_JOB
,
920 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
921 NULL
, "job-canceled-by-user");
924 case IPP_JSTATE_ABORTED
:
925 ippAddString(client
->response
, IPP_TAG_JOB
,
926 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
927 NULL
, "aborted-by-system");
930 case IPP_JSTATE_COMPLETED
:
931 ippAddString(client
->response
, IPP_TAG_JOB
,
932 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
933 NULL
, "job-completed-successfully");
938 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
939 ippAddInteger(client
->response
, IPP_TAG_JOB
,
940 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
941 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
943 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
944 ippAddInteger(client
->response
, IPP_TAG_JOB
,
945 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
946 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
951 * 'create_client()' - Accept a new network connection and create a client
955 static _ipp_client_t
* /* O - Client */
956 create_client(_ipp_printer_t
*printer
, /* I - Printer */
957 int sock
) /* I - Listen socket */
959 _ipp_client_t
*client
; /* Client */
962 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
964 perror("Unable to allocate memory for client");
968 client
->printer
= printer
;
971 * Accept the client and get the remote address...
974 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
976 perror("Unable to accept client connection");
983 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
986 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
993 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
997 static _ipp_job_t
* /* O - Job */
998 create_job(_ipp_client_t
*client
) /* I - Client */
1000 _ipp_job_t
*job
; /* Job */
1001 ipp_attribute_t
*attr
; /* Job attribute */
1002 char uri
[1024], /* job-uri value */
1003 uuid
[64]; /* job-uuid value */
1006 _cupsRWLockWrite(&(client
->printer
->rwlock
));
1007 if (client
->printer
->active_job
&&
1008 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
1011 * Only accept a single job at a time...
1014 _cupsRWLockWrite(&(client
->printer
->rwlock
));
1019 * Allocate and initialize the job object...
1022 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
1024 perror("Unable to allocate memory for job");
1028 job
->printer
= client
->printer
;
1029 job
->attrs
= ippNew();
1030 job
->state
= IPP_JSTATE_HELD
;
1034 * Copy all of the job attributes...
1037 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
1040 * Get the requesting-user-name, document format, and priority...
1043 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1044 job
->username
= ippGetString(attr
, 0, NULL
);
1046 job
->username
= "anonymous";
1048 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1050 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1052 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1053 job
->format
= ippGetString(attr
, 0, NULL
);
1054 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1055 job
->format
= ippGetString(attr
, 0, NULL
);
1057 job
->format
= "application/octet-stream";
1060 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1061 job
->impressions
= ippGetInteger(attr
, 0);
1063 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1064 job
->name
= ippGetString(attr
, 0, NULL
);
1067 * Add job description attributes and add to the jobs array...
1070 job
->id
= client
->printer
->next_job_id
++;
1072 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1073 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1075 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1076 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1077 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1078 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1079 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1080 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1082 cupsArrayAdd(client
->printer
->jobs
, job
);
1083 client
->printer
->active_job
= job
;
1085 _cupsRWUnlock(&(client
->printer
->rwlock
));
1092 * 'create_job_filename()' - Create the filename for a document in a job.
1095 static void create_job_filename(
1096 _ipp_printer_t
*printer
, /* I - Printer */
1097 _ipp_job_t
*job
, /* I - Job */
1098 char *fname
, /* I - Filename buffer */
1099 size_t fnamesize
) /* I - Size of filename buffer */
1101 char name
[256], /* "Safe" filename */
1102 *nameptr
; /* Pointer into filename */
1103 const char *ext
, /* Filename extension */
1104 *job_name
; /* job-name value */
1105 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1109 * Make a name from the job-name attribute...
1112 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1113 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1115 job_name
= "untitled";
1117 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1118 if (isalnum(*job_name
& 255) || *job_name
== '-')
1119 *nameptr
++ = (char)tolower(*job_name
& 255);
1126 * Figure out the extension...
1129 if (!strcasecmp(job
->format
, "image/jpeg"))
1131 else if (!strcasecmp(job
->format
, "image/png"))
1133 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1135 else if (!strcasecmp(job
->format
, "image/urf"))
1137 else if (!strcasecmp(job
->format
, "application/pdf"))
1139 else if (!strcasecmp(job
->format
, "application/postscript"))
1145 * Create a filename with the job-id, job-name, and document-format (extension)...
1148 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1153 * 'create_listener()' - Create a listener socket.
1156 static int /* O - Listener socket or -1 on error */
1157 create_listener(int family
, /* I - Address family */
1158 int port
) /* I - Port number */
1160 int sock
; /* Listener socket */
1161 http_addrlist_t
*addrlist
; /* Listen address */
1162 char service
[255]; /* Service port */
1165 snprintf(service
, sizeof(service
), "%d", port
);
1166 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1169 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1171 httpAddrFreeList(addrlist
);
1178 * 'create_media_col()' - Create a media-col value.
1181 static ipp_t
* /* O - media-col collection */
1182 create_media_col(const char *media
, /* I - Media name */
1183 const char *source
, /* I - Media source */
1184 const char *type
, /* I - Media type */
1185 int width
, /* I - x-dimension in 2540ths */
1186 int length
, /* I - y-dimension in 2540ths */
1187 int margins
) /* I - Value for margins */
1189 ipp_t
*media_col
= ippNew(), /* media-col value */
1190 *media_size
= create_media_size(width
, length
);
1191 /* media-size value */
1192 char media_key
[256]; /* media-key value */
1196 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1198 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1200 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1202 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1204 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1206 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1207 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1208 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1209 "media-bottom-margin", margins
);
1210 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1211 "media-left-margin", margins
);
1212 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1213 "media-right-margin", margins
);
1214 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1215 "media-top-margin", margins
);
1217 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1219 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1221 ippDelete(media_size
);
1228 * 'create_media_size()' - Create a media-size value.
1231 static ipp_t
* /* O - media-col collection */
1232 create_media_size(int width
, /* I - x-dimension in 2540ths */
1233 int length
) /* I - y-dimension in 2540ths */
1235 ipp_t
*media_size
= ippNew(); /* media-size value */
1238 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1240 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1243 return (media_size
);
1248 * 'create_printer()' - Create, register, and listen for connections to a
1252 static _ipp_printer_t
* /* O - Printer */
1253 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1254 const char *name
, /* I - printer-name */
1255 const char *location
, /* I - printer-location */
1256 const char *make
, /* I - printer-make-and-model */
1257 const char *model
, /* I - printer-make-and-model */
1258 const char *icon
, /* I - printer-icons */
1259 const char *docformats
, /* I - document-format-supported */
1260 int ppm
, /* I - Pages per minute in grayscale */
1261 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1262 int duplex
, /* I - 1 = duplex, 0 = simplex */
1263 int port
, /* I - Port for listeners or 0 for auto */
1264 int pin
, /* I - Require PIN printing */
1265 const char *subtype
, /* I - Bonjour service subtype */
1266 const char *directory
, /* I - Spool directory */
1267 const char *command
, /* I - Command to run on job files */
1268 const char *attrfile
) /* I - Attributes file */
1270 int i
, j
; /* Looping vars */
1271 _ipp_printer_t
*printer
; /* Printer */
1273 char path
[1024]; /* Full path to command */
1275 char uri
[1024], /* Printer URI */
1277 securi
[1024], /* Secure printer URI */
1278 *uris
[2], /* All URIs */
1279 #endif /* HAVE_SSL */
1280 icons
[1024], /* printer-icons URI */
1281 adminurl
[1024], /* printer-more-info URI */
1282 supplyurl
[1024],/* printer-supply-info-uri URI */
1283 device_id
[1024],/* printer-device-id */
1284 make_model
[128],/* printer-make-and-model */
1285 uuid
[128]; /* printer-uuid */
1286 int num_formats
; /* Number of document-format-supported values */
1287 char *defformat
, /* document-format-default value */
1288 *formats
[100], /* document-format-supported values */
1289 *ptr
; /* Pointer into string */
1290 const char *prefix
; /* Prefix string */
1291 int num_database
; /* Number of database values */
1292 ipp_attribute_t
*media_col_database
,
1293 /* media-col-database value */
1294 *media_size_supported
;
1295 /* media-size-supported value */
1296 ipp_t
*media_col_default
;
1297 /* media-col-default value */
1298 int media_col_index
;/* Current media-col-database value */
1299 int k_supported
; /* Maximum file size supported */
1301 struct statvfs spoolinfo
; /* FS info for spool directory */
1302 double spoolsize
; /* FS size */
1303 #elif defined(HAVE_STATFS)
1304 struct statfs spoolinfo
; /* FS info for spool directory */
1305 double spoolsize
; /* FS size */
1306 #endif /* HAVE_STATVFS */
1307 static const int orients
[4] = /* orientation-requested-supported values */
1309 IPP_ORIENT_PORTRAIT
,
1310 IPP_ORIENT_LANDSCAPE
,
1311 IPP_ORIENT_REVERSE_LANDSCAPE
,
1312 IPP_ORIENT_REVERSE_PORTRAIT
1314 static const char * const versions
[] =/* ipp-versions-supported values */
1320 static const char * const features
[] =/* ipp-features-supported values */
1324 static const int ops
[] = /* operations-supported values */
1328 IPP_OP_VALIDATE_JOB
,
1330 IPP_OP_SEND_DOCUMENT
,
1333 IPP_OP_GET_JOB_ATTRIBUTES
,
1335 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1336 IPP_OP_CANCEL_MY_JOBS
,
1338 IPP_OP_IDENTIFY_PRINTER
1340 static const char * const charsets
[] =/* charset-supported values */
1345 static const char * const compressions
[] =/* compression-supported values */
1350 #endif /* HAVE_LIBZ */
1353 static const char * const identify_actions
[] =
1358 static const char * const job_creation
[] =
1359 { /* job-creation-attributes-supported values */
1361 "ipp-attribute-fidelity",
1363 "job-accounting-user-id",
1369 "multiple-document-handling",
1370 "orientation-requested",
1374 static const char * const media_col_supported
[] =
1375 { /* media-col-supported values */
1376 "media-bottom-margin",
1377 "media-left-margin",
1378 "media-right-margin",
1384 static const int media_xxx_margin_supported
[] =
1385 { /* media-xxx-margin-supported values */
1389 static const char * const multiple_document_handling
[] =
1390 { /* multiple-document-handling-supported values */
1391 "separate-documents-uncollated-copies",
1392 "separate-documents-collated-copies"
1394 static const char * const overrides
[] =
1395 { /* overrides-supported */
1399 static const char * const print_color_mode_supported
[] =
1400 { /* print-color-mode-supported values */
1405 static const int print_quality_supported
[] =
1406 { /* print-quality-supported values */
1411 static const int pwg_raster_document_resolution_supported
[] =
1416 static const char * const pwg_raster_document_type_supported
[] =
1424 static const char * const reference_uri_schemes_supported
[] =
1425 { /* reference-uri-schemes-supported */
1431 #endif /* HAVE_SSL */
1433 static const char * const sides_supported
[] =
1434 { /* sides-supported values */
1436 "two-sided-long-edge",
1437 "two-sided-short-edge"
1439 static const char * const urf_supported
[] =
1440 { /* urf-supported values */
1443 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1451 static const char * const uri_authentication_supported
[] =
1452 { /* uri-authentication-supported values */
1456 static const char * const uri_security_supported
[] =
1457 { /* uri-security-supported values */
1461 #endif /* HAVE_SSL */
1462 static const char * const which_jobs
[] =
1463 { /* which-jobs-supported values */
1472 "processing-stopped"
1478 * If a command was specified, make sure it exists and is executable...
1483 if (*command
== '/' || !strncmp(command
, "./", 2))
1485 if (access(command
, X_OK
))
1487 fprintf(stderr
, "ippserver: Unable to execute command \"%s\": %s\n", command
, strerror(errno
));
1493 if (!cupsFileFind(command
, getenv("PATH"), 1, path
, sizeof(path
)))
1495 fprintf(stderr
, "ippserver: Unable to find command \"%s\".\n", command
);
1505 * Allocate memory for the printer...
1508 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1510 perror("ippserver: Unable to allocate memory for printer");
1516 printer
->name
= strdup(name
);
1517 printer
->dnssd_name
= strdup(printer
->name
);
1518 printer
->command
= command
? strdup(command
) : NULL
;
1519 printer
->directory
= strdup(directory
);
1520 printer
->hostname
= strdup(servername
);
1521 printer
->port
= port
;
1522 printer
->start_time
= time(NULL
);
1523 printer
->config_time
= printer
->start_time
;
1524 printer
->state
= IPP_PSTATE_IDLE
;
1525 printer
->state_reasons
= _IPP_PREASON_NONE
;
1526 printer
->state_time
= printer
->start_time
;
1527 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1528 printer
->next_job_id
= 1;
1530 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1531 printer
->uri
= strdup(uri
);
1532 printer
->urilen
= strlen(uri
);
1535 httpAssembleURI(HTTP_URI_CODING_ALL
, securi
, sizeof(securi
), "ipps", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1536 #endif /* HAVE_SSL */
1539 printer
->icon
= strdup(icon
);
1541 printer
->main_size
= _IPP_MEDIA_SIZE_A4
;
1542 printer
->main_type
= _IPP_MEDIA_TYPE_STATIONERY
;
1543 printer
->main_level
= 500;
1545 printer
->envelope_size
= _IPP_MEDIA_SIZE_NONE
;
1546 printer
->envelope_level
= 0;
1548 printer
->photo_size
= _IPP_MEDIA_SIZE_NONE
;
1549 printer
->photo_type
= _IPP_MEDIA_TYPE_NONE
;
1550 printer
->photo_level
= 0;
1552 printer
->supplies
[_IPP_SUPPLY_CYAN
] = 100;
1553 printer
->supplies
[_IPP_SUPPLY_MAGENTA
] = 100;
1554 printer
->supplies
[_IPP_SUPPLY_YELLOW
] = 100;
1555 printer
->supplies
[_IPP_SUPPLY_BLACK
] = 100;
1556 printer
->supplies
[_IPP_SUPPLY_WASTE
] = 0;
1558 _cupsRWInit(&(printer
->rwlock
));
1561 * Create the listener sockets...
1564 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1566 perror("Unable to create IPv4 listener");
1570 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1572 perror("Unable to create IPv6 listener");
1577 * Prepare values for the printer attributes...
1581 # define WEB_SCHEME "https"
1583 # define WEB_SCHEME "http"
1584 #endif /* HAVE_SSL */
1586 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/icon.png");
1587 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/");
1588 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/supplies");
1592 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1593 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1594 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1597 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1600 formats
[0] = strdup(docformats
);
1601 defformat
= formats
[0];
1602 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1605 formats
[num_formats
++] = ptr
;
1607 if (!strcasecmp(ptr
, "application/octet-stream"))
1611 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1612 ptr
= device_id
+ strlen(device_id
);
1614 for (i
= 0; i
< num_formats
; i
++)
1616 if (!strcasecmp(formats
[i
], "application/pdf"))
1617 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1618 else if (!strcasecmp(formats
[i
], "application/postscript"))
1619 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1620 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1621 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1622 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1623 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1624 else if (!strcasecmp(formats
[i
], "image/png"))
1625 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1626 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1627 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1632 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
1639 * Get the maximum spool size based on the size of the filesystem used for
1640 * the spool directory. If the host OS doesn't support the statfs call
1641 * or the filesystem is larger than 2TiB, always report INT_MAX.
1645 if (statvfs(printer
->directory
, &spoolinfo
))
1646 k_supported
= INT_MAX
;
1647 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1648 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1649 k_supported
= INT_MAX
;
1651 k_supported
= (int)spoolsize
;
1653 #elif defined(HAVE_STATFS)
1654 if (statfs(printer
->directory
, &spoolinfo
))
1655 k_supported
= INT_MAX
;
1656 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1657 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1658 k_supported
= INT_MAX
;
1660 k_supported
= (int)spoolsize
;
1663 k_supported
= INT_MAX
;
1664 #endif /* HAVE_STATVFS */
1667 * Create the printer attributes. This list of attributes is sorted to improve
1668 * performance when the client provides a requested-attributes attribute...
1671 printer
->attrs
= ippNew();
1674 load_attributes(attrfile
, printer
->attrs
);
1676 /* charset-configured */
1677 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-configured", NULL
, "utf-8");
1679 /* charset-supported */
1680 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]), NULL
, charsets
);
1682 /* color-supported */
1683 if (!ippFindAttribute(printer
->attrs
, "color-supported", IPP_TAG_ZERO
))
1684 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
1686 /* compression-supported */
1687 if (!ippFindAttribute(printer
->attrs
, "compression-supported", IPP_TAG_ZERO
))
1688 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "compression-supported", (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
, compressions
);
1690 /* copies-default */
1691 if (!ippFindAttribute(printer
->attrs
, "copies-default", IPP_TAG_ZERO
))
1692 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
1694 /* copies-supported */
1695 if (!ippFindAttribute(printer
->attrs
, "copies-supported", IPP_TAG_ZERO
))
1696 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1698 /* document-format-default */
1699 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1700 "document-format-default", NULL
, defformat
);
1702 /* document-format-supported */
1703 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1704 "document-format-supported", num_formats
, NULL
,
1705 (const char * const *)formats
);
1707 /* document-password-supported */
1708 if (!ippFindAttribute(printer
->attrs
, "document-password-supported", IPP_TAG_ZERO
))
1709 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1711 /* finishings-default */
1712 if (!ippFindAttribute(printer
->attrs
, "finishings-default", IPP_TAG_ZERO
))
1713 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
1715 /* finishings-supported */
1716 if (!ippFindAttribute(printer
->attrs
, "finishings-supported", IPP_TAG_ZERO
))
1717 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
1719 /* generated-natural-language-supported */
1720 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "generated-natural-language-supported", NULL
, "en");
1722 /* identify-actions-default */
1723 if (!ippFindAttribute(printer
->attrs
, "identify-actions-default", IPP_TAG_ZERO
))
1724 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1726 /* identify-actions-supported */
1727 if (!ippFindAttribute(printer
->attrs
, "identify-actions-supported", IPP_TAG_ZERO
))
1728 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
);
1730 /* ipp-features-supported */
1731 if (!ippFindAttribute(printer
->attrs
, "ipp-features-supported", IPP_TAG_ZERO
))
1732 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1734 /* ipp-versions-supported */
1735 if (!ippFindAttribute(printer
->attrs
, "ipp-versions-supported", IPP_TAG_ZERO
))
1736 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1738 /* job-account-id-default */
1739 if (!ippFindAttribute(printer
->attrs
, "job-account-id-default", IPP_TAG_ZERO
))
1740 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1742 /* job-account-id-supported */
1743 if (!ippFindAttribute(printer
->attrs
, "job-account-id-supported", IPP_TAG_ZERO
))
1744 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1746 /* job-accounting-user-id-default */
1747 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-default", IPP_TAG_ZERO
))
1748 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1750 /* job-accounting-user-id-supported */
1751 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-supported", IPP_TAG_ZERO
))
1752 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1754 /* job-creation-attributes-supported */
1755 if (!ippFindAttribute(printer
->attrs
, "job-creation-attributes-supported", IPP_TAG_ZERO
))
1756 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
);
1758 /* job-ids-supported */
1759 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1761 /* job-k-octets-supported */
1762 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1765 /* job-password-supported */
1766 if (!ippFindAttribute(printer
->attrs
, "job-password-supported", IPP_TAG_ZERO
))
1767 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-password-supported", 4);
1769 /* job-preferred-attributes-supported */
1770 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-preferred-attributes-supported", 0);
1772 /* job-priority-default */
1773 if (!ippFindAttribute(printer
->attrs
, "job-priority-default", IPP_TAG_ZERO
))
1774 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-default", 50);
1776 /* job-priority-supported */
1777 if (!ippFindAttribute(printer
->attrs
, "job-priority-supported", IPP_TAG_ZERO
))
1778 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-supported", 100);
1780 /* job-sheets-default */
1781 if (!ippFindAttribute(printer
->attrs
, "job-sheets-default", IPP_TAG_ZERO
))
1782 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-default", NULL
, "none");
1784 /* job-sheets-supported */
1785 if (!ippFindAttribute(printer
->attrs
, "job-sheets-supported", IPP_TAG_ZERO
))
1786 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-supported", NULL
, "none");
1788 /* media-bottom-margin-supported */
1789 if (!ippFindAttribute(printer
->attrs
, "media-bottom-margin-supported", IPP_TAG_ZERO
))
1790 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
);
1792 /* media-col-database */
1793 if (!ippFindAttribute(printer
->attrs
, "media-col-database", IPP_TAG_ZERO
))
1795 for (num_database
= 0, i
= 0;
1796 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1799 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1800 num_database
+= 3; /* auto + manual + envelope */
1801 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1802 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1804 num_database
+= 2; /* Regular + borderless */
1807 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-database", num_database
, NULL
);
1808 for (media_col_index
= 0, i
= 0;
1809 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1812 switch (media_col_sizes
[i
][2])
1816 * Regular + borderless for the general class; no source/type
1820 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]));
1821 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]));
1824 case _IPP_ENV_ONLY
:
1826 * Regular margins for "auto", "manual", and "envelope" sources.
1829 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]));
1830 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]));
1831 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]));
1833 case _IPP_PHOTO_ONLY
:
1835 * Photos have specific media types and can only be printed via
1836 * the auto, manual, and photo sources...
1840 j
< (int)(sizeof(media_type_supported
) /
1841 sizeof(media_type_supported
[0]));
1844 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1847 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]));
1848 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]));
1849 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]));
1856 /* media-col-default */
1857 if (!ippFindAttribute(printer
->attrs
, "media-col-default", IPP_TAG_ZERO
))
1859 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]);
1861 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1863 ippDelete(media_col_default
);
1866 /* media-col-supported */
1867 if (!ippFindAttribute(printer
->attrs
, "media-col-supported", IPP_TAG_ZERO
))
1868 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
);
1871 if (!ippFindAttribute(printer
->attrs
, "media-default", IPP_TAG_ZERO
))
1872 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media_supported
[0]);
1874 /* media-left-margin-supported */
1875 if (!ippFindAttribute(printer
->attrs
, "media-left-margin-supported", IPP_TAG_ZERO
))
1876 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
);
1878 /* media-right-margin-supported */
1879 if (!ippFindAttribute(printer
->attrs
, "media-right-margin-supported", IPP_TAG_ZERO
))
1880 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
);
1882 /* media-supported */
1883 if (!ippFindAttribute(printer
->attrs
, "media-supported", IPP_TAG_ZERO
))
1884 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
);
1886 /* media-size-supported */
1887 if (!ippFindAttribute(printer
->attrs
, "media-size-supported", IPP_TAG_ZERO
))
1889 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-size-supported", (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0])), NULL
);
1892 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1895 ipp_t
*size
= create_media_size(media_col_sizes
[i
][0], media_col_sizes
[i
][1]);
1897 ippSetCollection(printer
->attrs
, &media_size_supported
, i
, size
);
1902 /* media-source-supported */
1903 if (!ippFindAttribute(printer
->attrs
, "media-source-supported", IPP_TAG_ZERO
))
1904 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
);
1906 /* media-top-margin-supported */
1907 if (!ippFindAttribute(printer
->attrs
, "media-top-margin-supported", IPP_TAG_ZERO
))
1908 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
);
1910 /* media-type-supported */
1911 if (!ippFindAttribute(printer
->attrs
, "media-type-supported", IPP_TAG_ZERO
))
1912 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
);
1914 /* multiple-document-handling-supported */
1915 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
);
1917 /* multiple-document-jobs-supported */
1918 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1920 /* multiple-operation-time-out */
1921 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1923 /* multiple-operation-time-out-action */
1924 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1926 /* natural-language-configured */
1927 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1928 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1929 "natural-language-configured", NULL
, "en");
1931 /* number-up-default */
1932 if (!ippFindAttribute(printer
->attrs
, "number-up-default", IPP_TAG_ZERO
))
1933 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-default", 1);
1935 /* number-up-supported */
1936 if (!ippFindAttribute(printer
->attrs
, "number-up-supported", IPP_TAG_ZERO
))
1937 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-supported", 1);
1939 /* operations-supported */
1940 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1942 /* orientation-requested-default */
1943 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-default", IPP_TAG_ZERO
))
1944 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "orientation-requested-default", 0);
1946 /* orientation-requested-supported */
1947 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-supported", IPP_TAG_ZERO
))
1948 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", 4, orients
);
1950 /* output-bin-default */
1951 if (!ippFindAttribute(printer
->attrs
, "output-bin-default", IPP_TAG_ZERO
))
1952 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
1954 /* output-bin-supported */
1955 if (!ippFindAttribute(printer
->attrs
, "output-bin-supported", IPP_TAG_ZERO
))
1956 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
1958 /* overrides-supported */
1959 if (!ippFindAttribute(printer
->attrs
, "overrides-supported", IPP_TAG_ZERO
))
1960 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1962 /* page-ranges-supported */
1963 if (!ippFindAttribute(printer
->attrs
, "page-ranges-supported", IPP_TAG_ZERO
))
1964 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1966 /* pages-per-minute */
1967 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1968 "pages-per-minute", ppm
);
1970 /* pages-per-minute-color */
1972 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1973 "pages-per-minute-color", ppm_color
);
1975 /* pdl-override-supported */
1976 if (!ippFindAttribute(printer
->attrs
, "pdl-override-supported", IPP_TAG_ZERO
))
1977 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
1979 /* print-color-mode-default */
1980 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-default", IPP_TAG_ZERO
))
1981 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1983 /* print-color-mode-supported */
1984 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-supported", IPP_TAG_ZERO
))
1985 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
);
1987 /* print-content-optimize-default */
1988 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-default", IPP_TAG_ZERO
))
1989 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1991 /* print-content-optimize-supported */
1992 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-supported", IPP_TAG_ZERO
))
1993 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
1995 /* print-rendering-intent-default */
1996 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-default", IPP_TAG_ZERO
))
1997 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
1999 /* print-rendering-intent-supported */
2000 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-supported", IPP_TAG_ZERO
))
2001 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
2003 /* print-quality-default */
2004 if (!ippFindAttribute(printer
->attrs
, "print-quality-default", IPP_TAG_ZERO
))
2005 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
2007 /* print-quality-supported */
2008 if (!ippFindAttribute(printer
->attrs
, "print-quality-supported", IPP_TAG_ZERO
))
2009 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
);
2011 /* printer-device-id */
2012 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2013 "printer-device-id", NULL
, device_id
);
2015 /* printer-get-attributes-supported */
2016 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
2018 /* printer-geo-location */
2019 if (!ippFindAttribute(printer
->attrs
, "printer-geo-location", IPP_TAG_ZERO
))
2020 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
2023 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
2024 "printer-icons", NULL
, icons
);
2026 /* printer-is-accepting-jobs */
2027 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
2030 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
2032 /* printer-location */
2033 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2034 "printer-location", NULL
, location
);
2036 /* printer-make-and-model */
2037 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2038 "printer-make-and-model", NULL
, make_model
);
2040 /* printer-mandatory-job-attributes */
2041 if (pin
&& !ippFindAttribute(printer
->attrs
, "", IPP_TAG_ZERO
))
2043 static const char * const names
[] =
2046 "job-accounting-user-id",
2050 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2051 "printer-mandatory-job-attributes",
2052 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
2055 /* printer-more-info */
2056 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
2059 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
2061 /* printer-organization */
2062 if (!ippFindAttribute(printer
->attrs
, "printer-organization", IPP_TAG_ZERO
))
2063 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
2065 /* printer-organizational-unit */
2066 if (!ippFindAttribute(printer
->attrs
, "printer-organizational-unit", IPP_TAG_ZERO
))
2067 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
2069 /* printer-resolution-default */
2070 if (!ippFindAttribute(printer
->attrs
, "printer-resolution-default", IPP_TAG_ZERO
))
2071 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
2073 /* printer-resolution-supported */
2074 if (!ippFindAttribute(printer
->attrs
, "printer-resolutions-supported", IPP_TAG_ZERO
))
2075 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
2077 /* printer-supply-description */
2078 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
);
2080 /* printer-supply-info-uri */
2081 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
2083 /* printer-uri-supported */
2088 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", 2, NULL
, (const char **)uris
);
2091 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
2092 #endif /* HAVE_SSL */
2095 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
2096 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
2098 /* pwg-raster-document-xxx-supported */
2099 for (i
= 0; i
< num_formats
; i
++)
2100 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
2103 if (i
< num_formats
)
2105 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-resolution-supported", IPP_TAG_ZERO
))
2106 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
);
2107 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-sheet-back", IPP_TAG_ZERO
))
2108 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "pwg-raster-document-sheet-back", NULL
, "normal");
2109 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-type-supported", IPP_TAG_ZERO
))
2110 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
);
2113 /* reference-uri-scheme-supported */
2114 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
);
2117 if (!ippFindAttribute(printer
->attrs
, "sides-default", IPP_TAG_ZERO
))
2118 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
2120 /* sides-supported */
2121 if (!ippFindAttribute(printer
->attrs
, "sides-supported", IPP_TAG_ZERO
))
2122 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
2125 for (i
= 0; i
< num_formats
; i
++)
2126 if (!strcasecmp(formats
[i
], "image/urf"))
2129 if (i
< num_formats
&& !ippFindAttribute(printer
->attrs
, "urf-supported", IPP_TAG_ZERO
))
2130 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2132 /* uri-authentication-supported */
2134 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_supported
);
2136 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "none");
2137 #endif /* HAVE_SSL */
2139 /* uri-security-supported */
2141 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", 2, NULL
, uri_security_supported
);
2143 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", NULL
, "none");
2144 #endif /* HAVE_SSL */
2146 /* which-jobs-supported */
2147 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
);
2151 debug_attributes("Printer", printer
->attrs
, 0);
2154 * Register the printer with Bonjour...
2157 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2168 * If we get here we were unable to create the printer...
2173 delete_printer(printer
);
2179 * 'debug_attributes()' - Print attributes in a request or response.
2183 debug_attributes(const char *title
, /* I - Title */
2184 ipp_t
*ipp
, /* I - Request/response */
2185 int type
) /* I - 0 = object, 1 = request, 2 = response */
2187 ipp_tag_t group_tag
; /* Current group */
2188 ipp_attribute_t
*attr
; /* Current attribute */
2189 char buffer
[2048]; /* String buffer for value */
2190 int major
, minor
; /* Version */
2196 fprintf(stderr
, "%s:\n", title
);
2197 major
= ippGetVersion(ipp
, &minor
);
2198 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2200 fprintf(stderr
, " operation-id=%s(%04x)\n",
2201 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2203 fprintf(stderr
, " status-code=%s(%04x)\n",
2204 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2205 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2207 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2209 attr
= ippNextAttribute(ipp
))
2211 if (ippGetGroupTag(attr
) != group_tag
)
2213 group_tag
= ippGetGroupTag(attr
);
2214 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2217 if (ippGetName(attr
))
2219 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2220 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2221 ippGetCount(attr
) > 1 ? "1setOf " : "",
2222 ippTagString(ippGetValueTag(attr
)), buffer
);
2229 * 'delete_client()' - Close the socket and free all memory used by a client
2234 delete_client(_ipp_client_t
*client
) /* I - Client */
2237 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2240 * Flush pending writes before closing...
2243 httpFlushWrite(client
->http
);
2249 httpClose(client
->http
);
2251 ippDelete(client
->request
);
2252 ippDelete(client
->response
);
2259 * 'delete_job()' - Remove from the printer and free all memory used by a job
2264 delete_job(_ipp_job_t
*job
) /* I - Job */
2267 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2269 ippDelete(job
->attrs
);
2274 unlink(job
->filename
);
2276 free(job
->filename
);
2284 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2285 * used by a printer object.
2289 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2291 if (printer
->ipv4
>= 0)
2292 close(printer
->ipv4
);
2294 if (printer
->ipv6
>= 0)
2295 close(printer
->ipv6
);
2298 if (printer
->printer_ref
)
2299 DNSServiceRefDeallocate(printer
->printer_ref
);
2300 if (printer
->ipp_ref
)
2301 DNSServiceRefDeallocate(printer
->ipp_ref
);
2302 if (printer
->ipps_ref
)
2303 DNSServiceRefDeallocate(printer
->ipps_ref
);
2304 if (printer
->http_ref
)
2305 DNSServiceRefDeallocate(printer
->http_ref
);
2306 #elif defined(HAVE_AVAHI)
2307 avahi_threaded_poll_lock(DNSSDMaster
);
2309 if (printer
->printer_ref
)
2310 avahi_entry_group_free(printer
->printer_ref
);
2311 if (printer
->ipp_ref
)
2312 avahi_entry_group_free(printer
->ipp_ref
);
2313 if (printer
->ipps_ref
)
2314 avahi_entry_group_free(printer
->ipps_ref
);
2315 if (printer
->http_ref
)
2316 avahi_entry_group_free(printer
->http_ref
);
2318 avahi_threaded_poll_unlock(DNSSDMaster
);
2319 #endif /* HAVE_DNSSD */
2321 if (printer
->dnssd_name
)
2322 free(printer
->dnssd_name
);
2324 free(printer
->name
);
2326 free(printer
->icon
);
2327 if (printer
->command
)
2328 free(printer
->command
);
2329 if (printer
->directory
)
2330 free(printer
->directory
);
2331 if (printer
->hostname
)
2332 free(printer
->hostname
);
2336 ippDelete(printer
->attrs
);
2337 cupsArrayDelete(printer
->jobs
);
2345 * 'dnssd_callback()' - Handle Bonjour registration events.
2348 static void DNSSD_API
2350 DNSServiceRef sdRef
, /* I - Service reference */
2351 DNSServiceFlags flags
, /* I - Status flags */
2352 DNSServiceErrorType errorCode
, /* I - Error, if any */
2353 const char *name
, /* I - Service name */
2354 const char *regtype
, /* I - Service type */
2355 const char *domain
, /* I - Domain for service */
2356 _ipp_printer_t
*printer
) /* I - Printer */
2364 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2365 regtype
, (int)errorCode
);
2368 else if (strcasecmp(name
, printer
->dnssd_name
))
2371 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2373 /* No lock needed since only the main thread accesses/changes this */
2374 free(printer
->dnssd_name
);
2375 printer
->dnssd_name
= strdup(name
);
2380 #elif defined(HAVE_AVAHI)
2382 * 'dnssd_callback()' - Handle Bonjour registration events.
2387 AvahiEntryGroup
*srv
, /* I - Service */
2388 AvahiEntryGroupState state
, /* I - Registration state */
2389 void *context
) /* I - Printer */
2398 * 'dnssd_client_cb()' - Client callback for Avahi.
2400 * Called whenever the client or server state changes...
2405 AvahiClient
*c
, /* I - Client */
2406 AvahiClientState state
, /* I - Current state */
2407 void *userdata
) /* I - User data (unused) */
2417 fprintf(stderr
, "Ignore Avahi state %d.\n", state
);
2420 case AVAHI_CLIENT_FAILURE
:
2421 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
2423 fputs("Avahi server crashed, exiting.\n", stderr
);
2429 #endif /* HAVE_DNSSD */
2433 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2440 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2442 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2446 #elif defined(HAVE_AVAHI)
2447 int error
; /* Error code, if any */
2449 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2451 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2455 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2457 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2461 avahi_threaded_poll_start(DNSSDMaster
);
2462 #endif /* HAVE_DNSSD */
2467 * 'filter_cb()' - Filter printer attributes based on the requested array.
2470 static int /* O - 1 to copy, 0 to ignore */
2471 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2472 ipp_t
*dst
, /* I - Destination (unused) */
2473 ipp_attribute_t
*attr
) /* I - Source attribute */
2476 * Filter attributes as needed...
2479 #ifndef WIN32 /* Avoid MS compiler bug */
2483 ipp_tag_t group
= ippGetGroupTag(attr
);
2484 const char *name
= ippGetName(attr
);
2486 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
)))
2489 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2494 * 'find_job()' - Find a job specified in a request.
2497 static _ipp_job_t
* /* O - Job or NULL */
2498 find_job(_ipp_client_t
*client
) /* I - Client */
2500 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2501 _ipp_job_t key
, /* Job search key */
2502 *job
; /* Matching job, if any */
2505 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2507 const char *uri
= ippGetString(attr
, 0, NULL
);
2509 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2510 uri
[client
->printer
->urilen
] == '/')
2511 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2515 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2516 key
.id
= ippGetInteger(attr
, 0);
2518 _cupsRWLockRead(&(client
->printer
->rwlock
));
2519 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2520 _cupsRWUnlock(&(client
->printer
->rwlock
));
2527 * 'get_collection()' - Get a collection value from a file.
2530 static ipp_t
* /* O - Collection value */
2531 get_collection(FILE *fp
, /* I - File to read from */
2532 const char *filename
, /* I - Attributes filename */
2533 int *linenum
) /* IO - Line number */
2535 char token
[1024], /* Token from file */
2536 attr
[128]; /* Attribute name */
2537 ipp_tag_t value
; /* Current value type */
2538 ipp_t
*col
= ippNew(); /* Collection value */
2539 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
2542 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
2544 if (!strcmp(token
, "}"))
2546 else if (!strcmp(token
, "{") && lastcol
)
2549 * Another collection value
2552 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2553 /* Collection value */
2556 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
2560 else if (!_cups_strcasecmp(token
, "MEMBER"))
2568 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2570 fprintf(stderr
, "ippserver: Missing MEMBER value tag on line %d of \"%s\".\n", *linenum
, filename
);
2574 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
2576 fprintf(stderr
, "ippserver: Bad MEMBER value tag \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2580 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
2582 fprintf(stderr
, "ippserver: Missing MEMBER name on line %d of \"%s\".\n", *linenum
, filename
);
2586 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2588 fprintf(stderr
, "ippserver: Missing MEMBER value on line %d of \"%s\".\n", *linenum
, filename
);
2594 case IPP_TAG_BOOLEAN
:
2595 if (!_cups_strcasecmp(token
, "true"))
2596 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
2598 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
2601 case IPP_TAG_INTEGER
:
2603 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
2606 case IPP_TAG_RESOLUTION
:
2608 int xres
, /* X resolution */
2609 yres
; /* Y resolution */
2610 char units
[6]; /* Units */
2612 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
2613 (_cups_strcasecmp(units
, "dpi") &&
2614 _cups_strcasecmp(units
, "dpc") &&
2615 _cups_strcasecmp(units
, "dpcm") &&
2616 _cups_strcasecmp(units
, "other")))
2618 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2622 if (!_cups_strcasecmp(units
, "dpi"))
2623 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
2624 else if (!_cups_strcasecmp(units
, "dpc") ||
2625 !_cups_strcasecmp(units
, "dpcm"))
2626 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
2628 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
2632 case IPP_TAG_RANGE
:
2634 int lowers
[4], /* Lower value */
2635 uppers
[4], /* Upper values */
2636 num_vals
; /* Number of values */
2639 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
2640 lowers
+ 0, uppers
+ 0,
2641 lowers
+ 1, uppers
+ 1,
2642 lowers
+ 2, uppers
+ 2,
2643 lowers
+ 3, uppers
+ 3);
2645 if ((num_vals
& 1) || num_vals
== 0)
2647 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2651 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
2656 case IPP_TAG_BEGIN_COLLECTION
:
2657 if (!strcmp(token
, "{"))
2659 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2660 /* Collection value */
2664 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
2672 fprintf(stderr
, "ippserver: Bad collection value on line %d of \"%s\".\n", *linenum
, filename
);
2676 case IPP_TAG_STRING
:
2677 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
2681 if (!strchr(token
, ','))
2682 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
2686 * Multiple string values...
2689 int num_values
; /* Number of values */
2690 char *values
[100], /* Values */
2691 *ptr
; /* Pointer to next value */
2697 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
2700 values
[num_values
] = ptr
;
2702 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
2706 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
2707 NULL
, (const char **)values
);
2717 * If we get here there was a parse error; free memory and return.
2729 * 'get_token()' - Get a token from a file.
2732 static char * /* O - Token from file or NULL on EOF */
2733 get_token(FILE *fp
, /* I - File to read from */
2734 char *buf
, /* I - Buffer to read into */
2735 int buflen
, /* I - Length of buffer */
2736 int *linenum
) /* IO - Current line number */
2738 int ch
, /* Character from file */
2739 quote
; /* Quoting character */
2740 char *bufptr
, /* Pointer into buffer */
2741 *bufend
; /* End of buffer */
2747 * Skip whitespace...
2750 while (isspace(ch
= getc(fp
)))
2762 else if (ch
== '\'' || ch
== '\"')
2765 * Quoted text or regular expression...
2770 bufend
= buf
+ buflen
- 1;
2772 while ((ch
= getc(fp
)) != EOF
)
2777 * Escape next character...
2780 if (bufptr
< bufend
)
2781 *bufptr
++ = (char)ch
;
2783 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
2784 *bufptr
++ = (char)ch
;
2786 else if (ch
== quote
)
2788 else if (bufptr
< bufend
)
2789 *bufptr
++ = (char)ch
;
2802 while ((ch
= getc(fp
)) != EOF
)
2808 else if (ch
== '{' || ch
== '}' || ch
== ',')
2818 * Whitespace delimited text...
2824 bufend
= buf
+ buflen
- 1;
2826 while ((ch
= getc(fp
)) != EOF
)
2827 if (isspace(ch
) || ch
== '#')
2829 else if (bufptr
< bufend
)
2830 *bufptr
++ = (char)ch
;
2834 else if (ch
== '\n')
2846 * 'html_escape()' - Write a HTML-safe string.
2850 html_escape(_ipp_client_t
*client
, /* I - Client */
2851 const char *s
, /* I - String to write */
2852 size_t slen
) /* I - Number of characters to write */
2854 const char *start
, /* Start of segment */
2855 *end
; /* End of string */
2859 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2861 while (*s
&& s
< end
)
2863 if (*s
== '&' || *s
== '<')
2866 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2869 httpWrite2(client
->http
, "&", 5);
2871 httpWrite2(client
->http
, "<", 4);
2880 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2885 * 'html_footer()' - Show the web interface footer.
2887 * This function also writes the trailing 0-length chunk.
2891 html_footer(_ipp_client_t
*client
) /* I - Client */
2897 httpWrite2(client
->http
, "", 0);
2902 * 'html_header()' - Show the web interface header and title.
2906 html_header(_ipp_client_t
*client
, /* I - Client */
2907 const char *title
) /* I - Title */
2913 "<title>%s</title>\n"
2914 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2915 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2916 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2917 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2919 "body { font-family: sans-serif; margin: 0; }\n"
2920 "div.body { padding: 0px 10px 10px; }\n"
2921 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2922 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2923 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2924 "table.form th { text-align: right; }\n"
2925 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2926 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2927 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2928 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2929 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2930 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2931 "table.nav td { margin: 0; text-align: center; }\n"
2932 "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"
2933 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2934 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2935 "td.nav:hover { background: #666; color: #fff; }\n"
2936 "td.nav:active { background: #000; color: #ff0; }\n"
2940 "<table class=\"nav\"><tr>"
2941 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2942 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2943 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2945 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2950 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2954 html_printf(_ipp_client_t
*client
, /* I - Client */
2955 const char *format
, /* I - Printf-style format string */
2956 ...) /* I - Additional arguments as needed */
2958 va_list ap
; /* Pointer to arguments */
2959 const char *start
; /* Start of string */
2960 char size
, /* Size character (h, l, L) */
2961 type
; /* Format type character */
2962 int width
, /* Width of field */
2963 prec
; /* Number of characters of precision */
2964 char tformat
[100], /* Temporary format string for sprintf() */
2965 *tptr
, /* Pointer into temporary format */
2966 temp
[1024]; /* Buffer for formatted numbers */
2967 char *s
; /* Pointer to string */
2971 * Loop through the format string, formatting as needed...
2974 va_start(ap
, format
);
2982 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2985 *tptr
++ = *format
++;
2989 httpWrite2(client
->http
, "%", 1);
2994 else if (strchr(" -+#\'", *format
))
2995 *tptr
++ = *format
++;
3000 * Get width from argument...
3004 width
= va_arg(ap
, int);
3006 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
3007 tptr
+= strlen(tptr
);
3013 while (isdigit(*format
& 255))
3015 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3018 width
= width
* 10 + *format
++ - '0';
3024 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3032 * Get precision from argument...
3036 prec
= va_arg(ap
, int);
3038 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
3039 tptr
+= strlen(tptr
);
3045 while (isdigit(*format
& 255))
3047 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3050 prec
= prec
* 10 + *format
++ - '0';
3055 if (*format
== 'l' && format
[1] == 'l')
3059 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
3067 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
3069 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3084 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3093 case 'E' : /* Floating point formats */
3098 if ((size_t)(width
+ 2) > sizeof(temp
))
3101 sprintf(temp
, tformat
, va_arg(ap
, double));
3103 httpWrite2(client
->http
, temp
, strlen(temp
));
3106 case 'B' : /* Integer formats */
3114 if ((size_t)(width
+ 2) > sizeof(temp
))
3117 # ifdef HAVE_LONG_LONG
3119 sprintf(temp
, tformat
, va_arg(ap
, long long));
3121 # endif /* HAVE_LONG_LONG */
3123 sprintf(temp
, tformat
, va_arg(ap
, long));
3125 sprintf(temp
, tformat
, va_arg(ap
, int));
3127 httpWrite2(client
->http
, temp
, strlen(temp
));
3130 case 'p' : /* Pointer value */
3131 if ((size_t)(width
+ 2) > sizeof(temp
))
3134 sprintf(temp
, tformat
, va_arg(ap
, void *));
3136 httpWrite2(client
->http
, temp
, strlen(temp
));
3139 case 'c' : /* Character or character array */
3142 temp
[0] = (char)va_arg(ap
, int);
3144 html_escape(client
, temp
, 1);
3147 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
3150 case 's' : /* String */
3151 if ((s
= va_arg(ap
, char *)) == NULL
)
3154 html_escape(client
, s
, strlen(s
));
3163 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
3170 * 'ipp_cancel_job()' - Cancel a job.
3174 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
3176 _ipp_job_t
*job
; /* Job information */
3183 if ((job
= find_job(client
)) == NULL
)
3185 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3190 * See if the job is already completed, canceled, or aborted; if so,
3191 * we can't cancel...
3196 case IPP_JSTATE_CANCELED
:
3197 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3198 "Job #%d is already canceled - can\'t cancel.", job
->id
);
3201 case IPP_JSTATE_ABORTED
:
3202 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3203 "Job #%d is already aborted - can\'t cancel.", job
->id
);
3206 case IPP_JSTATE_COMPLETED
:
3207 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3208 "Job #%d is already completed - can\'t cancel.", job
->id
);
3216 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3218 if (job
->state
== IPP_JSTATE_PROCESSING
||
3219 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
3223 job
->state
= IPP_JSTATE_CANCELED
;
3224 job
->completed
= time(NULL
);
3227 _cupsRWUnlock(&(client
->printer
->rwlock
));
3229 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3236 * 'ipp_close_job()' - Close an open job.
3240 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
3242 _ipp_job_t
*job
; /* Job information */
3249 if ((job
= find_job(client
)) == NULL
)
3251 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3256 * See if the job is already completed, canceled, or aborted; if so,
3257 * we can't cancel...
3262 case IPP_JSTATE_CANCELED
:
3263 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3264 "Job #%d is canceled - can\'t close.", job
->id
);
3267 case IPP_JSTATE_ABORTED
:
3268 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3269 "Job #%d is aborted - can\'t close.", job
->id
);
3272 case IPP_JSTATE_COMPLETED
:
3273 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3274 "Job #%d is completed - can\'t close.", job
->id
);
3277 case IPP_JSTATE_PROCESSING
:
3278 case IPP_JSTATE_STOPPED
:
3279 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3280 "Job #%d is already closed.", job
->id
);
3284 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3291 * 'ipp_create_job()' - Create a job object.
3295 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
3297 _ipp_job_t
*job
; /* New job */
3298 cups_array_t
*ra
; /* Attributes to send in response */
3302 * Validate print job attributes...
3305 if (!valid_job_attributes(client
))
3307 httpFlush(client
->http
);
3312 * Do we have a file to print?
3315 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3317 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3318 "Unexpected document data following request.");
3326 if ((job
= create_job(client
)) == NULL
)
3328 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3329 "Currently printing another job.");
3334 * Return the job info...
3337 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3339 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3340 cupsArrayAdd(ra
, "job-id");
3341 cupsArrayAdd(ra
, "job-state");
3342 cupsArrayAdd(ra
, "job-state-message");
3343 cupsArrayAdd(ra
, "job-state-reasons");
3344 cupsArrayAdd(ra
, "job-uri");
3346 copy_job_attributes(client
, job
, ra
);
3347 cupsArrayDelete(ra
);
3352 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3356 ipp_get_job_attributes(
3357 _ipp_client_t
*client
) /* I - Client */
3359 _ipp_job_t
*job
; /* Job */
3360 cups_array_t
*ra
; /* requested-attributes */
3363 if ((job
= find_job(client
)) == NULL
)
3365 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3369 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3371 ra
= ippCreateRequestedArray(client
->request
);
3372 copy_job_attributes(client
, job
, ra
);
3373 cupsArrayDelete(ra
);
3378 * 'ipp_get_jobs()' - Get a list of job objects.
3382 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
3384 ipp_attribute_t
*attr
; /* Current attribute */
3385 const char *which_jobs
= NULL
;
3386 /* which-jobs values */
3387 int job_comparison
; /* Job comparison */
3388 ipp_jstate_t job_state
; /* job-state value */
3389 int first_job_id
, /* First job ID */
3390 limit
, /* Maximum number of jobs to return */
3391 count
; /* Number of jobs that match */
3392 const char *username
; /* Username */
3393 _ipp_job_t
*job
; /* Current job pointer */
3394 cups_array_t
*ra
; /* Requested attributes array */
3398 * See if the "which-jobs" attribute have been specified...
3401 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3402 IPP_TAG_KEYWORD
)) != NULL
)
3404 which_jobs
= ippGetString(attr
, 0, NULL
);
3405 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3408 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3410 job_comparison
= -1;
3411 job_state
= IPP_JSTATE_STOPPED
;
3413 else if (!strcmp(which_jobs
, "completed"))
3416 job_state
= IPP_JSTATE_CANCELED
;
3418 else if (!strcmp(which_jobs
, "aborted"))
3421 job_state
= IPP_JSTATE_ABORTED
;
3423 else if (!strcmp(which_jobs
, "all"))
3426 job_state
= IPP_JSTATE_PENDING
;
3428 else if (!strcmp(which_jobs
, "canceled"))
3431 job_state
= IPP_JSTATE_CANCELED
;
3433 else if (!strcmp(which_jobs
, "pending"))
3436 job_state
= IPP_JSTATE_PENDING
;
3438 else if (!strcmp(which_jobs
, "pending-held"))
3441 job_state
= IPP_JSTATE_HELD
;
3443 else if (!strcmp(which_jobs
, "processing"))
3446 job_state
= IPP_JSTATE_PROCESSING
;
3448 else if (!strcmp(which_jobs
, "processing-stopped"))
3451 job_state
= IPP_JSTATE_STOPPED
;
3455 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3456 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3457 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3458 "which-jobs", NULL
, which_jobs
);
3463 * See if they want to limit the number of jobs reported...
3466 if ((attr
= ippFindAttribute(client
->request
, "limit",
3467 IPP_TAG_INTEGER
)) != NULL
)
3469 limit
= ippGetInteger(attr
, 0);
3471 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3476 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3477 IPP_TAG_INTEGER
)) != NULL
)
3479 first_job_id
= ippGetInteger(attr
, 0);
3481 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3488 * See if we only want to see jobs for a specific user...
3493 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3494 IPP_TAG_BOOLEAN
)) != NULL
)
3496 int my_jobs
= ippGetBoolean(attr
, 0);
3498 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3499 my_jobs
? "true" : "false");
3503 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3504 IPP_TAG_NAME
)) == NULL
)
3506 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3507 "Need requesting-user-name with my-jobs.");
3511 username
= ippGetString(attr
, 0, NULL
);
3513 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3514 client
->hostname
, username
);
3519 * OK, build a list of jobs for this printer...
3522 ra
= ippCreateRequestedArray(client
->request
);
3524 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3526 _cupsRWLockRead(&(client
->printer
->rwlock
));
3528 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3529 (limit
<= 0 || count
< limit
) && job
;
3530 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3533 * Filter out jobs that don't match...
3536 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3537 (job_comparison
== 0 && job
->state
!= job_state
) ||
3538 (job_comparison
> 0 && job
->state
< job_state
) ||
3539 job
->id
< first_job_id
||
3540 (username
&& job
->username
&&
3541 strcasecmp(username
, job
->username
)))
3545 ippAddSeparator(client
->response
);
3548 copy_job_attributes(client
, job
, ra
);
3551 cupsArrayDelete(ra
);
3553 _cupsRWUnlock(&(client
->printer
->rwlock
));
3558 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3562 ipp_get_printer_attributes(
3563 _ipp_client_t
*client
) /* I - Client */
3565 cups_array_t
*ra
; /* Requested attributes array */
3566 _ipp_printer_t
*printer
; /* Printer */
3570 * Send the attributes...
3573 ra
= ippCreateRequestedArray(client
->request
);
3574 printer
= client
->printer
;
3576 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3578 _cupsRWLockRead(&(printer
->rwlock
));
3580 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3581 IPP_TAG_CUPS_CONST
);
3583 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3585 int i
, /* Looping var */
3586 num_ready
= 0; /* Number of ready media */
3587 ipp_t
*ready
[3]; /* Ready media */
3589 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3591 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
3592 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);
3594 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);
3596 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3597 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);
3598 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3600 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
3601 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);
3603 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);
3608 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3609 for (i
= 0; i
< num_ready
; i
++)
3610 ippDelete(ready
[i
]);
3613 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3616 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3618 int num_ready
= 0; /* Number of ready media */
3619 const char *ready
[3]; /* Ready media */
3621 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3622 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3624 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3625 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3627 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3628 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3631 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3633 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3636 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3637 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3639 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3640 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3642 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3643 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3646 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3647 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3648 "printer-state", printer
->state
);
3650 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3651 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3653 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3654 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3656 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3658 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3660 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3663 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3665 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3666 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3667 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3668 "printer-state-reasons", NULL
, "none");
3671 ipp_attribute_t
*attr
= NULL
; /* printer-state-reasons */
3672 _ipp_preason_t bit
; /* Reason bit */
3673 int i
; /* Looping var */
3674 char reason
[32]; /* Reason string */
3676 for (i
= 0, bit
= 1; i
< (int)(sizeof(_ipp_preason_strings
) / sizeof(_ipp_preason_strings
[0])); i
++, bit
*= 2)
3678 if (printer
->state_reasons
& bit
)
3680 snprintf(reason
, sizeof(reason
), "%s-%s", _ipp_preason_strings
[0], printer
->state
== IPP_PSTATE_IDLE
? "report" : printer
->state
== IPP_PSTATE_PROCESSING
? "warning" : "error");
3682 ippSetString(client
->response
, &attr
, ippGetCount(attr
), reason
);
3684 attr
= ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
, reason
);
3690 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3692 int i
; /* Looping var */
3693 char buffer
[256]; /* Supply value buffer */
3694 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3695 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3697 for (i
= 0; i
< 5; i
++)
3699 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
]);
3702 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3704 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3708 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3709 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3711 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3712 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3713 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3715 _cupsRWUnlock(&(printer
->rwlock
));
3717 cupsArrayDelete(ra
);
3722 * 'ipp_identify_printer()' - Beep or display a message.
3726 ipp_identify_printer(
3727 _ipp_client_t
*client
) /* I - Client */
3729 /* TODO: Do something */
3731 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3736 * 'ipp_print_job()' - Create a job object with an attached document.
3740 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3742 _ipp_job_t
*job
; /* New job */
3743 char filename
[1024], /* Filename buffer */
3744 buffer
[4096]; /* Copy buffer */
3745 ssize_t bytes
; /* Bytes read */
3746 cups_array_t
*ra
; /* Attributes to send in response */
3750 * Validate print job attributes...
3753 if (!valid_job_attributes(client
))
3755 httpFlush(client
->http
);
3760 * Do we have a file to print?
3763 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3765 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3773 if ((job
= create_job(client
)) == NULL
)
3775 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3776 "Currently printing another job.");
3781 * Create a file for the request data...
3784 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3787 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3789 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3791 job
->state
= IPP_JSTATE_ABORTED
;
3793 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3794 "Unable to create print file: %s", strerror(errno
));
3798 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3800 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3802 int error
= errno
; /* Write error */
3804 job
->state
= IPP_JSTATE_ABORTED
;
3811 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3812 "Unable to write print file: %s", strerror(error
));
3820 * Got an error while reading the print data, so abort this job.
3823 job
->state
= IPP_JSTATE_ABORTED
;
3830 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3831 "Unable to read print file.");
3837 int error
= errno
; /* Write error */
3839 job
->state
= IPP_JSTATE_ABORTED
;
3844 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3845 "Unable to write print file: %s", strerror(error
));
3850 job
->filename
= strdup(filename
);
3851 job
->state
= IPP_JSTATE_PENDING
;
3854 * Process the job...
3857 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3859 job
->state
= IPP_JSTATE_ABORTED
;
3860 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3865 * Return the job info...
3868 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3870 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3871 cupsArrayAdd(ra
, "job-id");
3872 cupsArrayAdd(ra
, "job-state");
3873 cupsArrayAdd(ra
, "job-state-message");
3874 cupsArrayAdd(ra
, "job-state-reasons");
3875 cupsArrayAdd(ra
, "job-uri");
3877 copy_job_attributes(client
, job
, ra
);
3878 cupsArrayDelete(ra
);
3883 * 'ipp_print_uri()' - Create a job object with a referenced document.
3887 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3889 _ipp_job_t
*job
; /* New job */
3890 ipp_attribute_t
*uri
; /* document-uri */
3891 char scheme
[256], /* URI scheme */
3892 userpass
[256], /* Username and password info */
3893 hostname
[256], /* Hostname */
3894 resource
[1024]; /* Resource path */
3895 int port
; /* Port number */
3896 http_uri_status_t uri_status
; /* URI decode status */
3897 http_encryption_t encryption
; /* Encryption to use, if any */
3898 http_t
*http
; /* Connection for http/https URIs */
3899 http_status_t status
; /* Access status for http/https URIs */
3900 int infile
; /* Input file for local file URIs */
3901 char filename
[1024], /* Filename buffer */
3902 buffer
[4096]; /* Copy buffer */
3903 ssize_t bytes
; /* Bytes read */
3904 cups_array_t
*ra
; /* Attributes to send in response */
3905 static const char * const uri_status_strings
[] =
3906 { /* URI decode errors */
3908 "Bad arguments to function.",
3909 "Bad resource in URI.",
3910 "Bad port number in URI.",
3911 "Bad hostname in URI.",
3912 "Bad username in URI.",
3913 "Bad scheme in URI.",
3919 * Validate print job attributes...
3922 if (!valid_job_attributes(client
))
3924 httpFlush(client
->http
);
3929 * Do we have a file to print?
3932 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3934 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3935 "Unexpected document data following request.");
3940 * Do we have a document URI?
3943 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3944 IPP_TAG_URI
)) == NULL
)
3946 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3950 if (ippGetCount(uri
) != 1)
3952 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3953 "Too many document-uri values.");
3957 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3958 scheme
, sizeof(scheme
), userpass
,
3959 sizeof(userpass
), hostname
, sizeof(hostname
),
3960 &port
, resource
, sizeof(resource
));
3961 if (uri_status
< HTTP_URI_STATUS_OK
)
3963 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3964 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3968 if (strcmp(scheme
, "file") &&
3970 strcmp(scheme
, "https") &&
3971 #endif /* HAVE_SSL */
3972 strcmp(scheme
, "http"))
3974 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3975 "URI scheme \"%s\" not supported.", scheme
);
3979 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3981 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3982 "Unable to access URI: %s", strerror(errno
));
3990 if ((job
= create_job(client
)) == NULL
)
3992 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3993 "Currently printing another job.");
3998 * Create a file for the request data...
4001 if (!strcasecmp(job
->format
, "image/jpeg"))
4002 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4003 client
->printer
->directory
, job
->id
);
4004 else if (!strcasecmp(job
->format
, "image/png"))
4005 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4006 client
->printer
->directory
, job
->id
);
4007 else if (!strcasecmp(job
->format
, "application/pdf"))
4008 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4009 client
->printer
->directory
, job
->id
);
4010 else if (!strcasecmp(job
->format
, "application/postscript"))
4011 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4012 client
->printer
->directory
, job
->id
);
4014 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4015 client
->printer
->directory
, job
->id
);
4017 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
4019 job
->state
= IPP_JSTATE_ABORTED
;
4021 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4022 "Unable to create print file: %s", strerror(errno
));
4026 if (!strcmp(scheme
, "file"))
4028 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4030 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4031 "Unable to access URI: %s", strerror(errno
));
4037 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4038 (errno
== EAGAIN
|| errno
== EINTR
))
4040 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4042 int error
= errno
; /* Write error */
4044 job
->state
= IPP_JSTATE_ABORTED
;
4052 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4053 "Unable to write print file: %s", strerror(error
));
4064 if (port
== 443 || !strcmp(scheme
, "https"))
4065 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4067 #endif /* HAVE_SSL */
4068 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4070 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4071 1, 30000, NULL
)) == NULL
)
4073 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4074 "Unable to connect to %s: %s", hostname
,
4075 cupsLastErrorString());
4076 job
->state
= IPP_JSTATE_ABORTED
;
4085 httpClearFields(http
);
4086 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4087 if (httpGet(http
, resource
))
4089 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4090 "Unable to GET URI: %s", strerror(errno
));
4092 job
->state
= IPP_JSTATE_ABORTED
;
4102 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4104 if (status
!= HTTP_STATUS_OK
)
4106 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4107 "Unable to GET URI: %s", httpStatus(status
));
4109 job
->state
= IPP_JSTATE_ABORTED
;
4119 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4121 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4123 int error
= errno
; /* Write error */
4125 job
->state
= IPP_JSTATE_ABORTED
;
4133 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4134 "Unable to write print file: %s", strerror(error
));
4144 int error
= errno
; /* Write error */
4146 job
->state
= IPP_JSTATE_ABORTED
;
4151 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4152 "Unable to write print file: %s", strerror(error
));
4157 job
->filename
= strdup(filename
);
4158 job
->state
= IPP_JSTATE_PENDING
;
4161 * Process the job...
4165 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4167 job
->state
= IPP_JSTATE_ABORTED
;
4168 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4177 * Return the job info...
4180 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4182 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4183 cupsArrayAdd(ra
, "job-id");
4184 cupsArrayAdd(ra
, "job-state");
4185 cupsArrayAdd(ra
, "job-state-reasons");
4186 cupsArrayAdd(ra
, "job-uri");
4188 copy_job_attributes(client
, job
, ra
);
4189 cupsArrayDelete(ra
);
4194 * 'ipp_send_document()' - Add an attached document to a job object created with
4199 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
4201 _ipp_job_t
*job
; /* Job information */
4202 char filename
[1024], /* Filename buffer */
4203 buffer
[4096]; /* Copy buffer */
4204 ssize_t bytes
; /* Bytes read */
4205 ipp_attribute_t
*attr
; /* Current attribute */
4206 cups_array_t
*ra
; /* Attributes to send in response */
4213 if ((job
= find_job(client
)) == NULL
)
4215 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4216 httpFlush(client
->http
);
4221 * See if we already have a document for this job or the job has already
4222 * in a non-pending state...
4225 if (job
->state
> IPP_JSTATE_HELD
)
4227 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4228 "Job is not in a pending state.");
4229 httpFlush(client
->http
);
4232 else if (job
->filename
|| job
->fd
>= 0)
4234 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4235 "Multiple document jobs are not supported.");
4236 httpFlush(client
->http
);
4240 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4241 IPP_TAG_ZERO
)) == NULL
)
4243 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4244 "Missing required last-document attribute.");
4245 httpFlush(client
->http
);
4248 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4249 !ippGetBoolean(attr
, 0))
4251 respond_unsupported(client
, attr
);
4252 httpFlush(client
->http
);
4257 * Validate document attributes...
4260 if (!valid_doc_attributes(client
))
4262 httpFlush(client
->http
);
4266 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
4269 * Get the document format for the job...
4272 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4274 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
4275 job
->format
= ippGetString(attr
, 0, NULL
);
4276 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
4277 job
->format
= ippGetString(attr
, 0, NULL
);
4279 job
->format
= "application/octet-stream";
4282 * Create a file for the request data...
4285 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
4288 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
4290 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4292 _cupsRWUnlock(&(client
->printer
->rwlock
));
4296 job
->state
= IPP_JSTATE_ABORTED
;
4298 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4299 "Unable to create print file: %s", strerror(errno
));
4303 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
4305 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4307 int error
= errno
; /* Write error */
4309 job
->state
= IPP_JSTATE_ABORTED
;
4316 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4317 "Unable to write print file: %s", strerror(error
));
4325 * Got an error while reading the print data, so abort this job.
4328 job
->state
= IPP_JSTATE_ABORTED
;
4335 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4336 "Unable to read print file.");
4342 int error
= errno
; /* Write error */
4344 job
->state
= IPP_JSTATE_ABORTED
;
4349 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4350 "Unable to write print file: %s", strerror(error
));
4354 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4357 job
->filename
= strdup(filename
);
4358 job
->state
= IPP_JSTATE_PENDING
;
4360 _cupsRWUnlock(&(client
->printer
->rwlock
));
4363 * Process the job...
4367 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4369 job
->state
= IPP_JSTATE_ABORTED
;
4370 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process 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...
4736 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4738 job
->state
= IPP_JSTATE_ABORTED
;
4739 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4748 * Return the job info...
4751 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4753 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4754 cupsArrayAdd(ra
, "job-id");
4755 cupsArrayAdd(ra
, "job-state");
4756 cupsArrayAdd(ra
, "job-state-reasons");
4757 cupsArrayAdd(ra
, "job-uri");
4759 copy_job_attributes(client
, job
, ra
);
4760 cupsArrayDelete(ra
);
4765 * 'ipp_validate_job()' - Validate job creation attributes.
4769 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4771 if (valid_job_attributes(client
))
4772 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4777 * 'load_attributes()' - Load printer attributes from a file.
4779 * Syntax is based on ipptool format:
4781 * ATTR value-tag name value
4785 load_attributes(const char *filename
, /* I - File to load */
4786 ipp_t
*attrs
) /* I - Printer attributes */
4788 int linenum
= 0; /* Current line number */
4789 FILE *fp
= NULL
; /* Test file */
4790 char attr
[128], /* Attribute name */
4791 token
[1024], /* Token from file */
4792 *tokenptr
; /* Pointer into token */
4793 ipp_tag_t value
; /* Current value type */
4794 ipp_attribute_t
*attrptr
, /* Attribute pointer */
4795 *lastcol
= NULL
; /* Last collection attribute */
4798 if ((fp
= fopen(filename
, "r")) == NULL
)
4800 fprintf(stderr
, "ippserver: Unable to open \"%s\": %s\n", filename
, strerror(errno
));
4804 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
4806 if (!_cups_strcasecmp(token
, "ATTR"))
4812 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4814 fprintf(stderr
, "ippserver: Missing ATTR value tag on line %d of \"%s\".\n", linenum
, filename
);
4818 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
4820 fprintf(stderr
, "ippserver: Bad ATTR value tag \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4824 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
4826 fprintf(stderr
, "ippserver: Missing ATTR name on line %d of \"%s\".\n", linenum
, filename
);
4830 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4832 fprintf(stderr
, "ippserver: Missing ATTR value on line %d of \"%s\".\n", linenum
, filename
);
4840 case IPP_TAG_BOOLEAN
:
4841 if (!_cups_strcasecmp(token
, "true"))
4842 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, 1);
4844 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, (char)atoi(token
));
4847 case IPP_TAG_INTEGER
:
4849 if (!strchr(token
, ','))
4850 attrptr
= ippAddInteger(attrs
, IPP_TAG_PRINTER
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
4853 int values
[100], /* Values */
4854 num_values
= 1; /* Number of values */
4856 values
[0] = (int)strtol(token
, &tokenptr
, 10);
4857 while (tokenptr
&& *tokenptr
&&
4858 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
4860 if (*tokenptr
== ',')
4862 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
4865 values
[num_values
] = (int)strtol(tokenptr
, &tokenptr
, 0);
4869 attrptr
= ippAddIntegers(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, values
);
4872 if (!tokenptr
|| *tokenptr
)
4874 fprintf(stderr
, "ippserver: Bad %s value \"%s\" on line %d of \"%s\".\n", ippTagString(value
), token
, linenum
, filename
);
4879 case IPP_TAG_RESOLUTION
:
4881 int xres
, /* X resolution */
4882 yres
; /* Y resolution */
4883 ipp_res_t units
; /* Units */
4884 char *start
, /* Start of value */
4885 *ptr
, /* Pointer into value */
4886 *next
= NULL
; /* Next value */
4888 for (start
= token
; start
; start
= next
)
4890 xres
= yres
= (int)strtol(start
, (char **)&ptr
, 10);
4891 if (ptr
> start
&& xres
> 0)
4894 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
4897 if (ptr
&& (next
= strchr(ptr
, ',')) != NULL
)
4900 if (ptr
<= start
|| xres
<= 0 || yres
<= 0 || !ptr
||
4901 (_cups_strcasecmp(ptr
, "dpi") &&
4902 _cups_strcasecmp(ptr
, "dpc") &&
4903 _cups_strcasecmp(ptr
, "dpcm") &&
4904 _cups_strcasecmp(ptr
, "other")))
4906 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4910 if (!_cups_strcasecmp(ptr
, "dpc") || !_cups_strcasecmp(ptr
, "dpcm"))
4911 units
= IPP_RES_PER_CM
;
4913 units
= IPP_RES_PER_INCH
;
4916 ippSetResolution(attrs
, &attrptr
, ippGetCount(attrptr
), units
, xres
, yres
);
4918 attrptr
= ippAddResolution(attrs
, IPP_TAG_PRINTER
, attr
, units
, xres
, yres
);
4923 case IPP_TAG_RANGE
:
4925 int lowers
[4], /* Lower value */
4926 uppers
[4], /* Upper values */
4927 num_vals
; /* Number of values */
4930 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
4931 lowers
+ 0, uppers
+ 0,
4932 lowers
+ 1, uppers
+ 1,
4933 lowers
+ 2, uppers
+ 2,
4934 lowers
+ 3, uppers
+ 3);
4936 if ((num_vals
& 1) || num_vals
== 0)
4938 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", token
, linenum
, filename
);
4942 attrptr
= ippAddRanges(attrs
, IPP_TAG_PRINTER
, attr
, num_vals
/ 2, lowers
,
4947 case IPP_TAG_BEGIN_COLLECTION
:
4948 if (!strcmp(token
, "{"))
4950 ipp_t
*col
= get_collection(fp
, filename
, &linenum
);
4951 /* Collection value */
4955 attrptr
= lastcol
= ippAddCollection(attrs
, IPP_TAG_PRINTER
, attr
, col
);
4963 fprintf(stderr
, "ippserver: Bad ATTR collection value on line %d of \"%s\".\n", linenum
, filename
);
4969 ipp_t
*col
; /* Collection value */
4970 long pos
= ftell(fp
); /* Save position of file */
4972 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4975 if (strcmp(token
, ","))
4977 fseek(fp
, pos
, SEEK_SET
);
4981 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
4983 fprintf(stderr
, "ippserver: Unexpected \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4987 if ((col
= get_collection(fp
, filename
, &linenum
)) == NULL
)
4990 ippSetCollection(attrs
, &attrptr
, ippGetCount(attrptr
), col
);
4993 while (!strcmp(token
, "{"));
4996 case IPP_TAG_STRING
:
4997 attrptr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, attr
, token
, (int)strlen(token
));
5001 fprintf(stderr
, "ippserver: Unsupported ATTR value tag %s on line %d of \"%s\".\n", ippTagString(value
), linenum
, filename
);
5004 case IPP_TAG_TEXTLANG
:
5005 case IPP_TAG_NAMELANG
:
5008 case IPP_TAG_KEYWORD
:
5010 case IPP_TAG_URISCHEME
:
5011 case IPP_TAG_CHARSET
:
5012 case IPP_TAG_LANGUAGE
:
5013 case IPP_TAG_MIMETYPE
:
5014 if (!strchr(token
, ','))
5015 attrptr
= ippAddString(attrs
, IPP_TAG_PRINTER
, value
, attr
, NULL
, token
);
5019 * Multiple string values...
5022 int num_values
; /* Number of values */
5023 char *values
[100], /* Values */
5024 *ptr
; /* Pointer to next value */
5030 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
5032 if (ptr
> token
&& ptr
[-1] == '\\')
5033 _cups_strcpy(ptr
- 1, ptr
);
5037 values
[num_values
] = ptr
;
5039 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
5044 attrptr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, NULL
, (const char **)values
);
5051 fprintf(stderr
, "ippserver: Unable to add attribute on line %d of \"%s\": %s\n", linenum
, filename
, cupsLastErrorString());
5057 fprintf(stderr
, "ippserver: Unknown directive \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
5067 * 'parse_options()' - Parse URL options into CUPS options.
5069 * The client->options string is destroyed by this function.
5072 static int /* O - Number of options */
5073 parse_options(_ipp_client_t
*client
, /* I - Client */
5074 cups_option_t
**options
) /* O - Options */
5076 char *name
, /* Name */
5078 *next
; /* Next name=value pair */
5079 int num_options
= 0; /* Number of options */
5084 for (name
= client
->options
; name
&& *name
; name
= next
)
5086 if ((value
= strchr(name
, '=')) == NULL
)
5090 if ((next
= strchr(value
, '&')) != NULL
)
5093 num_options
= cupsAddOption(name
, value
, num_options
, options
);
5096 return (num_options
);
5101 * 'process_attr_message()' - Process an ATTR: message from a command.
5105 process_attr_message(
5106 _ipp_job_t
*job
, /* I - Job */
5107 char *message
) /* I - Message */
5115 * 'process_client()' - Process client requests on a thread.
5118 static void * /* O - Exit status */
5119 process_client(_ipp_client_t
*client
) /* I - Client */
5122 * Loop until we are out of requests or timeout (30 seconds)...
5126 int first_time
= 1; /* First time request? */
5127 #endif /* HAVE_SSL */
5129 while (httpWait(client
->http
, 30000))
5135 * See if we need to negotiate a TLS connection...
5138 char buf
[1]; /* First byte from client */
5140 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
5142 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
5144 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
5146 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5150 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5155 #endif /* HAVE_SSL */
5157 if (!process_http(client
))
5162 * Close the conection to the client and return...
5165 delete_client(client
);
5172 * 'process_http()' - Process a HTTP request.
5175 int /* O - 1 on success, 0 on failure */
5176 process_http(_ipp_client_t
*client
) /* I - Client connection */
5178 char uri
[1024]; /* URI */
5179 http_state_t http_state
; /* HTTP state */
5180 http_status_t http_status
; /* HTTP status */
5181 ipp_state_t ipp_state
; /* State of IPP transfer */
5182 char scheme
[32], /* Method/scheme */
5183 userpass
[128], /* Username:password */
5184 hostname
[HTTP_MAX_HOST
];
5186 int port
; /* Port number */
5187 const char *encoding
; /* Content-Encoding value */
5188 static const char * const http_states
[] =
5189 { /* Strings for logging HTTP method */
5210 * Clear state variables...
5213 ippDelete(client
->request
);
5214 ippDelete(client
->response
);
5216 client
->request
= NULL
;
5217 client
->response
= NULL
;
5218 client
->operation
= HTTP_STATE_WAITING
;
5221 * Read a request from the connection...
5224 while ((http_state
= httpReadRequest(client
->http
, uri
,
5225 sizeof(uri
))) == HTTP_STATE_WAITING
)
5229 * Parse the request line...
5232 if (http_state
== HTTP_STATE_ERROR
)
5234 if (httpError(client
->http
) == EPIPE
)
5235 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
5237 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
5238 strerror(httpError(client
->http
)));
5242 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
5244 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
5245 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5248 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
5250 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
5251 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5255 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
5259 * Separate the URI into its components...
5262 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
5263 userpass
, sizeof(userpass
),
5264 hostname
, sizeof(hostname
), &port
,
5265 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
5266 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
5268 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
5269 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5273 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
5274 *(client
->options
)++ = '\0';
5277 * Process the request...
5280 client
->start
= time(NULL
);
5281 client
->operation
= httpGetState(client
->http
);
5284 * Parse incoming parameters until the status changes...
5287 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
5289 if (http_status
!= HTTP_STATUS_OK
)
5291 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5295 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
5296 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
5299 * HTTP/1.1 and higher require the "Host:" field...
5302 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5307 * Handle HTTP Upgrade...
5310 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
5314 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
5316 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
5319 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
5321 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
5323 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5327 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5330 #endif /* HAVE_SSL */
5332 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
5337 * Handle HTTP Expect...
5340 if (httpGetExpect(client
->http
) &&
5341 (client
->operation
== HTTP_STATE_POST
||
5342 client
->operation
== HTTP_STATE_PUT
))
5344 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
5347 * Send 100-continue header...
5350 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
5356 * Send 417-expectation-failed header...
5359 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
5365 * Handle new transfers...
5368 encoding
= httpGetContentEncoding(client
->http
);
5370 switch (client
->operation
)
5372 case HTTP_STATE_OPTIONS
:
5374 * Do OPTIONS command...
5377 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
5379 case HTTP_STATE_HEAD
:
5380 if (!strcmp(client
->uri
, "/icon.png"))
5381 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
5382 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
5383 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
5385 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5387 case HTTP_STATE_GET
:
5388 if (!strcmp(client
->uri
, "/icon.png"))
5391 * Send PNG icon file.
5394 int fd
; /* Icon file */
5395 struct stat fileinfo
; /* Icon file information */
5396 char buffer
[4096]; /* Copy buffer */
5397 ssize_t bytes
; /* Bytes */
5399 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5401 if (!stat(client
->printer
->icon
, &fileinfo
) &&
5402 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5404 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
5405 (size_t)fileinfo
.st_size
))
5411 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5412 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5414 httpFlushWrite(client
->http
);
5419 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5421 else if (!strcmp(client
->uri
, "/"))
5424 * Show web status page...
5427 _ipp_job_t
*job
; /* Current job */
5428 int i
; /* Looping var */
5429 _ipp_preason_t reason
; /* Current reason */
5430 static const char * const reasons
[] =
5431 { /* Reason strings */
5434 "Input Tray Missing",
5435 "Marker Supply Empty",
5436 "Marker Supply Low",
5437 "Marker Waste Almost Full",
5438 "Marker Waste Full",
5450 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5453 html_header(client
, client
->printer
->name
);
5455 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
5456 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
5457 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
5458 if (client
->printer
->state_reasons
& reason
)
5459 html_printf(client
, "\n<br> %s", reasons
[i
]);
5460 html_printf(client
, "</p>\n");
5462 if (cupsArrayCount(client
->printer
->jobs
) > 0)
5464 _cupsRWLockRead(&(client
->printer
->rwlock
));
5466 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");
5467 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
5469 char when
[256], /* When job queued/started/finished */
5470 hhmmss
[64]; /* Time HH:MM:SS */
5474 case IPP_JSTATE_PENDING
:
5475 case IPP_JSTATE_HELD
:
5476 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
5478 case IPP_JSTATE_PROCESSING
:
5479 case IPP_JSTATE_STOPPED
:
5480 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
5482 case IPP_JSTATE_ABORTED
:
5483 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5485 case IPP_JSTATE_CANCELED
:
5486 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5488 case IPP_JSTATE_COMPLETED
:
5489 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5493 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
);
5495 html_printf(client
, "</tbody></table>\n");
5497 _cupsRWUnlock(&(client
->printer
->rwlock
));
5499 html_footer(client
);
5503 else if (!strcmp(client
->uri
, "/media"))
5506 * Show web media page...
5509 int i
, /* Looping var */
5510 num_options
; /* Number of form options */
5511 cups_option_t
*options
; /* Form options */
5512 static const char * const sizes
[] =
5513 { /* Size strings */
5526 static const char * const types
[] =
5543 static const int sheets
[] = /* Number of sheets */
5552 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5555 html_header(client
, client
->printer
->name
);
5557 if ((num_options
= parse_options(client
, &options
)) > 0)
5560 * WARNING: A real printer/server implementation MUST NOT implement
5561 * media updates via a GET request - GET requests are supposed to be
5562 * idempotent (without side-effects) and we obviously are not
5563 * authenticating access here. This form is provided solely to
5564 * enable testing and development!
5567 const char *val
; /* Form value */
5569 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
5570 client
->printer
->main_size
= atoi(val
);
5571 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
5572 client
->printer
->main_type
= atoi(val
);
5573 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
5574 client
->printer
->main_level
= atoi(val
);
5576 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
5577 client
->printer
->envelope_size
= atoi(val
);
5578 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
5579 client
->printer
->envelope_level
= atoi(val
);
5581 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
5582 client
->printer
->photo_size
= atoi(val
);
5583 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
5584 client
->printer
->photo_type
= atoi(val
);
5585 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
5586 client
->printer
->photo_level
= atoi(val
);
5588 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))
5589 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
5591 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
5593 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
))
5595 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
5596 if (client
->printer
->active_job
)
5597 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5600 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
5602 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
5605 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
5607 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
5608 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
5609 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5610 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
5611 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
5612 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
5613 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5614 if (!strstr(types
[i
], "Photo"))
5615 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
5616 html_printf(client
, "</select> <select name=\"main_level\">");
5617 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5618 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
5619 html_printf(client
, "</select></td></tr>\n");
5622 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
5623 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5624 if (strstr(sizes
[i
], "Envelope"))
5625 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
5626 html_printf(client
, "</select> <select name=\"envelope_level\">");
5627 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5628 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
5629 html_printf(client
, "</select></td></tr>\n");
5632 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
5633 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5634 if (strstr(sizes
[i
], "Photo"))
5635 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
5636 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
5637 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5638 if (strstr(types
[i
], "Photo"))
5639 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
5640 html_printf(client
, "</select> <select name=\"photo_level\">");
5641 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5642 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
5643 html_printf(client
, "</select></td></tr>\n");
5645 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5646 html_footer(client
);
5650 else if (!strcmp(client
->uri
, "/supplies"))
5653 * Show web supplies page...
5656 int i
, j
, /* Looping vars */
5657 num_options
; /* Number of form options */
5658 cups_option_t
*options
; /* Form options */
5659 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5661 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5664 html_header(client
, client
->printer
->name
);
5666 if ((num_options
= parse_options(client
, &options
)) > 0)
5669 * WARNING: A real printer/server implementation MUST NOT implement
5670 * supply updates via a GET request - GET requests are supposed to be
5671 * idempotent (without side-effects) and we obviously are not
5672 * authenticating access here. This form is provided solely to
5673 * enable testing and development!
5676 char name
[64]; /* Form field */
5677 const char *val
; /* Form value */
5679 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
);
5681 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5683 snprintf(name
, sizeof(name
), "supply_%d", i
);
5684 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5686 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5692 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
5693 else if (level
< 10)
5694 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
5699 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
5700 else if (level
> 90)
5701 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
5706 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5709 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5711 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5712 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5714 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5715 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5716 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5717 html_printf(client
, "</select></td></tr>\n");
5719 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5720 html_footer(client
);
5725 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5728 case HTTP_STATE_POST
:
5729 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5733 * Not an IPP request...
5736 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5740 * Read the IPP request...
5743 client
->request
= ippNew();
5745 while ((ipp_state
= ippRead(client
->http
,
5746 client
->request
)) != IPP_STATE_DATA
)
5748 if (ipp_state
== IPP_STATE_ERROR
)
5750 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5751 cupsLastErrorString());
5752 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5758 * Now that we have the IPP request, process the request...
5761 return (process_ipp(client
));
5764 break; /* Anti-compiler-warning-code */
5772 * 'process_ipp()' - Process an IPP request.
5775 static int /* O - 1 on success, 0 on error */
5776 process_ipp(_ipp_client_t
*client
) /* I - Client */
5778 ipp_tag_t group
; /* Current group tag */
5779 ipp_attribute_t
*attr
; /* Current attribute */
5780 ipp_attribute_t
*charset
; /* Character set attribute */
5781 ipp_attribute_t
*language
; /* Language attribute */
5782 ipp_attribute_t
*uri
; /* Printer URI attribute */
5783 int major
, minor
; /* Version number */
5784 const char *name
; /* Name of attribute */
5787 debug_attributes("Request", client
->request
, 1);
5790 * First build an empty response message for this request...
5793 client
->operation_id
= ippGetOperation(client
->request
);
5794 client
->response
= ippNewResponse(client
->request
);
5797 * Then validate the request header and required attributes...
5800 major
= ippGetVersion(client
->request
, &minor
);
5802 if (major
< 1 || major
> 2)
5805 * Return an error, since we only support IPP 1.x and 2.x.
5808 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
5809 "Bad request version number %d.%d.", major
, minor
);
5811 else if (ippGetRequestId(client
->request
) <= 0)
5812 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
5813 ippGetRequestId(client
->request
));
5814 else if (!ippFirstAttribute(client
->request
))
5815 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5816 "No attributes in request.");
5820 * Make sure that the attributes are provided in the correct order and
5821 * don't repeat groups...
5824 for (attr
= ippFirstAttribute(client
->request
),
5825 group
= ippGetGroupTag(attr
);
5827 attr
= ippNextAttribute(client
->request
))
5829 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5832 * Out of order; return an error...
5835 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5836 "Attribute groups are out of order (%x < %x).",
5837 ippGetGroupTag(attr
), group
);
5841 group
= ippGetGroupTag(attr
);
5847 * Then make sure that the first three attributes are:
5849 * attributes-charset
5850 * attributes-natural-language
5851 * printer-uri/job-uri
5854 attr
= ippFirstAttribute(client
->request
);
5855 name
= ippGetName(attr
);
5856 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5857 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5862 attr
= ippNextAttribute(client
->request
);
5863 name
= ippGetName(attr
);
5865 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5866 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5871 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5872 IPP_TAG_URI
)) != NULL
)
5874 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5875 IPP_TAG_URI
)) != NULL
)
5881 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5882 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5885 * Bad character set...
5888 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5889 "Unsupported character set \"%s\".",
5890 ippGetString(charset
, 0, NULL
));
5892 else if (!charset
|| !language
|| !uri
)
5895 * Return an error, since attributes-charset,
5896 * attributes-natural-language, and printer-uri/job-uri are required
5897 * for all operations.
5900 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5901 "Missing required attributes.");
5905 char scheme
[32], /* URI scheme */
5906 userpass
[32], /* Username/password in URI */
5907 host
[256], /* Host name in URI */
5908 resource
[256]; /* Resource path in URI */
5909 int port
; /* Port number in URI */
5911 name
= ippGetName(uri
);
5913 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5914 scheme
, sizeof(scheme
),
5915 userpass
, sizeof(userpass
),
5916 host
, sizeof(host
), &port
,
5917 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5918 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5919 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5920 else if ((!strcmp(name
, "job-uri") &&
5921 strncmp(resource
, "/ipp/print/", 11)) ||
5922 (!strcmp(name
, "printer-uri") &&
5923 strcmp(resource
, "/ipp/print")))
5924 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5925 name
, ippGetString(uri
, 0, NULL
));
5929 * Try processing the operation...
5932 switch (ippGetOperation(client
->request
))
5934 case IPP_OP_PRINT_JOB
:
5935 ipp_print_job(client
);
5938 case IPP_OP_PRINT_URI
:
5939 ipp_print_uri(client
);
5942 case IPP_OP_VALIDATE_JOB
:
5943 ipp_validate_job(client
);
5946 case IPP_OP_CREATE_JOB
:
5947 ipp_create_job(client
);
5950 case IPP_OP_SEND_DOCUMENT
:
5951 ipp_send_document(client
);
5954 case IPP_OP_SEND_URI
:
5955 ipp_send_uri(client
);
5958 case IPP_OP_CANCEL_JOB
:
5959 ipp_cancel_job(client
);
5962 case IPP_OP_GET_JOB_ATTRIBUTES
:
5963 ipp_get_job_attributes(client
);
5966 case IPP_OP_GET_JOBS
:
5967 ipp_get_jobs(client
);
5970 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5971 ipp_get_printer_attributes(client
);
5974 case IPP_OP_CLOSE_JOB
:
5975 ipp_close_job(client
);
5978 case IPP_OP_IDENTIFY_PRINTER
:
5979 ipp_identify_printer(client
);
5983 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5984 "Operation not supported.");
5993 * Send the HTTP header and return...
5996 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5997 httpFlush(client
->http
); /* Flush trailing (junk) data */
5999 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
6000 ippLength(client
->response
)));
6005 * 'process_job()' - Process a print job.
6008 static void * /* O - Thread exit status */
6009 process_job(_ipp_job_t
*job
) /* I - Job */
6011 job
->state
= IPP_JSTATE_PROCESSING
;
6012 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
6013 job
->processing
= time(NULL
);
6015 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
6017 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
6022 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
6024 if (job
->printer
->command
)
6027 * Execute a command with the job spool file and wait for it to complete...
6030 int pid
, /* Process ID */
6031 status
; /* Exit status */
6032 time_t start
, /* Start time */
6034 char *myargv
[3], /* Command-line arguments */
6035 *myenvp
[200]; /* Environment variables */
6036 int myenvc
; /* Number of environment variables */
6037 ipp_attribute_t
*attr
; /* Job attribute */
6038 char val
[1280], /* IPP_NAME=value */
6039 *valptr
; /* Pointer into string */
6041 int mypipe
[2]; /* Pipe for stderr */
6042 char line
[2048], /* Line from stderr */
6043 *ptr
, /* Pointer into line */
6044 *endptr
; /* End of line */
6045 ssize_t bytes
; /* Bytes read */
6048 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
6053 * Setup the command-line arguments...
6056 myargv
[0] = job
->printer
->command
;
6057 myargv
[1] = job
->filename
;
6061 * Copy the current environment, then add ENV variables for every Job
6065 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
6066 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
6068 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
6071 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6072 * value(s) from the attribute.
6075 const char *name
= ippGetName(attr
);
6084 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6089 *valptr
++ = (char)toupper(*name
& 255);
6094 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6096 myenvp
[myenvc
++] = strdup(val
);
6098 myenvp
[myenvc
] = NULL
;
6101 * Now run the program...
6105 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
6110 perror("Unable to create pipe for stderr");
6111 mypipe
[0] = mypipe
[1] = -1;
6114 if ((pid
= fork()) == 0)
6117 * Child comes here...
6125 execve(job
->printer
->command
, myargv
, myenvp
);
6131 * Unable to fork process...
6134 perror("Unable to start job processing command");
6141 * Free memory used for environment...
6145 free(myenvp
[-- myenvc
]);
6150 * Free memory used for environment...
6154 free(myenvp
[-- myenvc
]);
6157 * If the pipe exists, read from it until EOF...
6165 while ((bytes
= read(mypipe
[0], endptr
, sizeof(line
) - (size_t)(endptr
- line
) - 1)) > 0)
6170 while ((ptr
= strchr(line
, '\n')) != NULL
)
6174 if (!strncmp(line
, "STATE:", 6))
6177 * Process printer-state-reasons keywords.
6180 process_state_message(job
, line
);
6182 else if (!strncmp(line
, "ATTR:", 5))
6185 * Process printer attribute update.
6188 process_attr_message(job
, line
);
6190 else if (Verbosity
> 1)
6191 fprintf(stderr
, "%s: %s\n", job
->printer
->command
, line
);
6195 memmove(line
, ptr
, endptr
- ptr
);
6205 * Wait for child to complete...
6208 # ifdef HAVE_WAITPID
6209 while (waitpid(pid
, &status
, 0) < 0);
6211 while (wait(&status
) < 0);
6212 # endif /* HAVE_WAITPID */
6219 if (WIFEXITED(status
))
6221 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
6222 job
->printer
->command
, WEXITSTATUS(status
));
6225 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
6226 job
->printer
->command
, WTERMSIG(status
));
6228 job
->state
= IPP_JSTATE_ABORTED
;
6230 else if (status
< 0)
6231 job
->state
= IPP_JSTATE_ABORTED
;
6233 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
6234 job
->printer
->command
);
6237 * Make sure processing takes at least 5 seconds...
6241 if ((end
- start
) < 5)
6247 * Sleep for a random amount of time to simulate job processing.
6250 sleep((unsigned)(5 + (rand() % 11)));
6254 job
->state
= IPP_JSTATE_CANCELED
;
6255 else if (job
->state
== IPP_JSTATE_PROCESSING
)
6256 job
->state
= IPP_JSTATE_COMPLETED
;
6258 job
->completed
= time(NULL
);
6259 job
->printer
->state
= IPP_PSTATE_IDLE
;
6260 job
->printer
->active_job
= NULL
;
6267 * 'process_state_message()' - Process a STATE: message from a command.
6271 process_state_message(
6272 _ipp_job_t
*job
, /* I - Job */
6273 char *message
) /* I - Message */
6275 int i
; /* Looping var */
6276 _ipp_preason_t state_reasons
, /* printer-state-reasons values */
6277 bit
; /* Current reason bit */
6278 char *ptr
, /* Pointer into message */
6279 *next
; /* Next keyword in message */
6280 int remove
; /* Non-zero if we are removing keywords */
6284 * Skip leading "STATE:" and any whitespace...
6287 for (message
+= 6; *message
; message
++)
6288 if (*message
!= ' ' && *message
!= '\t')
6292 * Support the following forms of message:
6294 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6296 * "-keyword[,keyword,...]" to remove keywords.
6298 * "+keyword[,keyword,...]" to add keywords.
6300 * Keywords may or may not have a suffix (-report, -warning, -error) per
6304 if (*message
== '-')
6307 state_reasons
= job
->printer
->state_reasons
;
6310 else if (*message
== '+')
6313 state_reasons
= job
->printer
->state_reasons
;
6319 state_reasons
= _IPP_PREASON_NONE
;
6324 if ((next
= strchr(message
, ',')) != NULL
)
6327 if ((ptr
= strstr(message
, "-error")) != NULL
)
6329 else if ((ptr
= strstr(message
, "-report")) != NULL
)
6331 else if ((ptr
= strstr(message
, "-warning")) != NULL
)
6334 for (i
= 0, bit
= 1; i
< (int)(sizeof(_ipp_preason_strings
) / sizeof(_ipp_preason_strings
[0])); i
++, bit
*= 2)
6336 if (!strcmp(message
, _ipp_preason_strings
[i
]))
6339 state_reasons
&= ~bit
;
6341 state_reasons
|= bit
;
6351 job
->printer
->state_reasons
= state_reasons
;
6356 * 'register_printer()' - Register a printer object via Bonjour.
6359 static int /* O - 1 on success, 0 on error */
6361 _ipp_printer_t
*printer
, /* I - Printer */
6362 const char *location
, /* I - Location */
6363 const char *make
, /* I - Manufacturer */
6364 const char *model
, /* I - Model name */
6365 const char *formats
, /* I - Supported formats */
6366 const char *adminurl
, /* I - Web interface URL */
6367 const char *uuid
, /* I - Printer UUID */
6368 int color
, /* I - 1 = color, 0 = monochrome */
6369 int duplex
, /* I - 1 = duplex, 0 = simplex */
6370 const char *subtype
) /* I - Service subtype */
6372 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6373 _ipp_txt_t ipp_txt
; /* Bonjour IPP TXT record */
6374 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6376 DNSServiceErrorType error
; /* Error from Bonjour */
6377 char make_model
[256],/* Make and model together */
6378 product
[256], /* Product string */
6379 regtype
[256]; /* Bonjour service type */
6383 * Build the TXT record for IPP...
6386 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
6387 snprintf(product
, sizeof(product
), "(%s)", model
);
6389 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
6390 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
6391 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(make_model
),
6393 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(adminurl
),
6396 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(location
),
6398 TXTRecordSetValue(&ipp_txt
, "product", (uint8_t)strlen(product
),
6400 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
),
6402 TXTRecordSetValue(&ipp_txt
, "Color", 1, color
? "T" : "F");
6403 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, duplex
? "T" : "F");
6404 TXTRecordSetValue(&ipp_txt
, "usb_MFG", (uint8_t)strlen(make
),
6406 TXTRecordSetValue(&ipp_txt
, "usb_MDL", (uint8_t)strlen(model
),
6408 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(uuid
), uuid
);
6410 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
6411 # endif /* HAVE_SSL */
6412 if (strstr(formats
, "image/urf"))
6413 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");
6415 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
6416 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
6419 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6420 * defend our service name but not actually support LPD...
6423 printer
->printer_ref
= DNSSDMaster
;
6425 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
6426 kDNSServiceFlagsShareConnection
,
6427 0 /* interfaceIndex */, printer
->dnssd_name
,
6428 "_printer._tcp", NULL
/* domain */,
6429 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
6430 NULL
/* txtRecord */,
6431 (DNSServiceRegisterReply
)dnssd_callback
,
6432 printer
)) != kDNSServiceErr_NoError
)
6434 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
6435 printer
->dnssd_name
, error
);
6440 * Then register the _ipp._tcp (IPP) service type with the real port number to
6441 * advertise our IPP printer...
6444 printer
->ipp_ref
= DNSSDMaster
;
6446 if (subtype
&& *subtype
)
6447 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
6449 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
6451 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
6452 kDNSServiceFlagsShareConnection
,
6453 0 /* interfaceIndex */, printer
->dnssd_name
,
6454 regtype
, NULL
/* domain */,
6455 NULL
/* host */, htons(printer
->port
),
6456 TXTRecordGetLength(&ipp_txt
),
6457 TXTRecordGetBytesPtr(&ipp_txt
),
6458 (DNSServiceRegisterReply
)dnssd_callback
,
6459 printer
)) != kDNSServiceErr_NoError
)
6461 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6462 printer
->dnssd_name
, regtype
, error
);
6468 * Then register the _ipps._tcp (IPP) service type with the real port number to
6469 * advertise our IPPS printer...
6472 printer
->ipps_ref
= DNSSDMaster
;
6474 if (subtype
&& *subtype
)
6475 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
6477 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
6479 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
6480 kDNSServiceFlagsShareConnection
,
6481 0 /* interfaceIndex */, printer
->dnssd_name
,
6482 regtype
, NULL
/* domain */,
6483 NULL
/* host */, htons(printer
->port
),
6484 TXTRecordGetLength(&ipp_txt
),
6485 TXTRecordGetBytesPtr(&ipp_txt
),
6486 (DNSServiceRegisterReply
)dnssd_callback
,
6487 printer
)) != kDNSServiceErr_NoError
)
6489 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6490 printer
->dnssd_name
, regtype
, error
);
6493 # endif /* HAVE_SSL */
6496 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6497 * real port number to advertise our IPP printer...
6500 printer
->http_ref
= DNSSDMaster
;
6502 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
6503 kDNSServiceFlagsShareConnection
,
6504 0 /* interfaceIndex */, printer
->dnssd_name
,
6505 "_http._tcp,_printer", NULL
/* domain */,
6506 NULL
/* host */, htons(printer
->port
),
6507 0 /* txtLen */, NULL
, /* txtRecord */
6508 (DNSServiceRegisterReply
)dnssd_callback
,
6509 printer
)) != kDNSServiceErr_NoError
)
6511 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6512 printer
->dnssd_name
, regtype
, error
);
6516 TXTRecordDeallocate(&ipp_txt
);
6518 #elif defined(HAVE_AVAHI)
6519 char temp
[256]; /* Subtype service string */
6522 * Create the TXT record...
6526 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
6527 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s %s", make
, model
);
6528 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", adminurl
);
6530 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", location
);
6531 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "product=(%s)", model
);
6532 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
6533 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", color
? "T" : "F");
6534 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", duplex
? "T" : "F");
6535 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MFG=%s", make
);
6536 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MDL=%s", model
);
6537 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", uuid
);
6539 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
6540 # endif /* HAVE_SSL */
6543 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6546 avahi_threaded_poll_lock(DNSSDMaster
);
6548 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
6550 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
);
6553 * Then register the _ipp._tcp (IPP)...
6556 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
);
6557 if (subtype
&& *subtype
)
6559 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
6560 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
6565 * _ipps._tcp (IPPS) for secure printing...
6568 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
);
6569 if (subtype
&& *subtype
)
6571 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
6572 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
6574 #endif /* HAVE_SSL */
6577 * Finally _http.tcp (HTTP) for the web interface...
6580 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
);
6581 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");
6587 avahi_entry_group_commit(printer
->ipp_ref
);
6588 avahi_threaded_poll_unlock(DNSSDMaster
);
6590 avahi_string_list_free(ipp_txt
);
6591 #endif /* HAVE_DNSSD */
6598 * 'respond_http()' - Send a HTTP response.
6601 int /* O - 1 on success, 0 on failure */
6603 _ipp_client_t
*client
, /* I - Client */
6604 http_status_t code
, /* I - HTTP status of response */
6605 const char *content_encoding
, /* I - Content-Encoding of response */
6606 const char *type
, /* I - MIME media type of response */
6607 size_t length
) /* I - Length of response */
6609 char message
[1024]; /* Text message */
6612 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
6614 if (code
== HTTP_STATUS_CONTINUE
)
6617 * 100-continue doesn't send any headers...
6620 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
6624 * Format an error message...
6627 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
6629 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
6631 type
= "text/plain";
6632 length
= strlen(message
);
6638 * Send the HTTP response header...
6641 httpClearFields(client
->http
);
6643 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
6644 client
->operation
== HTTP_STATE_OPTIONS
)
6645 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
6649 if (!strcmp(type
, "text/html"))
6650 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
6651 "text/html; charset=utf-8");
6653 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
6655 if (content_encoding
)
6656 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
6659 httpSetLength(client
->http
, length
);
6661 if (httpWriteResponse(client
->http
, code
) < 0)
6665 * Send the response data...
6671 * Send a plain text message.
6674 if (httpPrintf(client
->http
, "%s", message
) < 0)
6677 if (httpWrite2(client
->http
, "", 0) < 0)
6680 else if (client
->response
)
6683 * Send an IPP response...
6686 debug_attributes("Response", client
->response
, 2);
6688 ippSetState(client
->response
, IPP_STATE_IDLE
);
6690 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6699 * 'respond_ipp()' - Send an IPP response.
6703 respond_ipp(_ipp_client_t
*client
, /* I - Client */
6704 ipp_status_t status
, /* I - status-code */
6705 const char *message
, /* I - printf-style status-message */
6706 ...) /* I - Additional args as needed */
6708 const char *formatted
= NULL
; /* Formatted message */
6711 ippSetStatusCode(client
->response
, status
);
6715 va_list ap
; /* Pointer to additional args */
6716 ipp_attribute_t
*attr
; /* New status-message attribute */
6718 va_start(ap
, message
);
6719 if ((attr
= ippFindAttribute(client
->response
, "status-message",
6720 IPP_TAG_TEXT
)) != NULL
)
6721 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6723 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
6724 "status-message", NULL
, message
, ap
);
6727 formatted
= ippGetString(attr
, 0, NULL
);
6731 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
6732 ippOpString(client
->operation_id
), ippErrorString(status
),
6735 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
6736 ippOpString(client
->operation_id
), ippErrorString(status
));
6741 * 'respond_unsupported()' - Respond with an unsupported attribute.
6745 respond_unsupported(
6746 _ipp_client_t
*client
, /* I - Client */
6747 ipp_attribute_t
*attr
) /* I - Atribute */
6749 ipp_attribute_t
*temp
; /* Copy of attribute */
6752 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6753 "Unsupported %s %s%s value.", ippGetName(attr
),
6754 ippGetCount(attr
) > 1 ? "1setOf " : "",
6755 ippTagString(ippGetValueTag(attr
)));
6757 temp
= ippCopyAttribute(client
->response
, attr
, 0);
6758 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
6763 * 'run_printer()' - Run the printer service.
6767 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
6769 int num_fds
; /* Number of file descriptors */
6770 struct pollfd polldata
[3]; /* poll() data */
6771 int timeout
; /* Timeout for poll() */
6772 _ipp_client_t
*client
; /* New client */
6776 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6779 polldata
[0].fd
= printer
->ipv4
;
6780 polldata
[0].events
= POLLIN
;
6782 polldata
[1].fd
= printer
->ipv6
;
6783 polldata
[1].events
= POLLIN
;
6788 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
6789 polldata
[num_fds
++].events
= POLLIN
;
6790 #endif /* HAVE_DNSSD */
6793 * Loop until we are killed or have a hard error...
6798 if (cupsArrayCount(printer
->jobs
))
6803 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
6805 perror("poll() failed");
6809 if (polldata
[0].revents
& POLLIN
)
6811 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6813 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
6815 perror("Unable to create client thread");
6816 delete_client(client
);
6821 if (polldata
[1].revents
& POLLIN
)
6823 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6825 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
6827 perror("Unable to create client thread");
6828 delete_client(client
);
6834 if (polldata
[2].revents
& POLLIN
)
6835 DNSServiceProcessResult(DNSSDMaster
);
6836 #endif /* HAVE_DNSSD */
6839 * Clean out old jobs...
6842 clean_jobs(printer
);
6848 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6852 time_string(time_t tv
, /* I - Time value */
6853 char *buffer
, /* I - Buffer */
6854 size_t bufsize
) /* I - Size of buffer */
6856 struct tm
*curtime
= localtime(&tv
);
6859 strftime(buffer
, bufsize
, "%X", curtime
);
6865 * 'usage()' - Show program usage.
6869 usage(int status
) /* O - Exit status */
6873 puts(CUPS_SVERSION
" - Copyright 2010-2015 by Apple Inc. All rights "
6878 puts("Usage: ippserver [options] \"name\"");
6881 puts("-2 Supports 2-sided printing (default=1-sided)");
6882 puts("-M manufacturer Manufacturer name (default=Test)");
6883 puts("-P PIN printing mode");
6884 puts("-a attributes-file Load printer attributes from file");
6885 puts("-c command Run command for every print job");
6886 printf("-d spool-directory Spool directory "
6887 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6888 puts("-f type/subtype[,...] List of supported types "
6889 "(default=application/pdf,image/jpeg)");
6890 puts("-h Show program help");
6891 puts("-i iconfile.png PNG icon file (default=printer.png)");
6892 puts("-k Keep job spool files");
6893 puts("-l location Location of printer (default=empty string)");
6894 puts("-m model Model name (default=Printer)");
6895 puts("-n hostname Hostname for printer");
6896 puts("-p port Port number (default=auto)");
6897 puts("-r subtype Bonjour service subtype (default=_print)");
6898 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6899 puts("-v[vvv] Be (very) verbose");
6906 * 'valid_doc_attributes()' - Determine whether the document attributes are
6909 * When one or more document attributes are invalid, this function adds a
6910 * suitable response and attributes to the unsupported group.
6913 static int /* O - 1 if valid, 0 if not */
6914 valid_doc_attributes(
6915 _ipp_client_t
*client
) /* I - Client */
6917 int valid
= 1; /* Valid attributes? */
6918 ipp_op_t op
= ippGetOperation(client
->request
);
6920 const char *op_name
= ippOpString(op
);
6921 /* IPP operation name */
6922 ipp_attribute_t
*attr
, /* Current attribute */
6923 *supported
; /* xxx-supported attribute */
6924 const char *compression
= NULL
,
6925 /* compression value */
6926 *format
= NULL
; /* document-format value */
6930 * Check operation attributes...
6933 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6936 * If compression is specified, only accept a supported value in a Print-Job
6937 * or Send-Document request...
6940 compression
= ippGetString(attr
, 0, NULL
);
6941 supported
= ippFindAttribute(client
->printer
->attrs
,
6942 "compression-supported", IPP_TAG_KEYWORD
);
6944 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6945 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6946 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6947 op
!= IPP_OP_VALIDATE_JOB
) ||
6948 !ippContainsString(supported
, compression
))
6950 respond_unsupported(client
, attr
);
6955 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6957 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6959 if (strcmp(compression
, "none"))
6962 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6963 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
6969 * Is it a format we support?
6972 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
6974 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
6975 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
6977 respond_unsupported(client
, attr
);
6982 format
= ippGetString(attr
, 0, NULL
);
6984 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
6985 client
->hostname
, op_name
, format
);
6987 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
6992 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
6994 format
= "application/octet-stream"; /* Should never happen */
6996 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
6999 if (!strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
7002 * Auto-type the file using the first 8 bytes of the file...
7005 unsigned char header
[8]; /* First 8 bytes of file */
7007 memset(header
, 0, sizeof(header
));
7008 httpPeek(client
->http
, (char *)header
, sizeof(header
));
7010 if (!memcmp(header
, "%PDF", 4))
7011 format
= "application/pdf";
7012 else if (!memcmp(header
, "%!", 2))
7013 format
= "application/postscript";
7014 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
7015 format
= "image/jpeg";
7016 else if (!memcmp(header
, "\211PNG", 4))
7017 format
= "image/png";
7018 else if (!memcmp(header
, "RAS2", 4))
7019 format
= "image/pwg-raster";
7020 else if (!memcmp(header
, "UNIRAST", 8))
7021 format
= "image/urf";
7027 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
7028 client
->hostname
, op_name
, format
);
7030 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
7034 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
7036 respond_unsupported(client
, attr
);
7044 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
7045 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
7052 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
7054 * When one or more job attributes are invalid, this function adds a suitable
7055 * response and attributes to the unsupported group.
7058 static int /* O - 1 if valid, 0 if not */
7059 valid_job_attributes(
7060 _ipp_client_t
*client
) /* I - Client */
7062 int i
, /* Looping var */
7063 valid
= 1; /* Valid attributes? */
7064 ipp_attribute_t
*attr
, /* Current attribute */
7065 *supported
; /* xxx-supported attribute */
7069 * Check operation attributes...
7072 valid
= valid_doc_attributes(client
);
7075 * Check the various job template attributes...
7078 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
7080 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7081 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
7083 respond_unsupported(client
, attr
);
7088 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
7090 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
7092 respond_unsupported(client
, attr
);
7097 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
7099 if (ippGetCount(attr
) != 1 ||
7100 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7101 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7102 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7103 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
7105 respond_unsupported(client
, attr
);
7110 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
7112 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
7114 respond_unsupported(client
, attr
);
7119 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
7121 if (ippGetCount(attr
) != 1 ||
7122 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7123 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
7125 respond_unsupported(client
, attr
);
7129 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
7132 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
7134 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
7136 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7137 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
7139 respond_unsupported(client
, attr
);
7144 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
7146 if (ippGetCount(attr
) != 1 ||
7147 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7148 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7149 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7150 strcmp(ippGetString(attr
, 0, NULL
), "none"))
7152 respond_unsupported(client
, attr
);
7157 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
7159 if (ippGetCount(attr
) != 1 ||
7160 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7161 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7162 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
7164 respond_unsupported(client
, attr
);
7170 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
7172 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
7175 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
7177 respond_unsupported(client
, attr
);
7183 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
7185 if (ippGetCount(attr
) != 1 ||
7186 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
7188 respond_unsupported(client
, attr
);
7191 /* TODO: check for valid media-col */
7194 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
7196 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7197 (strcmp(ippGetString(attr
, 0, NULL
),
7198 "separate-documents-uncollated-copies") &&
7199 strcmp(ippGetString(attr
, 0, NULL
),
7200 "separate-documents-collated-copies")))
7202 respond_unsupported(client
, attr
);
7207 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
7209 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7210 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
7211 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
7213 respond_unsupported(client
, attr
);
7218 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
7220 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
7222 respond_unsupported(client
, attr
);
7227 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
7229 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7230 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
7231 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
7233 respond_unsupported(client
, attr
);
7238 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
7240 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
7242 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
7245 respond_unsupported(client
, attr
);
7250 int count
, /* Number of supported values */
7251 xdpi
, /* Horizontal resolution for job template attribute */
7252 ydpi
, /* Vertical resolution for job template attribute */
7253 sydpi
; /* Vertical resolution for supported value */
7254 ipp_res_t units
, /* Units for job template attribute */
7255 sunits
; /* Units for supported value */
7257 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
7258 count
= ippGetCount(supported
);
7260 for (i
= 0; i
< count
; i
++)
7262 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
7268 respond_unsupported(client
, attr
);
7274 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
7276 const char *sides
= ippGetString(attr
, 0, NULL
);
7277 /* "sides" value... */
7279 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
7281 respond_unsupported(client
, attr
);
7284 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
7286 if (!ippContainsString(supported
, sides
))
7288 respond_unsupported(client
, attr
);
7292 else if (strcmp(sides
, "one-sided"))
7294 respond_unsupported(client
, attr
);