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...
2420 #ifndef WIN32 /* Avoid MS compiler bug */
2424 ipp_tag_t group
= ippGetGroupTag(attr
);
2425 const char *name
= ippGetName(attr
);
2427 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
)))
2430 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2435 * 'find_job()' - Find a job specified in a request.
2438 static _ipp_job_t
* /* O - Job or NULL */
2439 find_job(_ipp_client_t
*client
) /* I - Client */
2441 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2442 _ipp_job_t key
, /* Job search key */
2443 *job
; /* Matching job, if any */
2446 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2448 const char *uri
= ippGetString(attr
, 0, NULL
);
2450 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2451 uri
[client
->printer
->urilen
] == '/')
2452 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2456 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2457 key
.id
= ippGetInteger(attr
, 0);
2459 _cupsRWLockRead(&(client
->printer
->rwlock
));
2460 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2461 _cupsRWUnlock(&(client
->printer
->rwlock
));
2468 * 'html_escape()' - Write a HTML-safe string.
2472 html_escape(_ipp_client_t
*client
, /* I - Client */
2473 const char *s
, /* I - String to write */
2474 size_t slen
) /* I - Number of characters to write */
2476 const char *start
, /* Start of segment */
2477 *end
; /* End of string */
2481 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2483 while (*s
&& s
< end
)
2485 if (*s
== '&' || *s
== '<')
2488 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2491 httpWrite2(client
->http
, "&", 5);
2493 httpWrite2(client
->http
, "<", 4);
2502 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2507 * 'html_footer()' - Show the web interface footer.
2509 * This function also writes the trailing 0-length chunk.
2513 html_footer(_ipp_client_t
*client
) /* I - Client */
2519 httpWrite2(client
->http
, "", 0);
2524 * 'html_header()' - Show the web interface header and title.
2528 html_header(_ipp_client_t
*client
, /* I - Client */
2529 const char *title
) /* I - Title */
2535 "<title>%s</title>\n"
2536 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2537 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2538 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2539 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2541 "body { font-family: sans-serif; margin: 0; }\n"
2542 "div.body { padding: 0px 10px 10px; }\n"
2543 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2544 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2545 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2546 "table.form th { text-align: right; }\n"
2547 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2548 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2549 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2550 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2551 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2552 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2553 "table.nav td { margin: 0; text-align: center; }\n"
2554 "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"
2555 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2556 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2557 "td.nav:hover { background: #666; color: #fff; }\n"
2558 "td.nav:active { background: #000; color: #ff0; }\n"
2562 "<table class=\"nav\"><tr>"
2563 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2564 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2565 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2567 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2572 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2576 html_printf(_ipp_client_t
*client
, /* I - Client */
2577 const char *format
, /* I - Printf-style format string */
2578 ...) /* I - Additional arguments as needed */
2580 va_list ap
; /* Pointer to arguments */
2581 const char *start
; /* Start of string */
2582 char size
, /* Size character (h, l, L) */
2583 type
; /* Format type character */
2584 int width
, /* Width of field */
2585 prec
; /* Number of characters of precision */
2586 char tformat
[100], /* Temporary format string for sprintf() */
2587 *tptr
, /* Pointer into temporary format */
2588 temp
[1024]; /* Buffer for formatted numbers */
2589 char *s
; /* Pointer to string */
2593 * Loop through the format string, formatting as needed...
2596 va_start(ap
, format
);
2604 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2607 *tptr
++ = *format
++;
2611 httpWrite2(client
->http
, "%", 1);
2616 else if (strchr(" -+#\'", *format
))
2617 *tptr
++ = *format
++;
2622 * Get width from argument...
2626 width
= va_arg(ap
, int);
2628 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2629 tptr
+= strlen(tptr
);
2635 while (isdigit(*format
& 255))
2637 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2640 width
= width
* 10 + *format
++ - '0';
2646 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2654 * Get precision from argument...
2658 prec
= va_arg(ap
, int);
2660 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2661 tptr
+= strlen(tptr
);
2667 while (isdigit(*format
& 255))
2669 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2672 prec
= prec
* 10 + *format
++ - '0';
2677 if (*format
== 'l' && format
[1] == 'l')
2681 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2689 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2691 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2706 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2715 case 'E' : /* Floating point formats */
2720 if ((size_t)(width
+ 2) > sizeof(temp
))
2723 sprintf(temp
, tformat
, va_arg(ap
, double));
2725 httpWrite2(client
->http
, temp
, strlen(temp
));
2728 case 'B' : /* Integer formats */
2736 if ((size_t)(width
+ 2) > sizeof(temp
))
2739 # ifdef HAVE_LONG_LONG
2741 sprintf(temp
, tformat
, va_arg(ap
, long long));
2743 # endif /* HAVE_LONG_LONG */
2745 sprintf(temp
, tformat
, va_arg(ap
, long));
2747 sprintf(temp
, tformat
, va_arg(ap
, int));
2749 httpWrite2(client
->http
, temp
, strlen(temp
));
2752 case 'p' : /* Pointer value */
2753 if ((size_t)(width
+ 2) > sizeof(temp
))
2756 sprintf(temp
, tformat
, va_arg(ap
, void *));
2758 httpWrite2(client
->http
, temp
, strlen(temp
));
2761 case 'c' : /* Character or character array */
2764 temp
[0] = (char)va_arg(ap
, int);
2766 html_escape(client
, temp
, 1);
2769 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2772 case 's' : /* String */
2773 if ((s
= va_arg(ap
, char *)) == NULL
)
2776 html_escape(client
, s
, strlen(s
));
2785 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2792 * 'ipp_cancel_job()' - Cancel a job.
2796 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2798 _ipp_job_t
*job
; /* Job information */
2805 if ((job
= find_job(client
)) == NULL
)
2807 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2812 * See if the job is already completed, canceled, or aborted; if so,
2813 * we can't cancel...
2818 case IPP_JSTATE_CANCELED
:
2819 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2820 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2823 case IPP_JSTATE_ABORTED
:
2824 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2825 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2828 case IPP_JSTATE_COMPLETED
:
2829 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2830 "Job #%d is already completed - can\'t cancel.", job
->id
);
2838 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2840 if (job
->state
== IPP_JSTATE_PROCESSING
||
2841 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2845 job
->state
= IPP_JSTATE_CANCELED
;
2846 job
->completed
= time(NULL
);
2849 _cupsRWUnlock(&(client
->printer
->rwlock
));
2851 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2858 * 'ipp_close_job()' - Close an open job.
2862 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
2864 _ipp_job_t
*job
; /* Job information */
2871 if ((job
= find_job(client
)) == NULL
)
2873 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2878 * See if the job is already completed, canceled, or aborted; if so,
2879 * we can't cancel...
2884 case IPP_JSTATE_CANCELED
:
2885 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2886 "Job #%d is canceled - can\'t close.", job
->id
);
2889 case IPP_JSTATE_ABORTED
:
2890 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2891 "Job #%d is aborted - can\'t close.", job
->id
);
2894 case IPP_JSTATE_COMPLETED
:
2895 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2896 "Job #%d is completed - can\'t close.", job
->id
);
2899 case IPP_JSTATE_PROCESSING
:
2900 case IPP_JSTATE_STOPPED
:
2901 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2902 "Job #%d is already closed.", job
->id
);
2906 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2913 * 'ipp_create_job()' - Create a job object.
2917 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2919 _ipp_job_t
*job
; /* New job */
2920 cups_array_t
*ra
; /* Attributes to send in response */
2924 * Validate print job attributes...
2927 if (!valid_job_attributes(client
))
2929 httpFlush(client
->http
);
2934 * Do we have a file to print?
2937 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2939 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2940 "Unexpected document data following request.");
2948 if ((job
= create_job(client
)) == NULL
)
2950 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2951 "Currently printing another job.");
2956 * Return the job info...
2959 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2961 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2962 cupsArrayAdd(ra
, "job-id");
2963 cupsArrayAdd(ra
, "job-state");
2964 cupsArrayAdd(ra
, "job-state-message");
2965 cupsArrayAdd(ra
, "job-state-reasons");
2966 cupsArrayAdd(ra
, "job-uri");
2968 copy_job_attributes(client
, job
, ra
);
2969 cupsArrayDelete(ra
);
2974 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2978 ipp_get_job_attributes(
2979 _ipp_client_t
*client
) /* I - Client */
2981 _ipp_job_t
*job
; /* Job */
2982 cups_array_t
*ra
; /* requested-attributes */
2985 if ((job
= find_job(client
)) == NULL
)
2987 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2991 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2993 ra
= ippCreateRequestedArray(client
->request
);
2994 copy_job_attributes(client
, job
, ra
);
2995 cupsArrayDelete(ra
);
3000 * 'ipp_get_jobs()' - Get a list of job objects.
3004 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
3006 ipp_attribute_t
*attr
; /* Current attribute */
3007 const char *which_jobs
= NULL
;
3008 /* which-jobs values */
3009 int job_comparison
; /* Job comparison */
3010 ipp_jstate_t job_state
; /* job-state value */
3011 int first_job_id
, /* First job ID */
3012 limit
, /* Maximum number of jobs to return */
3013 count
; /* Number of jobs that match */
3014 const char *username
; /* Username */
3015 _ipp_job_t
*job
; /* Current job pointer */
3016 cups_array_t
*ra
; /* Requested attributes array */
3020 * See if the "which-jobs" attribute have been specified...
3023 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3024 IPP_TAG_KEYWORD
)) != NULL
)
3026 which_jobs
= ippGetString(attr
, 0, NULL
);
3027 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3030 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3032 job_comparison
= -1;
3033 job_state
= IPP_JSTATE_STOPPED
;
3035 else if (!strcmp(which_jobs
, "completed"))
3038 job_state
= IPP_JSTATE_CANCELED
;
3040 else if (!strcmp(which_jobs
, "aborted"))
3043 job_state
= IPP_JSTATE_ABORTED
;
3045 else if (!strcmp(which_jobs
, "all"))
3048 job_state
= IPP_JSTATE_PENDING
;
3050 else if (!strcmp(which_jobs
, "canceled"))
3053 job_state
= IPP_JSTATE_CANCELED
;
3055 else if (!strcmp(which_jobs
, "pending"))
3058 job_state
= IPP_JSTATE_PENDING
;
3060 else if (!strcmp(which_jobs
, "pending-held"))
3063 job_state
= IPP_JSTATE_HELD
;
3065 else if (!strcmp(which_jobs
, "processing"))
3068 job_state
= IPP_JSTATE_PROCESSING
;
3070 else if (!strcmp(which_jobs
, "processing-stopped"))
3073 job_state
= IPP_JSTATE_STOPPED
;
3077 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3078 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3079 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3080 "which-jobs", NULL
, which_jobs
);
3085 * See if they want to limit the number of jobs reported...
3088 if ((attr
= ippFindAttribute(client
->request
, "limit",
3089 IPP_TAG_INTEGER
)) != NULL
)
3091 limit
= ippGetInteger(attr
, 0);
3093 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3098 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3099 IPP_TAG_INTEGER
)) != NULL
)
3101 first_job_id
= ippGetInteger(attr
, 0);
3103 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3110 * See if we only want to see jobs for a specific user...
3115 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3116 IPP_TAG_BOOLEAN
)) != NULL
)
3118 int my_jobs
= ippGetBoolean(attr
, 0);
3120 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3121 my_jobs
? "true" : "false");
3125 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3126 IPP_TAG_NAME
)) == NULL
)
3128 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3129 "Need requesting-user-name with my-jobs.");
3133 username
= ippGetString(attr
, 0, NULL
);
3135 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3136 client
->hostname
, username
);
3141 * OK, build a list of jobs for this printer...
3144 ra
= ippCreateRequestedArray(client
->request
);
3146 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3148 _cupsRWLockRead(&(client
->printer
->rwlock
));
3150 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3151 (limit
<= 0 || count
< limit
) && job
;
3152 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3155 * Filter out jobs that don't match...
3158 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3159 (job_comparison
== 0 && job
->state
!= job_state
) ||
3160 (job_comparison
> 0 && job
->state
< job_state
) ||
3161 job
->id
< first_job_id
||
3162 (username
&& job
->username
&&
3163 strcasecmp(username
, job
->username
)))
3167 ippAddSeparator(client
->response
);
3170 copy_job_attributes(client
, job
, ra
);
3173 cupsArrayDelete(ra
);
3175 _cupsRWUnlock(&(client
->printer
->rwlock
));
3180 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3184 ipp_get_printer_attributes(
3185 _ipp_client_t
*client
) /* I - Client */
3187 cups_array_t
*ra
; /* Requested attributes array */
3188 _ipp_printer_t
*printer
; /* Printer */
3192 * Send the attributes...
3195 ra
= ippCreateRequestedArray(client
->request
);
3196 printer
= client
->printer
;
3198 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3200 _cupsRWLockRead(&(printer
->rwlock
));
3202 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3203 IPP_TAG_CUPS_CONST
);
3205 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3207 int i
, /* Looping var */
3208 num_ready
= 0; /* Number of ready media */
3209 ipp_t
*ready
[3]; /* Ready media */
3211 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3213 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
3214 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);
3216 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);
3218 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3219 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);
3220 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3222 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
3223 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);
3225 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);
3230 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3231 for (i
= 0; i
< num_ready
; i
++)
3232 ippDelete(ready
[i
]);
3235 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3238 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3240 int num_ready
= 0; /* Number of ready media */
3241 const char *ready
[3]; /* Ready media */
3243 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3244 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3246 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3247 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3249 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3250 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3253 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3255 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3258 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3259 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3261 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3262 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3264 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3265 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3268 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3269 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3270 "printer-state", printer
->state
);
3272 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3273 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3275 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3276 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3278 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3280 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3282 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3285 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3287 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3288 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3289 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3290 "printer-state-reasons", NULL
, "none");
3293 int num_reasons
= 0;/* Number of reasons */
3294 const char *reasons
[32]; /* Reason strings */
3296 if (printer
->state_reasons
& _IPP_PREASON_OTHER
)
3297 reasons
[num_reasons
++] = "other";
3298 if (printer
->state_reasons
& _IPP_PREASON_COVER_OPEN
)
3299 reasons
[num_reasons
++] = "cover-open";
3300 if (printer
->state_reasons
& _IPP_PREASON_INPUT_TRAY_MISSING
)
3301 reasons
[num_reasons
++] = "input-tray-missing";
3302 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_EMPTY
)
3303 reasons
[num_reasons
++] = "marker-supply-empty-warning";
3304 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_LOW
)
3305 reasons
[num_reasons
++] = "marker-supply-low-report";
3306 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
)
3307 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
3308 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_FULL
)
3309 reasons
[num_reasons
++] = "marker-waste-full-warning";
3310 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
3311 reasons
[num_reasons
++] = "media-empty-warning";
3312 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_JAM
)
3313 reasons
[num_reasons
++] = "media-jam-warning";
3314 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_LOW
)
3315 reasons
[num_reasons
++] = "media-low-report";
3316 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_NEEDED
)
3317 reasons
[num_reasons
++] = "media-needed-report";
3318 if (printer
->state_reasons
& _IPP_PREASON_MOVING_TO_PAUSED
)
3319 reasons
[num_reasons
++] = "moving-to-paused";
3320 if (printer
->state_reasons
& _IPP_PREASON_PAUSED
)
3321 reasons
[num_reasons
++] = "paused";
3322 if (printer
->state_reasons
& _IPP_PREASON_SPOOL_AREA_FULL
)
3323 reasons
[num_reasons
++] = "spool-area-full";
3324 if (printer
->state_reasons
& _IPP_PREASON_TONER_EMPTY
)
3325 reasons
[num_reasons
++] = "toner-empty-warning";
3326 if (printer
->state_reasons
& _IPP_PREASON_TONER_LOW
)
3327 reasons
[num_reasons
++] = "toner-low-report";
3329 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
3330 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3331 "printer-state-reasons", num_reasons
, NULL
, reasons
);
3335 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3337 int i
; /* Looping var */
3338 char buffer
[256]; /* Supply value buffer */
3339 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3340 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3342 for (i
= 0; i
< 5; i
++)
3344 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
]);
3347 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3349 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3353 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3354 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3356 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3357 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3358 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3360 _cupsRWUnlock(&(printer
->rwlock
));
3362 cupsArrayDelete(ra
);
3367 * 'ipp_identify_printer()' - Beep or display a message.
3371 ipp_identify_printer(
3372 _ipp_client_t
*client
) /* I - Client */
3374 /* TODO: Do something */
3376 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3381 * 'ipp_print_job()' - Create a job object with an attached document.
3385 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3387 _ipp_job_t
*job
; /* New job */
3388 char filename
[1024], /* Filename buffer */
3389 buffer
[4096]; /* Copy buffer */
3390 ssize_t bytes
; /* Bytes read */
3391 cups_array_t
*ra
; /* Attributes to send in response */
3395 * Validate print job attributes...
3398 if (!valid_job_attributes(client
))
3400 httpFlush(client
->http
);
3405 * Do we have a file to print?
3408 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3410 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3418 if ((job
= create_job(client
)) == NULL
)
3420 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3421 "Currently printing another job.");
3426 * Create a file for the request data...
3429 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3432 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3434 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3436 job
->state
= IPP_JSTATE_ABORTED
;
3438 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3439 "Unable to create print file: %s", strerror(errno
));
3443 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3445 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3447 int error
= errno
; /* Write error */
3449 job
->state
= IPP_JSTATE_ABORTED
;
3456 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3457 "Unable to write print file: %s", strerror(error
));
3465 * Got an error while reading the print data, so abort this job.
3468 job
->state
= IPP_JSTATE_ABORTED
;
3475 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3476 "Unable to read print file.");
3482 int error
= errno
; /* Write error */
3484 job
->state
= IPP_JSTATE_ABORTED
;
3489 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3490 "Unable to write print file: %s", strerror(error
));
3495 job
->filename
= strdup(filename
);
3496 job
->state
= IPP_JSTATE_PENDING
;
3499 * Process the job...
3502 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3504 job
->state
= IPP_JSTATE_ABORTED
;
3505 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3510 * Return the job info...
3513 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3515 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3516 cupsArrayAdd(ra
, "job-id");
3517 cupsArrayAdd(ra
, "job-state");
3518 cupsArrayAdd(ra
, "job-state-message");
3519 cupsArrayAdd(ra
, "job-state-reasons");
3520 cupsArrayAdd(ra
, "job-uri");
3522 copy_job_attributes(client
, job
, ra
);
3523 cupsArrayDelete(ra
);
3528 * 'ipp_print_uri()' - Create a job object with a referenced document.
3532 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3534 _ipp_job_t
*job
; /* New job */
3535 ipp_attribute_t
*uri
; /* document-uri */
3536 char scheme
[256], /* URI scheme */
3537 userpass
[256], /* Username and password info */
3538 hostname
[256], /* Hostname */
3539 resource
[1024]; /* Resource path */
3540 int port
; /* Port number */
3541 http_uri_status_t uri_status
; /* URI decode status */
3542 http_encryption_t encryption
; /* Encryption to use, if any */
3543 http_t
*http
; /* Connection for http/https URIs */
3544 http_status_t status
; /* Access status for http/https URIs */
3545 int infile
; /* Input file for local file URIs */
3546 char filename
[1024], /* Filename buffer */
3547 buffer
[4096]; /* Copy buffer */
3548 ssize_t bytes
; /* Bytes read */
3549 cups_array_t
*ra
; /* Attributes to send in response */
3550 static const char * const uri_status_strings
[] =
3551 { /* URI decode errors */
3553 "Bad arguments to function.",
3554 "Bad resource in URI.",
3555 "Bad port number in URI.",
3556 "Bad hostname in URI.",
3557 "Bad username in URI.",
3558 "Bad scheme in URI.",
3564 * Validate print job attributes...
3567 if (!valid_job_attributes(client
))
3569 httpFlush(client
->http
);
3574 * Do we have a file to print?
3577 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3579 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3580 "Unexpected document data following request.");
3585 * Do we have a document URI?
3588 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3589 IPP_TAG_URI
)) == NULL
)
3591 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3595 if (ippGetCount(uri
) != 1)
3597 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3598 "Too many document-uri values.");
3602 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3603 scheme
, sizeof(scheme
), userpass
,
3604 sizeof(userpass
), hostname
, sizeof(hostname
),
3605 &port
, resource
, sizeof(resource
));
3606 if (uri_status
< HTTP_URI_STATUS_OK
)
3608 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3609 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3613 if (strcmp(scheme
, "file") &&
3615 strcmp(scheme
, "https") &&
3616 #endif /* HAVE_SSL */
3617 strcmp(scheme
, "http"))
3619 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3620 "URI scheme \"%s\" not supported.", scheme
);
3624 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3626 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3627 "Unable to access URI: %s", strerror(errno
));
3635 if ((job
= create_job(client
)) == NULL
)
3637 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3638 "Currently printing another job.");
3643 * Create a file for the request data...
3646 if (!strcasecmp(job
->format
, "image/jpeg"))
3647 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3648 client
->printer
->directory
, job
->id
);
3649 else if (!strcasecmp(job
->format
, "image/png"))
3650 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3651 client
->printer
->directory
, job
->id
);
3652 else if (!strcasecmp(job
->format
, "application/pdf"))
3653 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3654 client
->printer
->directory
, job
->id
);
3655 else if (!strcasecmp(job
->format
, "application/postscript"))
3656 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3657 client
->printer
->directory
, job
->id
);
3659 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3660 client
->printer
->directory
, job
->id
);
3662 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3664 job
->state
= IPP_JSTATE_ABORTED
;
3666 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3667 "Unable to create print file: %s", strerror(errno
));
3671 if (!strcmp(scheme
, "file"))
3673 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3675 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3676 "Unable to access URI: %s", strerror(errno
));
3682 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3683 (errno
== EAGAIN
|| errno
== EINTR
))
3685 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3687 int error
= errno
; /* Write error */
3689 job
->state
= IPP_JSTATE_ABORTED
;
3697 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3698 "Unable to write print file: %s", strerror(error
));
3709 if (port
== 443 || !strcmp(scheme
, "https"))
3710 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3712 #endif /* HAVE_SSL */
3713 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3715 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3716 1, 30000, NULL
)) == NULL
)
3718 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3719 "Unable to connect to %s: %s", hostname
,
3720 cupsLastErrorString());
3721 job
->state
= IPP_JSTATE_ABORTED
;
3730 httpClearFields(http
);
3731 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3732 if (httpGet(http
, resource
))
3734 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3735 "Unable to GET URI: %s", strerror(errno
));
3737 job
->state
= IPP_JSTATE_ABORTED
;
3747 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3749 if (status
!= HTTP_STATUS_OK
)
3751 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3752 "Unable to GET URI: %s", httpStatus(status
));
3754 job
->state
= IPP_JSTATE_ABORTED
;
3764 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3766 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3768 int error
= errno
; /* Write error */
3770 job
->state
= IPP_JSTATE_ABORTED
;
3778 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3779 "Unable to write print file: %s", strerror(error
));
3789 int error
= errno
; /* Write error */
3791 job
->state
= IPP_JSTATE_ABORTED
;
3796 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3797 "Unable to write print file: %s", strerror(error
));
3802 job
->filename
= strdup(filename
);
3803 job
->state
= IPP_JSTATE_PENDING
;
3806 * Process the job...
3810 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3812 job
->state
= IPP_JSTATE_ABORTED
;
3813 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3822 * Return the job info...
3825 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3827 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3828 cupsArrayAdd(ra
, "job-id");
3829 cupsArrayAdd(ra
, "job-state");
3830 cupsArrayAdd(ra
, "job-state-reasons");
3831 cupsArrayAdd(ra
, "job-uri");
3833 copy_job_attributes(client
, job
, ra
);
3834 cupsArrayDelete(ra
);
3839 * 'ipp_send_document()' - Add an attached document to a job object created with
3844 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3846 _ipp_job_t
*job
; /* Job information */
3847 char filename
[1024], /* Filename buffer */
3848 buffer
[4096]; /* Copy buffer */
3849 ssize_t bytes
; /* Bytes read */
3850 ipp_attribute_t
*attr
; /* Current attribute */
3851 cups_array_t
*ra
; /* Attributes to send in response */
3858 if ((job
= find_job(client
)) == NULL
)
3860 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3861 httpFlush(client
->http
);
3866 * See if we already have a document for this job or the job has already
3867 * in a non-pending state...
3870 if (job
->state
> IPP_JSTATE_HELD
)
3872 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3873 "Job is not in a pending state.");
3874 httpFlush(client
->http
);
3877 else if (job
->filename
|| job
->fd
>= 0)
3879 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3880 "Multiple document jobs are not supported.");
3881 httpFlush(client
->http
);
3885 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3886 IPP_TAG_ZERO
)) == NULL
)
3888 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3889 "Missing required last-document attribute.");
3890 httpFlush(client
->http
);
3893 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3894 !ippGetBoolean(attr
, 0))
3896 respond_unsupported(client
, attr
);
3897 httpFlush(client
->http
);
3902 * Validate document attributes...
3905 if (!valid_doc_attributes(client
))
3907 httpFlush(client
->http
);
3911 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3914 * Get the document format for the job...
3917 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3919 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3920 job
->format
= ippGetString(attr
, 0, NULL
);
3921 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3922 job
->format
= ippGetString(attr
, 0, NULL
);
3924 job
->format
= "application/octet-stream";
3927 * Create a file for the request data...
3930 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3933 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3935 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3937 _cupsRWUnlock(&(client
->printer
->rwlock
));
3941 job
->state
= IPP_JSTATE_ABORTED
;
3943 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3944 "Unable to create print file: %s", strerror(errno
));
3948 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3950 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3952 int error
= errno
; /* Write error */
3954 job
->state
= IPP_JSTATE_ABORTED
;
3961 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3962 "Unable to write print file: %s", strerror(error
));
3970 * Got an error while reading the print data, so abort this job.
3973 job
->state
= IPP_JSTATE_ABORTED
;
3980 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3981 "Unable to read print file.");
3987 int error
= errno
; /* Write error */
3989 job
->state
= IPP_JSTATE_ABORTED
;
3994 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3995 "Unable to write print file: %s", strerror(error
));
3999 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4002 job
->filename
= strdup(filename
);
4003 job
->state
= IPP_JSTATE_PENDING
;
4005 _cupsRWUnlock(&(client
->printer
->rwlock
));
4008 * Process the job...
4012 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4014 job
->state
= IPP_JSTATE_ABORTED
;
4015 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4024 * Return the job info...
4027 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4029 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4030 cupsArrayAdd(ra
, "job-id");
4031 cupsArrayAdd(ra
, "job-state");
4032 cupsArrayAdd(ra
, "job-state-reasons");
4033 cupsArrayAdd(ra
, "job-uri");
4035 copy_job_attributes(client
, job
, ra
);
4036 cupsArrayDelete(ra
);
4041 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4046 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
4048 _ipp_job_t
*job
; /* Job information */
4049 ipp_attribute_t
*uri
; /* document-uri */
4050 char scheme
[256], /* URI scheme */
4051 userpass
[256], /* Username and password info */
4052 hostname
[256], /* Hostname */
4053 resource
[1024]; /* Resource path */
4054 int port
; /* Port number */
4055 http_uri_status_t uri_status
; /* URI decode status */
4056 http_encryption_t encryption
; /* Encryption to use, if any */
4057 http_t
*http
; /* Connection for http/https URIs */
4058 http_status_t status
; /* Access status for http/https URIs */
4059 int infile
; /* Input file for local file URIs */
4060 char filename
[1024], /* Filename buffer */
4061 buffer
[4096]; /* Copy buffer */
4062 ssize_t bytes
; /* Bytes read */
4063 ipp_attribute_t
*attr
; /* Current attribute */
4064 cups_array_t
*ra
; /* Attributes to send in response */
4065 static const char * const uri_status_strings
[] =
4066 { /* URI decode errors */
4068 "Bad arguments to function.",
4069 "Bad resource in URI.",
4070 "Bad port number in URI.",
4071 "Bad hostname in URI.",
4072 "Bad username in URI.",
4073 "Bad scheme in URI.",
4082 if ((job
= find_job(client
)) == NULL
)
4084 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4085 httpFlush(client
->http
);
4090 * See if we already have a document for this job or the job has already
4091 * in a non-pending state...
4094 if (job
->state
> IPP_JSTATE_HELD
)
4096 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4097 "Job is not in a pending state.");
4098 httpFlush(client
->http
);
4101 else if (job
->filename
|| job
->fd
>= 0)
4103 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4104 "Multiple document jobs are not supported.");
4105 httpFlush(client
->http
);
4109 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4110 IPP_TAG_ZERO
)) == NULL
)
4112 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4113 "Missing required last-document attribute.");
4114 httpFlush(client
->http
);
4117 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4118 !ippGetBoolean(attr
, 0))
4120 respond_unsupported(client
, attr
);
4121 httpFlush(client
->http
);
4126 * Validate document attributes...
4129 if (!valid_doc_attributes(client
))
4131 httpFlush(client
->http
);
4136 * Do we have a file to print?
4139 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4141 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4142 "Unexpected document data following request.");
4147 * Do we have a document URI?
4150 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4151 IPP_TAG_URI
)) == NULL
)
4153 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4157 if (ippGetCount(uri
) != 1)
4159 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4160 "Too many document-uri values.");
4164 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4165 scheme
, sizeof(scheme
), userpass
,
4166 sizeof(userpass
), hostname
, sizeof(hostname
),
4167 &port
, resource
, sizeof(resource
));
4168 if (uri_status
< HTTP_URI_STATUS_OK
)
4170 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4171 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4175 if (strcmp(scheme
, "file") &&
4177 strcmp(scheme
, "https") &&
4178 #endif /* HAVE_SSL */
4179 strcmp(scheme
, "http"))
4181 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4182 "URI scheme \"%s\" not supported.", scheme
);
4186 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4188 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4189 "Unable to access URI: %s", strerror(errno
));
4194 * Get the document format for the job...
4197 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4199 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4200 IPP_TAG_MIMETYPE
)) != NULL
)
4201 job
->format
= ippGetString(attr
, 0, NULL
);
4203 job
->format
= "application/octet-stream";
4206 * Create a file for the request data...
4209 if (!strcasecmp(job
->format
, "image/jpeg"))
4210 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4211 client
->printer
->directory
, job
->id
);
4212 else if (!strcasecmp(job
->format
, "image/png"))
4213 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4214 client
->printer
->directory
, job
->id
);
4215 else if (!strcasecmp(job
->format
, "application/pdf"))
4216 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4217 client
->printer
->directory
, job
->id
);
4218 else if (!strcasecmp(job
->format
, "application/postscript"))
4219 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4220 client
->printer
->directory
, job
->id
);
4222 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4223 client
->printer
->directory
, job
->id
);
4225 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4227 _cupsRWUnlock(&(client
->printer
->rwlock
));
4231 job
->state
= IPP_JSTATE_ABORTED
;
4233 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4234 "Unable to create print file: %s", strerror(errno
));
4238 if (!strcmp(scheme
, "file"))
4240 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4242 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4243 "Unable to access URI: %s", strerror(errno
));
4249 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4250 (errno
== EAGAIN
|| errno
== EINTR
))
4252 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4254 int error
= errno
; /* Write error */
4256 job
->state
= IPP_JSTATE_ABORTED
;
4264 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4265 "Unable to write print file: %s", strerror(error
));
4276 if (port
== 443 || !strcmp(scheme
, "https"))
4277 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4279 #endif /* HAVE_SSL */
4280 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4282 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4283 1, 30000, NULL
)) == NULL
)
4285 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4286 "Unable to connect to %s: %s", hostname
,
4287 cupsLastErrorString());
4288 job
->state
= IPP_JSTATE_ABORTED
;
4297 httpClearFields(http
);
4298 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4299 if (httpGet(http
, resource
))
4301 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4302 "Unable to GET URI: %s", strerror(errno
));
4304 job
->state
= IPP_JSTATE_ABORTED
;
4314 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4316 if (status
!= HTTP_STATUS_OK
)
4318 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4319 "Unable to GET URI: %s", httpStatus(status
));
4321 job
->state
= IPP_JSTATE_ABORTED
;
4331 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4333 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4335 int error
= errno
; /* Write error */
4337 job
->state
= IPP_JSTATE_ABORTED
;
4345 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4346 "Unable to write print file: %s", strerror(error
));
4356 int error
= errno
; /* Write error */
4358 job
->state
= IPP_JSTATE_ABORTED
;
4363 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4364 "Unable to write print file: %s", strerror(error
));
4368 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4371 job
->filename
= strdup(filename
);
4372 job
->state
= IPP_JSTATE_PENDING
;
4374 _cupsRWUnlock(&(client
->printer
->rwlock
));
4377 * Process the job...
4381 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4383 job
->state
= IPP_JSTATE_ABORTED
;
4384 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4393 * Return the job info...
4396 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4398 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4399 cupsArrayAdd(ra
, "job-id");
4400 cupsArrayAdd(ra
, "job-state");
4401 cupsArrayAdd(ra
, "job-state-reasons");
4402 cupsArrayAdd(ra
, "job-uri");
4404 copy_job_attributes(client
, job
, ra
);
4405 cupsArrayDelete(ra
);
4410 * 'ipp_validate_job()' - Validate job creation attributes.
4414 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4416 if (valid_job_attributes(client
))
4417 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4422 * 'parse_options()' - Parse URL options into CUPS options.
4424 * The client->options string is destroyed by this function.
4427 static int /* O - Number of options */
4428 parse_options(_ipp_client_t
*client
, /* I - Client */
4429 cups_option_t
**options
) /* O - Options */
4431 char *name
, /* Name */
4433 *next
; /* Next name=value pair */
4434 int num_options
= 0; /* Number of options */
4439 for (name
= client
->options
; name
&& *name
; name
= next
)
4441 if ((value
= strchr(name
, '=')) == NULL
)
4445 if ((next
= strchr(value
, '&')) != NULL
)
4448 num_options
= cupsAddOption(name
, value
, num_options
, options
);
4451 return (num_options
);
4456 * 'process_client()' - Process client requests on a thread.
4459 static void * /* O - Exit status */
4460 process_client(_ipp_client_t
*client
) /* I - Client */
4463 * Loop until we are out of requests or timeout (30 seconds)...
4467 int first_time
= 1; /* First time request? */
4468 #endif /* HAVE_SSL */
4470 while (httpWait(client
->http
, 30000))
4476 * See if we need to negotiate a TLS connection...
4479 char buf
[1]; /* First byte from client */
4481 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
4483 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
4485 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
4487 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4491 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4496 #endif /* HAVE_SSL */
4498 if (!process_http(client
))
4503 * Close the conection to the client and return...
4506 delete_client(client
);
4513 * 'process_http()' - Process a HTTP request.
4516 int /* O - 1 on success, 0 on failure */
4517 process_http(_ipp_client_t
*client
) /* I - Client connection */
4519 char uri
[1024]; /* URI */
4520 http_state_t http_state
; /* HTTP state */
4521 http_status_t http_status
; /* HTTP status */
4522 ipp_state_t ipp_state
; /* State of IPP transfer */
4523 char scheme
[32], /* Method/scheme */
4524 userpass
[128], /* Username:password */
4525 hostname
[HTTP_MAX_HOST
];
4527 int port
; /* Port number */
4528 const char *encoding
; /* Content-Encoding value */
4529 static const char * const http_states
[] =
4530 { /* Strings for logging HTTP method */
4551 * Clear state variables...
4554 ippDelete(client
->request
);
4555 ippDelete(client
->response
);
4557 client
->request
= NULL
;
4558 client
->response
= NULL
;
4559 client
->operation
= HTTP_STATE_WAITING
;
4562 * Read a request from the connection...
4565 while ((http_state
= httpReadRequest(client
->http
, uri
,
4566 sizeof(uri
))) == HTTP_STATE_WAITING
)
4570 * Parse the request line...
4573 if (http_state
== HTTP_STATE_ERROR
)
4575 if (httpError(client
->http
) == EPIPE
)
4576 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
4578 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
4579 strerror(httpError(client
->http
)));
4583 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
4585 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
4586 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4589 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
4591 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
4592 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4596 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
4600 * Separate the URI into its components...
4603 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4604 userpass
, sizeof(userpass
),
4605 hostname
, sizeof(hostname
), &port
,
4606 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
4607 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
4609 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
4610 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4614 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
4615 *(client
->options
)++ = '\0';
4618 * Process the request...
4621 client
->start
= time(NULL
);
4622 client
->operation
= httpGetState(client
->http
);
4625 * Parse incoming parameters until the status changes...
4628 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4630 if (http_status
!= HTTP_STATUS_OK
)
4632 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4636 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4637 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4640 * HTTP/1.1 and higher require the "Host:" field...
4643 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4648 * Handle HTTP Upgrade...
4651 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4655 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
4657 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
4660 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
4662 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
4664 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4668 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4671 #endif /* HAVE_SSL */
4673 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4678 * Handle HTTP Expect...
4681 if (httpGetExpect(client
->http
) &&
4682 (client
->operation
== HTTP_STATE_POST
||
4683 client
->operation
== HTTP_STATE_PUT
))
4685 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4688 * Send 100-continue header...
4691 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4697 * Send 417-expectation-failed header...
4700 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4706 * Handle new transfers...
4709 encoding
= httpGetContentEncoding(client
->http
);
4711 switch (client
->operation
)
4713 case HTTP_STATE_OPTIONS
:
4715 * Do OPTIONS command...
4718 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4720 case HTTP_STATE_HEAD
:
4721 if (!strcmp(client
->uri
, "/icon.png"))
4722 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4723 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
4724 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4726 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4728 case HTTP_STATE_GET
:
4729 if (!strcmp(client
->uri
, "/icon.png"))
4732 * Send PNG icon file.
4735 int fd
; /* Icon file */
4736 struct stat fileinfo
; /* Icon file information */
4737 char buffer
[4096]; /* Copy buffer */
4738 ssize_t bytes
; /* Bytes */
4740 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
4742 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4743 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4745 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
4746 (size_t)fileinfo
.st_size
))
4752 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4753 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
4755 httpFlushWrite(client
->http
);
4760 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4762 else if (!strcmp(client
->uri
, "/"))
4765 * Show web status page...
4768 _ipp_job_t
*job
; /* Current job */
4769 int i
; /* Looping var */
4770 _ipp_preason_t reason
; /* Current reason */
4771 static const char * const reasons
[] =
4772 { /* Reason strings */
4775 "Input Tray Missing",
4776 "Marker Supply Empty",
4777 "Marker Supply Low",
4778 "Marker Waste Almost Full",
4779 "Marker Waste Full",
4791 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4794 html_header(client
, client
->printer
->name
);
4796 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
4797 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
4798 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
4799 if (client
->printer
->state_reasons
& reason
)
4800 html_printf(client
, "\n<br> %s", reasons
[i
]);
4801 html_printf(client
, "</p>\n");
4803 if (cupsArrayCount(client
->printer
->jobs
) > 0)
4805 _cupsRWLockRead(&(client
->printer
->rwlock
));
4807 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");
4808 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
4810 char when
[256], /* When job queued/started/finished */
4811 hhmmss
[64]; /* Time HH:MM:SS */
4815 case IPP_JSTATE_PENDING
:
4816 case IPP_JSTATE_HELD
:
4817 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
4819 case IPP_JSTATE_PROCESSING
:
4820 case IPP_JSTATE_STOPPED
:
4821 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
4823 case IPP_JSTATE_ABORTED
:
4824 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4826 case IPP_JSTATE_CANCELED
:
4827 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4829 case IPP_JSTATE_COMPLETED
:
4830 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4834 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
);
4836 html_printf(client
, "</tbody></table>\n");
4838 _cupsRWUnlock(&(client
->printer
->rwlock
));
4840 html_footer(client
);
4844 else if (!strcmp(client
->uri
, "/media"))
4847 * Show web media page...
4850 int i
, /* Looping var */
4851 num_options
; /* Number of form options */
4852 cups_option_t
*options
; /* Form options */
4853 static const char * const sizes
[] =
4854 { /* Size strings */
4867 static const char * const types
[] =
4884 static const int sheets
[] = /* Number of sheets */
4893 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4896 html_header(client
, client
->printer
->name
);
4898 if ((num_options
= parse_options(client
, &options
)) > 0)
4901 * WARNING: A real printer/server implementation MUST NOT implement
4902 * media updates via a GET request - GET requests are supposed to be
4903 * idempotent (without side-effects) and we obviously are not
4904 * authenticating access here. This form is provided solely to
4905 * enable testing and development!
4908 const char *val
; /* Form value */
4910 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
4911 client
->printer
->main_size
= atoi(val
);
4912 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
4913 client
->printer
->main_type
= atoi(val
);
4914 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
4915 client
->printer
->main_level
= atoi(val
);
4917 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
4918 client
->printer
->envelope_size
= atoi(val
);
4919 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
4920 client
->printer
->envelope_level
= atoi(val
);
4922 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
4923 client
->printer
->photo_size
= atoi(val
);
4924 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
4925 client
->printer
->photo_type
= atoi(val
);
4926 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
4927 client
->printer
->photo_level
= atoi(val
);
4929 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))
4930 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
4932 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
4934 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
))
4936 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
4937 if (client
->printer
->active_job
)
4938 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
4941 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
4943 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
4946 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
4948 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
4949 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
4950 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4951 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
4952 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
4953 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
4954 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4955 if (!strstr(types
[i
], "Photo"))
4956 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
4957 html_printf(client
, "</select> <select name=\"main_level\">");
4958 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4959 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
4960 html_printf(client
, "</select></td></tr>\n");
4963 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
4964 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4965 if (strstr(sizes
[i
], "Envelope"))
4966 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
4967 html_printf(client
, "</select> <select name=\"envelope_level\">");
4968 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4969 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
4970 html_printf(client
, "</select></td></tr>\n");
4973 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
4974 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4975 if (strstr(sizes
[i
], "Photo"))
4976 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
4977 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
4978 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4979 if (strstr(types
[i
], "Photo"))
4980 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
4981 html_printf(client
, "</select> <select name=\"photo_level\">");
4982 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4983 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
4984 html_printf(client
, "</select></td></tr>\n");
4986 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
4987 html_footer(client
);
4991 else if (!strcmp(client
->uri
, "/supplies"))
4994 * Show web supplies page...
4997 int i
, j
, /* Looping vars */
4998 num_options
; /* Number of form options */
4999 cups_option_t
*options
; /* Form options */
5000 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5002 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5005 html_header(client
, client
->printer
->name
);
5007 if ((num_options
= parse_options(client
, &options
)) > 0)
5010 * WARNING: A real printer/server implementation MUST NOT implement
5011 * supply updates via a GET request - GET requests are supposed to be
5012 * idempotent (without side-effects) and we obviously are not
5013 * authenticating access here. This form is provided solely to
5014 * enable testing and development!
5017 char name
[64]; /* Form field */
5018 const char *val
; /* Form value */
5020 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
);
5022 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5024 snprintf(name
, sizeof(name
), "supply_%d", i
);
5025 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5027 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5033 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
5034 else if (level
< 10)
5035 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
5040 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
5041 else if (level
> 90)
5042 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
5047 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5050 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5052 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5053 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5055 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5056 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5057 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5058 html_printf(client
, "</select></td></tr>\n");
5060 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5061 html_footer(client
);
5066 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5069 case HTTP_STATE_POST
:
5070 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5074 * Not an IPP request...
5077 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5081 * Read the IPP request...
5084 client
->request
= ippNew();
5086 while ((ipp_state
= ippRead(client
->http
,
5087 client
->request
)) != IPP_STATE_DATA
)
5089 if (ipp_state
== IPP_STATE_ERROR
)
5091 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5092 cupsLastErrorString());
5093 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5099 * Now that we have the IPP request, process the request...
5102 return (process_ipp(client
));
5105 break; /* Anti-compiler-warning-code */
5113 * 'process_ipp()' - Process an IPP request.
5116 static int /* O - 1 on success, 0 on error */
5117 process_ipp(_ipp_client_t
*client
) /* I - Client */
5119 ipp_tag_t group
; /* Current group tag */
5120 ipp_attribute_t
*attr
; /* Current attribute */
5121 ipp_attribute_t
*charset
; /* Character set attribute */
5122 ipp_attribute_t
*language
; /* Language attribute */
5123 ipp_attribute_t
*uri
; /* Printer URI attribute */
5124 int major
, minor
; /* Version number */
5125 const char *name
; /* Name of attribute */
5128 debug_attributes("Request", client
->request
, 1);
5131 * First build an empty response message for this request...
5134 client
->operation_id
= ippGetOperation(client
->request
);
5135 client
->response
= ippNewResponse(client
->request
);
5138 * Then validate the request header and required attributes...
5141 major
= ippGetVersion(client
->request
, &minor
);
5143 if (major
< 1 || major
> 2)
5146 * Return an error, since we only support IPP 1.x and 2.x.
5149 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
5150 "Bad request version number %d.%d.", major
, minor
);
5152 else if (ippGetRequestId(client
->request
) <= 0)
5153 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
5154 ippGetRequestId(client
->request
));
5155 else if (!ippFirstAttribute(client
->request
))
5156 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5157 "No attributes in request.");
5161 * Make sure that the attributes are provided in the correct order and
5162 * don't repeat groups...
5165 for (attr
= ippFirstAttribute(client
->request
),
5166 group
= ippGetGroupTag(attr
);
5168 attr
= ippNextAttribute(client
->request
))
5170 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5173 * Out of order; return an error...
5176 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5177 "Attribute groups are out of order (%x < %x).",
5178 ippGetGroupTag(attr
), group
);
5182 group
= ippGetGroupTag(attr
);
5188 * Then make sure that the first three attributes are:
5190 * attributes-charset
5191 * attributes-natural-language
5192 * printer-uri/job-uri
5195 attr
= ippFirstAttribute(client
->request
);
5196 name
= ippGetName(attr
);
5197 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5198 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5203 attr
= ippNextAttribute(client
->request
);
5204 name
= ippGetName(attr
);
5206 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5207 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5212 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5213 IPP_TAG_URI
)) != NULL
)
5215 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5216 IPP_TAG_URI
)) != NULL
)
5222 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5223 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5226 * Bad character set...
5229 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5230 "Unsupported character set \"%s\".",
5231 ippGetString(charset
, 0, NULL
));
5233 else if (!charset
|| !language
|| !uri
)
5236 * Return an error, since attributes-charset,
5237 * attributes-natural-language, and printer-uri/job-uri are required
5238 * for all operations.
5241 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5242 "Missing required attributes.");
5246 char scheme
[32], /* URI scheme */
5247 userpass
[32], /* Username/password in URI */
5248 host
[256], /* Host name in URI */
5249 resource
[256]; /* Resource path in URI */
5250 int port
; /* Port number in URI */
5252 name
= ippGetName(uri
);
5254 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5255 scheme
, sizeof(scheme
),
5256 userpass
, sizeof(userpass
),
5257 host
, sizeof(host
), &port
,
5258 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5259 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5260 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5261 else if ((!strcmp(name
, "job-uri") &&
5262 strncmp(resource
, "/ipp/print/", 11)) ||
5263 (!strcmp(name
, "printer-uri") &&
5264 strcmp(resource
, "/ipp/print")))
5265 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5266 name
, ippGetString(uri
, 0, NULL
));
5270 * Try processing the operation...
5273 switch (ippGetOperation(client
->request
))
5275 case IPP_OP_PRINT_JOB
:
5276 ipp_print_job(client
);
5279 case IPP_OP_PRINT_URI
:
5280 ipp_print_uri(client
);
5283 case IPP_OP_VALIDATE_JOB
:
5284 ipp_validate_job(client
);
5287 case IPP_OP_CREATE_JOB
:
5288 ipp_create_job(client
);
5291 case IPP_OP_SEND_DOCUMENT
:
5292 ipp_send_document(client
);
5295 case IPP_OP_SEND_URI
:
5296 ipp_send_uri(client
);
5299 case IPP_OP_CANCEL_JOB
:
5300 ipp_cancel_job(client
);
5303 case IPP_OP_GET_JOB_ATTRIBUTES
:
5304 ipp_get_job_attributes(client
);
5307 case IPP_OP_GET_JOBS
:
5308 ipp_get_jobs(client
);
5311 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5312 ipp_get_printer_attributes(client
);
5315 case IPP_OP_CLOSE_JOB
:
5316 ipp_close_job(client
);
5319 case IPP_OP_IDENTIFY_PRINTER
:
5320 ipp_identify_printer(client
);
5324 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5325 "Operation not supported.");
5334 * Send the HTTP header and return...
5337 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5338 httpFlush(client
->http
); /* Flush trailing (junk) data */
5340 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5341 ippLength(client
->response
)));
5346 * 'process_job()' - Process a print job.
5349 static void * /* O - Thread exit status */
5350 process_job(_ipp_job_t
*job
) /* I - Job */
5352 job
->state
= IPP_JSTATE_PROCESSING
;
5353 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
5354 job
->processing
= time(NULL
);
5356 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
5358 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5363 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
5365 if (job
->printer
->command
)
5368 * Execute a command with the job spool file and wait for it to complete...
5371 int pid
, /* Process ID */
5372 status
; /* Exit status */
5373 time_t start
, /* Start time */
5375 char *myargv
[3], /* Command-line arguments */
5376 *myenvp
[200]; /* Environment variables */
5377 int myenvc
; /* Number of environment variables */
5378 ipp_attribute_t
*attr
; /* Job attribute */
5379 char val
[1280], /* IPP_NAME=value */
5380 *valptr
; /* Pointer into string */
5382 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
5387 * Setup the command-line arguments...
5390 myargv
[0] = job
->printer
->command
;
5391 myargv
[1] = job
->filename
;
5395 * Copy the current environment, then add ENV variables for every Job
5399 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
5400 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
5402 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
5405 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
5406 * value(s) from the attribute.
5409 const char *name
= ippGetName(attr
);
5418 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
5423 *valptr
++ = (char)toupper(*name
& 255);
5428 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
5430 myenvp
[myenvc
++] = strdup(val
);
5432 myenvp
[myenvc
] = NULL
;
5435 * Now run the program...
5439 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
5441 if ((pid
= fork()) == 0)
5444 * Child comes here...
5447 execve(job
->printer
->command
, myargv
, myenvp
);
5453 * Unable to fork process...
5456 perror("Unable to start job processing command");
5462 * Free memory used for environment...
5466 free(myenvp
[-- myenvc
]);
5469 * Wait for child to complete...
5472 # ifdef HAVE_WAITPID
5473 while (waitpid(pid
, &status
, 0) < 0);
5475 while (wait(&status
) < 0);
5476 # endif /* HAVE_WAITPID */
5483 if (WIFEXITED(status
))
5485 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
5486 job
->printer
->command
, WEXITSTATUS(status
));
5489 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
5490 job
->printer
->command
, WTERMSIG(status
));
5492 job
->state
= IPP_JSTATE_ABORTED
;
5494 else if (status
< 0)
5495 job
->state
= IPP_JSTATE_ABORTED
;
5497 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
5498 job
->printer
->command
);
5501 * Make sure processing takes at least 5 seconds...
5505 if ((end
- start
) < 5)
5511 * Sleep for a random amount of time to simulate job processing.
5514 sleep((unsigned)(5 + (rand() % 11)));
5518 job
->state
= IPP_JSTATE_CANCELED
;
5519 else if (job
->state
== IPP_JSTATE_PROCESSING
)
5520 job
->state
= IPP_JSTATE_COMPLETED
;
5522 job
->completed
= time(NULL
);
5523 job
->printer
->state
= IPP_PSTATE_IDLE
;
5524 job
->printer
->active_job
= NULL
;
5531 * 'register_printer()' - Register a printer object via Bonjour.
5534 static int /* O - 1 on success, 0 on error */
5536 _ipp_printer_t
*printer
, /* I - Printer */
5537 const char *location
, /* I - Location */
5538 const char *make
, /* I - Manufacturer */
5539 const char *model
, /* I - Model name */
5540 const char *formats
, /* I - Supported formats */
5541 const char *adminurl
, /* I - Web interface URL */
5542 const char *uuid
, /* I - Printer UUID */
5543 int color
, /* I - 1 = color, 0 = monochrome */
5544 int duplex
, /* I - 1 = duplex, 0 = simplex */
5545 const char *subtype
) /* I - Service subtype */
5547 _ipp_txt_t ipp_txt
; /* Bonjour IPP TXT record */
5549 DNSServiceErrorType error
; /* Error from Bonjour */
5550 char make_model
[256],/* Make and model together */
5551 product
[256], /* Product string */
5552 regtype
[256]; /* Bonjour service type */
5556 * Build the TXT record for IPP...
5559 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
5560 snprintf(product
, sizeof(product
), "(%s)", model
);
5562 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
5563 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
5564 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(make_model
),
5566 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(adminurl
),
5569 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(location
),
5571 TXTRecordSetValue(&ipp_txt
, "product", (uint8_t)strlen(product
),
5573 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
),
5575 TXTRecordSetValue(&ipp_txt
, "Color", 1, color
? "T" : "F");
5576 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, duplex
? "T" : "F");
5577 TXTRecordSetValue(&ipp_txt
, "usb_MFG", (uint8_t)strlen(make
),
5579 TXTRecordSetValue(&ipp_txt
, "usb_MDL", (uint8_t)strlen(model
),
5581 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(uuid
), uuid
);
5583 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
5584 # endif /* HAVE_SSL */
5587 * Register the _printer._tcp (LPD) service type with a port number of 0 to
5588 * defend our service name but not actually support LPD...
5591 printer
->printer_ref
= DNSSDMaster
;
5593 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
5594 kDNSServiceFlagsShareConnection
,
5595 0 /* interfaceIndex */, printer
->dnssd_name
,
5596 "_printer._tcp", NULL
/* domain */,
5597 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
5598 NULL
/* txtRecord */,
5599 (DNSServiceRegisterReply
)dnssd_callback
,
5600 printer
)) != kDNSServiceErr_NoError
)
5602 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
5603 printer
->dnssd_name
, error
);
5608 * Then register the _ipp._tcp (IPP) service type with the real port number to
5609 * advertise our IPP printer...
5612 printer
->ipp_ref
= DNSSDMaster
;
5614 if (subtype
&& *subtype
)
5615 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
5617 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
5619 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
5620 kDNSServiceFlagsShareConnection
,
5621 0 /* interfaceIndex */, printer
->dnssd_name
,
5622 regtype
, NULL
/* domain */,
5623 NULL
/* host */, htons(printer
->port
),
5624 TXTRecordGetLength(&ipp_txt
),
5625 TXTRecordGetBytesPtr(&ipp_txt
),
5626 (DNSServiceRegisterReply
)dnssd_callback
,
5627 printer
)) != kDNSServiceErr_NoError
)
5629 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5630 printer
->dnssd_name
, regtype
, error
);
5636 * Then register the _ipps._tcp (IPP) service type with the real port number to
5637 * advertise our IPPS printer...
5640 printer
->ipps_ref
= DNSSDMaster
;
5642 if (subtype
&& *subtype
)
5643 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
5645 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
5647 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
5648 kDNSServiceFlagsShareConnection
,
5649 0 /* interfaceIndex */, printer
->dnssd_name
,
5650 regtype
, NULL
/* domain */,
5651 NULL
/* host */, htons(printer
->port
),
5652 TXTRecordGetLength(&ipp_txt
),
5653 TXTRecordGetBytesPtr(&ipp_txt
),
5654 (DNSServiceRegisterReply
)dnssd_callback
,
5655 printer
)) != kDNSServiceErr_NoError
)
5657 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5658 printer
->dnssd_name
, regtype
, error
);
5661 # endif /* HAVE_SSL */
5664 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
5665 * real port number to advertise our IPP printer...
5668 printer
->http_ref
= DNSSDMaster
;
5670 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
5671 kDNSServiceFlagsShareConnection
,
5672 0 /* interfaceIndex */, printer
->dnssd_name
,
5673 "_http._tcp,_printer", NULL
/* domain */,
5674 NULL
/* host */, htons(printer
->port
),
5675 0 /* txtLen */, NULL
, /* txtRecord */
5676 (DNSServiceRegisterReply
)dnssd_callback
,
5677 printer
)) != kDNSServiceErr_NoError
)
5679 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5680 printer
->dnssd_name
, regtype
, error
);
5684 TXTRecordDeallocate(&ipp_txt
);
5686 #elif defined(HAVE_AVAHI)
5687 char temp
[256]; /* Subtype service string */
5690 * Create the TXT record...
5694 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
5695 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s %s", make
, model
);
5696 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", adminurl
);
5698 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", location
);
5699 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "product=(%s)", model
);
5700 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
5701 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", color
? "T" : "F");
5702 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", duplex
? "T" : "F");
5703 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MFG=%s", make
);
5704 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MDL=%s", model
);
5705 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", uuid
);
5707 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
5708 # endif /* HAVE_SSL */
5711 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
5714 avahi_threaded_poll_lock(DNSSDMaster
);
5716 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
5718 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
);
5721 * Then register the _ipp._tcp (IPP)...
5724 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
);
5725 if (subtype
&& *subtype
)
5727 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
5728 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
5733 * _ipps._tcp (IPPS) for secure printing...
5736 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
);
5737 if (subtype
&& *subtype
)
5739 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
5740 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
5742 #endif /* HAVE_SSL */
5745 * Finally _http.tcp (HTTP) for the web interface...
5748 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
);
5749 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");
5755 avahi_entry_group_commit(printer
->ipp_ref
);
5756 avahi_threaded_poll_unlock(DNSSDMaster
);
5758 avahi_string_list_free(ipp_txt
);
5759 #endif /* HAVE_DNSSD */
5766 * 'respond_http()' - Send a HTTP response.
5769 int /* O - 1 on success, 0 on failure */
5771 _ipp_client_t
*client
, /* I - Client */
5772 http_status_t code
, /* I - HTTP status of response */
5773 const char *content_encoding
, /* I - Content-Encoding of response */
5774 const char *type
, /* I - MIME media type of response */
5775 size_t length
) /* I - Length of response */
5777 char message
[1024]; /* Text message */
5780 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
5782 if (code
== HTTP_STATUS_CONTINUE
)
5785 * 100-continue doesn't send any headers...
5788 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
5792 * Format an error message...
5795 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
5797 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
5799 type
= "text/plain";
5800 length
= strlen(message
);
5806 * Send the HTTP response header...
5809 httpClearFields(client
->http
);
5811 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
5812 client
->operation
== HTTP_STATE_OPTIONS
)
5813 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
5817 if (!strcmp(type
, "text/html"))
5818 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
5819 "text/html; charset=utf-8");
5821 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
5823 if (content_encoding
)
5824 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
5827 httpSetLength(client
->http
, length
);
5829 if (httpWriteResponse(client
->http
, code
) < 0)
5833 * Send the response data...
5839 * Send a plain text message.
5842 if (httpPrintf(client
->http
, "%s", message
) < 0)
5845 if (httpWrite2(client
->http
, "", 0) < 0)
5848 else if (client
->response
)
5851 * Send an IPP response...
5854 debug_attributes("Response", client
->response
, 2);
5856 ippSetState(client
->response
, IPP_STATE_IDLE
);
5858 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
5867 * 'respond_ipp()' - Send an IPP response.
5871 respond_ipp(_ipp_client_t
*client
, /* I - Client */
5872 ipp_status_t status
, /* I - status-code */
5873 const char *message
, /* I - printf-style status-message */
5874 ...) /* I - Additional args as needed */
5876 const char *formatted
= NULL
; /* Formatted message */
5879 ippSetStatusCode(client
->response
, status
);
5883 va_list ap
; /* Pointer to additional args */
5884 ipp_attribute_t
*attr
; /* New status-message attribute */
5886 va_start(ap
, message
);
5887 if ((attr
= ippFindAttribute(client
->response
, "status-message",
5888 IPP_TAG_TEXT
)) != NULL
)
5889 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
5891 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
5892 "status-message", NULL
, message
, ap
);
5895 formatted
= ippGetString(attr
, 0, NULL
);
5899 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
5900 ippOpString(client
->operation_id
), ippErrorString(status
),
5903 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
5904 ippOpString(client
->operation_id
), ippErrorString(status
));
5909 * 'respond_unsupported()' - Respond with an unsupported attribute.
5913 respond_unsupported(
5914 _ipp_client_t
*client
, /* I - Client */
5915 ipp_attribute_t
*attr
) /* I - Atribute */
5917 ipp_attribute_t
*temp
; /* Copy of attribute */
5920 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5921 "Unsupported %s %s%s value.", ippGetName(attr
),
5922 ippGetCount(attr
) > 1 ? "1setOf " : "",
5923 ippTagString(ippGetValueTag(attr
)));
5925 temp
= ippCopyAttribute(client
->response
, attr
, 0);
5926 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
5931 * 'run_printer()' - Run the printer service.
5935 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
5937 int num_fds
; /* Number of file descriptors */
5938 struct pollfd polldata
[3]; /* poll() data */
5939 int timeout
; /* Timeout for poll() */
5940 _ipp_client_t
*client
; /* New client */
5944 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5947 polldata
[0].fd
= printer
->ipv4
;
5948 polldata
[0].events
= POLLIN
;
5950 polldata
[1].fd
= printer
->ipv6
;
5951 polldata
[1].events
= POLLIN
;
5956 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
5957 polldata
[num_fds
++].events
= POLLIN
;
5958 #endif /* HAVE_DNSSD */
5961 * Loop until we are killed or have a hard error...
5966 if (cupsArrayCount(printer
->jobs
))
5971 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
5973 perror("poll() failed");
5977 if (polldata
[0].revents
& POLLIN
)
5979 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
5981 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5983 perror("Unable to create client thread");
5984 delete_client(client
);
5989 if (polldata
[1].revents
& POLLIN
)
5991 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
5993 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5995 perror("Unable to create client thread");
5996 delete_client(client
);
6002 if (polldata
[2].revents
& POLLIN
)
6003 DNSServiceProcessResult(DNSSDMaster
);
6004 #endif /* HAVE_DNSSD */
6007 * Clean out old jobs...
6010 clean_jobs(printer
);
6016 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6020 time_string(time_t tv
, /* I - Time value */
6021 char *buffer
, /* I - Buffer */
6022 size_t bufsize
) /* I - Size of buffer */
6024 struct tm
*curtime
= localtime(&tv
);
6027 strftime(buffer
, bufsize
, "%X", curtime
);
6033 * 'usage()' - Show program usage.
6037 usage(int status
) /* O - Exit status */
6041 puts(CUPS_SVERSION
" - Copyright 2010-2014 by Apple Inc. All rights "
6046 puts("Usage: ippserver [options] \"name\"");
6049 puts("-2 Supports 2-sided printing (default=1-sided)");
6050 puts("-M manufacturer Manufacturer name (default=Test)");
6051 puts("-P PIN printing mode");
6052 puts("-c command Run command for every print job");
6053 printf("-d spool-directory Spool directory "
6054 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6055 puts("-f type/subtype[,...] List of supported types "
6056 "(default=application/pdf,image/jpeg)");
6057 puts("-h Show program help");
6058 puts("-i iconfile.png PNG icon file (default=printer.png)");
6059 puts("-k Keep job spool files");
6060 puts("-l location Location of printer (default=empty string)");
6061 puts("-m model Model name (default=Printer)");
6062 puts("-n hostname Hostname for printer");
6063 puts("-p port Port number (default=auto)");
6064 puts("-r subtype Bonjour service subtype (default=_print)");
6065 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6066 puts("-v[vvv] Be (very) verbose");
6073 * 'valid_doc_attributes()' - Determine whether the document attributes are
6076 * When one or more document attributes are invalid, this function adds a
6077 * suitable response and attributes to the unsupported group.
6080 static int /* O - 1 if valid, 0 if not */
6081 valid_doc_attributes(
6082 _ipp_client_t
*client
) /* I - Client */
6084 int valid
= 1; /* Valid attributes? */
6085 ipp_op_t op
= ippGetOperation(client
->request
);
6087 const char *op_name
= ippOpString(op
);
6088 /* IPP operation name */
6089 ipp_attribute_t
*attr
, /* Current attribute */
6090 *supported
; /* xxx-supported attribute */
6091 const char *compression
= NULL
,
6092 /* compression value */
6093 *format
= NULL
; /* document-format value */
6097 * Check operation attributes...
6100 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6103 * If compression is specified, only accept a supported value in a Print-Job
6104 * or Send-Document request...
6107 compression
= ippGetString(attr
, 0, NULL
);
6108 supported
= ippFindAttribute(client
->printer
->attrs
,
6109 "compression-supported", IPP_TAG_KEYWORD
);
6111 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6112 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6113 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6114 op
!= IPP_OP_VALIDATE_JOB
) ||
6115 !ippContainsString(supported
, compression
))
6117 respond_unsupported(client
, attr
);
6122 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6124 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6126 if (strcmp(compression
, "none"))
6129 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6130 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
6136 * Is it a format we support?
6139 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
6141 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
6142 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
6144 respond_unsupported(client
, attr
);
6149 format
= ippGetString(attr
, 0, NULL
);
6151 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
6152 client
->hostname
, op_name
, format
);
6154 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
6159 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
6161 format
= "application/octet-stream"; /* Should never happen */
6163 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
6166 if (!strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
6169 * Auto-type the file using the first 8 bytes of the file...
6172 unsigned char header
[8]; /* First 8 bytes of file */
6174 memset(header
, 0, sizeof(header
));
6175 httpPeek(client
->http
, (char *)header
, sizeof(header
));
6177 if (!memcmp(header
, "%PDF", 4))
6178 format
= "application/pdf";
6179 else if (!memcmp(header
, "%!", 2))
6180 format
= "application/postscript";
6181 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
6182 format
= "image/jpeg";
6183 else if (!memcmp(header
, "\211PNG", 4))
6184 format
= "image/png";
6185 else if (!memcmp(header
, "RAS2", 4))
6186 format
= "image/pwg-raster";
6187 else if (!memcmp(header
, "UNIRAST", 8))
6188 format
= "image/urf";
6194 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
6195 client
->hostname
, op_name
, format
);
6197 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
6201 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
6203 respond_unsupported(client
, attr
);
6211 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
6212 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
6219 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6221 * When one or more job attributes are invalid, this function adds a suitable
6222 * response and attributes to the unsupported group.
6225 static int /* O - 1 if valid, 0 if not */
6226 valid_job_attributes(
6227 _ipp_client_t
*client
) /* I - Client */
6229 int i
, /* Looping var */
6230 valid
= 1; /* Valid attributes? */
6231 ipp_attribute_t
*attr
, /* Current attribute */
6232 *supported
; /* xxx-supported attribute */
6236 * Check operation attributes...
6239 valid
= valid_doc_attributes(client
);
6242 * Check the various job template attributes...
6245 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
6247 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6248 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
6250 respond_unsupported(client
, attr
);
6255 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
6257 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
6259 respond_unsupported(client
, attr
);
6264 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
6266 if (ippGetCount(attr
) != 1 ||
6267 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6268 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6269 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6270 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
6272 respond_unsupported(client
, attr
);
6277 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
6279 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
6281 respond_unsupported(client
, attr
);
6286 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
6288 if (ippGetCount(attr
) != 1 ||
6289 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6290 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
6292 respond_unsupported(client
, attr
);
6296 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
6299 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
6301 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
6303 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6304 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
6306 respond_unsupported(client
, attr
);
6311 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
6313 if (ippGetCount(attr
) != 1 ||
6314 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6315 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6316 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6317 strcmp(ippGetString(attr
, 0, NULL
), "none"))
6319 respond_unsupported(client
, attr
);
6324 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
6326 if (ippGetCount(attr
) != 1 ||
6327 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6328 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6329 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
6331 respond_unsupported(client
, attr
);
6337 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
6339 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
6342 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
6344 respond_unsupported(client
, attr
);
6350 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
6352 if (ippGetCount(attr
) != 1 ||
6353 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
6355 respond_unsupported(client
, attr
);
6358 /* TODO: check for valid media-col */
6361 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
6363 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6364 (strcmp(ippGetString(attr
, 0, NULL
),
6365 "separate-documents-uncollated-copies") &&
6366 strcmp(ippGetString(attr
, 0, NULL
),
6367 "separate-documents-collated-copies")))
6369 respond_unsupported(client
, attr
);
6374 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
6376 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6377 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
6378 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
6380 respond_unsupported(client
, attr
);
6385 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
6387 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
6389 respond_unsupported(client
, attr
);
6394 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
6396 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6397 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
6398 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
6400 respond_unsupported(client
, attr
);
6405 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
6407 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
6409 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
6412 respond_unsupported(client
, attr
);
6417 int count
, /* Number of supported values */
6418 xdpi
, /* Horizontal resolution for job template attribute */
6419 ydpi
, /* Vertical resolution for job template attribute */
6420 sydpi
; /* Vertical resolution for supported value */
6421 ipp_res_t units
, /* Units for job template attribute */
6422 sunits
; /* Units for supported value */
6424 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
6425 count
= ippGetCount(supported
);
6427 for (i
= 0; i
< count
; i
++)
6429 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
6435 respond_unsupported(client
, attr
);
6441 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
6443 const char *sides
= ippGetString(attr
, 0, NULL
);
6444 /* "sides" value... */
6446 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
6448 respond_unsupported(client
, attr
);
6451 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
6453 if (!ippContainsString(supported
, sides
))
6455 respond_unsupported(client
, attr
);
6459 else if (strcmp(sides
, "one-sided"))
6461 respond_unsupported(client
, attr
);