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 # else /* 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
[] =
1982 "job-accounting-user-id",
1986 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1987 "printer-mandatory-job-attributes",
1988 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
1991 /* printer-more-info */
1992 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1995 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1998 /* printer-organization */
1999 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
2001 /* printer-organizational-unit */
2002 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
2004 /* printer-resolution-default */
2005 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
2006 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
2008 /* printer-resolution-supported */
2009 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
2010 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
2012 /* printer-supply-description */
2013 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
);
2015 /* printer-supply-info-uri */
2016 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
2018 /* printer-uri-supported */
2019 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
2022 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
2023 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
2025 /* pwg-raster-document-xxx-supported */
2026 for (i
= 0; i
< num_formats
; i
++)
2027 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
2030 if (i
< num_formats
)
2032 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
,
2033 "pwg-raster-document-resolution-supported",
2034 (int)(sizeof(pwg_raster_document_resolution_supported
) /
2035 sizeof(pwg_raster_document_resolution_supported
[0])),
2037 pwg_raster_document_resolution_supported
,
2038 pwg_raster_document_resolution_supported
);
2039 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2040 "pwg-raster-document-sheet-back", NULL
, "normal");
2041 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2042 "pwg-raster-document-type-supported",
2043 (int)(sizeof(pwg_raster_document_type_supported
) /
2044 sizeof(pwg_raster_document_type_supported
[0])), NULL
,
2045 pwg_raster_document_type_supported
);
2048 /* reference-uri-scheme-supported */
2049 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2050 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
2051 "reference-uri-schemes-supported",
2052 (int)(sizeof(reference_uri_schemes_supported
) /
2053 sizeof(reference_uri_schemes_supported
[0])),
2054 NULL
, reference_uri_schemes_supported
);
2057 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2058 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2059 "sides-default", NULL
, "one-sided");
2061 /* sides-supported */
2062 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2063 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2064 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
2067 for (i
= 0; i
< num_formats
; i
++)
2068 if (!strcasecmp(formats
[i
], "image/urf"))
2071 if (i
< num_formats
)
2072 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2074 /* uri-authentication-supported */
2075 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2076 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2077 "uri-authentication-supported", NULL
, "none");
2079 /* uri-security-supported */
2080 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2081 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2082 "uri-security-supported", NULL
, "none");
2084 /* which-jobs-supported */
2085 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2086 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2087 "which-jobs-supported",
2088 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
2092 debug_attributes("Printer", printer
->attrs
, 0);
2095 * Register the printer with Bonjour...
2098 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2109 * If we get here we were unable to create the printer...
2114 delete_printer(printer
);
2120 * 'debug_attributes()' - Print attributes in a request or response.
2124 debug_attributes(const char *title
, /* I - Title */
2125 ipp_t
*ipp
, /* I - Request/response */
2126 int type
) /* I - 0 = object, 1 = request, 2 = response */
2128 ipp_tag_t group_tag
; /* Current group */
2129 ipp_attribute_t
*attr
; /* Current attribute */
2130 char buffer
[2048]; /* String buffer for value */
2131 int major
, minor
; /* Version */
2137 fprintf(stderr
, "%s:\n", title
);
2138 major
= ippGetVersion(ipp
, &minor
);
2139 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2141 fprintf(stderr
, " operation-id=%s(%04x)\n",
2142 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2144 fprintf(stderr
, " status-code=%s(%04x)\n",
2145 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2146 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2148 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2150 attr
= ippNextAttribute(ipp
))
2152 if (ippGetGroupTag(attr
) != group_tag
)
2154 group_tag
= ippGetGroupTag(attr
);
2155 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2158 if (ippGetName(attr
))
2160 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2161 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2162 ippGetCount(attr
) > 1 ? "1setOf " : "",
2163 ippTagString(ippGetValueTag(attr
)), buffer
);
2170 * 'delete_client()' - Close the socket and free all memory used by a client
2175 delete_client(_ipp_client_t
*client
) /* I - Client */
2178 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2181 * Flush pending writes before closing...
2184 httpFlushWrite(client
->http
);
2190 httpClose(client
->http
);
2192 ippDelete(client
->request
);
2193 ippDelete(client
->response
);
2200 * 'delete_job()' - Remove from the printer and free all memory used by a job
2205 delete_job(_ipp_job_t
*job
) /* I - Job */
2208 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2210 ippDelete(job
->attrs
);
2215 unlink(job
->filename
);
2217 free(job
->filename
);
2225 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2226 * used by a printer object.
2230 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2232 if (printer
->ipv4
>= 0)
2233 close(printer
->ipv4
);
2235 if (printer
->ipv6
>= 0)
2236 close(printer
->ipv6
);
2239 if (printer
->printer_ref
)
2240 DNSServiceRefDeallocate(printer
->printer_ref
);
2241 if (printer
->ipp_ref
)
2242 DNSServiceRefDeallocate(printer
->ipp_ref
);
2243 if (printer
->ipps_ref
)
2244 DNSServiceRefDeallocate(printer
->ipps_ref
);
2245 if (printer
->http_ref
)
2246 DNSServiceRefDeallocate(printer
->http_ref
);
2247 #elif defined(HAVE_AVAHI)
2248 avahi_threaded_poll_lock(DNSSDMaster
);
2250 if (printer
->printer_ref
)
2251 avahi_entry_group_free(printer
->printer_ref
);
2252 if (printer
->ipp_ref
)
2253 avahi_entry_group_free(printer
->ipp_ref
);
2254 if (printer
->ipps_ref
)
2255 avahi_entry_group_free(printer
->ipps_ref
);
2256 if (printer
->http_ref
)
2257 avahi_entry_group_free(printer
->http_ref
);
2259 avahi_threaded_poll_unlock(DNSSDMaster
);
2260 #endif /* HAVE_DNSSD */
2262 if (printer
->dnssd_name
)
2263 free(printer
->dnssd_name
);
2265 free(printer
->name
);
2267 free(printer
->icon
);
2268 if (printer
->command
)
2269 free(printer
->command
);
2270 if (printer
->directory
)
2271 free(printer
->directory
);
2272 if (printer
->hostname
)
2273 free(printer
->hostname
);
2277 ippDelete(printer
->attrs
);
2278 cupsArrayDelete(printer
->jobs
);
2286 * 'dnssd_callback()' - Handle Bonjour registration events.
2291 DNSServiceRef sdRef
, /* I - Service reference */
2292 DNSServiceFlags flags
, /* I - Status flags */
2293 DNSServiceErrorType errorCode
, /* I - Error, if any */
2294 const char *name
, /* I - Service name */
2295 const char *regtype
, /* I - Service type */
2296 const char *domain
, /* I - Domain for service */
2297 _ipp_printer_t
*printer
) /* I - Printer */
2305 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2306 regtype
, (int)errorCode
);
2309 else if (strcasecmp(name
, printer
->dnssd_name
))
2312 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2314 /* No lock needed since only the main thread accesses/changes this */
2315 free(printer
->dnssd_name
);
2316 printer
->dnssd_name
= strdup(name
);
2321 #elif defined(HAVE_AVAHI)
2323 * 'dnssd_callback()' - Handle Bonjour registration events.
2328 AvahiEntryGroup
*srv
, /* I - Service */
2329 AvahiEntryGroupState state
, /* I - Registration state */
2330 void *context
) /* I - Printer */
2339 * 'dnssd_client_cb()' - Client callback for Avahi.
2341 * Called whenever the client or server state changes...
2346 AvahiClient
*c
, /* I - Client */
2347 AvahiClientState state
, /* I - Current state */
2348 void *userdata
) /* I - User data (unused) */
2358 fprintf(stderr
, "Ignore Avahi state %d.\n", state
);
2361 case AVAHI_CLIENT_FAILURE
:
2362 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
2364 fputs("Avahi server crashed, exiting.\n", stderr
);
2370 #endif /* HAVE_DNSSD */
2374 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2381 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2383 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2387 #elif defined(HAVE_AVAHI)
2388 int error
; /* Error code, if any */
2390 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2392 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2396 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2398 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2402 avahi_threaded_poll_start(DNSSDMaster
);
2403 #endif /* HAVE_DNSSD */
2408 * 'filter_cb()' - Filter printer attributes based on the requested array.
2411 static int /* O - 1 to copy, 0 to ignore */
2412 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2413 ipp_t
*dst
, /* I - Destination (unused) */
2414 ipp_attribute_t
*attr
) /* I - Source attribute */
2417 * Filter attributes as needed...
2422 ipp_tag_t group
= ippGetGroupTag(attr
);
2423 const char *name
= ippGetName(attr
);
2425 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
)))
2428 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2433 * 'find_job()' - Find a job specified in a request.
2436 static _ipp_job_t
* /* O - Job or NULL */
2437 find_job(_ipp_client_t
*client
) /* I - Client */
2439 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2440 _ipp_job_t key
, /* Job search key */
2441 *job
; /* Matching job, if any */
2444 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2446 const char *uri
= ippGetString(attr
, 0, NULL
);
2448 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2449 uri
[client
->printer
->urilen
] == '/')
2450 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2454 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2455 key
.id
= ippGetInteger(attr
, 0);
2457 _cupsRWLockRead(&(client
->printer
->rwlock
));
2458 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2459 _cupsRWUnlock(&(client
->printer
->rwlock
));
2466 * 'html_escape()' - Write a HTML-safe string.
2470 html_escape(_ipp_client_t
*client
, /* I - Client */
2471 const char *s
, /* I - String to write */
2472 size_t slen
) /* I - Number of characters to write */
2474 const char *start
, /* Start of segment */
2475 *end
; /* End of string */
2479 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2481 while (*s
&& s
< end
)
2483 if (*s
== '&' || *s
== '<')
2486 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2489 httpWrite2(client
->http
, "&", 5);
2491 httpWrite2(client
->http
, "<", 4);
2500 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2505 * 'html_footer()' - Show the web interface footer.
2507 * This function also writes the trailing 0-length chunk.
2511 html_footer(_ipp_client_t
*client
) /* I - Client */
2517 httpWrite2(client
->http
, "", 0);
2522 * 'html_header()' - Show the web interface header and title.
2526 html_header(_ipp_client_t
*client
, /* I - Client */
2527 const char *title
) /* I - Title */
2533 "<title>%s</title>\n"
2534 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2535 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2536 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2537 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2539 "body { font-family: sans-serif; margin: 0; }\n"
2540 "div.body { padding: 0px 10px 10px; }\n"
2541 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2542 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2543 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2544 "table.form th { text-align: right; }\n"
2545 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2546 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2547 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2548 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2549 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2550 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2551 "table.nav td { margin: 0; text-align: center; }\n"
2552 "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"
2553 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2554 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2555 "td.nav:hover { background: #666; color: #fff; }\n"
2556 "td.nav:active { background: #000; color: #ff0; }\n"
2560 "<table class=\"nav\"><tr>"
2561 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2562 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2563 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2565 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2570 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2574 html_printf(_ipp_client_t
*client
, /* I - Client */
2575 const char *format
, /* I - Printf-style format string */
2576 ...) /* I - Additional arguments as needed */
2578 va_list ap
; /* Pointer to arguments */
2579 const char *start
; /* Start of string */
2580 char size
, /* Size character (h, l, L) */
2581 type
; /* Format type character */
2582 int width
, /* Width of field */
2583 prec
; /* Number of characters of precision */
2584 char tformat
[100], /* Temporary format string for sprintf() */
2585 *tptr
, /* Pointer into temporary format */
2586 temp
[1024]; /* Buffer for formatted numbers */
2587 char *s
; /* Pointer to string */
2591 * Loop through the format string, formatting as needed...
2594 va_start(ap
, format
);
2602 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2605 *tptr
++ = *format
++;
2609 httpWrite2(client
->http
, "%", 1);
2614 else if (strchr(" -+#\'", *format
))
2615 *tptr
++ = *format
++;
2620 * Get width from argument...
2624 width
= va_arg(ap
, int);
2626 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2627 tptr
+= strlen(tptr
);
2633 while (isdigit(*format
& 255))
2635 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2638 width
= width
* 10 + *format
++ - '0';
2644 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2652 * Get precision from argument...
2656 prec
= va_arg(ap
, int);
2658 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2659 tptr
+= strlen(tptr
);
2665 while (isdigit(*format
& 255))
2667 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2670 prec
= prec
* 10 + *format
++ - '0';
2675 if (*format
== 'l' && format
[1] == 'l')
2679 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2687 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2689 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2704 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2713 case 'E' : /* Floating point formats */
2718 if ((size_t)(width
+ 2) > sizeof(temp
))
2721 sprintf(temp
, tformat
, va_arg(ap
, double));
2723 httpWrite2(client
->http
, temp
, strlen(temp
));
2726 case 'B' : /* Integer formats */
2734 if ((size_t)(width
+ 2) > sizeof(temp
))
2737 # ifdef HAVE_LONG_LONG
2739 sprintf(temp
, tformat
, va_arg(ap
, long long));
2741 # endif /* HAVE_LONG_LONG */
2743 sprintf(temp
, tformat
, va_arg(ap
, long));
2745 sprintf(temp
, tformat
, va_arg(ap
, int));
2747 httpWrite2(client
->http
, temp
, strlen(temp
));
2750 case 'p' : /* Pointer value */
2751 if ((size_t)(width
+ 2) > sizeof(temp
))
2754 sprintf(temp
, tformat
, va_arg(ap
, void *));
2756 httpWrite2(client
->http
, temp
, strlen(temp
));
2759 case 'c' : /* Character or character array */
2762 temp
[0] = (char)va_arg(ap
, int);
2764 html_escape(client
, temp
, 1);
2767 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2770 case 's' : /* String */
2771 if ((s
= va_arg(ap
, char *)) == NULL
)
2774 html_escape(client
, s
, strlen(s
));
2783 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2790 * 'ipp_cancel_job()' - Cancel a job.
2794 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2796 _ipp_job_t
*job
; /* Job information */
2803 if ((job
= find_job(client
)) == NULL
)
2805 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2810 * See if the job is already completed, canceled, or aborted; if so,
2811 * we can't cancel...
2816 case IPP_JSTATE_CANCELED
:
2817 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2818 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2821 case IPP_JSTATE_ABORTED
:
2822 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2823 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2826 case IPP_JSTATE_COMPLETED
:
2827 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2828 "Job #%d is already completed - can\'t cancel.", job
->id
);
2836 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2838 if (job
->state
== IPP_JSTATE_PROCESSING
||
2839 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2843 job
->state
= IPP_JSTATE_CANCELED
;
2844 job
->completed
= time(NULL
);
2847 _cupsRWUnlock(&(client
->printer
->rwlock
));
2849 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2856 * 'ipp_close_job()' - Close an open job.
2860 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
2862 _ipp_job_t
*job
; /* Job information */
2869 if ((job
= find_job(client
)) == NULL
)
2871 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2876 * See if the job is already completed, canceled, or aborted; if so,
2877 * we can't cancel...
2882 case IPP_JSTATE_CANCELED
:
2883 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2884 "Job #%d is canceled - can\'t close.", job
->id
);
2887 case IPP_JSTATE_ABORTED
:
2888 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2889 "Job #%d is aborted - can\'t close.", job
->id
);
2892 case IPP_JSTATE_COMPLETED
:
2893 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2894 "Job #%d is completed - can\'t close.", job
->id
);
2897 case IPP_JSTATE_PROCESSING
:
2898 case IPP_JSTATE_STOPPED
:
2899 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2900 "Job #%d is already closed.", job
->id
);
2904 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2911 * 'ipp_create_job()' - Create a job object.
2915 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2917 _ipp_job_t
*job
; /* New job */
2918 cups_array_t
*ra
; /* Attributes to send in response */
2922 * Validate print job attributes...
2925 if (!valid_job_attributes(client
))
2927 httpFlush(client
->http
);
2932 * Do we have a file to print?
2935 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2937 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2938 "Unexpected document data following request.");
2946 if ((job
= create_job(client
)) == NULL
)
2948 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2949 "Currently printing another job.");
2954 * Return the job info...
2957 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2959 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2960 cupsArrayAdd(ra
, "job-id");
2961 cupsArrayAdd(ra
, "job-state");
2962 cupsArrayAdd(ra
, "job-state-message");
2963 cupsArrayAdd(ra
, "job-state-reasons");
2964 cupsArrayAdd(ra
, "job-uri");
2966 copy_job_attributes(client
, job
, ra
);
2967 cupsArrayDelete(ra
);
2972 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2976 ipp_get_job_attributes(
2977 _ipp_client_t
*client
) /* I - Client */
2979 _ipp_job_t
*job
; /* Job */
2980 cups_array_t
*ra
; /* requested-attributes */
2983 if ((job
= find_job(client
)) == NULL
)
2985 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2989 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2991 ra
= ippCreateRequestedArray(client
->request
);
2992 copy_job_attributes(client
, job
, ra
);
2993 cupsArrayDelete(ra
);
2998 * 'ipp_get_jobs()' - Get a list of job objects.
3002 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
3004 ipp_attribute_t
*attr
; /* Current attribute */
3005 const char *which_jobs
= NULL
;
3006 /* which-jobs values */
3007 int job_comparison
; /* Job comparison */
3008 ipp_jstate_t job_state
; /* job-state value */
3009 int first_job_id
, /* First job ID */
3010 limit
, /* Maximum number of jobs to return */
3011 count
; /* Number of jobs that match */
3012 const char *username
; /* Username */
3013 _ipp_job_t
*job
; /* Current job pointer */
3014 cups_array_t
*ra
; /* Requested attributes array */
3018 * See if the "which-jobs" attribute have been specified...
3021 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3022 IPP_TAG_KEYWORD
)) != NULL
)
3024 which_jobs
= ippGetString(attr
, 0, NULL
);
3025 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3028 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3030 job_comparison
= -1;
3031 job_state
= IPP_JSTATE_STOPPED
;
3033 else if (!strcmp(which_jobs
, "completed"))
3036 job_state
= IPP_JSTATE_CANCELED
;
3038 else if (!strcmp(which_jobs
, "aborted"))
3041 job_state
= IPP_JSTATE_ABORTED
;
3043 else if (!strcmp(which_jobs
, "all"))
3046 job_state
= IPP_JSTATE_PENDING
;
3048 else if (!strcmp(which_jobs
, "canceled"))
3051 job_state
= IPP_JSTATE_CANCELED
;
3053 else if (!strcmp(which_jobs
, "pending"))
3056 job_state
= IPP_JSTATE_PENDING
;
3058 else if (!strcmp(which_jobs
, "pending-held"))
3061 job_state
= IPP_JSTATE_HELD
;
3063 else if (!strcmp(which_jobs
, "processing"))
3066 job_state
= IPP_JSTATE_PROCESSING
;
3068 else if (!strcmp(which_jobs
, "processing-stopped"))
3071 job_state
= IPP_JSTATE_STOPPED
;
3075 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3076 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3077 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3078 "which-jobs", NULL
, which_jobs
);
3083 * See if they want to limit the number of jobs reported...
3086 if ((attr
= ippFindAttribute(client
->request
, "limit",
3087 IPP_TAG_INTEGER
)) != NULL
)
3089 limit
= ippGetInteger(attr
, 0);
3091 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3096 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3097 IPP_TAG_INTEGER
)) != NULL
)
3099 first_job_id
= ippGetInteger(attr
, 0);
3101 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3108 * See if we only want to see jobs for a specific user...
3113 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3114 IPP_TAG_BOOLEAN
)) != NULL
)
3116 int my_jobs
= ippGetBoolean(attr
, 0);
3118 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3119 my_jobs
? "true" : "false");
3123 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3124 IPP_TAG_NAME
)) == NULL
)
3126 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3127 "Need requesting-user-name with my-jobs.");
3131 username
= ippGetString(attr
, 0, NULL
);
3133 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3134 client
->hostname
, username
);
3139 * OK, build a list of jobs for this printer...
3142 ra
= ippCreateRequestedArray(client
->request
);
3144 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3146 _cupsRWLockRead(&(client
->printer
->rwlock
));
3148 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3149 (limit
<= 0 || count
< limit
) && job
;
3150 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3153 * Filter out jobs that don't match...
3156 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3157 (job_comparison
== 0 && job
->state
!= job_state
) ||
3158 (job_comparison
> 0 && job
->state
< job_state
) ||
3159 job
->id
< first_job_id
||
3160 (username
&& job
->username
&&
3161 strcasecmp(username
, job
->username
)))
3165 ippAddSeparator(client
->response
);
3168 copy_job_attributes(client
, job
, ra
);
3171 cupsArrayDelete(ra
);
3173 _cupsRWUnlock(&(client
->printer
->rwlock
));
3178 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3182 ipp_get_printer_attributes(
3183 _ipp_client_t
*client
) /* I - Client */
3185 cups_array_t
*ra
; /* Requested attributes array */
3186 _ipp_printer_t
*printer
; /* Printer */
3190 * Send the attributes...
3193 ra
= ippCreateRequestedArray(client
->request
);
3194 printer
= client
->printer
;
3196 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3198 _cupsRWLockRead(&(printer
->rwlock
));
3200 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3201 IPP_TAG_CUPS_CONST
);
3203 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3205 int i
, /* Looping var */
3206 num_ready
= 0; /* Number of ready media */
3207 ipp_t
*ready
[3]; /* Ready media */
3209 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3211 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
3212 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);
3214 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);
3216 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3217 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);
3218 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3220 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
3221 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);
3223 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);
3228 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3229 for (i
= 0; i
< num_ready
; i
++)
3230 ippDelete(ready
[i
]);
3233 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3236 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3238 int num_ready
= 0; /* Number of ready media */
3239 const char *ready
[3]; /* Ready media */
3241 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3242 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3244 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3245 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3247 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3248 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3251 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3253 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3256 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3257 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3259 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3260 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3262 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3263 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3266 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3267 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3268 "printer-state", printer
->state
);
3270 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3271 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3273 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3274 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3276 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3278 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3280 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3283 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3285 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3286 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3287 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3288 "printer-state-reasons", NULL
, "none");
3291 int num_reasons
= 0;/* Number of reasons */
3292 const char *reasons
[32]; /* Reason strings */
3294 if (printer
->state_reasons
& _IPP_PREASON_OTHER
)
3295 reasons
[num_reasons
++] = "other";
3296 if (printer
->state_reasons
& _IPP_PREASON_COVER_OPEN
)
3297 reasons
[num_reasons
++] = "cover-open";
3298 if (printer
->state_reasons
& _IPP_PREASON_INPUT_TRAY_MISSING
)
3299 reasons
[num_reasons
++] = "input-tray-missing";
3300 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_EMPTY
)
3301 reasons
[num_reasons
++] = "marker-supply-empty-warning";
3302 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_LOW
)
3303 reasons
[num_reasons
++] = "marker-supply-low-report";
3304 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
)
3305 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
3306 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_FULL
)
3307 reasons
[num_reasons
++] = "marker-waste-full-warning";
3308 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
3309 reasons
[num_reasons
++] = "media-empty-warning";
3310 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_JAM
)
3311 reasons
[num_reasons
++] = "media-jam-warning";
3312 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_LOW
)
3313 reasons
[num_reasons
++] = "media-low-report";
3314 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_NEEDED
)
3315 reasons
[num_reasons
++] = "media-needed-report";
3316 if (printer
->state_reasons
& _IPP_PREASON_MOVING_TO_PAUSED
)
3317 reasons
[num_reasons
++] = "moving-to-paused";
3318 if (printer
->state_reasons
& _IPP_PREASON_PAUSED
)
3319 reasons
[num_reasons
++] = "paused";
3320 if (printer
->state_reasons
& _IPP_PREASON_SPOOL_AREA_FULL
)
3321 reasons
[num_reasons
++] = "spool-area-full";
3322 if (printer
->state_reasons
& _IPP_PREASON_TONER_EMPTY
)
3323 reasons
[num_reasons
++] = "toner-empty-warning";
3324 if (printer
->state_reasons
& _IPP_PREASON_TONER_LOW
)
3325 reasons
[num_reasons
++] = "toner-low-report";
3327 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
3328 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3329 "printer-state-reasons", num_reasons
, NULL
, reasons
);
3333 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3335 int i
; /* Looping var */
3336 char buffer
[256]; /* Supply value buffer */
3337 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3338 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3340 for (i
= 0; i
< 5; i
++)
3342 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
]);
3345 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3347 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3351 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3352 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3354 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3355 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3356 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3358 _cupsRWUnlock(&(printer
->rwlock
));
3360 cupsArrayDelete(ra
);
3365 * 'ipp_identify_printer()' - Beep or display a message.
3369 ipp_identify_printer(
3370 _ipp_client_t
*client
) /* I - Client */
3372 /* TODO: Do something */
3374 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3379 * 'ipp_print_job()' - Create a job object with an attached document.
3383 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3385 _ipp_job_t
*job
; /* New job */
3386 char filename
[1024], /* Filename buffer */
3387 buffer
[4096]; /* Copy buffer */
3388 ssize_t bytes
; /* Bytes read */
3389 cups_array_t
*ra
; /* Attributes to send in response */
3393 * Validate print job attributes...
3396 if (!valid_job_attributes(client
))
3398 httpFlush(client
->http
);
3403 * Do we have a file to print?
3406 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3408 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3416 if ((job
= create_job(client
)) == NULL
)
3418 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3419 "Currently printing another job.");
3424 * Create a file for the request data...
3427 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3430 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3432 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3434 job
->state
= IPP_JSTATE_ABORTED
;
3436 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3437 "Unable to create print file: %s", strerror(errno
));
3441 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3443 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3445 int error
= errno
; /* Write error */
3447 job
->state
= IPP_JSTATE_ABORTED
;
3454 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3455 "Unable to write print file: %s", strerror(error
));
3463 * Got an error while reading the print data, so abort this job.
3466 job
->state
= IPP_JSTATE_ABORTED
;
3473 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3474 "Unable to read print file.");
3480 int error
= errno
; /* Write error */
3482 job
->state
= IPP_JSTATE_ABORTED
;
3487 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3488 "Unable to write print file: %s", strerror(error
));
3493 job
->filename
= strdup(filename
);
3494 job
->state
= IPP_JSTATE_PENDING
;
3497 * Process the job...
3500 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3502 job
->state
= IPP_JSTATE_ABORTED
;
3503 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3508 * Return the job info...
3511 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3513 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3514 cupsArrayAdd(ra
, "job-id");
3515 cupsArrayAdd(ra
, "job-state");
3516 cupsArrayAdd(ra
, "job-state-message");
3517 cupsArrayAdd(ra
, "job-state-reasons");
3518 cupsArrayAdd(ra
, "job-uri");
3520 copy_job_attributes(client
, job
, ra
);
3521 cupsArrayDelete(ra
);
3526 * 'ipp_print_uri()' - Create a job object with a referenced document.
3530 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3532 _ipp_job_t
*job
; /* New job */
3533 ipp_attribute_t
*uri
; /* document-uri */
3534 char scheme
[256], /* URI scheme */
3535 userpass
[256], /* Username and password info */
3536 hostname
[256], /* Hostname */
3537 resource
[1024]; /* Resource path */
3538 int port
; /* Port number */
3539 http_uri_status_t uri_status
; /* URI decode status */
3540 http_encryption_t encryption
; /* Encryption to use, if any */
3541 http_t
*http
; /* Connection for http/https URIs */
3542 http_status_t status
; /* Access status for http/https URIs */
3543 int infile
; /* Input file for local file URIs */
3544 char filename
[1024], /* Filename buffer */
3545 buffer
[4096]; /* Copy buffer */
3546 ssize_t bytes
; /* Bytes read */
3547 cups_array_t
*ra
; /* Attributes to send in response */
3548 static const char * const uri_status_strings
[] =
3549 { /* URI decode errors */
3551 "Bad arguments to function.",
3552 "Bad resource in URI.",
3553 "Bad port number in URI.",
3554 "Bad hostname in URI.",
3555 "Bad username in URI.",
3556 "Bad scheme in URI.",
3562 * Validate print job attributes...
3565 if (!valid_job_attributes(client
))
3567 httpFlush(client
->http
);
3572 * Do we have a file to print?
3575 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3577 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3578 "Unexpected document data following request.");
3583 * Do we have a document URI?
3586 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3587 IPP_TAG_URI
)) == NULL
)
3589 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3593 if (ippGetCount(uri
) != 1)
3595 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3596 "Too many document-uri values.");
3600 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3601 scheme
, sizeof(scheme
), userpass
,
3602 sizeof(userpass
), hostname
, sizeof(hostname
),
3603 &port
, resource
, sizeof(resource
));
3604 if (uri_status
< HTTP_URI_STATUS_OK
)
3606 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3607 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3611 if (strcmp(scheme
, "file") &&
3613 strcmp(scheme
, "https") &&
3614 #endif /* HAVE_SSL */
3615 strcmp(scheme
, "http"))
3617 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3618 "URI scheme \"%s\" not supported.", scheme
);
3622 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3624 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3625 "Unable to access URI: %s", strerror(errno
));
3633 if ((job
= create_job(client
)) == NULL
)
3635 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3636 "Currently printing another job.");
3641 * Create a file for the request data...
3644 if (!strcasecmp(job
->format
, "image/jpeg"))
3645 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3646 client
->printer
->directory
, job
->id
);
3647 else if (!strcasecmp(job
->format
, "image/png"))
3648 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3649 client
->printer
->directory
, job
->id
);
3650 else if (!strcasecmp(job
->format
, "application/pdf"))
3651 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3652 client
->printer
->directory
, job
->id
);
3653 else if (!strcasecmp(job
->format
, "application/postscript"))
3654 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3655 client
->printer
->directory
, job
->id
);
3657 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3658 client
->printer
->directory
, job
->id
);
3660 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3662 job
->state
= IPP_JSTATE_ABORTED
;
3664 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3665 "Unable to create print file: %s", strerror(errno
));
3669 if (!strcmp(scheme
, "file"))
3671 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3673 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3674 "Unable to access URI: %s", strerror(errno
));
3680 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3681 (errno
== EAGAIN
|| errno
== EINTR
))
3683 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3685 int error
= errno
; /* Write error */
3687 job
->state
= IPP_JSTATE_ABORTED
;
3695 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3696 "Unable to write print file: %s", strerror(error
));
3707 if (port
== 443 || !strcmp(scheme
, "https"))
3708 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3710 #endif /* HAVE_SSL */
3711 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3713 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3714 1, 30000, NULL
)) == NULL
)
3716 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3717 "Unable to connect to %s: %s", hostname
,
3718 cupsLastErrorString());
3719 job
->state
= IPP_JSTATE_ABORTED
;
3728 httpClearFields(http
);
3729 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3730 if (httpGet(http
, resource
))
3732 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3733 "Unable to GET URI: %s", strerror(errno
));
3735 job
->state
= IPP_JSTATE_ABORTED
;
3745 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3747 if (status
!= HTTP_STATUS_OK
)
3749 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3750 "Unable to GET URI: %s", httpStatus(status
));
3752 job
->state
= IPP_JSTATE_ABORTED
;
3762 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3764 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3766 int error
= errno
; /* Write error */
3768 job
->state
= IPP_JSTATE_ABORTED
;
3776 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3777 "Unable to write print file: %s", strerror(error
));
3787 int error
= errno
; /* Write error */
3789 job
->state
= IPP_JSTATE_ABORTED
;
3794 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3795 "Unable to write print file: %s", strerror(error
));
3800 job
->filename
= strdup(filename
);
3801 job
->state
= IPP_JSTATE_PENDING
;
3804 * Process the job...
3808 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3810 job
->state
= IPP_JSTATE_ABORTED
;
3811 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3820 * Return the job info...
3823 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3825 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3826 cupsArrayAdd(ra
, "job-id");
3827 cupsArrayAdd(ra
, "job-state");
3828 cupsArrayAdd(ra
, "job-state-reasons");
3829 cupsArrayAdd(ra
, "job-uri");
3831 copy_job_attributes(client
, job
, ra
);
3832 cupsArrayDelete(ra
);
3837 * 'ipp_send_document()' - Add an attached document to a job object created with
3842 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3844 _ipp_job_t
*job
; /* Job information */
3845 char filename
[1024], /* Filename buffer */
3846 buffer
[4096]; /* Copy buffer */
3847 ssize_t bytes
; /* Bytes read */
3848 ipp_attribute_t
*attr
; /* Current attribute */
3849 cups_array_t
*ra
; /* Attributes to send in response */
3856 if ((job
= find_job(client
)) == NULL
)
3858 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3859 httpFlush(client
->http
);
3864 * See if we already have a document for this job or the job has already
3865 * in a non-pending state...
3868 if (job
->state
> IPP_JSTATE_HELD
)
3870 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3871 "Job is not in a pending state.");
3872 httpFlush(client
->http
);
3875 else if (job
->filename
|| job
->fd
>= 0)
3877 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3878 "Multiple document jobs are not supported.");
3879 httpFlush(client
->http
);
3883 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3884 IPP_TAG_ZERO
)) == NULL
)
3886 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3887 "Missing required last-document attribute.");
3888 httpFlush(client
->http
);
3891 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3892 !ippGetBoolean(attr
, 0))
3894 respond_unsupported(client
, attr
);
3895 httpFlush(client
->http
);
3900 * Validate document attributes...
3903 if (!valid_doc_attributes(client
))
3905 httpFlush(client
->http
);
3909 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3912 * Get the document format for the job...
3915 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3917 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3918 job
->format
= ippGetString(attr
, 0, NULL
);
3919 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3920 job
->format
= ippGetString(attr
, 0, NULL
);
3922 job
->format
= "application/octet-stream";
3925 * Create a file for the request data...
3928 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3931 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3933 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3935 _cupsRWUnlock(&(client
->printer
->rwlock
));
3939 job
->state
= IPP_JSTATE_ABORTED
;
3941 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3942 "Unable to create print file: %s", strerror(errno
));
3946 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3948 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3950 int error
= errno
; /* Write error */
3952 job
->state
= IPP_JSTATE_ABORTED
;
3959 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3960 "Unable to write print file: %s", strerror(error
));
3968 * Got an error while reading the print data, so abort this job.
3971 job
->state
= IPP_JSTATE_ABORTED
;
3978 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3979 "Unable to read print file.");
3985 int error
= errno
; /* Write error */
3987 job
->state
= IPP_JSTATE_ABORTED
;
3992 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3993 "Unable to write print file: %s", strerror(error
));
3997 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4000 job
->filename
= strdup(filename
);
4001 job
->state
= IPP_JSTATE_PENDING
;
4003 _cupsRWUnlock(&(client
->printer
->rwlock
));
4006 * Process the job...
4010 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4012 job
->state
= IPP_JSTATE_ABORTED
;
4013 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4022 * Return the job info...
4025 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4027 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4028 cupsArrayAdd(ra
, "job-id");
4029 cupsArrayAdd(ra
, "job-state");
4030 cupsArrayAdd(ra
, "job-state-reasons");
4031 cupsArrayAdd(ra
, "job-uri");
4033 copy_job_attributes(client
, job
, ra
);
4034 cupsArrayDelete(ra
);
4039 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4044 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
4046 _ipp_job_t
*job
; /* Job information */
4047 ipp_attribute_t
*uri
; /* document-uri */
4048 char scheme
[256], /* URI scheme */
4049 userpass
[256], /* Username and password info */
4050 hostname
[256], /* Hostname */
4051 resource
[1024]; /* Resource path */
4052 int port
; /* Port number */
4053 http_uri_status_t uri_status
; /* URI decode status */
4054 http_encryption_t encryption
; /* Encryption to use, if any */
4055 http_t
*http
; /* Connection for http/https URIs */
4056 http_status_t status
; /* Access status for http/https URIs */
4057 int infile
; /* Input file for local file URIs */
4058 char filename
[1024], /* Filename buffer */
4059 buffer
[4096]; /* Copy buffer */
4060 ssize_t bytes
; /* Bytes read */
4061 ipp_attribute_t
*attr
; /* Current attribute */
4062 cups_array_t
*ra
; /* Attributes to send in response */
4063 static const char * const uri_status_strings
[] =
4064 { /* URI decode errors */
4066 "Bad arguments to function.",
4067 "Bad resource in URI.",
4068 "Bad port number in URI.",
4069 "Bad hostname in URI.",
4070 "Bad username in URI.",
4071 "Bad scheme in URI.",
4080 if ((job
= find_job(client
)) == NULL
)
4082 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4083 httpFlush(client
->http
);
4088 * See if we already have a document for this job or the job has already
4089 * in a non-pending state...
4092 if (job
->state
> IPP_JSTATE_HELD
)
4094 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4095 "Job is not in a pending state.");
4096 httpFlush(client
->http
);
4099 else if (job
->filename
|| job
->fd
>= 0)
4101 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4102 "Multiple document jobs are not supported.");
4103 httpFlush(client
->http
);
4107 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4108 IPP_TAG_ZERO
)) == NULL
)
4110 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4111 "Missing required last-document attribute.");
4112 httpFlush(client
->http
);
4115 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4116 !ippGetBoolean(attr
, 0))
4118 respond_unsupported(client
, attr
);
4119 httpFlush(client
->http
);
4124 * Validate document attributes...
4127 if (!valid_doc_attributes(client
))
4129 httpFlush(client
->http
);
4134 * Do we have a file to print?
4137 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4139 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4140 "Unexpected document data following request.");
4145 * Do we have a document URI?
4148 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4149 IPP_TAG_URI
)) == NULL
)
4151 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4155 if (ippGetCount(uri
) != 1)
4157 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4158 "Too many document-uri values.");
4162 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4163 scheme
, sizeof(scheme
), userpass
,
4164 sizeof(userpass
), hostname
, sizeof(hostname
),
4165 &port
, resource
, sizeof(resource
));
4166 if (uri_status
< HTTP_URI_STATUS_OK
)
4168 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4169 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4173 if (strcmp(scheme
, "file") &&
4175 strcmp(scheme
, "https") &&
4176 #endif /* HAVE_SSL */
4177 strcmp(scheme
, "http"))
4179 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4180 "URI scheme \"%s\" not supported.", scheme
);
4184 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4186 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4187 "Unable to access URI: %s", strerror(errno
));
4192 * Get the document format for the job...
4195 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4197 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4198 IPP_TAG_MIMETYPE
)) != NULL
)
4199 job
->format
= ippGetString(attr
, 0, NULL
);
4201 job
->format
= "application/octet-stream";
4204 * Create a file for the request data...
4207 if (!strcasecmp(job
->format
, "image/jpeg"))
4208 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4209 client
->printer
->directory
, job
->id
);
4210 else if (!strcasecmp(job
->format
, "image/png"))
4211 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4212 client
->printer
->directory
, job
->id
);
4213 else if (!strcasecmp(job
->format
, "application/pdf"))
4214 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4215 client
->printer
->directory
, job
->id
);
4216 else if (!strcasecmp(job
->format
, "application/postscript"))
4217 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4218 client
->printer
->directory
, job
->id
);
4220 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4221 client
->printer
->directory
, job
->id
);
4223 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4225 _cupsRWUnlock(&(client
->printer
->rwlock
));
4229 job
->state
= IPP_JSTATE_ABORTED
;
4231 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4232 "Unable to create print file: %s", strerror(errno
));
4236 if (!strcmp(scheme
, "file"))
4238 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4240 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4241 "Unable to access URI: %s", strerror(errno
));
4247 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4248 (errno
== EAGAIN
|| errno
== EINTR
))
4250 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4252 int error
= errno
; /* Write error */
4254 job
->state
= IPP_JSTATE_ABORTED
;
4262 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4263 "Unable to write print file: %s", strerror(error
));
4274 if (port
== 443 || !strcmp(scheme
, "https"))
4275 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4277 #endif /* HAVE_SSL */
4278 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4280 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4281 1, 30000, NULL
)) == NULL
)
4283 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4284 "Unable to connect to %s: %s", hostname
,
4285 cupsLastErrorString());
4286 job
->state
= IPP_JSTATE_ABORTED
;
4295 httpClearFields(http
);
4296 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4297 if (httpGet(http
, resource
))
4299 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4300 "Unable to GET URI: %s", strerror(errno
));
4302 job
->state
= IPP_JSTATE_ABORTED
;
4312 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4314 if (status
!= HTTP_STATUS_OK
)
4316 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4317 "Unable to GET URI: %s", httpStatus(status
));
4319 job
->state
= IPP_JSTATE_ABORTED
;
4329 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4331 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4333 int error
= errno
; /* Write error */
4335 job
->state
= IPP_JSTATE_ABORTED
;
4343 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4344 "Unable to write print file: %s", strerror(error
));
4354 int error
= errno
; /* Write error */
4356 job
->state
= IPP_JSTATE_ABORTED
;
4361 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4362 "Unable to write print file: %s", strerror(error
));
4366 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4369 job
->filename
= strdup(filename
);
4370 job
->state
= IPP_JSTATE_PENDING
;
4372 _cupsRWUnlock(&(client
->printer
->rwlock
));
4375 * Process the job...
4379 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4381 job
->state
= IPP_JSTATE_ABORTED
;
4382 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4391 * Return the job info...
4394 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4396 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4397 cupsArrayAdd(ra
, "job-id");
4398 cupsArrayAdd(ra
, "job-state");
4399 cupsArrayAdd(ra
, "job-state-reasons");
4400 cupsArrayAdd(ra
, "job-uri");
4402 copy_job_attributes(client
, job
, ra
);
4403 cupsArrayDelete(ra
);
4408 * 'ipp_validate_job()' - Validate job creation attributes.
4412 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4414 if (valid_job_attributes(client
))
4415 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4420 * 'parse_options()' - Parse URL options into CUPS options.
4422 * The client->options string is destroyed by this function.
4425 static int /* O - Number of options */
4426 parse_options(_ipp_client_t
*client
, /* I - Client */
4427 cups_option_t
**options
) /* O - Options */
4429 char *name
, /* Name */
4431 *next
; /* Next name=value pair */
4432 int num_options
= 0; /* Number of options */
4437 for (name
= client
->options
; name
&& *name
; name
= next
)
4439 if ((value
= strchr(name
, '=')) == NULL
)
4443 if ((next
= strchr(value
, '&')) != NULL
)
4446 num_options
= cupsAddOption(name
, value
, num_options
, options
);
4449 return (num_options
);
4454 * 'process_client()' - Process client requests on a thread.
4457 static void * /* O - Exit status */
4458 process_client(_ipp_client_t
*client
) /* I - Client */
4461 * Loop until we are out of requests or timeout (30 seconds)...
4465 int first_time
= 1; /* First time request? */
4466 #endif /* HAVE_SSL */
4468 while (httpWait(client
->http
, 30000))
4474 * See if we need to negotiate a TLS connection...
4477 char buf
[1]; /* First byte from client */
4479 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
4481 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
4483 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
4485 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4489 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4494 #endif /* HAVE_SSL */
4496 if (!process_http(client
))
4501 * Close the conection to the client and return...
4504 delete_client(client
);
4511 * 'process_http()' - Process a HTTP request.
4514 int /* O - 1 on success, 0 on failure */
4515 process_http(_ipp_client_t
*client
) /* I - Client connection */
4517 char uri
[1024]; /* URI */
4518 http_state_t http_state
; /* HTTP state */
4519 http_status_t http_status
; /* HTTP status */
4520 ipp_state_t ipp_state
; /* State of IPP transfer */
4521 char scheme
[32], /* Method/scheme */
4522 userpass
[128], /* Username:password */
4523 hostname
[HTTP_MAX_HOST
];
4525 int port
; /* Port number */
4526 const char *encoding
; /* Content-Encoding value */
4527 static const char * const http_states
[] =
4528 { /* Strings for logging HTTP method */
4549 * Clear state variables...
4552 ippDelete(client
->request
);
4553 ippDelete(client
->response
);
4555 client
->request
= NULL
;
4556 client
->response
= NULL
;
4557 client
->operation
= HTTP_STATE_WAITING
;
4560 * Read a request from the connection...
4563 while ((http_state
= httpReadRequest(client
->http
, uri
,
4564 sizeof(uri
))) == HTTP_STATE_WAITING
)
4568 * Parse the request line...
4571 if (http_state
== HTTP_STATE_ERROR
)
4573 if (httpError(client
->http
) == EPIPE
)
4574 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
4576 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
4577 strerror(httpError(client
->http
)));
4581 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
4583 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
4584 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4587 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
4589 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
4590 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4594 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
4598 * Separate the URI into its components...
4601 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4602 userpass
, sizeof(userpass
),
4603 hostname
, sizeof(hostname
), &port
,
4604 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
4605 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
4607 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
4608 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4612 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
4613 *(client
->options
)++ = '\0';
4616 * Process the request...
4619 client
->start
= time(NULL
);
4620 client
->operation
= httpGetState(client
->http
);
4623 * Parse incoming parameters until the status changes...
4626 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4628 if (http_status
!= HTTP_STATUS_OK
)
4630 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4634 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4635 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4638 * HTTP/1.1 and higher require the "Host:" field...
4641 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4646 * Handle HTTP Upgrade...
4649 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4653 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
4655 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
4658 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
4660 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
4662 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4666 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4669 #endif /* HAVE_SSL */
4671 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4676 * Handle HTTP Expect...
4679 if (httpGetExpect(client
->http
) &&
4680 (client
->operation
== HTTP_STATE_POST
||
4681 client
->operation
== HTTP_STATE_PUT
))
4683 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4686 * Send 100-continue header...
4689 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4695 * Send 417-expectation-failed header...
4698 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4704 * Handle new transfers...
4707 encoding
= httpGetContentEncoding(client
->http
);
4709 switch (client
->operation
)
4711 case HTTP_STATE_OPTIONS
:
4713 * Do OPTIONS command...
4716 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4718 case HTTP_STATE_HEAD
:
4719 if (!strcmp(client
->uri
, "/icon.png"))
4720 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4721 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
4722 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4724 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4726 case HTTP_STATE_GET
:
4727 if (!strcmp(client
->uri
, "/icon.png"))
4730 * Send PNG icon file.
4733 int fd
; /* Icon file */
4734 struct stat fileinfo
; /* Icon file information */
4735 char buffer
[4096]; /* Copy buffer */
4736 ssize_t bytes
; /* Bytes */
4738 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
4740 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4741 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4743 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
4744 (size_t)fileinfo
.st_size
))
4750 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4751 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
4753 httpFlushWrite(client
->http
);
4758 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4760 else if (!strcmp(client
->uri
, "/"))
4763 * Show web status page...
4766 _ipp_job_t
*job
; /* Current job */
4767 int i
; /* Looping var */
4768 _ipp_preason_t reason
; /* Current reason */
4769 static const char * const reasons
[] =
4770 { /* Reason strings */
4773 "Input Tray Missing",
4774 "Marker Supply Empty",
4775 "Marker Supply Low",
4776 "Marker Waste Almost Full",
4777 "Marker Waste Full",
4789 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4792 html_header(client
, client
->printer
->name
);
4794 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
4795 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
4796 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
4797 if (client
->printer
->state_reasons
& reason
)
4798 html_printf(client
, "\n<br> %s", reasons
[i
]);
4799 html_printf(client
, "</p>\n");
4801 if (cupsArrayCount(client
->printer
->jobs
) > 0)
4803 _cupsRWLockRead(&(client
->printer
->rwlock
));
4805 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");
4806 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
4808 char when
[256], /* When job queued/started/finished */
4809 hhmmss
[64]; /* Time HH:MM:SS */
4813 case IPP_JSTATE_PENDING
:
4814 case IPP_JSTATE_HELD
:
4815 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
4817 case IPP_JSTATE_PROCESSING
:
4818 case IPP_JSTATE_STOPPED
:
4819 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
4821 case IPP_JSTATE_ABORTED
:
4822 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4824 case IPP_JSTATE_CANCELED
:
4825 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4827 case IPP_JSTATE_COMPLETED
:
4828 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4832 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
);
4834 html_printf(client
, "</tbody></table>\n");
4836 _cupsRWUnlock(&(client
->printer
->rwlock
));
4838 html_footer(client
);
4842 else if (!strcmp(client
->uri
, "/media"))
4845 * Show web media page...
4848 int i
, /* Looping var */
4849 num_options
; /* Number of form options */
4850 cups_option_t
*options
; /* Form options */
4851 static const char * const sizes
[] =
4852 { /* Size strings */
4865 static const char * const types
[] =
4882 static const int sheets
[] = /* Number of sheets */
4891 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4894 html_header(client
, client
->printer
->name
);
4896 if ((num_options
= parse_options(client
, &options
)) > 0)
4899 * WARNING: A real printer/server implementation MUST NOT implement
4900 * media updates via a GET request - GET requests are supposed to be
4901 * idempotent (without side-effects) and we obviously are not
4902 * authenticating access here. This form is provided solely to
4903 * enable testing and development!
4906 const char *val
; /* Form value */
4908 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
4909 client
->printer
->main_size
= atoi(val
);
4910 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
4911 client
->printer
->main_type
= atoi(val
);
4912 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
4913 client
->printer
->main_level
= atoi(val
);
4915 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
4916 client
->printer
->envelope_size
= atoi(val
);
4917 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
4918 client
->printer
->envelope_level
= atoi(val
);
4920 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
4921 client
->printer
->photo_size
= atoi(val
);
4922 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
4923 client
->printer
->photo_type
= atoi(val
);
4924 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
4925 client
->printer
->photo_level
= atoi(val
);
4927 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))
4928 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
4930 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
4932 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
))
4934 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
4935 if (client
->printer
->active_job
)
4936 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
4939 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
4941 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
4944 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
4946 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
4947 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
4948 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4949 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
4950 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
4951 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
4952 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4953 if (!strstr(types
[i
], "Photo"))
4954 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
4955 html_printf(client
, "</select> <select name=\"main_level\">");
4956 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4957 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
4958 html_printf(client
, "</select></td></tr>\n");
4961 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
4962 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4963 if (strstr(sizes
[i
], "Envelope"))
4964 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
4965 html_printf(client
, "</select> <select name=\"envelope_level\">");
4966 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4967 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
4968 html_printf(client
, "</select></td></tr>\n");
4971 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
4972 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4973 if (strstr(sizes
[i
], "Photo"))
4974 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
4975 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
4976 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4977 if (strstr(types
[i
], "Photo"))
4978 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
4979 html_printf(client
, "</select> <select name=\"photo_level\">");
4980 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4981 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
4982 html_printf(client
, "</select></td></tr>\n");
4984 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
4985 html_footer(client
);
4989 else if (!strcmp(client
->uri
, "/supplies"))
4992 * Show web supplies page...
4995 int i
, j
, /* Looping vars */
4996 num_options
; /* Number of form options */
4997 cups_option_t
*options
; /* Form options */
4998 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5000 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5003 html_header(client
, client
->printer
->name
);
5005 if ((num_options
= parse_options(client
, &options
)) > 0)
5008 * WARNING: A real printer/server implementation MUST NOT implement
5009 * supply updates via a GET request - GET requests are supposed to be
5010 * idempotent (without side-effects) and we obviously are not
5011 * authenticating access here. This form is provided solely to
5012 * enable testing and development!
5015 char name
[64]; /* Form field */
5016 const char *val
; /* Form value */
5018 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
);
5020 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5022 snprintf(name
, sizeof(name
), "supply_%d", i
);
5023 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5025 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5031 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
5032 else if (level
< 10)
5033 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
5038 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
5039 else if (level
> 90)
5040 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
5045 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5048 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5050 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5051 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5053 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5054 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5055 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5056 html_printf(client
, "</select></td></tr>\n");
5058 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5059 html_footer(client
);
5064 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5067 case HTTP_STATE_POST
:
5068 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5072 * Not an IPP request...
5075 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5079 * Read the IPP request...
5082 client
->request
= ippNew();
5084 while ((ipp_state
= ippRead(client
->http
,
5085 client
->request
)) != IPP_STATE_DATA
)
5087 if (ipp_state
== IPP_STATE_ERROR
)
5089 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5090 cupsLastErrorString());
5091 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5097 * Now that we have the IPP request, process the request...
5100 return (process_ipp(client
));
5103 break; /* Anti-compiler-warning-code */
5111 * 'process_ipp()' - Process an IPP request.
5114 static int /* O - 1 on success, 0 on error */
5115 process_ipp(_ipp_client_t
*client
) /* I - Client */
5117 ipp_tag_t group
; /* Current group tag */
5118 ipp_attribute_t
*attr
; /* Current attribute */
5119 ipp_attribute_t
*charset
; /* Character set attribute */
5120 ipp_attribute_t
*language
; /* Language attribute */
5121 ipp_attribute_t
*uri
; /* Printer URI attribute */
5122 int major
, minor
; /* Version number */
5123 const char *name
; /* Name of attribute */
5126 debug_attributes("Request", client
->request
, 1);
5129 * First build an empty response message for this request...
5132 client
->operation_id
= ippGetOperation(client
->request
);
5133 client
->response
= ippNewResponse(client
->request
);
5136 * Then validate the request header and required attributes...
5139 major
= ippGetVersion(client
->request
, &minor
);
5141 if (major
< 1 || major
> 2)
5144 * Return an error, since we only support IPP 1.x and 2.x.
5147 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
5148 "Bad request version number %d.%d.", major
, minor
);
5150 else if (ippGetRequestId(client
->request
) <= 0)
5151 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
5152 ippGetRequestId(client
->request
));
5153 else if (!ippFirstAttribute(client
->request
))
5154 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5155 "No attributes in request.");
5159 * Make sure that the attributes are provided in the correct order and
5160 * don't repeat groups...
5163 for (attr
= ippFirstAttribute(client
->request
),
5164 group
= ippGetGroupTag(attr
);
5166 attr
= ippNextAttribute(client
->request
))
5168 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5171 * Out of order; return an error...
5174 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5175 "Attribute groups are out of order (%x < %x).",
5176 ippGetGroupTag(attr
), group
);
5180 group
= ippGetGroupTag(attr
);
5186 * Then make sure that the first three attributes are:
5188 * attributes-charset
5189 * attributes-natural-language
5190 * printer-uri/job-uri
5193 attr
= ippFirstAttribute(client
->request
);
5194 name
= ippGetName(attr
);
5195 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5196 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5201 attr
= ippNextAttribute(client
->request
);
5202 name
= ippGetName(attr
);
5204 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5205 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5210 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5211 IPP_TAG_URI
)) != NULL
)
5213 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5214 IPP_TAG_URI
)) != NULL
)
5220 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5221 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5224 * Bad character set...
5227 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5228 "Unsupported character set \"%s\".",
5229 ippGetString(charset
, 0, NULL
));
5231 else if (!charset
|| !language
|| !uri
)
5234 * Return an error, since attributes-charset,
5235 * attributes-natural-language, and printer-uri/job-uri are required
5236 * for all operations.
5239 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5240 "Missing required attributes.");
5244 char scheme
[32], /* URI scheme */
5245 userpass
[32], /* Username/password in URI */
5246 host
[256], /* Host name in URI */
5247 resource
[256]; /* Resource path in URI */
5248 int port
; /* Port number in URI */
5250 name
= ippGetName(uri
);
5252 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5253 scheme
, sizeof(scheme
),
5254 userpass
, sizeof(userpass
),
5255 host
, sizeof(host
), &port
,
5256 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5257 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5258 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5259 else if ((!strcmp(name
, "job-uri") &&
5260 strncmp(resource
, "/ipp/print/", 11)) ||
5261 (!strcmp(name
, "printer-uri") &&
5262 strcmp(resource
, "/ipp/print")))
5263 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5264 name
, ippGetString(uri
, 0, NULL
));
5268 * Try processing the operation...
5271 switch (ippGetOperation(client
->request
))
5273 case IPP_OP_PRINT_JOB
:
5274 ipp_print_job(client
);
5277 case IPP_OP_PRINT_URI
:
5278 ipp_print_uri(client
);
5281 case IPP_OP_VALIDATE_JOB
:
5282 ipp_validate_job(client
);
5285 case IPP_OP_CREATE_JOB
:
5286 ipp_create_job(client
);
5289 case IPP_OP_SEND_DOCUMENT
:
5290 ipp_send_document(client
);
5293 case IPP_OP_SEND_URI
:
5294 ipp_send_uri(client
);
5297 case IPP_OP_CANCEL_JOB
:
5298 ipp_cancel_job(client
);
5301 case IPP_OP_GET_JOB_ATTRIBUTES
:
5302 ipp_get_job_attributes(client
);
5305 case IPP_OP_GET_JOBS
:
5306 ipp_get_jobs(client
);
5309 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5310 ipp_get_printer_attributes(client
);
5313 case IPP_OP_CLOSE_JOB
:
5314 ipp_close_job(client
);
5317 case IPP_OP_IDENTIFY_PRINTER
:
5318 ipp_identify_printer(client
);
5322 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5323 "Operation not supported.");
5332 * Send the HTTP header and return...
5335 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5336 httpFlush(client
->http
); /* Flush trailing (junk) data */
5338 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5339 ippLength(client
->response
)));
5344 * 'process_job()' - Process a print job.
5347 static void * /* O - Thread exit status */
5348 process_job(_ipp_job_t
*job
) /* I - Job */
5350 job
->state
= IPP_JSTATE_PROCESSING
;
5351 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
5352 job
->processing
= time(NULL
);
5354 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
5356 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5361 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
5363 if (job
->printer
->command
)
5366 * Execute a command with the job spool file and wait for it to complete...
5369 int pid
, /* Process ID */
5370 status
; /* Exit status */
5371 time_t start
, /* Start time */
5373 char *myargv
[3], /* Command-line arguments */
5374 *myenvp
[200]; /* Environment variables */
5375 int myenvc
; /* Number of environment variables */
5376 ipp_attribute_t
*attr
; /* Job attribute */
5377 char val
[1280], /* IPP_NAME=value */
5378 *valptr
; /* Pointer into string */
5380 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
5385 * Setup the command-line arguments...
5388 myargv
[0] = job
->printer
->command
;
5389 myargv
[1] = job
->filename
;
5393 * Copy the current environment, then add ENV variables for every Job
5397 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
5398 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
5400 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
5403 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
5404 * value(s) from the attribute.
5407 const char *name
= ippGetName(attr
);
5416 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
5421 *valptr
++ = (char)toupper(*name
& 255);
5426 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
5428 myenvp
[myenvc
++] = strdup(val
);
5430 myenvp
[myenvc
] = NULL
;
5433 * Now run the program...
5437 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
5439 if ((pid
= fork()) == 0)
5442 * Child comes here...
5445 execve(job
->printer
->command
, myargv
, myenvp
);
5451 * Unable to fork process...
5454 perror("Unable to start job processing command");
5460 * Free memory used for environment...
5464 free(myenvp
[-- myenvc
]);
5467 * Wait for child to complete...
5470 # ifdef HAVE_WAITPID
5471 while (waitpid(pid
, &status
, 0) < 0);
5473 while (wait(&status
) < 0);
5474 # endif /* HAVE_WAITPID */
5481 if (WIFEXITED(status
))
5483 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
5484 job
->printer
->command
, WEXITSTATUS(status
));
5487 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
5488 job
->printer
->command
, WTERMSIG(status
));
5490 job
->state
= IPP_JSTATE_ABORTED
;
5492 else if (status
< 0)
5493 job
->state
= IPP_JSTATE_ABORTED
;
5495 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
5496 job
->printer
->command
);
5499 * Make sure processing takes at least 5 seconds...
5503 if ((end
- start
) < 5)
5509 * Sleep for a random amount of time to simulate job processing.
5512 sleep((unsigned)(5 + (rand() % 11)));
5516 job
->state
= IPP_JSTATE_CANCELED
;
5517 else if (job
->state
== IPP_JSTATE_PROCESSING
)
5518 job
->state
= IPP_JSTATE_COMPLETED
;
5520 job
->completed
= time(NULL
);
5521 job
->printer
->state
= IPP_PSTATE_IDLE
;
5522 job
->printer
->active_job
= NULL
;
5529 * 'register_printer()' - Register a printer object via Bonjour.
5532 static int /* O - 1 on success, 0 on error */
5534 _ipp_printer_t
*printer
, /* I - Printer */
5535 const char *location
, /* I - Location */
5536 const char *make
, /* I - Manufacturer */
5537 const char *model
, /* I - Model name */
5538 const char *formats
, /* I - Supported formats */
5539 const char *adminurl
, /* I - Web interface URL */
5540 const char *uuid
, /* I - Printer UUID */
5541 int color
, /* I - 1 = color, 0 = monochrome */
5542 int duplex
, /* I - 1 = duplex, 0 = simplex */
5543 const char *subtype
) /* I - Service subtype */
5545 _ipp_txt_t ipp_txt
; /* Bonjour IPP TXT record */
5547 DNSServiceErrorType error
; /* Error from Bonjour */
5548 char make_model
[256],/* Make and model together */
5549 product
[256], /* Product string */
5550 regtype
[256]; /* Bonjour service type */
5554 * Build the TXT record for IPP...
5557 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
5558 snprintf(product
, sizeof(product
), "(%s)", model
);
5560 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
5561 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
5562 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(make_model
),
5564 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(adminurl
),
5567 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(location
),
5569 TXTRecordSetValue(&ipp_txt
, "product", (uint8_t)strlen(product
),
5571 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
),
5573 TXTRecordSetValue(&ipp_txt
, "Color", 1, color
? "T" : "F");
5574 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, duplex
? "T" : "F");
5575 TXTRecordSetValue(&ipp_txt
, "usb_MFG", (uint8_t)strlen(make
),
5577 TXTRecordSetValue(&ipp_txt
, "usb_MDL", (uint8_t)strlen(model
),
5579 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(uuid
), uuid
);
5581 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
5582 # endif /* HAVE_SSL */
5585 * Register the _printer._tcp (LPD) service type with a port number of 0 to
5586 * defend our service name but not actually support LPD...
5589 printer
->printer_ref
= DNSSDMaster
;
5591 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
5592 kDNSServiceFlagsShareConnection
,
5593 0 /* interfaceIndex */, printer
->dnssd_name
,
5594 "_printer._tcp", NULL
/* domain */,
5595 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
5596 NULL
/* txtRecord */,
5597 (DNSServiceRegisterReply
)dnssd_callback
,
5598 printer
)) != kDNSServiceErr_NoError
)
5600 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
5601 printer
->dnssd_name
, error
);
5606 * Then register the _ipp._tcp (IPP) service type with the real port number to
5607 * advertise our IPP printer...
5610 printer
->ipp_ref
= DNSSDMaster
;
5612 if (subtype
&& *subtype
)
5613 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
5615 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
5617 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
5618 kDNSServiceFlagsShareConnection
,
5619 0 /* interfaceIndex */, printer
->dnssd_name
,
5620 regtype
, NULL
/* domain */,
5621 NULL
/* host */, htons(printer
->port
),
5622 TXTRecordGetLength(&ipp_txt
),
5623 TXTRecordGetBytesPtr(&ipp_txt
),
5624 (DNSServiceRegisterReply
)dnssd_callback
,
5625 printer
)) != kDNSServiceErr_NoError
)
5627 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5628 printer
->dnssd_name
, regtype
, error
);
5634 * Then register the _ipps._tcp (IPP) service type with the real port number to
5635 * advertise our IPPS printer...
5638 printer
->ipps_ref
= DNSSDMaster
;
5640 if (subtype
&& *subtype
)
5641 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
5643 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
5645 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
5646 kDNSServiceFlagsShareConnection
,
5647 0 /* interfaceIndex */, printer
->dnssd_name
,
5648 regtype
, NULL
/* domain */,
5649 NULL
/* host */, htons(printer
->port
),
5650 TXTRecordGetLength(&ipp_txt
),
5651 TXTRecordGetBytesPtr(&ipp_txt
),
5652 (DNSServiceRegisterReply
)dnssd_callback
,
5653 printer
)) != kDNSServiceErr_NoError
)
5655 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5656 printer
->dnssd_name
, regtype
, error
);
5659 # endif /* HAVE_SSL */
5662 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
5663 * real port number to advertise our IPP printer...
5666 printer
->http_ref
= DNSSDMaster
;
5668 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
5669 kDNSServiceFlagsShareConnection
,
5670 0 /* interfaceIndex */, printer
->dnssd_name
,
5671 "_http._tcp,_printer", NULL
/* domain */,
5672 NULL
/* host */, htons(printer
->port
),
5673 0 /* txtLen */, NULL
, /* txtRecord */
5674 (DNSServiceRegisterReply
)dnssd_callback
,
5675 printer
)) != kDNSServiceErr_NoError
)
5677 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5678 printer
->dnssd_name
, regtype
, error
);
5682 TXTRecordDeallocate(&ipp_txt
);
5684 #elif defined(HAVE_AVAHI)
5685 char temp
[256]; /* Subtype service string */
5688 * Create the TXT record...
5692 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
5693 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s %s", make
, model
);
5694 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", adminurl
);
5696 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", location
);
5697 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "product=(%s)", model
);
5698 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
5699 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", color
? "T" : "F");
5700 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", duplex
? "T" : "F");
5701 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MFG=%s", make
);
5702 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MDL=%s", model
);
5703 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", uuid
);
5705 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
5706 # endif /* HAVE_SSL */
5709 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
5712 avahi_threaded_poll_lock(DNSSDMaster
);
5714 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
5716 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
);
5719 * Then register the _ipp._tcp (IPP)...
5722 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
);
5723 if (subtype
&& *subtype
)
5725 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
5726 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
5731 * _ipps._tcp (IPPS) for secure printing...
5734 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
);
5735 if (subtype
&& *subtype
)
5737 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
5738 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
5740 #endif /* HAVE_SSL */
5743 * Finally _http.tcp (HTTP) for the web interface...
5746 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
);
5747 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");
5753 avahi_entry_group_commit(printer
->ipp_ref
);
5754 avahi_threaded_poll_unlock(DNSSDMaster
);
5756 avahi_string_list_free(ipp_txt
);
5757 #endif /* HAVE_DNSSD */
5764 * 'respond_http()' - Send a HTTP response.
5767 int /* O - 1 on success, 0 on failure */
5769 _ipp_client_t
*client
, /* I - Client */
5770 http_status_t code
, /* I - HTTP status of response */
5771 const char *content_encoding
, /* I - Content-Encoding of response */
5772 const char *type
, /* I - MIME media type of response */
5773 size_t length
) /* I - Length of response */
5775 char message
[1024]; /* Text message */
5778 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
5780 if (code
== HTTP_STATUS_CONTINUE
)
5783 * 100-continue doesn't send any headers...
5786 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
5790 * Format an error message...
5793 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
5795 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
5797 type
= "text/plain";
5798 length
= strlen(message
);
5804 * Send the HTTP response header...
5807 httpClearFields(client
->http
);
5809 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
5810 client
->operation
== HTTP_STATE_OPTIONS
)
5811 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
5815 if (!strcmp(type
, "text/html"))
5816 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
5817 "text/html; charset=utf-8");
5819 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
5821 if (content_encoding
)
5822 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
5825 httpSetLength(client
->http
, length
);
5827 if (httpWriteResponse(client
->http
, code
) < 0)
5831 * Send the response data...
5837 * Send a plain text message.
5840 if (httpPrintf(client
->http
, "%s", message
) < 0)
5843 if (httpWrite2(client
->http
, "", 0) < 0)
5846 else if (client
->response
)
5849 * Send an IPP response...
5852 debug_attributes("Response", client
->response
, 2);
5854 ippSetState(client
->response
, IPP_STATE_IDLE
);
5856 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
5865 * 'respond_ipp()' - Send an IPP response.
5869 respond_ipp(_ipp_client_t
*client
, /* I - Client */
5870 ipp_status_t status
, /* I - status-code */
5871 const char *message
, /* I - printf-style status-message */
5872 ...) /* I - Additional args as needed */
5874 const char *formatted
= NULL
; /* Formatted message */
5877 ippSetStatusCode(client
->response
, status
);
5881 va_list ap
; /* Pointer to additional args */
5882 ipp_attribute_t
*attr
; /* New status-message attribute */
5884 va_start(ap
, message
);
5885 if ((attr
= ippFindAttribute(client
->response
, "status-message",
5886 IPP_TAG_TEXT
)) != NULL
)
5887 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
5889 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
5890 "status-message", NULL
, message
, ap
);
5893 formatted
= ippGetString(attr
, 0, NULL
);
5897 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
5898 ippOpString(client
->operation_id
), ippErrorString(status
),
5901 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
5902 ippOpString(client
->operation_id
), ippErrorString(status
));
5907 * 'respond_unsupported()' - Respond with an unsupported attribute.
5911 respond_unsupported(
5912 _ipp_client_t
*client
, /* I - Client */
5913 ipp_attribute_t
*attr
) /* I - Atribute */
5915 ipp_attribute_t
*temp
; /* Copy of attribute */
5918 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5919 "Unsupported %s %s%s value.", ippGetName(attr
),
5920 ippGetCount(attr
) > 1 ? "1setOf " : "",
5921 ippTagString(ippGetValueTag(attr
)));
5923 temp
= ippCopyAttribute(client
->response
, attr
, 0);
5924 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
5929 * 'run_printer()' - Run the printer service.
5933 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
5935 int num_fds
; /* Number of file descriptors */
5936 struct pollfd polldata
[3]; /* poll() data */
5937 int timeout
; /* Timeout for poll() */
5938 _ipp_client_t
*client
; /* New client */
5942 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5945 polldata
[0].fd
= printer
->ipv4
;
5946 polldata
[0].events
= POLLIN
;
5948 polldata
[1].fd
= printer
->ipv6
;
5949 polldata
[1].events
= POLLIN
;
5954 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
5955 polldata
[num_fds
++].events
= POLLIN
;
5956 #endif /* HAVE_DNSSD */
5959 * Loop until we are killed or have a hard error...
5964 if (cupsArrayCount(printer
->jobs
))
5969 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
5971 perror("poll() failed");
5975 if (polldata
[0].revents
& POLLIN
)
5977 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
5979 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5981 perror("Unable to create client thread");
5982 delete_client(client
);
5987 if (polldata
[1].revents
& POLLIN
)
5989 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
5991 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5993 perror("Unable to create client thread");
5994 delete_client(client
);
6000 if (polldata
[2].revents
& POLLIN
)
6001 DNSServiceProcessResult(DNSSDMaster
);
6002 #endif /* HAVE_DNSSD */
6005 * Clean out old jobs...
6008 clean_jobs(printer
);
6014 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6018 time_string(time_t tv
, /* I - Time value */
6019 char *buffer
, /* I - Buffer */
6020 size_t bufsize
) /* I - Size of buffer */
6022 struct tm
*curtime
= localtime(&tv
);
6025 strftime(buffer
, bufsize
, "%X", curtime
);
6031 * 'usage()' - Show program usage.
6035 usage(int status
) /* O - Exit status */
6039 puts(CUPS_SVERSION
" - Copyright 2010-2014 by Apple Inc. All rights "
6044 puts("Usage: ippserver [options] \"name\"");
6047 puts("-2 Supports 2-sided printing (default=1-sided)");
6048 puts("-M manufacturer Manufacturer name (default=Test)");
6049 puts("-P PIN printing mode");
6050 puts("-c command Run command for every print job");
6051 printf("-d spool-directory Spool directory "
6052 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6053 puts("-f type/subtype[,...] List of supported types "
6054 "(default=application/pdf,image/jpeg)");
6055 puts("-h Show program help");
6056 puts("-i iconfile.png PNG icon file (default=printer.png)");
6057 puts("-k Keep job spool files");
6058 puts("-l location Location of printer (default=empty string)");
6059 puts("-m model Model name (default=Printer)");
6060 puts("-n hostname Hostname for printer");
6061 puts("-p port Port number (default=auto)");
6062 puts("-r subtype Bonjour service subtype (default=_print)");
6063 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6064 puts("-v[vvv] Be (very) verbose");
6071 * 'valid_doc_attributes()' - Determine whether the document attributes are
6074 * When one or more document attributes are invalid, this function adds a
6075 * suitable response and attributes to the unsupported group.
6078 static int /* O - 1 if valid, 0 if not */
6079 valid_doc_attributes(
6080 _ipp_client_t
*client
) /* I - Client */
6082 int valid
= 1; /* Valid attributes? */
6083 ipp_op_t op
= ippGetOperation(client
->request
);
6085 const char *op_name
= ippOpString(op
);
6086 /* IPP operation name */
6087 ipp_attribute_t
*attr
, /* Current attribute */
6088 *supported
; /* xxx-supported attribute */
6089 const char *compression
= NULL
,
6090 /* compression value */
6091 *format
= NULL
; /* document-format value */
6095 * Check operation attributes...
6098 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6101 * If compression is specified, only accept a supported value in a Print-Job
6102 * or Send-Document request...
6105 compression
= ippGetString(attr
, 0, NULL
);
6106 supported
= ippFindAttribute(client
->printer
->attrs
,
6107 "compression-supported", IPP_TAG_KEYWORD
);
6109 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6110 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6111 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6112 op
!= IPP_OP_VALIDATE_JOB
) ||
6113 !ippContainsString(supported
, compression
))
6115 respond_unsupported(client
, attr
);
6120 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6122 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6124 if (strcmp(compression
, "none"))
6127 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6128 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
6134 * Is it a format we support?
6137 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
6139 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
6140 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
6142 respond_unsupported(client
, attr
);
6147 format
= ippGetString(attr
, 0, NULL
);
6149 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
6150 client
->hostname
, op_name
, format
);
6152 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
6157 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
6159 format
= "application/octet-stream"; /* Should never happen */
6161 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
6164 if (!strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
6167 * Auto-type the file using the first 8 bytes of the file...
6170 unsigned char header
[8]; /* First 8 bytes of file */
6172 memset(header
, 0, sizeof(header
));
6173 httpPeek(client
->http
, (char *)header
, sizeof(header
));
6175 if (!memcmp(header
, "%PDF", 4))
6176 format
= "application/pdf";
6177 else if (!memcmp(header
, "%!", 2))
6178 format
= "application/postscript";
6179 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
6180 format
= "image/jpeg";
6181 else if (!memcmp(header
, "\211PNG", 4))
6182 format
= "image/png";
6183 else if (!memcmp(header
, "RAS2", 4))
6184 format
= "image/pwg-raster";
6185 else if (!memcmp(header
, "UNIRAST", 8))
6186 format
= "image/urf";
6192 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
6193 client
->hostname
, op_name
, format
);
6195 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
6199 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
6201 respond_unsupported(client
, attr
);
6209 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
6210 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
6217 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6219 * When one or more job attributes are invalid, this function adds a suitable
6220 * response and attributes to the unsupported group.
6223 static int /* O - 1 if valid, 0 if not */
6224 valid_job_attributes(
6225 _ipp_client_t
*client
) /* I - Client */
6227 int i
, /* Looping var */
6228 valid
= 1; /* Valid attributes? */
6229 ipp_attribute_t
*attr
, /* Current attribute */
6230 *supported
; /* xxx-supported attribute */
6234 * Check operation attributes...
6237 valid
= valid_doc_attributes(client
);
6240 * Check the various job template attributes...
6243 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
6245 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6246 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
6248 respond_unsupported(client
, attr
);
6253 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
6255 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
6257 respond_unsupported(client
, attr
);
6262 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
6264 if (ippGetCount(attr
) != 1 ||
6265 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6266 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6267 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6268 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
6270 respond_unsupported(client
, attr
);
6275 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
6277 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
6279 respond_unsupported(client
, attr
);
6284 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
6286 if (ippGetCount(attr
) != 1 ||
6287 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6288 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
6290 respond_unsupported(client
, attr
);
6294 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
6297 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
6299 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
6301 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6302 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
6304 respond_unsupported(client
, attr
);
6309 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
6311 if (ippGetCount(attr
) != 1 ||
6312 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6313 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6314 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6315 strcmp(ippGetString(attr
, 0, NULL
), "none"))
6317 respond_unsupported(client
, attr
);
6322 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
6324 if (ippGetCount(attr
) != 1 ||
6325 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6326 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6327 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
6329 respond_unsupported(client
, attr
);
6335 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
6337 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
6340 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
6342 respond_unsupported(client
, attr
);
6348 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
6350 if (ippGetCount(attr
) != 1 ||
6351 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
6353 respond_unsupported(client
, attr
);
6356 /* TODO: check for valid media-col */
6359 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
6361 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6362 (strcmp(ippGetString(attr
, 0, NULL
),
6363 "separate-documents-uncollated-copies") &&
6364 strcmp(ippGetString(attr
, 0, NULL
),
6365 "separate-documents-collated-copies")))
6367 respond_unsupported(client
, attr
);
6372 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
6374 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6375 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
6376 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
6378 respond_unsupported(client
, attr
);
6383 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
6385 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
6387 respond_unsupported(client
, attr
);
6392 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
6394 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6395 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
6396 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
6398 respond_unsupported(client
, attr
);
6403 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
6405 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
6407 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
6410 respond_unsupported(client
, attr
);
6415 int count
, /* Number of supported values */
6416 xdpi
, /* Horizontal resolution for job template attribute */
6417 ydpi
, /* Vertical resolution for job template attribute */
6418 sydpi
; /* Vertical resolution for supported value */
6419 ipp_res_t units
, /* Units for job template attribute */
6420 sunits
; /* Units for supported value */
6422 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
6423 count
= ippGetCount(supported
);
6425 for (i
= 0; i
< count
; i
++)
6427 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
6433 respond_unsupported(client
, attr
);
6439 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
6441 const char *sides
= ippGetString(attr
, 0, NULL
);
6442 /* "sides" value... */
6444 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
6446 respond_unsupported(client
, attr
);
6449 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
6451 if (!ippContainsString(supported
, sides
))
6453 respond_unsupported(client
, attr
);
6457 else if (strcmp(sides
, "one-sided"))
6459 respond_unsupported(client
, attr
);