4 * Sample IPP Everywhere server for CUPS.
6 * Copyright 2010-2015 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * Disable private and deprecated stuff so we can verify that the public API
19 * is sufficient to implement a server.
22 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
23 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
27 * Include necessary headers...
30 #include <config.h> /* CUPS configuration header */
31 #include <cups/cups.h> /* Public API */
32 #include <cups/string-private.h> /* CUPS string functions */
33 #include <cups/thread-private.h> /* For multithreading functions */
46 # define WEXITSTATUS(s) (s)
47 # include <winsock2.h>
51 extern char **environ
;
53 # include <sys/fcntl.h>
54 # include <sys/wait.h>
60 #elif defined(HAVE_AVAHI)
61 # include <avahi-client/client.h>
62 # include <avahi-client/publish.h>
63 # include <avahi-common/error.h>
64 # include <avahi-common/thread-watch.h>
65 #endif /* HAVE_DNSSD */
66 #ifdef HAVE_SYS_MOUNT_H
67 # include <sys/mount.h>
68 #endif /* HAVE_SYS_MOUNT_H */
69 #ifdef HAVE_SYS_STATFS_H
70 # include <sys/statfs.h>
71 #endif /* HAVE_SYS_STATFS_H */
72 #ifdef HAVE_SYS_STATVFS_H
73 # include <sys/statvfs.h>
74 #endif /* HAVE_SYS_STATVFS_H */
77 #endif /* HAVE_SYS_VFS_H */
84 enum _ipp_preason_e
/* printer-state-reasons bit values */
86 _IPP_PREASON_NONE
= 0x0000, /* none */
87 _IPP_PREASON_OTHER
= 0x0001, /* other */
88 _IPP_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
89 _IPP_PREASON_INPUT_TRAY_MISSING
= 0x0004,
90 /* input-tray-missing */
91 _IPP_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
92 /* marker-supply-empty */
93 _IPP_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
94 /* marker-supply-low */
95 _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
96 /* marker-waste-almost-full */
97 _IPP_PREASON_MARKER_WASTE_FULL
= 0x0040,
98 /* marker-waste-full */
99 _IPP_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
100 _IPP_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
101 _IPP_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
102 _IPP_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
103 _IPP_PREASON_MOVING_TO_PAUSED
= 0x0800,
104 /* moving-to-paused */
105 _IPP_PREASON_PAUSED
= 0x1000, /* paused */
106 _IPP_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
107 _IPP_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
108 _IPP_PREASON_TONER_LOW
= 0x8000 /* toner-low */
110 typedef unsigned int _ipp_preason_t
; /* Bitfield for printer-state-reasons */
111 static const char * const _ipp_preason_strings
[] =
112 { /* Strings for each bit */
113 /* "none" is implied for no bits set */
116 "input-tray-missing",
117 "marker-supply-empty",
119 "marker-waste-almost-full",
132 typedef enum _ipp_media_class_e
134 _IPP_GENERAL
, /* General-purpose size */
135 _IPP_PHOTO_ONLY
, /* Photo-only size */
136 _IPP_ENV_ONLY
/* Envelope-only size */
137 } _ipp_media_class_t
;
139 typedef enum _ipp_media_size_e
141 _IPP_MEDIA_SIZE_NONE
= -1,
146 _IPP_MEDIA_SIZE_LEGAL
,
147 _IPP_MEDIA_SIZE_LETTER
,
148 _IPP_MEDIA_SIZE_COM10
,
154 static const char * const media_supported
[] =
155 { /* media-supported values */
156 "iso_a4_210x297mm", /* A4 */
157 "iso_a5_148x210mm", /* A5 */
158 "iso_a6_105x148mm", /* A6 */
159 "iso_dl_110x220mm", /* DL */
160 "na_legal_8.5x14in", /* Legal */
161 "na_letter_8.5x11in", /* Letter */
162 "na_number-10_4.125x9.5in", /* #10 */
163 "na_index-3x5_3x5in", /* 3x5 */
164 "oe_photo-l_3.5x5in", /* L */
165 "na_index-4x6_4x6in", /* 4x6 */
166 "na_5x7_5x7in" /* 5x7 aka 2L */
168 static const int media_col_sizes
[][3] =
169 { /* media-col-database sizes */
170 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
171 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
172 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
173 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
174 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
175 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
176 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
177 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
178 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
179 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
180 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
183 typedef enum _ipp_media_source_e
185 _IPP_MEDIA_SOURCE_NONE
= -1,
186 _IPP_MEDIA_SOURCE_AUTO
,
187 _IPP_MEDIA_SOURCE_MAIN
,
188 _IPP_MEDIA_SOURCE_MANUAL
,
189 _IPP_MEDIA_SOURCE_ENVELOPE
,
190 _IPP_MEDIA_SOURCE_PHOTO
191 } _ipp_media_source_t
;
192 static const char * const media_source_supported
[] =
193 /* media-source-supported values */
202 typedef enum _ipp_media_type_e
204 _IPP_MEDIA_TYPE_NONE
= -1,
205 _IPP_MEDIA_TYPE_AUTO
,
206 _IPP_MEDIA_TYPE_CARDSTOCK
,
207 _IPP_MEDIA_TYPE_ENVELOPE
,
208 _IPP_MEDIA_TYPE_LABELS
,
209 _IPP_MEDIA_TYPE_OTHER
,
210 _IPP_MEDIA_TYPE_GLOSSY
,
211 _IPP_MEDIA_TYPE_HIGH_GLOSS
,
212 _IPP_MEDIA_TYPE_MATTE
,
213 _IPP_MEDIA_TYPE_SATIN
,
214 _IPP_MEDIA_TYPE_SEMI_GLOSS
,
215 _IPP_MEDIA_TYPE_STATIONERY
,
216 _IPP_MEDIA_TYPE_LETTERHEAD
,
217 _IPP_MEDIA_TYPE_TRANSPARENCY
219 static const char * const media_type_supported
[] =
220 /* media-type-supported values */
227 "photographic-glossy",
228 "photographic-high-gloss",
229 "photographic-matte",
230 "photographic-satin",
231 "photographic-semi-gloss",
233 "stationery-letterhead",
237 typedef enum _ipp_supply_e
239 _IPP_SUPPLY_CYAN
, /* Cyan Toner */
240 _IPP_SUPPLY_MAGENTA
, /* Magenta Toner */
241 _IPP_SUPPLY_YELLOW
, /* Yellow Toner */
242 _IPP_SUPPLY_BLACK
, /* Black Toner */
243 _IPP_SUPPLY_WASTE
/* Waste Toner */
245 static const char * const printer_supplies
[] =
246 { /* printer-supply-description values */
255 * URL scheme for web resources...
259 # define WEB_SCHEME "https"
261 # define WEB_SCHEME "http"
262 #endif /* HAVE_SSL */
270 typedef DNSServiceRef _ipp_srv_t
; /* Service reference */
271 typedef TXTRecordRef _ipp_txt_t
; /* TXT record */
273 #elif defined(HAVE_AVAHI)
274 typedef AvahiEntryGroup
*_ipp_srv_t
; /* Service reference */
275 typedef AvahiStringList
*_ipp_txt_t
; /* TXT record */
278 typedef void *_ipp_srv_t
; /* Service reference */
279 typedef void *_ipp_txt_t
; /* TXT record */
280 #endif /* HAVE_DNSSD */
282 typedef struct _ipp_filter_s
/**** Attribute filter ****/
284 cups_array_t
*ra
; /* Requested attributes */
285 ipp_tag_t group_tag
; /* Group to copy */
288 typedef struct _ipp_job_s _ipp_job_t
;
290 typedef struct _ipp_printer_s
/**** Printer data ****/
292 int ipv4
, /* IPv4 listener */
293 ipv6
; /* IPv6 listener */
294 _ipp_srv_t ipp_ref
, /* Bonjour IPP service */
295 ipps_ref
, /* Bonjour IPPS service */
296 http_ref
, /* Bonjour HTTP service */
297 printer_ref
; /* Bonjour LPD service */
298 char *dnssd_name
, /* printer-dnssd-name */
299 *name
, /* printer-name */
300 *icon
, /* Icon filename */
301 *directory
, /* Spool directory */
302 *hostname
, /* Hostname */
303 *uri
, /* printer-uri-supported */
304 *command
; /* Command to run with job file */
306 size_t urilen
; /* Length of printer URI */
307 ipp_t
*attrs
; /* Static attributes */
308 time_t start_time
; /* Startup time */
309 time_t config_time
; /* printer-config-change-time */
310 ipp_pstate_t state
; /* printer-state value */
311 _ipp_preason_t state_reasons
; /* printer-state-reasons values */
312 time_t state_time
; /* printer-state-change-time */
313 cups_array_t
*jobs
; /* Jobs */
314 _ipp_job_t
*active_job
; /* Current active/pending job */
315 int next_job_id
; /* Next job-id value */
316 _cups_rwlock_t rwlock
; /* Printer lock */
317 _ipp_media_size_t main_size
; /* Ready media */
318 _ipp_media_type_t main_type
;
320 _ipp_media_size_t envelope_size
;
322 _ipp_media_size_t photo_size
;
323 _ipp_media_type_t photo_type
;
325 int supplies
[5]; /* Supply levels (0-100) */
328 struct _ipp_job_s
/**** Job data ****/
331 const char *name
, /* job-name */
332 *username
, /* job-originating-user-name */
333 *format
; /* document-format */
334 ipp_jstate_t state
; /* job-state value */
335 time_t created
, /* time-at-creation value */
336 processing
, /* time-at-processing value */
337 completed
; /* time-at-completed value */
338 int impressions
, /* job-impressions value */
339 impcompleted
; /* job-impressions-completed value */
340 ipp_t
*attrs
; /* Static attributes */
341 int cancel
; /* Non-zero when job canceled */
342 char *filename
; /* Print file name */
343 int fd
; /* Print file descriptor */
344 _ipp_printer_t
*printer
; /* Printer */
347 typedef struct _ipp_client_s
/**** Client data ****/
349 http_t
*http
; /* HTTP connection */
350 ipp_t
*request
, /* IPP request */
351 *response
; /* IPP response */
352 time_t start
; /* Request start time */
353 http_state_t operation
; /* Request operation */
354 ipp_op_t operation_id
; /* IPP operation-id */
355 char uri
[1024], /* Request URI */
356 *options
; /* URI options */
357 http_addr_t addr
; /* Client address */
358 char hostname
[256]; /* Client hostname */
359 _ipp_printer_t
*printer
; /* Printer */
360 _ipp_job_t
*job
; /* Current job, if any */
368 static void clean_jobs(_ipp_printer_t
*printer
);
369 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
370 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
371 ipp_tag_t group_tag
, int quickcopy
);
372 static void copy_job_attributes(_ipp_client_t
*client
,
373 _ipp_job_t
*job
, cups_array_t
*ra
);
374 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
375 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
376 static int create_listener(int family
, int port
);
377 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
378 static ipp_t
*create_media_size(int width
, int length
);
379 static _ipp_printer_t
*create_printer(const char *servername
,
380 const char *name
, const char *location
,
381 const char *make
, const char *model
,
383 const char *docformats
, int ppm
,
384 int ppm_color
, int duplex
, int port
,
385 int pin
, const char *subtype
,
386 const char *directory
,
388 const char *attrfile
);
389 static void debug_attributes(const char *title
, ipp_t
*ipp
,
391 static void delete_client(_ipp_client_t
*client
);
392 static void delete_job(_ipp_job_t
*job
);
393 static void delete_printer(_ipp_printer_t
*printer
);
395 static void DNSSD_API
dnssd_callback(DNSServiceRef sdRef
,
396 DNSServiceFlags flags
,
397 DNSServiceErrorType errorCode
,
401 _ipp_printer_t
*printer
);
402 #elif defined(HAVE_AVAHI)
403 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
404 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
405 #endif /* HAVE_DNSSD */
406 static void dnssd_init(void);
407 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
408 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
409 static ipp_t
*get_collection(FILE *fp
, const char *filename
, int *linenum
);
410 static char *get_token(FILE *fp
, char *buf
, int buflen
, int *linenum
);
411 static void html_escape(_ipp_client_t
*client
, const char *s
,
413 static void html_footer(_ipp_client_t
*client
);
414 static void html_header(_ipp_client_t
*client
, const char *title
);
415 static void html_printf(_ipp_client_t
*client
, const char *format
,
416 ...) __attribute__((__format__(__printf__
,
418 static void ipp_cancel_job(_ipp_client_t
*client
);
419 static void ipp_close_job(_ipp_client_t
*client
);
420 static void ipp_create_job(_ipp_client_t
*client
);
421 static void ipp_get_job_attributes(_ipp_client_t
*client
);
422 static void ipp_get_jobs(_ipp_client_t
*client
);
423 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
424 static void ipp_identify_printer(_ipp_client_t
*client
);
425 static void ipp_print_job(_ipp_client_t
*client
);
426 static void ipp_print_uri(_ipp_client_t
*client
);
427 static void ipp_send_document(_ipp_client_t
*client
);
428 static void ipp_send_uri(_ipp_client_t
*client
);
429 static void ipp_validate_job(_ipp_client_t
*client
);
430 static void load_attributes(const char *filename
, ipp_t
*attrs
);
431 static int parse_options(_ipp_client_t
*client
, cups_option_t
**options
);
432 static void process_attr_message(_ipp_job_t
*job
, char *message
);
433 static void *process_client(_ipp_client_t
*client
);
434 static int process_http(_ipp_client_t
*client
);
435 static int process_ipp(_ipp_client_t
*client
);
436 static void *process_job(_ipp_job_t
*job
);
437 static void process_state_message(_ipp_job_t
*job
, char *message
);
438 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
);
439 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
440 const char *content_coding
,
441 const char *type
, size_t length
);
442 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
443 const char *message
, ...)
444 __attribute__ ((__format__ (__printf__
, 3, 4)));
445 static void respond_unsupported(_ipp_client_t
*client
,
446 ipp_attribute_t
*attr
);
447 static void run_printer(_ipp_printer_t
*printer
);
448 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
449 static void usage(int status
) __attribute__((noreturn
));
450 static int valid_doc_attributes(_ipp_client_t
*client
);
451 static int valid_job_attributes(_ipp_client_t
*client
);
459 static DNSServiceRef DNSSDMaster
= NULL
;
460 #elif defined(HAVE_AVAHI)
461 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
462 static AvahiClient
*DNSSDClient
= NULL
;
463 #endif /* HAVE_DNSSD */
465 static int KeepFiles
= 0,
470 * 'main()' - Main entry to the sample server.
473 int /* O - Exit status */
474 main(int argc
, /* I - Number of command-line args */
475 char *argv
[]) /* I - Command-line arguments */
477 int i
; /* Looping var */
478 const char *opt
, /* Current option character */
479 *attrfile
= NULL
, /* Attributes file */
480 *command
= NULL
, /* Command to run with job files */
481 *servername
= NULL
, /* Server host name */
482 *name
= NULL
, /* Printer name */
483 *location
= "", /* Location of printer */
484 *make
= "Test", /* Manufacturer */
485 *model
= "Printer", /* Model */
486 *icon
= "printer.png", /* Icon file */
487 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
488 /* Supported formats */
490 const char *keypath
= NULL
; /* Keychain path */
491 #endif /* HAVE_SSL */
492 const char *subtype
= "_print"; /* Bonjour service subtype */
493 int port
= 0, /* Port number (0 = auto) */
494 duplex
= 0, /* Duplex mode */
495 ppm
= 10, /* Pages per minute for mono */
496 ppm_color
= 0, /* Pages per minute for color */
497 pin
= 0; /* PIN printing mode? */
498 char directory
[1024] = "", /* Spool directory */
499 hostname
[1024]; /* Auto-detected hostname */
500 _ipp_printer_t
*printer
; /* Printer object */
504 * Parse command-line arguments...
507 for (i
= 1; i
< argc
; i
++)
508 if (argv
[i
][0] == '-')
510 for (opt
= argv
[i
] + 1; *opt
; opt
++)
514 case '2' : /* -2 (enable 2-sided printing) */
519 case 'K' : /* -K keypath */
525 #endif /* HAVE_SSL */
527 case 'M' : /* -M manufacturer */
534 case 'P' : /* -P (PIN printing mode) */
538 case 'a' : /* -a attributes-file */
546 case 'c' : /* -c command */
554 case 'd' : /* -d spool-directory */
558 strlcpy(directory
, argv
[i
], sizeof(directory
));
561 case 'f' : /* -f type/subtype[,...] */
568 case 'h' : /* -h (show help) */
571 case 'i' : /* -i icon.png */
578 case 'k' : /* -k (keep files) */
582 case 'l' : /* -l location */
589 case 'm' : /* -m model */
596 case 'n' : /* -n hostname */
600 servername
= argv
[i
];
603 case 'p' : /* -p port */
605 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
607 port
= atoi(argv
[i
]);
610 case 'r' : /* -r subtype */
617 case 's' : /* -s speed[,color-speed] */
621 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
625 case 'v' : /* -v (be verbose) */
629 default : /* Unknown */
630 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
641 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
649 * Apply defaults as needed...
653 servername
= httpGetHostname(NULL
, hostname
, sizeof(hostname
));
659 * Windows is almost always used as a single user system, so use a default
660 * port number of 8631.
667 * Use 8000 + UID mod 1000 for the default port number...
670 port
= 8000 + ((int)getuid() % 1000);
673 fprintf(stderr
, "Listening on port %d.\n", port
);
678 const char *tmpdir
; /* Temporary directory */
681 if ((tmpdir
= getenv("TEMP")) == NULL
)
683 #elif defined(__APPLE__) && !TARGET_OS_IOS
684 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
685 tmpdir
= "/private/tmp";
687 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
691 snprintf(directory
, sizeof(directory
), "%s/ippserver.%d", tmpdir
, (int)getpid());
693 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
695 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
696 directory
, strerror(errno
));
701 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
705 cupsSetServerCredentials(keypath
, servername
, 1);
706 #endif /* HAVE_SSL */
709 * Initialize Bonjour...
715 * Create the printer...
718 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
719 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
720 subtype
, directory
, command
, attrfile
)) == NULL
)
724 * Run the print service...
727 run_printer(printer
);
730 * Destroy the printer and exit...
733 delete_printer(printer
);
740 * 'clean_jobs()' - Clean out old (completed) jobs.
744 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
746 _ipp_job_t
*job
; /* Current job */
747 time_t cleantime
; /* Clean time */
750 if (cupsArrayCount(printer
->jobs
) == 0)
753 cleantime
= time(NULL
) - 60;
755 _cupsRWLockWrite(&(printer
->rwlock
));
756 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
758 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
759 if (job
->completed
&& job
->completed
< cleantime
)
761 cupsArrayRemove(printer
->jobs
, job
);
766 _cupsRWUnlock(&(printer
->rwlock
));
771 * 'compare_jobs()' - Compare two jobs.
774 static int /* O - Result of comparison */
775 compare_jobs(_ipp_job_t
*a
, /* I - First job */
776 _ipp_job_t
*b
) /* I - Second job */
778 return (b
->id
- a
->id
);
783 * 'copy_attributes()' - Copy attributes from one request to another.
787 copy_attributes(ipp_t
*to
, /* I - Destination request */
788 ipp_t
*from
, /* I - Source request */
789 cups_array_t
*ra
, /* I - Requested attributes */
790 ipp_tag_t group_tag
, /* I - Group to copy */
791 int quickcopy
) /* I - Do a quick copy? */
793 _ipp_filter_t filter
; /* Filter data */
797 filter
.group_tag
= group_tag
;
799 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
804 * 'copy_job_attrs()' - Copy job attributes to the response.
809 _ipp_client_t
*client
, /* I - Client */
810 _ipp_job_t
*job
, /* I - Job */
811 cups_array_t
*ra
) /* I - requested-attributes */
813 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
815 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
818 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
820 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
823 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
826 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
828 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
831 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
832 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
834 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
835 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
837 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
838 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
840 if (!ra
|| cupsArrayFind(ra
, "job-state"))
841 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
842 "job-state", job
->state
);
844 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
848 case IPP_JSTATE_PENDING
:
849 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
852 case IPP_JSTATE_HELD
:
854 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
855 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
856 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
858 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
861 case IPP_JSTATE_PROCESSING
:
863 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
865 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
868 case IPP_JSTATE_STOPPED
:
869 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
872 case IPP_JSTATE_CANCELED
:
873 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
876 case IPP_JSTATE_ABORTED
:
877 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
880 case IPP_JSTATE_COMPLETED
:
881 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
886 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
890 case IPP_JSTATE_PENDING
:
891 ippAddString(client
->response
, IPP_TAG_JOB
,
892 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
896 case IPP_JSTATE_HELD
:
898 ippAddString(client
->response
, IPP_TAG_JOB
,
899 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
900 "job-state-reasons", NULL
, "job-incoming");
901 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
902 ippAddString(client
->response
, IPP_TAG_JOB
,
903 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
904 "job-state-reasons", NULL
, "job-hold-until-specified");
906 ippAddString(client
->response
, IPP_TAG_JOB
,
907 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
908 "job-state-reasons", NULL
, "job-data-insufficient");
911 case IPP_JSTATE_PROCESSING
:
913 ippAddString(client
->response
, IPP_TAG_JOB
,
914 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
915 "job-state-reasons", NULL
, "processing-to-stop-point");
917 ippAddString(client
->response
, IPP_TAG_JOB
,
918 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
919 "job-state-reasons", NULL
, "job-printing");
922 case IPP_JSTATE_STOPPED
:
923 ippAddString(client
->response
, IPP_TAG_JOB
,
924 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
925 NULL
, "job-stopped");
928 case IPP_JSTATE_CANCELED
:
929 ippAddString(client
->response
, IPP_TAG_JOB
,
930 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
931 NULL
, "job-canceled-by-user");
934 case IPP_JSTATE_ABORTED
:
935 ippAddString(client
->response
, IPP_TAG_JOB
,
936 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
937 NULL
, "aborted-by-system");
940 case IPP_JSTATE_COMPLETED
:
941 ippAddString(client
->response
, IPP_TAG_JOB
,
942 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
943 NULL
, "job-completed-successfully");
948 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
949 ippAddInteger(client
->response
, IPP_TAG_JOB
,
950 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
951 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
953 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
954 ippAddInteger(client
->response
, IPP_TAG_JOB
,
955 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
956 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
961 * 'create_client()' - Accept a new network connection and create a client
965 static _ipp_client_t
* /* O - Client */
966 create_client(_ipp_printer_t
*printer
, /* I - Printer */
967 int sock
) /* I - Listen socket */
969 _ipp_client_t
*client
; /* Client */
972 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
974 perror("Unable to allocate memory for client");
978 client
->printer
= printer
;
981 * Accept the client and get the remote address...
984 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
986 perror("Unable to accept client connection");
993 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
996 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
1003 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
1007 static _ipp_job_t
* /* O - Job */
1008 create_job(_ipp_client_t
*client
) /* I - Client */
1010 _ipp_job_t
*job
; /* Job */
1011 ipp_attribute_t
*attr
; /* Job attribute */
1012 char uri
[1024], /* job-uri value */
1013 uuid
[64]; /* job-uuid value */
1016 _cupsRWLockWrite(&(client
->printer
->rwlock
));
1017 if (client
->printer
->active_job
&&
1018 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
1021 * Only accept a single job at a time...
1024 _cupsRWLockWrite(&(client
->printer
->rwlock
));
1029 * Allocate and initialize the job object...
1032 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
1034 perror("Unable to allocate memory for job");
1038 job
->printer
= client
->printer
;
1039 job
->attrs
= ippNew();
1040 job
->state
= IPP_JSTATE_HELD
;
1044 * Copy all of the job attributes...
1047 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
1050 * Get the requesting-user-name, document format, and priority...
1053 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1054 job
->username
= ippGetString(attr
, 0, NULL
);
1056 job
->username
= "anonymous";
1058 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1060 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1062 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1063 job
->format
= ippGetString(attr
, 0, NULL
);
1064 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1065 job
->format
= ippGetString(attr
, 0, NULL
);
1067 job
->format
= "application/octet-stream";
1070 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1071 job
->impressions
= ippGetInteger(attr
, 0);
1073 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1074 job
->name
= ippGetString(attr
, 0, NULL
);
1077 * Add job description attributes and add to the jobs array...
1080 job
->id
= client
->printer
->next_job_id
++;
1082 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1083 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1085 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1086 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1087 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1088 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1089 if ((attr
= ippFindAttribute(client
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
1090 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, ippGetString(attr
, 0, NULL
));
1092 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1093 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1095 cupsArrayAdd(client
->printer
->jobs
, job
);
1096 client
->printer
->active_job
= job
;
1098 _cupsRWUnlock(&(client
->printer
->rwlock
));
1105 * 'create_job_filename()' - Create the filename for a document in a job.
1108 static void create_job_filename(
1109 _ipp_printer_t
*printer
, /* I - Printer */
1110 _ipp_job_t
*job
, /* I - Job */
1111 char *fname
, /* I - Filename buffer */
1112 size_t fnamesize
) /* I - Size of filename buffer */
1114 char name
[256], /* "Safe" filename */
1115 *nameptr
; /* Pointer into filename */
1116 const char *ext
, /* Filename extension */
1117 *job_name
; /* job-name value */
1118 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1122 * Make a name from the job-name attribute...
1125 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1126 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1128 job_name
= "untitled";
1130 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1131 if (isalnum(*job_name
& 255) || *job_name
== '-')
1132 *nameptr
++ = (char)tolower(*job_name
& 255);
1139 * Figure out the extension...
1142 if (!strcasecmp(job
->format
, "image/jpeg"))
1144 else if (!strcasecmp(job
->format
, "image/png"))
1146 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1148 else if (!strcasecmp(job
->format
, "image/urf"))
1150 else if (!strcasecmp(job
->format
, "application/pdf"))
1152 else if (!strcasecmp(job
->format
, "application/postscript"))
1158 * Create a filename with the job-id, job-name, and document-format (extension)...
1161 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1166 * 'create_listener()' - Create a listener socket.
1169 static int /* O - Listener socket or -1 on error */
1170 create_listener(int family
, /* I - Address family */
1171 int port
) /* I - Port number */
1173 int sock
; /* Listener socket */
1174 http_addrlist_t
*addrlist
; /* Listen address */
1175 char service
[255]; /* Service port */
1178 snprintf(service
, sizeof(service
), "%d", port
);
1179 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1182 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1184 httpAddrFreeList(addrlist
);
1191 * 'create_media_col()' - Create a media-col value.
1194 static ipp_t
* /* O - media-col collection */
1195 create_media_col(const char *media
, /* I - Media name */
1196 const char *source
, /* I - Media source */
1197 const char *type
, /* I - Media type */
1198 int width
, /* I - x-dimension in 2540ths */
1199 int length
, /* I - y-dimension in 2540ths */
1200 int margins
) /* I - Value for margins */
1202 ipp_t
*media_col
= ippNew(), /* media-col value */
1203 *media_size
= create_media_size(width
, length
);
1204 /* media-size value */
1205 char media_key
[256]; /* media-key value */
1209 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1211 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1213 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1215 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1217 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1219 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1220 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1221 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1222 "media-bottom-margin", margins
);
1223 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1224 "media-left-margin", margins
);
1225 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1226 "media-right-margin", margins
);
1227 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1228 "media-top-margin", margins
);
1230 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1232 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1234 ippDelete(media_size
);
1241 * 'create_media_size()' - Create a media-size value.
1244 static ipp_t
* /* O - media-col collection */
1245 create_media_size(int width
, /* I - x-dimension in 2540ths */
1246 int length
) /* I - y-dimension in 2540ths */
1248 ipp_t
*media_size
= ippNew(); /* media-size value */
1251 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1253 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1256 return (media_size
);
1261 * 'create_printer()' - Create, register, and listen for connections to a
1265 static _ipp_printer_t
* /* O - Printer */
1266 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1267 const char *name
, /* I - printer-name */
1268 const char *location
, /* I - printer-location */
1269 const char *make
, /* I - printer-make-and-model */
1270 const char *model
, /* I - printer-make-and-model */
1271 const char *icon
, /* I - printer-icons */
1272 const char *docformats
, /* I - document-format-supported */
1273 int ppm
, /* I - Pages per minute in grayscale */
1274 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1275 int duplex
, /* I - 1 = duplex, 0 = simplex */
1276 int port
, /* I - Port for listeners or 0 for auto */
1277 int pin
, /* I - Require PIN printing */
1278 const char *subtype
, /* I - Bonjour service subtype */
1279 const char *directory
, /* I - Spool directory */
1280 const char *command
, /* I - Command to run on job files */
1281 const char *attrfile
) /* I - Attributes file */
1283 int i
, j
; /* Looping vars */
1284 _ipp_printer_t
*printer
; /* Printer */
1286 char path
[1024]; /* Full path to command */
1288 char uri
[1024], /* Printer URI */
1290 securi
[1024], /* Secure printer URI */
1291 *uris
[2], /* All URIs */
1292 #endif /* HAVE_SSL */
1293 icons
[1024], /* printer-icons URI */
1294 adminurl
[1024], /* printer-more-info URI */
1295 supplyurl
[1024],/* printer-supply-info-uri URI */
1296 device_id
[1024],/* printer-device-id */
1297 make_model
[128],/* printer-make-and-model */
1298 uuid
[128]; /* printer-uuid */
1299 int num_formats
; /* Number of document-format-supported values */
1300 char *defformat
, /* document-format-default value */
1301 *formats
[100], /* document-format-supported values */
1302 *ptr
; /* Pointer into string */
1303 const char *prefix
; /* Prefix string */
1304 int num_database
; /* Number of database values */
1305 ipp_attribute_t
*media_col_database
,
1306 /* media-col-database value */
1307 *media_size_supported
;
1308 /* media-size-supported value */
1309 ipp_t
*media_col_default
;
1310 /* media-col-default value */
1311 int media_col_index
;/* Current media-col-database value */
1312 int k_supported
; /* Maximum file size supported */
1314 struct statvfs spoolinfo
; /* FS info for spool directory */
1315 double spoolsize
; /* FS size */
1316 #elif defined(HAVE_STATFS)
1317 struct statfs spoolinfo
; /* FS info for spool directory */
1318 double spoolsize
; /* FS size */
1319 #endif /* HAVE_STATVFS */
1320 static const int orients
[4] = /* orientation-requested-supported values */
1322 IPP_ORIENT_PORTRAIT
,
1323 IPP_ORIENT_LANDSCAPE
,
1324 IPP_ORIENT_REVERSE_LANDSCAPE
,
1325 IPP_ORIENT_REVERSE_PORTRAIT
1327 static const char * const versions
[] =/* ipp-versions-supported values */
1333 static const char * const features
[] =/* ipp-features-supported values */
1337 static const int ops
[] = /* operations-supported values */
1341 IPP_OP_VALIDATE_JOB
,
1343 IPP_OP_SEND_DOCUMENT
,
1346 IPP_OP_GET_JOB_ATTRIBUTES
,
1348 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1349 IPP_OP_CANCEL_MY_JOBS
,
1351 IPP_OP_IDENTIFY_PRINTER
1353 static const char * const charsets
[] =/* charset-supported values */
1358 static const char * const compressions
[] =/* compression-supported values */
1363 #endif /* HAVE_LIBZ */
1366 static const char * const identify_actions
[] =
1371 static const char * const job_creation
[] =
1372 { /* job-creation-attributes-supported values */
1374 "ipp-attribute-fidelity",
1376 "job-accounting-user-id",
1382 "multiple-document-handling",
1383 "orientation-requested",
1387 static const char * const media_col_supported
[] =
1388 { /* media-col-supported values */
1389 "media-bottom-margin",
1390 "media-left-margin",
1391 "media-right-margin",
1397 static const int media_xxx_margin_supported
[] =
1398 { /* media-xxx-margin-supported values */
1402 static const char * const multiple_document_handling
[] =
1403 { /* multiple-document-handling-supported values */
1404 "separate-documents-uncollated-copies",
1405 "separate-documents-collated-copies"
1407 static const char * const overrides
[] =
1408 { /* overrides-supported */
1412 static const char * const print_color_mode_supported
[] =
1413 { /* print-color-mode-supported values */
1418 static const int print_quality_supported
[] =
1419 { /* print-quality-supported values */
1424 static const int pwg_raster_document_resolution_supported
[] =
1429 static const char * const pwg_raster_document_type_supported
[] =
1437 static const char * const reference_uri_schemes_supported
[] =
1438 { /* reference-uri-schemes-supported */
1444 #endif /* HAVE_SSL */
1446 static const char * const sides_supported
[] =
1447 { /* sides-supported values */
1449 "two-sided-long-edge",
1450 "two-sided-short-edge"
1452 static const char * const urf_supported
[] =
1453 { /* urf-supported values */
1456 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1464 static const char * const uri_authentication_supported
[] =
1465 { /* uri-authentication-supported values */
1469 static const char * const uri_security_supported
[] =
1470 { /* uri-security-supported values */
1474 #endif /* HAVE_SSL */
1475 static const char * const which_jobs
[] =
1476 { /* which-jobs-supported values */
1485 "processing-stopped"
1491 * If a command was specified, make sure it exists and is executable...
1496 if (*command
== '/' || !strncmp(command
, "./", 2))
1498 if (access(command
, X_OK
))
1500 fprintf(stderr
, "ippserver: Unable to execute command \"%s\": %s\n", command
, strerror(errno
));
1506 if (!cupsFileFind(command
, getenv("PATH"), 1, path
, sizeof(path
)))
1508 fprintf(stderr
, "ippserver: Unable to find command \"%s\".\n", command
);
1518 * Allocate memory for the printer...
1521 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1523 perror("ippserver: Unable to allocate memory for printer");
1529 printer
->name
= strdup(name
);
1530 printer
->dnssd_name
= strdup(printer
->name
);
1531 printer
->command
= command
? strdup(command
) : NULL
;
1532 printer
->directory
= strdup(directory
);
1533 printer
->hostname
= strdup(servername
);
1534 printer
->port
= port
;
1535 printer
->start_time
= time(NULL
);
1536 printer
->config_time
= printer
->start_time
;
1537 printer
->state
= IPP_PSTATE_IDLE
;
1538 printer
->state_reasons
= _IPP_PREASON_NONE
;
1539 printer
->state_time
= printer
->start_time
;
1540 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1541 printer
->next_job_id
= 1;
1543 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1544 printer
->uri
= strdup(uri
);
1545 printer
->urilen
= strlen(uri
);
1548 httpAssembleURI(HTTP_URI_CODING_ALL
, securi
, sizeof(securi
), "ipps", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1549 #endif /* HAVE_SSL */
1552 printer
->icon
= strdup(icon
);
1554 printer
->main_size
= _IPP_MEDIA_SIZE_A4
;
1555 printer
->main_type
= _IPP_MEDIA_TYPE_STATIONERY
;
1556 printer
->main_level
= 500;
1558 printer
->envelope_size
= _IPP_MEDIA_SIZE_NONE
;
1559 printer
->envelope_level
= 0;
1561 printer
->photo_size
= _IPP_MEDIA_SIZE_NONE
;
1562 printer
->photo_type
= _IPP_MEDIA_TYPE_NONE
;
1563 printer
->photo_level
= 0;
1565 printer
->supplies
[_IPP_SUPPLY_CYAN
] = 100;
1566 printer
->supplies
[_IPP_SUPPLY_MAGENTA
] = 100;
1567 printer
->supplies
[_IPP_SUPPLY_YELLOW
] = 100;
1568 printer
->supplies
[_IPP_SUPPLY_BLACK
] = 100;
1569 printer
->supplies
[_IPP_SUPPLY_WASTE
] = 0;
1571 _cupsRWInit(&(printer
->rwlock
));
1574 * Create the listener sockets...
1577 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1579 perror("Unable to create IPv4 listener");
1583 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1585 perror("Unable to create IPv6 listener");
1590 * Prepare values for the printer attributes...
1593 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/icon.png");
1594 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/");
1595 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/supplies");
1599 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1600 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1601 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1604 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1607 formats
[0] = strdup(docformats
);
1608 defformat
= formats
[0];
1609 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1612 formats
[num_formats
++] = ptr
;
1614 if (!strcasecmp(ptr
, "application/octet-stream"))
1618 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1619 ptr
= device_id
+ strlen(device_id
);
1621 for (i
= 0; i
< num_formats
; i
++)
1623 if (!strcasecmp(formats
[i
], "application/pdf"))
1624 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1625 else if (!strcasecmp(formats
[i
], "application/postscript"))
1626 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1627 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1628 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1629 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1630 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1631 else if (!strcasecmp(formats
[i
], "image/png"))
1632 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1633 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1634 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1639 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
1646 * Get the maximum spool size based on the size of the filesystem used for
1647 * the spool directory. If the host OS doesn't support the statfs call
1648 * or the filesystem is larger than 2TiB, always report INT_MAX.
1652 if (statvfs(printer
->directory
, &spoolinfo
))
1653 k_supported
= INT_MAX
;
1654 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1655 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1656 k_supported
= INT_MAX
;
1658 k_supported
= (int)spoolsize
;
1660 #elif defined(HAVE_STATFS)
1661 if (statfs(printer
->directory
, &spoolinfo
))
1662 k_supported
= INT_MAX
;
1663 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1664 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1665 k_supported
= INT_MAX
;
1667 k_supported
= (int)spoolsize
;
1670 k_supported
= INT_MAX
;
1671 #endif /* HAVE_STATVFS */
1674 * Create the printer attributes. This list of attributes is sorted to improve
1675 * performance when the client provides a requested-attributes attribute...
1678 printer
->attrs
= ippNew();
1681 load_attributes(attrfile
, printer
->attrs
);
1683 /* charset-configured */
1684 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-configured", NULL
, "utf-8");
1686 /* charset-supported */
1687 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]), NULL
, charsets
);
1689 /* color-supported */
1690 if (!ippFindAttribute(printer
->attrs
, "color-supported", IPP_TAG_ZERO
))
1691 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
1693 /* compression-supported */
1694 if (!ippFindAttribute(printer
->attrs
, "compression-supported", IPP_TAG_ZERO
))
1695 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "compression-supported", (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
, compressions
);
1697 /* copies-default */
1698 if (!ippFindAttribute(printer
->attrs
, "copies-default", IPP_TAG_ZERO
))
1699 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
1701 /* copies-supported */
1702 if (!ippFindAttribute(printer
->attrs
, "copies-supported", IPP_TAG_ZERO
))
1703 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1705 /* document-format-default */
1706 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1707 "document-format-default", NULL
, defformat
);
1709 /* document-format-supported */
1710 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1711 "document-format-supported", num_formats
, NULL
,
1712 (const char * const *)formats
);
1714 /* document-password-supported */
1715 if (!ippFindAttribute(printer
->attrs
, "document-password-supported", IPP_TAG_ZERO
))
1716 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1718 /* finishings-default */
1719 if (!ippFindAttribute(printer
->attrs
, "finishings-default", IPP_TAG_ZERO
))
1720 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
1722 /* finishings-supported */
1723 if (!ippFindAttribute(printer
->attrs
, "finishings-supported", IPP_TAG_ZERO
))
1724 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
1726 /* generated-natural-language-supported */
1727 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "generated-natural-language-supported", NULL
, "en");
1729 /* identify-actions-default */
1730 if (!ippFindAttribute(printer
->attrs
, "identify-actions-default", IPP_TAG_ZERO
))
1731 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1733 /* identify-actions-supported */
1734 if (!ippFindAttribute(printer
->attrs
, "identify-actions-supported", IPP_TAG_ZERO
))
1735 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
);
1737 /* ipp-features-supported */
1738 if (!ippFindAttribute(printer
->attrs
, "ipp-features-supported", IPP_TAG_ZERO
))
1739 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1741 /* ipp-versions-supported */
1742 if (!ippFindAttribute(printer
->attrs
, "ipp-versions-supported", IPP_TAG_ZERO
))
1743 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1745 /* job-account-id-default */
1746 if (!ippFindAttribute(printer
->attrs
, "job-account-id-default", IPP_TAG_ZERO
))
1747 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1749 /* job-account-id-supported */
1750 if (!ippFindAttribute(printer
->attrs
, "job-account-id-supported", IPP_TAG_ZERO
))
1751 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1753 /* job-accounting-user-id-default */
1754 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-default", IPP_TAG_ZERO
))
1755 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1757 /* job-accounting-user-id-supported */
1758 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-supported", IPP_TAG_ZERO
))
1759 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1761 /* job-creation-attributes-supported */
1762 if (!ippFindAttribute(printer
->attrs
, "job-creation-attributes-supported", IPP_TAG_ZERO
))
1763 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
);
1765 /* job-ids-supported */
1766 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1768 /* job-k-octets-supported */
1769 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1772 /* job-password-supported */
1773 if (!ippFindAttribute(printer
->attrs
, "job-password-supported", IPP_TAG_ZERO
))
1774 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-password-supported", 4);
1776 /* job-priority-default */
1777 if (!ippFindAttribute(printer
->attrs
, "job-priority-default", IPP_TAG_ZERO
))
1778 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-default", 50);
1780 /* job-priority-supported */
1781 if (!ippFindAttribute(printer
->attrs
, "job-priority-supported", IPP_TAG_ZERO
))
1782 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-supported", 100);
1784 /* job-sheets-default */
1785 if (!ippFindAttribute(printer
->attrs
, "job-sheets-default", IPP_TAG_ZERO
))
1786 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-default", NULL
, "none");
1788 /* job-sheets-supported */
1789 if (!ippFindAttribute(printer
->attrs
, "job-sheets-supported", IPP_TAG_ZERO
))
1790 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-supported", NULL
, "none");
1792 /* media-bottom-margin-supported */
1793 if (!ippFindAttribute(printer
->attrs
, "media-bottom-margin-supported", IPP_TAG_ZERO
))
1794 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
);
1796 /* media-col-database */
1797 if (!ippFindAttribute(printer
->attrs
, "media-col-database", IPP_TAG_ZERO
))
1799 for (num_database
= 0, i
= 0;
1800 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1803 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1804 num_database
+= 3; /* auto + manual + envelope */
1805 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1806 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1808 num_database
+= 2; /* Regular + borderless */
1811 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-database", num_database
, NULL
);
1812 for (media_col_index
= 0, i
= 0;
1813 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1816 switch (media_col_sizes
[i
][2])
1820 * Regular + borderless for the general class; no source/type
1824 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]));
1825 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]));
1828 case _IPP_ENV_ONLY
:
1830 * Regular margins for "auto", "manual", and "envelope" sources.
1833 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]));
1834 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]));
1835 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]));
1837 case _IPP_PHOTO_ONLY
:
1839 * Photos have specific media types and can only be printed via
1840 * the auto, manual, and photo sources...
1844 j
< (int)(sizeof(media_type_supported
) /
1845 sizeof(media_type_supported
[0]));
1848 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1851 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]));
1852 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]));
1853 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]));
1860 /* media-col-default */
1861 if (!ippFindAttribute(printer
->attrs
, "media-col-default", IPP_TAG_ZERO
))
1863 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]);
1865 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1867 ippDelete(media_col_default
);
1870 /* media-col-supported */
1871 if (!ippFindAttribute(printer
->attrs
, "media-col-supported", IPP_TAG_ZERO
))
1872 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
);
1875 if (!ippFindAttribute(printer
->attrs
, "media-default", IPP_TAG_ZERO
))
1876 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media_supported
[0]);
1878 /* media-left-margin-supported */
1879 if (!ippFindAttribute(printer
->attrs
, "media-left-margin-supported", IPP_TAG_ZERO
))
1880 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
);
1882 /* media-right-margin-supported */
1883 if (!ippFindAttribute(printer
->attrs
, "media-right-margin-supported", IPP_TAG_ZERO
))
1884 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
);
1886 /* media-supported */
1887 if (!ippFindAttribute(printer
->attrs
, "media-supported", IPP_TAG_ZERO
))
1888 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
);
1890 /* media-size-supported */
1891 if (!ippFindAttribute(printer
->attrs
, "media-size-supported", IPP_TAG_ZERO
))
1893 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-size-supported", (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0])), NULL
);
1896 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1899 ipp_t
*size
= create_media_size(media_col_sizes
[i
][0], media_col_sizes
[i
][1]);
1901 ippSetCollection(printer
->attrs
, &media_size_supported
, i
, size
);
1906 /* media-source-supported */
1907 if (!ippFindAttribute(printer
->attrs
, "media-source-supported", IPP_TAG_ZERO
))
1908 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
);
1910 /* media-top-margin-supported */
1911 if (!ippFindAttribute(printer
->attrs
, "media-top-margin-supported", IPP_TAG_ZERO
))
1912 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
);
1914 /* media-type-supported */
1915 if (!ippFindAttribute(printer
->attrs
, "media-type-supported", IPP_TAG_ZERO
))
1916 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
);
1918 /* multiple-document-handling-supported */
1919 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
);
1921 /* multiple-document-jobs-supported */
1922 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1924 /* multiple-operation-time-out */
1925 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1927 /* multiple-operation-time-out-action */
1928 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1930 /* natural-language-configured */
1931 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1932 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1933 "natural-language-configured", NULL
, "en");
1935 /* number-up-default */
1936 if (!ippFindAttribute(printer
->attrs
, "number-up-default", IPP_TAG_ZERO
))
1937 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-default", 1);
1939 /* number-up-supported */
1940 if (!ippFindAttribute(printer
->attrs
, "number-up-supported", IPP_TAG_ZERO
))
1941 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-supported", 1);
1943 /* operations-supported */
1944 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1946 /* orientation-requested-default */
1947 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-default", IPP_TAG_ZERO
))
1948 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "orientation-requested-default", 0);
1950 /* orientation-requested-supported */
1951 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-supported", IPP_TAG_ZERO
))
1952 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", 4, orients
);
1954 /* output-bin-default */
1955 if (!ippFindAttribute(printer
->attrs
, "output-bin-default", IPP_TAG_ZERO
))
1956 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
1958 /* output-bin-supported */
1959 if (!ippFindAttribute(printer
->attrs
, "output-bin-supported", IPP_TAG_ZERO
))
1960 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
1962 /* overrides-supported */
1963 if (!ippFindAttribute(printer
->attrs
, "overrides-supported", IPP_TAG_ZERO
))
1964 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1966 /* page-ranges-supported */
1967 if (!ippFindAttribute(printer
->attrs
, "page-ranges-supported", IPP_TAG_ZERO
))
1968 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1970 /* pages-per-minute */
1971 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1972 "pages-per-minute", ppm
);
1974 /* pages-per-minute-color */
1976 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1977 "pages-per-minute-color", ppm_color
);
1979 /* pdl-override-supported */
1980 if (!ippFindAttribute(printer
->attrs
, "pdl-override-supported", IPP_TAG_ZERO
))
1981 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
1983 /* preferred-attributes-supported */
1984 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "preferred-attributes-supported", 0);
1986 /* print-color-mode-default */
1987 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-default", IPP_TAG_ZERO
))
1988 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1990 /* print-color-mode-supported */
1991 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-supported", IPP_TAG_ZERO
))
1992 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
);
1994 /* print-content-optimize-default */
1995 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-default", IPP_TAG_ZERO
))
1996 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1998 /* print-content-optimize-supported */
1999 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-supported", IPP_TAG_ZERO
))
2000 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
2002 /* print-rendering-intent-default */
2003 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-default", IPP_TAG_ZERO
))
2004 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
2006 /* print-rendering-intent-supported */
2007 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-supported", IPP_TAG_ZERO
))
2008 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
2010 /* print-quality-default */
2011 if (!ippFindAttribute(printer
->attrs
, "print-quality-default", IPP_TAG_ZERO
))
2012 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
2014 /* print-quality-supported */
2015 if (!ippFindAttribute(printer
->attrs
, "print-quality-supported", IPP_TAG_ZERO
))
2016 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
);
2018 /* printer-device-id */
2019 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2020 "printer-device-id", NULL
, device_id
);
2022 /* printer-get-attributes-supported */
2023 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
2025 /* printer-geo-location */
2026 if (!ippFindAttribute(printer
->attrs
, "printer-geo-location", IPP_TAG_ZERO
))
2027 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
2030 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
2031 "printer-icons", NULL
, icons
);
2033 /* printer-is-accepting-jobs */
2034 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
2037 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
2039 /* printer-location */
2040 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2041 "printer-location", NULL
, location
);
2043 /* printer-make-and-model */
2044 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2045 "printer-make-and-model", NULL
, make_model
);
2047 /* printer-mandatory-job-attributes */
2048 if (pin
&& !ippFindAttribute(printer
->attrs
, "", IPP_TAG_ZERO
))
2050 static const char * const names
[] =
2053 "job-accounting-user-id",
2057 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2058 "printer-mandatory-job-attributes",
2059 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
2062 /* printer-more-info */
2063 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
2066 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
2068 /* printer-organization */
2069 if (!ippFindAttribute(printer
->attrs
, "printer-organization", IPP_TAG_ZERO
))
2070 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
2072 /* printer-organizational-unit */
2073 if (!ippFindAttribute(printer
->attrs
, "printer-organizational-unit", IPP_TAG_ZERO
))
2074 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
2076 /* printer-resolution-default */
2077 if (!ippFindAttribute(printer
->attrs
, "printer-resolution-default", IPP_TAG_ZERO
))
2078 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
2080 /* printer-resolution-supported */
2081 if (!ippFindAttribute(printer
->attrs
, "printer-resolutions-supported", IPP_TAG_ZERO
))
2082 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
2084 /* printer-supply-description */
2085 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
);
2087 /* printer-supply-info-uri */
2088 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
2090 /* printer-uri-supported */
2095 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", 2, NULL
, (const char **)uris
);
2098 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
2099 #endif /* HAVE_SSL */
2102 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
2103 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
2105 /* pwg-raster-document-xxx-supported */
2106 for (i
= 0; i
< num_formats
; i
++)
2107 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
2110 if (i
< num_formats
)
2112 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-resolution-supported", IPP_TAG_ZERO
))
2113 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
);
2114 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-sheet-back", IPP_TAG_ZERO
))
2115 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "pwg-raster-document-sheet-back", NULL
, "normal");
2116 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-type-supported", IPP_TAG_ZERO
))
2117 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
);
2120 /* reference-uri-scheme-supported */
2121 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
);
2124 if (!ippFindAttribute(printer
->attrs
, "sides-default", IPP_TAG_ZERO
))
2125 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
2127 /* sides-supported */
2128 if (!ippFindAttribute(printer
->attrs
, "sides-supported", IPP_TAG_ZERO
))
2129 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
2132 for (i
= 0; i
< num_formats
; i
++)
2133 if (!strcasecmp(formats
[i
], "image/urf"))
2136 if (i
< num_formats
&& !ippFindAttribute(printer
->attrs
, "urf-supported", IPP_TAG_ZERO
))
2137 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2139 /* uri-authentication-supported */
2141 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_supported
);
2143 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "none");
2144 #endif /* HAVE_SSL */
2146 /* uri-security-supported */
2148 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", 2, NULL
, uri_security_supported
);
2150 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", NULL
, "none");
2151 #endif /* HAVE_SSL */
2153 /* which-jobs-supported */
2154 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
);
2158 debug_attributes("Printer", printer
->attrs
, 0);
2161 * Register the printer with Bonjour...
2164 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2175 * If we get here we were unable to create the printer...
2180 delete_printer(printer
);
2186 * 'debug_attributes()' - Print attributes in a request or response.
2190 debug_attributes(const char *title
, /* I - Title */
2191 ipp_t
*ipp
, /* I - Request/response */
2192 int type
) /* I - 0 = object, 1 = request, 2 = response */
2194 ipp_tag_t group_tag
; /* Current group */
2195 ipp_attribute_t
*attr
; /* Current attribute */
2196 char buffer
[2048]; /* String buffer for value */
2197 int major
, minor
; /* Version */
2203 fprintf(stderr
, "%s:\n", title
);
2204 major
= ippGetVersion(ipp
, &minor
);
2205 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2207 fprintf(stderr
, " operation-id=%s(%04x)\n",
2208 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2210 fprintf(stderr
, " status-code=%s(%04x)\n",
2211 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2212 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2214 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2216 attr
= ippNextAttribute(ipp
))
2218 if (ippGetGroupTag(attr
) != group_tag
)
2220 group_tag
= ippGetGroupTag(attr
);
2221 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2224 if (ippGetName(attr
))
2226 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2227 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2228 ippGetCount(attr
) > 1 ? "1setOf " : "",
2229 ippTagString(ippGetValueTag(attr
)), buffer
);
2236 * 'delete_client()' - Close the socket and free all memory used by a client
2241 delete_client(_ipp_client_t
*client
) /* I - Client */
2244 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2247 * Flush pending writes before closing...
2250 httpFlushWrite(client
->http
);
2256 httpClose(client
->http
);
2258 ippDelete(client
->request
);
2259 ippDelete(client
->response
);
2266 * 'delete_job()' - Remove from the printer and free all memory used by a job
2271 delete_job(_ipp_job_t
*job
) /* I - Job */
2274 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2276 ippDelete(job
->attrs
);
2281 unlink(job
->filename
);
2283 free(job
->filename
);
2291 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2292 * used by a printer object.
2296 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2298 if (printer
->ipv4
>= 0)
2299 close(printer
->ipv4
);
2301 if (printer
->ipv6
>= 0)
2302 close(printer
->ipv6
);
2305 if (printer
->printer_ref
)
2306 DNSServiceRefDeallocate(printer
->printer_ref
);
2307 if (printer
->ipp_ref
)
2308 DNSServiceRefDeallocate(printer
->ipp_ref
);
2309 if (printer
->ipps_ref
)
2310 DNSServiceRefDeallocate(printer
->ipps_ref
);
2311 if (printer
->http_ref
)
2312 DNSServiceRefDeallocate(printer
->http_ref
);
2313 #elif defined(HAVE_AVAHI)
2314 avahi_threaded_poll_lock(DNSSDMaster
);
2316 if (printer
->printer_ref
)
2317 avahi_entry_group_free(printer
->printer_ref
);
2318 if (printer
->ipp_ref
)
2319 avahi_entry_group_free(printer
->ipp_ref
);
2320 if (printer
->ipps_ref
)
2321 avahi_entry_group_free(printer
->ipps_ref
);
2322 if (printer
->http_ref
)
2323 avahi_entry_group_free(printer
->http_ref
);
2325 avahi_threaded_poll_unlock(DNSSDMaster
);
2326 #endif /* HAVE_DNSSD */
2328 if (printer
->dnssd_name
)
2329 free(printer
->dnssd_name
);
2331 free(printer
->name
);
2333 free(printer
->icon
);
2334 if (printer
->command
)
2335 free(printer
->command
);
2336 if (printer
->directory
)
2337 free(printer
->directory
);
2338 if (printer
->hostname
)
2339 free(printer
->hostname
);
2343 ippDelete(printer
->attrs
);
2344 cupsArrayDelete(printer
->jobs
);
2352 * 'dnssd_callback()' - Handle Bonjour registration events.
2355 static void DNSSD_API
2357 DNSServiceRef sdRef
, /* I - Service reference */
2358 DNSServiceFlags flags
, /* I - Status flags */
2359 DNSServiceErrorType errorCode
, /* I - Error, if any */
2360 const char *name
, /* I - Service name */
2361 const char *regtype
, /* I - Service type */
2362 const char *domain
, /* I - Domain for service */
2363 _ipp_printer_t
*printer
) /* I - Printer */
2371 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2372 regtype
, (int)errorCode
);
2375 else if (strcasecmp(name
, printer
->dnssd_name
))
2378 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2380 /* No lock needed since only the main thread accesses/changes this */
2381 free(printer
->dnssd_name
);
2382 printer
->dnssd_name
= strdup(name
);
2387 #elif defined(HAVE_AVAHI)
2389 * 'dnssd_callback()' - Handle Bonjour registration events.
2394 AvahiEntryGroup
*srv
, /* I - Service */
2395 AvahiEntryGroupState state
, /* I - Registration state */
2396 void *context
) /* I - Printer */
2405 * 'dnssd_client_cb()' - Client callback for Avahi.
2407 * Called whenever the client or server state changes...
2412 AvahiClient
*c
, /* I - Client */
2413 AvahiClientState state
, /* I - Current state */
2414 void *userdata
) /* I - User data (unused) */
2424 fprintf(stderr
, "Ignore Avahi state %d.\n", state
);
2427 case AVAHI_CLIENT_FAILURE
:
2428 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
2430 fputs("Avahi server crashed, exiting.\n", stderr
);
2436 #endif /* HAVE_DNSSD */
2440 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2447 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2449 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2453 #elif defined(HAVE_AVAHI)
2454 int error
; /* Error code, if any */
2456 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2458 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2462 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2464 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2468 avahi_threaded_poll_start(DNSSDMaster
);
2469 #endif /* HAVE_DNSSD */
2474 * 'filter_cb()' - Filter printer attributes based on the requested array.
2477 static int /* O - 1 to copy, 0 to ignore */
2478 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2479 ipp_t
*dst
, /* I - Destination (unused) */
2480 ipp_attribute_t
*attr
) /* I - Source attribute */
2483 * Filter attributes as needed...
2486 #ifndef WIN32 /* Avoid MS compiler bug */
2490 ipp_tag_t group
= ippGetGroupTag(attr
);
2491 const char *name
= ippGetName(attr
);
2493 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
)))
2496 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2501 * 'find_job()' - Find a job specified in a request.
2504 static _ipp_job_t
* /* O - Job or NULL */
2505 find_job(_ipp_client_t
*client
) /* I - Client */
2507 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2508 _ipp_job_t key
, /* Job search key */
2509 *job
; /* Matching job, if any */
2512 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2514 const char *uri
= ippGetString(attr
, 0, NULL
);
2516 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2517 uri
[client
->printer
->urilen
] == '/')
2518 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2522 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2523 key
.id
= ippGetInteger(attr
, 0);
2525 _cupsRWLockRead(&(client
->printer
->rwlock
));
2526 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2527 _cupsRWUnlock(&(client
->printer
->rwlock
));
2534 * 'get_collection()' - Get a collection value from a file.
2537 static ipp_t
* /* O - Collection value */
2538 get_collection(FILE *fp
, /* I - File to read from */
2539 const char *filename
, /* I - Attributes filename */
2540 int *linenum
) /* IO - Line number */
2542 char token
[1024], /* Token from file */
2543 attr
[128]; /* Attribute name */
2544 ipp_tag_t value
; /* Current value type */
2545 ipp_t
*col
= ippNew(); /* Collection value */
2546 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
2549 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
2551 if (!strcmp(token
, "}"))
2553 else if (!strcmp(token
, "{") && lastcol
)
2556 * Another collection value
2559 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2560 /* Collection value */
2563 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
2567 else if (!_cups_strcasecmp(token
, "MEMBER"))
2575 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2577 fprintf(stderr
, "ippserver: Missing MEMBER value tag on line %d of \"%s\".\n", *linenum
, filename
);
2581 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
2583 fprintf(stderr
, "ippserver: Bad MEMBER value tag \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2587 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
2589 fprintf(stderr
, "ippserver: Missing MEMBER name on line %d of \"%s\".\n", *linenum
, filename
);
2593 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2595 fprintf(stderr
, "ippserver: Missing MEMBER value on line %d of \"%s\".\n", *linenum
, filename
);
2601 case IPP_TAG_BOOLEAN
:
2602 if (!_cups_strcasecmp(token
, "true"))
2603 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
2605 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
2608 case IPP_TAG_INTEGER
:
2610 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
2613 case IPP_TAG_RESOLUTION
:
2615 int xres
, /* X resolution */
2616 yres
; /* Y resolution */
2617 char units
[6]; /* Units */
2619 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
2620 (_cups_strcasecmp(units
, "dpi") &&
2621 _cups_strcasecmp(units
, "dpc") &&
2622 _cups_strcasecmp(units
, "dpcm") &&
2623 _cups_strcasecmp(units
, "other")))
2625 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2629 if (!_cups_strcasecmp(units
, "dpi"))
2630 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
2631 else if (!_cups_strcasecmp(units
, "dpc") ||
2632 !_cups_strcasecmp(units
, "dpcm"))
2633 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
2635 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
2639 case IPP_TAG_RANGE
:
2641 int lowers
[4], /* Lower value */
2642 uppers
[4], /* Upper values */
2643 num_vals
; /* Number of values */
2646 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
2647 lowers
+ 0, uppers
+ 0,
2648 lowers
+ 1, uppers
+ 1,
2649 lowers
+ 2, uppers
+ 2,
2650 lowers
+ 3, uppers
+ 3);
2652 if ((num_vals
& 1) || num_vals
== 0)
2654 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2658 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
2663 case IPP_TAG_BEGIN_COLLECTION
:
2664 if (!strcmp(token
, "{"))
2666 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2667 /* Collection value */
2671 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
2679 fprintf(stderr
, "ippserver: Bad collection value on line %d of \"%s\".\n", *linenum
, filename
);
2683 case IPP_TAG_STRING
:
2684 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
2688 if (!strchr(token
, ','))
2689 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
2693 * Multiple string values...
2696 int num_values
; /* Number of values */
2697 char *values
[100], /* Values */
2698 *ptr
; /* Pointer to next value */
2704 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
2707 values
[num_values
] = ptr
;
2709 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
2713 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
2714 NULL
, (const char **)values
);
2724 * If we get here there was a parse error; free memory and return.
2736 * 'get_token()' - Get a token from a file.
2739 static char * /* O - Token from file or NULL on EOF */
2740 get_token(FILE *fp
, /* I - File to read from */
2741 char *buf
, /* I - Buffer to read into */
2742 int buflen
, /* I - Length of buffer */
2743 int *linenum
) /* IO - Current line number */
2745 int ch
, /* Character from file */
2746 quote
; /* Quoting character */
2747 char *bufptr
, /* Pointer into buffer */
2748 *bufend
; /* End of buffer */
2754 * Skip whitespace...
2757 while (isspace(ch
= getc(fp
)))
2769 else if (ch
== '\'' || ch
== '\"')
2772 * Quoted text or regular expression...
2777 bufend
= buf
+ buflen
- 1;
2779 while ((ch
= getc(fp
)) != EOF
)
2784 * Escape next character...
2787 if (bufptr
< bufend
)
2788 *bufptr
++ = (char)ch
;
2790 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
2791 *bufptr
++ = (char)ch
;
2793 else if (ch
== quote
)
2795 else if (bufptr
< bufend
)
2796 *bufptr
++ = (char)ch
;
2809 while ((ch
= getc(fp
)) != EOF
)
2815 else if (ch
== '{' || ch
== '}' || ch
== ',')
2825 * Whitespace delimited text...
2831 bufend
= buf
+ buflen
- 1;
2833 while ((ch
= getc(fp
)) != EOF
)
2834 if (isspace(ch
) || ch
== '#')
2836 else if (bufptr
< bufend
)
2837 *bufptr
++ = (char)ch
;
2841 else if (ch
== '\n')
2853 * 'html_escape()' - Write a HTML-safe string.
2857 html_escape(_ipp_client_t
*client
, /* I - Client */
2858 const char *s
, /* I - String to write */
2859 size_t slen
) /* I - Number of characters to write */
2861 const char *start
, /* Start of segment */
2862 *end
; /* End of string */
2866 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2868 while (*s
&& s
< end
)
2870 if (*s
== '&' || *s
== '<')
2873 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2876 httpWrite2(client
->http
, "&", 5);
2878 httpWrite2(client
->http
, "<", 4);
2887 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2892 * 'html_footer()' - Show the web interface footer.
2894 * This function also writes the trailing 0-length chunk.
2898 html_footer(_ipp_client_t
*client
) /* I - Client */
2904 httpWrite2(client
->http
, "", 0);
2909 * 'html_header()' - Show the web interface header and title.
2913 html_header(_ipp_client_t
*client
, /* I - Client */
2914 const char *title
) /* I - Title */
2920 "<title>%s</title>\n"
2921 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2922 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2923 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2924 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2926 "body { font-family: sans-serif; margin: 0; }\n"
2927 "div.body { padding: 0px 10px 10px; }\n"
2928 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2929 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2930 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2931 "table.form th { text-align: right; }\n"
2932 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2933 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2934 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2935 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2936 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2937 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2938 "table.nav td { margin: 0; text-align: center; }\n"
2939 "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"
2940 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2941 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2942 "td.nav:hover { background: #666; color: #fff; }\n"
2943 "td.nav:active { background: #000; color: #ff0; }\n"
2947 "<table class=\"nav\"><tr>"
2948 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2949 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2950 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2952 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2957 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2961 html_printf(_ipp_client_t
*client
, /* I - Client */
2962 const char *format
, /* I - Printf-style format string */
2963 ...) /* I - Additional arguments as needed */
2965 va_list ap
; /* Pointer to arguments */
2966 const char *start
; /* Start of string */
2967 char size
, /* Size character (h, l, L) */
2968 type
; /* Format type character */
2969 int width
, /* Width of field */
2970 prec
; /* Number of characters of precision */
2971 char tformat
[100], /* Temporary format string for sprintf() */
2972 *tptr
, /* Pointer into temporary format */
2973 temp
[1024]; /* Buffer for formatted numbers */
2974 char *s
; /* Pointer to string */
2978 * Loop through the format string, formatting as needed...
2981 va_start(ap
, format
);
2989 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2992 *tptr
++ = *format
++;
2996 httpWrite2(client
->http
, "%", 1);
3001 else if (strchr(" -+#\'", *format
))
3002 *tptr
++ = *format
++;
3007 * Get width from argument...
3011 width
= va_arg(ap
, int);
3013 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
3014 tptr
+= strlen(tptr
);
3020 while (isdigit(*format
& 255))
3022 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3025 width
= width
* 10 + *format
++ - '0';
3031 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3039 * Get precision from argument...
3043 prec
= va_arg(ap
, int);
3045 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
3046 tptr
+= strlen(tptr
);
3052 while (isdigit(*format
& 255))
3054 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3057 prec
= prec
* 10 + *format
++ - '0';
3062 if (*format
== 'l' && format
[1] == 'l')
3066 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
3074 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
3076 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3091 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3100 case 'E' : /* Floating point formats */
3105 if ((size_t)(width
+ 2) > sizeof(temp
))
3108 sprintf(temp
, tformat
, va_arg(ap
, double));
3110 httpWrite2(client
->http
, temp
, strlen(temp
));
3113 case 'B' : /* Integer formats */
3121 if ((size_t)(width
+ 2) > sizeof(temp
))
3124 # ifdef HAVE_LONG_LONG
3126 sprintf(temp
, tformat
, va_arg(ap
, long long));
3128 # endif /* HAVE_LONG_LONG */
3130 sprintf(temp
, tformat
, va_arg(ap
, long));
3132 sprintf(temp
, tformat
, va_arg(ap
, int));
3134 httpWrite2(client
->http
, temp
, strlen(temp
));
3137 case 'p' : /* Pointer value */
3138 if ((size_t)(width
+ 2) > sizeof(temp
))
3141 sprintf(temp
, tformat
, va_arg(ap
, void *));
3143 httpWrite2(client
->http
, temp
, strlen(temp
));
3146 case 'c' : /* Character or character array */
3149 temp
[0] = (char)va_arg(ap
, int);
3151 html_escape(client
, temp
, 1);
3154 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
3157 case 's' : /* String */
3158 if ((s
= va_arg(ap
, char *)) == NULL
)
3161 html_escape(client
, s
, strlen(s
));
3170 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
3177 * 'ipp_cancel_job()' - Cancel a job.
3181 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
3183 _ipp_job_t
*job
; /* Job information */
3190 if ((job
= find_job(client
)) == NULL
)
3192 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3197 * See if the job is already completed, canceled, or aborted; if so,
3198 * we can't cancel...
3203 case IPP_JSTATE_CANCELED
:
3204 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3205 "Job #%d is already canceled - can\'t cancel.", job
->id
);
3208 case IPP_JSTATE_ABORTED
:
3209 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3210 "Job #%d is already aborted - can\'t cancel.", job
->id
);
3213 case IPP_JSTATE_COMPLETED
:
3214 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3215 "Job #%d is already completed - can\'t cancel.", job
->id
);
3223 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3225 if (job
->state
== IPP_JSTATE_PROCESSING
||
3226 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
3230 job
->state
= IPP_JSTATE_CANCELED
;
3231 job
->completed
= time(NULL
);
3234 _cupsRWUnlock(&(client
->printer
->rwlock
));
3236 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3243 * 'ipp_close_job()' - Close an open job.
3247 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
3249 _ipp_job_t
*job
; /* Job information */
3256 if ((job
= find_job(client
)) == NULL
)
3258 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3263 * See if the job is already completed, canceled, or aborted; if so,
3264 * we can't cancel...
3269 case IPP_JSTATE_CANCELED
:
3270 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3271 "Job #%d is canceled - can\'t close.", job
->id
);
3274 case IPP_JSTATE_ABORTED
:
3275 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3276 "Job #%d is aborted - can\'t close.", job
->id
);
3279 case IPP_JSTATE_COMPLETED
:
3280 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3281 "Job #%d is completed - can\'t close.", job
->id
);
3284 case IPP_JSTATE_PROCESSING
:
3285 case IPP_JSTATE_STOPPED
:
3286 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3287 "Job #%d is already closed.", job
->id
);
3291 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3298 * 'ipp_create_job()' - Create a job object.
3302 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
3304 _ipp_job_t
*job
; /* New job */
3305 cups_array_t
*ra
; /* Attributes to send in response */
3309 * Validate print job attributes...
3312 if (!valid_job_attributes(client
))
3314 httpFlush(client
->http
);
3319 * Do we have a file to print?
3322 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3324 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3325 "Unexpected document data following request.");
3333 if ((job
= create_job(client
)) == NULL
)
3335 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3336 "Currently printing another job.");
3341 * Return the job info...
3344 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3346 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3347 cupsArrayAdd(ra
, "job-id");
3348 cupsArrayAdd(ra
, "job-state");
3349 cupsArrayAdd(ra
, "job-state-message");
3350 cupsArrayAdd(ra
, "job-state-reasons");
3351 cupsArrayAdd(ra
, "job-uri");
3353 copy_job_attributes(client
, job
, ra
);
3354 cupsArrayDelete(ra
);
3359 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3363 ipp_get_job_attributes(
3364 _ipp_client_t
*client
) /* I - Client */
3366 _ipp_job_t
*job
; /* Job */
3367 cups_array_t
*ra
; /* requested-attributes */
3370 if ((job
= find_job(client
)) == NULL
)
3372 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3376 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3378 ra
= ippCreateRequestedArray(client
->request
);
3379 copy_job_attributes(client
, job
, ra
);
3380 cupsArrayDelete(ra
);
3385 * 'ipp_get_jobs()' - Get a list of job objects.
3389 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
3391 ipp_attribute_t
*attr
; /* Current attribute */
3392 const char *which_jobs
= NULL
;
3393 /* which-jobs values */
3394 int job_comparison
; /* Job comparison */
3395 ipp_jstate_t job_state
; /* job-state value */
3396 int first_job_id
, /* First job ID */
3397 limit
, /* Maximum number of jobs to return */
3398 count
; /* Number of jobs that match */
3399 const char *username
; /* Username */
3400 _ipp_job_t
*job
; /* Current job pointer */
3401 cups_array_t
*ra
; /* Requested attributes array */
3405 * See if the "which-jobs" attribute have been specified...
3408 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3409 IPP_TAG_KEYWORD
)) != NULL
)
3411 which_jobs
= ippGetString(attr
, 0, NULL
);
3412 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3415 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3417 job_comparison
= -1;
3418 job_state
= IPP_JSTATE_STOPPED
;
3420 else if (!strcmp(which_jobs
, "completed"))
3423 job_state
= IPP_JSTATE_CANCELED
;
3425 else if (!strcmp(which_jobs
, "aborted"))
3428 job_state
= IPP_JSTATE_ABORTED
;
3430 else if (!strcmp(which_jobs
, "all"))
3433 job_state
= IPP_JSTATE_PENDING
;
3435 else if (!strcmp(which_jobs
, "canceled"))
3438 job_state
= IPP_JSTATE_CANCELED
;
3440 else if (!strcmp(which_jobs
, "pending"))
3443 job_state
= IPP_JSTATE_PENDING
;
3445 else if (!strcmp(which_jobs
, "pending-held"))
3448 job_state
= IPP_JSTATE_HELD
;
3450 else if (!strcmp(which_jobs
, "processing"))
3453 job_state
= IPP_JSTATE_PROCESSING
;
3455 else if (!strcmp(which_jobs
, "processing-stopped"))
3458 job_state
= IPP_JSTATE_STOPPED
;
3462 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3463 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3464 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3465 "which-jobs", NULL
, which_jobs
);
3470 * See if they want to limit the number of jobs reported...
3473 if ((attr
= ippFindAttribute(client
->request
, "limit",
3474 IPP_TAG_INTEGER
)) != NULL
)
3476 limit
= ippGetInteger(attr
, 0);
3478 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3483 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3484 IPP_TAG_INTEGER
)) != NULL
)
3486 first_job_id
= ippGetInteger(attr
, 0);
3488 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3495 * See if we only want to see jobs for a specific user...
3500 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3501 IPP_TAG_BOOLEAN
)) != NULL
)
3503 int my_jobs
= ippGetBoolean(attr
, 0);
3505 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3506 my_jobs
? "true" : "false");
3510 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3511 IPP_TAG_NAME
)) == NULL
)
3513 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3514 "Need requesting-user-name with my-jobs.");
3518 username
= ippGetString(attr
, 0, NULL
);
3520 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3521 client
->hostname
, username
);
3526 * OK, build a list of jobs for this printer...
3529 ra
= ippCreateRequestedArray(client
->request
);
3531 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3533 _cupsRWLockRead(&(client
->printer
->rwlock
));
3535 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3536 (limit
<= 0 || count
< limit
) && job
;
3537 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3540 * Filter out jobs that don't match...
3543 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3544 (job_comparison
== 0 && job
->state
!= job_state
) ||
3545 (job_comparison
> 0 && job
->state
< job_state
) ||
3546 job
->id
< first_job_id
||
3547 (username
&& job
->username
&&
3548 strcasecmp(username
, job
->username
)))
3552 ippAddSeparator(client
->response
);
3555 copy_job_attributes(client
, job
, ra
);
3558 cupsArrayDelete(ra
);
3560 _cupsRWUnlock(&(client
->printer
->rwlock
));
3565 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3569 ipp_get_printer_attributes(
3570 _ipp_client_t
*client
) /* I - Client */
3572 cups_array_t
*ra
; /* Requested attributes array */
3573 _ipp_printer_t
*printer
; /* Printer */
3577 * Send the attributes...
3580 ra
= ippCreateRequestedArray(client
->request
);
3581 printer
= client
->printer
;
3583 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3585 _cupsRWLockRead(&(printer
->rwlock
));
3587 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3588 IPP_TAG_CUPS_CONST
);
3590 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3592 int i
, /* Looping var */
3593 num_ready
= 0; /* Number of ready media */
3594 ipp_t
*ready
[3]; /* Ready media */
3596 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3598 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
3599 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);
3601 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);
3603 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3604 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);
3605 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3607 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
3608 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);
3610 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);
3615 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3616 for (i
= 0; i
< num_ready
; i
++)
3617 ippDelete(ready
[i
]);
3620 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3623 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3625 int num_ready
= 0; /* Number of ready media */
3626 const char *ready
[3]; /* Ready media */
3628 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3629 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3631 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3632 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3634 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3635 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3638 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3640 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3643 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3644 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3646 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3647 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3649 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3650 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3653 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3654 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3655 "printer-state", printer
->state
);
3657 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3658 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3660 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3661 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3663 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3665 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3667 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3670 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3672 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3673 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3674 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3675 "printer-state-reasons", NULL
, "none");
3678 ipp_attribute_t
*attr
= NULL
; /* printer-state-reasons */
3679 _ipp_preason_t bit
; /* Reason bit */
3680 int i
; /* Looping var */
3681 char reason
[32]; /* Reason string */
3683 for (i
= 0, bit
= 1; i
< (int)(sizeof(_ipp_preason_strings
) / sizeof(_ipp_preason_strings
[0])); i
++, bit
*= 2)
3685 if (printer
->state_reasons
& bit
)
3687 snprintf(reason
, sizeof(reason
), "%s-%s", _ipp_preason_strings
[i
], printer
->state
== IPP_PSTATE_IDLE
? "report" : printer
->state
== IPP_PSTATE_PROCESSING
? "warning" : "error");
3689 ippSetString(client
->response
, &attr
, ippGetCount(attr
), reason
);
3691 attr
= ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
, reason
);
3697 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3699 int i
; /* Looping var */
3700 char buffer
[256]; /* Supply value buffer */
3701 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3702 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3704 for (i
= 0; i
< 5; i
++)
3706 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
]);
3709 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3711 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3715 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3716 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3718 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3719 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3720 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3722 _cupsRWUnlock(&(printer
->rwlock
));
3724 cupsArrayDelete(ra
);
3729 * 'ipp_identify_printer()' - Beep or display a message.
3733 ipp_identify_printer(
3734 _ipp_client_t
*client
) /* I - Client */
3736 ipp_attribute_t
*actions
, /* identify-actions */
3737 *message
; /* message */
3740 actions
= ippFindAttribute(client
->request
, "identify-actions", IPP_TAG_KEYWORD
);
3741 message
= ippFindAttribute(client
->request
, "message", IPP_TAG_TEXT
);
3743 if (!actions
|| ippContainsString(actions
, "sound"))
3749 if (ippContainsString(actions
, "display"))
3750 printf("IDENTIFY from %s: %s\n", client
->hostname
, message
? ippGetString(message
, 0, NULL
) : "No message supplied");
3752 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3757 * 'ipp_print_job()' - Create a job object with an attached document.
3761 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3763 _ipp_job_t
*job
; /* New job */
3764 char filename
[1024], /* Filename buffer */
3765 buffer
[4096]; /* Copy buffer */
3766 ssize_t bytes
; /* Bytes read */
3767 cups_array_t
*ra
; /* Attributes to send in response */
3771 * Validate print job attributes...
3774 if (!valid_job_attributes(client
))
3776 httpFlush(client
->http
);
3781 * Do we have a file to print?
3784 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3786 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3794 if ((job
= create_job(client
)) == NULL
)
3796 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3797 "Currently printing another job.");
3802 * Create a file for the request data...
3805 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3808 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3810 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3812 job
->state
= IPP_JSTATE_ABORTED
;
3814 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3815 "Unable to create print file: %s", strerror(errno
));
3819 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3821 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3823 int error
= errno
; /* Write error */
3825 job
->state
= IPP_JSTATE_ABORTED
;
3832 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3833 "Unable to write print file: %s", strerror(error
));
3841 * Got an error while reading the print data, so abort this job.
3844 job
->state
= IPP_JSTATE_ABORTED
;
3851 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3852 "Unable to read print file.");
3858 int error
= errno
; /* Write error */
3860 job
->state
= IPP_JSTATE_ABORTED
;
3865 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3866 "Unable to write print file: %s", strerror(error
));
3871 job
->filename
= strdup(filename
);
3872 job
->state
= IPP_JSTATE_PENDING
;
3875 * Process the job...
3878 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3880 job
->state
= IPP_JSTATE_ABORTED
;
3881 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3886 * Return the job info...
3889 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3891 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3892 cupsArrayAdd(ra
, "job-id");
3893 cupsArrayAdd(ra
, "job-state");
3894 cupsArrayAdd(ra
, "job-state-message");
3895 cupsArrayAdd(ra
, "job-state-reasons");
3896 cupsArrayAdd(ra
, "job-uri");
3898 copy_job_attributes(client
, job
, ra
);
3899 cupsArrayDelete(ra
);
3904 * 'ipp_print_uri()' - Create a job object with a referenced document.
3908 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3910 _ipp_job_t
*job
; /* New job */
3911 ipp_attribute_t
*uri
; /* document-uri */
3912 char scheme
[256], /* URI scheme */
3913 userpass
[256], /* Username and password info */
3914 hostname
[256], /* Hostname */
3915 resource
[1024]; /* Resource path */
3916 int port
; /* Port number */
3917 http_uri_status_t uri_status
; /* URI decode status */
3918 http_encryption_t encryption
; /* Encryption to use, if any */
3919 http_t
*http
; /* Connection for http/https URIs */
3920 http_status_t status
; /* Access status for http/https URIs */
3921 int infile
; /* Input file for local file URIs */
3922 char filename
[1024], /* Filename buffer */
3923 buffer
[4096]; /* Copy buffer */
3924 ssize_t bytes
; /* Bytes read */
3925 cups_array_t
*ra
; /* Attributes to send in response */
3926 static const char * const uri_status_strings
[] =
3927 { /* URI decode errors */
3929 "Bad arguments to function.",
3930 "Bad resource in URI.",
3931 "Bad port number in URI.",
3932 "Bad hostname in URI.",
3933 "Bad username in URI.",
3934 "Bad scheme in URI.",
3940 * Validate print job attributes...
3943 if (!valid_job_attributes(client
))
3945 httpFlush(client
->http
);
3950 * Do we have a file to print?
3953 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3955 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3956 "Unexpected document data following request.");
3961 * Do we have a document URI?
3964 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3965 IPP_TAG_URI
)) == NULL
)
3967 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3971 if (ippGetCount(uri
) != 1)
3973 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3974 "Too many document-uri values.");
3978 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3979 scheme
, sizeof(scheme
), userpass
,
3980 sizeof(userpass
), hostname
, sizeof(hostname
),
3981 &port
, resource
, sizeof(resource
));
3982 if (uri_status
< HTTP_URI_STATUS_OK
)
3984 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3985 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3989 if (strcmp(scheme
, "file") &&
3991 strcmp(scheme
, "https") &&
3992 #endif /* HAVE_SSL */
3993 strcmp(scheme
, "http"))
3995 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3996 "URI scheme \"%s\" not supported.", scheme
);
4000 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4002 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4003 "Unable to access URI: %s", strerror(errno
));
4011 if ((job
= create_job(client
)) == NULL
)
4013 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
4014 "Currently printing another job.");
4019 * Create a file for the request data...
4022 if (!strcasecmp(job
->format
, "image/jpeg"))
4023 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4024 client
->printer
->directory
, job
->id
);
4025 else if (!strcasecmp(job
->format
, "image/png"))
4026 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4027 client
->printer
->directory
, job
->id
);
4028 else if (!strcasecmp(job
->format
, "application/pdf"))
4029 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4030 client
->printer
->directory
, job
->id
);
4031 else if (!strcasecmp(job
->format
, "application/postscript"))
4032 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4033 client
->printer
->directory
, job
->id
);
4035 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4036 client
->printer
->directory
, job
->id
);
4038 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
4040 job
->state
= IPP_JSTATE_ABORTED
;
4042 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4043 "Unable to create print file: %s", strerror(errno
));
4047 if (!strcmp(scheme
, "file"))
4049 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4051 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4052 "Unable to access URI: %s", strerror(errno
));
4058 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4059 (errno
== EAGAIN
|| errno
== EINTR
))
4061 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4063 int error
= errno
; /* Write error */
4065 job
->state
= IPP_JSTATE_ABORTED
;
4073 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4074 "Unable to write print file: %s", strerror(error
));
4085 if (port
== 443 || !strcmp(scheme
, "https"))
4086 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4088 #endif /* HAVE_SSL */
4089 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4091 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4092 1, 30000, NULL
)) == NULL
)
4094 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4095 "Unable to connect to %s: %s", hostname
,
4096 cupsLastErrorString());
4097 job
->state
= IPP_JSTATE_ABORTED
;
4106 httpClearFields(http
);
4107 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4108 if (httpGet(http
, resource
))
4110 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4111 "Unable to GET URI: %s", strerror(errno
));
4113 job
->state
= IPP_JSTATE_ABORTED
;
4123 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4125 if (status
!= HTTP_STATUS_OK
)
4127 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4128 "Unable to GET URI: %s", httpStatus(status
));
4130 job
->state
= IPP_JSTATE_ABORTED
;
4140 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4142 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4144 int error
= errno
; /* Write error */
4146 job
->state
= IPP_JSTATE_ABORTED
;
4154 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4155 "Unable to write print file: %s", strerror(error
));
4165 int error
= errno
; /* Write error */
4167 job
->state
= IPP_JSTATE_ABORTED
;
4172 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4173 "Unable to write print file: %s", strerror(error
));
4178 job
->filename
= strdup(filename
);
4179 job
->state
= IPP_JSTATE_PENDING
;
4182 * Process the job...
4186 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4188 job
->state
= IPP_JSTATE_ABORTED
;
4189 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4198 * Return the job info...
4201 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4203 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4204 cupsArrayAdd(ra
, "job-id");
4205 cupsArrayAdd(ra
, "job-state");
4206 cupsArrayAdd(ra
, "job-state-reasons");
4207 cupsArrayAdd(ra
, "job-uri");
4209 copy_job_attributes(client
, job
, ra
);
4210 cupsArrayDelete(ra
);
4215 * 'ipp_send_document()' - Add an attached document to a job object created with
4220 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
4222 _ipp_job_t
*job
; /* Job information */
4223 char filename
[1024], /* Filename buffer */
4224 buffer
[4096]; /* Copy buffer */
4225 ssize_t bytes
; /* Bytes read */
4226 ipp_attribute_t
*attr
; /* Current attribute */
4227 cups_array_t
*ra
; /* Attributes to send in response */
4234 if ((job
= find_job(client
)) == NULL
)
4236 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4237 httpFlush(client
->http
);
4242 * See if we already have a document for this job or the job has already
4243 * in a non-pending state...
4246 if (job
->state
> IPP_JSTATE_HELD
)
4248 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4249 "Job is not in a pending state.");
4250 httpFlush(client
->http
);
4253 else if (job
->filename
|| job
->fd
>= 0)
4255 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4256 "Multiple document jobs are not supported.");
4257 httpFlush(client
->http
);
4261 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4262 IPP_TAG_ZERO
)) == NULL
)
4264 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4265 "Missing required last-document attribute.");
4266 httpFlush(client
->http
);
4269 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4270 !ippGetBoolean(attr
, 0))
4272 respond_unsupported(client
, attr
);
4273 httpFlush(client
->http
);
4278 * Validate document attributes...
4281 if (!valid_doc_attributes(client
))
4283 httpFlush(client
->http
);
4287 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
4290 * Get the document format for the job...
4293 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4295 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
4296 job
->format
= ippGetString(attr
, 0, NULL
);
4297 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
4298 job
->format
= ippGetString(attr
, 0, NULL
);
4300 job
->format
= "application/octet-stream";
4303 * Create a file for the request data...
4306 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
4309 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
4311 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4313 _cupsRWUnlock(&(client
->printer
->rwlock
));
4317 job
->state
= IPP_JSTATE_ABORTED
;
4319 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4320 "Unable to create print file: %s", strerror(errno
));
4324 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
4326 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4328 int error
= errno
; /* Write error */
4330 job
->state
= IPP_JSTATE_ABORTED
;
4337 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4338 "Unable to write print file: %s", strerror(error
));
4346 * Got an error while reading the print data, so abort this job.
4349 job
->state
= IPP_JSTATE_ABORTED
;
4356 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4357 "Unable to read print file.");
4363 int error
= errno
; /* Write error */
4365 job
->state
= IPP_JSTATE_ABORTED
;
4370 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4371 "Unable to write print file: %s", strerror(error
));
4375 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4378 job
->filename
= strdup(filename
);
4379 job
->state
= IPP_JSTATE_PENDING
;
4381 _cupsRWUnlock(&(client
->printer
->rwlock
));
4384 * Process the job...
4388 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4390 job
->state
= IPP_JSTATE_ABORTED
;
4391 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4400 * Return the job info...
4403 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4405 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4406 cupsArrayAdd(ra
, "job-id");
4407 cupsArrayAdd(ra
, "job-state");
4408 cupsArrayAdd(ra
, "job-state-reasons");
4409 cupsArrayAdd(ra
, "job-uri");
4411 copy_job_attributes(client
, job
, ra
);
4412 cupsArrayDelete(ra
);
4417 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4422 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
4424 _ipp_job_t
*job
; /* Job information */
4425 ipp_attribute_t
*uri
; /* document-uri */
4426 char scheme
[256], /* URI scheme */
4427 userpass
[256], /* Username and password info */
4428 hostname
[256], /* Hostname */
4429 resource
[1024]; /* Resource path */
4430 int port
; /* Port number */
4431 http_uri_status_t uri_status
; /* URI decode status */
4432 http_encryption_t encryption
; /* Encryption to use, if any */
4433 http_t
*http
; /* Connection for http/https URIs */
4434 http_status_t status
; /* Access status for http/https URIs */
4435 int infile
; /* Input file for local file URIs */
4436 char filename
[1024], /* Filename buffer */
4437 buffer
[4096]; /* Copy buffer */
4438 ssize_t bytes
; /* Bytes read */
4439 ipp_attribute_t
*attr
; /* Current attribute */
4440 cups_array_t
*ra
; /* Attributes to send in response */
4441 static const char * const uri_status_strings
[] =
4442 { /* URI decode errors */
4444 "Bad arguments to function.",
4445 "Bad resource in URI.",
4446 "Bad port number in URI.",
4447 "Bad hostname in URI.",
4448 "Bad username in URI.",
4449 "Bad scheme in URI.",
4458 if ((job
= find_job(client
)) == NULL
)
4460 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4461 httpFlush(client
->http
);
4466 * See if we already have a document for this job or the job has already
4467 * in a non-pending state...
4470 if (job
->state
> IPP_JSTATE_HELD
)
4472 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4473 "Job is not in a pending state.");
4474 httpFlush(client
->http
);
4477 else if (job
->filename
|| job
->fd
>= 0)
4479 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4480 "Multiple document jobs are not supported.");
4481 httpFlush(client
->http
);
4485 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4486 IPP_TAG_ZERO
)) == NULL
)
4488 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4489 "Missing required last-document attribute.");
4490 httpFlush(client
->http
);
4493 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4494 !ippGetBoolean(attr
, 0))
4496 respond_unsupported(client
, attr
);
4497 httpFlush(client
->http
);
4502 * Validate document attributes...
4505 if (!valid_doc_attributes(client
))
4507 httpFlush(client
->http
);
4512 * Do we have a file to print?
4515 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4517 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4518 "Unexpected document data following request.");
4523 * Do we have a document URI?
4526 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4527 IPP_TAG_URI
)) == NULL
)
4529 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4533 if (ippGetCount(uri
) != 1)
4535 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4536 "Too many document-uri values.");
4540 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4541 scheme
, sizeof(scheme
), userpass
,
4542 sizeof(userpass
), hostname
, sizeof(hostname
),
4543 &port
, resource
, sizeof(resource
));
4544 if (uri_status
< HTTP_URI_STATUS_OK
)
4546 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4547 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4551 if (strcmp(scheme
, "file") &&
4553 strcmp(scheme
, "https") &&
4554 #endif /* HAVE_SSL */
4555 strcmp(scheme
, "http"))
4557 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4558 "URI scheme \"%s\" not supported.", scheme
);
4562 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4564 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4565 "Unable to access URI: %s", strerror(errno
));
4570 * Get the document format for the job...
4573 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4575 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4576 IPP_TAG_MIMETYPE
)) != NULL
)
4577 job
->format
= ippGetString(attr
, 0, NULL
);
4579 job
->format
= "application/octet-stream";
4582 * Create a file for the request data...
4585 if (!strcasecmp(job
->format
, "image/jpeg"))
4586 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4587 client
->printer
->directory
, job
->id
);
4588 else if (!strcasecmp(job
->format
, "image/png"))
4589 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4590 client
->printer
->directory
, job
->id
);
4591 else if (!strcasecmp(job
->format
, "application/pdf"))
4592 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4593 client
->printer
->directory
, job
->id
);
4594 else if (!strcasecmp(job
->format
, "application/postscript"))
4595 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4596 client
->printer
->directory
, job
->id
);
4598 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4599 client
->printer
->directory
, job
->id
);
4601 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4603 _cupsRWUnlock(&(client
->printer
->rwlock
));
4607 job
->state
= IPP_JSTATE_ABORTED
;
4609 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4610 "Unable to create print file: %s", strerror(errno
));
4614 if (!strcmp(scheme
, "file"))
4616 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4618 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4619 "Unable to access URI: %s", strerror(errno
));
4625 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4626 (errno
== EAGAIN
|| errno
== EINTR
))
4628 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4630 int error
= errno
; /* Write error */
4632 job
->state
= IPP_JSTATE_ABORTED
;
4640 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4641 "Unable to write print file: %s", strerror(error
));
4652 if (port
== 443 || !strcmp(scheme
, "https"))
4653 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4655 #endif /* HAVE_SSL */
4656 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4658 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4659 1, 30000, NULL
)) == NULL
)
4661 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4662 "Unable to connect to %s: %s", hostname
,
4663 cupsLastErrorString());
4664 job
->state
= IPP_JSTATE_ABORTED
;
4673 httpClearFields(http
);
4674 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4675 if (httpGet(http
, resource
))
4677 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4678 "Unable to GET URI: %s", strerror(errno
));
4680 job
->state
= IPP_JSTATE_ABORTED
;
4690 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4692 if (status
!= HTTP_STATUS_OK
)
4694 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4695 "Unable to GET URI: %s", httpStatus(status
));
4697 job
->state
= IPP_JSTATE_ABORTED
;
4707 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4709 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4711 int error
= errno
; /* Write error */
4713 job
->state
= IPP_JSTATE_ABORTED
;
4721 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4722 "Unable to write print file: %s", strerror(error
));
4732 int error
= errno
; /* Write error */
4734 job
->state
= IPP_JSTATE_ABORTED
;
4739 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4740 "Unable to write print file: %s", strerror(error
));
4744 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4747 job
->filename
= strdup(filename
);
4748 job
->state
= IPP_JSTATE_PENDING
;
4750 _cupsRWUnlock(&(client
->printer
->rwlock
));
4753 * Process the job...
4757 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4759 job
->state
= IPP_JSTATE_ABORTED
;
4760 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4769 * Return the job info...
4772 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4774 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4775 cupsArrayAdd(ra
, "job-id");
4776 cupsArrayAdd(ra
, "job-state");
4777 cupsArrayAdd(ra
, "job-state-reasons");
4778 cupsArrayAdd(ra
, "job-uri");
4780 copy_job_attributes(client
, job
, ra
);
4781 cupsArrayDelete(ra
);
4786 * 'ipp_validate_job()' - Validate job creation attributes.
4790 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4792 if (valid_job_attributes(client
))
4793 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4798 * 'load_attributes()' - Load printer attributes from a file.
4800 * Syntax is based on ipptool format:
4802 * ATTR value-tag name value
4806 load_attributes(const char *filename
, /* I - File to load */
4807 ipp_t
*attrs
) /* I - Printer attributes */
4809 int linenum
= 0; /* Current line number */
4810 FILE *fp
= NULL
; /* Test file */
4811 char attr
[128], /* Attribute name */
4812 token
[1024], /* Token from file */
4813 *tokenptr
; /* Pointer into token */
4814 ipp_tag_t value
; /* Current value type */
4815 ipp_attribute_t
*attrptr
, /* Attribute pointer */
4816 *lastcol
= NULL
; /* Last collection attribute */
4819 if ((fp
= fopen(filename
, "r")) == NULL
)
4821 fprintf(stderr
, "ippserver: Unable to open \"%s\": %s\n", filename
, strerror(errno
));
4825 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
4827 if (!_cups_strcasecmp(token
, "ATTR"))
4833 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4835 fprintf(stderr
, "ippserver: Missing ATTR value tag on line %d of \"%s\".\n", linenum
, filename
);
4839 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
4841 fprintf(stderr
, "ippserver: Bad ATTR value tag \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4845 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
4847 fprintf(stderr
, "ippserver: Missing ATTR name on line %d of \"%s\".\n", linenum
, filename
);
4851 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4853 fprintf(stderr
, "ippserver: Missing ATTR value on line %d of \"%s\".\n", linenum
, filename
);
4861 case IPP_TAG_BOOLEAN
:
4862 if (!_cups_strcasecmp(token
, "true"))
4863 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, 1);
4865 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, (char)atoi(token
));
4868 case IPP_TAG_INTEGER
:
4870 if (!strchr(token
, ','))
4871 attrptr
= ippAddInteger(attrs
, IPP_TAG_PRINTER
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
4874 int values
[100], /* Values */
4875 num_values
= 1; /* Number of values */
4877 values
[0] = (int)strtol(token
, &tokenptr
, 10);
4878 while (tokenptr
&& *tokenptr
&&
4879 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
4881 if (*tokenptr
== ',')
4883 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
4886 values
[num_values
] = (int)strtol(tokenptr
, &tokenptr
, 0);
4890 attrptr
= ippAddIntegers(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, values
);
4893 if (!tokenptr
|| *tokenptr
)
4895 fprintf(stderr
, "ippserver: Bad %s value \"%s\" on line %d of \"%s\".\n", ippTagString(value
), token
, linenum
, filename
);
4900 case IPP_TAG_RESOLUTION
:
4902 int xres
, /* X resolution */
4903 yres
; /* Y resolution */
4904 ipp_res_t units
; /* Units */
4905 char *start
, /* Start of value */
4906 *ptr
, /* Pointer into value */
4907 *next
= NULL
; /* Next value */
4909 for (start
= token
; start
; start
= next
)
4911 xres
= yres
= (int)strtol(start
, (char **)&ptr
, 10);
4912 if (ptr
> start
&& xres
> 0)
4915 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
4918 if (ptr
&& (next
= strchr(ptr
, ',')) != NULL
)
4921 if (ptr
<= start
|| xres
<= 0 || yres
<= 0 || !ptr
||
4922 (_cups_strcasecmp(ptr
, "dpi") &&
4923 _cups_strcasecmp(ptr
, "dpc") &&
4924 _cups_strcasecmp(ptr
, "dpcm") &&
4925 _cups_strcasecmp(ptr
, "other")))
4927 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4931 if (!_cups_strcasecmp(ptr
, "dpc") || !_cups_strcasecmp(ptr
, "dpcm"))
4932 units
= IPP_RES_PER_CM
;
4934 units
= IPP_RES_PER_INCH
;
4937 ippSetResolution(attrs
, &attrptr
, ippGetCount(attrptr
), units
, xres
, yres
);
4939 attrptr
= ippAddResolution(attrs
, IPP_TAG_PRINTER
, attr
, units
, xres
, yres
);
4944 case IPP_TAG_RANGE
:
4946 int lowers
[4], /* Lower value */
4947 uppers
[4], /* Upper values */
4948 num_vals
; /* Number of values */
4951 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
4952 lowers
+ 0, uppers
+ 0,
4953 lowers
+ 1, uppers
+ 1,
4954 lowers
+ 2, uppers
+ 2,
4955 lowers
+ 3, uppers
+ 3);
4957 if ((num_vals
& 1) || num_vals
== 0)
4959 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", token
, linenum
, filename
);
4963 attrptr
= ippAddRanges(attrs
, IPP_TAG_PRINTER
, attr
, num_vals
/ 2, lowers
,
4968 case IPP_TAG_BEGIN_COLLECTION
:
4969 if (!strcmp(token
, "{"))
4971 ipp_t
*col
= get_collection(fp
, filename
, &linenum
);
4972 /* Collection value */
4976 attrptr
= lastcol
= ippAddCollection(attrs
, IPP_TAG_PRINTER
, attr
, col
);
4984 fprintf(stderr
, "ippserver: Bad ATTR collection value on line %d of \"%s\".\n", linenum
, filename
);
4990 ipp_t
*col
; /* Collection value */
4991 long pos
= ftell(fp
); /* Save position of file */
4993 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4996 if (strcmp(token
, ","))
4998 fseek(fp
, pos
, SEEK_SET
);
5002 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
5004 fprintf(stderr
, "ippserver: Unexpected \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
5008 if ((col
= get_collection(fp
, filename
, &linenum
)) == NULL
)
5011 ippSetCollection(attrs
, &attrptr
, ippGetCount(attrptr
), col
);
5014 while (!strcmp(token
, "{"));
5017 case IPP_TAG_STRING
:
5018 attrptr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, attr
, token
, (int)strlen(token
));
5022 fprintf(stderr
, "ippserver: Unsupported ATTR value tag %s on line %d of \"%s\".\n", ippTagString(value
), linenum
, filename
);
5025 case IPP_TAG_TEXTLANG
:
5026 case IPP_TAG_NAMELANG
:
5029 case IPP_TAG_KEYWORD
:
5031 case IPP_TAG_URISCHEME
:
5032 case IPP_TAG_CHARSET
:
5033 case IPP_TAG_LANGUAGE
:
5034 case IPP_TAG_MIMETYPE
:
5035 if (!strchr(token
, ','))
5036 attrptr
= ippAddString(attrs
, IPP_TAG_PRINTER
, value
, attr
, NULL
, token
);
5040 * Multiple string values...
5043 int num_values
; /* Number of values */
5044 char *values
[100], /* Values */
5045 *ptr
; /* Pointer to next value */
5051 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
5053 if (ptr
> token
&& ptr
[-1] == '\\')
5054 _cups_strcpy(ptr
- 1, ptr
);
5058 values
[num_values
] = ptr
;
5060 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
5065 attrptr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, NULL
, (const char **)values
);
5072 fprintf(stderr
, "ippserver: Unable to add attribute on line %d of \"%s\": %s\n", linenum
, filename
, cupsLastErrorString());
5078 fprintf(stderr
, "ippserver: Unknown directive \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
5088 * 'parse_options()' - Parse URL options into CUPS options.
5090 * The client->options string is destroyed by this function.
5093 static int /* O - Number of options */
5094 parse_options(_ipp_client_t
*client
, /* I - Client */
5095 cups_option_t
**options
) /* O - Options */
5097 char *name
, /* Name */
5099 *next
; /* Next name=value pair */
5100 int num_options
= 0; /* Number of options */
5105 for (name
= client
->options
; name
&& *name
; name
= next
)
5107 if ((value
= strchr(name
, '=')) == NULL
)
5111 if ((next
= strchr(value
, '&')) != NULL
)
5114 num_options
= cupsAddOption(name
, value
, num_options
, options
);
5117 return (num_options
);
5122 * 'process_attr_message()' - Process an ATTR: message from a command.
5126 process_attr_message(
5127 _ipp_job_t
*job
, /* I - Job */
5128 char *message
) /* I - Message */
5136 * 'process_client()' - Process client requests on a thread.
5139 static void * /* O - Exit status */
5140 process_client(_ipp_client_t
*client
) /* I - Client */
5143 * Loop until we are out of requests or timeout (30 seconds)...
5147 int first_time
= 1; /* First time request? */
5148 #endif /* HAVE_SSL */
5150 while (httpWait(client
->http
, 30000))
5156 * See if we need to negotiate a TLS connection...
5159 char buf
[1]; /* First byte from client */
5161 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
5163 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
5165 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
5167 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5171 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5176 #endif /* HAVE_SSL */
5178 if (!process_http(client
))
5183 * Close the conection to the client and return...
5186 delete_client(client
);
5193 * 'process_http()' - Process a HTTP request.
5196 int /* O - 1 on success, 0 on failure */
5197 process_http(_ipp_client_t
*client
) /* I - Client connection */
5199 char uri
[1024]; /* URI */
5200 http_state_t http_state
; /* HTTP state */
5201 http_status_t http_status
; /* HTTP status */
5202 ipp_state_t ipp_state
; /* State of IPP transfer */
5203 char scheme
[32], /* Method/scheme */
5204 userpass
[128], /* Username:password */
5205 hostname
[HTTP_MAX_HOST
];
5207 int port
; /* Port number */
5208 const char *encoding
; /* Content-Encoding value */
5209 static const char * const http_states
[] =
5210 { /* Strings for logging HTTP method */
5231 * Clear state variables...
5234 ippDelete(client
->request
);
5235 ippDelete(client
->response
);
5237 client
->request
= NULL
;
5238 client
->response
= NULL
;
5239 client
->operation
= HTTP_STATE_WAITING
;
5242 * Read a request from the connection...
5245 while ((http_state
= httpReadRequest(client
->http
, uri
,
5246 sizeof(uri
))) == HTTP_STATE_WAITING
)
5250 * Parse the request line...
5253 if (http_state
== HTTP_STATE_ERROR
)
5255 if (httpError(client
->http
) == EPIPE
)
5256 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
5258 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
5259 strerror(httpError(client
->http
)));
5263 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
5265 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
5266 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5269 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
5271 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
5272 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5276 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
5280 * Separate the URI into its components...
5283 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
5284 userpass
, sizeof(userpass
),
5285 hostname
, sizeof(hostname
), &port
,
5286 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
5287 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
5289 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
5290 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5294 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
5295 *(client
->options
)++ = '\0';
5298 * Process the request...
5301 client
->start
= time(NULL
);
5302 client
->operation
= httpGetState(client
->http
);
5305 * Parse incoming parameters until the status changes...
5308 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
5310 if (http_status
!= HTTP_STATUS_OK
)
5312 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5316 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
5317 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
5320 * HTTP/1.1 and higher require the "Host:" field...
5323 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5328 * Handle HTTP Upgrade...
5331 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
5335 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
5337 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
5340 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
5342 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
5344 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5348 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5351 #endif /* HAVE_SSL */
5353 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
5358 * Handle HTTP Expect...
5361 if (httpGetExpect(client
->http
) &&
5362 (client
->operation
== HTTP_STATE_POST
||
5363 client
->operation
== HTTP_STATE_PUT
))
5365 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
5368 * Send 100-continue header...
5371 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
5377 * Send 417-expectation-failed header...
5380 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
5386 * Handle new transfers...
5389 encoding
= httpGetContentEncoding(client
->http
);
5391 switch (client
->operation
)
5393 case HTTP_STATE_OPTIONS
:
5395 * Do OPTIONS command...
5398 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
5400 case HTTP_STATE_HEAD
:
5401 if (!strcmp(client
->uri
, "/icon.png"))
5402 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
5403 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
5404 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
5406 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5408 case HTTP_STATE_GET
:
5409 if (!strcmp(client
->uri
, "/icon.png"))
5412 * Send PNG icon file.
5415 int fd
; /* Icon file */
5416 struct stat fileinfo
; /* Icon file information */
5417 char buffer
[4096]; /* Copy buffer */
5418 ssize_t bytes
; /* Bytes */
5420 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5422 if (!stat(client
->printer
->icon
, &fileinfo
) &&
5423 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5425 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
5426 (size_t)fileinfo
.st_size
))
5432 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5433 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5435 httpFlushWrite(client
->http
);
5440 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5442 else if (!strcmp(client
->uri
, "/"))
5445 * Show web status page...
5448 _ipp_job_t
*job
; /* Current job */
5449 int i
; /* Looping var */
5450 _ipp_preason_t reason
; /* Current reason */
5451 static const char * const reasons
[] =
5452 { /* Reason strings */
5455 "Input Tray Missing",
5456 "Marker Supply Empty",
5457 "Marker Supply Low",
5458 "Marker Waste Almost Full",
5459 "Marker Waste Full",
5471 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5474 html_header(client
, client
->printer
->name
);
5476 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
5477 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
5478 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
5479 if (client
->printer
->state_reasons
& reason
)
5480 html_printf(client
, "\n<br> %s", reasons
[i
]);
5481 html_printf(client
, "</p>\n");
5483 if (cupsArrayCount(client
->printer
->jobs
) > 0)
5485 _cupsRWLockRead(&(client
->printer
->rwlock
));
5487 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");
5488 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
5490 char when
[256], /* When job queued/started/finished */
5491 hhmmss
[64]; /* Time HH:MM:SS */
5495 case IPP_JSTATE_PENDING
:
5496 case IPP_JSTATE_HELD
:
5497 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
5499 case IPP_JSTATE_PROCESSING
:
5500 case IPP_JSTATE_STOPPED
:
5501 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
5503 case IPP_JSTATE_ABORTED
:
5504 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5506 case IPP_JSTATE_CANCELED
:
5507 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5509 case IPP_JSTATE_COMPLETED
:
5510 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5514 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
);
5516 html_printf(client
, "</tbody></table>\n");
5518 _cupsRWUnlock(&(client
->printer
->rwlock
));
5520 html_footer(client
);
5524 else if (!strcmp(client
->uri
, "/media"))
5527 * Show web media page...
5530 int i
, /* Looping var */
5531 num_options
; /* Number of form options */
5532 cups_option_t
*options
; /* Form options */
5533 static const char * const sizes
[] =
5534 { /* Size strings */
5547 static const char * const types
[] =
5564 static const int sheets
[] = /* Number of sheets */
5573 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5576 html_header(client
, client
->printer
->name
);
5578 if ((num_options
= parse_options(client
, &options
)) > 0)
5581 * WARNING: A real printer/server implementation MUST NOT implement
5582 * media updates via a GET request - GET requests are supposed to be
5583 * idempotent (without side-effects) and we obviously are not
5584 * authenticating access here. This form is provided solely to
5585 * enable testing and development!
5588 const char *val
; /* Form value */
5590 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
5591 client
->printer
->main_size
= atoi(val
);
5592 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
5593 client
->printer
->main_type
= atoi(val
);
5594 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
5595 client
->printer
->main_level
= atoi(val
);
5597 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
5598 client
->printer
->envelope_size
= atoi(val
);
5599 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
5600 client
->printer
->envelope_level
= atoi(val
);
5602 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
5603 client
->printer
->photo_size
= atoi(val
);
5604 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
5605 client
->printer
->photo_type
= atoi(val
);
5606 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
5607 client
->printer
->photo_level
= atoi(val
);
5609 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))
5610 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
5612 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
5614 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
))
5616 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
5617 if (client
->printer
->active_job
)
5618 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5621 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
5623 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
5626 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
5628 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
5629 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
5630 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5631 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
5632 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
5633 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
5634 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5635 if (!strstr(types
[i
], "Photo"))
5636 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
5637 html_printf(client
, "</select> <select name=\"main_level\">");
5638 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5639 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
5640 html_printf(client
, "</select></td></tr>\n");
5643 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
5644 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5645 if (strstr(sizes
[i
], "Envelope"))
5646 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
5647 html_printf(client
, "</select> <select name=\"envelope_level\">");
5648 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5649 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
5650 html_printf(client
, "</select></td></tr>\n");
5653 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
5654 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5655 if (strstr(sizes
[i
], "Photo"))
5656 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
5657 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
5658 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5659 if (strstr(types
[i
], "Photo"))
5660 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
5661 html_printf(client
, "</select> <select name=\"photo_level\">");
5662 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5663 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
5664 html_printf(client
, "</select></td></tr>\n");
5666 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5667 html_footer(client
);
5671 else if (!strcmp(client
->uri
, "/supplies"))
5674 * Show web supplies page...
5677 int i
, j
, /* Looping vars */
5678 num_options
; /* Number of form options */
5679 cups_option_t
*options
; /* Form options */
5680 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5682 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5685 html_header(client
, client
->printer
->name
);
5687 if ((num_options
= parse_options(client
, &options
)) > 0)
5690 * WARNING: A real printer/server implementation MUST NOT implement
5691 * supply updates via a GET request - GET requests are supposed to be
5692 * idempotent (without side-effects) and we obviously are not
5693 * authenticating access here. This form is provided solely to
5694 * enable testing and development!
5697 char name
[64]; /* Form field */
5698 const char *val
; /* Form value */
5700 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
);
5702 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5704 snprintf(name
, sizeof(name
), "supply_%d", i
);
5705 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5707 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5713 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
5714 else if (level
< 10)
5715 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
5720 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
5721 else if (level
> 90)
5722 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
5727 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5730 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5732 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5733 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5735 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5736 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5737 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5738 html_printf(client
, "</select></td></tr>\n");
5740 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5741 html_footer(client
);
5746 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5749 case HTTP_STATE_POST
:
5750 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5754 * Not an IPP request...
5757 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5761 * Read the IPP request...
5764 client
->request
= ippNew();
5766 while ((ipp_state
= ippRead(client
->http
,
5767 client
->request
)) != IPP_STATE_DATA
)
5769 if (ipp_state
== IPP_STATE_ERROR
)
5771 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5772 cupsLastErrorString());
5773 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5779 * Now that we have the IPP request, process the request...
5782 return (process_ipp(client
));
5785 break; /* Anti-compiler-warning-code */
5793 * 'process_ipp()' - Process an IPP request.
5796 static int /* O - 1 on success, 0 on error */
5797 process_ipp(_ipp_client_t
*client
) /* I - Client */
5799 ipp_tag_t group
; /* Current group tag */
5800 ipp_attribute_t
*attr
; /* Current attribute */
5801 ipp_attribute_t
*charset
; /* Character set attribute */
5802 ipp_attribute_t
*language
; /* Language attribute */
5803 ipp_attribute_t
*uri
; /* Printer URI attribute */
5804 int major
, minor
; /* Version number */
5805 const char *name
; /* Name of attribute */
5808 debug_attributes("Request", client
->request
, 1);
5811 * First build an empty response message for this request...
5814 client
->operation_id
= ippGetOperation(client
->request
);
5815 client
->response
= ippNewResponse(client
->request
);
5818 * Then validate the request header and required attributes...
5821 major
= ippGetVersion(client
->request
, &minor
);
5823 if (major
< 1 || major
> 2)
5826 * Return an error, since we only support IPP 1.x and 2.x.
5829 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
5830 "Bad request version number %d.%d.", major
, minor
);
5832 else if (ippGetRequestId(client
->request
) <= 0)
5833 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
5834 ippGetRequestId(client
->request
));
5835 else if (!ippFirstAttribute(client
->request
))
5836 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5837 "No attributes in request.");
5841 * Make sure that the attributes are provided in the correct order and
5842 * don't repeat groups...
5845 for (attr
= ippFirstAttribute(client
->request
),
5846 group
= ippGetGroupTag(attr
);
5848 attr
= ippNextAttribute(client
->request
))
5850 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5853 * Out of order; return an error...
5856 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5857 "Attribute groups are out of order (%x < %x).",
5858 ippGetGroupTag(attr
), group
);
5862 group
= ippGetGroupTag(attr
);
5868 * Then make sure that the first three attributes are:
5870 * attributes-charset
5871 * attributes-natural-language
5872 * printer-uri/job-uri
5875 attr
= ippFirstAttribute(client
->request
);
5876 name
= ippGetName(attr
);
5877 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5878 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5883 attr
= ippNextAttribute(client
->request
);
5884 name
= ippGetName(attr
);
5886 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5887 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5892 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5893 IPP_TAG_URI
)) != NULL
)
5895 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5896 IPP_TAG_URI
)) != NULL
)
5902 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5903 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5906 * Bad character set...
5909 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5910 "Unsupported character set \"%s\".",
5911 ippGetString(charset
, 0, NULL
));
5913 else if (!charset
|| !language
|| !uri
)
5916 * Return an error, since attributes-charset,
5917 * attributes-natural-language, and printer-uri/job-uri are required
5918 * for all operations.
5921 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5922 "Missing required attributes.");
5926 char scheme
[32], /* URI scheme */
5927 userpass
[32], /* Username/password in URI */
5928 host
[256], /* Host name in URI */
5929 resource
[256]; /* Resource path in URI */
5930 int port
; /* Port number in URI */
5932 name
= ippGetName(uri
);
5934 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5935 scheme
, sizeof(scheme
),
5936 userpass
, sizeof(userpass
),
5937 host
, sizeof(host
), &port
,
5938 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5939 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5940 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5941 else if ((!strcmp(name
, "job-uri") &&
5942 strncmp(resource
, "/ipp/print/", 11)) ||
5943 (!strcmp(name
, "printer-uri") &&
5944 strcmp(resource
, "/ipp/print")))
5945 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5946 name
, ippGetString(uri
, 0, NULL
));
5950 * Try processing the operation...
5953 switch (ippGetOperation(client
->request
))
5955 case IPP_OP_PRINT_JOB
:
5956 ipp_print_job(client
);
5959 case IPP_OP_PRINT_URI
:
5960 ipp_print_uri(client
);
5963 case IPP_OP_VALIDATE_JOB
:
5964 ipp_validate_job(client
);
5967 case IPP_OP_CREATE_JOB
:
5968 ipp_create_job(client
);
5971 case IPP_OP_SEND_DOCUMENT
:
5972 ipp_send_document(client
);
5975 case IPP_OP_SEND_URI
:
5976 ipp_send_uri(client
);
5979 case IPP_OP_CANCEL_JOB
:
5980 ipp_cancel_job(client
);
5983 case IPP_OP_GET_JOB_ATTRIBUTES
:
5984 ipp_get_job_attributes(client
);
5987 case IPP_OP_GET_JOBS
:
5988 ipp_get_jobs(client
);
5991 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5992 ipp_get_printer_attributes(client
);
5995 case IPP_OP_CLOSE_JOB
:
5996 ipp_close_job(client
);
5999 case IPP_OP_IDENTIFY_PRINTER
:
6000 ipp_identify_printer(client
);
6004 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
6005 "Operation not supported.");
6014 * Send the HTTP header and return...
6017 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
6018 httpFlush(client
->http
); /* Flush trailing (junk) data */
6020 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
6021 ippLength(client
->response
)));
6026 * 'process_job()' - Process a print job.
6029 static void * /* O - Thread exit status */
6030 process_job(_ipp_job_t
*job
) /* I - Job */
6032 job
->state
= IPP_JSTATE_PROCESSING
;
6033 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
6034 job
->processing
= time(NULL
);
6036 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
6038 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
6043 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
6045 if (job
->printer
->command
)
6048 * Execute a command with the job spool file and wait for it to complete...
6051 int pid
, /* Process ID */
6052 status
; /* Exit status */
6053 time_t start
, /* Start time */
6055 char *myargv
[3], /* Command-line arguments */
6056 *myenvp
[200]; /* Environment variables */
6057 int myenvc
; /* Number of environment variables */
6058 ipp_attribute_t
*attr
; /* Job attribute */
6059 char val
[1280], /* IPP_NAME=value */
6060 *valptr
; /* Pointer into string */
6062 int mypipe
[2]; /* Pipe for stderr */
6063 char line
[2048], /* Line from stderr */
6064 *ptr
, /* Pointer into line */
6065 *endptr
; /* End of line */
6066 ssize_t bytes
; /* Bytes read */
6069 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
6074 * Setup the command-line arguments...
6077 myargv
[0] = job
->printer
->command
;
6078 myargv
[1] = job
->filename
;
6082 * Copy the current environment, then add ENV variables for every Job
6086 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
6087 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
6089 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
6092 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6093 * value(s) from the attribute.
6096 const char *name
= ippGetName(attr
);
6105 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6110 *valptr
++ = (char)toupper(*name
& 255);
6115 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6117 myenvp
[myenvc
++] = strdup(val
);
6119 myenvp
[myenvc
] = NULL
;
6122 * Now run the program...
6126 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
6131 perror("Unable to create pipe for stderr");
6132 mypipe
[0] = mypipe
[1] = -1;
6135 if ((pid
= fork()) == 0)
6138 * Child comes here...
6146 execve(job
->printer
->command
, myargv
, myenvp
);
6152 * Unable to fork process...
6155 perror("Unable to start job processing command");
6162 * Free memory used for environment...
6166 free(myenvp
[-- myenvc
]);
6171 * Free memory used for environment...
6175 free(myenvp
[-- myenvc
]);
6178 * If the pipe exists, read from it until EOF...
6186 while ((bytes
= read(mypipe
[0], endptr
, sizeof(line
) - (size_t)(endptr
- line
) - 1)) > 0)
6191 while ((ptr
= strchr(line
, '\n')) != NULL
)
6195 if (!strncmp(line
, "STATE:", 6))
6198 * Process printer-state-reasons keywords.
6201 process_state_message(job
, line
);
6203 else if (!strncmp(line
, "ATTR:", 5))
6206 * Process printer attribute update.
6209 process_attr_message(job
, line
);
6211 else if (Verbosity
> 1)
6212 fprintf(stderr
, "%s: %s\n", job
->printer
->command
, line
);
6216 memmove(line
, ptr
, (size_t)(endptr
- ptr
));
6226 * Wait for child to complete...
6229 # ifdef HAVE_WAITPID
6230 while (waitpid(pid
, &status
, 0) < 0);
6232 while (wait(&status
) < 0);
6233 # endif /* HAVE_WAITPID */
6240 if (WIFEXITED(status
))
6242 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
6243 job
->printer
->command
, WEXITSTATUS(status
));
6246 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
6247 job
->printer
->command
, WTERMSIG(status
));
6249 job
->state
= IPP_JSTATE_ABORTED
;
6251 else if (status
< 0)
6252 job
->state
= IPP_JSTATE_ABORTED
;
6254 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
6255 job
->printer
->command
);
6258 * Make sure processing takes at least 5 seconds...
6262 if ((end
- start
) < 5)
6268 * Sleep for a random amount of time to simulate job processing.
6271 sleep((unsigned)(5 + (rand() % 11)));
6275 job
->state
= IPP_JSTATE_CANCELED
;
6276 else if (job
->state
== IPP_JSTATE_PROCESSING
)
6277 job
->state
= IPP_JSTATE_COMPLETED
;
6279 job
->completed
= time(NULL
);
6280 job
->printer
->state
= IPP_PSTATE_IDLE
;
6281 job
->printer
->active_job
= NULL
;
6288 * 'process_state_message()' - Process a STATE: message from a command.
6292 process_state_message(
6293 _ipp_job_t
*job
, /* I - Job */
6294 char *message
) /* I - Message */
6296 int i
; /* Looping var */
6297 _ipp_preason_t state_reasons
, /* printer-state-reasons values */
6298 bit
; /* Current reason bit */
6299 char *ptr
, /* Pointer into message */
6300 *next
; /* Next keyword in message */
6301 int remove
; /* Non-zero if we are removing keywords */
6305 * Skip leading "STATE:" and any whitespace...
6308 for (message
+= 6; *message
; message
++)
6309 if (*message
!= ' ' && *message
!= '\t')
6313 * Support the following forms of message:
6315 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6317 * "-keyword[,keyword,...]" to remove keywords.
6319 * "+keyword[,keyword,...]" to add keywords.
6321 * Keywords may or may not have a suffix (-report, -warning, -error) per
6325 if (*message
== '-')
6328 state_reasons
= job
->printer
->state_reasons
;
6331 else if (*message
== '+')
6334 state_reasons
= job
->printer
->state_reasons
;
6340 state_reasons
= _IPP_PREASON_NONE
;
6345 if ((next
= strchr(message
, ',')) != NULL
)
6348 if ((ptr
= strstr(message
, "-error")) != NULL
)
6350 else if ((ptr
= strstr(message
, "-report")) != NULL
)
6352 else if ((ptr
= strstr(message
, "-warning")) != NULL
)
6355 for (i
= 0, bit
= 1; i
< (int)(sizeof(_ipp_preason_strings
) / sizeof(_ipp_preason_strings
[0])); i
++, bit
*= 2)
6357 if (!strcmp(message
, _ipp_preason_strings
[i
]))
6360 state_reasons
&= ~bit
;
6362 state_reasons
|= bit
;
6372 job
->printer
->state_reasons
= state_reasons
;
6377 * 'register_printer()' - Register a printer object via Bonjour.
6380 static int /* O - 1 on success, 0 on error */
6382 _ipp_printer_t
*printer
, /* I - Printer */
6383 const char *location
, /* I - Location */
6384 const char *make
, /* I - Manufacturer */
6385 const char *model
, /* I - Model name */
6386 const char *formats
, /* I - Supported formats */
6387 const char *adminurl
, /* I - Web interface URL */
6388 const char *uuid
, /* I - Printer UUID */
6389 int color
, /* I - 1 = color, 0 = monochrome */
6390 int duplex
, /* I - 1 = duplex, 0 = simplex */
6391 const char *subtype
) /* I - Service subtype */
6393 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6394 _ipp_txt_t ipp_txt
; /* Bonjour IPP TXT record */
6395 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6397 DNSServiceErrorType error
; /* Error from Bonjour */
6398 char make_model
[256],/* Make and model together */
6399 product
[256], /* Product string */
6400 regtype
[256]; /* Bonjour service type */
6404 * Build the TXT record for IPP...
6407 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
6408 snprintf(product
, sizeof(product
), "(%s)", model
);
6410 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
6411 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
6412 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(make_model
),
6414 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(adminurl
),
6417 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(location
),
6419 TXTRecordSetValue(&ipp_txt
, "product", (uint8_t)strlen(product
),
6421 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
),
6423 TXTRecordSetValue(&ipp_txt
, "Color", 1, color
? "T" : "F");
6424 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, duplex
? "T" : "F");
6425 TXTRecordSetValue(&ipp_txt
, "usb_MFG", (uint8_t)strlen(make
),
6427 TXTRecordSetValue(&ipp_txt
, "usb_MDL", (uint8_t)strlen(model
),
6429 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(uuid
), uuid
);
6431 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
6432 # endif /* HAVE_SSL */
6433 if (strstr(formats
, "image/urf"))
6434 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");
6436 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
6437 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
6440 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6441 * defend our service name but not actually support LPD...
6444 printer
->printer_ref
= DNSSDMaster
;
6446 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
6447 kDNSServiceFlagsShareConnection
,
6448 0 /* interfaceIndex */, printer
->dnssd_name
,
6449 "_printer._tcp", NULL
/* domain */,
6450 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
6451 NULL
/* txtRecord */,
6452 (DNSServiceRegisterReply
)dnssd_callback
,
6453 printer
)) != kDNSServiceErr_NoError
)
6455 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
6456 printer
->dnssd_name
, error
);
6461 * Then register the _ipp._tcp (IPP) service type with the real port number to
6462 * advertise our IPP printer...
6465 printer
->ipp_ref
= DNSSDMaster
;
6467 if (subtype
&& *subtype
)
6468 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
6470 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
6472 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
6473 kDNSServiceFlagsShareConnection
,
6474 0 /* interfaceIndex */, printer
->dnssd_name
,
6475 regtype
, NULL
/* domain */,
6476 NULL
/* host */, htons(printer
->port
),
6477 TXTRecordGetLength(&ipp_txt
),
6478 TXTRecordGetBytesPtr(&ipp_txt
),
6479 (DNSServiceRegisterReply
)dnssd_callback
,
6480 printer
)) != kDNSServiceErr_NoError
)
6482 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6483 printer
->dnssd_name
, regtype
, error
);
6489 * Then register the _ipps._tcp (IPP) service type with the real port number to
6490 * advertise our IPPS printer...
6493 printer
->ipps_ref
= DNSSDMaster
;
6495 if (subtype
&& *subtype
)
6496 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
6498 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
6500 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
6501 kDNSServiceFlagsShareConnection
,
6502 0 /* interfaceIndex */, printer
->dnssd_name
,
6503 regtype
, NULL
/* domain */,
6504 NULL
/* host */, htons(printer
->port
),
6505 TXTRecordGetLength(&ipp_txt
),
6506 TXTRecordGetBytesPtr(&ipp_txt
),
6507 (DNSServiceRegisterReply
)dnssd_callback
,
6508 printer
)) != kDNSServiceErr_NoError
)
6510 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6511 printer
->dnssd_name
, regtype
, error
);
6514 # endif /* HAVE_SSL */
6517 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6518 * real port number to advertise our IPP printer...
6521 printer
->http_ref
= DNSSDMaster
;
6523 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
6524 kDNSServiceFlagsShareConnection
,
6525 0 /* interfaceIndex */, printer
->dnssd_name
,
6526 "_http._tcp,_printer", NULL
/* domain */,
6527 NULL
/* host */, htons(printer
->port
),
6528 0 /* txtLen */, NULL
, /* txtRecord */
6529 (DNSServiceRegisterReply
)dnssd_callback
,
6530 printer
)) != kDNSServiceErr_NoError
)
6532 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6533 printer
->dnssd_name
, regtype
, error
);
6537 TXTRecordDeallocate(&ipp_txt
);
6539 #elif defined(HAVE_AVAHI)
6540 char temp
[256]; /* Subtype service string */
6543 * Create the TXT record...
6547 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
6548 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s %s", make
, model
);
6549 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", adminurl
);
6551 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", location
);
6552 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "product=(%s)", model
);
6553 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
6554 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", color
? "T" : "F");
6555 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", duplex
? "T" : "F");
6556 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MFG=%s", make
);
6557 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MDL=%s", model
);
6558 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", uuid
);
6560 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
6561 # endif /* HAVE_SSL */
6564 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6567 avahi_threaded_poll_lock(DNSSDMaster
);
6569 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
6571 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
);
6574 * Then register the _ipp._tcp (IPP)...
6577 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
);
6578 if (subtype
&& *subtype
)
6580 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
6581 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
6586 * _ipps._tcp (IPPS) for secure printing...
6589 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
);
6590 if (subtype
&& *subtype
)
6592 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
6593 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
6595 #endif /* HAVE_SSL */
6598 * Finally _http.tcp (HTTP) for the web interface...
6601 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
);
6602 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");
6608 avahi_entry_group_commit(printer
->ipp_ref
);
6609 avahi_threaded_poll_unlock(DNSSDMaster
);
6611 avahi_string_list_free(ipp_txt
);
6612 #endif /* HAVE_DNSSD */
6619 * 'respond_http()' - Send a HTTP response.
6622 int /* O - 1 on success, 0 on failure */
6624 _ipp_client_t
*client
, /* I - Client */
6625 http_status_t code
, /* I - HTTP status of response */
6626 const char *content_encoding
, /* I - Content-Encoding of response */
6627 const char *type
, /* I - MIME media type of response */
6628 size_t length
) /* I - Length of response */
6630 char message
[1024]; /* Text message */
6633 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
6635 if (code
== HTTP_STATUS_CONTINUE
)
6638 * 100-continue doesn't send any headers...
6641 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
6645 * Format an error message...
6648 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
6650 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
6652 type
= "text/plain";
6653 length
= strlen(message
);
6659 * Send the HTTP response header...
6662 httpClearFields(client
->http
);
6664 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
6665 client
->operation
== HTTP_STATE_OPTIONS
)
6666 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
6670 if (!strcmp(type
, "text/html"))
6671 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
6672 "text/html; charset=utf-8");
6674 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
6676 if (content_encoding
)
6677 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
6680 httpSetLength(client
->http
, length
);
6682 if (httpWriteResponse(client
->http
, code
) < 0)
6686 * Send the response data...
6692 * Send a plain text message.
6695 if (httpPrintf(client
->http
, "%s", message
) < 0)
6698 if (httpWrite2(client
->http
, "", 0) < 0)
6701 else if (client
->response
)
6704 * Send an IPP response...
6707 debug_attributes("Response", client
->response
, 2);
6709 ippSetState(client
->response
, IPP_STATE_IDLE
);
6711 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6720 * 'respond_ipp()' - Send an IPP response.
6724 respond_ipp(_ipp_client_t
*client
, /* I - Client */
6725 ipp_status_t status
, /* I - status-code */
6726 const char *message
, /* I - printf-style status-message */
6727 ...) /* I - Additional args as needed */
6729 const char *formatted
= NULL
; /* Formatted message */
6732 ippSetStatusCode(client
->response
, status
);
6736 va_list ap
; /* Pointer to additional args */
6737 ipp_attribute_t
*attr
; /* New status-message attribute */
6739 va_start(ap
, message
);
6740 if ((attr
= ippFindAttribute(client
->response
, "status-message",
6741 IPP_TAG_TEXT
)) != NULL
)
6742 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6744 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
6745 "status-message", NULL
, message
, ap
);
6748 formatted
= ippGetString(attr
, 0, NULL
);
6752 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
6753 ippOpString(client
->operation_id
), ippErrorString(status
),
6756 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
6757 ippOpString(client
->operation_id
), ippErrorString(status
));
6762 * 'respond_unsupported()' - Respond with an unsupported attribute.
6766 respond_unsupported(
6767 _ipp_client_t
*client
, /* I - Client */
6768 ipp_attribute_t
*attr
) /* I - Atribute */
6770 ipp_attribute_t
*temp
; /* Copy of attribute */
6773 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6774 "Unsupported %s %s%s value.", ippGetName(attr
),
6775 ippGetCount(attr
) > 1 ? "1setOf " : "",
6776 ippTagString(ippGetValueTag(attr
)));
6778 temp
= ippCopyAttribute(client
->response
, attr
, 0);
6779 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
6784 * 'run_printer()' - Run the printer service.
6788 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
6790 int num_fds
; /* Number of file descriptors */
6791 struct pollfd polldata
[3]; /* poll() data */
6792 int timeout
; /* Timeout for poll() */
6793 _ipp_client_t
*client
; /* New client */
6797 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6800 polldata
[0].fd
= printer
->ipv4
;
6801 polldata
[0].events
= POLLIN
;
6803 polldata
[1].fd
= printer
->ipv6
;
6804 polldata
[1].events
= POLLIN
;
6809 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
6810 polldata
[num_fds
++].events
= POLLIN
;
6811 #endif /* HAVE_DNSSD */
6814 * Loop until we are killed or have a hard error...
6819 if (cupsArrayCount(printer
->jobs
))
6824 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
6826 perror("poll() failed");
6830 if (polldata
[0].revents
& POLLIN
)
6832 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6834 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
6836 perror("Unable to create client thread");
6837 delete_client(client
);
6842 if (polldata
[1].revents
& POLLIN
)
6844 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6846 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
6848 perror("Unable to create client thread");
6849 delete_client(client
);
6855 if (polldata
[2].revents
& POLLIN
)
6856 DNSServiceProcessResult(DNSSDMaster
);
6857 #endif /* HAVE_DNSSD */
6860 * Clean out old jobs...
6863 clean_jobs(printer
);
6869 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6873 time_string(time_t tv
, /* I - Time value */
6874 char *buffer
, /* I - Buffer */
6875 size_t bufsize
) /* I - Size of buffer */
6877 struct tm
*curtime
= localtime(&tv
);
6880 strftime(buffer
, bufsize
, "%X", curtime
);
6886 * 'usage()' - Show program usage.
6890 usage(int status
) /* O - Exit status */
6894 puts(CUPS_SVERSION
" - Copyright 2010-2015 by Apple Inc. All rights "
6899 puts("Usage: ippserver [options] \"name\"");
6902 puts("-2 Supports 2-sided printing (default=1-sided)");
6903 puts("-M manufacturer Manufacturer name (default=Test)");
6904 puts("-P PIN printing mode");
6905 puts("-a attributes-file Load printer attributes from file");
6906 puts("-c command Run command for every print job");
6907 printf("-d spool-directory Spool directory "
6908 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6909 puts("-f type/subtype[,...] List of supported types "
6910 "(default=application/pdf,image/jpeg)");
6911 puts("-h Show program help");
6912 puts("-i iconfile.png PNG icon file (default=printer.png)");
6913 puts("-k Keep job spool files");
6914 puts("-l location Location of printer (default=empty string)");
6915 puts("-m model Model name (default=Printer)");
6916 puts("-n hostname Hostname for printer");
6917 puts("-p port Port number (default=auto)");
6918 puts("-r subtype Bonjour service subtype (default=_print)");
6919 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6920 puts("-v[vvv] Be (very) verbose");
6927 * 'valid_doc_attributes()' - Determine whether the document attributes are
6930 * When one or more document attributes are invalid, this function adds a
6931 * suitable response and attributes to the unsupported group.
6934 static int /* O - 1 if valid, 0 if not */
6935 valid_doc_attributes(
6936 _ipp_client_t
*client
) /* I - Client */
6938 int valid
= 1; /* Valid attributes? */
6939 ipp_op_t op
= ippGetOperation(client
->request
);
6941 const char *op_name
= ippOpString(op
);
6942 /* IPP operation name */
6943 ipp_attribute_t
*attr
, /* Current attribute */
6944 *supported
; /* xxx-supported attribute */
6945 const char *compression
= NULL
,
6946 /* compression value */
6947 *format
= NULL
; /* document-format value */
6951 * Check operation attributes...
6954 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6957 * If compression is specified, only accept a supported value in a Print-Job
6958 * or Send-Document request...
6961 compression
= ippGetString(attr
, 0, NULL
);
6962 supported
= ippFindAttribute(client
->printer
->attrs
,
6963 "compression-supported", IPP_TAG_KEYWORD
);
6965 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6966 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6967 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6968 op
!= IPP_OP_VALIDATE_JOB
) ||
6969 !ippContainsString(supported
, compression
))
6971 respond_unsupported(client
, attr
);
6976 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6978 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6980 if (strcmp(compression
, "none"))
6983 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6984 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
6990 * Is it a format we support?
6993 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
6995 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
6996 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
6998 respond_unsupported(client
, attr
);
7003 format
= ippGetString(attr
, 0, NULL
);
7005 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
7006 client
->hostname
, op_name
, format
);
7008 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
7013 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
7015 format
= "application/octet-stream"; /* Should never happen */
7017 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
7020 if (!strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
7023 * Auto-type the file using the first 8 bytes of the file...
7026 unsigned char header
[8]; /* First 8 bytes of file */
7028 memset(header
, 0, sizeof(header
));
7029 httpPeek(client
->http
, (char *)header
, sizeof(header
));
7031 if (!memcmp(header
, "%PDF", 4))
7032 format
= "application/pdf";
7033 else if (!memcmp(header
, "%!", 2))
7034 format
= "application/postscript";
7035 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
7036 format
= "image/jpeg";
7037 else if (!memcmp(header
, "\211PNG", 4))
7038 format
= "image/png";
7039 else if (!memcmp(header
, "RAS2", 4))
7040 format
= "image/pwg-raster";
7041 else if (!memcmp(header
, "UNIRAST", 8))
7042 format
= "image/urf";
7048 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
7049 client
->hostname
, op_name
, format
);
7051 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
7055 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
7057 respond_unsupported(client
, attr
);
7065 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
7066 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
7073 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
7075 * When one or more job attributes are invalid, this function adds a suitable
7076 * response and attributes to the unsupported group.
7079 static int /* O - 1 if valid, 0 if not */
7080 valid_job_attributes(
7081 _ipp_client_t
*client
) /* I - Client */
7083 int i
, /* Looping var */
7084 count
, /* Number of values */
7085 valid
= 1; /* Valid attributes? */
7086 ipp_attribute_t
*attr
, /* Current attribute */
7087 *supported
; /* xxx-supported attribute */
7091 * Check operation attributes...
7094 valid
= valid_doc_attributes(client
);
7097 * Check the various job template attributes...
7100 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
7102 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7103 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
7105 respond_unsupported(client
, attr
);
7110 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
7112 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
7114 respond_unsupported(client
, attr
);
7119 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
7121 if (ippGetCount(attr
) != 1 ||
7122 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7123 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7124 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7125 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
7127 respond_unsupported(client
, attr
);
7132 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
7134 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
7136 respond_unsupported(client
, attr
);
7141 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
7143 if (ippGetCount(attr
) != 1 ||
7144 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7145 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
7147 respond_unsupported(client
, attr
);
7151 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
7154 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
7156 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
7158 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7159 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
7161 respond_unsupported(client
, attr
);
7166 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
7168 if (ippGetCount(attr
) != 1 ||
7169 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7170 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7171 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7172 strcmp(ippGetString(attr
, 0, NULL
), "none"))
7174 respond_unsupported(client
, attr
);
7179 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
7181 if (ippGetCount(attr
) != 1 ||
7182 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7183 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7184 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
7186 respond_unsupported(client
, attr
);
7191 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7193 if (!ippContainsString(supported
, ippGetString(attr
, 0, NULL
)))
7195 respond_unsupported(client
, attr
);
7201 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
7203 ipp_t
*col
, /* media-col collection */
7204 *size
; /* media-size collection */
7205 ipp_attribute_t
*member
, /* Member attribute */
7206 *x_dim
, /* x-dimension */
7207 *y_dim
; /* y-dimension */
7208 int x_value
, /* y-dimension value */
7209 y_value
; /* x-dimension value */
7211 if (ippGetCount(attr
) != 1 ||
7212 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
7214 respond_unsupported(client
, attr
);
7218 col
= ippGetCollection(attr
, 0);
7220 if ((member
= ippFindAttribute(col
, "media-size-name", IPP_TAG_ZERO
)) != NULL
)
7222 if (ippGetCount(member
) != 1 ||
7223 (ippGetValueTag(member
) != IPP_TAG_NAME
&&
7224 ippGetValueTag(member
) != IPP_TAG_NAMELANG
&&
7225 ippGetValueTag(member
) != IPP_TAG_KEYWORD
))
7227 respond_unsupported(client
, attr
);
7232 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7234 if (!ippContainsString(supported
, ippGetString(member
, 0, NULL
)))
7236 respond_unsupported(client
, attr
);
7241 else if ((member
= ippFindAttribute(col
, "media-size", IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
7243 if (ippGetCount(member
) != 1)
7245 respond_unsupported(client
, attr
);
7250 size
= ippGetCollection(member
, 0);
7252 if ((x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(x_dim
) != 1 ||
7253 (y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(y_dim
) != 1)
7255 respond_unsupported(client
, attr
);
7260 x_value
= ippGetInteger(x_dim
, 0);
7261 y_value
= ippGetInteger(y_dim
, 0);
7262 supported
= ippFindAttribute(client
->printer
->attrs
, "media-size-supported", IPP_TAG_BEGIN_COLLECTION
);
7263 count
= ippGetCount(supported
);
7265 for (i
= 0; i
< count
; i
++)
7267 size
= ippGetCollection(supported
, i
);
7268 x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_ZERO
);
7269 y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_ZERO
);
7271 if (ippContainsInteger(x_dim
, x_value
) && ippContainsInteger(y_dim
, y_value
))
7277 respond_unsupported(client
, attr
);
7285 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
7287 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7288 (strcmp(ippGetString(attr
, 0, NULL
),
7289 "separate-documents-uncollated-copies") &&
7290 strcmp(ippGetString(attr
, 0, NULL
),
7291 "separate-documents-collated-copies")))
7293 respond_unsupported(client
, attr
);
7298 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
7300 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7301 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
7302 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
7304 respond_unsupported(client
, attr
);
7309 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
7311 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
7313 respond_unsupported(client
, attr
);
7318 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
7320 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7321 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
7322 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
7324 respond_unsupported(client
, attr
);
7329 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
7331 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
7333 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
7336 respond_unsupported(client
, attr
);
7341 int xdpi
, /* Horizontal resolution for job template attribute */
7342 ydpi
, /* Vertical resolution for job template attribute */
7343 sydpi
; /* Vertical resolution for supported value */
7344 ipp_res_t units
, /* Units for job template attribute */
7345 sunits
; /* Units for supported value */
7347 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
7348 count
= ippGetCount(supported
);
7350 for (i
= 0; i
< count
; i
++)
7352 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
7358 respond_unsupported(client
, attr
);
7364 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
7366 const char *sides
= ippGetString(attr
, 0, NULL
);
7367 /* "sides" value... */
7369 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
7371 respond_unsupported(client
, attr
);
7374 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
7376 if (!ippContainsString(supported
, sides
))
7378 respond_unsupported(client
, attr
);
7382 else if (strcmp(sides
, "one-sided"))
7384 respond_unsupported(client
, attr
);