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 */
112 typedef enum _ipp_media_class_e
114 _IPP_GENERAL
, /* General-purpose size */
115 _IPP_PHOTO_ONLY
, /* Photo-only size */
116 _IPP_ENV_ONLY
/* Envelope-only size */
117 } _ipp_media_class_t
;
119 typedef enum _ipp_media_size_e
121 _IPP_MEDIA_SIZE_NONE
= -1,
126 _IPP_MEDIA_SIZE_LEGAL
,
127 _IPP_MEDIA_SIZE_LETTER
,
128 _IPP_MEDIA_SIZE_COM10
,
134 static const char * const media_supported
[] =
135 { /* media-supported values */
136 "iso_a4_210x297mm", /* A4 */
137 "iso_a5_148x210mm", /* A5 */
138 "iso_a6_105x148mm", /* A6 */
139 "iso_dl_110x220mm", /* DL */
140 "na_legal_8.5x14in", /* Legal */
141 "na_letter_8.5x11in", /* Letter */
142 "na_number-10_4.125x9.5in", /* #10 */
143 "na_index-3x5_3x5in", /* 3x5 */
144 "oe_photo-l_3.5x5in", /* L */
145 "na_index-4x6_4x6in", /* 4x6 */
146 "na_5x7_5x7in" /* 5x7 aka 2L */
148 static const int media_col_sizes
[][3] =
149 { /* media-col-database sizes */
150 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
151 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
152 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
153 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
154 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
155 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
156 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
157 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
158 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
159 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
160 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
163 typedef enum _ipp_media_source_e
165 _IPP_MEDIA_SOURCE_NONE
= -1,
166 _IPP_MEDIA_SOURCE_AUTO
,
167 _IPP_MEDIA_SOURCE_MAIN
,
168 _IPP_MEDIA_SOURCE_MANUAL
,
169 _IPP_MEDIA_SOURCE_ENVELOPE
,
170 _IPP_MEDIA_SOURCE_PHOTO
171 } _ipp_media_source_t
;
172 static const char * const media_source_supported
[] =
173 /* media-source-supported values */
182 typedef enum _ipp_media_type_e
184 _IPP_MEDIA_TYPE_NONE
= -1,
185 _IPP_MEDIA_TYPE_AUTO
,
186 _IPP_MEDIA_TYPE_CARDSTOCK
,
187 _IPP_MEDIA_TYPE_ENVELOPE
,
188 _IPP_MEDIA_TYPE_LABELS
,
189 _IPP_MEDIA_TYPE_OTHER
,
190 _IPP_MEDIA_TYPE_GLOSSY
,
191 _IPP_MEDIA_TYPE_HIGH_GLOSS
,
192 _IPP_MEDIA_TYPE_MATTE
,
193 _IPP_MEDIA_TYPE_SATIN
,
194 _IPP_MEDIA_TYPE_SEMI_GLOSS
,
195 _IPP_MEDIA_TYPE_STATIONERY
,
196 _IPP_MEDIA_TYPE_LETTERHEAD
,
197 _IPP_MEDIA_TYPE_TRANSPARENCY
199 static const char * const media_type_supported
[] =
200 /* media-type-supported values */
207 "photographic-glossy",
208 "photographic-high-gloss",
209 "photographic-matte",
210 "photographic-satin",
211 "photographic-semi-gloss",
213 "stationery-letterhead",
217 typedef enum _ipp_supply_e
219 _IPP_SUPPLY_CYAN
, /* Cyan Toner */
220 _IPP_SUPPLY_MAGENTA
, /* Magenta Toner */
221 _IPP_SUPPLY_YELLOW
, /* Yellow Toner */
222 _IPP_SUPPLY_BLACK
, /* Black Toner */
223 _IPP_SUPPLY_WASTE
/* Waste Toner */
225 static const char * const printer_supplies
[] =
226 { /* printer-supply-description values */
240 typedef DNSServiceRef _ipp_srv_t
; /* Service reference */
241 typedef TXTRecordRef _ipp_txt_t
; /* TXT record */
243 #elif defined(HAVE_AVAHI)
244 typedef AvahiEntryGroup
*_ipp_srv_t
; /* Service reference */
245 typedef AvahiStringList
*_ipp_txt_t
; /* TXT record */
248 typedef void *_ipp_srv_t
; /* Service reference */
249 typedef void *_ipp_txt_t
; /* TXT record */
250 #endif /* HAVE_DNSSD */
252 typedef struct _ipp_filter_s
/**** Attribute filter ****/
254 cups_array_t
*ra
; /* Requested attributes */
255 ipp_tag_t group_tag
; /* Group to copy */
258 typedef struct _ipp_job_s _ipp_job_t
;
260 typedef struct _ipp_printer_s
/**** Printer data ****/
262 int ipv4
, /* IPv4 listener */
263 ipv6
; /* IPv6 listener */
264 _ipp_srv_t ipp_ref
, /* Bonjour IPP service */
265 ipps_ref
, /* Bonjour IPPS service */
266 http_ref
, /* Bonjour HTTP service */
267 printer_ref
; /* Bonjour LPD service */
268 char *dnssd_name
, /* printer-dnssd-name */
269 *name
, /* printer-name */
270 *icon
, /* Icon filename */
271 *directory
, /* Spool directory */
272 *hostname
, /* Hostname */
273 *uri
, /* printer-uri-supported */
274 *command
; /* Command to run with job file */
276 size_t urilen
; /* Length of printer URI */
277 ipp_t
*attrs
; /* Static attributes */
278 time_t start_time
; /* Startup time */
279 time_t config_time
; /* printer-config-change-time */
280 ipp_pstate_t state
; /* printer-state value */
281 _ipp_preason_t state_reasons
; /* printer-state-reasons values */
282 time_t state_time
; /* printer-state-change-time */
283 cups_array_t
*jobs
; /* Jobs */
284 _ipp_job_t
*active_job
; /* Current active/pending job */
285 int next_job_id
; /* Next job-id value */
286 _cups_rwlock_t rwlock
; /* Printer lock */
287 _ipp_media_size_t main_size
; /* Ready media */
288 _ipp_media_type_t main_type
;
290 _ipp_media_size_t envelope_size
;
292 _ipp_media_size_t photo_size
;
293 _ipp_media_type_t photo_type
;
295 int supplies
[5]; /* Supply levels (0-100) */
298 struct _ipp_job_s
/**** Job data ****/
301 const char *name
, /* job-name */
302 *username
, /* job-originating-user-name */
303 *format
; /* document-format */
304 ipp_jstate_t state
; /* job-state value */
305 time_t created
, /* time-at-creation value */
306 processing
, /* time-at-processing value */
307 completed
; /* time-at-completed value */
308 int impressions
, /* job-impressions value */
309 impcompleted
; /* job-impressions-completed value */
310 ipp_t
*attrs
; /* Static attributes */
311 int cancel
; /* Non-zero when job canceled */
312 char *filename
; /* Print file name */
313 int fd
; /* Print file descriptor */
314 _ipp_printer_t
*printer
; /* Printer */
317 typedef struct _ipp_client_s
/**** Client data ****/
319 http_t
*http
; /* HTTP connection */
320 ipp_t
*request
, /* IPP request */
321 *response
; /* IPP response */
322 time_t start
; /* Request start time */
323 http_state_t operation
; /* Request operation */
324 ipp_op_t operation_id
; /* IPP operation-id */
325 char uri
[1024], /* Request URI */
326 *options
; /* URI options */
327 http_addr_t addr
; /* Client address */
328 char hostname
[256]; /* Client hostname */
329 _ipp_printer_t
*printer
; /* Printer */
330 _ipp_job_t
*job
; /* Current job, if any */
338 static void clean_jobs(_ipp_printer_t
*printer
);
339 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
340 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
341 ipp_tag_t group_tag
, int quickcopy
);
342 static void copy_job_attributes(_ipp_client_t
*client
,
343 _ipp_job_t
*job
, cups_array_t
*ra
);
344 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
345 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
346 static int create_listener(int family
, int port
);
347 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
348 static ipp_t
*create_media_size(int width
, int length
);
349 static _ipp_printer_t
*create_printer(const char *servername
,
350 const char *name
, const char *location
,
351 const char *make
, const char *model
,
353 const char *docformats
, int ppm
,
354 int ppm_color
, int duplex
, int port
,
355 int pin
, const char *subtype
,
356 const char *directory
,
358 const char *attrfile
);
359 static void debug_attributes(const char *title
, ipp_t
*ipp
,
361 static void delete_client(_ipp_client_t
*client
);
362 static void delete_job(_ipp_job_t
*job
);
363 static void delete_printer(_ipp_printer_t
*printer
);
365 static void DNSSD_API
dnssd_callback(DNSServiceRef sdRef
,
366 DNSServiceFlags flags
,
367 DNSServiceErrorType errorCode
,
371 _ipp_printer_t
*printer
);
372 #elif defined(HAVE_AVAHI)
373 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
374 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
375 #endif /* HAVE_DNSSD */
376 static void dnssd_init(void);
377 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
378 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
379 static ipp_t
*get_collection(FILE *fp
, const char *filename
, int *linenum
);
380 static char *get_token(FILE *fp
, char *buf
, int buflen
, int *linenum
);
381 static void html_escape(_ipp_client_t
*client
, const char *s
,
383 static void html_footer(_ipp_client_t
*client
);
384 static void html_header(_ipp_client_t
*client
, const char *title
);
385 static void html_printf(_ipp_client_t
*client
, const char *format
,
386 ...) __attribute__((__format__(__printf__
,
388 static void ipp_cancel_job(_ipp_client_t
*client
);
389 static void ipp_close_job(_ipp_client_t
*client
);
390 static void ipp_create_job(_ipp_client_t
*client
);
391 static void ipp_get_job_attributes(_ipp_client_t
*client
);
392 static void ipp_get_jobs(_ipp_client_t
*client
);
393 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
394 static void ipp_identify_printer(_ipp_client_t
*client
);
395 static void ipp_print_job(_ipp_client_t
*client
);
396 static void ipp_print_uri(_ipp_client_t
*client
);
397 static void ipp_send_document(_ipp_client_t
*client
);
398 static void ipp_send_uri(_ipp_client_t
*client
);
399 static void ipp_validate_job(_ipp_client_t
*client
);
400 static void load_attributes(const char *filename
, ipp_t
*attrs
);
401 static int parse_options(_ipp_client_t
*client
, cups_option_t
**options
);
402 static void *process_client(_ipp_client_t
*client
);
403 static int process_http(_ipp_client_t
*client
);
404 static int process_ipp(_ipp_client_t
*client
);
405 static void *process_job(_ipp_job_t
*job
);
406 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
);
407 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
408 const char *content_coding
,
409 const char *type
, size_t length
);
410 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
411 const char *message
, ...)
412 __attribute__ ((__format__ (__printf__
, 3, 4)));
413 static void respond_unsupported(_ipp_client_t
*client
,
414 ipp_attribute_t
*attr
);
415 static void run_printer(_ipp_printer_t
*printer
);
416 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
417 static void usage(int status
) __attribute__((noreturn
));
418 static int valid_doc_attributes(_ipp_client_t
*client
);
419 static int valid_job_attributes(_ipp_client_t
*client
);
427 static DNSServiceRef DNSSDMaster
= NULL
;
428 #elif defined(HAVE_AVAHI)
429 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
430 static AvahiClient
*DNSSDClient
= NULL
;
431 #endif /* HAVE_DNSSD */
433 static int KeepFiles
= 0,
438 * 'main()' - Main entry to the sample server.
441 int /* O - Exit status */
442 main(int argc
, /* I - Number of command-line args */
443 char *argv
[]) /* I - Command-line arguments */
445 int i
; /* Looping var */
446 const char *opt
, /* Current option character */
447 *attrfile
= NULL
, /* Attributes file */
448 *command
= NULL
, /* Command to run with job files */
449 *servername
= NULL
, /* Server host name */
450 *name
= NULL
, /* Printer name */
451 *location
= "", /* Location of printer */
452 *make
= "Test", /* Manufacturer */
453 *model
= "Printer", /* Model */
454 *icon
= "printer.png", /* Icon file */
455 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
456 /* Supported formats */
458 const char *keypath
= NULL
; /* Keychain path */
459 #endif /* HAVE_SSL */
460 const char *subtype
= "_print"; /* Bonjour service subtype */
461 int port
= 0, /* Port number (0 = auto) */
462 duplex
= 0, /* Duplex mode */
463 ppm
= 10, /* Pages per minute for mono */
464 ppm_color
= 0, /* Pages per minute for color */
465 pin
= 0; /* PIN printing mode? */
466 char directory
[1024] = "", /* Spool directory */
467 hostname
[1024]; /* Auto-detected hostname */
468 _ipp_printer_t
*printer
; /* Printer object */
472 * Parse command-line arguments...
475 for (i
= 1; i
< argc
; i
++)
476 if (argv
[i
][0] == '-')
478 for (opt
= argv
[i
] + 1; *opt
; opt
++)
482 case '2' : /* -2 (enable 2-sided printing) */
487 case 'K' : /* -K keypath */
493 #endif /* HAVE_SSL */
495 case 'M' : /* -M manufacturer */
502 case 'P' : /* -P (PIN printing mode) */
506 case 'a' : /* -a attributes-file */
514 case 'c' : /* -c command */
522 case 'd' : /* -d spool-directory */
526 strlcpy(directory
, argv
[i
], sizeof(directory
));
529 case 'f' : /* -f type/subtype[,...] */
536 case 'h' : /* -h (show help) */
539 case 'i' : /* -i icon.png */
546 case 'k' : /* -k (keep files) */
550 case 'l' : /* -l location */
557 case 'm' : /* -m model */
564 case 'n' : /* -n hostname */
568 servername
= argv
[i
];
571 case 'p' : /* -p port */
573 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
575 port
= atoi(argv
[i
]);
578 case 'r' : /* -r subtype */
585 case 's' : /* -s speed[,color-speed] */
589 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
593 case 'v' : /* -v (be verbose) */
597 default : /* Unknown */
598 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
609 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
617 * Apply defaults as needed...
621 servername
= httpGetHostname(NULL
, hostname
, sizeof(hostname
));
627 * Windows is almost always used as a single user system, so use a default
628 * port number of 8631.
635 * Use 8000 + UID mod 1000 for the default port number...
638 port
= 8000 + ((int)getuid() % 1000);
641 fprintf(stderr
, "Listening on port %d.\n", port
);
646 const char *tmpdir
; /* Temporary directory */
649 if ((tmpdir
= getenv("TEMP")) == NULL
)
651 #elif defined(__APPLE__)
652 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
653 tmpdir
= "/private/tmp";
655 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
659 snprintf(directory
, sizeof(directory
), "%s/ippserver.%d", tmpdir
, (int)getpid());
661 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
663 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
664 directory
, strerror(errno
));
669 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
673 cupsSetServerCredentials(keypath
, servername
, 1);
674 #endif /* HAVE_SSL */
677 * Initialize Bonjour...
683 * Create the printer...
686 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
687 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
688 subtype
, directory
, command
, attrfile
)) == NULL
)
692 * Run the print service...
695 run_printer(printer
);
698 * Destroy the printer and exit...
701 delete_printer(printer
);
708 * 'clean_jobs()' - Clean out old (completed) jobs.
712 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
714 _ipp_job_t
*job
; /* Current job */
715 time_t cleantime
; /* Clean time */
718 if (cupsArrayCount(printer
->jobs
) == 0)
721 cleantime
= time(NULL
) - 60;
723 _cupsRWLockWrite(&(printer
->rwlock
));
724 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
726 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
727 if (job
->completed
&& job
->completed
< cleantime
)
729 cupsArrayRemove(printer
->jobs
, job
);
734 _cupsRWUnlock(&(printer
->rwlock
));
739 * 'compare_jobs()' - Compare two jobs.
742 static int /* O - Result of comparison */
743 compare_jobs(_ipp_job_t
*a
, /* I - First job */
744 _ipp_job_t
*b
) /* I - Second job */
746 return (b
->id
- a
->id
);
751 * 'copy_attributes()' - Copy attributes from one request to another.
755 copy_attributes(ipp_t
*to
, /* I - Destination request */
756 ipp_t
*from
, /* I - Source request */
757 cups_array_t
*ra
, /* I - Requested attributes */
758 ipp_tag_t group_tag
, /* I - Group to copy */
759 int quickcopy
) /* I - Do a quick copy? */
761 _ipp_filter_t filter
; /* Filter data */
765 filter
.group_tag
= group_tag
;
767 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
772 * 'copy_job_attrs()' - Copy job attributes to the response.
777 _ipp_client_t
*client
, /* I - Client */
778 _ipp_job_t
*job
, /* I - Job */
779 cups_array_t
*ra
) /* I - requested-attributes */
781 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
783 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
786 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
788 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
791 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
794 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
796 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
799 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
800 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
802 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
803 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
805 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
806 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
808 if (!ra
|| cupsArrayFind(ra
, "job-state"))
809 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
810 "job-state", job
->state
);
812 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
816 case IPP_JSTATE_PENDING
:
817 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
820 case IPP_JSTATE_HELD
:
822 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
823 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
824 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
826 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
829 case IPP_JSTATE_PROCESSING
:
831 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
833 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
836 case IPP_JSTATE_STOPPED
:
837 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
840 case IPP_JSTATE_CANCELED
:
841 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
844 case IPP_JSTATE_ABORTED
:
845 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
848 case IPP_JSTATE_COMPLETED
:
849 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
854 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
858 case IPP_JSTATE_PENDING
:
859 ippAddString(client
->response
, IPP_TAG_JOB
,
860 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
864 case IPP_JSTATE_HELD
:
866 ippAddString(client
->response
, IPP_TAG_JOB
,
867 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
868 "job-state-reasons", NULL
, "job-incoming");
869 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
870 ippAddString(client
->response
, IPP_TAG_JOB
,
871 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
872 "job-state-reasons", NULL
, "job-hold-until-specified");
874 ippAddString(client
->response
, IPP_TAG_JOB
,
875 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
876 "job-state-reasons", NULL
, "job-data-insufficient");
879 case IPP_JSTATE_PROCESSING
:
881 ippAddString(client
->response
, IPP_TAG_JOB
,
882 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
883 "job-state-reasons", NULL
, "processing-to-stop-point");
885 ippAddString(client
->response
, IPP_TAG_JOB
,
886 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
887 "job-state-reasons", NULL
, "job-printing");
890 case IPP_JSTATE_STOPPED
:
891 ippAddString(client
->response
, IPP_TAG_JOB
,
892 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
893 NULL
, "job-stopped");
896 case IPP_JSTATE_CANCELED
:
897 ippAddString(client
->response
, IPP_TAG_JOB
,
898 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
899 NULL
, "job-canceled-by-user");
902 case IPP_JSTATE_ABORTED
:
903 ippAddString(client
->response
, IPP_TAG_JOB
,
904 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
905 NULL
, "aborted-by-system");
908 case IPP_JSTATE_COMPLETED
:
909 ippAddString(client
->response
, IPP_TAG_JOB
,
910 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
911 NULL
, "job-completed-successfully");
916 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
917 ippAddInteger(client
->response
, IPP_TAG_JOB
,
918 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
919 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
921 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
922 ippAddInteger(client
->response
, IPP_TAG_JOB
,
923 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
924 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
929 * 'create_client()' - Accept a new network connection and create a client
933 static _ipp_client_t
* /* O - Client */
934 create_client(_ipp_printer_t
*printer
, /* I - Printer */
935 int sock
) /* I - Listen socket */
937 _ipp_client_t
*client
; /* Client */
940 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
942 perror("Unable to allocate memory for client");
946 client
->printer
= printer
;
949 * Accept the client and get the remote address...
952 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
954 perror("Unable to accept client connection");
961 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
964 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
971 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
975 static _ipp_job_t
* /* O - Job */
976 create_job(_ipp_client_t
*client
) /* I - Client */
978 _ipp_job_t
*job
; /* Job */
979 ipp_attribute_t
*attr
; /* Job attribute */
980 char uri
[1024], /* job-uri value */
981 uuid
[64]; /* job-uuid value */
984 _cupsRWLockWrite(&(client
->printer
->rwlock
));
985 if (client
->printer
->active_job
&&
986 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
989 * Only accept a single job at a time...
992 _cupsRWLockWrite(&(client
->printer
->rwlock
));
997 * Allocate and initialize the job object...
1000 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
1002 perror("Unable to allocate memory for job");
1006 job
->printer
= client
->printer
;
1007 job
->attrs
= ippNew();
1008 job
->state
= IPP_JSTATE_HELD
;
1012 * Copy all of the job attributes...
1015 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
1018 * Get the requesting-user-name, document format, and priority...
1021 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1022 job
->username
= ippGetString(attr
, 0, NULL
);
1024 job
->username
= "anonymous";
1026 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1028 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1030 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1031 job
->format
= ippGetString(attr
, 0, NULL
);
1032 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1033 job
->format
= ippGetString(attr
, 0, NULL
);
1035 job
->format
= "application/octet-stream";
1038 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1039 job
->impressions
= ippGetInteger(attr
, 0);
1041 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1042 job
->name
= ippGetString(attr
, 0, NULL
);
1045 * Add job description attributes and add to the jobs array...
1048 job
->id
= client
->printer
->next_job_id
++;
1050 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1051 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1053 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1054 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1055 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1056 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1057 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1058 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1060 cupsArrayAdd(client
->printer
->jobs
, job
);
1061 client
->printer
->active_job
= job
;
1063 _cupsRWUnlock(&(client
->printer
->rwlock
));
1070 * 'create_job_filename()' - Create the filename for a document in a job.
1073 static void create_job_filename(
1074 _ipp_printer_t
*printer
, /* I - Printer */
1075 _ipp_job_t
*job
, /* I - Job */
1076 char *fname
, /* I - Filename buffer */
1077 size_t fnamesize
) /* I - Size of filename buffer */
1079 char name
[256], /* "Safe" filename */
1080 *nameptr
; /* Pointer into filename */
1081 const char *ext
, /* Filename extension */
1082 *job_name
; /* job-name value */
1083 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1087 * Make a name from the job-name attribute...
1090 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1091 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1093 job_name
= "untitled";
1095 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1096 if (isalnum(*job_name
& 255) || *job_name
== '-')
1097 *nameptr
++ = (char)tolower(*job_name
& 255);
1104 * Figure out the extension...
1107 if (!strcasecmp(job
->format
, "image/jpeg"))
1109 else if (!strcasecmp(job
->format
, "image/png"))
1111 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1113 else if (!strcasecmp(job
->format
, "image/urf"))
1115 else if (!strcasecmp(job
->format
, "application/pdf"))
1117 else if (!strcasecmp(job
->format
, "application/postscript"))
1123 * Create a filename with the job-id, job-name, and document-format (extension)...
1126 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1131 * 'create_listener()' - Create a listener socket.
1134 static int /* O - Listener socket or -1 on error */
1135 create_listener(int family
, /* I - Address family */
1136 int port
) /* I - Port number */
1138 int sock
; /* Listener socket */
1139 http_addrlist_t
*addrlist
; /* Listen address */
1140 char service
[255]; /* Service port */
1143 snprintf(service
, sizeof(service
), "%d", port
);
1144 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1147 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1149 httpAddrFreeList(addrlist
);
1156 * 'create_media_col()' - Create a media-col value.
1159 static ipp_t
* /* O - media-col collection */
1160 create_media_col(const char *media
, /* I - Media name */
1161 const char *source
, /* I - Media source */
1162 const char *type
, /* I - Media type */
1163 int width
, /* I - x-dimension in 2540ths */
1164 int length
, /* I - y-dimension in 2540ths */
1165 int margins
) /* I - Value for margins */
1167 ipp_t
*media_col
= ippNew(), /* media-col value */
1168 *media_size
= create_media_size(width
, length
);
1169 /* media-size value */
1170 char media_key
[256]; /* media-key value */
1174 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1176 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1178 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1180 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1182 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1184 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1185 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1186 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1187 "media-bottom-margin", margins
);
1188 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1189 "media-left-margin", margins
);
1190 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1191 "media-right-margin", margins
);
1192 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1193 "media-top-margin", margins
);
1195 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1197 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1199 ippDelete(media_size
);
1206 * 'create_media_size()' - Create a media-size value.
1209 static ipp_t
* /* O - media-col collection */
1210 create_media_size(int width
, /* I - x-dimension in 2540ths */
1211 int length
) /* I - y-dimension in 2540ths */
1213 ipp_t
*media_size
= ippNew(); /* media-size value */
1216 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1218 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1221 return (media_size
);
1226 * 'create_printer()' - Create, register, and listen for connections to a
1230 static _ipp_printer_t
* /* O - Printer */
1231 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1232 const char *name
, /* I - printer-name */
1233 const char *location
, /* I - printer-location */
1234 const char *make
, /* I - printer-make-and-model */
1235 const char *model
, /* I - printer-make-and-model */
1236 const char *icon
, /* I - printer-icons */
1237 const char *docformats
, /* I - document-format-supported */
1238 int ppm
, /* I - Pages per minute in grayscale */
1239 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1240 int duplex
, /* I - 1 = duplex, 0 = simplex */
1241 int port
, /* I - Port for listeners or 0 for auto */
1242 int pin
, /* I - Require PIN printing */
1243 const char *subtype
, /* I - Bonjour service subtype */
1244 const char *directory
, /* I - Spool directory */
1245 const char *command
, /* I - Command to run on job files */
1246 const char *attrfile
) /* I - Attributes file */
1248 int i
, j
; /* Looping vars */
1249 _ipp_printer_t
*printer
; /* Printer */
1251 char path
[1024]; /* Full path to command */
1253 char uri
[1024], /* Printer URI */
1255 securi
[1024], /* Secure printer URI */
1256 *uris
[2], /* All URIs */
1257 #endif /* HAVE_SSL */
1258 icons
[1024], /* printer-icons URI */
1259 adminurl
[1024], /* printer-more-info URI */
1260 supplyurl
[1024],/* printer-supply-info-uri URI */
1261 device_id
[1024],/* printer-device-id */
1262 make_model
[128],/* printer-make-and-model */
1263 uuid
[128]; /* printer-uuid */
1264 int num_formats
; /* Number of document-format-supported values */
1265 char *defformat
, /* document-format-default value */
1266 *formats
[100], /* document-format-supported values */
1267 *ptr
; /* Pointer into string */
1268 const char *prefix
; /* Prefix string */
1269 int num_database
; /* Number of database values */
1270 ipp_attribute_t
*media_col_database
,
1271 /* media-col-database value */
1272 *media_size_supported
;
1273 /* media-size-supported value */
1274 ipp_t
*media_col_default
;
1275 /* media-col-default value */
1276 int media_col_index
;/* Current media-col-database value */
1277 int k_supported
; /* Maximum file size supported */
1279 struct statvfs spoolinfo
; /* FS info for spool directory */
1280 double spoolsize
; /* FS size */
1281 #elif defined(HAVE_STATFS)
1282 struct statfs spoolinfo
; /* FS info for spool directory */
1283 double spoolsize
; /* FS size */
1284 #endif /* HAVE_STATVFS */
1285 static const int orients
[4] = /* orientation-requested-supported values */
1287 IPP_ORIENT_PORTRAIT
,
1288 IPP_ORIENT_LANDSCAPE
,
1289 IPP_ORIENT_REVERSE_LANDSCAPE
,
1290 IPP_ORIENT_REVERSE_PORTRAIT
1292 static const char * const versions
[] =/* ipp-versions-supported values */
1298 static const char * const features
[] =/* ipp-features-supported values */
1302 static const int ops
[] = /* operations-supported values */
1306 IPP_OP_VALIDATE_JOB
,
1308 IPP_OP_SEND_DOCUMENT
,
1311 IPP_OP_GET_JOB_ATTRIBUTES
,
1313 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1314 IPP_OP_CANCEL_MY_JOBS
,
1316 IPP_OP_IDENTIFY_PRINTER
1318 static const char * const charsets
[] =/* charset-supported values */
1323 static const char * const compressions
[] =/* compression-supported values */
1328 #endif /* HAVE_LIBZ */
1331 static const char * const identify_actions
[] =
1336 static const char * const job_creation
[] =
1337 { /* job-creation-attributes-supported values */
1339 "ipp-attribute-fidelity",
1341 "job-accounting-user-id",
1347 "multiple-document-handling",
1348 "orientation-requested",
1352 static const char * const media_col_supported
[] =
1353 { /* media-col-supported values */
1354 "media-bottom-margin",
1355 "media-left-margin",
1356 "media-right-margin",
1362 static const int media_xxx_margin_supported
[] =
1363 { /* media-xxx-margin-supported values */
1367 static const char * const multiple_document_handling
[] =
1368 { /* multiple-document-handling-supported values */
1369 "separate-documents-uncollated-copies",
1370 "separate-documents-collated-copies"
1372 static const char * const overrides
[] =
1373 { /* overrides-supported */
1377 static const char * const print_color_mode_supported
[] =
1378 { /* print-color-mode-supported values */
1383 static const int print_quality_supported
[] =
1384 { /* print-quality-supported values */
1389 static const int pwg_raster_document_resolution_supported
[] =
1394 static const char * const pwg_raster_document_type_supported
[] =
1402 static const char * const reference_uri_schemes_supported
[] =
1403 { /* reference-uri-schemes-supported */
1409 #endif /* HAVE_SSL */
1411 static const char * const sides_supported
[] =
1412 { /* sides-supported values */
1414 "two-sided-long-edge",
1415 "two-sided-short-edge"
1417 static const char * const urf_supported
[] =
1418 { /* urf-supported values */
1421 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1429 static const char * const uri_authentication_supported
[] =
1430 { /* uri-authentication-supported values */
1434 static const char * const uri_security_supported
[] =
1435 { /* uri-security-supported values */
1439 #endif /* HAVE_SSL */
1440 static const char * const which_jobs
[] =
1441 { /* which-jobs-supported values */
1450 "processing-stopped"
1456 * If a command was specified, make sure it exists and is executable...
1461 if (*command
== '/' || !strncmp(command
, "./", 2))
1463 if (access(command
, X_OK
))
1465 fprintf(stderr
, "ippserver: Unable to execute command \"%s\": %s\n", command
, strerror(errno
));
1471 if (!cupsFileFind(command
, getenv("PATH"), 1, path
, sizeof(path
)))
1473 fprintf(stderr
, "ippserver: Unable to find command \"%s\".\n", command
);
1483 * Allocate memory for the printer...
1486 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1488 perror("ippserver: Unable to allocate memory for printer");
1494 printer
->name
= strdup(name
);
1495 printer
->dnssd_name
= strdup(printer
->name
);
1496 printer
->command
= command
? strdup(command
) : NULL
;
1497 printer
->directory
= strdup(directory
);
1498 printer
->hostname
= strdup(servername
);
1499 printer
->port
= port
;
1500 printer
->start_time
= time(NULL
);
1501 printer
->config_time
= printer
->start_time
;
1502 printer
->state
= IPP_PSTATE_IDLE
;
1503 printer
->state_reasons
= _IPP_PREASON_NONE
;
1504 printer
->state_time
= printer
->start_time
;
1505 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1506 printer
->next_job_id
= 1;
1508 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1509 printer
->uri
= strdup(uri
);
1510 printer
->urilen
= strlen(uri
);
1513 httpAssembleURI(HTTP_URI_CODING_ALL
, securi
, sizeof(securi
), "ipps", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1514 #endif /* HAVE_SSL */
1517 printer
->icon
= strdup(icon
);
1519 printer
->main_size
= _IPP_MEDIA_SIZE_A4
;
1520 printer
->main_type
= _IPP_MEDIA_TYPE_STATIONERY
;
1521 printer
->main_level
= 500;
1523 printer
->envelope_size
= _IPP_MEDIA_SIZE_NONE
;
1524 printer
->envelope_level
= 0;
1526 printer
->photo_size
= _IPP_MEDIA_SIZE_NONE
;
1527 printer
->photo_type
= _IPP_MEDIA_TYPE_NONE
;
1528 printer
->photo_level
= 0;
1530 printer
->supplies
[_IPP_SUPPLY_CYAN
] = 100;
1531 printer
->supplies
[_IPP_SUPPLY_MAGENTA
] = 100;
1532 printer
->supplies
[_IPP_SUPPLY_YELLOW
] = 100;
1533 printer
->supplies
[_IPP_SUPPLY_BLACK
] = 100;
1534 printer
->supplies
[_IPP_SUPPLY_WASTE
] = 0;
1536 _cupsRWInit(&(printer
->rwlock
));
1539 * Create the listener sockets...
1542 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1544 perror("Unable to create IPv4 listener");
1548 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1550 perror("Unable to create IPv6 listener");
1555 * Prepare values for the printer attributes...
1559 # define WEB_SCHEME "https"
1561 # define WEB_SCHEME "http"
1562 #endif /* HAVE_SSL */
1564 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/icon.png");
1565 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/");
1566 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/supplies");
1570 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1571 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1572 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1575 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1578 formats
[0] = strdup(docformats
);
1579 defformat
= formats
[0];
1580 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1583 formats
[num_formats
++] = ptr
;
1585 if (!strcasecmp(ptr
, "application/octet-stream"))
1589 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1590 ptr
= device_id
+ strlen(device_id
);
1592 for (i
= 0; i
< num_formats
; i
++)
1594 if (!strcasecmp(formats
[i
], "application/pdf"))
1595 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1596 else if (!strcasecmp(formats
[i
], "application/postscript"))
1597 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1598 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1599 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1600 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1601 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1602 else if (!strcasecmp(formats
[i
], "image/png"))
1603 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1604 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1605 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1610 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
1617 * Get the maximum spool size based on the size of the filesystem used for
1618 * the spool directory. If the host OS doesn't support the statfs call
1619 * or the filesystem is larger than 2TiB, always report INT_MAX.
1623 if (statvfs(printer
->directory
, &spoolinfo
))
1624 k_supported
= INT_MAX
;
1625 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1626 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1627 k_supported
= INT_MAX
;
1629 k_supported
= (int)spoolsize
;
1631 #elif defined(HAVE_STATFS)
1632 if (statfs(printer
->directory
, &spoolinfo
))
1633 k_supported
= INT_MAX
;
1634 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1635 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1636 k_supported
= INT_MAX
;
1638 k_supported
= (int)spoolsize
;
1641 k_supported
= INT_MAX
;
1642 #endif /* HAVE_STATVFS */
1645 * Create the printer attributes. This list of attributes is sorted to improve
1646 * performance when the client provides a requested-attributes attribute...
1649 printer
->attrs
= ippNew();
1652 load_attributes(attrfile
, printer
->attrs
);
1654 /* charset-configured */
1655 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-configured", NULL
, "utf-8");
1657 /* charset-supported */
1658 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]), NULL
, charsets
);
1660 /* color-supported */
1661 if (!ippFindAttribute(printer
->attrs
, "color-supported", IPP_TAG_ZERO
))
1662 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
1664 /* compression-supported */
1665 if (!ippFindAttribute(printer
->attrs
, "compression-supported", IPP_TAG_ZERO
))
1666 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "compression-supported", (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
, compressions
);
1668 /* copies-default */
1669 if (!ippFindAttribute(printer
->attrs
, "copies-default", IPP_TAG_ZERO
))
1670 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
1672 /* copies-supported */
1673 if (!ippFindAttribute(printer
->attrs
, "copies-supported", IPP_TAG_ZERO
))
1674 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1676 /* document-format-default */
1677 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1678 "document-format-default", NULL
, defformat
);
1680 /* document-format-supported */
1681 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1682 "document-format-supported", num_formats
, NULL
,
1683 (const char * const *)formats
);
1685 /* document-password-supported */
1686 if (!ippFindAttribute(printer
->attrs
, "document-password-supported", IPP_TAG_ZERO
))
1687 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1689 /* finishings-default */
1690 if (!ippFindAttribute(printer
->attrs
, "finishings-default", IPP_TAG_ZERO
))
1691 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
1693 /* finishings-supported */
1694 if (!ippFindAttribute(printer
->attrs
, "finishings-supported", IPP_TAG_ZERO
))
1695 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
1697 /* generated-natural-language-supported */
1698 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "generated-natural-language-supported", NULL
, "en");
1700 /* identify-actions-default */
1701 if (!ippFindAttribute(printer
->attrs
, "identify-actions-default", IPP_TAG_ZERO
))
1702 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1704 /* identify-actions-supported */
1705 if (!ippFindAttribute(printer
->attrs
, "identify-actions-supported", IPP_TAG_ZERO
))
1706 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
);
1708 /* ipp-features-supported */
1709 if (!ippFindAttribute(printer
->attrs
, "ipp-features-supported", IPP_TAG_ZERO
))
1710 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1712 /* ipp-versions-supported */
1713 if (!ippFindAttribute(printer
->attrs
, "ipp-versions-supported", IPP_TAG_ZERO
))
1714 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1716 /* job-account-id-default */
1717 if (!ippFindAttribute(printer
->attrs
, "job-account-id-default", IPP_TAG_ZERO
))
1718 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1720 /* job-account-id-supported */
1721 if (!ippFindAttribute(printer
->attrs
, "job-account-id-supported", IPP_TAG_ZERO
))
1722 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1724 /* job-accounting-user-id-default */
1725 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-default", IPP_TAG_ZERO
))
1726 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1728 /* job-accounting-user-id-supported */
1729 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-supported", IPP_TAG_ZERO
))
1730 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1732 /* job-creation-attributes-supported */
1733 if (!ippFindAttribute(printer
->attrs
, "job-creation-attributes-supported", IPP_TAG_ZERO
))
1734 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
);
1736 /* job-ids-supported */
1737 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1739 /* job-k-octets-supported */
1740 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1743 /* job-password-supported */
1744 if (!ippFindAttribute(printer
->attrs
, "job-password-supported", IPP_TAG_ZERO
))
1745 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-password-supported", 4);
1747 /* job-preferred-attributes-supported */
1748 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-preferred-attributes-supported", 0);
1750 /* job-priority-default */
1751 if (!ippFindAttribute(printer
->attrs
, "job-priority-default", IPP_TAG_ZERO
))
1752 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-default", 50);
1754 /* job-priority-supported */
1755 if (!ippFindAttribute(printer
->attrs
, "job-priority-supported", IPP_TAG_ZERO
))
1756 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-supported", 100);
1758 /* job-sheets-default */
1759 if (!ippFindAttribute(printer
->attrs
, "job-sheets-default", IPP_TAG_ZERO
))
1760 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-default", NULL
, "none");
1762 /* job-sheets-supported */
1763 if (!ippFindAttribute(printer
->attrs
, "job-sheets-supported", IPP_TAG_ZERO
))
1764 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-supported", NULL
, "none");
1766 /* media-bottom-margin-supported */
1767 if (!ippFindAttribute(printer
->attrs
, "media-bottom-margin-supported", IPP_TAG_ZERO
))
1768 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
);
1770 /* media-col-database */
1771 if (!ippFindAttribute(printer
->attrs
, "media-col-database", IPP_TAG_ZERO
))
1773 for (num_database
= 0, i
= 0;
1774 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1777 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1778 num_database
+= 3; /* auto + manual + envelope */
1779 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1780 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1782 num_database
+= 2; /* Regular + borderless */
1785 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-database", num_database
, NULL
);
1786 for (media_col_index
= 0, i
= 0;
1787 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1790 switch (media_col_sizes
[i
][2])
1794 * Regular + borderless for the general class; no source/type
1798 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]));
1799 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]));
1802 case _IPP_ENV_ONLY
:
1804 * Regular margins for "auto", "manual", and "envelope" sources.
1807 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]));
1808 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]));
1809 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]));
1811 case _IPP_PHOTO_ONLY
:
1813 * Photos have specific media types and can only be printed via
1814 * the auto, manual, and photo sources...
1818 j
< (int)(sizeof(media_type_supported
) /
1819 sizeof(media_type_supported
[0]));
1822 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1825 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]));
1826 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]));
1827 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]));
1834 /* media-col-default */
1835 if (!ippFindAttribute(printer
->attrs
, "media-col-default", IPP_TAG_ZERO
))
1837 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]);
1839 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1841 ippDelete(media_col_default
);
1844 /* media-col-supported */
1845 if (!ippFindAttribute(printer
->attrs
, "media-col-supported", IPP_TAG_ZERO
))
1846 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
);
1849 if (!ippFindAttribute(printer
->attrs
, "media-default", IPP_TAG_ZERO
))
1850 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media_supported
[0]);
1852 /* media-left-margin-supported */
1853 if (!ippFindAttribute(printer
->attrs
, "media-left-margin-supported", IPP_TAG_ZERO
))
1854 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
);
1856 /* media-right-margin-supported */
1857 if (!ippFindAttribute(printer
->attrs
, "media-right-margin-supported", IPP_TAG_ZERO
))
1858 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
);
1860 /* media-supported */
1861 if (!ippFindAttribute(printer
->attrs
, "media-supported", IPP_TAG_ZERO
))
1862 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
);
1864 /* media-size-supported */
1865 if (!ippFindAttribute(printer
->attrs
, "media-size-supported", IPP_TAG_ZERO
))
1867 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-size-supported", (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0])), NULL
);
1870 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1873 ipp_t
*size
= create_media_size(media_col_sizes
[i
][0], media_col_sizes
[i
][1]);
1875 ippSetCollection(printer
->attrs
, &media_size_supported
, i
, size
);
1880 /* media-source-supported */
1881 if (!ippFindAttribute(printer
->attrs
, "media-source-supported", IPP_TAG_ZERO
))
1882 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
);
1884 /* media-top-margin-supported */
1885 if (!ippFindAttribute(printer
->attrs
, "media-top-margin-supported", IPP_TAG_ZERO
))
1886 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
);
1888 /* media-type-supported */
1889 if (!ippFindAttribute(printer
->attrs
, "media-type-supported", IPP_TAG_ZERO
))
1890 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
);
1892 /* multiple-document-handling-supported */
1893 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
);
1895 /* multiple-document-jobs-supported */
1896 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1898 /* multiple-operation-time-out */
1899 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1901 /* multiple-operation-time-out-action */
1902 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1904 /* natural-language-configured */
1905 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1906 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1907 "natural-language-configured", NULL
, "en");
1909 /* number-up-default */
1910 if (!ippFindAttribute(printer
->attrs
, "number-up-default", IPP_TAG_ZERO
))
1911 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-default", 1);
1913 /* number-up-supported */
1914 if (!ippFindAttribute(printer
->attrs
, "number-up-supported", IPP_TAG_ZERO
))
1915 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-supported", 1);
1917 /* operations-supported */
1918 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1920 /* orientation-requested-default */
1921 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-default", IPP_TAG_ZERO
))
1922 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "orientation-requested-default", 0);
1924 /* orientation-requested-supported */
1925 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-supported", IPP_TAG_ZERO
))
1926 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", 4, orients
);
1928 /* output-bin-default */
1929 if (!ippFindAttribute(printer
->attrs
, "output-bin-default", IPP_TAG_ZERO
))
1930 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
1932 /* output-bin-supported */
1933 if (!ippFindAttribute(printer
->attrs
, "output-bin-supported", IPP_TAG_ZERO
))
1934 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
1936 /* overrides-supported */
1937 if (!ippFindAttribute(printer
->attrs
, "overrides-supported", IPP_TAG_ZERO
))
1938 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1940 /* page-ranges-supported */
1941 if (!ippFindAttribute(printer
->attrs
, "page-ranges-supported", IPP_TAG_ZERO
))
1942 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1944 /* pages-per-minute */
1945 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1946 "pages-per-minute", ppm
);
1948 /* pages-per-minute-color */
1950 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1951 "pages-per-minute-color", ppm_color
);
1953 /* pdl-override-supported */
1954 if (!ippFindAttribute(printer
->attrs
, "pdl-override-supported", IPP_TAG_ZERO
))
1955 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
1957 /* print-color-mode-default */
1958 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-default", IPP_TAG_ZERO
))
1959 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1961 /* print-color-mode-supported */
1962 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-supported", IPP_TAG_ZERO
))
1963 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
);
1965 /* print-content-optimize-default */
1966 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-default", IPP_TAG_ZERO
))
1967 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1969 /* print-content-optimize-supported */
1970 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-supported", IPP_TAG_ZERO
))
1971 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
1973 /* print-rendering-intent-default */
1974 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-default", IPP_TAG_ZERO
))
1975 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
1977 /* print-rendering-intent-supported */
1978 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-supported", IPP_TAG_ZERO
))
1979 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
1981 /* print-quality-default */
1982 if (!ippFindAttribute(printer
->attrs
, "print-quality-default", IPP_TAG_ZERO
))
1983 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
1985 /* print-quality-supported */
1986 if (!ippFindAttribute(printer
->attrs
, "print-quality-supported", IPP_TAG_ZERO
))
1987 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
);
1989 /* printer-device-id */
1990 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1991 "printer-device-id", NULL
, device_id
);
1993 /* printer-get-attributes-supported */
1994 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1996 /* printer-geo-location */
1997 if (!ippFindAttribute(printer
->attrs
, "printer-geo-location", IPP_TAG_ZERO
))
1998 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
2001 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
2002 "printer-icons", NULL
, icons
);
2004 /* printer-is-accepting-jobs */
2005 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
2008 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
2010 /* printer-location */
2011 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2012 "printer-location", NULL
, location
);
2014 /* printer-make-and-model */
2015 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2016 "printer-make-and-model", NULL
, make_model
);
2018 /* printer-mandatory-job-attributes */
2019 if (pin
&& !ippFindAttribute(printer
->attrs
, "", IPP_TAG_ZERO
))
2021 static const char * const names
[] =
2024 "job-accounting-user-id",
2028 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2029 "printer-mandatory-job-attributes",
2030 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
2033 /* printer-more-info */
2034 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
2037 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
2039 /* printer-organization */
2040 if (!ippFindAttribute(printer
->attrs
, "printer-organization", IPP_TAG_ZERO
))
2041 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
2043 /* printer-organizational-unit */
2044 if (!ippFindAttribute(printer
->attrs
, "printer-organizational-unit", IPP_TAG_ZERO
))
2045 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
2047 /* printer-resolution-default */
2048 if (!ippFindAttribute(printer
->attrs
, "printer-resolution-default", IPP_TAG_ZERO
))
2049 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
2051 /* printer-resolution-supported */
2052 if (!ippFindAttribute(printer
->attrs
, "printer-resolutions-supported", IPP_TAG_ZERO
))
2053 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
2055 /* printer-supply-description */
2056 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
);
2058 /* printer-supply-info-uri */
2059 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
2061 /* printer-uri-supported */
2066 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", 2, NULL
, (const char **)uris
);
2069 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
2070 #endif /* HAVE_SSL */
2073 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
2074 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
2076 /* pwg-raster-document-xxx-supported */
2077 for (i
= 0; i
< num_formats
; i
++)
2078 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
2081 if (i
< num_formats
)
2083 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-resolution-supported", IPP_TAG_ZERO
))
2084 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
);
2085 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-sheet-back", IPP_TAG_ZERO
))
2086 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "pwg-raster-document-sheet-back", NULL
, "normal");
2087 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-type-supported", IPP_TAG_ZERO
))
2088 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
);
2091 /* reference-uri-scheme-supported */
2092 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
);
2095 if (!ippFindAttribute(printer
->attrs
, "sides-default", IPP_TAG_ZERO
))
2096 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
2098 /* sides-supported */
2099 if (!ippFindAttribute(printer
->attrs
, "sides-supported", IPP_TAG_ZERO
))
2100 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
2103 for (i
= 0; i
< num_formats
; i
++)
2104 if (!strcasecmp(formats
[i
], "image/urf"))
2107 if (i
< num_formats
&& !ippFindAttribute(printer
->attrs
, "urf-supported", IPP_TAG_ZERO
))
2108 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2110 /* uri-authentication-supported */
2112 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_supported
);
2114 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "none");
2115 #endif /* HAVE_SSL */
2117 /* uri-security-supported */
2119 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", 2, NULL
, uri_security_supported
);
2121 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", NULL
, "none");
2122 #endif /* HAVE_SSL */
2124 /* which-jobs-supported */
2125 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
);
2129 debug_attributes("Printer", printer
->attrs
, 0);
2132 * Register the printer with Bonjour...
2135 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2146 * If we get here we were unable to create the printer...
2151 delete_printer(printer
);
2157 * 'debug_attributes()' - Print attributes in a request or response.
2161 debug_attributes(const char *title
, /* I - Title */
2162 ipp_t
*ipp
, /* I - Request/response */
2163 int type
) /* I - 0 = object, 1 = request, 2 = response */
2165 ipp_tag_t group_tag
; /* Current group */
2166 ipp_attribute_t
*attr
; /* Current attribute */
2167 char buffer
[2048]; /* String buffer for value */
2168 int major
, minor
; /* Version */
2174 fprintf(stderr
, "%s:\n", title
);
2175 major
= ippGetVersion(ipp
, &minor
);
2176 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2178 fprintf(stderr
, " operation-id=%s(%04x)\n",
2179 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2181 fprintf(stderr
, " status-code=%s(%04x)\n",
2182 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2183 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2185 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2187 attr
= ippNextAttribute(ipp
))
2189 if (ippGetGroupTag(attr
) != group_tag
)
2191 group_tag
= ippGetGroupTag(attr
);
2192 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2195 if (ippGetName(attr
))
2197 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2198 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2199 ippGetCount(attr
) > 1 ? "1setOf " : "",
2200 ippTagString(ippGetValueTag(attr
)), buffer
);
2207 * 'delete_client()' - Close the socket and free all memory used by a client
2212 delete_client(_ipp_client_t
*client
) /* I - Client */
2215 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2218 * Flush pending writes before closing...
2221 httpFlushWrite(client
->http
);
2227 httpClose(client
->http
);
2229 ippDelete(client
->request
);
2230 ippDelete(client
->response
);
2237 * 'delete_job()' - Remove from the printer and free all memory used by a job
2242 delete_job(_ipp_job_t
*job
) /* I - Job */
2245 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2247 ippDelete(job
->attrs
);
2252 unlink(job
->filename
);
2254 free(job
->filename
);
2262 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2263 * used by a printer object.
2267 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2269 if (printer
->ipv4
>= 0)
2270 close(printer
->ipv4
);
2272 if (printer
->ipv6
>= 0)
2273 close(printer
->ipv6
);
2276 if (printer
->printer_ref
)
2277 DNSServiceRefDeallocate(printer
->printer_ref
);
2278 if (printer
->ipp_ref
)
2279 DNSServiceRefDeallocate(printer
->ipp_ref
);
2280 if (printer
->ipps_ref
)
2281 DNSServiceRefDeallocate(printer
->ipps_ref
);
2282 if (printer
->http_ref
)
2283 DNSServiceRefDeallocate(printer
->http_ref
);
2284 #elif defined(HAVE_AVAHI)
2285 avahi_threaded_poll_lock(DNSSDMaster
);
2287 if (printer
->printer_ref
)
2288 avahi_entry_group_free(printer
->printer_ref
);
2289 if (printer
->ipp_ref
)
2290 avahi_entry_group_free(printer
->ipp_ref
);
2291 if (printer
->ipps_ref
)
2292 avahi_entry_group_free(printer
->ipps_ref
);
2293 if (printer
->http_ref
)
2294 avahi_entry_group_free(printer
->http_ref
);
2296 avahi_threaded_poll_unlock(DNSSDMaster
);
2297 #endif /* HAVE_DNSSD */
2299 if (printer
->dnssd_name
)
2300 free(printer
->dnssd_name
);
2302 free(printer
->name
);
2304 free(printer
->icon
);
2305 if (printer
->command
)
2306 free(printer
->command
);
2307 if (printer
->directory
)
2308 free(printer
->directory
);
2309 if (printer
->hostname
)
2310 free(printer
->hostname
);
2314 ippDelete(printer
->attrs
);
2315 cupsArrayDelete(printer
->jobs
);
2323 * 'dnssd_callback()' - Handle Bonjour registration events.
2326 static void DNSSD_API
2328 DNSServiceRef sdRef
, /* I - Service reference */
2329 DNSServiceFlags flags
, /* I - Status flags */
2330 DNSServiceErrorType errorCode
, /* I - Error, if any */
2331 const char *name
, /* I - Service name */
2332 const char *regtype
, /* I - Service type */
2333 const char *domain
, /* I - Domain for service */
2334 _ipp_printer_t
*printer
) /* I - Printer */
2342 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2343 regtype
, (int)errorCode
);
2346 else if (strcasecmp(name
, printer
->dnssd_name
))
2349 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2351 /* No lock needed since only the main thread accesses/changes this */
2352 free(printer
->dnssd_name
);
2353 printer
->dnssd_name
= strdup(name
);
2358 #elif defined(HAVE_AVAHI)
2360 * 'dnssd_callback()' - Handle Bonjour registration events.
2365 AvahiEntryGroup
*srv
, /* I - Service */
2366 AvahiEntryGroupState state
, /* I - Registration state */
2367 void *context
) /* I - Printer */
2376 * 'dnssd_client_cb()' - Client callback for Avahi.
2378 * Called whenever the client or server state changes...
2383 AvahiClient
*c
, /* I - Client */
2384 AvahiClientState state
, /* I - Current state */
2385 void *userdata
) /* I - User data (unused) */
2395 fprintf(stderr
, "Ignore Avahi state %d.\n", state
);
2398 case AVAHI_CLIENT_FAILURE
:
2399 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
2401 fputs("Avahi server crashed, exiting.\n", stderr
);
2407 #endif /* HAVE_DNSSD */
2411 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2418 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2420 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2424 #elif defined(HAVE_AVAHI)
2425 int error
; /* Error code, if any */
2427 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2429 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2433 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2435 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2439 avahi_threaded_poll_start(DNSSDMaster
);
2440 #endif /* HAVE_DNSSD */
2445 * 'filter_cb()' - Filter printer attributes based on the requested array.
2448 static int /* O - 1 to copy, 0 to ignore */
2449 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2450 ipp_t
*dst
, /* I - Destination (unused) */
2451 ipp_attribute_t
*attr
) /* I - Source attribute */
2454 * Filter attributes as needed...
2457 #ifndef WIN32 /* Avoid MS compiler bug */
2461 ipp_tag_t group
= ippGetGroupTag(attr
);
2462 const char *name
= ippGetName(attr
);
2464 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
)))
2467 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2472 * 'find_job()' - Find a job specified in a request.
2475 static _ipp_job_t
* /* O - Job or NULL */
2476 find_job(_ipp_client_t
*client
) /* I - Client */
2478 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2479 _ipp_job_t key
, /* Job search key */
2480 *job
; /* Matching job, if any */
2483 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2485 const char *uri
= ippGetString(attr
, 0, NULL
);
2487 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2488 uri
[client
->printer
->urilen
] == '/')
2489 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2493 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2494 key
.id
= ippGetInteger(attr
, 0);
2496 _cupsRWLockRead(&(client
->printer
->rwlock
));
2497 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2498 _cupsRWUnlock(&(client
->printer
->rwlock
));
2505 * 'get_collection()' - Get a collection value from a file.
2508 static ipp_t
* /* O - Collection value */
2509 get_collection(FILE *fp
, /* I - File to read from */
2510 const char *filename
, /* I - Attributes filename */
2511 int *linenum
) /* IO - Line number */
2513 char token
[1024], /* Token from file */
2514 attr
[128]; /* Attribute name */
2515 ipp_tag_t value
; /* Current value type */
2516 ipp_t
*col
= ippNew(); /* Collection value */
2517 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
2520 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
2522 if (!strcmp(token
, "}"))
2524 else if (!strcmp(token
, "{") && lastcol
)
2527 * Another collection value
2530 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2531 /* Collection value */
2534 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
2538 else if (!_cups_strcasecmp(token
, "MEMBER"))
2546 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2548 fprintf(stderr
, "ippserver: Missing MEMBER value tag on line %d of \"%s\".\n", *linenum
, filename
);
2552 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
2554 fprintf(stderr
, "ippserver: Bad MEMBER value tag \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2558 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
2560 fprintf(stderr
, "ippserver: Missing MEMBER name on line %d of \"%s\".\n", *linenum
, filename
);
2564 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2566 fprintf(stderr
, "ippserver: Missing MEMBER value on line %d of \"%s\".\n", *linenum
, filename
);
2572 case IPP_TAG_BOOLEAN
:
2573 if (!_cups_strcasecmp(token
, "true"))
2574 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
2576 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
2579 case IPP_TAG_INTEGER
:
2581 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
2584 case IPP_TAG_RESOLUTION
:
2586 int xres
, /* X resolution */
2587 yres
; /* Y resolution */
2588 char units
[6]; /* Units */
2590 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
2591 (_cups_strcasecmp(units
, "dpi") &&
2592 _cups_strcasecmp(units
, "dpc") &&
2593 _cups_strcasecmp(units
, "dpcm") &&
2594 _cups_strcasecmp(units
, "other")))
2596 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2600 if (!_cups_strcasecmp(units
, "dpi"))
2601 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
2602 else if (!_cups_strcasecmp(units
, "dpc") ||
2603 !_cups_strcasecmp(units
, "dpcm"))
2604 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
2606 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
2610 case IPP_TAG_RANGE
:
2612 int lowers
[4], /* Lower value */
2613 uppers
[4], /* Upper values */
2614 num_vals
; /* Number of values */
2617 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
2618 lowers
+ 0, uppers
+ 0,
2619 lowers
+ 1, uppers
+ 1,
2620 lowers
+ 2, uppers
+ 2,
2621 lowers
+ 3, uppers
+ 3);
2623 if ((num_vals
& 1) || num_vals
== 0)
2625 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2629 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
2634 case IPP_TAG_BEGIN_COLLECTION
:
2635 if (!strcmp(token
, "{"))
2637 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2638 /* Collection value */
2642 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
2650 fprintf(stderr
, "ippserver: Bad collection value on line %d of \"%s\".\n", *linenum
, filename
);
2654 case IPP_TAG_STRING
:
2655 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
2659 if (!strchr(token
, ','))
2660 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
2664 * Multiple string values...
2667 int num_values
; /* Number of values */
2668 char *values
[100], /* Values */
2669 *ptr
; /* Pointer to next value */
2675 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
2678 values
[num_values
] = ptr
;
2680 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
2684 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
2685 NULL
, (const char **)values
);
2695 * If we get here there was a parse error; free memory and return.
2707 * 'get_token()' - Get a token from a file.
2710 static char * /* O - Token from file or NULL on EOF */
2711 get_token(FILE *fp
, /* I - File to read from */
2712 char *buf
, /* I - Buffer to read into */
2713 int buflen
, /* I - Length of buffer */
2714 int *linenum
) /* IO - Current line number */
2716 int ch
, /* Character from file */
2717 quote
; /* Quoting character */
2718 char *bufptr
, /* Pointer into buffer */
2719 *bufend
; /* End of buffer */
2725 * Skip whitespace...
2728 while (isspace(ch
= getc(fp
)))
2740 else if (ch
== '\'' || ch
== '\"')
2743 * Quoted text or regular expression...
2748 bufend
= buf
+ buflen
- 1;
2750 while ((ch
= getc(fp
)) != EOF
)
2755 * Escape next character...
2758 if (bufptr
< bufend
)
2759 *bufptr
++ = (char)ch
;
2761 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
2762 *bufptr
++ = (char)ch
;
2764 else if (ch
== quote
)
2766 else if (bufptr
< bufend
)
2767 *bufptr
++ = (char)ch
;
2780 while ((ch
= getc(fp
)) != EOF
)
2786 else if (ch
== '{' || ch
== '}' || ch
== ',')
2796 * Whitespace delimited text...
2802 bufend
= buf
+ buflen
- 1;
2804 while ((ch
= getc(fp
)) != EOF
)
2805 if (isspace(ch
) || ch
== '#')
2807 else if (bufptr
< bufend
)
2808 *bufptr
++ = (char)ch
;
2812 else if (ch
== '\n')
2824 * 'html_escape()' - Write a HTML-safe string.
2828 html_escape(_ipp_client_t
*client
, /* I - Client */
2829 const char *s
, /* I - String to write */
2830 size_t slen
) /* I - Number of characters to write */
2832 const char *start
, /* Start of segment */
2833 *end
; /* End of string */
2837 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2839 while (*s
&& s
< end
)
2841 if (*s
== '&' || *s
== '<')
2844 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2847 httpWrite2(client
->http
, "&", 5);
2849 httpWrite2(client
->http
, "<", 4);
2858 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2863 * 'html_footer()' - Show the web interface footer.
2865 * This function also writes the trailing 0-length chunk.
2869 html_footer(_ipp_client_t
*client
) /* I - Client */
2875 httpWrite2(client
->http
, "", 0);
2880 * 'html_header()' - Show the web interface header and title.
2884 html_header(_ipp_client_t
*client
, /* I - Client */
2885 const char *title
) /* I - Title */
2891 "<title>%s</title>\n"
2892 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2893 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2894 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2895 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2897 "body { font-family: sans-serif; margin: 0; }\n"
2898 "div.body { padding: 0px 10px 10px; }\n"
2899 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2900 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2901 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2902 "table.form th { text-align: right; }\n"
2903 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2904 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2905 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2906 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2907 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2908 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2909 "table.nav td { margin: 0; text-align: center; }\n"
2910 "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"
2911 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2912 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2913 "td.nav:hover { background: #666; color: #fff; }\n"
2914 "td.nav:active { background: #000; color: #ff0; }\n"
2918 "<table class=\"nav\"><tr>"
2919 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2920 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2921 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2923 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2928 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2932 html_printf(_ipp_client_t
*client
, /* I - Client */
2933 const char *format
, /* I - Printf-style format string */
2934 ...) /* I - Additional arguments as needed */
2936 va_list ap
; /* Pointer to arguments */
2937 const char *start
; /* Start of string */
2938 char size
, /* Size character (h, l, L) */
2939 type
; /* Format type character */
2940 int width
, /* Width of field */
2941 prec
; /* Number of characters of precision */
2942 char tformat
[100], /* Temporary format string for sprintf() */
2943 *tptr
, /* Pointer into temporary format */
2944 temp
[1024]; /* Buffer for formatted numbers */
2945 char *s
; /* Pointer to string */
2949 * Loop through the format string, formatting as needed...
2952 va_start(ap
, format
);
2960 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2963 *tptr
++ = *format
++;
2967 httpWrite2(client
->http
, "%", 1);
2972 else if (strchr(" -+#\'", *format
))
2973 *tptr
++ = *format
++;
2978 * Get width from argument...
2982 width
= va_arg(ap
, int);
2984 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2985 tptr
+= strlen(tptr
);
2991 while (isdigit(*format
& 255))
2993 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2996 width
= width
* 10 + *format
++ - '0';
3002 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3010 * Get precision from argument...
3014 prec
= va_arg(ap
, int);
3016 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
3017 tptr
+= strlen(tptr
);
3023 while (isdigit(*format
& 255))
3025 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3028 prec
= prec
* 10 + *format
++ - '0';
3033 if (*format
== 'l' && format
[1] == 'l')
3037 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
3045 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
3047 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3062 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3071 case 'E' : /* Floating point formats */
3076 if ((size_t)(width
+ 2) > sizeof(temp
))
3079 sprintf(temp
, tformat
, va_arg(ap
, double));
3081 httpWrite2(client
->http
, temp
, strlen(temp
));
3084 case 'B' : /* Integer formats */
3092 if ((size_t)(width
+ 2) > sizeof(temp
))
3095 # ifdef HAVE_LONG_LONG
3097 sprintf(temp
, tformat
, va_arg(ap
, long long));
3099 # endif /* HAVE_LONG_LONG */
3101 sprintf(temp
, tformat
, va_arg(ap
, long));
3103 sprintf(temp
, tformat
, va_arg(ap
, int));
3105 httpWrite2(client
->http
, temp
, strlen(temp
));
3108 case 'p' : /* Pointer value */
3109 if ((size_t)(width
+ 2) > sizeof(temp
))
3112 sprintf(temp
, tformat
, va_arg(ap
, void *));
3114 httpWrite2(client
->http
, temp
, strlen(temp
));
3117 case 'c' : /* Character or character array */
3120 temp
[0] = (char)va_arg(ap
, int);
3122 html_escape(client
, temp
, 1);
3125 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
3128 case 's' : /* String */
3129 if ((s
= va_arg(ap
, char *)) == NULL
)
3132 html_escape(client
, s
, strlen(s
));
3141 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
3148 * 'ipp_cancel_job()' - Cancel a job.
3152 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
3154 _ipp_job_t
*job
; /* Job information */
3161 if ((job
= find_job(client
)) == NULL
)
3163 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3168 * See if the job is already completed, canceled, or aborted; if so,
3169 * we can't cancel...
3174 case IPP_JSTATE_CANCELED
:
3175 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3176 "Job #%d is already canceled - can\'t cancel.", job
->id
);
3179 case IPP_JSTATE_ABORTED
:
3180 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3181 "Job #%d is already aborted - can\'t cancel.", job
->id
);
3184 case IPP_JSTATE_COMPLETED
:
3185 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3186 "Job #%d is already completed - can\'t cancel.", job
->id
);
3194 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3196 if (job
->state
== IPP_JSTATE_PROCESSING
||
3197 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
3201 job
->state
= IPP_JSTATE_CANCELED
;
3202 job
->completed
= time(NULL
);
3205 _cupsRWUnlock(&(client
->printer
->rwlock
));
3207 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3214 * 'ipp_close_job()' - Close an open job.
3218 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
3220 _ipp_job_t
*job
; /* Job information */
3227 if ((job
= find_job(client
)) == NULL
)
3229 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3234 * See if the job is already completed, canceled, or aborted; if so,
3235 * we can't cancel...
3240 case IPP_JSTATE_CANCELED
:
3241 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3242 "Job #%d is canceled - can\'t close.", job
->id
);
3245 case IPP_JSTATE_ABORTED
:
3246 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3247 "Job #%d is aborted - can\'t close.", job
->id
);
3250 case IPP_JSTATE_COMPLETED
:
3251 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3252 "Job #%d is completed - can\'t close.", job
->id
);
3255 case IPP_JSTATE_PROCESSING
:
3256 case IPP_JSTATE_STOPPED
:
3257 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3258 "Job #%d is already closed.", job
->id
);
3262 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3269 * 'ipp_create_job()' - Create a job object.
3273 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
3275 _ipp_job_t
*job
; /* New job */
3276 cups_array_t
*ra
; /* Attributes to send in response */
3280 * Validate print job attributes...
3283 if (!valid_job_attributes(client
))
3285 httpFlush(client
->http
);
3290 * Do we have a file to print?
3293 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3295 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3296 "Unexpected document data following request.");
3304 if ((job
= create_job(client
)) == NULL
)
3306 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3307 "Currently printing another job.");
3312 * Return the job info...
3315 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3317 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3318 cupsArrayAdd(ra
, "job-id");
3319 cupsArrayAdd(ra
, "job-state");
3320 cupsArrayAdd(ra
, "job-state-message");
3321 cupsArrayAdd(ra
, "job-state-reasons");
3322 cupsArrayAdd(ra
, "job-uri");
3324 copy_job_attributes(client
, job
, ra
);
3325 cupsArrayDelete(ra
);
3330 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3334 ipp_get_job_attributes(
3335 _ipp_client_t
*client
) /* I - Client */
3337 _ipp_job_t
*job
; /* Job */
3338 cups_array_t
*ra
; /* requested-attributes */
3341 if ((job
= find_job(client
)) == NULL
)
3343 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3347 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3349 ra
= ippCreateRequestedArray(client
->request
);
3350 copy_job_attributes(client
, job
, ra
);
3351 cupsArrayDelete(ra
);
3356 * 'ipp_get_jobs()' - Get a list of job objects.
3360 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
3362 ipp_attribute_t
*attr
; /* Current attribute */
3363 const char *which_jobs
= NULL
;
3364 /* which-jobs values */
3365 int job_comparison
; /* Job comparison */
3366 ipp_jstate_t job_state
; /* job-state value */
3367 int first_job_id
, /* First job ID */
3368 limit
, /* Maximum number of jobs to return */
3369 count
; /* Number of jobs that match */
3370 const char *username
; /* Username */
3371 _ipp_job_t
*job
; /* Current job pointer */
3372 cups_array_t
*ra
; /* Requested attributes array */
3376 * See if the "which-jobs" attribute have been specified...
3379 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3380 IPP_TAG_KEYWORD
)) != NULL
)
3382 which_jobs
= ippGetString(attr
, 0, NULL
);
3383 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3386 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3388 job_comparison
= -1;
3389 job_state
= IPP_JSTATE_STOPPED
;
3391 else if (!strcmp(which_jobs
, "completed"))
3394 job_state
= IPP_JSTATE_CANCELED
;
3396 else if (!strcmp(which_jobs
, "aborted"))
3399 job_state
= IPP_JSTATE_ABORTED
;
3401 else if (!strcmp(which_jobs
, "all"))
3404 job_state
= IPP_JSTATE_PENDING
;
3406 else if (!strcmp(which_jobs
, "canceled"))
3409 job_state
= IPP_JSTATE_CANCELED
;
3411 else if (!strcmp(which_jobs
, "pending"))
3414 job_state
= IPP_JSTATE_PENDING
;
3416 else if (!strcmp(which_jobs
, "pending-held"))
3419 job_state
= IPP_JSTATE_HELD
;
3421 else if (!strcmp(which_jobs
, "processing"))
3424 job_state
= IPP_JSTATE_PROCESSING
;
3426 else if (!strcmp(which_jobs
, "processing-stopped"))
3429 job_state
= IPP_JSTATE_STOPPED
;
3433 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3434 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3435 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3436 "which-jobs", NULL
, which_jobs
);
3441 * See if they want to limit the number of jobs reported...
3444 if ((attr
= ippFindAttribute(client
->request
, "limit",
3445 IPP_TAG_INTEGER
)) != NULL
)
3447 limit
= ippGetInteger(attr
, 0);
3449 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3454 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3455 IPP_TAG_INTEGER
)) != NULL
)
3457 first_job_id
= ippGetInteger(attr
, 0);
3459 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3466 * See if we only want to see jobs for a specific user...
3471 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3472 IPP_TAG_BOOLEAN
)) != NULL
)
3474 int my_jobs
= ippGetBoolean(attr
, 0);
3476 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3477 my_jobs
? "true" : "false");
3481 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3482 IPP_TAG_NAME
)) == NULL
)
3484 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3485 "Need requesting-user-name with my-jobs.");
3489 username
= ippGetString(attr
, 0, NULL
);
3491 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3492 client
->hostname
, username
);
3497 * OK, build a list of jobs for this printer...
3500 ra
= ippCreateRequestedArray(client
->request
);
3502 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3504 _cupsRWLockRead(&(client
->printer
->rwlock
));
3506 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3507 (limit
<= 0 || count
< limit
) && job
;
3508 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3511 * Filter out jobs that don't match...
3514 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3515 (job_comparison
== 0 && job
->state
!= job_state
) ||
3516 (job_comparison
> 0 && job
->state
< job_state
) ||
3517 job
->id
< first_job_id
||
3518 (username
&& job
->username
&&
3519 strcasecmp(username
, job
->username
)))
3523 ippAddSeparator(client
->response
);
3526 copy_job_attributes(client
, job
, ra
);
3529 cupsArrayDelete(ra
);
3531 _cupsRWUnlock(&(client
->printer
->rwlock
));
3536 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3540 ipp_get_printer_attributes(
3541 _ipp_client_t
*client
) /* I - Client */
3543 cups_array_t
*ra
; /* Requested attributes array */
3544 _ipp_printer_t
*printer
; /* Printer */
3548 * Send the attributes...
3551 ra
= ippCreateRequestedArray(client
->request
);
3552 printer
= client
->printer
;
3554 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3556 _cupsRWLockRead(&(printer
->rwlock
));
3558 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3559 IPP_TAG_CUPS_CONST
);
3561 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3563 int i
, /* Looping var */
3564 num_ready
= 0; /* Number of ready media */
3565 ipp_t
*ready
[3]; /* Ready media */
3567 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3569 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
3570 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);
3572 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);
3574 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3575 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);
3576 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3578 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
3579 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);
3581 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);
3586 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3587 for (i
= 0; i
< num_ready
; i
++)
3588 ippDelete(ready
[i
]);
3591 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3594 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3596 int num_ready
= 0; /* Number of ready media */
3597 const char *ready
[3]; /* Ready media */
3599 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3600 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3602 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3603 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3605 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3606 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3609 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3611 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3614 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3615 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3617 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3618 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3620 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3621 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3624 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3625 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3626 "printer-state", printer
->state
);
3628 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3629 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3631 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3632 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3634 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3636 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3638 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3641 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3643 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3644 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3645 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3646 "printer-state-reasons", NULL
, "none");
3649 int num_reasons
= 0;/* Number of reasons */
3650 const char *reasons
[32]; /* Reason strings */
3652 if (printer
->state_reasons
& _IPP_PREASON_OTHER
)
3653 reasons
[num_reasons
++] = "other";
3654 if (printer
->state_reasons
& _IPP_PREASON_COVER_OPEN
)
3655 reasons
[num_reasons
++] = "cover-open";
3656 if (printer
->state_reasons
& _IPP_PREASON_INPUT_TRAY_MISSING
)
3657 reasons
[num_reasons
++] = "input-tray-missing";
3658 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_EMPTY
)
3659 reasons
[num_reasons
++] = "marker-supply-empty-warning";
3660 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_LOW
)
3661 reasons
[num_reasons
++] = "marker-supply-low-report";
3662 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
)
3663 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
3664 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_FULL
)
3665 reasons
[num_reasons
++] = "marker-waste-full-warning";
3666 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
3667 reasons
[num_reasons
++] = "media-empty-warning";
3668 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_JAM
)
3669 reasons
[num_reasons
++] = "media-jam-warning";
3670 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_LOW
)
3671 reasons
[num_reasons
++] = "media-low-report";
3672 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_NEEDED
)
3673 reasons
[num_reasons
++] = "media-needed-report";
3674 if (printer
->state_reasons
& _IPP_PREASON_MOVING_TO_PAUSED
)
3675 reasons
[num_reasons
++] = "moving-to-paused";
3676 if (printer
->state_reasons
& _IPP_PREASON_PAUSED
)
3677 reasons
[num_reasons
++] = "paused";
3678 if (printer
->state_reasons
& _IPP_PREASON_SPOOL_AREA_FULL
)
3679 reasons
[num_reasons
++] = "spool-area-full";
3680 if (printer
->state_reasons
& _IPP_PREASON_TONER_EMPTY
)
3681 reasons
[num_reasons
++] = "toner-empty-warning";
3682 if (printer
->state_reasons
& _IPP_PREASON_TONER_LOW
)
3683 reasons
[num_reasons
++] = "toner-low-report";
3685 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
3686 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3687 "printer-state-reasons", num_reasons
, NULL
, reasons
);
3691 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3693 int i
; /* Looping var */
3694 char buffer
[256]; /* Supply value buffer */
3695 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3696 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3698 for (i
= 0; i
< 5; i
++)
3700 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
]);
3703 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3705 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3709 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3710 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3712 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3713 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3714 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3716 _cupsRWUnlock(&(printer
->rwlock
));
3718 cupsArrayDelete(ra
);
3723 * 'ipp_identify_printer()' - Beep or display a message.
3727 ipp_identify_printer(
3728 _ipp_client_t
*client
) /* I - Client */
3730 /* TODO: Do something */
3732 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3737 * 'ipp_print_job()' - Create a job object with an attached document.
3741 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3743 _ipp_job_t
*job
; /* New job */
3744 char filename
[1024], /* Filename buffer */
3745 buffer
[4096]; /* Copy buffer */
3746 ssize_t bytes
; /* Bytes read */
3747 cups_array_t
*ra
; /* Attributes to send in response */
3751 * Validate print job attributes...
3754 if (!valid_job_attributes(client
))
3756 httpFlush(client
->http
);
3761 * Do we have a file to print?
3764 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3766 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3774 if ((job
= create_job(client
)) == NULL
)
3776 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3777 "Currently printing another job.");
3782 * Create a file for the request data...
3785 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3788 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3790 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3792 job
->state
= IPP_JSTATE_ABORTED
;
3794 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3795 "Unable to create print file: %s", strerror(errno
));
3799 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3801 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3803 int error
= errno
; /* Write error */
3805 job
->state
= IPP_JSTATE_ABORTED
;
3812 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3813 "Unable to write print file: %s", strerror(error
));
3821 * Got an error while reading the print data, so abort this job.
3824 job
->state
= IPP_JSTATE_ABORTED
;
3831 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3832 "Unable to read print file.");
3838 int error
= errno
; /* Write error */
3840 job
->state
= IPP_JSTATE_ABORTED
;
3845 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3846 "Unable to write print file: %s", strerror(error
));
3851 job
->filename
= strdup(filename
);
3852 job
->state
= IPP_JSTATE_PENDING
;
3855 * Process the job...
3858 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3860 job
->state
= IPP_JSTATE_ABORTED
;
3861 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3866 * Return the job info...
3869 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3871 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3872 cupsArrayAdd(ra
, "job-id");
3873 cupsArrayAdd(ra
, "job-state");
3874 cupsArrayAdd(ra
, "job-state-message");
3875 cupsArrayAdd(ra
, "job-state-reasons");
3876 cupsArrayAdd(ra
, "job-uri");
3878 copy_job_attributes(client
, job
, ra
);
3879 cupsArrayDelete(ra
);
3884 * 'ipp_print_uri()' - Create a job object with a referenced document.
3888 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3890 _ipp_job_t
*job
; /* New job */
3891 ipp_attribute_t
*uri
; /* document-uri */
3892 char scheme
[256], /* URI scheme */
3893 userpass
[256], /* Username and password info */
3894 hostname
[256], /* Hostname */
3895 resource
[1024]; /* Resource path */
3896 int port
; /* Port number */
3897 http_uri_status_t uri_status
; /* URI decode status */
3898 http_encryption_t encryption
; /* Encryption to use, if any */
3899 http_t
*http
; /* Connection for http/https URIs */
3900 http_status_t status
; /* Access status for http/https URIs */
3901 int infile
; /* Input file for local file URIs */
3902 char filename
[1024], /* Filename buffer */
3903 buffer
[4096]; /* Copy buffer */
3904 ssize_t bytes
; /* Bytes read */
3905 cups_array_t
*ra
; /* Attributes to send in response */
3906 static const char * const uri_status_strings
[] =
3907 { /* URI decode errors */
3909 "Bad arguments to function.",
3910 "Bad resource in URI.",
3911 "Bad port number in URI.",
3912 "Bad hostname in URI.",
3913 "Bad username in URI.",
3914 "Bad scheme in URI.",
3920 * Validate print job attributes...
3923 if (!valid_job_attributes(client
))
3925 httpFlush(client
->http
);
3930 * Do we have a file to print?
3933 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3935 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3936 "Unexpected document data following request.");
3941 * Do we have a document URI?
3944 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3945 IPP_TAG_URI
)) == NULL
)
3947 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3951 if (ippGetCount(uri
) != 1)
3953 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3954 "Too many document-uri values.");
3958 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3959 scheme
, sizeof(scheme
), userpass
,
3960 sizeof(userpass
), hostname
, sizeof(hostname
),
3961 &port
, resource
, sizeof(resource
));
3962 if (uri_status
< HTTP_URI_STATUS_OK
)
3964 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3965 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3969 if (strcmp(scheme
, "file") &&
3971 strcmp(scheme
, "https") &&
3972 #endif /* HAVE_SSL */
3973 strcmp(scheme
, "http"))
3975 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3976 "URI scheme \"%s\" not supported.", scheme
);
3980 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3982 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3983 "Unable to access URI: %s", strerror(errno
));
3991 if ((job
= create_job(client
)) == NULL
)
3993 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3994 "Currently printing another job.");
3999 * Create a file for the request data...
4002 if (!strcasecmp(job
->format
, "image/jpeg"))
4003 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4004 client
->printer
->directory
, job
->id
);
4005 else if (!strcasecmp(job
->format
, "image/png"))
4006 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4007 client
->printer
->directory
, job
->id
);
4008 else if (!strcasecmp(job
->format
, "application/pdf"))
4009 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4010 client
->printer
->directory
, job
->id
);
4011 else if (!strcasecmp(job
->format
, "application/postscript"))
4012 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4013 client
->printer
->directory
, job
->id
);
4015 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4016 client
->printer
->directory
, job
->id
);
4018 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
4020 job
->state
= IPP_JSTATE_ABORTED
;
4022 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4023 "Unable to create print file: %s", strerror(errno
));
4027 if (!strcmp(scheme
, "file"))
4029 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4031 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4032 "Unable to access URI: %s", strerror(errno
));
4038 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4039 (errno
== EAGAIN
|| errno
== EINTR
))
4041 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4043 int error
= errno
; /* Write error */
4045 job
->state
= IPP_JSTATE_ABORTED
;
4053 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4054 "Unable to write print file: %s", strerror(error
));
4065 if (port
== 443 || !strcmp(scheme
, "https"))
4066 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4068 #endif /* HAVE_SSL */
4069 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4071 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4072 1, 30000, NULL
)) == NULL
)
4074 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4075 "Unable to connect to %s: %s", hostname
,
4076 cupsLastErrorString());
4077 job
->state
= IPP_JSTATE_ABORTED
;
4086 httpClearFields(http
);
4087 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4088 if (httpGet(http
, resource
))
4090 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4091 "Unable to GET URI: %s", strerror(errno
));
4093 job
->state
= IPP_JSTATE_ABORTED
;
4103 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4105 if (status
!= HTTP_STATUS_OK
)
4107 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4108 "Unable to GET URI: %s", httpStatus(status
));
4110 job
->state
= IPP_JSTATE_ABORTED
;
4120 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4122 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4124 int error
= errno
; /* Write error */
4126 job
->state
= IPP_JSTATE_ABORTED
;
4134 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4135 "Unable to write print file: %s", strerror(error
));
4145 int error
= errno
; /* Write error */
4147 job
->state
= IPP_JSTATE_ABORTED
;
4152 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4153 "Unable to write print file: %s", strerror(error
));
4158 job
->filename
= strdup(filename
);
4159 job
->state
= IPP_JSTATE_PENDING
;
4162 * Process the job...
4166 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4168 job
->state
= IPP_JSTATE_ABORTED
;
4169 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4178 * Return the job info...
4181 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4183 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4184 cupsArrayAdd(ra
, "job-id");
4185 cupsArrayAdd(ra
, "job-state");
4186 cupsArrayAdd(ra
, "job-state-reasons");
4187 cupsArrayAdd(ra
, "job-uri");
4189 copy_job_attributes(client
, job
, ra
);
4190 cupsArrayDelete(ra
);
4195 * 'ipp_send_document()' - Add an attached document to a job object created with
4200 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
4202 _ipp_job_t
*job
; /* Job information */
4203 char filename
[1024], /* Filename buffer */
4204 buffer
[4096]; /* Copy buffer */
4205 ssize_t bytes
; /* Bytes read */
4206 ipp_attribute_t
*attr
; /* Current attribute */
4207 cups_array_t
*ra
; /* Attributes to send in response */
4214 if ((job
= find_job(client
)) == NULL
)
4216 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4217 httpFlush(client
->http
);
4222 * See if we already have a document for this job or the job has already
4223 * in a non-pending state...
4226 if (job
->state
> IPP_JSTATE_HELD
)
4228 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4229 "Job is not in a pending state.");
4230 httpFlush(client
->http
);
4233 else if (job
->filename
|| job
->fd
>= 0)
4235 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4236 "Multiple document jobs are not supported.");
4237 httpFlush(client
->http
);
4241 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4242 IPP_TAG_ZERO
)) == NULL
)
4244 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4245 "Missing required last-document attribute.");
4246 httpFlush(client
->http
);
4249 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4250 !ippGetBoolean(attr
, 0))
4252 respond_unsupported(client
, attr
);
4253 httpFlush(client
->http
);
4258 * Validate document attributes...
4261 if (!valid_doc_attributes(client
))
4263 httpFlush(client
->http
);
4267 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
4270 * Get the document format for the job...
4273 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4275 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
4276 job
->format
= ippGetString(attr
, 0, NULL
);
4277 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
4278 job
->format
= ippGetString(attr
, 0, NULL
);
4280 job
->format
= "application/octet-stream";
4283 * Create a file for the request data...
4286 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
4289 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
4291 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4293 _cupsRWUnlock(&(client
->printer
->rwlock
));
4297 job
->state
= IPP_JSTATE_ABORTED
;
4299 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4300 "Unable to create print file: %s", strerror(errno
));
4304 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
4306 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4308 int error
= errno
; /* Write error */
4310 job
->state
= IPP_JSTATE_ABORTED
;
4317 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4318 "Unable to write print file: %s", strerror(error
));
4326 * Got an error while reading the print data, so abort this job.
4329 job
->state
= IPP_JSTATE_ABORTED
;
4336 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4337 "Unable to read print file.");
4343 int error
= errno
; /* Write error */
4345 job
->state
= IPP_JSTATE_ABORTED
;
4350 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4351 "Unable to write print file: %s", strerror(error
));
4355 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4358 job
->filename
= strdup(filename
);
4359 job
->state
= IPP_JSTATE_PENDING
;
4361 _cupsRWUnlock(&(client
->printer
->rwlock
));
4364 * Process the job...
4368 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4370 job
->state
= IPP_JSTATE_ABORTED
;
4371 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4380 * Return the job info...
4383 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4385 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4386 cupsArrayAdd(ra
, "job-id");
4387 cupsArrayAdd(ra
, "job-state");
4388 cupsArrayAdd(ra
, "job-state-reasons");
4389 cupsArrayAdd(ra
, "job-uri");
4391 copy_job_attributes(client
, job
, ra
);
4392 cupsArrayDelete(ra
);
4397 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4402 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
4404 _ipp_job_t
*job
; /* Job information */
4405 ipp_attribute_t
*uri
; /* document-uri */
4406 char scheme
[256], /* URI scheme */
4407 userpass
[256], /* Username and password info */
4408 hostname
[256], /* Hostname */
4409 resource
[1024]; /* Resource path */
4410 int port
; /* Port number */
4411 http_uri_status_t uri_status
; /* URI decode status */
4412 http_encryption_t encryption
; /* Encryption to use, if any */
4413 http_t
*http
; /* Connection for http/https URIs */
4414 http_status_t status
; /* Access status for http/https URIs */
4415 int infile
; /* Input file for local file URIs */
4416 char filename
[1024], /* Filename buffer */
4417 buffer
[4096]; /* Copy buffer */
4418 ssize_t bytes
; /* Bytes read */
4419 ipp_attribute_t
*attr
; /* Current attribute */
4420 cups_array_t
*ra
; /* Attributes to send in response */
4421 static const char * const uri_status_strings
[] =
4422 { /* URI decode errors */
4424 "Bad arguments to function.",
4425 "Bad resource in URI.",
4426 "Bad port number in URI.",
4427 "Bad hostname in URI.",
4428 "Bad username in URI.",
4429 "Bad scheme in URI.",
4438 if ((job
= find_job(client
)) == NULL
)
4440 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4441 httpFlush(client
->http
);
4446 * See if we already have a document for this job or the job has already
4447 * in a non-pending state...
4450 if (job
->state
> IPP_JSTATE_HELD
)
4452 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4453 "Job is not in a pending state.");
4454 httpFlush(client
->http
);
4457 else if (job
->filename
|| job
->fd
>= 0)
4459 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4460 "Multiple document jobs are not supported.");
4461 httpFlush(client
->http
);
4465 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4466 IPP_TAG_ZERO
)) == NULL
)
4468 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4469 "Missing required last-document attribute.");
4470 httpFlush(client
->http
);
4473 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4474 !ippGetBoolean(attr
, 0))
4476 respond_unsupported(client
, attr
);
4477 httpFlush(client
->http
);
4482 * Validate document attributes...
4485 if (!valid_doc_attributes(client
))
4487 httpFlush(client
->http
);
4492 * Do we have a file to print?
4495 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4497 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4498 "Unexpected document data following request.");
4503 * Do we have a document URI?
4506 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4507 IPP_TAG_URI
)) == NULL
)
4509 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4513 if (ippGetCount(uri
) != 1)
4515 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4516 "Too many document-uri values.");
4520 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4521 scheme
, sizeof(scheme
), userpass
,
4522 sizeof(userpass
), hostname
, sizeof(hostname
),
4523 &port
, resource
, sizeof(resource
));
4524 if (uri_status
< HTTP_URI_STATUS_OK
)
4526 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4527 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4531 if (strcmp(scheme
, "file") &&
4533 strcmp(scheme
, "https") &&
4534 #endif /* HAVE_SSL */
4535 strcmp(scheme
, "http"))
4537 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4538 "URI scheme \"%s\" not supported.", scheme
);
4542 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4544 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4545 "Unable to access URI: %s", strerror(errno
));
4550 * Get the document format for the job...
4553 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4555 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4556 IPP_TAG_MIMETYPE
)) != NULL
)
4557 job
->format
= ippGetString(attr
, 0, NULL
);
4559 job
->format
= "application/octet-stream";
4562 * Create a file for the request data...
4565 if (!strcasecmp(job
->format
, "image/jpeg"))
4566 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4567 client
->printer
->directory
, job
->id
);
4568 else if (!strcasecmp(job
->format
, "image/png"))
4569 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4570 client
->printer
->directory
, job
->id
);
4571 else if (!strcasecmp(job
->format
, "application/pdf"))
4572 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4573 client
->printer
->directory
, job
->id
);
4574 else if (!strcasecmp(job
->format
, "application/postscript"))
4575 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4576 client
->printer
->directory
, job
->id
);
4578 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4579 client
->printer
->directory
, job
->id
);
4581 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4583 _cupsRWUnlock(&(client
->printer
->rwlock
));
4587 job
->state
= IPP_JSTATE_ABORTED
;
4589 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4590 "Unable to create print file: %s", strerror(errno
));
4594 if (!strcmp(scheme
, "file"))
4596 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4598 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4599 "Unable to access URI: %s", strerror(errno
));
4605 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4606 (errno
== EAGAIN
|| errno
== EINTR
))
4608 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4610 int error
= errno
; /* Write error */
4612 job
->state
= IPP_JSTATE_ABORTED
;
4620 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4621 "Unable to write print file: %s", strerror(error
));
4632 if (port
== 443 || !strcmp(scheme
, "https"))
4633 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4635 #endif /* HAVE_SSL */
4636 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4638 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4639 1, 30000, NULL
)) == NULL
)
4641 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4642 "Unable to connect to %s: %s", hostname
,
4643 cupsLastErrorString());
4644 job
->state
= IPP_JSTATE_ABORTED
;
4653 httpClearFields(http
);
4654 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4655 if (httpGet(http
, resource
))
4657 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4658 "Unable to GET URI: %s", strerror(errno
));
4660 job
->state
= IPP_JSTATE_ABORTED
;
4670 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4672 if (status
!= HTTP_STATUS_OK
)
4674 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4675 "Unable to GET URI: %s", httpStatus(status
));
4677 job
->state
= IPP_JSTATE_ABORTED
;
4687 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4689 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4691 int error
= errno
; /* Write error */
4693 job
->state
= IPP_JSTATE_ABORTED
;
4701 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4702 "Unable to write print file: %s", strerror(error
));
4712 int error
= errno
; /* Write error */
4714 job
->state
= IPP_JSTATE_ABORTED
;
4719 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4720 "Unable to write print file: %s", strerror(error
));
4724 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4727 job
->filename
= strdup(filename
);
4728 job
->state
= IPP_JSTATE_PENDING
;
4730 _cupsRWUnlock(&(client
->printer
->rwlock
));
4733 * Process the job...
4737 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4739 job
->state
= IPP_JSTATE_ABORTED
;
4740 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4749 * Return the job info...
4752 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4754 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4755 cupsArrayAdd(ra
, "job-id");
4756 cupsArrayAdd(ra
, "job-state");
4757 cupsArrayAdd(ra
, "job-state-reasons");
4758 cupsArrayAdd(ra
, "job-uri");
4760 copy_job_attributes(client
, job
, ra
);
4761 cupsArrayDelete(ra
);
4766 * 'ipp_validate_job()' - Validate job creation attributes.
4770 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4772 if (valid_job_attributes(client
))
4773 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4778 * 'load_attributes()' - Load printer attributes from a file.
4780 * Syntax is based on ipptool format:
4782 * ATTR value-tag name value
4786 load_attributes(const char *filename
, /* I - File to load */
4787 ipp_t
*attrs
) /* I - Printer attributes */
4789 int linenum
= 0; /* Current line number */
4790 FILE *fp
= NULL
; /* Test file */
4791 char attr
[128], /* Attribute name */
4792 token
[1024], /* Token from file */
4793 *tokenptr
; /* Pointer into token */
4794 ipp_tag_t value
; /* Current value type */
4795 ipp_attribute_t
*attrptr
, /* Attribute pointer */
4796 *lastcol
= NULL
; /* Last collection attribute */
4799 if ((fp
= fopen(filename
, "r")) == NULL
)
4801 fprintf(stderr
, "ippserver: Unable to open \"%s\": %s\n", filename
, strerror(errno
));
4805 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
4807 if (!_cups_strcasecmp(token
, "ATTR"))
4813 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4815 fprintf(stderr
, "ippserver: Missing ATTR value tag on line %d of \"%s\".\n", linenum
, filename
);
4819 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
4821 fprintf(stderr
, "ippserver: Bad ATTR value tag \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4825 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
4827 fprintf(stderr
, "ippserver: Missing ATTR name on line %d of \"%s\".\n", linenum
, filename
);
4831 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4833 fprintf(stderr
, "ippserver: Missing ATTR value on line %d of \"%s\".\n", linenum
, filename
);
4841 case IPP_TAG_BOOLEAN
:
4842 if (!_cups_strcasecmp(token
, "true"))
4843 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, 1);
4845 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, (char)atoi(token
));
4848 case IPP_TAG_INTEGER
:
4850 if (!strchr(token
, ','))
4851 attrptr
= ippAddInteger(attrs
, IPP_TAG_PRINTER
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
4854 int values
[100], /* Values */
4855 num_values
= 1; /* Number of values */
4857 values
[0] = (int)strtol(token
, &tokenptr
, 10);
4858 while (tokenptr
&& *tokenptr
&&
4859 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
4861 if (*tokenptr
== ',')
4863 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
4866 values
[num_values
] = (int)strtol(tokenptr
, &tokenptr
, 0);
4870 attrptr
= ippAddIntegers(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, values
);
4873 if (!tokenptr
|| *tokenptr
)
4875 fprintf(stderr
, "ippserver: Bad %s value \"%s\" on line %d of \"%s\".\n", ippTagString(value
), token
, linenum
, filename
);
4880 case IPP_TAG_RESOLUTION
:
4882 int xres
, /* X resolution */
4883 yres
; /* Y resolution */
4884 ipp_res_t units
; /* Units */
4885 char *start
, /* Start of value */
4886 *ptr
, /* Pointer into value */
4887 *next
= NULL
; /* Next value */
4889 for (start
= token
; start
; start
= next
)
4891 xres
= yres
= (int)strtol(start
, (char **)&ptr
, 10);
4892 if (ptr
> start
&& xres
> 0)
4895 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
4898 if (ptr
&& (next
= strchr(ptr
, ',')) != NULL
)
4901 if (ptr
<= start
|| xres
<= 0 || yres
<= 0 || !ptr
||
4902 (_cups_strcasecmp(ptr
, "dpi") &&
4903 _cups_strcasecmp(ptr
, "dpc") &&
4904 _cups_strcasecmp(ptr
, "dpcm") &&
4905 _cups_strcasecmp(ptr
, "other")))
4907 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4911 if (!_cups_strcasecmp(ptr
, "dpc") || !_cups_strcasecmp(ptr
, "dpcm"))
4912 units
= IPP_RES_PER_CM
;
4914 units
= IPP_RES_PER_INCH
;
4917 ippSetResolution(attrs
, &attrptr
, ippGetCount(attrptr
), units
, xres
, yres
);
4919 attrptr
= ippAddResolution(attrs
, IPP_TAG_PRINTER
, attr
, units
, xres
, yres
);
4924 case IPP_TAG_RANGE
:
4926 int lowers
[4], /* Lower value */
4927 uppers
[4], /* Upper values */
4928 num_vals
; /* Number of values */
4931 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
4932 lowers
+ 0, uppers
+ 0,
4933 lowers
+ 1, uppers
+ 1,
4934 lowers
+ 2, uppers
+ 2,
4935 lowers
+ 3, uppers
+ 3);
4937 if ((num_vals
& 1) || num_vals
== 0)
4939 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", token
, linenum
, filename
);
4943 attrptr
= ippAddRanges(attrs
, IPP_TAG_PRINTER
, attr
, num_vals
/ 2, lowers
,
4948 case IPP_TAG_BEGIN_COLLECTION
:
4949 if (!strcmp(token
, "{"))
4951 ipp_t
*col
= get_collection(fp
, filename
, &linenum
);
4952 /* Collection value */
4956 attrptr
= lastcol
= ippAddCollection(attrs
, IPP_TAG_PRINTER
, attr
, col
);
4964 fprintf(stderr
, "ippserver: Bad ATTR collection value on line %d of \"%s\".\n", linenum
, filename
);
4970 ipp_t
*col
; /* Collection value */
4971 long pos
= ftell(fp
); /* Save position of file */
4973 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4976 if (strcmp(token
, ","))
4978 fseek(fp
, pos
, SEEK_SET
);
4982 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
4984 fprintf(stderr
, "ippserver: Unexpected \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4988 if ((col
= get_collection(fp
, filename
, &linenum
)) == NULL
)
4991 ippSetCollection(attrs
, &attrptr
, ippGetCount(attrptr
), col
);
4994 while (!strcmp(token
, "{"));
4997 case IPP_TAG_STRING
:
4998 attrptr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, attr
, token
, (int)strlen(token
));
5002 fprintf(stderr
, "ippserver: Unsupported ATTR value tag %s on line %d of \"%s\".\n", ippTagString(value
), linenum
, filename
);
5005 case IPP_TAG_TEXTLANG
:
5006 case IPP_TAG_NAMELANG
:
5009 case IPP_TAG_KEYWORD
:
5011 case IPP_TAG_URISCHEME
:
5012 case IPP_TAG_CHARSET
:
5013 case IPP_TAG_LANGUAGE
:
5014 case IPP_TAG_MIMETYPE
:
5015 if (!strchr(token
, ','))
5016 attrptr
= ippAddString(attrs
, IPP_TAG_PRINTER
, value
, attr
, NULL
, token
);
5020 * Multiple string values...
5023 int num_values
; /* Number of values */
5024 char *values
[100], /* Values */
5025 *ptr
; /* Pointer to next value */
5031 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
5033 if (ptr
> token
&& ptr
[-1] == '\\')
5034 _cups_strcpy(ptr
- 1, ptr
);
5038 values
[num_values
] = ptr
;
5040 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
5045 attrptr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, NULL
, (const char **)values
);
5052 fprintf(stderr
, "ippserver: Unable to add attribute on line %d of \"%s\": %s\n", linenum
, filename
, cupsLastErrorString());
5058 fprintf(stderr
, "ippserver: Unknown directive \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
5068 * 'parse_options()' - Parse URL options into CUPS options.
5070 * The client->options string is destroyed by this function.
5073 static int /* O - Number of options */
5074 parse_options(_ipp_client_t
*client
, /* I - Client */
5075 cups_option_t
**options
) /* O - Options */
5077 char *name
, /* Name */
5079 *next
; /* Next name=value pair */
5080 int num_options
= 0; /* Number of options */
5085 for (name
= client
->options
; name
&& *name
; name
= next
)
5087 if ((value
= strchr(name
, '=')) == NULL
)
5091 if ((next
= strchr(value
, '&')) != NULL
)
5094 num_options
= cupsAddOption(name
, value
, num_options
, options
);
5097 return (num_options
);
5102 * 'process_client()' - Process client requests on a thread.
5105 static void * /* O - Exit status */
5106 process_client(_ipp_client_t
*client
) /* I - Client */
5109 * Loop until we are out of requests or timeout (30 seconds)...
5113 int first_time
= 1; /* First time request? */
5114 #endif /* HAVE_SSL */
5116 while (httpWait(client
->http
, 30000))
5122 * See if we need to negotiate a TLS connection...
5125 char buf
[1]; /* First byte from client */
5127 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
5129 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
5131 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
5133 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5137 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5142 #endif /* HAVE_SSL */
5144 if (!process_http(client
))
5149 * Close the conection to the client and return...
5152 delete_client(client
);
5159 * 'process_http()' - Process a HTTP request.
5162 int /* O - 1 on success, 0 on failure */
5163 process_http(_ipp_client_t
*client
) /* I - Client connection */
5165 char uri
[1024]; /* URI */
5166 http_state_t http_state
; /* HTTP state */
5167 http_status_t http_status
; /* HTTP status */
5168 ipp_state_t ipp_state
; /* State of IPP transfer */
5169 char scheme
[32], /* Method/scheme */
5170 userpass
[128], /* Username:password */
5171 hostname
[HTTP_MAX_HOST
];
5173 int port
; /* Port number */
5174 const char *encoding
; /* Content-Encoding value */
5175 static const char * const http_states
[] =
5176 { /* Strings for logging HTTP method */
5197 * Clear state variables...
5200 ippDelete(client
->request
);
5201 ippDelete(client
->response
);
5203 client
->request
= NULL
;
5204 client
->response
= NULL
;
5205 client
->operation
= HTTP_STATE_WAITING
;
5208 * Read a request from the connection...
5211 while ((http_state
= httpReadRequest(client
->http
, uri
,
5212 sizeof(uri
))) == HTTP_STATE_WAITING
)
5216 * Parse the request line...
5219 if (http_state
== HTTP_STATE_ERROR
)
5221 if (httpError(client
->http
) == EPIPE
)
5222 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
5224 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
5225 strerror(httpError(client
->http
)));
5229 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
5231 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
5232 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5235 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
5237 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
5238 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5242 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
5246 * Separate the URI into its components...
5249 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
5250 userpass
, sizeof(userpass
),
5251 hostname
, sizeof(hostname
), &port
,
5252 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
5253 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
5255 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
5256 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5260 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
5261 *(client
->options
)++ = '\0';
5264 * Process the request...
5267 client
->start
= time(NULL
);
5268 client
->operation
= httpGetState(client
->http
);
5271 * Parse incoming parameters until the status changes...
5274 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
5276 if (http_status
!= HTTP_STATUS_OK
)
5278 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5282 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
5283 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
5286 * HTTP/1.1 and higher require the "Host:" field...
5289 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5294 * Handle HTTP Upgrade...
5297 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
5301 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
5303 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
5306 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
5308 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
5310 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5314 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5317 #endif /* HAVE_SSL */
5319 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
5324 * Handle HTTP Expect...
5327 if (httpGetExpect(client
->http
) &&
5328 (client
->operation
== HTTP_STATE_POST
||
5329 client
->operation
== HTTP_STATE_PUT
))
5331 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
5334 * Send 100-continue header...
5337 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
5343 * Send 417-expectation-failed header...
5346 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
5352 * Handle new transfers...
5355 encoding
= httpGetContentEncoding(client
->http
);
5357 switch (client
->operation
)
5359 case HTTP_STATE_OPTIONS
:
5361 * Do OPTIONS command...
5364 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
5366 case HTTP_STATE_HEAD
:
5367 if (!strcmp(client
->uri
, "/icon.png"))
5368 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
5369 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
5370 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
5372 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5374 case HTTP_STATE_GET
:
5375 if (!strcmp(client
->uri
, "/icon.png"))
5378 * Send PNG icon file.
5381 int fd
; /* Icon file */
5382 struct stat fileinfo
; /* Icon file information */
5383 char buffer
[4096]; /* Copy buffer */
5384 ssize_t bytes
; /* Bytes */
5386 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5388 if (!stat(client
->printer
->icon
, &fileinfo
) &&
5389 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5391 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
5392 (size_t)fileinfo
.st_size
))
5398 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5399 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5401 httpFlushWrite(client
->http
);
5406 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5408 else if (!strcmp(client
->uri
, "/"))
5411 * Show web status page...
5414 _ipp_job_t
*job
; /* Current job */
5415 int i
; /* Looping var */
5416 _ipp_preason_t reason
; /* Current reason */
5417 static const char * const reasons
[] =
5418 { /* Reason strings */
5421 "Input Tray Missing",
5422 "Marker Supply Empty",
5423 "Marker Supply Low",
5424 "Marker Waste Almost Full",
5425 "Marker Waste Full",
5437 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5440 html_header(client
, client
->printer
->name
);
5442 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
5443 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
5444 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
5445 if (client
->printer
->state_reasons
& reason
)
5446 html_printf(client
, "\n<br> %s", reasons
[i
]);
5447 html_printf(client
, "</p>\n");
5449 if (cupsArrayCount(client
->printer
->jobs
) > 0)
5451 _cupsRWLockRead(&(client
->printer
->rwlock
));
5453 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");
5454 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
5456 char when
[256], /* When job queued/started/finished */
5457 hhmmss
[64]; /* Time HH:MM:SS */
5461 case IPP_JSTATE_PENDING
:
5462 case IPP_JSTATE_HELD
:
5463 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
5465 case IPP_JSTATE_PROCESSING
:
5466 case IPP_JSTATE_STOPPED
:
5467 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
5469 case IPP_JSTATE_ABORTED
:
5470 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5472 case IPP_JSTATE_CANCELED
:
5473 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5475 case IPP_JSTATE_COMPLETED
:
5476 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5480 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
);
5482 html_printf(client
, "</tbody></table>\n");
5484 _cupsRWUnlock(&(client
->printer
->rwlock
));
5486 html_footer(client
);
5490 else if (!strcmp(client
->uri
, "/media"))
5493 * Show web media page...
5496 int i
, /* Looping var */
5497 num_options
; /* Number of form options */
5498 cups_option_t
*options
; /* Form options */
5499 static const char * const sizes
[] =
5500 { /* Size strings */
5513 static const char * const types
[] =
5530 static const int sheets
[] = /* Number of sheets */
5539 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5542 html_header(client
, client
->printer
->name
);
5544 if ((num_options
= parse_options(client
, &options
)) > 0)
5547 * WARNING: A real printer/server implementation MUST NOT implement
5548 * media updates via a GET request - GET requests are supposed to be
5549 * idempotent (without side-effects) and we obviously are not
5550 * authenticating access here. This form is provided solely to
5551 * enable testing and development!
5554 const char *val
; /* Form value */
5556 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
5557 client
->printer
->main_size
= atoi(val
);
5558 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
5559 client
->printer
->main_type
= atoi(val
);
5560 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
5561 client
->printer
->main_level
= atoi(val
);
5563 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
5564 client
->printer
->envelope_size
= atoi(val
);
5565 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
5566 client
->printer
->envelope_level
= atoi(val
);
5568 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
5569 client
->printer
->photo_size
= atoi(val
);
5570 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
5571 client
->printer
->photo_type
= atoi(val
);
5572 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
5573 client
->printer
->photo_level
= atoi(val
);
5575 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))
5576 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
5578 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
5580 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
))
5582 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
5583 if (client
->printer
->active_job
)
5584 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5587 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
5589 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
5592 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
5594 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
5595 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
5596 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5597 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
5598 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
5599 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
5600 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5601 if (!strstr(types
[i
], "Photo"))
5602 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
5603 html_printf(client
, "</select> <select name=\"main_level\">");
5604 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5605 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
5606 html_printf(client
, "</select></td></tr>\n");
5609 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
5610 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5611 if (strstr(sizes
[i
], "Envelope"))
5612 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
5613 html_printf(client
, "</select> <select name=\"envelope_level\">");
5614 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5615 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
5616 html_printf(client
, "</select></td></tr>\n");
5619 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
5620 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5621 if (strstr(sizes
[i
], "Photo"))
5622 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
5623 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
5624 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5625 if (strstr(types
[i
], "Photo"))
5626 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
5627 html_printf(client
, "</select> <select name=\"photo_level\">");
5628 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5629 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
5630 html_printf(client
, "</select></td></tr>\n");
5632 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5633 html_footer(client
);
5637 else if (!strcmp(client
->uri
, "/supplies"))
5640 * Show web supplies page...
5643 int i
, j
, /* Looping vars */
5644 num_options
; /* Number of form options */
5645 cups_option_t
*options
; /* Form options */
5646 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5648 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5651 html_header(client
, client
->printer
->name
);
5653 if ((num_options
= parse_options(client
, &options
)) > 0)
5656 * WARNING: A real printer/server implementation MUST NOT implement
5657 * supply updates via a GET request - GET requests are supposed to be
5658 * idempotent (without side-effects) and we obviously are not
5659 * authenticating access here. This form is provided solely to
5660 * enable testing and development!
5663 char name
[64]; /* Form field */
5664 const char *val
; /* Form value */
5666 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
);
5668 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5670 snprintf(name
, sizeof(name
), "supply_%d", i
);
5671 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5673 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5679 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
5680 else if (level
< 10)
5681 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
5686 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
5687 else if (level
> 90)
5688 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
5693 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5696 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5698 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5699 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5701 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5702 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5703 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5704 html_printf(client
, "</select></td></tr>\n");
5706 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5707 html_footer(client
);
5712 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5715 case HTTP_STATE_POST
:
5716 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5720 * Not an IPP request...
5723 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5727 * Read the IPP request...
5730 client
->request
= ippNew();
5732 while ((ipp_state
= ippRead(client
->http
,
5733 client
->request
)) != IPP_STATE_DATA
)
5735 if (ipp_state
== IPP_STATE_ERROR
)
5737 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5738 cupsLastErrorString());
5739 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5745 * Now that we have the IPP request, process the request...
5748 return (process_ipp(client
));
5751 break; /* Anti-compiler-warning-code */
5759 * 'process_ipp()' - Process an IPP request.
5762 static int /* O - 1 on success, 0 on error */
5763 process_ipp(_ipp_client_t
*client
) /* I - Client */
5765 ipp_tag_t group
; /* Current group tag */
5766 ipp_attribute_t
*attr
; /* Current attribute */
5767 ipp_attribute_t
*charset
; /* Character set attribute */
5768 ipp_attribute_t
*language
; /* Language attribute */
5769 ipp_attribute_t
*uri
; /* Printer URI attribute */
5770 int major
, minor
; /* Version number */
5771 const char *name
; /* Name of attribute */
5774 debug_attributes("Request", client
->request
, 1);
5777 * First build an empty response message for this request...
5780 client
->operation_id
= ippGetOperation(client
->request
);
5781 client
->response
= ippNewResponse(client
->request
);
5784 * Then validate the request header and required attributes...
5787 major
= ippGetVersion(client
->request
, &minor
);
5789 if (major
< 1 || major
> 2)
5792 * Return an error, since we only support IPP 1.x and 2.x.
5795 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
5796 "Bad request version number %d.%d.", major
, minor
);
5798 else if (ippGetRequestId(client
->request
) <= 0)
5799 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
5800 ippGetRequestId(client
->request
));
5801 else if (!ippFirstAttribute(client
->request
))
5802 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5803 "No attributes in request.");
5807 * Make sure that the attributes are provided in the correct order and
5808 * don't repeat groups...
5811 for (attr
= ippFirstAttribute(client
->request
),
5812 group
= ippGetGroupTag(attr
);
5814 attr
= ippNextAttribute(client
->request
))
5816 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5819 * Out of order; return an error...
5822 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5823 "Attribute groups are out of order (%x < %x).",
5824 ippGetGroupTag(attr
), group
);
5828 group
= ippGetGroupTag(attr
);
5834 * Then make sure that the first three attributes are:
5836 * attributes-charset
5837 * attributes-natural-language
5838 * printer-uri/job-uri
5841 attr
= ippFirstAttribute(client
->request
);
5842 name
= ippGetName(attr
);
5843 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5844 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5849 attr
= ippNextAttribute(client
->request
);
5850 name
= ippGetName(attr
);
5852 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5853 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5858 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5859 IPP_TAG_URI
)) != NULL
)
5861 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5862 IPP_TAG_URI
)) != NULL
)
5868 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5869 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5872 * Bad character set...
5875 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5876 "Unsupported character set \"%s\".",
5877 ippGetString(charset
, 0, NULL
));
5879 else if (!charset
|| !language
|| !uri
)
5882 * Return an error, since attributes-charset,
5883 * attributes-natural-language, and printer-uri/job-uri are required
5884 * for all operations.
5887 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5888 "Missing required attributes.");
5892 char scheme
[32], /* URI scheme */
5893 userpass
[32], /* Username/password in URI */
5894 host
[256], /* Host name in URI */
5895 resource
[256]; /* Resource path in URI */
5896 int port
; /* Port number in URI */
5898 name
= ippGetName(uri
);
5900 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5901 scheme
, sizeof(scheme
),
5902 userpass
, sizeof(userpass
),
5903 host
, sizeof(host
), &port
,
5904 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5905 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5906 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5907 else if ((!strcmp(name
, "job-uri") &&
5908 strncmp(resource
, "/ipp/print/", 11)) ||
5909 (!strcmp(name
, "printer-uri") &&
5910 strcmp(resource
, "/ipp/print")))
5911 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5912 name
, ippGetString(uri
, 0, NULL
));
5916 * Try processing the operation...
5919 switch (ippGetOperation(client
->request
))
5921 case IPP_OP_PRINT_JOB
:
5922 ipp_print_job(client
);
5925 case IPP_OP_PRINT_URI
:
5926 ipp_print_uri(client
);
5929 case IPP_OP_VALIDATE_JOB
:
5930 ipp_validate_job(client
);
5933 case IPP_OP_CREATE_JOB
:
5934 ipp_create_job(client
);
5937 case IPP_OP_SEND_DOCUMENT
:
5938 ipp_send_document(client
);
5941 case IPP_OP_SEND_URI
:
5942 ipp_send_uri(client
);
5945 case IPP_OP_CANCEL_JOB
:
5946 ipp_cancel_job(client
);
5949 case IPP_OP_GET_JOB_ATTRIBUTES
:
5950 ipp_get_job_attributes(client
);
5953 case IPP_OP_GET_JOBS
:
5954 ipp_get_jobs(client
);
5957 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5958 ipp_get_printer_attributes(client
);
5961 case IPP_OP_CLOSE_JOB
:
5962 ipp_close_job(client
);
5965 case IPP_OP_IDENTIFY_PRINTER
:
5966 ipp_identify_printer(client
);
5970 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5971 "Operation not supported.");
5980 * Send the HTTP header and return...
5983 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5984 httpFlush(client
->http
); /* Flush trailing (junk) data */
5986 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5987 ippLength(client
->response
)));
5992 * 'process_job()' - Process a print job.
5995 static void * /* O - Thread exit status */
5996 process_job(_ipp_job_t
*job
) /* I - Job */
5998 job
->state
= IPP_JSTATE_PROCESSING
;
5999 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
6000 job
->processing
= time(NULL
);
6002 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
6004 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
6009 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
6011 if (job
->printer
->command
)
6014 * Execute a command with the job spool file and wait for it to complete...
6017 int pid
, /* Process ID */
6018 status
; /* Exit status */
6019 time_t start
, /* Start time */
6021 char *myargv
[3], /* Command-line arguments */
6022 *myenvp
[200]; /* Environment variables */
6023 int myenvc
; /* Number of environment variables */
6024 ipp_attribute_t
*attr
; /* Job attribute */
6025 char val
[1280], /* IPP_NAME=value */
6026 *valptr
; /* Pointer into string */
6028 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
6033 * Setup the command-line arguments...
6036 myargv
[0] = job
->printer
->command
;
6037 myargv
[1] = job
->filename
;
6041 * Copy the current environment, then add ENV variables for every Job
6045 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
6046 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
6048 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
6051 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6052 * value(s) from the attribute.
6055 const char *name
= ippGetName(attr
);
6064 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6069 *valptr
++ = (char)toupper(*name
& 255);
6074 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6076 myenvp
[myenvc
++] = strdup(val
);
6078 myenvp
[myenvc
] = NULL
;
6081 * Now run the program...
6085 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
6087 if ((pid
= fork()) == 0)
6090 * Child comes here...
6093 execve(job
->printer
->command
, myargv
, myenvp
);
6099 * Unable to fork process...
6102 perror("Unable to start job processing command");
6106 * Free memory used for environment...
6110 free(myenvp
[-- myenvc
]);
6115 * Free memory used for environment...
6119 free(myenvp
[-- myenvc
]);
6121 * Wait for child to complete...
6124 # ifdef HAVE_WAITPID
6125 while (waitpid(pid
, &status
, 0) < 0);
6127 while (wait(&status
) < 0);
6128 # endif /* HAVE_WAITPID */
6135 if (WIFEXITED(status
))
6137 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
6138 job
->printer
->command
, WEXITSTATUS(status
));
6141 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
6142 job
->printer
->command
, WTERMSIG(status
));
6144 job
->state
= IPP_JSTATE_ABORTED
;
6146 else if (status
< 0)
6147 job
->state
= IPP_JSTATE_ABORTED
;
6149 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
6150 job
->printer
->command
);
6153 * Make sure processing takes at least 5 seconds...
6157 if ((end
- start
) < 5)
6163 * Sleep for a random amount of time to simulate job processing.
6166 sleep((unsigned)(5 + (rand() % 11)));
6170 job
->state
= IPP_JSTATE_CANCELED
;
6171 else if (job
->state
== IPP_JSTATE_PROCESSING
)
6172 job
->state
= IPP_JSTATE_COMPLETED
;
6174 job
->completed
= time(NULL
);
6175 job
->printer
->state
= IPP_PSTATE_IDLE
;
6176 job
->printer
->active_job
= NULL
;
6183 * 'register_printer()' - Register a printer object via Bonjour.
6186 static int /* O - 1 on success, 0 on error */
6188 _ipp_printer_t
*printer
, /* I - Printer */
6189 const char *location
, /* I - Location */
6190 const char *make
, /* I - Manufacturer */
6191 const char *model
, /* I - Model name */
6192 const char *formats
, /* I - Supported formats */
6193 const char *adminurl
, /* I - Web interface URL */
6194 const char *uuid
, /* I - Printer UUID */
6195 int color
, /* I - 1 = color, 0 = monochrome */
6196 int duplex
, /* I - 1 = duplex, 0 = simplex */
6197 const char *subtype
) /* I - Service subtype */
6199 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6200 _ipp_txt_t ipp_txt
; /* Bonjour IPP TXT record */
6201 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6203 DNSServiceErrorType error
; /* Error from Bonjour */
6204 char make_model
[256],/* Make and model together */
6205 product
[256], /* Product string */
6206 regtype
[256]; /* Bonjour service type */
6210 * Build the TXT record for IPP...
6213 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
6214 snprintf(product
, sizeof(product
), "(%s)", model
);
6216 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
6217 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
6218 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(make_model
),
6220 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(adminurl
),
6223 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(location
),
6225 TXTRecordSetValue(&ipp_txt
, "product", (uint8_t)strlen(product
),
6227 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
),
6229 TXTRecordSetValue(&ipp_txt
, "Color", 1, color
? "T" : "F");
6230 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, duplex
? "T" : "F");
6231 TXTRecordSetValue(&ipp_txt
, "usb_MFG", (uint8_t)strlen(make
),
6233 TXTRecordSetValue(&ipp_txt
, "usb_MDL", (uint8_t)strlen(model
),
6235 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(uuid
), uuid
);
6237 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
6238 # endif /* HAVE_SSL */
6239 if (strstr(formats
, "image/urf"))
6240 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");
6242 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
6243 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
6246 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6247 * defend our service name but not actually support LPD...
6250 printer
->printer_ref
= DNSSDMaster
;
6252 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
6253 kDNSServiceFlagsShareConnection
,
6254 0 /* interfaceIndex */, printer
->dnssd_name
,
6255 "_printer._tcp", NULL
/* domain */,
6256 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
6257 NULL
/* txtRecord */,
6258 (DNSServiceRegisterReply
)dnssd_callback
,
6259 printer
)) != kDNSServiceErr_NoError
)
6261 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
6262 printer
->dnssd_name
, error
);
6267 * Then register the _ipp._tcp (IPP) service type with the real port number to
6268 * advertise our IPP printer...
6271 printer
->ipp_ref
= DNSSDMaster
;
6273 if (subtype
&& *subtype
)
6274 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
6276 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
6278 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
6279 kDNSServiceFlagsShareConnection
,
6280 0 /* interfaceIndex */, printer
->dnssd_name
,
6281 regtype
, NULL
/* domain */,
6282 NULL
/* host */, htons(printer
->port
),
6283 TXTRecordGetLength(&ipp_txt
),
6284 TXTRecordGetBytesPtr(&ipp_txt
),
6285 (DNSServiceRegisterReply
)dnssd_callback
,
6286 printer
)) != kDNSServiceErr_NoError
)
6288 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6289 printer
->dnssd_name
, regtype
, error
);
6295 * Then register the _ipps._tcp (IPP) service type with the real port number to
6296 * advertise our IPPS printer...
6299 printer
->ipps_ref
= DNSSDMaster
;
6301 if (subtype
&& *subtype
)
6302 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
6304 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
6306 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
6307 kDNSServiceFlagsShareConnection
,
6308 0 /* interfaceIndex */, printer
->dnssd_name
,
6309 regtype
, NULL
/* domain */,
6310 NULL
/* host */, htons(printer
->port
),
6311 TXTRecordGetLength(&ipp_txt
),
6312 TXTRecordGetBytesPtr(&ipp_txt
),
6313 (DNSServiceRegisterReply
)dnssd_callback
,
6314 printer
)) != kDNSServiceErr_NoError
)
6316 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6317 printer
->dnssd_name
, regtype
, error
);
6320 # endif /* HAVE_SSL */
6323 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6324 * real port number to advertise our IPP printer...
6327 printer
->http_ref
= DNSSDMaster
;
6329 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
6330 kDNSServiceFlagsShareConnection
,
6331 0 /* interfaceIndex */, printer
->dnssd_name
,
6332 "_http._tcp,_printer", NULL
/* domain */,
6333 NULL
/* host */, htons(printer
->port
),
6334 0 /* txtLen */, NULL
, /* txtRecord */
6335 (DNSServiceRegisterReply
)dnssd_callback
,
6336 printer
)) != kDNSServiceErr_NoError
)
6338 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6339 printer
->dnssd_name
, regtype
, error
);
6343 TXTRecordDeallocate(&ipp_txt
);
6345 #elif defined(HAVE_AVAHI)
6346 char temp
[256]; /* Subtype service string */
6349 * Create the TXT record...
6353 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
6354 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s %s", make
, model
);
6355 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", adminurl
);
6357 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", location
);
6358 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "product=(%s)", model
);
6359 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
6360 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", color
? "T" : "F");
6361 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", duplex
? "T" : "F");
6362 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MFG=%s", make
);
6363 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MDL=%s", model
);
6364 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", uuid
);
6366 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
6367 # endif /* HAVE_SSL */
6370 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6373 avahi_threaded_poll_lock(DNSSDMaster
);
6375 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
6377 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
);
6380 * Then register the _ipp._tcp (IPP)...
6383 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
);
6384 if (subtype
&& *subtype
)
6386 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
6387 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
6392 * _ipps._tcp (IPPS) for secure printing...
6395 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
);
6396 if (subtype
&& *subtype
)
6398 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
6399 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
6401 #endif /* HAVE_SSL */
6404 * Finally _http.tcp (HTTP) for the web interface...
6407 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
);
6408 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");
6414 avahi_entry_group_commit(printer
->ipp_ref
);
6415 avahi_threaded_poll_unlock(DNSSDMaster
);
6417 avahi_string_list_free(ipp_txt
);
6418 #endif /* HAVE_DNSSD */
6425 * 'respond_http()' - Send a HTTP response.
6428 int /* O - 1 on success, 0 on failure */
6430 _ipp_client_t
*client
, /* I - Client */
6431 http_status_t code
, /* I - HTTP status of response */
6432 const char *content_encoding
, /* I - Content-Encoding of response */
6433 const char *type
, /* I - MIME media type of response */
6434 size_t length
) /* I - Length of response */
6436 char message
[1024]; /* Text message */
6439 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
6441 if (code
== HTTP_STATUS_CONTINUE
)
6444 * 100-continue doesn't send any headers...
6447 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
6451 * Format an error message...
6454 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
6456 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
6458 type
= "text/plain";
6459 length
= strlen(message
);
6465 * Send the HTTP response header...
6468 httpClearFields(client
->http
);
6470 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
6471 client
->operation
== HTTP_STATE_OPTIONS
)
6472 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
6476 if (!strcmp(type
, "text/html"))
6477 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
6478 "text/html; charset=utf-8");
6480 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
6482 if (content_encoding
)
6483 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
6486 httpSetLength(client
->http
, length
);
6488 if (httpWriteResponse(client
->http
, code
) < 0)
6492 * Send the response data...
6498 * Send a plain text message.
6501 if (httpPrintf(client
->http
, "%s", message
) < 0)
6504 if (httpWrite2(client
->http
, "", 0) < 0)
6507 else if (client
->response
)
6510 * Send an IPP response...
6513 debug_attributes("Response", client
->response
, 2);
6515 ippSetState(client
->response
, IPP_STATE_IDLE
);
6517 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6526 * 'respond_ipp()' - Send an IPP response.
6530 respond_ipp(_ipp_client_t
*client
, /* I - Client */
6531 ipp_status_t status
, /* I - status-code */
6532 const char *message
, /* I - printf-style status-message */
6533 ...) /* I - Additional args as needed */
6535 const char *formatted
= NULL
; /* Formatted message */
6538 ippSetStatusCode(client
->response
, status
);
6542 va_list ap
; /* Pointer to additional args */
6543 ipp_attribute_t
*attr
; /* New status-message attribute */
6545 va_start(ap
, message
);
6546 if ((attr
= ippFindAttribute(client
->response
, "status-message",
6547 IPP_TAG_TEXT
)) != NULL
)
6548 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6550 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
6551 "status-message", NULL
, message
, ap
);
6554 formatted
= ippGetString(attr
, 0, NULL
);
6558 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
6559 ippOpString(client
->operation_id
), ippErrorString(status
),
6562 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
6563 ippOpString(client
->operation_id
), ippErrorString(status
));
6568 * 'respond_unsupported()' - Respond with an unsupported attribute.
6572 respond_unsupported(
6573 _ipp_client_t
*client
, /* I - Client */
6574 ipp_attribute_t
*attr
) /* I - Atribute */
6576 ipp_attribute_t
*temp
; /* Copy of attribute */
6579 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6580 "Unsupported %s %s%s value.", ippGetName(attr
),
6581 ippGetCount(attr
) > 1 ? "1setOf " : "",
6582 ippTagString(ippGetValueTag(attr
)));
6584 temp
= ippCopyAttribute(client
->response
, attr
, 0);
6585 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
6590 * 'run_printer()' - Run the printer service.
6594 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
6596 int num_fds
; /* Number of file descriptors */
6597 struct pollfd polldata
[3]; /* poll() data */
6598 int timeout
; /* Timeout for poll() */
6599 _ipp_client_t
*client
; /* New client */
6603 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6606 polldata
[0].fd
= printer
->ipv4
;
6607 polldata
[0].events
= POLLIN
;
6609 polldata
[1].fd
= printer
->ipv6
;
6610 polldata
[1].events
= POLLIN
;
6615 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
6616 polldata
[num_fds
++].events
= POLLIN
;
6617 #endif /* HAVE_DNSSD */
6620 * Loop until we are killed or have a hard error...
6625 if (cupsArrayCount(printer
->jobs
))
6630 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
6632 perror("poll() failed");
6636 if (polldata
[0].revents
& POLLIN
)
6638 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6640 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
6642 perror("Unable to create client thread");
6643 delete_client(client
);
6648 if (polldata
[1].revents
& POLLIN
)
6650 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6652 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
6654 perror("Unable to create client thread");
6655 delete_client(client
);
6661 if (polldata
[2].revents
& POLLIN
)
6662 DNSServiceProcessResult(DNSSDMaster
);
6663 #endif /* HAVE_DNSSD */
6666 * Clean out old jobs...
6669 clean_jobs(printer
);
6675 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6679 time_string(time_t tv
, /* I - Time value */
6680 char *buffer
, /* I - Buffer */
6681 size_t bufsize
) /* I - Size of buffer */
6683 struct tm
*curtime
= localtime(&tv
);
6686 strftime(buffer
, bufsize
, "%X", curtime
);
6692 * 'usage()' - Show program usage.
6696 usage(int status
) /* O - Exit status */
6700 puts(CUPS_SVERSION
" - Copyright 2010-2015 by Apple Inc. All rights "
6705 puts("Usage: ippserver [options] \"name\"");
6708 puts("-2 Supports 2-sided printing (default=1-sided)");
6709 puts("-M manufacturer Manufacturer name (default=Test)");
6710 puts("-P PIN printing mode");
6711 puts("-a attributes-file Load printer attributes from file");
6712 puts("-c command Run command for every print job");
6713 printf("-d spool-directory Spool directory "
6714 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6715 puts("-f type/subtype[,...] List of supported types "
6716 "(default=application/pdf,image/jpeg)");
6717 puts("-h Show program help");
6718 puts("-i iconfile.png PNG icon file (default=printer.png)");
6719 puts("-k Keep job spool files");
6720 puts("-l location Location of printer (default=empty string)");
6721 puts("-m model Model name (default=Printer)");
6722 puts("-n hostname Hostname for printer");
6723 puts("-p port Port number (default=auto)");
6724 puts("-r subtype Bonjour service subtype (default=_print)");
6725 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6726 puts("-v[vvv] Be (very) verbose");
6733 * 'valid_doc_attributes()' - Determine whether the document attributes are
6736 * When one or more document attributes are invalid, this function adds a
6737 * suitable response and attributes to the unsupported group.
6740 static int /* O - 1 if valid, 0 if not */
6741 valid_doc_attributes(
6742 _ipp_client_t
*client
) /* I - Client */
6744 int valid
= 1; /* Valid attributes? */
6745 ipp_op_t op
= ippGetOperation(client
->request
);
6747 const char *op_name
= ippOpString(op
);
6748 /* IPP operation name */
6749 ipp_attribute_t
*attr
, /* Current attribute */
6750 *supported
; /* xxx-supported attribute */
6751 const char *compression
= NULL
,
6752 /* compression value */
6753 *format
= NULL
; /* document-format value */
6757 * Check operation attributes...
6760 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6763 * If compression is specified, only accept a supported value in a Print-Job
6764 * or Send-Document request...
6767 compression
= ippGetString(attr
, 0, NULL
);
6768 supported
= ippFindAttribute(client
->printer
->attrs
,
6769 "compression-supported", IPP_TAG_KEYWORD
);
6771 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6772 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6773 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6774 op
!= IPP_OP_VALIDATE_JOB
) ||
6775 !ippContainsString(supported
, compression
))
6777 respond_unsupported(client
, attr
);
6782 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6784 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6786 if (strcmp(compression
, "none"))
6789 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6790 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
6796 * Is it a format we support?
6799 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
6801 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
6802 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
6804 respond_unsupported(client
, attr
);
6809 format
= ippGetString(attr
, 0, NULL
);
6811 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
6812 client
->hostname
, op_name
, format
);
6814 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
6819 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
6821 format
= "application/octet-stream"; /* Should never happen */
6823 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
6826 if (!strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
6829 * Auto-type the file using the first 8 bytes of the file...
6832 unsigned char header
[8]; /* First 8 bytes of file */
6834 memset(header
, 0, sizeof(header
));
6835 httpPeek(client
->http
, (char *)header
, sizeof(header
));
6837 if (!memcmp(header
, "%PDF", 4))
6838 format
= "application/pdf";
6839 else if (!memcmp(header
, "%!", 2))
6840 format
= "application/postscript";
6841 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
6842 format
= "image/jpeg";
6843 else if (!memcmp(header
, "\211PNG", 4))
6844 format
= "image/png";
6845 else if (!memcmp(header
, "RAS2", 4))
6846 format
= "image/pwg-raster";
6847 else if (!memcmp(header
, "UNIRAST", 8))
6848 format
= "image/urf";
6854 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
6855 client
->hostname
, op_name
, format
);
6857 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
6861 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
6863 respond_unsupported(client
, attr
);
6871 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
6872 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
6879 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6881 * When one or more job attributes are invalid, this function adds a suitable
6882 * response and attributes to the unsupported group.
6885 static int /* O - 1 if valid, 0 if not */
6886 valid_job_attributes(
6887 _ipp_client_t
*client
) /* I - Client */
6889 int i
, /* Looping var */
6890 valid
= 1; /* Valid attributes? */
6891 ipp_attribute_t
*attr
, /* Current attribute */
6892 *supported
; /* xxx-supported attribute */
6896 * Check operation attributes...
6899 valid
= valid_doc_attributes(client
);
6902 * Check the various job template attributes...
6905 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
6907 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6908 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
6910 respond_unsupported(client
, attr
);
6915 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
6917 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
6919 respond_unsupported(client
, attr
);
6924 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
6926 if (ippGetCount(attr
) != 1 ||
6927 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6928 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6929 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6930 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
6932 respond_unsupported(client
, attr
);
6937 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
6939 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
6941 respond_unsupported(client
, attr
);
6946 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
6948 if (ippGetCount(attr
) != 1 ||
6949 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6950 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
6952 respond_unsupported(client
, attr
);
6956 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
6959 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
6961 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
6963 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6964 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
6966 respond_unsupported(client
, attr
);
6971 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
6973 if (ippGetCount(attr
) != 1 ||
6974 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6975 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6976 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6977 strcmp(ippGetString(attr
, 0, NULL
), "none"))
6979 respond_unsupported(client
, attr
);
6984 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
6986 if (ippGetCount(attr
) != 1 ||
6987 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6988 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6989 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
6991 respond_unsupported(client
, attr
);
6997 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
6999 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
7002 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
7004 respond_unsupported(client
, attr
);
7010 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
7012 if (ippGetCount(attr
) != 1 ||
7013 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
7015 respond_unsupported(client
, attr
);
7018 /* TODO: check for valid media-col */
7021 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
7023 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7024 (strcmp(ippGetString(attr
, 0, NULL
),
7025 "separate-documents-uncollated-copies") &&
7026 strcmp(ippGetString(attr
, 0, NULL
),
7027 "separate-documents-collated-copies")))
7029 respond_unsupported(client
, attr
);
7034 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
7036 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7037 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
7038 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
7040 respond_unsupported(client
, attr
);
7045 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
7047 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
7049 respond_unsupported(client
, attr
);
7054 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
7056 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7057 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
7058 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
7060 respond_unsupported(client
, attr
);
7065 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
7067 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
7069 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
7072 respond_unsupported(client
, attr
);
7077 int count
, /* Number of supported values */
7078 xdpi
, /* Horizontal resolution for job template attribute */
7079 ydpi
, /* Vertical resolution for job template attribute */
7080 sydpi
; /* Vertical resolution for supported value */
7081 ipp_res_t units
, /* Units for job template attribute */
7082 sunits
; /* Units for supported value */
7084 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
7085 count
= ippGetCount(supported
);
7087 for (i
= 0; i
< count
; i
++)
7089 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
7095 respond_unsupported(client
, attr
);
7101 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
7103 const char *sides
= ippGetString(attr
, 0, NULL
);
7104 /* "sides" value... */
7106 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
7108 respond_unsupported(client
, attr
);
7111 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
7113 if (!ippContainsString(supported
, sides
))
7115 respond_unsupported(client
, attr
);
7119 else if (strcmp(sides
, "one-sided"))
7121 respond_unsupported(client
, attr
);