4 * Sample IPP Everywhere server for CUPS.
6 * Copyright 2010-2014 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * Disable private and deprecated stuff so we can verify that the public API
19 * is sufficient to implement a server.
22 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
23 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
27 * Include necessary headers...
30 #include <config.h> /* CUPS configuration header */
31 #include <cups/cups.h> /* Public API */
32 #include <cups/string-private.h> /* CUPS string functions */
33 #include <cups/thread-private.h> /* For multithreading functions */
46 # define WEXITSTATUS(s) (s)
47 # include <winsock2.h>
51 extern char **environ
;
53 # include <sys/fcntl.h>
54 # include <sys/wait.h>
60 #elif defined(HAVE_AVAHI)
61 # include <avahi-client/client.h>
62 # include <avahi-client/publish.h>
63 # include <avahi-common/error.h>
64 # include <avahi-common/thread-watch.h>
65 #endif /* HAVE_DNSSD */
66 #ifdef HAVE_SYS_MOUNT_H
67 # include <sys/mount.h>
68 #endif /* HAVE_SYS_MOUNT_H */
69 #ifdef HAVE_SYS_STATFS_H
70 # include <sys/statfs.h>
71 #endif /* HAVE_SYS_STATFS_H */
72 #ifdef HAVE_SYS_STATVFS_H
73 # include <sys/statvfs.h>
74 #endif /* HAVE_SYS_STATVFS_H */
77 #endif /* HAVE_SYS_VFS_H */
84 enum _ipp_preason_e
/* printer-state-reasons bit values */
86 _IPP_PREASON_NONE
= 0x0000, /* none */
87 _IPP_PREASON_OTHER
= 0x0001, /* other */
88 _IPP_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
89 _IPP_PREASON_INPUT_TRAY_MISSING
= 0x0004,
90 /* input-tray-missing */
91 _IPP_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
92 /* marker-supply-empty */
93 _IPP_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
94 /* marker-supply-low */
95 _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
96 /* marker-waste-almost-full */
97 _IPP_PREASON_MARKER_WASTE_FULL
= 0x0040,
98 /* marker-waste-full */
99 _IPP_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
100 _IPP_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
101 _IPP_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
102 _IPP_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
103 _IPP_PREASON_MOVING_TO_PAUSED
= 0x0800,
104 /* moving-to-paused */
105 _IPP_PREASON_PAUSED
= 0x1000, /* paused */
106 _IPP_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
107 _IPP_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
108 _IPP_PREASON_TONER_LOW
= 0x8000 /* toner-low */
110 typedef unsigned int _ipp_preason_t
; /* Bitfield for printer-state-reasons */
112 typedef enum _ipp_media_class_e
114 _IPP_GENERAL
, /* General-purpose size */
115 _IPP_PHOTO_ONLY
, /* Photo-only size */
116 _IPP_ENV_ONLY
/* Envelope-only size */
117 } _ipp_media_class_t
;
119 typedef enum _ipp_media_size_e
121 _IPP_MEDIA_SIZE_NONE
= -1,
126 _IPP_MEDIA_SIZE_LEGAL
,
127 _IPP_MEDIA_SIZE_LETTER
,
128 _IPP_MEDIA_SIZE_COM10
,
134 static const char * const media_supported
[] =
135 { /* media-supported values */
136 "iso_a4_210x297mm", /* A4 */
137 "iso_a5_148x210mm", /* A5 */
138 "iso_a6_105x148mm", /* A6 */
139 "iso_dl_110x220mm", /* DL */
140 "na_legal_8.5x14in", /* Legal */
141 "na_letter_8.5x11in", /* Letter */
142 "na_number-10_4.125x9.5in", /* #10 */
143 "na_index-3x5_3x5in", /* 3x5 */
144 "oe_photo-l_3.5x5in", /* L */
145 "na_index-4x6_4x6in", /* 4x6 */
146 "na_5x7_5x7in" /* 5x7 aka 2L */
148 static const int media_col_sizes
[][3] =
149 { /* media-col-database sizes */
150 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
151 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
152 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
153 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
154 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
155 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
156 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
157 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
158 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
159 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
160 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
163 typedef enum _ipp_media_source_e
165 _IPP_MEDIA_SOURCE_NONE
= -1,
166 _IPP_MEDIA_SOURCE_AUTO
,
167 _IPP_MEDIA_SOURCE_MAIN
,
168 _IPP_MEDIA_SOURCE_MANUAL
,
169 _IPP_MEDIA_SOURCE_ENVELOPE
,
170 _IPP_MEDIA_SOURCE_PHOTO
171 } _ipp_media_source_t
;
172 static const char * const media_source_supported
[] =
173 /* media-source-supported values */
182 typedef enum _ipp_media_type_e
184 _IPP_MEDIA_TYPE_NONE
= -1,
185 _IPP_MEDIA_TYPE_AUTO
,
186 _IPP_MEDIA_TYPE_CARDSTOCK
,
187 _IPP_MEDIA_TYPE_ENVELOPE
,
188 _IPP_MEDIA_TYPE_LABELS
,
189 _IPP_MEDIA_TYPE_OTHER
,
190 _IPP_MEDIA_TYPE_GLOSSY
,
191 _IPP_MEDIA_TYPE_HIGH_GLOSS
,
192 _IPP_MEDIA_TYPE_MATTE
,
193 _IPP_MEDIA_TYPE_SATIN
,
194 _IPP_MEDIA_TYPE_SEMI_GLOSS
,
195 _IPP_MEDIA_TYPE_STATIONERY
,
196 _IPP_MEDIA_TYPE_LETTERHEAD
,
197 _IPP_MEDIA_TYPE_TRANSPARENCY
199 static const char * const media_type_supported
[] =
200 /* media-type-supported values */
207 "photographic-glossy",
208 "photographic-high-gloss",
209 "photographic-matte",
210 "photographic-satin",
211 "photographic-semi-gloss",
213 "stationery-letterhead",
217 typedef enum _ipp_supply_e
219 _IPP_SUPPLY_CYAN
, /* Cyan Toner */
220 _IPP_SUPPLY_MAGENTA
, /* Magenta Toner */
221 _IPP_SUPPLY_YELLOW
, /* Yellow Toner */
222 _IPP_SUPPLY_BLACK
, /* Black Toner */
223 _IPP_SUPPLY_WASTE
/* Waste Toner */
225 static const char * const printer_supplies
[] =
226 { /* printer-supply-description values */
240 typedef DNSServiceRef _ipp_srv_t
; /* Service reference */
241 typedef TXTRecordRef _ipp_txt_t
; /* TXT record */
243 #elif defined(HAVE_AVAHI)
244 typedef AvahiEntryGroup
*_ipp_srv_t
; /* Service reference */
245 typedef AvahiStringList
*_ipp_txt_t
; /* TXT record */
248 typedef void *_ipp_srv_t
; /* Service reference */
249 typedef void *_ipp_txt_t
; /* TXT record */
250 #endif /* HAVE_DNSSD */
252 typedef struct _ipp_filter_s
/**** Attribute filter ****/
254 cups_array_t
*ra
; /* Requested attributes */
255 ipp_tag_t group_tag
; /* Group to copy */
258 typedef struct _ipp_job_s _ipp_job_t
;
260 typedef struct _ipp_printer_s
/**** Printer data ****/
262 int ipv4
, /* IPv4 listener */
263 ipv6
; /* IPv6 listener */
264 _ipp_srv_t ipp_ref
, /* Bonjour IPP service */
265 ipps_ref
, /* Bonjour IPPS service */
266 http_ref
, /* Bonjour HTTP service */
267 printer_ref
; /* Bonjour LPD service */
268 char *dnssd_name
, /* printer-dnssd-name */
269 *name
, /* printer-name */
270 *icon
, /* Icon filename */
271 *directory
, /* Spool directory */
272 *hostname
, /* Hostname */
273 *uri
, /* printer-uri-supported */
274 *command
; /* Command to run with job file */
276 size_t urilen
; /* Length of printer URI */
277 ipp_t
*attrs
; /* Static attributes */
278 time_t start_time
; /* Startup time */
279 time_t config_time
; /* printer-config-change-time */
280 ipp_pstate_t state
; /* printer-state value */
281 _ipp_preason_t state_reasons
; /* printer-state-reasons values */
282 time_t state_time
; /* printer-state-change-time */
283 cups_array_t
*jobs
; /* Jobs */
284 _ipp_job_t
*active_job
; /* Current active/pending job */
285 int next_job_id
; /* Next job-id value */
286 _cups_rwlock_t rwlock
; /* Printer lock */
287 _ipp_media_size_t main_size
; /* Ready media */
288 _ipp_media_type_t main_type
;
290 _ipp_media_size_t envelope_size
;
292 _ipp_media_size_t photo_size
;
293 _ipp_media_type_t photo_type
;
295 int supplies
[5]; /* Supply levels (0-100) */
298 struct _ipp_job_s
/**** Job data ****/
301 const char *name
, /* job-name */
302 *username
, /* job-originating-user-name */
303 *format
; /* document-format */
304 ipp_jstate_t state
; /* job-state value */
305 time_t created
, /* time-at-creation value */
306 processing
, /* time-at-processing value */
307 completed
; /* time-at-completed value */
308 int impressions
, /* job-impressions value */
309 impcompleted
; /* job-impressions-completed value */
310 ipp_t
*attrs
; /* Static attributes */
311 int cancel
; /* Non-zero when job canceled */
312 char *filename
; /* Print file name */
313 int fd
; /* Print file descriptor */
314 _ipp_printer_t
*printer
; /* Printer */
317 typedef struct _ipp_client_s
/**** Client data ****/
319 http_t
*http
; /* HTTP connection */
320 ipp_t
*request
, /* IPP request */
321 *response
; /* IPP response */
322 time_t start
; /* Request start time */
323 http_state_t operation
; /* Request operation */
324 ipp_op_t operation_id
; /* IPP operation-id */
325 char uri
[1024], /* Request URI */
326 *options
; /* URI options */
327 http_addr_t addr
; /* Client address */
328 char hostname
[256]; /* Client hostname */
329 _ipp_printer_t
*printer
; /* Printer */
330 _ipp_job_t
*job
; /* Current job, if any */
338 static void clean_jobs(_ipp_printer_t
*printer
);
339 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
340 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
341 ipp_tag_t group_tag
, int quickcopy
);
342 static void copy_job_attributes(_ipp_client_t
*client
,
343 _ipp_job_t
*job
, cups_array_t
*ra
);
344 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
345 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
346 static int create_listener(int family
, int port
);
347 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
348 static ipp_t
*create_media_size(int width
, int length
);
349 static _ipp_printer_t
*create_printer(const char *servername
,
350 const char *name
, const char *location
,
351 const char *make
, const char *model
,
353 const char *docformats
, int ppm
,
354 int ppm_color
, int duplex
, int port
,
355 int pin
, const char *subtype
,
356 const char *directory
,
357 const char *command
);
358 static void debug_attributes(const char *title
, ipp_t
*ipp
,
360 static void delete_client(_ipp_client_t
*client
);
361 static void delete_job(_ipp_job_t
*job
);
362 static void delete_printer(_ipp_printer_t
*printer
);
364 static void dnssd_callback(DNSServiceRef sdRef
,
365 DNSServiceFlags flags
,
366 DNSServiceErrorType errorCode
,
370 _ipp_printer_t
*printer
);
371 #elif defined(HAVE_AVAHI)
372 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
373 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
374 #endif /* HAVE_DNSSD */
375 static void dnssd_init(void);
376 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
377 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
378 static void html_escape(_ipp_client_t
*client
, const char *s
,
380 static void html_footer(_ipp_client_t
*client
);
381 static void html_header(_ipp_client_t
*client
, const char *title
);
382 static void html_printf(_ipp_client_t
*client
, const char *format
,
383 ...) __attribute__((__format__(__printf__
,
385 static void ipp_cancel_job(_ipp_client_t
*client
);
386 static void ipp_close_job(_ipp_client_t
*client
);
387 static void ipp_create_job(_ipp_client_t
*client
);
388 static void ipp_get_job_attributes(_ipp_client_t
*client
);
389 static void ipp_get_jobs(_ipp_client_t
*client
);
390 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
391 static void ipp_identify_printer(_ipp_client_t
*client
);
392 static void ipp_print_job(_ipp_client_t
*client
);
393 static void ipp_print_uri(_ipp_client_t
*client
);
394 static void ipp_send_document(_ipp_client_t
*client
);
395 static void ipp_send_uri(_ipp_client_t
*client
);
396 static void ipp_validate_job(_ipp_client_t
*client
);
397 static int parse_options(_ipp_client_t
*client
, cups_option_t
**options
);
398 static void *process_client(_ipp_client_t
*client
);
399 static int process_http(_ipp_client_t
*client
);
400 static int process_ipp(_ipp_client_t
*client
);
401 static void *process_job(_ipp_job_t
*job
);
402 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
);
403 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
404 const char *content_coding
,
405 const char *type
, size_t length
);
406 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
407 const char *message
, ...)
408 __attribute__ ((__format__ (__printf__
, 3, 4)));
409 static void respond_unsupported(_ipp_client_t
*client
,
410 ipp_attribute_t
*attr
);
411 static void run_printer(_ipp_printer_t
*printer
);
412 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
413 static void usage(int status
) __attribute__((noreturn
));
414 static int valid_doc_attributes(_ipp_client_t
*client
);
415 static int valid_job_attributes(_ipp_client_t
*client
);
423 static DNSServiceRef DNSSDMaster
= NULL
;
424 #elif defined(HAVE_AVAHI)
425 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
426 static AvahiClient
*DNSSDClient
= NULL
;
427 #endif /* HAVE_DNSSD */
429 static int KeepFiles
= 0,
434 * 'main()' - Main entry to the sample server.
437 int /* O - Exit status */
438 main(int argc
, /* I - Number of command-line args */
439 char *argv
[]) /* I - Command-line arguments */
441 int i
; /* Looping var */
442 const char *opt
, /* Current option character */
443 *command
= NULL
, /* Command to run with job files */
444 *servername
= NULL
, /* Server host name */
445 *name
= NULL
, /* Printer name */
446 *location
= "", /* Location of printer */
447 *make
= "Test", /* Manufacturer */
448 *model
= "Printer", /* Model */
449 *icon
= "printer.png", /* Icon file */
450 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
451 /* Supported formats */
453 const char *keypath
= NULL
; /* Keychain path */
454 #endif /* HAVE_SSL */
455 const char *subtype
= "_print"; /* Bonjour service subtype */
456 int port
= 0, /* Port number (0 = auto) */
457 duplex
= 0, /* Duplex mode */
458 ppm
= 10, /* Pages per minute for mono */
459 ppm_color
= 0, /* Pages per minute for color */
460 pin
= 0; /* PIN printing mode? */
461 char directory
[1024] = "", /* Spool directory */
462 hostname
[1024]; /* Auto-detected hostname */
463 _ipp_printer_t
*printer
; /* Printer object */
467 * Parse command-line arguments...
470 for (i
= 1; i
< argc
; i
++)
471 if (argv
[i
][0] == '-')
473 for (opt
= argv
[i
] + 1; *opt
; opt
++)
477 case '2' : /* -2 (enable 2-sided printing) */
482 case 'K' : /* -K keypath */
488 #endif /* HAVE_SSL */
490 case 'M' : /* -M manufacturer */
497 case 'P' : /* -P (PIN printing mode) */
501 case 'c' : /* -c command */
509 case 'd' : /* -d spool-directory */
513 strlcpy(directory
, argv
[i
], sizeof(directory
));
516 case 'f' : /* -f type/subtype[,...] */
523 case 'h' : /* -h (show help) */
526 case 'i' : /* -i icon.png */
533 case 'k' : /* -k (keep files) */
537 case 'l' : /* -l location */
544 case 'm' : /* -m model */
551 case 'n' : /* -n hostname */
555 servername
= argv
[i
];
558 case 'p' : /* -p port */
560 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
562 port
= atoi(argv
[i
]);
565 case 'r' : /* -r subtype */
572 case 's' : /* -s speed[,color-speed] */
576 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
580 case 'v' : /* -v (be verbose) */
584 default : /* Unknown */
585 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
596 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
604 * Apply defaults as needed...
608 servername
= httpGetHostname(NULL
, hostname
, sizeof(hostname
));
614 * Windows is almost always used as a single user system, so use a default
615 * port number of 8631.
622 * Use 8000 + UID mod 1000 for the default port number...
625 port
= 8000 + ((int)getuid() % 1000);
628 fprintf(stderr
, "Listening on port %d.\n", port
);
633 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
635 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
637 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
638 directory
, strerror(errno
));
643 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
647 cupsSetServerCredentials(keypath
, servername
, 1);
648 #endif /* HAVE_SSL */
651 * Initialize Bonjour...
657 * Create the printer...
660 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
661 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
662 subtype
, directory
, command
)) == NULL
)
666 * Run the print service...
669 run_printer(printer
);
672 * Destroy the printer and exit...
675 delete_printer(printer
);
682 * 'clean_jobs()' - Clean out old (completed) jobs.
686 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
688 _ipp_job_t
*job
; /* Current job */
689 time_t cleantime
; /* Clean time */
692 if (cupsArrayCount(printer
->jobs
) == 0)
695 cleantime
= time(NULL
) - 60;
697 _cupsRWLockWrite(&(printer
->rwlock
));
698 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
700 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
701 if (job
->completed
&& job
->completed
< cleantime
)
703 cupsArrayRemove(printer
->jobs
, job
);
708 _cupsRWUnlock(&(printer
->rwlock
));
713 * 'compare_jobs()' - Compare two jobs.
716 static int /* O - Result of comparison */
717 compare_jobs(_ipp_job_t
*a
, /* I - First job */
718 _ipp_job_t
*b
) /* I - Second job */
720 return (b
->id
- a
->id
);
725 * 'copy_attributes()' - Copy attributes from one request to another.
729 copy_attributes(ipp_t
*to
, /* I - Destination request */
730 ipp_t
*from
, /* I - Source request */
731 cups_array_t
*ra
, /* I - Requested attributes */
732 ipp_tag_t group_tag
, /* I - Group to copy */
733 int quickcopy
) /* I - Do a quick copy? */
735 _ipp_filter_t filter
; /* Filter data */
739 filter
.group_tag
= group_tag
;
741 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
746 * 'copy_job_attrs()' - Copy job attributes to the response.
751 _ipp_client_t
*client
, /* I - Client */
752 _ipp_job_t
*job
, /* I - Job */
753 cups_array_t
*ra
) /* I - requested-attributes */
755 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
757 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
760 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
762 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
765 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
768 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
770 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
773 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
774 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
776 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
777 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
779 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
780 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
782 if (!ra
|| cupsArrayFind(ra
, "job-state"))
783 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
784 "job-state", job
->state
);
786 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
790 case IPP_JSTATE_PENDING
:
791 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
794 case IPP_JSTATE_HELD
:
796 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
797 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
798 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
800 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
803 case IPP_JSTATE_PROCESSING
:
805 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
807 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
810 case IPP_JSTATE_STOPPED
:
811 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
814 case IPP_JSTATE_CANCELED
:
815 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
818 case IPP_JSTATE_ABORTED
:
819 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
822 case IPP_JSTATE_COMPLETED
:
823 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
828 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
832 case IPP_JSTATE_PENDING
:
833 ippAddString(client
->response
, IPP_TAG_JOB
,
834 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
838 case IPP_JSTATE_HELD
:
840 ippAddString(client
->response
, IPP_TAG_JOB
,
841 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
842 "job-state-reasons", NULL
, "job-incoming");
843 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
844 ippAddString(client
->response
, IPP_TAG_JOB
,
845 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
846 "job-state-reasons", NULL
, "job-hold-until-specified");
848 ippAddString(client
->response
, IPP_TAG_JOB
,
849 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
850 "job-state-reasons", NULL
, "job-data-insufficient");
853 case IPP_JSTATE_PROCESSING
:
855 ippAddString(client
->response
, IPP_TAG_JOB
,
856 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
857 "job-state-reasons", NULL
, "processing-to-stop-point");
859 ippAddString(client
->response
, IPP_TAG_JOB
,
860 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
861 "job-state-reasons", NULL
, "job-printing");
864 case IPP_JSTATE_STOPPED
:
865 ippAddString(client
->response
, IPP_TAG_JOB
,
866 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
867 NULL
, "job-stopped");
870 case IPP_JSTATE_CANCELED
:
871 ippAddString(client
->response
, IPP_TAG_JOB
,
872 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
873 NULL
, "job-canceled-by-user");
876 case IPP_JSTATE_ABORTED
:
877 ippAddString(client
->response
, IPP_TAG_JOB
,
878 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
879 NULL
, "aborted-by-system");
882 case IPP_JSTATE_COMPLETED
:
883 ippAddString(client
->response
, IPP_TAG_JOB
,
884 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
885 NULL
, "job-completed-successfully");
890 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
891 ippAddInteger(client
->response
, IPP_TAG_JOB
,
892 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
893 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
895 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
896 ippAddInteger(client
->response
, IPP_TAG_JOB
,
897 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
898 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
903 * 'create_client()' - Accept a new network connection and create a client
907 static _ipp_client_t
* /* O - Client */
908 create_client(_ipp_printer_t
*printer
, /* I - Printer */
909 int sock
) /* I - Listen socket */
911 _ipp_client_t
*client
; /* Client */
914 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
916 perror("Unable to allocate memory for client");
920 client
->printer
= printer
;
923 * Accept the client and get the remote address...
926 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
928 perror("Unable to accept client connection");
935 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
938 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
945 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
949 static _ipp_job_t
* /* O - Job */
950 create_job(_ipp_client_t
*client
) /* I - Client */
952 _ipp_job_t
*job
; /* Job */
953 ipp_attribute_t
*attr
; /* Job attribute */
954 char uri
[1024], /* job-uri value */
955 uuid
[64]; /* job-uuid value */
958 _cupsRWLockWrite(&(client
->printer
->rwlock
));
959 if (client
->printer
->active_job
&&
960 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
963 * Only accept a single job at a time...
966 _cupsRWLockWrite(&(client
->printer
->rwlock
));
971 * Allocate and initialize the job object...
974 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
976 perror("Unable to allocate memory for job");
980 job
->printer
= client
->printer
;
981 job
->attrs
= ippNew();
982 job
->state
= IPP_JSTATE_HELD
;
986 * Copy all of the job attributes...
989 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
992 * Get the requesting-user-name, document format, and priority...
995 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
996 job
->username
= ippGetString(attr
, 0, NULL
);
998 job
->username
= "anonymous";
1000 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1002 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1004 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1005 job
->format
= ippGetString(attr
, 0, NULL
);
1006 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1007 job
->format
= ippGetString(attr
, 0, NULL
);
1009 job
->format
= "application/octet-stream";
1012 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1013 job
->impressions
= ippGetInteger(attr
, 0);
1015 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1016 job
->name
= ippGetString(attr
, 0, NULL
);
1019 * Add job description attributes and add to the jobs array...
1022 job
->id
= client
->printer
->next_job_id
++;
1024 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1025 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1027 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1028 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1029 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1030 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1031 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1032 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1034 cupsArrayAdd(client
->printer
->jobs
, job
);
1035 client
->printer
->active_job
= job
;
1037 _cupsRWUnlock(&(client
->printer
->rwlock
));
1044 * 'create_job_filename()' - Create the filename for a document in a job.
1047 static void create_job_filename(
1048 _ipp_printer_t
*printer
, /* I - Printer */
1049 _ipp_job_t
*job
, /* I - Job */
1050 char *fname
, /* I - Filename buffer */
1051 size_t fnamesize
) /* I - Size of filename buffer */
1053 char name
[256], /* "Safe" filename */
1054 *nameptr
; /* Pointer into filename */
1055 const char *ext
, /* Filename extension */
1056 *job_name
; /* job-name value */
1057 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1061 * Make a name from the job-name attribute...
1064 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1065 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1067 job_name
= "untitled";
1069 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1070 if (isalnum(*job_name
& 255) || *job_name
== '-')
1071 *nameptr
++ = (char)tolower(*job_name
& 255);
1078 * Figure out the extension...
1081 if (!strcasecmp(job
->format
, "image/jpeg"))
1083 else if (!strcasecmp(job
->format
, "image/png"))
1085 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1087 else if (!strcasecmp(job
->format
, "image/urf"))
1089 else if (!strcasecmp(job
->format
, "application/pdf"))
1091 else if (!strcasecmp(job
->format
, "application/postscript"))
1097 * Create a filename with the job-id, job-name, and document-format (extension)...
1100 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1105 * 'create_listener()' - Create a listener socket.
1108 static int /* O - Listener socket or -1 on error */
1109 create_listener(int family
, /* I - Address family */
1110 int port
) /* I - Port number */
1112 int sock
; /* Listener socket */
1113 http_addrlist_t
*addrlist
; /* Listen address */
1114 char service
[255]; /* Service port */
1117 snprintf(service
, sizeof(service
), "%d", port
);
1118 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1121 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1123 httpAddrFreeList(addrlist
);
1130 * 'create_media_col()' - Create a media-col value.
1133 static ipp_t
* /* O - media-col collection */
1134 create_media_col(const char *media
, /* I - Media name */
1135 const char *source
, /* I - Media source */
1136 const char *type
, /* I - Media type */
1137 int width
, /* I - x-dimension in 2540ths */
1138 int length
, /* I - y-dimension in 2540ths */
1139 int margins
) /* I - Value for margins */
1141 ipp_t
*media_col
= ippNew(), /* media-col value */
1142 *media_size
= create_media_size(width
, length
);
1143 /* media-size value */
1144 char media_key
[256]; /* media-key value */
1148 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1150 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1152 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1154 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1156 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1158 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1159 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1160 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1161 "media-bottom-margin", margins
);
1162 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1163 "media-left-margin", margins
);
1164 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1165 "media-right-margin", margins
);
1166 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1167 "media-top-margin", margins
);
1169 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1171 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1173 ippDelete(media_size
);
1180 * 'create_media_size()' - Create a media-size value.
1183 static ipp_t
* /* O - media-col collection */
1184 create_media_size(int width
, /* I - x-dimension in 2540ths */
1185 int length
) /* I - y-dimension in 2540ths */
1187 ipp_t
*media_size
= ippNew(); /* media-size value */
1190 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1192 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1195 return (media_size
);
1200 * 'create_printer()' - Create, register, and listen for connections to a
1204 static _ipp_printer_t
* /* O - Printer */
1205 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1206 const char *name
, /* I - printer-name */
1207 const char *location
, /* I - printer-location */
1208 const char *make
, /* I - printer-make-and-model */
1209 const char *model
, /* I - printer-make-and-model */
1210 const char *icon
, /* I - printer-icons */
1211 const char *docformats
, /* I - document-format-supported */
1212 int ppm
, /* I - Pages per minute in grayscale */
1213 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1214 int duplex
, /* I - 1 = duplex, 0 = simplex */
1215 int port
, /* I - Port for listeners or 0 for auto */
1216 int pin
, /* I - Require PIN printing */
1217 const char *subtype
, /* I - Bonjour service subtype */
1218 const char *directory
, /* I - Spool directory */
1219 const char *command
) /* I - Command to run on job files */
1221 int i
, j
; /* Looping vars */
1222 _ipp_printer_t
*printer
; /* Printer */
1224 char path
[1024]; /* Full path to command */
1226 char uri
[1024], /* Printer URI */
1227 icons
[1024], /* printer-icons URI */
1228 adminurl
[1024], /* printer-more-info URI */
1229 supplyurl
[1024],/* printer-supply-info-uri URI */
1230 device_id
[1024],/* printer-device-id */
1231 make_model
[128],/* printer-make-and-model */
1232 uuid
[128]; /* printer-uuid */
1233 int num_formats
; /* Number of document-format-supported values */
1234 char *defformat
, /* document-format-default value */
1235 *formats
[100], /* document-format-supported values */
1236 *ptr
; /* Pointer into string */
1237 const char *prefix
; /* Prefix string */
1238 int num_database
; /* Number of database values */
1239 ipp_attribute_t
*media_col_database
,
1240 /* media-col-database value */
1241 *media_size_supported
;
1242 /* media-size-supported value */
1243 ipp_t
*media_col_default
;
1244 /* media-col-default value */
1245 int media_col_index
;/* Current media-col-database value */
1246 int k_supported
; /* Maximum file size supported */
1248 struct statvfs spoolinfo
; /* FS info for spool directory */
1249 double spoolsize
; /* FS size */
1250 #elif defined(HAVE_STATFS)
1251 struct statfs spoolinfo
; /* FS info for spool directory */
1252 double spoolsize
; /* FS size */
1253 #endif /* HAVE_STATVFS */
1254 static const int orients
[4] = /* orientation-requested-supported values */
1256 IPP_ORIENT_PORTRAIT
,
1257 IPP_ORIENT_LANDSCAPE
,
1258 IPP_ORIENT_REVERSE_LANDSCAPE
,
1259 IPP_ORIENT_REVERSE_PORTRAIT
1261 static const char * const versions
[] =/* ipp-versions-supported values */
1267 static const char * const features
[] =/* ipp-features-supported values */
1271 static const int ops
[] = /* operations-supported values */
1275 IPP_OP_VALIDATE_JOB
,
1277 IPP_OP_SEND_DOCUMENT
,
1280 IPP_OP_GET_JOB_ATTRIBUTES
,
1282 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1283 IPP_OP_CANCEL_MY_JOBS
,
1285 IPP_OP_IDENTIFY_PRINTER
1287 static const char * const charsets
[] =/* charset-supported values */
1292 static const char * const compressions
[] =/* compression-supported values */
1297 #endif /* HAVE_LIBZ */
1300 static const char * const identify_actions
[] =
1305 static const char * const job_creation
[] =
1306 { /* job-creation-attributes-supported values */
1308 "ipp-attribute-fidelity",
1310 "job-accounting-user-id",
1316 "multiple-document-handling",
1317 "orientation-requested",
1321 static const char * const media_col_supported
[] =
1322 { /* media-col-supported values */
1323 "media-bottom-margin",
1324 "media-left-margin",
1325 "media-right-margin",
1331 static const int media_xxx_margin_supported
[] =
1332 { /* media-xxx-margin-supported values */
1336 static const char * const multiple_document_handling
[] =
1337 { /* multiple-document-handling-supported values */
1338 "separate-documents-uncollated-copies",
1339 "separate-documents-collated-copies"
1341 static const char * const overrides
[] =
1342 { /* overrides-supported */
1346 static const char * const print_color_mode_supported
[] =
1347 { /* print-color-mode-supported values */
1352 static const int print_quality_supported
[] =
1353 { /* print-quality-supported values */
1358 static const int pwg_raster_document_resolution_supported
[] =
1364 static const char * const pwg_raster_document_type_supported
[] =
1372 static const char * const reference_uri_schemes_supported
[] =
1373 { /* reference-uri-schemes-supported */
1379 #endif /* HAVE_SSL */
1381 static const char * const sides_supported
[] =
1382 { /* sides-supported values */
1384 "two-sided-long-edge",
1385 "two-sided-short-edge"
1387 static const char * const urf_supported
[] =
1388 { /* urf-supported values */
1391 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1398 static const char * const which_jobs
[] =
1399 { /* which-jobs-supported values */
1408 "processing-stopped"
1414 * If a command was specified, make sure it exists and is executable...
1419 if (*command
== '/' || !strncmp(command
, "./", 2))
1421 if (access(command
, X_OK
))
1423 fprintf(stderr
, "ippserver: Unable to execute command \"%s\": %s\n", command
, strerror(errno
));
1429 if (!cupsFileFind(command
, getenv("PATH"), 1, path
, sizeof(path
)))
1431 fprintf(stderr
, "ippserver: Unable to find command \"%s\".\n", command
);
1441 * Allocate memory for the printer...
1444 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1446 perror("ippserver: Unable to allocate memory for printer");
1452 printer
->name
= strdup(name
);
1453 printer
->dnssd_name
= strdup(printer
->name
);
1454 printer
->command
= command
? strdup(command
) : NULL
;
1455 printer
->directory
= strdup(directory
);
1456 printer
->hostname
= strdup(servername
);
1457 printer
->port
= port
;
1458 printer
->start_time
= time(NULL
);
1459 printer
->config_time
= printer
->start_time
;
1460 printer
->state
= IPP_PSTATE_IDLE
;
1461 printer
->state_reasons
= _IPP_PREASON_NONE
;
1462 printer
->state_time
= printer
->start_time
;
1463 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1464 printer
->next_job_id
= 1;
1466 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1467 printer
->hostname
, printer
->port
, "/ipp/print");
1468 printer
->uri
= strdup(uri
);
1469 printer
->urilen
= strlen(uri
);
1472 printer
->icon
= strdup(icon
);
1474 printer
->main_size
= _IPP_MEDIA_SIZE_A4
;
1475 printer
->main_type
= _IPP_MEDIA_TYPE_STATIONERY
;
1476 printer
->main_level
= 500;
1478 printer
->envelope_size
= _IPP_MEDIA_SIZE_NONE
;
1479 printer
->envelope_level
= 0;
1481 printer
->photo_size
= _IPP_MEDIA_SIZE_NONE
;
1482 printer
->photo_type
= _IPP_MEDIA_TYPE_NONE
;
1483 printer
->photo_level
= 0;
1485 printer
->supplies
[_IPP_SUPPLY_CYAN
] = 100;
1486 printer
->supplies
[_IPP_SUPPLY_MAGENTA
] = 100;
1487 printer
->supplies
[_IPP_SUPPLY_YELLOW
] = 100;
1488 printer
->supplies
[_IPP_SUPPLY_BLACK
] = 100;
1489 printer
->supplies
[_IPP_SUPPLY_WASTE
] = 0;
1491 _cupsRWInit(&(printer
->rwlock
));
1494 * Create the listener sockets...
1497 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1499 perror("Unable to create IPv4 listener");
1503 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1505 perror("Unable to create IPv6 listener");
1510 * Prepare values for the printer attributes...
1513 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1514 printer
->hostname
, printer
->port
, "/icon.png");
1515 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/");
1516 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/supplies");
1520 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1521 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1522 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1525 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1528 formats
[0] = strdup(docformats
);
1529 defformat
= formats
[0];
1530 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1533 formats
[num_formats
++] = ptr
;
1535 if (!strcasecmp(ptr
, "application/octet-stream"))
1539 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1540 ptr
= device_id
+ strlen(device_id
);
1542 for (i
= 0; i
< num_formats
; i
++)
1544 if (!strcasecmp(formats
[i
], "application/pdf"))
1545 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1546 else if (!strcasecmp(formats
[i
], "application/postscript"))
1547 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1548 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1549 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1550 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1551 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1552 else if (!strcasecmp(formats
[i
], "image/png"))
1553 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1554 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1555 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1560 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
1567 * Get the maximum spool size based on the size of the filesystem used for
1568 * the spool directory. If the host OS doesn't support the statfs call
1569 * or the filesystem is larger than 2TiB, always report INT_MAX.
1573 if (statvfs(printer
->directory
, &spoolinfo
))
1574 k_supported
= INT_MAX
;
1575 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1576 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1577 k_supported
= INT_MAX
;
1579 k_supported
= (int)spoolsize
;
1581 #elif defined(HAVE_STATFS)
1582 if (statfs(printer
->directory
, &spoolinfo
))
1583 k_supported
= INT_MAX
;
1584 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1585 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1586 k_supported
= INT_MAX
;
1588 k_supported
= (int)spoolsize
;
1591 k_supported
= INT_MAX
;
1592 #endif /* HAVE_STATVFS */
1595 * Create the printer attributes. This list of attributes is sorted to improve
1596 * performance when the client provides a requested-attributes attribute...
1599 printer
->attrs
= ippNew();
1601 /* charset-configured */
1602 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1603 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1604 "charset-configured", NULL
, "utf-8");
1606 /* charset-supported */
1607 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1608 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1609 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1612 /* color-supported */
1613 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1616 /* compression-supported */
1617 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1618 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1619 "compression-supported",
1620 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1623 /* copies-default */
1624 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1625 "copies-default", 1);
1627 /* copies-supported */
1628 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1630 /* document-format-default */
1631 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1632 "document-format-default", NULL
, defformat
);
1634 /* document-format-supported */
1635 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1636 "document-format-supported", num_formats
, NULL
,
1637 (const char * const *)formats
);
1639 /* document-password-supported */
1640 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1642 /* finishings-default */
1643 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1644 "finishings-default", IPP_FINISHINGS_NONE
);
1646 /* finishings-supported */
1647 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1648 "finishings-supported", IPP_FINISHINGS_NONE
);
1650 /* generated-natural-language-supported */
1651 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1652 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1653 "generated-natural-language-supported", NULL
, "en");
1655 /* identify-actions-default */
1656 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1658 /* identify-actions-supported */
1659 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
);
1661 /* ipp-features-supported */
1662 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1664 /* ipp-versions-supported */
1665 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1667 /* job-account-id-default */
1668 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1670 /* job-account-id-supported */
1671 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1673 /* job-accounting-user-id-default */
1674 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1676 /* job-accounting-user-id-supported */
1677 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1679 /* job-creation-attributes-supported */
1680 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
);
1682 /* job-ids-supported */
1683 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1685 /* job-k-octets-supported */
1686 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1689 /* job-password-supported */
1690 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1691 "job-password-supported", 4);
1693 /* job-preferred-attributes-supported */
1694 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-preferred-attributes-supported", 0);
1696 /* job-priority-default */
1697 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1698 "job-priority-default", 50);
1700 /* job-priority-supported */
1701 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1702 "job-priority-supported", 100);
1704 /* job-sheets-default */
1705 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1706 IPP_CONST_TAG(IPP_TAG_NAME
),
1707 "job-sheets-default", NULL
, "none");
1709 /* job-sheets-supported */
1710 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1711 IPP_CONST_TAG(IPP_TAG_NAME
),
1712 "job-sheets-supported", NULL
, "none");
1714 /* media-bottom-margin-supported */
1715 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1716 "media-bottom-margin-supported",
1717 (int)(sizeof(media_xxx_margin_supported
) /
1718 sizeof(media_xxx_margin_supported
[0])),
1719 media_xxx_margin_supported
);
1721 /* media-col-database */
1722 for (num_database
= 0, i
= 0;
1723 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1726 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1727 num_database
+= 3; /* auto + manual + envelope */
1728 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1729 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1731 num_database
+= 2; /* Regular + borderless */
1734 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1735 "media-col-database", num_database
,
1737 for (media_col_index
= 0, i
= 0;
1738 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1741 switch (media_col_sizes
[i
][2])
1745 * Regular + borderless for the general class; no source/type
1749 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]));
1750 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]));
1753 case _IPP_ENV_ONLY
:
1755 * Regular margins for "auto", "manual", and "envelope" sources.
1758 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]));
1759 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]));
1760 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]));
1762 case _IPP_PHOTO_ONLY
:
1764 * Photos have specific media types and can only be printed via
1765 * the auto, manual, and photo sources...
1769 j
< (int)(sizeof(media_type_supported
) /
1770 sizeof(media_type_supported
[0]));
1773 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1776 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]));
1777 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]));
1778 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]));
1784 /* media-col-default */
1785 media_col_default
= create_media_col(media_supported
[0],
1786 media_source_supported
[0],
1787 media_type_supported
[0],
1788 media_col_sizes
[0][0],
1789 media_col_sizes
[0][1],
1790 media_xxx_margin_supported
[1]);
1792 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1794 ippDelete(media_col_default
);
1796 /* media-col-supported */
1797 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1798 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1799 "media-col-supported",
1800 (int)(sizeof(media_col_supported
) /
1801 sizeof(media_col_supported
[0])), NULL
,
1802 media_col_supported
);
1805 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1806 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1807 "media-default", NULL
, media_supported
[0]);
1809 /* media-left-margin-supported */
1810 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1811 "media-left-margin-supported",
1812 (int)(sizeof(media_xxx_margin_supported
) /
1813 sizeof(media_xxx_margin_supported
[0])),
1814 media_xxx_margin_supported
);
1816 /* media-right-margin-supported */
1817 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1818 "media-right-margin-supported",
1819 (int)(sizeof(media_xxx_margin_supported
) /
1820 sizeof(media_xxx_margin_supported
[0])),
1821 media_xxx_margin_supported
);
1823 /* media-supported */
1824 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1825 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1827 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1828 NULL
, media_supported
);
1830 /* media-size-supported */
1831 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1832 "media-size-supported",
1833 (int)(sizeof(media_col_sizes
) /
1834 sizeof(media_col_sizes
[0])),
1837 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1839 ippSetCollection(printer
->attrs
, &media_size_supported
, i
,
1840 create_media_size(media_col_sizes
[i
][0],
1841 media_col_sizes
[i
][1]));
1843 /* media-source-supported */
1844 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
);
1846 /* media-top-margin-supported */
1847 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1848 "media-top-margin-supported",
1849 (int)(sizeof(media_xxx_margin_supported
) /
1850 sizeof(media_xxx_margin_supported
[0])),
1851 media_xxx_margin_supported
);
1853 /* media-type-supported */
1854 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
);
1856 /* multiple-document-handling-supported */
1857 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
);
1859 /* multiple-document-jobs-supported */
1860 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1862 /* multiple-operation-time-out */
1863 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1865 /* multiple-operation-time-out-action */
1866 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1868 /* natural-language-configured */
1869 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1870 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1871 "natural-language-configured", NULL
, "en");
1873 /* number-up-default */
1874 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1875 "number-up-default", 1);
1877 /* number-up-supported */
1878 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1879 "number-up-supported", 1);
1881 /* operations-supported */
1882 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1883 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1885 /* orientation-requested-default */
1886 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1887 "orientation-requested-default", 0);
1889 /* orientation-requested-supported */
1890 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1891 "orientation-requested-supported", 4, orients
);
1893 /* output-bin-default */
1894 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1895 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1896 "output-bin-default", NULL
, "face-down");
1898 /* output-bin-supported */
1899 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1900 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1901 "output-bin-supported", NULL
, "face-down");
1903 /* overrides-supported */
1904 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1906 /* page-ranges-supported */
1907 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1909 /* pages-per-minute */
1910 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1911 "pages-per-minute", ppm
);
1913 /* pages-per-minute-color */
1915 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1916 "pages-per-minute-color", ppm_color
);
1918 /* pdl-override-supported */
1919 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1920 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1921 "pdl-override-supported", NULL
, "attempted");
1923 /* print-color-mode-default */
1924 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1926 /* print-color-mode-supported */
1927 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
);
1929 /* print-content-optimize-default */
1930 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1932 /* print-content-optimize-supported */
1933 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
1935 /* print-rendering-intent-default */
1936 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
1938 /* print-rendering-intent-supported */
1939 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
1941 /* print-quality-default */
1942 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
1944 /* print-quality-supported */
1945 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
);
1947 /* printer-device-id */
1948 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1949 "printer-device-id", NULL
, device_id
);
1951 /* printer-get-attributes-supported */
1952 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1954 /* printer-geo-location */
1955 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
1958 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1959 "printer-icons", NULL
, icons
);
1961 /* printer-is-accepting-jobs */
1962 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1966 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1969 /* printer-location */
1970 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1971 "printer-location", NULL
, location
);
1973 /* printer-make-and-model */
1974 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1975 "printer-make-and-model", NULL
, make_model
);
1977 /* printer-mandatory-job-attributes */
1980 static const char * const names
[] =
1983 "job-accounting-user-id",
1987 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1988 "printer-mandatory-job-attributes",
1989 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
1992 /* printer-more-info */
1993 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1996 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1999 /* printer-organization */
2000 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
2002 /* printer-organizational-unit */
2003 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
2005 /* printer-resolution-default */
2006 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
2007 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
2009 /* printer-resolution-supported */
2010 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
2011 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
2013 /* printer-supply-description */
2014 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
);
2016 /* printer-supply-info-uri */
2017 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
2019 /* printer-uri-supported */
2020 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
2023 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
2024 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
2026 /* pwg-raster-document-xxx-supported */
2027 for (i
= 0; i
< num_formats
; i
++)
2028 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
2031 if (i
< num_formats
)
2033 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
,
2034 "pwg-raster-document-resolution-supported",
2035 (int)(sizeof(pwg_raster_document_resolution_supported
) /
2036 sizeof(pwg_raster_document_resolution_supported
[0])),
2038 pwg_raster_document_resolution_supported
,
2039 pwg_raster_document_resolution_supported
);
2040 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2041 "pwg-raster-document-sheet-back", NULL
, "normal");
2042 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2043 "pwg-raster-document-type-supported",
2044 (int)(sizeof(pwg_raster_document_type_supported
) /
2045 sizeof(pwg_raster_document_type_supported
[0])), NULL
,
2046 pwg_raster_document_type_supported
);
2049 /* reference-uri-scheme-supported */
2050 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2051 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
2052 "reference-uri-schemes-supported",
2053 (int)(sizeof(reference_uri_schemes_supported
) /
2054 sizeof(reference_uri_schemes_supported
[0])),
2055 NULL
, reference_uri_schemes_supported
);
2058 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2059 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2060 "sides-default", NULL
, "one-sided");
2062 /* sides-supported */
2063 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2064 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2065 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
2068 for (i
= 0; i
< num_formats
; i
++)
2069 if (!strcasecmp(formats
[i
], "image/urf"))
2072 if (i
< num_formats
)
2073 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2075 /* uri-authentication-supported */
2076 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2077 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2078 "uri-authentication-supported", NULL
, "none");
2080 /* uri-security-supported */
2081 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2082 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2083 "uri-security-supported", NULL
, "none");
2085 /* which-jobs-supported */
2086 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2087 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2088 "which-jobs-supported",
2089 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
2093 debug_attributes("Printer", printer
->attrs
, 0);
2096 * Register the printer with Bonjour...
2099 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2110 * If we get here we were unable to create the printer...
2115 delete_printer(printer
);
2121 * 'debug_attributes()' - Print attributes in a request or response.
2125 debug_attributes(const char *title
, /* I - Title */
2126 ipp_t
*ipp
, /* I - Request/response */
2127 int type
) /* I - 0 = object, 1 = request, 2 = response */
2129 ipp_tag_t group_tag
; /* Current group */
2130 ipp_attribute_t
*attr
; /* Current attribute */
2131 char buffer
[2048]; /* String buffer for value */
2132 int major
, minor
; /* Version */
2138 fprintf(stderr
, "%s:\n", title
);
2139 major
= ippGetVersion(ipp
, &minor
);
2140 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2142 fprintf(stderr
, " operation-id=%s(%04x)\n",
2143 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2145 fprintf(stderr
, " status-code=%s(%04x)\n",
2146 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2147 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2149 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2151 attr
= ippNextAttribute(ipp
))
2153 if (ippGetGroupTag(attr
) != group_tag
)
2155 group_tag
= ippGetGroupTag(attr
);
2156 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2159 if (ippGetName(attr
))
2161 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2162 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2163 ippGetCount(attr
) > 1 ? "1setOf " : "",
2164 ippTagString(ippGetValueTag(attr
)), buffer
);
2171 * 'delete_client()' - Close the socket and free all memory used by a client
2176 delete_client(_ipp_client_t
*client
) /* I - Client */
2179 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2182 * Flush pending writes before closing...
2185 httpFlushWrite(client
->http
);
2191 httpClose(client
->http
);
2193 ippDelete(client
->request
);
2194 ippDelete(client
->response
);
2201 * 'delete_job()' - Remove from the printer and free all memory used by a job
2206 delete_job(_ipp_job_t
*job
) /* I - Job */
2209 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2211 ippDelete(job
->attrs
);
2216 unlink(job
->filename
);
2218 free(job
->filename
);
2226 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2227 * used by a printer object.
2231 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2233 if (printer
->ipv4
>= 0)
2234 close(printer
->ipv4
);
2236 if (printer
->ipv6
>= 0)
2237 close(printer
->ipv6
);
2240 if (printer
->printer_ref
)
2241 DNSServiceRefDeallocate(printer
->printer_ref
);
2242 if (printer
->ipp_ref
)
2243 DNSServiceRefDeallocate(printer
->ipp_ref
);
2244 if (printer
->ipps_ref
)
2245 DNSServiceRefDeallocate(printer
->ipps_ref
);
2246 if (printer
->http_ref
)
2247 DNSServiceRefDeallocate(printer
->http_ref
);
2248 #elif defined(HAVE_AVAHI)
2249 avahi_threaded_poll_lock(DNSSDMaster
);
2251 if (printer
->printer_ref
)
2252 avahi_entry_group_free(printer
->printer_ref
);
2253 if (printer
->ipp_ref
)
2254 avahi_entry_group_free(printer
->ipp_ref
);
2255 if (printer
->ipps_ref
)
2256 avahi_entry_group_free(printer
->ipps_ref
);
2257 if (printer
->http_ref
)
2258 avahi_entry_group_free(printer
->http_ref
);
2260 avahi_threaded_poll_unlock(DNSSDMaster
);
2261 #endif /* HAVE_DNSSD */
2263 if (printer
->dnssd_name
)
2264 free(printer
->dnssd_name
);
2266 free(printer
->name
);
2268 free(printer
->icon
);
2269 if (printer
->command
)
2270 free(printer
->command
);
2271 if (printer
->directory
)
2272 free(printer
->directory
);
2273 if (printer
->hostname
)
2274 free(printer
->hostname
);
2278 ippDelete(printer
->attrs
);
2279 cupsArrayDelete(printer
->jobs
);
2287 * 'dnssd_callback()' - Handle Bonjour registration events.
2292 DNSServiceRef sdRef
, /* I - Service reference */
2293 DNSServiceFlags flags
, /* I - Status flags */
2294 DNSServiceErrorType errorCode
, /* I - Error, if any */
2295 const char *name
, /* I - Service name */
2296 const char *regtype
, /* I - Service type */
2297 const char *domain
, /* I - Domain for service */
2298 _ipp_printer_t
*printer
) /* I - Printer */
2306 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2307 regtype
, (int)errorCode
);
2310 else if (strcasecmp(name
, printer
->dnssd_name
))
2313 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2315 /* No lock needed since only the main thread accesses/changes this */
2316 free(printer
->dnssd_name
);
2317 printer
->dnssd_name
= strdup(name
);
2322 #elif defined(HAVE_AVAHI)
2324 * 'dnssd_callback()' - Handle Bonjour registration events.
2329 AvahiEntryGroup
*srv
, /* I - Service */
2330 AvahiEntryGroupState state
, /* I - Registration state */
2331 void *context
) /* I - Printer */
2340 * 'dnssd_client_cb()' - Client callback for Avahi.
2342 * Called whenever the client or server state changes...
2347 AvahiClient
*c
, /* I - Client */
2348 AvahiClientState state
, /* I - Current state */
2349 void *userdata
) /* I - User data (unused) */
2359 fprintf(stderr
, "Ignore Avahi state %d.\n", state
);
2362 case AVAHI_CLIENT_FAILURE
:
2363 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
2365 fputs("Avahi server crashed, exiting.\n", stderr
);
2371 #endif /* HAVE_DNSSD */
2375 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2382 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2384 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2388 #elif defined(HAVE_AVAHI)
2389 int error
; /* Error code, if any */
2391 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2393 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2397 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2399 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2403 avahi_threaded_poll_start(DNSSDMaster
);
2404 #endif /* HAVE_DNSSD */
2409 * 'filter_cb()' - Filter printer attributes based on the requested array.
2412 static int /* O - 1 to copy, 0 to ignore */
2413 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2414 ipp_t
*dst
, /* I - Destination (unused) */
2415 ipp_attribute_t
*attr
) /* I - Source attribute */
2418 * Filter attributes as needed...
2421 #ifndef WIN32 /* Avoid MS compiler bug */
2425 ipp_tag_t group
= ippGetGroupTag(attr
);
2426 const char *name
= ippGetName(attr
);
2428 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
)))
2431 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2436 * 'find_job()' - Find a job specified in a request.
2439 static _ipp_job_t
* /* O - Job or NULL */
2440 find_job(_ipp_client_t
*client
) /* I - Client */
2442 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2443 _ipp_job_t key
, /* Job search key */
2444 *job
; /* Matching job, if any */
2447 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2449 const char *uri
= ippGetString(attr
, 0, NULL
);
2451 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2452 uri
[client
->printer
->urilen
] == '/')
2453 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2457 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2458 key
.id
= ippGetInteger(attr
, 0);
2460 _cupsRWLockRead(&(client
->printer
->rwlock
));
2461 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2462 _cupsRWUnlock(&(client
->printer
->rwlock
));
2469 * 'html_escape()' - Write a HTML-safe string.
2473 html_escape(_ipp_client_t
*client
, /* I - Client */
2474 const char *s
, /* I - String to write */
2475 size_t slen
) /* I - Number of characters to write */
2477 const char *start
, /* Start of segment */
2478 *end
; /* End of string */
2482 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2484 while (*s
&& s
< end
)
2486 if (*s
== '&' || *s
== '<')
2489 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2492 httpWrite2(client
->http
, "&", 5);
2494 httpWrite2(client
->http
, "<", 4);
2503 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2508 * 'html_footer()' - Show the web interface footer.
2510 * This function also writes the trailing 0-length chunk.
2514 html_footer(_ipp_client_t
*client
) /* I - Client */
2520 httpWrite2(client
->http
, "", 0);
2525 * 'html_header()' - Show the web interface header and title.
2529 html_header(_ipp_client_t
*client
, /* I - Client */
2530 const char *title
) /* I - Title */
2536 "<title>%s</title>\n"
2537 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2538 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2539 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2540 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2542 "body { font-family: sans-serif; margin: 0; }\n"
2543 "div.body { padding: 0px 10px 10px; }\n"
2544 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2545 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2546 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2547 "table.form th { text-align: right; }\n"
2548 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2549 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2550 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2551 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2552 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2553 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2554 "table.nav td { margin: 0; text-align: center; }\n"
2555 "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"
2556 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2557 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2558 "td.nav:hover { background: #666; color: #fff; }\n"
2559 "td.nav:active { background: #000; color: #ff0; }\n"
2563 "<table class=\"nav\"><tr>"
2564 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2565 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2566 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2568 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2573 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2577 html_printf(_ipp_client_t
*client
, /* I - Client */
2578 const char *format
, /* I - Printf-style format string */
2579 ...) /* I - Additional arguments as needed */
2581 va_list ap
; /* Pointer to arguments */
2582 const char *start
; /* Start of string */
2583 char size
, /* Size character (h, l, L) */
2584 type
; /* Format type character */
2585 int width
, /* Width of field */
2586 prec
; /* Number of characters of precision */
2587 char tformat
[100], /* Temporary format string for sprintf() */
2588 *tptr
, /* Pointer into temporary format */
2589 temp
[1024]; /* Buffer for formatted numbers */
2590 char *s
; /* Pointer to string */
2594 * Loop through the format string, formatting as needed...
2597 va_start(ap
, format
);
2605 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2608 *tptr
++ = *format
++;
2612 httpWrite2(client
->http
, "%", 1);
2617 else if (strchr(" -+#\'", *format
))
2618 *tptr
++ = *format
++;
2623 * Get width from argument...
2627 width
= va_arg(ap
, int);
2629 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2630 tptr
+= strlen(tptr
);
2636 while (isdigit(*format
& 255))
2638 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2641 width
= width
* 10 + *format
++ - '0';
2647 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2655 * Get precision from argument...
2659 prec
= va_arg(ap
, int);
2661 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2662 tptr
+= strlen(tptr
);
2668 while (isdigit(*format
& 255))
2670 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2673 prec
= prec
* 10 + *format
++ - '0';
2678 if (*format
== 'l' && format
[1] == 'l')
2682 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2690 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2692 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2707 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2716 case 'E' : /* Floating point formats */
2721 if ((size_t)(width
+ 2) > sizeof(temp
))
2724 sprintf(temp
, tformat
, va_arg(ap
, double));
2726 httpWrite2(client
->http
, temp
, strlen(temp
));
2729 case 'B' : /* Integer formats */
2737 if ((size_t)(width
+ 2) > sizeof(temp
))
2740 # ifdef HAVE_LONG_LONG
2742 sprintf(temp
, tformat
, va_arg(ap
, long long));
2744 # endif /* HAVE_LONG_LONG */
2746 sprintf(temp
, tformat
, va_arg(ap
, long));
2748 sprintf(temp
, tformat
, va_arg(ap
, int));
2750 httpWrite2(client
->http
, temp
, strlen(temp
));
2753 case 'p' : /* Pointer value */
2754 if ((size_t)(width
+ 2) > sizeof(temp
))
2757 sprintf(temp
, tformat
, va_arg(ap
, void *));
2759 httpWrite2(client
->http
, temp
, strlen(temp
));
2762 case 'c' : /* Character or character array */
2765 temp
[0] = (char)va_arg(ap
, int);
2767 html_escape(client
, temp
, 1);
2770 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2773 case 's' : /* String */
2774 if ((s
= va_arg(ap
, char *)) == NULL
)
2777 html_escape(client
, s
, strlen(s
));
2786 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2793 * 'ipp_cancel_job()' - Cancel a job.
2797 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2799 _ipp_job_t
*job
; /* Job information */
2806 if ((job
= find_job(client
)) == NULL
)
2808 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2813 * See if the job is already completed, canceled, or aborted; if so,
2814 * we can't cancel...
2819 case IPP_JSTATE_CANCELED
:
2820 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2821 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2824 case IPP_JSTATE_ABORTED
:
2825 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2826 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2829 case IPP_JSTATE_COMPLETED
:
2830 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2831 "Job #%d is already completed - can\'t cancel.", job
->id
);
2839 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2841 if (job
->state
== IPP_JSTATE_PROCESSING
||
2842 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2846 job
->state
= IPP_JSTATE_CANCELED
;
2847 job
->completed
= time(NULL
);
2850 _cupsRWUnlock(&(client
->printer
->rwlock
));
2852 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2859 * 'ipp_close_job()' - Close an open job.
2863 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
2865 _ipp_job_t
*job
; /* Job information */
2872 if ((job
= find_job(client
)) == NULL
)
2874 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2879 * See if the job is already completed, canceled, or aborted; if so,
2880 * we can't cancel...
2885 case IPP_JSTATE_CANCELED
:
2886 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2887 "Job #%d is canceled - can\'t close.", job
->id
);
2890 case IPP_JSTATE_ABORTED
:
2891 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2892 "Job #%d is aborted - can\'t close.", job
->id
);
2895 case IPP_JSTATE_COMPLETED
:
2896 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2897 "Job #%d is completed - can\'t close.", job
->id
);
2900 case IPP_JSTATE_PROCESSING
:
2901 case IPP_JSTATE_STOPPED
:
2902 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2903 "Job #%d is already closed.", job
->id
);
2907 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2914 * 'ipp_create_job()' - Create a job object.
2918 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2920 _ipp_job_t
*job
; /* New job */
2921 cups_array_t
*ra
; /* Attributes to send in response */
2925 * Validate print job attributes...
2928 if (!valid_job_attributes(client
))
2930 httpFlush(client
->http
);
2935 * Do we have a file to print?
2938 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2940 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2941 "Unexpected document data following request.");
2949 if ((job
= create_job(client
)) == NULL
)
2951 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2952 "Currently printing another job.");
2957 * Return the job info...
2960 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2962 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2963 cupsArrayAdd(ra
, "job-id");
2964 cupsArrayAdd(ra
, "job-state");
2965 cupsArrayAdd(ra
, "job-state-message");
2966 cupsArrayAdd(ra
, "job-state-reasons");
2967 cupsArrayAdd(ra
, "job-uri");
2969 copy_job_attributes(client
, job
, ra
);
2970 cupsArrayDelete(ra
);
2975 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2979 ipp_get_job_attributes(
2980 _ipp_client_t
*client
) /* I - Client */
2982 _ipp_job_t
*job
; /* Job */
2983 cups_array_t
*ra
; /* requested-attributes */
2986 if ((job
= find_job(client
)) == NULL
)
2988 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2992 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2994 ra
= ippCreateRequestedArray(client
->request
);
2995 copy_job_attributes(client
, job
, ra
);
2996 cupsArrayDelete(ra
);
3001 * 'ipp_get_jobs()' - Get a list of job objects.
3005 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
3007 ipp_attribute_t
*attr
; /* Current attribute */
3008 const char *which_jobs
= NULL
;
3009 /* which-jobs values */
3010 int job_comparison
; /* Job comparison */
3011 ipp_jstate_t job_state
; /* job-state value */
3012 int first_job_id
, /* First job ID */
3013 limit
, /* Maximum number of jobs to return */
3014 count
; /* Number of jobs that match */
3015 const char *username
; /* Username */
3016 _ipp_job_t
*job
; /* Current job pointer */
3017 cups_array_t
*ra
; /* Requested attributes array */
3021 * See if the "which-jobs" attribute have been specified...
3024 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3025 IPP_TAG_KEYWORD
)) != NULL
)
3027 which_jobs
= ippGetString(attr
, 0, NULL
);
3028 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3031 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3033 job_comparison
= -1;
3034 job_state
= IPP_JSTATE_STOPPED
;
3036 else if (!strcmp(which_jobs
, "completed"))
3039 job_state
= IPP_JSTATE_CANCELED
;
3041 else if (!strcmp(which_jobs
, "aborted"))
3044 job_state
= IPP_JSTATE_ABORTED
;
3046 else if (!strcmp(which_jobs
, "all"))
3049 job_state
= IPP_JSTATE_PENDING
;
3051 else if (!strcmp(which_jobs
, "canceled"))
3054 job_state
= IPP_JSTATE_CANCELED
;
3056 else if (!strcmp(which_jobs
, "pending"))
3059 job_state
= IPP_JSTATE_PENDING
;
3061 else if (!strcmp(which_jobs
, "pending-held"))
3064 job_state
= IPP_JSTATE_HELD
;
3066 else if (!strcmp(which_jobs
, "processing"))
3069 job_state
= IPP_JSTATE_PROCESSING
;
3071 else if (!strcmp(which_jobs
, "processing-stopped"))
3074 job_state
= IPP_JSTATE_STOPPED
;
3078 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3079 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3080 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3081 "which-jobs", NULL
, which_jobs
);
3086 * See if they want to limit the number of jobs reported...
3089 if ((attr
= ippFindAttribute(client
->request
, "limit",
3090 IPP_TAG_INTEGER
)) != NULL
)
3092 limit
= ippGetInteger(attr
, 0);
3094 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3099 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3100 IPP_TAG_INTEGER
)) != NULL
)
3102 first_job_id
= ippGetInteger(attr
, 0);
3104 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3111 * See if we only want to see jobs for a specific user...
3116 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3117 IPP_TAG_BOOLEAN
)) != NULL
)
3119 int my_jobs
= ippGetBoolean(attr
, 0);
3121 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3122 my_jobs
? "true" : "false");
3126 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3127 IPP_TAG_NAME
)) == NULL
)
3129 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3130 "Need requesting-user-name with my-jobs.");
3134 username
= ippGetString(attr
, 0, NULL
);
3136 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3137 client
->hostname
, username
);
3142 * OK, build a list of jobs for this printer...
3145 ra
= ippCreateRequestedArray(client
->request
);
3147 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3149 _cupsRWLockRead(&(client
->printer
->rwlock
));
3151 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3152 (limit
<= 0 || count
< limit
) && job
;
3153 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3156 * Filter out jobs that don't match...
3159 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3160 (job_comparison
== 0 && job
->state
!= job_state
) ||
3161 (job_comparison
> 0 && job
->state
< job_state
) ||
3162 job
->id
< first_job_id
||
3163 (username
&& job
->username
&&
3164 strcasecmp(username
, job
->username
)))
3168 ippAddSeparator(client
->response
);
3171 copy_job_attributes(client
, job
, ra
);
3174 cupsArrayDelete(ra
);
3176 _cupsRWUnlock(&(client
->printer
->rwlock
));
3181 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3185 ipp_get_printer_attributes(
3186 _ipp_client_t
*client
) /* I - Client */
3188 cups_array_t
*ra
; /* Requested attributes array */
3189 _ipp_printer_t
*printer
; /* Printer */
3193 * Send the attributes...
3196 ra
= ippCreateRequestedArray(client
->request
);
3197 printer
= client
->printer
;
3199 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3201 _cupsRWLockRead(&(printer
->rwlock
));
3203 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3204 IPP_TAG_CUPS_CONST
);
3206 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3208 int i
, /* Looping var */
3209 num_ready
= 0; /* Number of ready media */
3210 ipp_t
*ready
[3]; /* Ready media */
3212 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3214 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
3215 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);
3217 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);
3219 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3220 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);
3221 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3223 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
3224 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);
3226 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);
3231 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3232 for (i
= 0; i
< num_ready
; i
++)
3233 ippDelete(ready
[i
]);
3236 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3239 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3241 int num_ready
= 0; /* Number of ready media */
3242 const char *ready
[3]; /* Ready media */
3244 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3245 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3247 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3248 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3250 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3251 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3254 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3256 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3259 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3260 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3262 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3263 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3265 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3266 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3269 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3270 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3271 "printer-state", printer
->state
);
3273 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3274 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3276 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3277 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3279 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3281 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3283 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3286 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3288 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3289 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3290 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3291 "printer-state-reasons", NULL
, "none");
3294 int num_reasons
= 0;/* Number of reasons */
3295 const char *reasons
[32]; /* Reason strings */
3297 if (printer
->state_reasons
& _IPP_PREASON_OTHER
)
3298 reasons
[num_reasons
++] = "other";
3299 if (printer
->state_reasons
& _IPP_PREASON_COVER_OPEN
)
3300 reasons
[num_reasons
++] = "cover-open";
3301 if (printer
->state_reasons
& _IPP_PREASON_INPUT_TRAY_MISSING
)
3302 reasons
[num_reasons
++] = "input-tray-missing";
3303 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_EMPTY
)
3304 reasons
[num_reasons
++] = "marker-supply-empty-warning";
3305 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_LOW
)
3306 reasons
[num_reasons
++] = "marker-supply-low-report";
3307 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
)
3308 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
3309 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_FULL
)
3310 reasons
[num_reasons
++] = "marker-waste-full-warning";
3311 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
3312 reasons
[num_reasons
++] = "media-empty-warning";
3313 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_JAM
)
3314 reasons
[num_reasons
++] = "media-jam-warning";
3315 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_LOW
)
3316 reasons
[num_reasons
++] = "media-low-report";
3317 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_NEEDED
)
3318 reasons
[num_reasons
++] = "media-needed-report";
3319 if (printer
->state_reasons
& _IPP_PREASON_MOVING_TO_PAUSED
)
3320 reasons
[num_reasons
++] = "moving-to-paused";
3321 if (printer
->state_reasons
& _IPP_PREASON_PAUSED
)
3322 reasons
[num_reasons
++] = "paused";
3323 if (printer
->state_reasons
& _IPP_PREASON_SPOOL_AREA_FULL
)
3324 reasons
[num_reasons
++] = "spool-area-full";
3325 if (printer
->state_reasons
& _IPP_PREASON_TONER_EMPTY
)
3326 reasons
[num_reasons
++] = "toner-empty-warning";
3327 if (printer
->state_reasons
& _IPP_PREASON_TONER_LOW
)
3328 reasons
[num_reasons
++] = "toner-low-report";
3330 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
3331 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3332 "printer-state-reasons", num_reasons
, NULL
, reasons
);
3336 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3338 int i
; /* Looping var */
3339 char buffer
[256]; /* Supply value buffer */
3340 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3341 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3343 for (i
= 0; i
< 5; i
++)
3345 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
]);
3348 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3350 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3354 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3355 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3357 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3358 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3359 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3361 _cupsRWUnlock(&(printer
->rwlock
));
3363 cupsArrayDelete(ra
);
3368 * 'ipp_identify_printer()' - Beep or display a message.
3372 ipp_identify_printer(
3373 _ipp_client_t
*client
) /* I - Client */
3375 /* TODO: Do something */
3377 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3382 * 'ipp_print_job()' - Create a job object with an attached document.
3386 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3388 _ipp_job_t
*job
; /* New job */
3389 char filename
[1024], /* Filename buffer */
3390 buffer
[4096]; /* Copy buffer */
3391 ssize_t bytes
; /* Bytes read */
3392 cups_array_t
*ra
; /* Attributes to send in response */
3396 * Validate print job attributes...
3399 if (!valid_job_attributes(client
))
3401 httpFlush(client
->http
);
3406 * Do we have a file to print?
3409 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3411 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3419 if ((job
= create_job(client
)) == NULL
)
3421 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3422 "Currently printing another job.");
3427 * Create a file for the request data...
3430 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3433 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3435 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3437 job
->state
= IPP_JSTATE_ABORTED
;
3439 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3440 "Unable to create print file: %s", strerror(errno
));
3444 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3446 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3448 int error
= errno
; /* Write error */
3450 job
->state
= IPP_JSTATE_ABORTED
;
3457 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3458 "Unable to write print file: %s", strerror(error
));
3466 * Got an error while reading the print data, so abort this job.
3469 job
->state
= IPP_JSTATE_ABORTED
;
3476 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3477 "Unable to read print file.");
3483 int error
= errno
; /* Write error */
3485 job
->state
= IPP_JSTATE_ABORTED
;
3490 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3491 "Unable to write print file: %s", strerror(error
));
3496 job
->filename
= strdup(filename
);
3497 job
->state
= IPP_JSTATE_PENDING
;
3500 * Process the job...
3503 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3505 job
->state
= IPP_JSTATE_ABORTED
;
3506 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3511 * Return the job info...
3514 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3516 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3517 cupsArrayAdd(ra
, "job-id");
3518 cupsArrayAdd(ra
, "job-state");
3519 cupsArrayAdd(ra
, "job-state-message");
3520 cupsArrayAdd(ra
, "job-state-reasons");
3521 cupsArrayAdd(ra
, "job-uri");
3523 copy_job_attributes(client
, job
, ra
);
3524 cupsArrayDelete(ra
);
3529 * 'ipp_print_uri()' - Create a job object with a referenced document.
3533 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3535 _ipp_job_t
*job
; /* New job */
3536 ipp_attribute_t
*uri
; /* document-uri */
3537 char scheme
[256], /* URI scheme */
3538 userpass
[256], /* Username and password info */
3539 hostname
[256], /* Hostname */
3540 resource
[1024]; /* Resource path */
3541 int port
; /* Port number */
3542 http_uri_status_t uri_status
; /* URI decode status */
3543 http_encryption_t encryption
; /* Encryption to use, if any */
3544 http_t
*http
; /* Connection for http/https URIs */
3545 http_status_t status
; /* Access status for http/https URIs */
3546 int infile
; /* Input file for local file URIs */
3547 char filename
[1024], /* Filename buffer */
3548 buffer
[4096]; /* Copy buffer */
3549 ssize_t bytes
; /* Bytes read */
3550 cups_array_t
*ra
; /* Attributes to send in response */
3551 static const char * const uri_status_strings
[] =
3552 { /* URI decode errors */
3554 "Bad arguments to function.",
3555 "Bad resource in URI.",
3556 "Bad port number in URI.",
3557 "Bad hostname in URI.",
3558 "Bad username in URI.",
3559 "Bad scheme in URI.",
3565 * Validate print job attributes...
3568 if (!valid_job_attributes(client
))
3570 httpFlush(client
->http
);
3575 * Do we have a file to print?
3578 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3580 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3581 "Unexpected document data following request.");
3586 * Do we have a document URI?
3589 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3590 IPP_TAG_URI
)) == NULL
)
3592 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3596 if (ippGetCount(uri
) != 1)
3598 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3599 "Too many document-uri values.");
3603 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3604 scheme
, sizeof(scheme
), userpass
,
3605 sizeof(userpass
), hostname
, sizeof(hostname
),
3606 &port
, resource
, sizeof(resource
));
3607 if (uri_status
< HTTP_URI_STATUS_OK
)
3609 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3610 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3614 if (strcmp(scheme
, "file") &&
3616 strcmp(scheme
, "https") &&
3617 #endif /* HAVE_SSL */
3618 strcmp(scheme
, "http"))
3620 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3621 "URI scheme \"%s\" not supported.", scheme
);
3625 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3627 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3628 "Unable to access URI: %s", strerror(errno
));
3636 if ((job
= create_job(client
)) == NULL
)
3638 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3639 "Currently printing another job.");
3644 * Create a file for the request data...
3647 if (!strcasecmp(job
->format
, "image/jpeg"))
3648 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3649 client
->printer
->directory
, job
->id
);
3650 else if (!strcasecmp(job
->format
, "image/png"))
3651 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3652 client
->printer
->directory
, job
->id
);
3653 else if (!strcasecmp(job
->format
, "application/pdf"))
3654 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3655 client
->printer
->directory
, job
->id
);
3656 else if (!strcasecmp(job
->format
, "application/postscript"))
3657 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3658 client
->printer
->directory
, job
->id
);
3660 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3661 client
->printer
->directory
, job
->id
);
3663 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3665 job
->state
= IPP_JSTATE_ABORTED
;
3667 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3668 "Unable to create print file: %s", strerror(errno
));
3672 if (!strcmp(scheme
, "file"))
3674 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3676 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3677 "Unable to access URI: %s", strerror(errno
));
3683 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3684 (errno
== EAGAIN
|| errno
== EINTR
))
3686 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3688 int error
= errno
; /* Write error */
3690 job
->state
= IPP_JSTATE_ABORTED
;
3698 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3699 "Unable to write print file: %s", strerror(error
));
3710 if (port
== 443 || !strcmp(scheme
, "https"))
3711 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3713 #endif /* HAVE_SSL */
3714 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3716 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3717 1, 30000, NULL
)) == NULL
)
3719 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3720 "Unable to connect to %s: %s", hostname
,
3721 cupsLastErrorString());
3722 job
->state
= IPP_JSTATE_ABORTED
;
3731 httpClearFields(http
);
3732 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3733 if (httpGet(http
, resource
))
3735 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3736 "Unable to GET URI: %s", strerror(errno
));
3738 job
->state
= IPP_JSTATE_ABORTED
;
3748 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3750 if (status
!= HTTP_STATUS_OK
)
3752 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3753 "Unable to GET URI: %s", httpStatus(status
));
3755 job
->state
= IPP_JSTATE_ABORTED
;
3765 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3767 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3769 int error
= errno
; /* Write error */
3771 job
->state
= IPP_JSTATE_ABORTED
;
3779 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3780 "Unable to write print file: %s", strerror(error
));
3790 int error
= errno
; /* Write error */
3792 job
->state
= IPP_JSTATE_ABORTED
;
3797 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3798 "Unable to write print file: %s", strerror(error
));
3803 job
->filename
= strdup(filename
);
3804 job
->state
= IPP_JSTATE_PENDING
;
3807 * Process the job...
3811 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3813 job
->state
= IPP_JSTATE_ABORTED
;
3814 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3823 * Return the job info...
3826 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3828 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3829 cupsArrayAdd(ra
, "job-id");
3830 cupsArrayAdd(ra
, "job-state");
3831 cupsArrayAdd(ra
, "job-state-reasons");
3832 cupsArrayAdd(ra
, "job-uri");
3834 copy_job_attributes(client
, job
, ra
);
3835 cupsArrayDelete(ra
);
3840 * 'ipp_send_document()' - Add an attached document to a job object created with
3845 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3847 _ipp_job_t
*job
; /* Job information */
3848 char filename
[1024], /* Filename buffer */
3849 buffer
[4096]; /* Copy buffer */
3850 ssize_t bytes
; /* Bytes read */
3851 ipp_attribute_t
*attr
; /* Current attribute */
3852 cups_array_t
*ra
; /* Attributes to send in response */
3859 if ((job
= find_job(client
)) == NULL
)
3861 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3862 httpFlush(client
->http
);
3867 * See if we already have a document for this job or the job has already
3868 * in a non-pending state...
3871 if (job
->state
> IPP_JSTATE_HELD
)
3873 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3874 "Job is not in a pending state.");
3875 httpFlush(client
->http
);
3878 else if (job
->filename
|| job
->fd
>= 0)
3880 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3881 "Multiple document jobs are not supported.");
3882 httpFlush(client
->http
);
3886 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3887 IPP_TAG_ZERO
)) == NULL
)
3889 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3890 "Missing required last-document attribute.");
3891 httpFlush(client
->http
);
3894 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3895 !ippGetBoolean(attr
, 0))
3897 respond_unsupported(client
, attr
);
3898 httpFlush(client
->http
);
3903 * Validate document attributes...
3906 if (!valid_doc_attributes(client
))
3908 httpFlush(client
->http
);
3912 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3915 * Get the document format for the job...
3918 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3920 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3921 job
->format
= ippGetString(attr
, 0, NULL
);
3922 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3923 job
->format
= ippGetString(attr
, 0, NULL
);
3925 job
->format
= "application/octet-stream";
3928 * Create a file for the request data...
3931 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3934 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3936 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3938 _cupsRWUnlock(&(client
->printer
->rwlock
));
3942 job
->state
= IPP_JSTATE_ABORTED
;
3944 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3945 "Unable to create print file: %s", strerror(errno
));
3949 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3951 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3953 int error
= errno
; /* Write error */
3955 job
->state
= IPP_JSTATE_ABORTED
;
3962 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3963 "Unable to write print file: %s", strerror(error
));
3971 * Got an error while reading the print data, so abort this job.
3974 job
->state
= IPP_JSTATE_ABORTED
;
3981 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3982 "Unable to read print file.");
3988 int error
= errno
; /* Write error */
3990 job
->state
= IPP_JSTATE_ABORTED
;
3995 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3996 "Unable to write print file: %s", strerror(error
));
4000 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4003 job
->filename
= strdup(filename
);
4004 job
->state
= IPP_JSTATE_PENDING
;
4006 _cupsRWUnlock(&(client
->printer
->rwlock
));
4009 * Process the job...
4013 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4015 job
->state
= IPP_JSTATE_ABORTED
;
4016 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4025 * Return the job info...
4028 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4030 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4031 cupsArrayAdd(ra
, "job-id");
4032 cupsArrayAdd(ra
, "job-state");
4033 cupsArrayAdd(ra
, "job-state-reasons");
4034 cupsArrayAdd(ra
, "job-uri");
4036 copy_job_attributes(client
, job
, ra
);
4037 cupsArrayDelete(ra
);
4042 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4047 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
4049 _ipp_job_t
*job
; /* Job information */
4050 ipp_attribute_t
*uri
; /* document-uri */
4051 char scheme
[256], /* URI scheme */
4052 userpass
[256], /* Username and password info */
4053 hostname
[256], /* Hostname */
4054 resource
[1024]; /* Resource path */
4055 int port
; /* Port number */
4056 http_uri_status_t uri_status
; /* URI decode status */
4057 http_encryption_t encryption
; /* Encryption to use, if any */
4058 http_t
*http
; /* Connection for http/https URIs */
4059 http_status_t status
; /* Access status for http/https URIs */
4060 int infile
; /* Input file for local file URIs */
4061 char filename
[1024], /* Filename buffer */
4062 buffer
[4096]; /* Copy buffer */
4063 ssize_t bytes
; /* Bytes read */
4064 ipp_attribute_t
*attr
; /* Current attribute */
4065 cups_array_t
*ra
; /* Attributes to send in response */
4066 static const char * const uri_status_strings
[] =
4067 { /* URI decode errors */
4069 "Bad arguments to function.",
4070 "Bad resource in URI.",
4071 "Bad port number in URI.",
4072 "Bad hostname in URI.",
4073 "Bad username in URI.",
4074 "Bad scheme in URI.",
4083 if ((job
= find_job(client
)) == NULL
)
4085 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4086 httpFlush(client
->http
);
4091 * See if we already have a document for this job or the job has already
4092 * in a non-pending state...
4095 if (job
->state
> IPP_JSTATE_HELD
)
4097 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4098 "Job is not in a pending state.");
4099 httpFlush(client
->http
);
4102 else if (job
->filename
|| job
->fd
>= 0)
4104 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4105 "Multiple document jobs are not supported.");
4106 httpFlush(client
->http
);
4110 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4111 IPP_TAG_ZERO
)) == NULL
)
4113 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4114 "Missing required last-document attribute.");
4115 httpFlush(client
->http
);
4118 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4119 !ippGetBoolean(attr
, 0))
4121 respond_unsupported(client
, attr
);
4122 httpFlush(client
->http
);
4127 * Validate document attributes...
4130 if (!valid_doc_attributes(client
))
4132 httpFlush(client
->http
);
4137 * Do we have a file to print?
4140 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4142 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4143 "Unexpected document data following request.");
4148 * Do we have a document URI?
4151 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4152 IPP_TAG_URI
)) == NULL
)
4154 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4158 if (ippGetCount(uri
) != 1)
4160 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4161 "Too many document-uri values.");
4165 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4166 scheme
, sizeof(scheme
), userpass
,
4167 sizeof(userpass
), hostname
, sizeof(hostname
),
4168 &port
, resource
, sizeof(resource
));
4169 if (uri_status
< HTTP_URI_STATUS_OK
)
4171 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4172 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4176 if (strcmp(scheme
, "file") &&
4178 strcmp(scheme
, "https") &&
4179 #endif /* HAVE_SSL */
4180 strcmp(scheme
, "http"))
4182 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4183 "URI scheme \"%s\" not supported.", scheme
);
4187 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4189 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4190 "Unable to access URI: %s", strerror(errno
));
4195 * Get the document format for the job...
4198 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4200 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4201 IPP_TAG_MIMETYPE
)) != NULL
)
4202 job
->format
= ippGetString(attr
, 0, NULL
);
4204 job
->format
= "application/octet-stream";
4207 * Create a file for the request data...
4210 if (!strcasecmp(job
->format
, "image/jpeg"))
4211 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4212 client
->printer
->directory
, job
->id
);
4213 else if (!strcasecmp(job
->format
, "image/png"))
4214 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4215 client
->printer
->directory
, job
->id
);
4216 else if (!strcasecmp(job
->format
, "application/pdf"))
4217 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4218 client
->printer
->directory
, job
->id
);
4219 else if (!strcasecmp(job
->format
, "application/postscript"))
4220 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4221 client
->printer
->directory
, job
->id
);
4223 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4224 client
->printer
->directory
, job
->id
);
4226 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4228 _cupsRWUnlock(&(client
->printer
->rwlock
));
4232 job
->state
= IPP_JSTATE_ABORTED
;
4234 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4235 "Unable to create print file: %s", strerror(errno
));
4239 if (!strcmp(scheme
, "file"))
4241 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4243 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4244 "Unable to access URI: %s", strerror(errno
));
4250 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4251 (errno
== EAGAIN
|| errno
== EINTR
))
4253 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4255 int error
= errno
; /* Write error */
4257 job
->state
= IPP_JSTATE_ABORTED
;
4265 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4266 "Unable to write print file: %s", strerror(error
));
4277 if (port
== 443 || !strcmp(scheme
, "https"))
4278 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4280 #endif /* HAVE_SSL */
4281 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4283 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4284 1, 30000, NULL
)) == NULL
)
4286 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4287 "Unable to connect to %s: %s", hostname
,
4288 cupsLastErrorString());
4289 job
->state
= IPP_JSTATE_ABORTED
;
4298 httpClearFields(http
);
4299 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4300 if (httpGet(http
, resource
))
4302 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4303 "Unable to GET URI: %s", strerror(errno
));
4305 job
->state
= IPP_JSTATE_ABORTED
;
4315 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4317 if (status
!= HTTP_STATUS_OK
)
4319 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4320 "Unable to GET URI: %s", httpStatus(status
));
4322 job
->state
= IPP_JSTATE_ABORTED
;
4332 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4334 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4336 int error
= errno
; /* Write error */
4338 job
->state
= IPP_JSTATE_ABORTED
;
4346 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4347 "Unable to write print file: %s", strerror(error
));
4357 int error
= errno
; /* Write error */
4359 job
->state
= IPP_JSTATE_ABORTED
;
4364 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4365 "Unable to write print file: %s", strerror(error
));
4369 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4372 job
->filename
= strdup(filename
);
4373 job
->state
= IPP_JSTATE_PENDING
;
4375 _cupsRWUnlock(&(client
->printer
->rwlock
));
4378 * Process the job...
4382 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4384 job
->state
= IPP_JSTATE_ABORTED
;
4385 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4394 * Return the job info...
4397 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4399 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4400 cupsArrayAdd(ra
, "job-id");
4401 cupsArrayAdd(ra
, "job-state");
4402 cupsArrayAdd(ra
, "job-state-reasons");
4403 cupsArrayAdd(ra
, "job-uri");
4405 copy_job_attributes(client
, job
, ra
);
4406 cupsArrayDelete(ra
);
4411 * 'ipp_validate_job()' - Validate job creation attributes.
4415 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4417 if (valid_job_attributes(client
))
4418 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4423 * 'parse_options()' - Parse URL options into CUPS options.
4425 * The client->options string is destroyed by this function.
4428 static int /* O - Number of options */
4429 parse_options(_ipp_client_t
*client
, /* I - Client */
4430 cups_option_t
**options
) /* O - Options */
4432 char *name
, /* Name */
4434 *next
; /* Next name=value pair */
4435 int num_options
= 0; /* Number of options */
4440 for (name
= client
->options
; name
&& *name
; name
= next
)
4442 if ((value
= strchr(name
, '=')) == NULL
)
4446 if ((next
= strchr(value
, '&')) != NULL
)
4449 num_options
= cupsAddOption(name
, value
, num_options
, options
);
4452 return (num_options
);
4457 * 'process_client()' - Process client requests on a thread.
4460 static void * /* O - Exit status */
4461 process_client(_ipp_client_t
*client
) /* I - Client */
4464 * Loop until we are out of requests or timeout (30 seconds)...
4468 int first_time
= 1; /* First time request? */
4469 #endif /* HAVE_SSL */
4471 while (httpWait(client
->http
, 30000))
4477 * See if we need to negotiate a TLS connection...
4480 char buf
[1]; /* First byte from client */
4482 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
4484 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
4486 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
4488 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4492 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4497 #endif /* HAVE_SSL */
4499 if (!process_http(client
))
4504 * Close the conection to the client and return...
4507 delete_client(client
);
4514 * 'process_http()' - Process a HTTP request.
4517 int /* O - 1 on success, 0 on failure */
4518 process_http(_ipp_client_t
*client
) /* I - Client connection */
4520 char uri
[1024]; /* URI */
4521 http_state_t http_state
; /* HTTP state */
4522 http_status_t http_status
; /* HTTP status */
4523 ipp_state_t ipp_state
; /* State of IPP transfer */
4524 char scheme
[32], /* Method/scheme */
4525 userpass
[128], /* Username:password */
4526 hostname
[HTTP_MAX_HOST
];
4528 int port
; /* Port number */
4529 const char *encoding
; /* Content-Encoding value */
4530 static const char * const http_states
[] =
4531 { /* Strings for logging HTTP method */
4552 * Clear state variables...
4555 ippDelete(client
->request
);
4556 ippDelete(client
->response
);
4558 client
->request
= NULL
;
4559 client
->response
= NULL
;
4560 client
->operation
= HTTP_STATE_WAITING
;
4563 * Read a request from the connection...
4566 while ((http_state
= httpReadRequest(client
->http
, uri
,
4567 sizeof(uri
))) == HTTP_STATE_WAITING
)
4571 * Parse the request line...
4574 if (http_state
== HTTP_STATE_ERROR
)
4576 if (httpError(client
->http
) == EPIPE
)
4577 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
4579 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
4580 strerror(httpError(client
->http
)));
4584 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
4586 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
4587 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4590 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
4592 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
4593 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4597 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
4601 * Separate the URI into its components...
4604 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4605 userpass
, sizeof(userpass
),
4606 hostname
, sizeof(hostname
), &port
,
4607 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
4608 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
4610 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
4611 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4615 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
4616 *(client
->options
)++ = '\0';
4619 * Process the request...
4622 client
->start
= time(NULL
);
4623 client
->operation
= httpGetState(client
->http
);
4626 * Parse incoming parameters until the status changes...
4629 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4631 if (http_status
!= HTTP_STATUS_OK
)
4633 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4637 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4638 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4641 * HTTP/1.1 and higher require the "Host:" field...
4644 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4649 * Handle HTTP Upgrade...
4652 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4656 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
4658 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
4661 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
4663 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
4665 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4669 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4672 #endif /* HAVE_SSL */
4674 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4679 * Handle HTTP Expect...
4682 if (httpGetExpect(client
->http
) &&
4683 (client
->operation
== HTTP_STATE_POST
||
4684 client
->operation
== HTTP_STATE_PUT
))
4686 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4689 * Send 100-continue header...
4692 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4698 * Send 417-expectation-failed header...
4701 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4707 * Handle new transfers...
4710 encoding
= httpGetContentEncoding(client
->http
);
4712 switch (client
->operation
)
4714 case HTTP_STATE_OPTIONS
:
4716 * Do OPTIONS command...
4719 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4721 case HTTP_STATE_HEAD
:
4722 if (!strcmp(client
->uri
, "/icon.png"))
4723 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4724 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
4725 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4727 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4729 case HTTP_STATE_GET
:
4730 if (!strcmp(client
->uri
, "/icon.png"))
4733 * Send PNG icon file.
4736 int fd
; /* Icon file */
4737 struct stat fileinfo
; /* Icon file information */
4738 char buffer
[4096]; /* Copy buffer */
4739 ssize_t bytes
; /* Bytes */
4741 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
4743 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4744 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4746 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
4747 (size_t)fileinfo
.st_size
))
4753 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4754 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
4756 httpFlushWrite(client
->http
);
4761 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4763 else if (!strcmp(client
->uri
, "/"))
4766 * Show web status page...
4769 _ipp_job_t
*job
; /* Current job */
4770 int i
; /* Looping var */
4771 _ipp_preason_t reason
; /* Current reason */
4772 static const char * const reasons
[] =
4773 { /* Reason strings */
4776 "Input Tray Missing",
4777 "Marker Supply Empty",
4778 "Marker Supply Low",
4779 "Marker Waste Almost Full",
4780 "Marker Waste Full",
4792 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4795 html_header(client
, client
->printer
->name
);
4797 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
4798 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
4799 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
4800 if (client
->printer
->state_reasons
& reason
)
4801 html_printf(client
, "\n<br> %s", reasons
[i
]);
4802 html_printf(client
, "</p>\n");
4804 if (cupsArrayCount(client
->printer
->jobs
) > 0)
4806 _cupsRWLockRead(&(client
->printer
->rwlock
));
4808 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");
4809 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
4811 char when
[256], /* When job queued/started/finished */
4812 hhmmss
[64]; /* Time HH:MM:SS */
4816 case IPP_JSTATE_PENDING
:
4817 case IPP_JSTATE_HELD
:
4818 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
4820 case IPP_JSTATE_PROCESSING
:
4821 case IPP_JSTATE_STOPPED
:
4822 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
4824 case IPP_JSTATE_ABORTED
:
4825 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4827 case IPP_JSTATE_CANCELED
:
4828 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4830 case IPP_JSTATE_COMPLETED
:
4831 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4835 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
);
4837 html_printf(client
, "</tbody></table>\n");
4839 _cupsRWUnlock(&(client
->printer
->rwlock
));
4841 html_footer(client
);
4845 else if (!strcmp(client
->uri
, "/media"))
4848 * Show web media page...
4851 int i
, /* Looping var */
4852 num_options
; /* Number of form options */
4853 cups_option_t
*options
; /* Form options */
4854 static const char * const sizes
[] =
4855 { /* Size strings */
4868 static const char * const types
[] =
4885 static const int sheets
[] = /* Number of sheets */
4894 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4897 html_header(client
, client
->printer
->name
);
4899 if ((num_options
= parse_options(client
, &options
)) > 0)
4902 * WARNING: A real printer/server implementation MUST NOT implement
4903 * media updates via a GET request - GET requests are supposed to be
4904 * idempotent (without side-effects) and we obviously are not
4905 * authenticating access here. This form is provided solely to
4906 * enable testing and development!
4909 const char *val
; /* Form value */
4911 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
4912 client
->printer
->main_size
= atoi(val
);
4913 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
4914 client
->printer
->main_type
= atoi(val
);
4915 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
4916 client
->printer
->main_level
= atoi(val
);
4918 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
4919 client
->printer
->envelope_size
= atoi(val
);
4920 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
4921 client
->printer
->envelope_level
= atoi(val
);
4923 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
4924 client
->printer
->photo_size
= atoi(val
);
4925 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
4926 client
->printer
->photo_type
= atoi(val
);
4927 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
4928 client
->printer
->photo_level
= atoi(val
);
4930 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))
4931 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
4933 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
4935 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
))
4937 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
4938 if (client
->printer
->active_job
)
4939 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
4942 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
4944 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
4947 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
4949 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
4950 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
4951 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4952 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
4953 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
4954 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
4955 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4956 if (!strstr(types
[i
], "Photo"))
4957 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
4958 html_printf(client
, "</select> <select name=\"main_level\">");
4959 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4960 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
4961 html_printf(client
, "</select></td></tr>\n");
4964 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
4965 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4966 if (strstr(sizes
[i
], "Envelope"))
4967 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
4968 html_printf(client
, "</select> <select name=\"envelope_level\">");
4969 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4970 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
4971 html_printf(client
, "</select></td></tr>\n");
4974 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
4975 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4976 if (strstr(sizes
[i
], "Photo"))
4977 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
4978 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
4979 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4980 if (strstr(types
[i
], "Photo"))
4981 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
4982 html_printf(client
, "</select> <select name=\"photo_level\">");
4983 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4984 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
4985 html_printf(client
, "</select></td></tr>\n");
4987 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
4988 html_footer(client
);
4992 else if (!strcmp(client
->uri
, "/supplies"))
4995 * Show web supplies page...
4998 int i
, j
, /* Looping vars */
4999 num_options
; /* Number of form options */
5000 cups_option_t
*options
; /* Form options */
5001 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5003 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5006 html_header(client
, client
->printer
->name
);
5008 if ((num_options
= parse_options(client
, &options
)) > 0)
5011 * WARNING: A real printer/server implementation MUST NOT implement
5012 * supply updates via a GET request - GET requests are supposed to be
5013 * idempotent (without side-effects) and we obviously are not
5014 * authenticating access here. This form is provided solely to
5015 * enable testing and development!
5018 char name
[64]; /* Form field */
5019 const char *val
; /* Form value */
5021 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
);
5023 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5025 snprintf(name
, sizeof(name
), "supply_%d", i
);
5026 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5028 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5034 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
5035 else if (level
< 10)
5036 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
5041 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
5042 else if (level
> 90)
5043 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
5048 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5051 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5053 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5054 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5056 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5057 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5058 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5059 html_printf(client
, "</select></td></tr>\n");
5061 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5062 html_footer(client
);
5067 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5070 case HTTP_STATE_POST
:
5071 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5075 * Not an IPP request...
5078 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5082 * Read the IPP request...
5085 client
->request
= ippNew();
5087 while ((ipp_state
= ippRead(client
->http
,
5088 client
->request
)) != IPP_STATE_DATA
)
5090 if (ipp_state
== IPP_STATE_ERROR
)
5092 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5093 cupsLastErrorString());
5094 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5100 * Now that we have the IPP request, process the request...
5103 return (process_ipp(client
));
5106 break; /* Anti-compiler-warning-code */
5114 * 'process_ipp()' - Process an IPP request.
5117 static int /* O - 1 on success, 0 on error */
5118 process_ipp(_ipp_client_t
*client
) /* I - Client */
5120 ipp_tag_t group
; /* Current group tag */
5121 ipp_attribute_t
*attr
; /* Current attribute */
5122 ipp_attribute_t
*charset
; /* Character set attribute */
5123 ipp_attribute_t
*language
; /* Language attribute */
5124 ipp_attribute_t
*uri
; /* Printer URI attribute */
5125 int major
, minor
; /* Version number */
5126 const char *name
; /* Name of attribute */
5129 debug_attributes("Request", client
->request
, 1);
5132 * First build an empty response message for this request...
5135 client
->operation_id
= ippGetOperation(client
->request
);
5136 client
->response
= ippNewResponse(client
->request
);
5139 * Then validate the request header and required attributes...
5142 major
= ippGetVersion(client
->request
, &minor
);
5144 if (major
< 1 || major
> 2)
5147 * Return an error, since we only support IPP 1.x and 2.x.
5150 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
5151 "Bad request version number %d.%d.", major
, minor
);
5153 else if (ippGetRequestId(client
->request
) <= 0)
5154 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
5155 ippGetRequestId(client
->request
));
5156 else if (!ippFirstAttribute(client
->request
))
5157 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5158 "No attributes in request.");
5162 * Make sure that the attributes are provided in the correct order and
5163 * don't repeat groups...
5166 for (attr
= ippFirstAttribute(client
->request
),
5167 group
= ippGetGroupTag(attr
);
5169 attr
= ippNextAttribute(client
->request
))
5171 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5174 * Out of order; return an error...
5177 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5178 "Attribute groups are out of order (%x < %x).",
5179 ippGetGroupTag(attr
), group
);
5183 group
= ippGetGroupTag(attr
);
5189 * Then make sure that the first three attributes are:
5191 * attributes-charset
5192 * attributes-natural-language
5193 * printer-uri/job-uri
5196 attr
= ippFirstAttribute(client
->request
);
5197 name
= ippGetName(attr
);
5198 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5199 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5204 attr
= ippNextAttribute(client
->request
);
5205 name
= ippGetName(attr
);
5207 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5208 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5213 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5214 IPP_TAG_URI
)) != NULL
)
5216 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5217 IPP_TAG_URI
)) != NULL
)
5223 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5224 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5227 * Bad character set...
5230 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5231 "Unsupported character set \"%s\".",
5232 ippGetString(charset
, 0, NULL
));
5234 else if (!charset
|| !language
|| !uri
)
5237 * Return an error, since attributes-charset,
5238 * attributes-natural-language, and printer-uri/job-uri are required
5239 * for all operations.
5242 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5243 "Missing required attributes.");
5247 char scheme
[32], /* URI scheme */
5248 userpass
[32], /* Username/password in URI */
5249 host
[256], /* Host name in URI */
5250 resource
[256]; /* Resource path in URI */
5251 int port
; /* Port number in URI */
5253 name
= ippGetName(uri
);
5255 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5256 scheme
, sizeof(scheme
),
5257 userpass
, sizeof(userpass
),
5258 host
, sizeof(host
), &port
,
5259 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5260 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5261 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5262 else if ((!strcmp(name
, "job-uri") &&
5263 strncmp(resource
, "/ipp/print/", 11)) ||
5264 (!strcmp(name
, "printer-uri") &&
5265 strcmp(resource
, "/ipp/print")))
5266 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5267 name
, ippGetString(uri
, 0, NULL
));
5271 * Try processing the operation...
5274 switch (ippGetOperation(client
->request
))
5276 case IPP_OP_PRINT_JOB
:
5277 ipp_print_job(client
);
5280 case IPP_OP_PRINT_URI
:
5281 ipp_print_uri(client
);
5284 case IPP_OP_VALIDATE_JOB
:
5285 ipp_validate_job(client
);
5288 case IPP_OP_CREATE_JOB
:
5289 ipp_create_job(client
);
5292 case IPP_OP_SEND_DOCUMENT
:
5293 ipp_send_document(client
);
5296 case IPP_OP_SEND_URI
:
5297 ipp_send_uri(client
);
5300 case IPP_OP_CANCEL_JOB
:
5301 ipp_cancel_job(client
);
5304 case IPP_OP_GET_JOB_ATTRIBUTES
:
5305 ipp_get_job_attributes(client
);
5308 case IPP_OP_GET_JOBS
:
5309 ipp_get_jobs(client
);
5312 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5313 ipp_get_printer_attributes(client
);
5316 case IPP_OP_CLOSE_JOB
:
5317 ipp_close_job(client
);
5320 case IPP_OP_IDENTIFY_PRINTER
:
5321 ipp_identify_printer(client
);
5325 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5326 "Operation not supported.");
5335 * Send the HTTP header and return...
5338 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5339 httpFlush(client
->http
); /* Flush trailing (junk) data */
5341 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5342 ippLength(client
->response
)));
5347 * 'process_job()' - Process a print job.
5350 static void * /* O - Thread exit status */
5351 process_job(_ipp_job_t
*job
) /* I - Job */
5353 job
->state
= IPP_JSTATE_PROCESSING
;
5354 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
5355 job
->processing
= time(NULL
);
5357 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
5359 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5364 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
5366 if (job
->printer
->command
)
5369 * Execute a command with the job spool file and wait for it to complete...
5372 int pid
, /* Process ID */
5373 status
; /* Exit status */
5374 time_t start
, /* Start time */
5376 char *myargv
[3], /* Command-line arguments */
5377 *myenvp
[200]; /* Environment variables */
5378 int myenvc
; /* Number of environment variables */
5379 ipp_attribute_t
*attr
; /* Job attribute */
5380 char val
[1280], /* IPP_NAME=value */
5381 *valptr
; /* Pointer into string */
5383 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
5388 * Setup the command-line arguments...
5391 myargv
[0] = job
->printer
->command
;
5392 myargv
[1] = job
->filename
;
5396 * Copy the current environment, then add ENV variables for every Job
5400 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
5401 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
5403 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
5406 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
5407 * value(s) from the attribute.
5410 const char *name
= ippGetName(attr
);
5419 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
5424 *valptr
++ = (char)toupper(*name
& 255);
5429 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
5431 myenvp
[myenvc
++] = strdup(val
);
5433 myenvp
[myenvc
] = NULL
;
5436 * Now run the program...
5440 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
5442 if ((pid
= fork()) == 0)
5445 * Child comes here...
5448 execve(job
->printer
->command
, myargv
, myenvp
);
5454 * Unable to fork process...
5457 perror("Unable to start job processing command");
5463 * Free memory used for environment...
5467 free(myenvp
[-- myenvc
]);
5470 * Wait for child to complete...
5473 # ifdef HAVE_WAITPID
5474 while (waitpid(pid
, &status
, 0) < 0);
5476 while (wait(&status
) < 0);
5477 # endif /* HAVE_WAITPID */
5484 if (WIFEXITED(status
))
5486 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
5487 job
->printer
->command
, WEXITSTATUS(status
));
5490 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
5491 job
->printer
->command
, WTERMSIG(status
));
5493 job
->state
= IPP_JSTATE_ABORTED
;
5495 else if (status
< 0)
5496 job
->state
= IPP_JSTATE_ABORTED
;
5498 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
5499 job
->printer
->command
);
5502 * Make sure processing takes at least 5 seconds...
5506 if ((end
- start
) < 5)
5512 * Sleep for a random amount of time to simulate job processing.
5515 sleep((unsigned)(5 + (rand() % 11)));
5519 job
->state
= IPP_JSTATE_CANCELED
;
5520 else if (job
->state
== IPP_JSTATE_PROCESSING
)
5521 job
->state
= IPP_JSTATE_COMPLETED
;
5523 job
->completed
= time(NULL
);
5524 job
->printer
->state
= IPP_PSTATE_IDLE
;
5525 job
->printer
->active_job
= NULL
;
5532 * 'register_printer()' - Register a printer object via Bonjour.
5535 static int /* O - 1 on success, 0 on error */
5537 _ipp_printer_t
*printer
, /* I - Printer */
5538 const char *location
, /* I - Location */
5539 const char *make
, /* I - Manufacturer */
5540 const char *model
, /* I - Model name */
5541 const char *formats
, /* I - Supported formats */
5542 const char *adminurl
, /* I - Web interface URL */
5543 const char *uuid
, /* I - Printer UUID */
5544 int color
, /* I - 1 = color, 0 = monochrome */
5545 int duplex
, /* I - 1 = duplex, 0 = simplex */
5546 const char *subtype
) /* I - Service subtype */
5548 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
5549 _ipp_txt_t ipp_txt
; /* Bonjour IPP TXT record */
5550 #endif /* HAVE_DNSSD || HAVE_AVAHI */
5552 DNSServiceErrorType error
; /* Error from Bonjour */
5553 char make_model
[256],/* Make and model together */
5554 product
[256], /* Product string */
5555 regtype
[256]; /* Bonjour service type */
5559 * Build the TXT record for IPP...
5562 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
5563 snprintf(product
, sizeof(product
), "(%s)", model
);
5565 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
5566 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
5567 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(make_model
),
5569 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(adminurl
),
5572 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(location
),
5574 TXTRecordSetValue(&ipp_txt
, "product", (uint8_t)strlen(product
),
5576 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
),
5578 TXTRecordSetValue(&ipp_txt
, "Color", 1, color
? "T" : "F");
5579 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, duplex
? "T" : "F");
5580 TXTRecordSetValue(&ipp_txt
, "usb_MFG", (uint8_t)strlen(make
),
5582 TXTRecordSetValue(&ipp_txt
, "usb_MDL", (uint8_t)strlen(model
),
5584 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(uuid
), uuid
);
5586 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
5587 # endif /* HAVE_SSL */
5588 if (strstr(formats
, "image/urf"))
5589 TXTRecordSetValue(&ipp_txt
, "URF", 66, "CP1,IS1-5-7,MT1-2-3-4-5-6-8-9-10-11-12-13,RS600,SRGB24,V1.4,W8,DM1");
5591 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
5592 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
5595 * Register the _printer._tcp (LPD) service type with a port number of 0 to
5596 * defend our service name but not actually support LPD...
5599 printer
->printer_ref
= DNSSDMaster
;
5601 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
5602 kDNSServiceFlagsShareConnection
,
5603 0 /* interfaceIndex */, printer
->dnssd_name
,
5604 "_printer._tcp", NULL
/* domain */,
5605 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
5606 NULL
/* txtRecord */,
5607 (DNSServiceRegisterReply
)dnssd_callback
,
5608 printer
)) != kDNSServiceErr_NoError
)
5610 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
5611 printer
->dnssd_name
, error
);
5616 * Then register the _ipp._tcp (IPP) service type with the real port number to
5617 * advertise our IPP printer...
5620 printer
->ipp_ref
= DNSSDMaster
;
5622 if (subtype
&& *subtype
)
5623 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
5625 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
5627 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
5628 kDNSServiceFlagsShareConnection
,
5629 0 /* interfaceIndex */, printer
->dnssd_name
,
5630 regtype
, NULL
/* domain */,
5631 NULL
/* host */, htons(printer
->port
),
5632 TXTRecordGetLength(&ipp_txt
),
5633 TXTRecordGetBytesPtr(&ipp_txt
),
5634 (DNSServiceRegisterReply
)dnssd_callback
,
5635 printer
)) != kDNSServiceErr_NoError
)
5637 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5638 printer
->dnssd_name
, regtype
, error
);
5644 * Then register the _ipps._tcp (IPP) service type with the real port number to
5645 * advertise our IPPS printer...
5648 printer
->ipps_ref
= DNSSDMaster
;
5650 if (subtype
&& *subtype
)
5651 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
5653 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
5655 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
5656 kDNSServiceFlagsShareConnection
,
5657 0 /* interfaceIndex */, printer
->dnssd_name
,
5658 regtype
, NULL
/* domain */,
5659 NULL
/* host */, htons(printer
->port
),
5660 TXTRecordGetLength(&ipp_txt
),
5661 TXTRecordGetBytesPtr(&ipp_txt
),
5662 (DNSServiceRegisterReply
)dnssd_callback
,
5663 printer
)) != kDNSServiceErr_NoError
)
5665 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5666 printer
->dnssd_name
, regtype
, error
);
5669 # endif /* HAVE_SSL */
5672 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
5673 * real port number to advertise our IPP printer...
5676 printer
->http_ref
= DNSSDMaster
;
5678 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
5679 kDNSServiceFlagsShareConnection
,
5680 0 /* interfaceIndex */, printer
->dnssd_name
,
5681 "_http._tcp,_printer", NULL
/* domain */,
5682 NULL
/* host */, htons(printer
->port
),
5683 0 /* txtLen */, NULL
, /* txtRecord */
5684 (DNSServiceRegisterReply
)dnssd_callback
,
5685 printer
)) != kDNSServiceErr_NoError
)
5687 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5688 printer
->dnssd_name
, regtype
, error
);
5692 TXTRecordDeallocate(&ipp_txt
);
5694 #elif defined(HAVE_AVAHI)
5695 char temp
[256]; /* Subtype service string */
5698 * Create the TXT record...
5702 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
5703 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s %s", make
, model
);
5704 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", adminurl
);
5706 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", location
);
5707 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "product=(%s)", model
);
5708 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
5709 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", color
? "T" : "F");
5710 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", duplex
? "T" : "F");
5711 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MFG=%s", make
);
5712 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MDL=%s", model
);
5713 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", uuid
);
5715 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
5716 # endif /* HAVE_SSL */
5719 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
5722 avahi_threaded_poll_lock(DNSSDMaster
);
5724 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
5726 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
);
5729 * Then register the _ipp._tcp (IPP)...
5732 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
);
5733 if (subtype
&& *subtype
)
5735 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
5736 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
5741 * _ipps._tcp (IPPS) for secure printing...
5744 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
);
5745 if (subtype
&& *subtype
)
5747 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
5748 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
5750 #endif /* HAVE_SSL */
5753 * Finally _http.tcp (HTTP) for the web interface...
5756 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
);
5757 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");
5763 avahi_entry_group_commit(printer
->ipp_ref
);
5764 avahi_threaded_poll_unlock(DNSSDMaster
);
5766 avahi_string_list_free(ipp_txt
);
5767 #endif /* HAVE_DNSSD */
5774 * 'respond_http()' - Send a HTTP response.
5777 int /* O - 1 on success, 0 on failure */
5779 _ipp_client_t
*client
, /* I - Client */
5780 http_status_t code
, /* I - HTTP status of response */
5781 const char *content_encoding
, /* I - Content-Encoding of response */
5782 const char *type
, /* I - MIME media type of response */
5783 size_t length
) /* I - Length of response */
5785 char message
[1024]; /* Text message */
5788 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
5790 if (code
== HTTP_STATUS_CONTINUE
)
5793 * 100-continue doesn't send any headers...
5796 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
5800 * Format an error message...
5803 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
5805 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
5807 type
= "text/plain";
5808 length
= strlen(message
);
5814 * Send the HTTP response header...
5817 httpClearFields(client
->http
);
5819 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
5820 client
->operation
== HTTP_STATE_OPTIONS
)
5821 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
5825 if (!strcmp(type
, "text/html"))
5826 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
5827 "text/html; charset=utf-8");
5829 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
5831 if (content_encoding
)
5832 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
5835 httpSetLength(client
->http
, length
);
5837 if (httpWriteResponse(client
->http
, code
) < 0)
5841 * Send the response data...
5847 * Send a plain text message.
5850 if (httpPrintf(client
->http
, "%s", message
) < 0)
5853 if (httpWrite2(client
->http
, "", 0) < 0)
5856 else if (client
->response
)
5859 * Send an IPP response...
5862 debug_attributes("Response", client
->response
, 2);
5864 ippSetState(client
->response
, IPP_STATE_IDLE
);
5866 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
5875 * 'respond_ipp()' - Send an IPP response.
5879 respond_ipp(_ipp_client_t
*client
, /* I - Client */
5880 ipp_status_t status
, /* I - status-code */
5881 const char *message
, /* I - printf-style status-message */
5882 ...) /* I - Additional args as needed */
5884 const char *formatted
= NULL
; /* Formatted message */
5887 ippSetStatusCode(client
->response
, status
);
5891 va_list ap
; /* Pointer to additional args */
5892 ipp_attribute_t
*attr
; /* New status-message attribute */
5894 va_start(ap
, message
);
5895 if ((attr
= ippFindAttribute(client
->response
, "status-message",
5896 IPP_TAG_TEXT
)) != NULL
)
5897 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
5899 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
5900 "status-message", NULL
, message
, ap
);
5903 formatted
= ippGetString(attr
, 0, NULL
);
5907 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
5908 ippOpString(client
->operation_id
), ippErrorString(status
),
5911 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
5912 ippOpString(client
->operation_id
), ippErrorString(status
));
5917 * 'respond_unsupported()' - Respond with an unsupported attribute.
5921 respond_unsupported(
5922 _ipp_client_t
*client
, /* I - Client */
5923 ipp_attribute_t
*attr
) /* I - Atribute */
5925 ipp_attribute_t
*temp
; /* Copy of attribute */
5928 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5929 "Unsupported %s %s%s value.", ippGetName(attr
),
5930 ippGetCount(attr
) > 1 ? "1setOf " : "",
5931 ippTagString(ippGetValueTag(attr
)));
5933 temp
= ippCopyAttribute(client
->response
, attr
, 0);
5934 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
5939 * 'run_printer()' - Run the printer service.
5943 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
5945 int num_fds
; /* Number of file descriptors */
5946 struct pollfd polldata
[3]; /* poll() data */
5947 int timeout
; /* Timeout for poll() */
5948 _ipp_client_t
*client
; /* New client */
5952 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5955 polldata
[0].fd
= printer
->ipv4
;
5956 polldata
[0].events
= POLLIN
;
5958 polldata
[1].fd
= printer
->ipv6
;
5959 polldata
[1].events
= POLLIN
;
5964 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
5965 polldata
[num_fds
++].events
= POLLIN
;
5966 #endif /* HAVE_DNSSD */
5969 * Loop until we are killed or have a hard error...
5974 if (cupsArrayCount(printer
->jobs
))
5979 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
5981 perror("poll() failed");
5985 if (polldata
[0].revents
& POLLIN
)
5987 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
5989 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5991 perror("Unable to create client thread");
5992 delete_client(client
);
5997 if (polldata
[1].revents
& POLLIN
)
5999 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6001 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
6003 perror("Unable to create client thread");
6004 delete_client(client
);
6010 if (polldata
[2].revents
& POLLIN
)
6011 DNSServiceProcessResult(DNSSDMaster
);
6012 #endif /* HAVE_DNSSD */
6015 * Clean out old jobs...
6018 clean_jobs(printer
);
6024 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6028 time_string(time_t tv
, /* I - Time value */
6029 char *buffer
, /* I - Buffer */
6030 size_t bufsize
) /* I - Size of buffer */
6032 struct tm
*curtime
= localtime(&tv
);
6035 strftime(buffer
, bufsize
, "%X", curtime
);
6041 * 'usage()' - Show program usage.
6045 usage(int status
) /* O - Exit status */
6049 puts(CUPS_SVERSION
" - Copyright 2010-2014 by Apple Inc. All rights "
6054 puts("Usage: ippserver [options] \"name\"");
6057 puts("-2 Supports 2-sided printing (default=1-sided)");
6058 puts("-M manufacturer Manufacturer name (default=Test)");
6059 puts("-P PIN printing mode");
6060 puts("-c command Run command for every print job");
6061 printf("-d spool-directory Spool directory "
6062 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6063 puts("-f type/subtype[,...] List of supported types "
6064 "(default=application/pdf,image/jpeg)");
6065 puts("-h Show program help");
6066 puts("-i iconfile.png PNG icon file (default=printer.png)");
6067 puts("-k Keep job spool files");
6068 puts("-l location Location of printer (default=empty string)");
6069 puts("-m model Model name (default=Printer)");
6070 puts("-n hostname Hostname for printer");
6071 puts("-p port Port number (default=auto)");
6072 puts("-r subtype Bonjour service subtype (default=_print)");
6073 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6074 puts("-v[vvv] Be (very) verbose");
6081 * 'valid_doc_attributes()' - Determine whether the document attributes are
6084 * When one or more document attributes are invalid, this function adds a
6085 * suitable response and attributes to the unsupported group.
6088 static int /* O - 1 if valid, 0 if not */
6089 valid_doc_attributes(
6090 _ipp_client_t
*client
) /* I - Client */
6092 int valid
= 1; /* Valid attributes? */
6093 ipp_op_t op
= ippGetOperation(client
->request
);
6095 const char *op_name
= ippOpString(op
);
6096 /* IPP operation name */
6097 ipp_attribute_t
*attr
, /* Current attribute */
6098 *supported
; /* xxx-supported attribute */
6099 const char *compression
= NULL
,
6100 /* compression value */
6101 *format
= NULL
; /* document-format value */
6105 * Check operation attributes...
6108 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6111 * If compression is specified, only accept a supported value in a Print-Job
6112 * or Send-Document request...
6115 compression
= ippGetString(attr
, 0, NULL
);
6116 supported
= ippFindAttribute(client
->printer
->attrs
,
6117 "compression-supported", IPP_TAG_KEYWORD
);
6119 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6120 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6121 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6122 op
!= IPP_OP_VALIDATE_JOB
) ||
6123 !ippContainsString(supported
, compression
))
6125 respond_unsupported(client
, attr
);
6130 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6132 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6134 if (strcmp(compression
, "none"))
6137 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6138 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
6144 * Is it a format we support?
6147 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
6149 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
6150 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
6152 respond_unsupported(client
, attr
);
6157 format
= ippGetString(attr
, 0, NULL
);
6159 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
6160 client
->hostname
, op_name
, format
);
6162 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
6167 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
6169 format
= "application/octet-stream"; /* Should never happen */
6171 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
6174 if (!strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
6177 * Auto-type the file using the first 8 bytes of the file...
6180 unsigned char header
[8]; /* First 8 bytes of file */
6182 memset(header
, 0, sizeof(header
));
6183 httpPeek(client
->http
, (char *)header
, sizeof(header
));
6185 if (!memcmp(header
, "%PDF", 4))
6186 format
= "application/pdf";
6187 else if (!memcmp(header
, "%!", 2))
6188 format
= "application/postscript";
6189 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
6190 format
= "image/jpeg";
6191 else if (!memcmp(header
, "\211PNG", 4))
6192 format
= "image/png";
6193 else if (!memcmp(header
, "RAS2", 4))
6194 format
= "image/pwg-raster";
6195 else if (!memcmp(header
, "UNIRAST", 8))
6196 format
= "image/urf";
6202 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
6203 client
->hostname
, op_name
, format
);
6205 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
6209 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
6211 respond_unsupported(client
, attr
);
6219 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
6220 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
6227 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6229 * When one or more job attributes are invalid, this function adds a suitable
6230 * response and attributes to the unsupported group.
6233 static int /* O - 1 if valid, 0 if not */
6234 valid_job_attributes(
6235 _ipp_client_t
*client
) /* I - Client */
6237 int i
, /* Looping var */
6238 valid
= 1; /* Valid attributes? */
6239 ipp_attribute_t
*attr
, /* Current attribute */
6240 *supported
; /* xxx-supported attribute */
6244 * Check operation attributes...
6247 valid
= valid_doc_attributes(client
);
6250 * Check the various job template attributes...
6253 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
6255 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6256 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
6258 respond_unsupported(client
, attr
);
6263 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
6265 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
6267 respond_unsupported(client
, attr
);
6272 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
6274 if (ippGetCount(attr
) != 1 ||
6275 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6276 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6277 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6278 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
6280 respond_unsupported(client
, attr
);
6285 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
6287 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
6289 respond_unsupported(client
, attr
);
6294 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
6296 if (ippGetCount(attr
) != 1 ||
6297 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6298 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
6300 respond_unsupported(client
, attr
);
6304 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
6307 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
6309 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
6311 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6312 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
6314 respond_unsupported(client
, attr
);
6319 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
6321 if (ippGetCount(attr
) != 1 ||
6322 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6323 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6324 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6325 strcmp(ippGetString(attr
, 0, NULL
), "none"))
6327 respond_unsupported(client
, attr
);
6332 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
6334 if (ippGetCount(attr
) != 1 ||
6335 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6336 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6337 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
6339 respond_unsupported(client
, attr
);
6345 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
6347 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
6350 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
6352 respond_unsupported(client
, attr
);
6358 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
6360 if (ippGetCount(attr
) != 1 ||
6361 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
6363 respond_unsupported(client
, attr
);
6366 /* TODO: check for valid media-col */
6369 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
6371 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6372 (strcmp(ippGetString(attr
, 0, NULL
),
6373 "separate-documents-uncollated-copies") &&
6374 strcmp(ippGetString(attr
, 0, NULL
),
6375 "separate-documents-collated-copies")))
6377 respond_unsupported(client
, attr
);
6382 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
6384 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6385 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
6386 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
6388 respond_unsupported(client
, attr
);
6393 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
6395 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
6397 respond_unsupported(client
, attr
);
6402 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
6404 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6405 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
6406 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
6408 respond_unsupported(client
, attr
);
6413 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
6415 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
6417 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
6420 respond_unsupported(client
, attr
);
6425 int count
, /* Number of supported values */
6426 xdpi
, /* Horizontal resolution for job template attribute */
6427 ydpi
, /* Vertical resolution for job template attribute */
6428 sydpi
; /* Vertical resolution for supported value */
6429 ipp_res_t units
, /* Units for job template attribute */
6430 sunits
; /* Units for supported value */
6432 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
6433 count
= ippGetCount(supported
);
6435 for (i
= 0; i
< count
; i
++)
6437 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
6443 respond_unsupported(client
, attr
);
6449 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
6451 const char *sides
= ippGetString(attr
, 0, NULL
);
6452 /* "sides" value... */
6454 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
6456 respond_unsupported(client
, attr
);
6459 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
6461 if (!ippContainsString(supported
, sides
))
6463 respond_unsupported(client
, attr
);
6467 else if (strcmp(sides
, "one-sided"))
6469 respond_unsupported(client
, attr
);