4 * Sample IPP Everywhere server for CUPS.
6 * Copyright 2010-2015 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * Disable private and deprecated stuff so we can verify that the public API
19 * is sufficient to implement a server.
22 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
23 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
27 * Include necessary headers...
30 #include <config.h> /* CUPS configuration header */
31 #include <cups/cups.h> /* Public API */
32 #include <cups/string-private.h> /* CUPS string functions */
33 #include <cups/thread-private.h> /* For multithreading functions */
46 # define WEXITSTATUS(s) (s)
47 # include <winsock2.h>
51 extern char **environ
;
53 # include <sys/fcntl.h>
54 # include <sys/wait.h>
60 #elif defined(HAVE_AVAHI)
61 # include <avahi-client/client.h>
62 # include <avahi-client/publish.h>
63 # include <avahi-common/error.h>
64 # include <avahi-common/thread-watch.h>
65 #endif /* HAVE_DNSSD */
66 #ifdef HAVE_SYS_MOUNT_H
67 # include <sys/mount.h>
68 #endif /* HAVE_SYS_MOUNT_H */
69 #ifdef HAVE_SYS_STATFS_H
70 # include <sys/statfs.h>
71 #endif /* HAVE_SYS_STATFS_H */
72 #ifdef HAVE_SYS_STATVFS_H
73 # include <sys/statvfs.h>
74 #endif /* HAVE_SYS_STATVFS_H */
77 #endif /* HAVE_SYS_VFS_H */
84 enum _ipp_preason_e
/* printer-state-reasons bit values */
86 _IPP_PREASON_NONE
= 0x0000, /* none */
87 _IPP_PREASON_OTHER
= 0x0001, /* other */
88 _IPP_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
89 _IPP_PREASON_INPUT_TRAY_MISSING
= 0x0004,
90 /* input-tray-missing */
91 _IPP_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
92 /* marker-supply-empty */
93 _IPP_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
94 /* marker-supply-low */
95 _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
96 /* marker-waste-almost-full */
97 _IPP_PREASON_MARKER_WASTE_FULL
= 0x0040,
98 /* marker-waste-full */
99 _IPP_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
100 _IPP_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
101 _IPP_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
102 _IPP_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
103 _IPP_PREASON_MOVING_TO_PAUSED
= 0x0800,
104 /* moving-to-paused */
105 _IPP_PREASON_PAUSED
= 0x1000, /* paused */
106 _IPP_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
107 _IPP_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
108 _IPP_PREASON_TONER_LOW
= 0x8000 /* toner-low */
110 typedef unsigned int _ipp_preason_t
; /* Bitfield for printer-state-reasons */
112 typedef enum _ipp_media_class_e
114 _IPP_GENERAL
, /* General-purpose size */
115 _IPP_PHOTO_ONLY
, /* Photo-only size */
116 _IPP_ENV_ONLY
/* Envelope-only size */
117 } _ipp_media_class_t
;
119 typedef enum _ipp_media_size_e
121 _IPP_MEDIA_SIZE_NONE
= -1,
126 _IPP_MEDIA_SIZE_LEGAL
,
127 _IPP_MEDIA_SIZE_LETTER
,
128 _IPP_MEDIA_SIZE_COM10
,
134 static const char * const media_supported
[] =
135 { /* media-supported values */
136 "iso_a4_210x297mm", /* A4 */
137 "iso_a5_148x210mm", /* A5 */
138 "iso_a6_105x148mm", /* A6 */
139 "iso_dl_110x220mm", /* DL */
140 "na_legal_8.5x14in", /* Legal */
141 "na_letter_8.5x11in", /* Letter */
142 "na_number-10_4.125x9.5in", /* #10 */
143 "na_index-3x5_3x5in", /* 3x5 */
144 "oe_photo-l_3.5x5in", /* L */
145 "na_index-4x6_4x6in", /* 4x6 */
146 "na_5x7_5x7in" /* 5x7 aka 2L */
148 static const int media_col_sizes
[][3] =
149 { /* media-col-database sizes */
150 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
151 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
152 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
153 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
154 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
155 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
156 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
157 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
158 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
159 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
160 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
163 typedef enum _ipp_media_source_e
165 _IPP_MEDIA_SOURCE_NONE
= -1,
166 _IPP_MEDIA_SOURCE_AUTO
,
167 _IPP_MEDIA_SOURCE_MAIN
,
168 _IPP_MEDIA_SOURCE_MANUAL
,
169 _IPP_MEDIA_SOURCE_ENVELOPE
,
170 _IPP_MEDIA_SOURCE_PHOTO
171 } _ipp_media_source_t
;
172 static const char * const media_source_supported
[] =
173 /* media-source-supported values */
182 typedef enum _ipp_media_type_e
184 _IPP_MEDIA_TYPE_NONE
= -1,
185 _IPP_MEDIA_TYPE_AUTO
,
186 _IPP_MEDIA_TYPE_CARDSTOCK
,
187 _IPP_MEDIA_TYPE_ENVELOPE
,
188 _IPP_MEDIA_TYPE_LABELS
,
189 _IPP_MEDIA_TYPE_OTHER
,
190 _IPP_MEDIA_TYPE_GLOSSY
,
191 _IPP_MEDIA_TYPE_HIGH_GLOSS
,
192 _IPP_MEDIA_TYPE_MATTE
,
193 _IPP_MEDIA_TYPE_SATIN
,
194 _IPP_MEDIA_TYPE_SEMI_GLOSS
,
195 _IPP_MEDIA_TYPE_STATIONERY
,
196 _IPP_MEDIA_TYPE_LETTERHEAD
,
197 _IPP_MEDIA_TYPE_TRANSPARENCY
199 static const char * const media_type_supported
[] =
200 /* media-type-supported values */
207 "photographic-glossy",
208 "photographic-high-gloss",
209 "photographic-matte",
210 "photographic-satin",
211 "photographic-semi-gloss",
213 "stationery-letterhead",
217 typedef enum _ipp_supply_e
219 _IPP_SUPPLY_CYAN
, /* Cyan Toner */
220 _IPP_SUPPLY_MAGENTA
, /* Magenta Toner */
221 _IPP_SUPPLY_YELLOW
, /* Yellow Toner */
222 _IPP_SUPPLY_BLACK
, /* Black Toner */
223 _IPP_SUPPLY_WASTE
/* Waste Toner */
225 static const char * const printer_supplies
[] =
226 { /* printer-supply-description values */
240 typedef DNSServiceRef _ipp_srv_t
; /* Service reference */
241 typedef TXTRecordRef _ipp_txt_t
; /* TXT record */
243 #elif defined(HAVE_AVAHI)
244 typedef AvahiEntryGroup
*_ipp_srv_t
; /* Service reference */
245 typedef AvahiStringList
*_ipp_txt_t
; /* TXT record */
248 typedef void *_ipp_srv_t
; /* Service reference */
249 typedef void *_ipp_txt_t
; /* TXT record */
250 #endif /* HAVE_DNSSD */
252 typedef struct _ipp_filter_s
/**** Attribute filter ****/
254 cups_array_t
*ra
; /* Requested attributes */
255 ipp_tag_t group_tag
; /* Group to copy */
258 typedef struct _ipp_job_s _ipp_job_t
;
260 typedef struct _ipp_printer_s
/**** Printer data ****/
262 int ipv4
, /* IPv4 listener */
263 ipv6
; /* IPv6 listener */
264 _ipp_srv_t ipp_ref
, /* Bonjour IPP service */
265 ipps_ref
, /* Bonjour IPPS service */
266 http_ref
, /* Bonjour HTTP service */
267 printer_ref
; /* Bonjour LPD service */
268 char *dnssd_name
, /* printer-dnssd-name */
269 *name
, /* printer-name */
270 *icon
, /* Icon filename */
271 *directory
, /* Spool directory */
272 *hostname
, /* Hostname */
273 *uri
, /* printer-uri-supported */
274 *command
; /* Command to run with job file */
276 size_t urilen
; /* Length of printer URI */
277 ipp_t
*attrs
; /* Static attributes */
278 time_t start_time
; /* Startup time */
279 time_t config_time
; /* printer-config-change-time */
280 ipp_pstate_t state
; /* printer-state value */
281 _ipp_preason_t state_reasons
; /* printer-state-reasons values */
282 time_t state_time
; /* printer-state-change-time */
283 cups_array_t
*jobs
; /* Jobs */
284 _ipp_job_t
*active_job
; /* Current active/pending job */
285 int next_job_id
; /* Next job-id value */
286 _cups_rwlock_t rwlock
; /* Printer lock */
287 _ipp_media_size_t main_size
; /* Ready media */
288 _ipp_media_type_t main_type
;
290 _ipp_media_size_t envelope_size
;
292 _ipp_media_size_t photo_size
;
293 _ipp_media_type_t photo_type
;
295 int supplies
[5]; /* Supply levels (0-100) */
298 struct _ipp_job_s
/**** Job data ****/
301 const char *name
, /* job-name */
302 *username
, /* job-originating-user-name */
303 *format
; /* document-format */
304 ipp_jstate_t state
; /* job-state value */
305 time_t created
, /* time-at-creation value */
306 processing
, /* time-at-processing value */
307 completed
; /* time-at-completed value */
308 int impressions
, /* job-impressions value */
309 impcompleted
; /* job-impressions-completed value */
310 ipp_t
*attrs
; /* Static attributes */
311 int cancel
; /* Non-zero when job canceled */
312 char *filename
; /* Print file name */
313 int fd
; /* Print file descriptor */
314 _ipp_printer_t
*printer
; /* Printer */
317 typedef struct _ipp_client_s
/**** Client data ****/
319 http_t
*http
; /* HTTP connection */
320 ipp_t
*request
, /* IPP request */
321 *response
; /* IPP response */
322 time_t start
; /* Request start time */
323 http_state_t operation
; /* Request operation */
324 ipp_op_t operation_id
; /* IPP operation-id */
325 char uri
[1024], /* Request URI */
326 *options
; /* URI options */
327 http_addr_t addr
; /* Client address */
328 char hostname
[256]; /* Client hostname */
329 _ipp_printer_t
*printer
; /* Printer */
330 _ipp_job_t
*job
; /* Current job, if any */
338 static void clean_jobs(_ipp_printer_t
*printer
);
339 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
340 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
341 ipp_tag_t group_tag
, int quickcopy
);
342 static void copy_job_attributes(_ipp_client_t
*client
,
343 _ipp_job_t
*job
, cups_array_t
*ra
);
344 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
345 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
346 static int create_listener(int family
, int port
);
347 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
348 static ipp_t
*create_media_size(int width
, int length
);
349 static _ipp_printer_t
*create_printer(const char *servername
,
350 const char *name
, const char *location
,
351 const char *make
, const char *model
,
353 const char *docformats
, int ppm
,
354 int ppm_color
, int duplex
, int port
,
355 int pin
, const char *subtype
,
356 const char *directory
,
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_API
dnssd_callback(DNSServiceRef sdRef
,
365 DNSServiceFlags flags
,
366 DNSServiceErrorType errorCode
,
370 _ipp_printer_t
*printer
);
371 #elif defined(HAVE_AVAHI)
372 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
373 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
374 #endif /* HAVE_DNSSD */
375 static void dnssd_init(void);
376 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
377 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
378 static void html_escape(_ipp_client_t
*client
, const char *s
,
380 static void html_footer(_ipp_client_t
*client
);
381 static void html_header(_ipp_client_t
*client
, const char *title
);
382 static void html_printf(_ipp_client_t
*client
, const char *format
,
383 ...) __attribute__((__format__(__printf__
,
385 static void ipp_cancel_job(_ipp_client_t
*client
);
386 static void ipp_close_job(_ipp_client_t
*client
);
387 static void ipp_create_job(_ipp_client_t
*client
);
388 static void ipp_get_job_attributes(_ipp_client_t
*client
);
389 static void ipp_get_jobs(_ipp_client_t
*client
);
390 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
391 static void ipp_identify_printer(_ipp_client_t
*client
);
392 static void ipp_print_job(_ipp_client_t
*client
);
393 static void ipp_print_uri(_ipp_client_t
*client
);
394 static void ipp_send_document(_ipp_client_t
*client
);
395 static void ipp_send_uri(_ipp_client_t
*client
);
396 static void ipp_validate_job(_ipp_client_t
*client
);
397 static int parse_options(_ipp_client_t
*client
, cups_option_t
**options
);
398 static void *process_client(_ipp_client_t
*client
);
399 static int process_http(_ipp_client_t
*client
);
400 static int process_ipp(_ipp_client_t
*client
);
401 static void *process_job(_ipp_job_t
*job
);
402 static int register_printer(_ipp_printer_t
*printer
, const char *location
, const char *make
, const char *model
, const char *formats
, const char *adminurl
, const char *uuid
, int color
, int duplex
, const char *regtype
);
403 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
404 const char *content_coding
,
405 const char *type
, size_t length
);
406 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
407 const char *message
, ...)
408 __attribute__ ((__format__ (__printf__
, 3, 4)));
409 static void respond_unsupported(_ipp_client_t
*client
,
410 ipp_attribute_t
*attr
);
411 static void run_printer(_ipp_printer_t
*printer
);
412 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
413 static void usage(int status
) __attribute__((noreturn
));
414 static int valid_doc_attributes(_ipp_client_t
*client
);
415 static int valid_job_attributes(_ipp_client_t
*client
);
423 static DNSServiceRef DNSSDMaster
= NULL
;
424 #elif defined(HAVE_AVAHI)
425 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
426 static AvahiClient
*DNSSDClient
= NULL
;
427 #endif /* HAVE_DNSSD */
429 static int KeepFiles
= 0,
434 * 'main()' - Main entry to the sample server.
437 int /* O - Exit status */
438 main(int argc
, /* I - Number of command-line args */
439 char *argv
[]) /* I - Command-line arguments */
441 int i
; /* Looping var */
442 const char *opt
, /* Current option character */
443 *command
= NULL
, /* Command to run with job files */
444 *servername
= NULL
, /* Server host name */
445 *name
= NULL
, /* Printer name */
446 *location
= "", /* Location of printer */
447 *make
= "Test", /* Manufacturer */
448 *model
= "Printer", /* Model */
449 *icon
= "printer.png", /* Icon file */
450 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
451 /* Supported formats */
453 const char *keypath
= NULL
; /* Keychain path */
454 #endif /* HAVE_SSL */
455 const char *subtype
= "_print"; /* Bonjour service subtype */
456 int port
= 0, /* Port number (0 = auto) */
457 duplex
= 0, /* Duplex mode */
458 ppm
= 10, /* Pages per minute for mono */
459 ppm_color
= 0, /* Pages per minute for color */
460 pin
= 0; /* PIN printing mode? */
461 char directory
[1024] = "", /* Spool directory */
462 hostname
[1024]; /* Auto-detected hostname */
463 _ipp_printer_t
*printer
; /* Printer object */
467 * Parse command-line arguments...
470 for (i
= 1; i
< argc
; i
++)
471 if (argv
[i
][0] == '-')
473 for (opt
= argv
[i
] + 1; *opt
; opt
++)
477 case '2' : /* -2 (enable 2-sided printing) */
482 case 'K' : /* -K keypath */
488 #endif /* HAVE_SSL */
490 case 'M' : /* -M manufacturer */
497 case 'P' : /* -P (PIN printing mode) */
501 case 'c' : /* -c command */
509 case 'd' : /* -d spool-directory */
513 strlcpy(directory
, argv
[i
], sizeof(directory
));
516 case 'f' : /* -f type/subtype[,...] */
523 case 'h' : /* -h (show help) */
526 case 'i' : /* -i icon.png */
533 case 'k' : /* -k (keep files) */
537 case 'l' : /* -l location */
544 case 'm' : /* -m model */
551 case 'n' : /* -n hostname */
555 servername
= argv
[i
];
558 case 'p' : /* -p port */
560 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
562 port
= atoi(argv
[i
]);
565 case 'r' : /* -r subtype */
572 case 's' : /* -s speed[,color-speed] */
576 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
580 case 'v' : /* -v (be verbose) */
584 default : /* Unknown */
585 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
596 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
604 * Apply defaults as needed...
608 servername
= httpGetHostname(NULL
, hostname
, sizeof(hostname
));
614 * Windows is almost always used as a single user system, so use a default
615 * port number of 8631.
622 * Use 8000 + UID mod 1000 for the default port number...
625 port
= 8000 + ((int)getuid() % 1000);
628 fprintf(stderr
, "Listening on port %d.\n", port
);
633 const char *tmpdir
; /* Temporary directory */
636 if ((tmpdir
= getenv("TEMP")) == NULL
)
638 #elif defined(__APPLE__)
639 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
640 tmpdir
= "/private/tmp";
642 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
646 snprintf(directory
, sizeof(directory
), "%s/ippserver.%d", tmpdir
, (int)getpid());
648 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
650 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
651 directory
, strerror(errno
));
656 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
660 cupsSetServerCredentials(keypath
, servername
, 1);
661 #endif /* HAVE_SSL */
664 * Initialize Bonjour...
670 * Create the printer...
673 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
674 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
675 subtype
, directory
, command
)) == NULL
)
679 * Run the print service...
682 run_printer(printer
);
685 * Destroy the printer and exit...
688 delete_printer(printer
);
695 * 'clean_jobs()' - Clean out old (completed) jobs.
699 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
701 _ipp_job_t
*job
; /* Current job */
702 time_t cleantime
; /* Clean time */
705 if (cupsArrayCount(printer
->jobs
) == 0)
708 cleantime
= time(NULL
) - 60;
710 _cupsRWLockWrite(&(printer
->rwlock
));
711 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
713 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
714 if (job
->completed
&& job
->completed
< cleantime
)
716 cupsArrayRemove(printer
->jobs
, job
);
721 _cupsRWUnlock(&(printer
->rwlock
));
726 * 'compare_jobs()' - Compare two jobs.
729 static int /* O - Result of comparison */
730 compare_jobs(_ipp_job_t
*a
, /* I - First job */
731 _ipp_job_t
*b
) /* I - Second job */
733 return (b
->id
- a
->id
);
738 * 'copy_attributes()' - Copy attributes from one request to another.
742 copy_attributes(ipp_t
*to
, /* I - Destination request */
743 ipp_t
*from
, /* I - Source request */
744 cups_array_t
*ra
, /* I - Requested attributes */
745 ipp_tag_t group_tag
, /* I - Group to copy */
746 int quickcopy
) /* I - Do a quick copy? */
748 _ipp_filter_t filter
; /* Filter data */
752 filter
.group_tag
= group_tag
;
754 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
759 * 'copy_job_attrs()' - Copy job attributes to the response.
764 _ipp_client_t
*client
, /* I - Client */
765 _ipp_job_t
*job
, /* I - Job */
766 cups_array_t
*ra
) /* I - requested-attributes */
768 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
770 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
773 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
775 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
778 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
781 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
783 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
786 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
787 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
789 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
790 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
792 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
793 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
795 if (!ra
|| cupsArrayFind(ra
, "job-state"))
796 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
797 "job-state", job
->state
);
799 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
803 case IPP_JSTATE_PENDING
:
804 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
807 case IPP_JSTATE_HELD
:
809 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
810 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
811 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
813 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
816 case IPP_JSTATE_PROCESSING
:
818 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
820 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
823 case IPP_JSTATE_STOPPED
:
824 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
827 case IPP_JSTATE_CANCELED
:
828 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
831 case IPP_JSTATE_ABORTED
:
832 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
835 case IPP_JSTATE_COMPLETED
:
836 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
841 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
845 case IPP_JSTATE_PENDING
:
846 ippAddString(client
->response
, IPP_TAG_JOB
,
847 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
851 case IPP_JSTATE_HELD
:
853 ippAddString(client
->response
, IPP_TAG_JOB
,
854 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
855 "job-state-reasons", NULL
, "job-incoming");
856 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
857 ippAddString(client
->response
, IPP_TAG_JOB
,
858 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
859 "job-state-reasons", NULL
, "job-hold-until-specified");
861 ippAddString(client
->response
, IPP_TAG_JOB
,
862 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
863 "job-state-reasons", NULL
, "job-data-insufficient");
866 case IPP_JSTATE_PROCESSING
:
868 ippAddString(client
->response
, IPP_TAG_JOB
,
869 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
870 "job-state-reasons", NULL
, "processing-to-stop-point");
872 ippAddString(client
->response
, IPP_TAG_JOB
,
873 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
874 "job-state-reasons", NULL
, "job-printing");
877 case IPP_JSTATE_STOPPED
:
878 ippAddString(client
->response
, IPP_TAG_JOB
,
879 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
880 NULL
, "job-stopped");
883 case IPP_JSTATE_CANCELED
:
884 ippAddString(client
->response
, IPP_TAG_JOB
,
885 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
886 NULL
, "job-canceled-by-user");
889 case IPP_JSTATE_ABORTED
:
890 ippAddString(client
->response
, IPP_TAG_JOB
,
891 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
892 NULL
, "aborted-by-system");
895 case IPP_JSTATE_COMPLETED
:
896 ippAddString(client
->response
, IPP_TAG_JOB
,
897 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
898 NULL
, "job-completed-successfully");
903 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
904 ippAddInteger(client
->response
, IPP_TAG_JOB
,
905 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
906 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
908 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
909 ippAddInteger(client
->response
, IPP_TAG_JOB
,
910 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
911 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
916 * 'create_client()' - Accept a new network connection and create a client
920 static _ipp_client_t
* /* O - Client */
921 create_client(_ipp_printer_t
*printer
, /* I - Printer */
922 int sock
) /* I - Listen socket */
924 _ipp_client_t
*client
; /* Client */
927 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
929 perror("Unable to allocate memory for client");
933 client
->printer
= printer
;
936 * Accept the client and get the remote address...
939 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
941 perror("Unable to accept client connection");
948 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
951 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
958 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
962 static _ipp_job_t
* /* O - Job */
963 create_job(_ipp_client_t
*client
) /* I - Client */
965 _ipp_job_t
*job
; /* Job */
966 ipp_attribute_t
*attr
; /* Job attribute */
967 char uri
[1024], /* job-uri value */
968 uuid
[64]; /* job-uuid value */
971 _cupsRWLockWrite(&(client
->printer
->rwlock
));
972 if (client
->printer
->active_job
&&
973 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
976 * Only accept a single job at a time...
979 _cupsRWLockWrite(&(client
->printer
->rwlock
));
984 * Allocate and initialize the job object...
987 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
989 perror("Unable to allocate memory for job");
993 job
->printer
= client
->printer
;
994 job
->attrs
= ippNew();
995 job
->state
= IPP_JSTATE_HELD
;
999 * Copy all of the job attributes...
1002 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
1005 * Get the requesting-user-name, document format, and priority...
1008 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1009 job
->username
= ippGetString(attr
, 0, NULL
);
1011 job
->username
= "anonymous";
1013 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1015 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1017 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1018 job
->format
= ippGetString(attr
, 0, NULL
);
1019 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1020 job
->format
= ippGetString(attr
, 0, NULL
);
1022 job
->format
= "application/octet-stream";
1025 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1026 job
->impressions
= ippGetInteger(attr
, 0);
1028 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1029 job
->name
= ippGetString(attr
, 0, NULL
);
1032 * Add job description attributes and add to the jobs array...
1035 job
->id
= client
->printer
->next_job_id
++;
1037 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1038 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1040 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1041 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1042 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1043 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1044 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1045 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1047 cupsArrayAdd(client
->printer
->jobs
, job
);
1048 client
->printer
->active_job
= job
;
1050 _cupsRWUnlock(&(client
->printer
->rwlock
));
1057 * 'create_job_filename()' - Create the filename for a document in a job.
1060 static void create_job_filename(
1061 _ipp_printer_t
*printer
, /* I - Printer */
1062 _ipp_job_t
*job
, /* I - Job */
1063 char *fname
, /* I - Filename buffer */
1064 size_t fnamesize
) /* I - Size of filename buffer */
1066 char name
[256], /* "Safe" filename */
1067 *nameptr
; /* Pointer into filename */
1068 const char *ext
, /* Filename extension */
1069 *job_name
; /* job-name value */
1070 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1074 * Make a name from the job-name attribute...
1077 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1078 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1080 job_name
= "untitled";
1082 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1083 if (isalnum(*job_name
& 255) || *job_name
== '-')
1084 *nameptr
++ = (char)tolower(*job_name
& 255);
1091 * Figure out the extension...
1094 if (!strcasecmp(job
->format
, "image/jpeg"))
1096 else if (!strcasecmp(job
->format
, "image/png"))
1098 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1100 else if (!strcasecmp(job
->format
, "image/urf"))
1102 else if (!strcasecmp(job
->format
, "application/pdf"))
1104 else if (!strcasecmp(job
->format
, "application/postscript"))
1110 * Create a filename with the job-id, job-name, and document-format (extension)...
1113 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1118 * 'create_listener()' - Create a listener socket.
1121 static int /* O - Listener socket or -1 on error */
1122 create_listener(int family
, /* I - Address family */
1123 int port
) /* I - Port number */
1125 int sock
; /* Listener socket */
1126 http_addrlist_t
*addrlist
; /* Listen address */
1127 char service
[255]; /* Service port */
1130 snprintf(service
, sizeof(service
), "%d", port
);
1131 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1134 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1136 httpAddrFreeList(addrlist
);
1143 * 'create_media_col()' - Create a media-col value.
1146 static ipp_t
* /* O - media-col collection */
1147 create_media_col(const char *media
, /* I - Media name */
1148 const char *source
, /* I - Media source */
1149 const char *type
, /* I - Media type */
1150 int width
, /* I - x-dimension in 2540ths */
1151 int length
, /* I - y-dimension in 2540ths */
1152 int margins
) /* I - Value for margins */
1154 ipp_t
*media_col
= ippNew(), /* media-col value */
1155 *media_size
= create_media_size(width
, length
);
1156 /* media-size value */
1157 char media_key
[256]; /* media-key value */
1161 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1163 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1165 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1167 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1169 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1171 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1172 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1173 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1174 "media-bottom-margin", margins
);
1175 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1176 "media-left-margin", margins
);
1177 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1178 "media-right-margin", margins
);
1179 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1180 "media-top-margin", margins
);
1182 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1184 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1186 ippDelete(media_size
);
1193 * 'create_media_size()' - Create a media-size value.
1196 static ipp_t
* /* O - media-col collection */
1197 create_media_size(int width
, /* I - x-dimension in 2540ths */
1198 int length
) /* I - y-dimension in 2540ths */
1200 ipp_t
*media_size
= ippNew(); /* media-size value */
1203 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1205 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1208 return (media_size
);
1213 * 'create_printer()' - Create, register, and listen for connections to a
1217 static _ipp_printer_t
* /* O - Printer */
1218 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1219 const char *name
, /* I - printer-name */
1220 const char *location
, /* I - printer-location */
1221 const char *make
, /* I - printer-make-and-model */
1222 const char *model
, /* I - printer-make-and-model */
1223 const char *icon
, /* I - printer-icons */
1224 const char *docformats
, /* I - document-format-supported */
1225 int ppm
, /* I - Pages per minute in grayscale */
1226 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1227 int duplex
, /* I - 1 = duplex, 0 = simplex */
1228 int port
, /* I - Port for listeners or 0 for auto */
1229 int pin
, /* I - Require PIN printing */
1230 const char *subtype
, /* I - Bonjour service subtype */
1231 const char *directory
, /* I - Spool directory */
1232 const char *command
) /* I - Command to run on job files */
1234 int i
, j
; /* Looping vars */
1235 _ipp_printer_t
*printer
; /* Printer */
1237 char path
[1024]; /* Full path to command */
1239 char uri
[1024], /* Printer URI */
1240 icons
[1024], /* printer-icons URI */
1241 adminurl
[1024], /* printer-more-info URI */
1242 supplyurl
[1024],/* printer-supply-info-uri URI */
1243 device_id
[1024],/* printer-device-id */
1244 make_model
[128],/* printer-make-and-model */
1245 uuid
[128]; /* printer-uuid */
1246 int num_formats
; /* Number of document-format-supported values */
1247 char *defformat
, /* document-format-default value */
1248 *formats
[100], /* document-format-supported values */
1249 *ptr
; /* Pointer into string */
1250 const char *prefix
; /* Prefix string */
1251 int num_database
; /* Number of database values */
1252 ipp_attribute_t
*media_col_database
,
1253 /* media-col-database value */
1254 *media_size_supported
;
1255 /* media-size-supported value */
1256 ipp_t
*media_col_default
;
1257 /* media-col-default value */
1258 int media_col_index
;/* Current media-col-database value */
1259 int k_supported
; /* Maximum file size supported */
1261 struct statvfs spoolinfo
; /* FS info for spool directory */
1262 double spoolsize
; /* FS size */
1263 #elif defined(HAVE_STATFS)
1264 struct statfs spoolinfo
; /* FS info for spool directory */
1265 double spoolsize
; /* FS size */
1266 #endif /* HAVE_STATVFS */
1267 static const int orients
[4] = /* orientation-requested-supported values */
1269 IPP_ORIENT_PORTRAIT
,
1270 IPP_ORIENT_LANDSCAPE
,
1271 IPP_ORIENT_REVERSE_LANDSCAPE
,
1272 IPP_ORIENT_REVERSE_PORTRAIT
1274 static const char * const versions
[] =/* ipp-versions-supported values */
1280 static const char * const features
[] =/* ipp-features-supported values */
1284 static const int ops
[] = /* operations-supported values */
1288 IPP_OP_VALIDATE_JOB
,
1290 IPP_OP_SEND_DOCUMENT
,
1293 IPP_OP_GET_JOB_ATTRIBUTES
,
1295 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1296 IPP_OP_CANCEL_MY_JOBS
,
1298 IPP_OP_IDENTIFY_PRINTER
1300 static const char * const charsets
[] =/* charset-supported values */
1305 static const char * const compressions
[] =/* compression-supported values */
1310 #endif /* HAVE_LIBZ */
1313 static const char * const identify_actions
[] =
1318 static const char * const job_creation
[] =
1319 { /* job-creation-attributes-supported values */
1321 "ipp-attribute-fidelity",
1323 "job-accounting-user-id",
1329 "multiple-document-handling",
1330 "orientation-requested",
1334 static const char * const media_col_supported
[] =
1335 { /* media-col-supported values */
1336 "media-bottom-margin",
1337 "media-left-margin",
1338 "media-right-margin",
1344 static const int media_xxx_margin_supported
[] =
1345 { /* media-xxx-margin-supported values */
1349 static const char * const multiple_document_handling
[] =
1350 { /* multiple-document-handling-supported values */
1351 "separate-documents-uncollated-copies",
1352 "separate-documents-collated-copies"
1354 static const char * const overrides
[] =
1355 { /* overrides-supported */
1359 static const char * const print_color_mode_supported
[] =
1360 { /* print-color-mode-supported values */
1365 static const int print_quality_supported
[] =
1366 { /* print-quality-supported values */
1371 static const int pwg_raster_document_resolution_supported
[] =
1377 static const char * const pwg_raster_document_type_supported
[] =
1385 static const char * const reference_uri_schemes_supported
[] =
1386 { /* reference-uri-schemes-supported */
1392 #endif /* HAVE_SSL */
1394 static const char * const sides_supported
[] =
1395 { /* sides-supported values */
1397 "two-sided-long-edge",
1398 "two-sided-short-edge"
1400 static const char * const urf_supported
[] =
1401 { /* urf-supported values */
1404 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1411 static const char * const which_jobs
[] =
1412 { /* which-jobs-supported values */
1421 "processing-stopped"
1427 * If a command was specified, make sure it exists and is executable...
1432 if (*command
== '/' || !strncmp(command
, "./", 2))
1434 if (access(command
, X_OK
))
1436 fprintf(stderr
, "ippserver: Unable to execute command \"%s\": %s\n", command
, strerror(errno
));
1442 if (!cupsFileFind(command
, getenv("PATH"), 1, path
, sizeof(path
)))
1444 fprintf(stderr
, "ippserver: Unable to find command \"%s\".\n", command
);
1454 * Allocate memory for the printer...
1457 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1459 perror("ippserver: Unable to allocate memory for printer");
1465 printer
->name
= strdup(name
);
1466 printer
->dnssd_name
= strdup(printer
->name
);
1467 printer
->command
= command
? strdup(command
) : NULL
;
1468 printer
->directory
= strdup(directory
);
1469 printer
->hostname
= strdup(servername
);
1470 printer
->port
= port
;
1471 printer
->start_time
= time(NULL
);
1472 printer
->config_time
= printer
->start_time
;
1473 printer
->state
= IPP_PSTATE_IDLE
;
1474 printer
->state_reasons
= _IPP_PREASON_NONE
;
1475 printer
->state_time
= printer
->start_time
;
1476 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1477 printer
->next_job_id
= 1;
1479 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1480 printer
->hostname
, printer
->port
, "/ipp/print");
1481 printer
->uri
= strdup(uri
);
1482 printer
->urilen
= strlen(uri
);
1485 printer
->icon
= strdup(icon
);
1487 printer
->main_size
= _IPP_MEDIA_SIZE_A4
;
1488 printer
->main_type
= _IPP_MEDIA_TYPE_STATIONERY
;
1489 printer
->main_level
= 500;
1491 printer
->envelope_size
= _IPP_MEDIA_SIZE_NONE
;
1492 printer
->envelope_level
= 0;
1494 printer
->photo_size
= _IPP_MEDIA_SIZE_NONE
;
1495 printer
->photo_type
= _IPP_MEDIA_TYPE_NONE
;
1496 printer
->photo_level
= 0;
1498 printer
->supplies
[_IPP_SUPPLY_CYAN
] = 100;
1499 printer
->supplies
[_IPP_SUPPLY_MAGENTA
] = 100;
1500 printer
->supplies
[_IPP_SUPPLY_YELLOW
] = 100;
1501 printer
->supplies
[_IPP_SUPPLY_BLACK
] = 100;
1502 printer
->supplies
[_IPP_SUPPLY_WASTE
] = 0;
1504 _cupsRWInit(&(printer
->rwlock
));
1507 * Create the listener sockets...
1510 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1512 perror("Unable to create IPv4 listener");
1516 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1518 perror("Unable to create IPv6 listener");
1523 * Prepare values for the printer attributes...
1526 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1527 printer
->hostname
, printer
->port
, "/icon.png");
1528 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/");
1529 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/supplies");
1533 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1534 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1535 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1538 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1541 formats
[0] = strdup(docformats
);
1542 defformat
= formats
[0];
1543 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1546 formats
[num_formats
++] = ptr
;
1548 if (!strcasecmp(ptr
, "application/octet-stream"))
1552 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1553 ptr
= device_id
+ strlen(device_id
);
1555 for (i
= 0; i
< num_formats
; i
++)
1557 if (!strcasecmp(formats
[i
], "application/pdf"))
1558 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1559 else if (!strcasecmp(formats
[i
], "application/postscript"))
1560 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1561 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1562 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1563 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1564 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1565 else if (!strcasecmp(formats
[i
], "image/png"))
1566 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1567 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1568 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1573 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
1580 * Get the maximum spool size based on the size of the filesystem used for
1581 * the spool directory. If the host OS doesn't support the statfs call
1582 * or the filesystem is larger than 2TiB, always report INT_MAX.
1586 if (statvfs(printer
->directory
, &spoolinfo
))
1587 k_supported
= INT_MAX
;
1588 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1589 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1590 k_supported
= INT_MAX
;
1592 k_supported
= (int)spoolsize
;
1594 #elif defined(HAVE_STATFS)
1595 if (statfs(printer
->directory
, &spoolinfo
))
1596 k_supported
= INT_MAX
;
1597 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1598 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1599 k_supported
= INT_MAX
;
1601 k_supported
= (int)spoolsize
;
1604 k_supported
= INT_MAX
;
1605 #endif /* HAVE_STATVFS */
1608 * Create the printer attributes. This list of attributes is sorted to improve
1609 * performance when the client provides a requested-attributes attribute...
1612 printer
->attrs
= ippNew();
1614 /* charset-configured */
1615 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1616 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1617 "charset-configured", NULL
, "utf-8");
1619 /* charset-supported */
1620 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1621 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1622 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1625 /* color-supported */
1626 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1629 /* compression-supported */
1630 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1631 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1632 "compression-supported",
1633 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1636 /* copies-default */
1637 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1638 "copies-default", 1);
1640 /* copies-supported */
1641 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1643 /* document-format-default */
1644 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1645 "document-format-default", NULL
, defformat
);
1647 /* document-format-supported */
1648 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1649 "document-format-supported", num_formats
, NULL
,
1650 (const char * const *)formats
);
1652 /* document-password-supported */
1653 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1655 /* finishings-default */
1656 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1657 "finishings-default", IPP_FINISHINGS_NONE
);
1659 /* finishings-supported */
1660 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1661 "finishings-supported", IPP_FINISHINGS_NONE
);
1663 /* generated-natural-language-supported */
1664 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1665 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1666 "generated-natural-language-supported", NULL
, "en");
1668 /* identify-actions-default */
1669 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1671 /* identify-actions-supported */
1672 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
);
1674 /* ipp-features-supported */
1675 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1677 /* ipp-versions-supported */
1678 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1680 /* job-account-id-default */
1681 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1683 /* job-account-id-supported */
1684 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1686 /* job-accounting-user-id-default */
1687 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1689 /* job-accounting-user-id-supported */
1690 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1692 /* job-creation-attributes-supported */
1693 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
);
1695 /* job-ids-supported */
1696 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1698 /* job-k-octets-supported */
1699 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1702 /* job-password-supported */
1703 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1704 "job-password-supported", 4);
1706 /* job-preferred-attributes-supported */
1707 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-preferred-attributes-supported", 0);
1709 /* job-priority-default */
1710 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1711 "job-priority-default", 50);
1713 /* job-priority-supported */
1714 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1715 "job-priority-supported", 100);
1717 /* job-sheets-default */
1718 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1719 IPP_CONST_TAG(IPP_TAG_NAME
),
1720 "job-sheets-default", NULL
, "none");
1722 /* job-sheets-supported */
1723 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1724 IPP_CONST_TAG(IPP_TAG_NAME
),
1725 "job-sheets-supported", NULL
, "none");
1727 /* media-bottom-margin-supported */
1728 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1729 "media-bottom-margin-supported",
1730 (int)(sizeof(media_xxx_margin_supported
) /
1731 sizeof(media_xxx_margin_supported
[0])),
1732 media_xxx_margin_supported
);
1734 /* media-col-database */
1735 for (num_database
= 0, i
= 0;
1736 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1739 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1740 num_database
+= 3; /* auto + manual + envelope */
1741 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1742 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1744 num_database
+= 2; /* Regular + borderless */
1747 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1748 "media-col-database", num_database
,
1750 for (media_col_index
= 0, i
= 0;
1751 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1754 switch (media_col_sizes
[i
][2])
1758 * Regular + borderless for the general class; no source/type
1762 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]));
1763 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]));
1766 case _IPP_ENV_ONLY
:
1768 * Regular margins for "auto", "manual", and "envelope" sources.
1771 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]));
1772 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]));
1773 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]));
1775 case _IPP_PHOTO_ONLY
:
1777 * Photos have specific media types and can only be printed via
1778 * the auto, manual, and photo sources...
1782 j
< (int)(sizeof(media_type_supported
) /
1783 sizeof(media_type_supported
[0]));
1786 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1789 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]));
1790 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]));
1791 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]));
1797 /* media-col-default */
1798 media_col_default
= create_media_col(media_supported
[0],
1799 media_source_supported
[0],
1800 media_type_supported
[0],
1801 media_col_sizes
[0][0],
1802 media_col_sizes
[0][1],
1803 media_xxx_margin_supported
[1]);
1805 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1807 ippDelete(media_col_default
);
1809 /* media-col-supported */
1810 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1811 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1812 "media-col-supported",
1813 (int)(sizeof(media_col_supported
) /
1814 sizeof(media_col_supported
[0])), NULL
,
1815 media_col_supported
);
1818 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1819 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1820 "media-default", NULL
, media_supported
[0]);
1822 /* media-left-margin-supported */
1823 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1824 "media-left-margin-supported",
1825 (int)(sizeof(media_xxx_margin_supported
) /
1826 sizeof(media_xxx_margin_supported
[0])),
1827 media_xxx_margin_supported
);
1829 /* media-right-margin-supported */
1830 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1831 "media-right-margin-supported",
1832 (int)(sizeof(media_xxx_margin_supported
) /
1833 sizeof(media_xxx_margin_supported
[0])),
1834 media_xxx_margin_supported
);
1836 /* media-supported */
1837 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1838 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1840 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1841 NULL
, media_supported
);
1843 /* media-size-supported */
1844 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1845 "media-size-supported",
1846 (int)(sizeof(media_col_sizes
) /
1847 sizeof(media_col_sizes
[0])),
1850 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1852 ippSetCollection(printer
->attrs
, &media_size_supported
, i
,
1853 create_media_size(media_col_sizes
[i
][0],
1854 media_col_sizes
[i
][1]));
1856 /* media-source-supported */
1857 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
);
1859 /* media-top-margin-supported */
1860 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1861 "media-top-margin-supported",
1862 (int)(sizeof(media_xxx_margin_supported
) /
1863 sizeof(media_xxx_margin_supported
[0])),
1864 media_xxx_margin_supported
);
1866 /* media-type-supported */
1867 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
);
1869 /* multiple-document-handling-supported */
1870 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
);
1872 /* multiple-document-jobs-supported */
1873 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1875 /* multiple-operation-time-out */
1876 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1878 /* multiple-operation-time-out-action */
1879 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1881 /* natural-language-configured */
1882 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1883 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1884 "natural-language-configured", NULL
, "en");
1886 /* number-up-default */
1887 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1888 "number-up-default", 1);
1890 /* number-up-supported */
1891 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1892 "number-up-supported", 1);
1894 /* operations-supported */
1895 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1896 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1898 /* orientation-requested-default */
1899 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1900 "orientation-requested-default", 0);
1902 /* orientation-requested-supported */
1903 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1904 "orientation-requested-supported", 4, orients
);
1906 /* output-bin-default */
1907 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1908 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1909 "output-bin-default", NULL
, "face-down");
1911 /* output-bin-supported */
1912 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1913 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1914 "output-bin-supported", NULL
, "face-down");
1916 /* overrides-supported */
1917 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1919 /* page-ranges-supported */
1920 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1922 /* pages-per-minute */
1923 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1924 "pages-per-minute", ppm
);
1926 /* pages-per-minute-color */
1928 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1929 "pages-per-minute-color", ppm_color
);
1931 /* pdl-override-supported */
1932 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1933 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1934 "pdl-override-supported", NULL
, "attempted");
1936 /* print-color-mode-default */
1937 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1939 /* print-color-mode-supported */
1940 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
);
1942 /* print-content-optimize-default */
1943 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1945 /* print-content-optimize-supported */
1946 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
1948 /* print-rendering-intent-default */
1949 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
1951 /* print-rendering-intent-supported */
1952 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
1954 /* print-quality-default */
1955 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
1957 /* print-quality-supported */
1958 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
);
1960 /* printer-device-id */
1961 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1962 "printer-device-id", NULL
, device_id
);
1964 /* printer-get-attributes-supported */
1965 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1967 /* printer-geo-location */
1968 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
1971 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1972 "printer-icons", NULL
, icons
);
1974 /* printer-is-accepting-jobs */
1975 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1979 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1982 /* printer-location */
1983 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1984 "printer-location", NULL
, location
);
1986 /* printer-make-and-model */
1987 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1988 "printer-make-and-model", NULL
, make_model
);
1990 /* printer-mandatory-job-attributes */
1993 static const char * const names
[] =
1996 "job-accounting-user-id",
2000 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2001 "printer-mandatory-job-attributes",
2002 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
2005 /* printer-more-info */
2006 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
2009 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
2012 /* printer-organization */
2013 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
2015 /* printer-organizational-unit */
2016 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
2018 /* printer-resolution-default */
2019 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
2020 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
2022 /* printer-resolution-supported */
2023 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
2024 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
2026 /* printer-supply-description */
2027 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
);
2029 /* printer-supply-info-uri */
2030 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
2032 /* printer-uri-supported */
2033 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
2036 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
2037 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
2039 /* pwg-raster-document-xxx-supported */
2040 for (i
= 0; i
< num_formats
; i
++)
2041 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
2044 if (i
< num_formats
)
2046 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
,
2047 "pwg-raster-document-resolution-supported",
2048 (int)(sizeof(pwg_raster_document_resolution_supported
) /
2049 sizeof(pwg_raster_document_resolution_supported
[0])),
2051 pwg_raster_document_resolution_supported
,
2052 pwg_raster_document_resolution_supported
);
2053 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2054 "pwg-raster-document-sheet-back", NULL
, "normal");
2055 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2056 "pwg-raster-document-type-supported",
2057 (int)(sizeof(pwg_raster_document_type_supported
) /
2058 sizeof(pwg_raster_document_type_supported
[0])), NULL
,
2059 pwg_raster_document_type_supported
);
2062 /* reference-uri-scheme-supported */
2063 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2064 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
2065 "reference-uri-schemes-supported",
2066 (int)(sizeof(reference_uri_schemes_supported
) /
2067 sizeof(reference_uri_schemes_supported
[0])),
2068 NULL
, reference_uri_schemes_supported
);
2071 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2072 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2073 "sides-default", NULL
, "one-sided");
2075 /* sides-supported */
2076 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2077 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2078 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
2081 for (i
= 0; i
< num_formats
; i
++)
2082 if (!strcasecmp(formats
[i
], "image/urf"))
2085 if (i
< num_formats
)
2086 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2088 /* uri-authentication-supported */
2089 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2090 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2091 "uri-authentication-supported", NULL
, "none");
2093 /* uri-security-supported */
2094 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2095 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2096 "uri-security-supported", NULL
, "none");
2098 /* which-jobs-supported */
2099 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2100 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2101 "which-jobs-supported",
2102 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
2106 debug_attributes("Printer", printer
->attrs
, 0);
2109 * Register the printer with Bonjour...
2112 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2123 * If we get here we were unable to create the printer...
2128 delete_printer(printer
);
2134 * 'debug_attributes()' - Print attributes in a request or response.
2138 debug_attributes(const char *title
, /* I - Title */
2139 ipp_t
*ipp
, /* I - Request/response */
2140 int type
) /* I - 0 = object, 1 = request, 2 = response */
2142 ipp_tag_t group_tag
; /* Current group */
2143 ipp_attribute_t
*attr
; /* Current attribute */
2144 char buffer
[2048]; /* String buffer for value */
2145 int major
, minor
; /* Version */
2151 fprintf(stderr
, "%s:\n", title
);
2152 major
= ippGetVersion(ipp
, &minor
);
2153 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2155 fprintf(stderr
, " operation-id=%s(%04x)\n",
2156 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2158 fprintf(stderr
, " status-code=%s(%04x)\n",
2159 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2160 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2162 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2164 attr
= ippNextAttribute(ipp
))
2166 if (ippGetGroupTag(attr
) != group_tag
)
2168 group_tag
= ippGetGroupTag(attr
);
2169 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2172 if (ippGetName(attr
))
2174 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2175 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2176 ippGetCount(attr
) > 1 ? "1setOf " : "",
2177 ippTagString(ippGetValueTag(attr
)), buffer
);
2184 * 'delete_client()' - Close the socket and free all memory used by a client
2189 delete_client(_ipp_client_t
*client
) /* I - Client */
2192 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2195 * Flush pending writes before closing...
2198 httpFlushWrite(client
->http
);
2204 httpClose(client
->http
);
2206 ippDelete(client
->request
);
2207 ippDelete(client
->response
);
2214 * 'delete_job()' - Remove from the printer and free all memory used by a job
2219 delete_job(_ipp_job_t
*job
) /* I - Job */
2222 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2224 ippDelete(job
->attrs
);
2229 unlink(job
->filename
);
2231 free(job
->filename
);
2239 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2240 * used by a printer object.
2244 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2246 if (printer
->ipv4
>= 0)
2247 close(printer
->ipv4
);
2249 if (printer
->ipv6
>= 0)
2250 close(printer
->ipv6
);
2253 if (printer
->printer_ref
)
2254 DNSServiceRefDeallocate(printer
->printer_ref
);
2255 if (printer
->ipp_ref
)
2256 DNSServiceRefDeallocate(printer
->ipp_ref
);
2257 if (printer
->ipps_ref
)
2258 DNSServiceRefDeallocate(printer
->ipps_ref
);
2259 if (printer
->http_ref
)
2260 DNSServiceRefDeallocate(printer
->http_ref
);
2261 #elif defined(HAVE_AVAHI)
2262 avahi_threaded_poll_lock(DNSSDMaster
);
2264 if (printer
->printer_ref
)
2265 avahi_entry_group_free(printer
->printer_ref
);
2266 if (printer
->ipp_ref
)
2267 avahi_entry_group_free(printer
->ipp_ref
);
2268 if (printer
->ipps_ref
)
2269 avahi_entry_group_free(printer
->ipps_ref
);
2270 if (printer
->http_ref
)
2271 avahi_entry_group_free(printer
->http_ref
);
2273 avahi_threaded_poll_unlock(DNSSDMaster
);
2274 #endif /* HAVE_DNSSD */
2276 if (printer
->dnssd_name
)
2277 free(printer
->dnssd_name
);
2279 free(printer
->name
);
2281 free(printer
->icon
);
2282 if (printer
->command
)
2283 free(printer
->command
);
2284 if (printer
->directory
)
2285 free(printer
->directory
);
2286 if (printer
->hostname
)
2287 free(printer
->hostname
);
2291 ippDelete(printer
->attrs
);
2292 cupsArrayDelete(printer
->jobs
);
2300 * 'dnssd_callback()' - Handle Bonjour registration events.
2303 static void DNSSD_API
2305 DNSServiceRef sdRef
, /* I - Service reference */
2306 DNSServiceFlags flags
, /* I - Status flags */
2307 DNSServiceErrorType errorCode
, /* I - Error, if any */
2308 const char *name
, /* I - Service name */
2309 const char *regtype
, /* I - Service type */
2310 const char *domain
, /* I - Domain for service */
2311 _ipp_printer_t
*printer
) /* I - Printer */
2319 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2320 regtype
, (int)errorCode
);
2323 else if (strcasecmp(name
, printer
->dnssd_name
))
2326 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2328 /* No lock needed since only the main thread accesses/changes this */
2329 free(printer
->dnssd_name
);
2330 printer
->dnssd_name
= strdup(name
);
2335 #elif defined(HAVE_AVAHI)
2337 * 'dnssd_callback()' - Handle Bonjour registration events.
2342 AvahiEntryGroup
*srv
, /* I - Service */
2343 AvahiEntryGroupState state
, /* I - Registration state */
2344 void *context
) /* I - Printer */
2353 * 'dnssd_client_cb()' - Client callback for Avahi.
2355 * Called whenever the client or server state changes...
2360 AvahiClient
*c
, /* I - Client */
2361 AvahiClientState state
, /* I - Current state */
2362 void *userdata
) /* I - User data (unused) */
2372 fprintf(stderr
, "Ignore Avahi state %d.\n", state
);
2375 case AVAHI_CLIENT_FAILURE
:
2376 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
2378 fputs("Avahi server crashed, exiting.\n", stderr
);
2384 #endif /* HAVE_DNSSD */
2388 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2395 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2397 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2401 #elif defined(HAVE_AVAHI)
2402 int error
; /* Error code, if any */
2404 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2406 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2410 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2412 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2416 avahi_threaded_poll_start(DNSSDMaster
);
2417 #endif /* HAVE_DNSSD */
2422 * 'filter_cb()' - Filter printer attributes based on the requested array.
2425 static int /* O - 1 to copy, 0 to ignore */
2426 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2427 ipp_t
*dst
, /* I - Destination (unused) */
2428 ipp_attribute_t
*attr
) /* I - Source attribute */
2431 * Filter attributes as needed...
2434 #ifndef WIN32 /* Avoid MS compiler bug */
2438 ipp_tag_t group
= ippGetGroupTag(attr
);
2439 const char *name
= ippGetName(attr
);
2441 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
)))
2444 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2449 * 'find_job()' - Find a job specified in a request.
2452 static _ipp_job_t
* /* O - Job or NULL */
2453 find_job(_ipp_client_t
*client
) /* I - Client */
2455 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2456 _ipp_job_t key
, /* Job search key */
2457 *job
; /* Matching job, if any */
2460 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2462 const char *uri
= ippGetString(attr
, 0, NULL
);
2464 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2465 uri
[client
->printer
->urilen
] == '/')
2466 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2470 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2471 key
.id
= ippGetInteger(attr
, 0);
2473 _cupsRWLockRead(&(client
->printer
->rwlock
));
2474 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2475 _cupsRWUnlock(&(client
->printer
->rwlock
));
2482 * 'html_escape()' - Write a HTML-safe string.
2486 html_escape(_ipp_client_t
*client
, /* I - Client */
2487 const char *s
, /* I - String to write */
2488 size_t slen
) /* I - Number of characters to write */
2490 const char *start
, /* Start of segment */
2491 *end
; /* End of string */
2495 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2497 while (*s
&& s
< end
)
2499 if (*s
== '&' || *s
== '<')
2502 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2505 httpWrite2(client
->http
, "&", 5);
2507 httpWrite2(client
->http
, "<", 4);
2516 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2521 * 'html_footer()' - Show the web interface footer.
2523 * This function also writes the trailing 0-length chunk.
2527 html_footer(_ipp_client_t
*client
) /* I - Client */
2533 httpWrite2(client
->http
, "", 0);
2538 * 'html_header()' - Show the web interface header and title.
2542 html_header(_ipp_client_t
*client
, /* I - Client */
2543 const char *title
) /* I - Title */
2549 "<title>%s</title>\n"
2550 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2551 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2552 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2553 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2555 "body { font-family: sans-serif; margin: 0; }\n"
2556 "div.body { padding: 0px 10px 10px; }\n"
2557 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2558 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2559 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2560 "table.form th { text-align: right; }\n"
2561 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2562 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2563 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2564 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2565 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2566 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2567 "table.nav td { margin: 0; text-align: center; }\n"
2568 "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"
2569 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2570 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2571 "td.nav:hover { background: #666; color: #fff; }\n"
2572 "td.nav:active { background: #000; color: #ff0; }\n"
2576 "<table class=\"nav\"><tr>"
2577 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2578 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2579 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2581 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2586 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2590 html_printf(_ipp_client_t
*client
, /* I - Client */
2591 const char *format
, /* I - Printf-style format string */
2592 ...) /* I - Additional arguments as needed */
2594 va_list ap
; /* Pointer to arguments */
2595 const char *start
; /* Start of string */
2596 char size
, /* Size character (h, l, L) */
2597 type
; /* Format type character */
2598 int width
, /* Width of field */
2599 prec
; /* Number of characters of precision */
2600 char tformat
[100], /* Temporary format string for sprintf() */
2601 *tptr
, /* Pointer into temporary format */
2602 temp
[1024]; /* Buffer for formatted numbers */
2603 char *s
; /* Pointer to string */
2607 * Loop through the format string, formatting as needed...
2610 va_start(ap
, format
);
2618 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2621 *tptr
++ = *format
++;
2625 httpWrite2(client
->http
, "%", 1);
2630 else if (strchr(" -+#\'", *format
))
2631 *tptr
++ = *format
++;
2636 * Get width from argument...
2640 width
= va_arg(ap
, int);
2642 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2643 tptr
+= strlen(tptr
);
2649 while (isdigit(*format
& 255))
2651 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2654 width
= width
* 10 + *format
++ - '0';
2660 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2668 * Get precision from argument...
2672 prec
= va_arg(ap
, int);
2674 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2675 tptr
+= strlen(tptr
);
2681 while (isdigit(*format
& 255))
2683 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2686 prec
= prec
* 10 + *format
++ - '0';
2691 if (*format
== 'l' && format
[1] == 'l')
2695 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2703 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2705 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2720 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2729 case 'E' : /* Floating point formats */
2734 if ((size_t)(width
+ 2) > sizeof(temp
))
2737 sprintf(temp
, tformat
, va_arg(ap
, double));
2739 httpWrite2(client
->http
, temp
, strlen(temp
));
2742 case 'B' : /* Integer formats */
2750 if ((size_t)(width
+ 2) > sizeof(temp
))
2753 # ifdef HAVE_LONG_LONG
2755 sprintf(temp
, tformat
, va_arg(ap
, long long));
2757 # endif /* HAVE_LONG_LONG */
2759 sprintf(temp
, tformat
, va_arg(ap
, long));
2761 sprintf(temp
, tformat
, va_arg(ap
, int));
2763 httpWrite2(client
->http
, temp
, strlen(temp
));
2766 case 'p' : /* Pointer value */
2767 if ((size_t)(width
+ 2) > sizeof(temp
))
2770 sprintf(temp
, tformat
, va_arg(ap
, void *));
2772 httpWrite2(client
->http
, temp
, strlen(temp
));
2775 case 'c' : /* Character or character array */
2778 temp
[0] = (char)va_arg(ap
, int);
2780 html_escape(client
, temp
, 1);
2783 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2786 case 's' : /* String */
2787 if ((s
= va_arg(ap
, char *)) == NULL
)
2790 html_escape(client
, s
, strlen(s
));
2799 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2806 * 'ipp_cancel_job()' - Cancel a job.
2810 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2812 _ipp_job_t
*job
; /* Job information */
2819 if ((job
= find_job(client
)) == NULL
)
2821 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2826 * See if the job is already completed, canceled, or aborted; if so,
2827 * we can't cancel...
2832 case IPP_JSTATE_CANCELED
:
2833 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2834 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2837 case IPP_JSTATE_ABORTED
:
2838 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2839 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2842 case IPP_JSTATE_COMPLETED
:
2843 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2844 "Job #%d is already completed - can\'t cancel.", job
->id
);
2852 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2854 if (job
->state
== IPP_JSTATE_PROCESSING
||
2855 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2859 job
->state
= IPP_JSTATE_CANCELED
;
2860 job
->completed
= time(NULL
);
2863 _cupsRWUnlock(&(client
->printer
->rwlock
));
2865 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2872 * 'ipp_close_job()' - Close an open job.
2876 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
2878 _ipp_job_t
*job
; /* Job information */
2885 if ((job
= find_job(client
)) == NULL
)
2887 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2892 * See if the job is already completed, canceled, or aborted; if so,
2893 * we can't cancel...
2898 case IPP_JSTATE_CANCELED
:
2899 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2900 "Job #%d is canceled - can\'t close.", job
->id
);
2903 case IPP_JSTATE_ABORTED
:
2904 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2905 "Job #%d is aborted - can\'t close.", job
->id
);
2908 case IPP_JSTATE_COMPLETED
:
2909 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2910 "Job #%d is completed - can\'t close.", job
->id
);
2913 case IPP_JSTATE_PROCESSING
:
2914 case IPP_JSTATE_STOPPED
:
2915 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2916 "Job #%d is already closed.", job
->id
);
2920 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2927 * 'ipp_create_job()' - Create a job object.
2931 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2933 _ipp_job_t
*job
; /* New job */
2934 cups_array_t
*ra
; /* Attributes to send in response */
2938 * Validate print job attributes...
2941 if (!valid_job_attributes(client
))
2943 httpFlush(client
->http
);
2948 * Do we have a file to print?
2951 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2953 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2954 "Unexpected document data following request.");
2962 if ((job
= create_job(client
)) == NULL
)
2964 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2965 "Currently printing another job.");
2970 * Return the job info...
2973 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2975 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2976 cupsArrayAdd(ra
, "job-id");
2977 cupsArrayAdd(ra
, "job-state");
2978 cupsArrayAdd(ra
, "job-state-message");
2979 cupsArrayAdd(ra
, "job-state-reasons");
2980 cupsArrayAdd(ra
, "job-uri");
2982 copy_job_attributes(client
, job
, ra
);
2983 cupsArrayDelete(ra
);
2988 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2992 ipp_get_job_attributes(
2993 _ipp_client_t
*client
) /* I - Client */
2995 _ipp_job_t
*job
; /* Job */
2996 cups_array_t
*ra
; /* requested-attributes */
2999 if ((job
= find_job(client
)) == NULL
)
3001 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3005 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3007 ra
= ippCreateRequestedArray(client
->request
);
3008 copy_job_attributes(client
, job
, ra
);
3009 cupsArrayDelete(ra
);
3014 * 'ipp_get_jobs()' - Get a list of job objects.
3018 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
3020 ipp_attribute_t
*attr
; /* Current attribute */
3021 const char *which_jobs
= NULL
;
3022 /* which-jobs values */
3023 int job_comparison
; /* Job comparison */
3024 ipp_jstate_t job_state
; /* job-state value */
3025 int first_job_id
, /* First job ID */
3026 limit
, /* Maximum number of jobs to return */
3027 count
; /* Number of jobs that match */
3028 const char *username
; /* Username */
3029 _ipp_job_t
*job
; /* Current job pointer */
3030 cups_array_t
*ra
; /* Requested attributes array */
3034 * See if the "which-jobs" attribute have been specified...
3037 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3038 IPP_TAG_KEYWORD
)) != NULL
)
3040 which_jobs
= ippGetString(attr
, 0, NULL
);
3041 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3044 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3046 job_comparison
= -1;
3047 job_state
= IPP_JSTATE_STOPPED
;
3049 else if (!strcmp(which_jobs
, "completed"))
3052 job_state
= IPP_JSTATE_CANCELED
;
3054 else if (!strcmp(which_jobs
, "aborted"))
3057 job_state
= IPP_JSTATE_ABORTED
;
3059 else if (!strcmp(which_jobs
, "all"))
3062 job_state
= IPP_JSTATE_PENDING
;
3064 else if (!strcmp(which_jobs
, "canceled"))
3067 job_state
= IPP_JSTATE_CANCELED
;
3069 else if (!strcmp(which_jobs
, "pending"))
3072 job_state
= IPP_JSTATE_PENDING
;
3074 else if (!strcmp(which_jobs
, "pending-held"))
3077 job_state
= IPP_JSTATE_HELD
;
3079 else if (!strcmp(which_jobs
, "processing"))
3082 job_state
= IPP_JSTATE_PROCESSING
;
3084 else if (!strcmp(which_jobs
, "processing-stopped"))
3087 job_state
= IPP_JSTATE_STOPPED
;
3091 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3092 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3093 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3094 "which-jobs", NULL
, which_jobs
);
3099 * See if they want to limit the number of jobs reported...
3102 if ((attr
= ippFindAttribute(client
->request
, "limit",
3103 IPP_TAG_INTEGER
)) != NULL
)
3105 limit
= ippGetInteger(attr
, 0);
3107 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3112 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3113 IPP_TAG_INTEGER
)) != NULL
)
3115 first_job_id
= ippGetInteger(attr
, 0);
3117 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3124 * See if we only want to see jobs for a specific user...
3129 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3130 IPP_TAG_BOOLEAN
)) != NULL
)
3132 int my_jobs
= ippGetBoolean(attr
, 0);
3134 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3135 my_jobs
? "true" : "false");
3139 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3140 IPP_TAG_NAME
)) == NULL
)
3142 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3143 "Need requesting-user-name with my-jobs.");
3147 username
= ippGetString(attr
, 0, NULL
);
3149 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3150 client
->hostname
, username
);
3155 * OK, build a list of jobs for this printer...
3158 ra
= ippCreateRequestedArray(client
->request
);
3160 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3162 _cupsRWLockRead(&(client
->printer
->rwlock
));
3164 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3165 (limit
<= 0 || count
< limit
) && job
;
3166 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3169 * Filter out jobs that don't match...
3172 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3173 (job_comparison
== 0 && job
->state
!= job_state
) ||
3174 (job_comparison
> 0 && job
->state
< job_state
) ||
3175 job
->id
< first_job_id
||
3176 (username
&& job
->username
&&
3177 strcasecmp(username
, job
->username
)))
3181 ippAddSeparator(client
->response
);
3184 copy_job_attributes(client
, job
, ra
);
3187 cupsArrayDelete(ra
);
3189 _cupsRWUnlock(&(client
->printer
->rwlock
));
3194 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3198 ipp_get_printer_attributes(
3199 _ipp_client_t
*client
) /* I - Client */
3201 cups_array_t
*ra
; /* Requested attributes array */
3202 _ipp_printer_t
*printer
; /* Printer */
3206 * Send the attributes...
3209 ra
= ippCreateRequestedArray(client
->request
);
3210 printer
= client
->printer
;
3212 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3214 _cupsRWLockRead(&(printer
->rwlock
));
3216 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3217 IPP_TAG_CUPS_CONST
);
3219 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3221 int i
, /* Looping var */
3222 num_ready
= 0; /* Number of ready media */
3223 ipp_t
*ready
[3]; /* Ready media */
3225 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3227 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
3228 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);
3230 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);
3232 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3233 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);
3234 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3236 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
3237 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);
3239 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);
3244 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3245 for (i
= 0; i
< num_ready
; i
++)
3246 ippDelete(ready
[i
]);
3249 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3252 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3254 int num_ready
= 0; /* Number of ready media */
3255 const char *ready
[3]; /* Ready media */
3257 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3258 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3260 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3261 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3263 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3264 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3267 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3269 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3272 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3273 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3275 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3276 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3278 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3279 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3282 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3283 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3284 "printer-state", printer
->state
);
3286 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3287 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3289 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3290 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3292 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3294 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3296 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3299 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3301 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3302 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3303 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3304 "printer-state-reasons", NULL
, "none");
3307 int num_reasons
= 0;/* Number of reasons */
3308 const char *reasons
[32]; /* Reason strings */
3310 if (printer
->state_reasons
& _IPP_PREASON_OTHER
)
3311 reasons
[num_reasons
++] = "other";
3312 if (printer
->state_reasons
& _IPP_PREASON_COVER_OPEN
)
3313 reasons
[num_reasons
++] = "cover-open";
3314 if (printer
->state_reasons
& _IPP_PREASON_INPUT_TRAY_MISSING
)
3315 reasons
[num_reasons
++] = "input-tray-missing";
3316 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_EMPTY
)
3317 reasons
[num_reasons
++] = "marker-supply-empty-warning";
3318 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_LOW
)
3319 reasons
[num_reasons
++] = "marker-supply-low-report";
3320 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
)
3321 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
3322 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_FULL
)
3323 reasons
[num_reasons
++] = "marker-waste-full-warning";
3324 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
3325 reasons
[num_reasons
++] = "media-empty-warning";
3326 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_JAM
)
3327 reasons
[num_reasons
++] = "media-jam-warning";
3328 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_LOW
)
3329 reasons
[num_reasons
++] = "media-low-report";
3330 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_NEEDED
)
3331 reasons
[num_reasons
++] = "media-needed-report";
3332 if (printer
->state_reasons
& _IPP_PREASON_MOVING_TO_PAUSED
)
3333 reasons
[num_reasons
++] = "moving-to-paused";
3334 if (printer
->state_reasons
& _IPP_PREASON_PAUSED
)
3335 reasons
[num_reasons
++] = "paused";
3336 if (printer
->state_reasons
& _IPP_PREASON_SPOOL_AREA_FULL
)
3337 reasons
[num_reasons
++] = "spool-area-full";
3338 if (printer
->state_reasons
& _IPP_PREASON_TONER_EMPTY
)
3339 reasons
[num_reasons
++] = "toner-empty-warning";
3340 if (printer
->state_reasons
& _IPP_PREASON_TONER_LOW
)
3341 reasons
[num_reasons
++] = "toner-low-report";
3343 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
3344 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3345 "printer-state-reasons", num_reasons
, NULL
, reasons
);
3349 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3351 int i
; /* Looping var */
3352 char buffer
[256]; /* Supply value buffer */
3353 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3354 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3356 for (i
= 0; i
< 5; i
++)
3358 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
]);
3361 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3363 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3367 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3368 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3370 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3371 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3372 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3374 _cupsRWUnlock(&(printer
->rwlock
));
3376 cupsArrayDelete(ra
);
3381 * 'ipp_identify_printer()' - Beep or display a message.
3385 ipp_identify_printer(
3386 _ipp_client_t
*client
) /* I - Client */
3388 /* TODO: Do something */
3390 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3395 * 'ipp_print_job()' - Create a job object with an attached document.
3399 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3401 _ipp_job_t
*job
; /* New job */
3402 char filename
[1024], /* Filename buffer */
3403 buffer
[4096]; /* Copy buffer */
3404 ssize_t bytes
; /* Bytes read */
3405 cups_array_t
*ra
; /* Attributes to send in response */
3409 * Validate print job attributes...
3412 if (!valid_job_attributes(client
))
3414 httpFlush(client
->http
);
3419 * Do we have a file to print?
3422 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3424 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3432 if ((job
= create_job(client
)) == NULL
)
3434 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3435 "Currently printing another job.");
3440 * Create a file for the request data...
3443 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3446 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3448 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3450 job
->state
= IPP_JSTATE_ABORTED
;
3452 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3453 "Unable to create print file: %s", strerror(errno
));
3457 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3459 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3461 int error
= errno
; /* Write error */
3463 job
->state
= IPP_JSTATE_ABORTED
;
3470 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3471 "Unable to write print file: %s", strerror(error
));
3479 * Got an error while reading the print data, so abort this job.
3482 job
->state
= IPP_JSTATE_ABORTED
;
3489 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3490 "Unable to read print file.");
3496 int error
= errno
; /* Write error */
3498 job
->state
= IPP_JSTATE_ABORTED
;
3503 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3504 "Unable to write print file: %s", strerror(error
));
3509 job
->filename
= strdup(filename
);
3510 job
->state
= IPP_JSTATE_PENDING
;
3513 * Process the job...
3516 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3518 job
->state
= IPP_JSTATE_ABORTED
;
3519 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3524 * Return the job info...
3527 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3529 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3530 cupsArrayAdd(ra
, "job-id");
3531 cupsArrayAdd(ra
, "job-state");
3532 cupsArrayAdd(ra
, "job-state-message");
3533 cupsArrayAdd(ra
, "job-state-reasons");
3534 cupsArrayAdd(ra
, "job-uri");
3536 copy_job_attributes(client
, job
, ra
);
3537 cupsArrayDelete(ra
);
3542 * 'ipp_print_uri()' - Create a job object with a referenced document.
3546 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3548 _ipp_job_t
*job
; /* New job */
3549 ipp_attribute_t
*uri
; /* document-uri */
3550 char scheme
[256], /* URI scheme */
3551 userpass
[256], /* Username and password info */
3552 hostname
[256], /* Hostname */
3553 resource
[1024]; /* Resource path */
3554 int port
; /* Port number */
3555 http_uri_status_t uri_status
; /* URI decode status */
3556 http_encryption_t encryption
; /* Encryption to use, if any */
3557 http_t
*http
; /* Connection for http/https URIs */
3558 http_status_t status
; /* Access status for http/https URIs */
3559 int infile
; /* Input file for local file URIs */
3560 char filename
[1024], /* Filename buffer */
3561 buffer
[4096]; /* Copy buffer */
3562 ssize_t bytes
; /* Bytes read */
3563 cups_array_t
*ra
; /* Attributes to send in response */
3564 static const char * const uri_status_strings
[] =
3565 { /* URI decode errors */
3567 "Bad arguments to function.",
3568 "Bad resource in URI.",
3569 "Bad port number in URI.",
3570 "Bad hostname in URI.",
3571 "Bad username in URI.",
3572 "Bad scheme in URI.",
3578 * Validate print job attributes...
3581 if (!valid_job_attributes(client
))
3583 httpFlush(client
->http
);
3588 * Do we have a file to print?
3591 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3593 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3594 "Unexpected document data following request.");
3599 * Do we have a document URI?
3602 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3603 IPP_TAG_URI
)) == NULL
)
3605 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3609 if (ippGetCount(uri
) != 1)
3611 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3612 "Too many document-uri values.");
3616 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3617 scheme
, sizeof(scheme
), userpass
,
3618 sizeof(userpass
), hostname
, sizeof(hostname
),
3619 &port
, resource
, sizeof(resource
));
3620 if (uri_status
< HTTP_URI_STATUS_OK
)
3622 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3623 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3627 if (strcmp(scheme
, "file") &&
3629 strcmp(scheme
, "https") &&
3630 #endif /* HAVE_SSL */
3631 strcmp(scheme
, "http"))
3633 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3634 "URI scheme \"%s\" not supported.", scheme
);
3638 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3640 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3641 "Unable to access URI: %s", strerror(errno
));
3649 if ((job
= create_job(client
)) == NULL
)
3651 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3652 "Currently printing another job.");
3657 * Create a file for the request data...
3660 if (!strcasecmp(job
->format
, "image/jpeg"))
3661 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3662 client
->printer
->directory
, job
->id
);
3663 else if (!strcasecmp(job
->format
, "image/png"))
3664 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3665 client
->printer
->directory
, job
->id
);
3666 else if (!strcasecmp(job
->format
, "application/pdf"))
3667 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3668 client
->printer
->directory
, job
->id
);
3669 else if (!strcasecmp(job
->format
, "application/postscript"))
3670 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3671 client
->printer
->directory
, job
->id
);
3673 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3674 client
->printer
->directory
, job
->id
);
3676 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3678 job
->state
= IPP_JSTATE_ABORTED
;
3680 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3681 "Unable to create print file: %s", strerror(errno
));
3685 if (!strcmp(scheme
, "file"))
3687 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3689 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3690 "Unable to access URI: %s", strerror(errno
));
3696 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3697 (errno
== EAGAIN
|| errno
== EINTR
))
3699 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3701 int error
= errno
; /* Write error */
3703 job
->state
= IPP_JSTATE_ABORTED
;
3711 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3712 "Unable to write print file: %s", strerror(error
));
3723 if (port
== 443 || !strcmp(scheme
, "https"))
3724 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3726 #endif /* HAVE_SSL */
3727 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3729 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3730 1, 30000, NULL
)) == NULL
)
3732 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3733 "Unable to connect to %s: %s", hostname
,
3734 cupsLastErrorString());
3735 job
->state
= IPP_JSTATE_ABORTED
;
3744 httpClearFields(http
);
3745 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3746 if (httpGet(http
, resource
))
3748 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3749 "Unable to GET URI: %s", strerror(errno
));
3751 job
->state
= IPP_JSTATE_ABORTED
;
3761 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3763 if (status
!= HTTP_STATUS_OK
)
3765 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3766 "Unable to GET URI: %s", httpStatus(status
));
3768 job
->state
= IPP_JSTATE_ABORTED
;
3778 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3780 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3782 int error
= errno
; /* Write error */
3784 job
->state
= IPP_JSTATE_ABORTED
;
3792 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3793 "Unable to write print file: %s", strerror(error
));
3803 int error
= errno
; /* Write error */
3805 job
->state
= IPP_JSTATE_ABORTED
;
3810 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3811 "Unable to write print file: %s", strerror(error
));
3816 job
->filename
= strdup(filename
);
3817 job
->state
= IPP_JSTATE_PENDING
;
3820 * Process the job...
3824 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3826 job
->state
= IPP_JSTATE_ABORTED
;
3827 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3836 * Return the job info...
3839 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3841 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3842 cupsArrayAdd(ra
, "job-id");
3843 cupsArrayAdd(ra
, "job-state");
3844 cupsArrayAdd(ra
, "job-state-reasons");
3845 cupsArrayAdd(ra
, "job-uri");
3847 copy_job_attributes(client
, job
, ra
);
3848 cupsArrayDelete(ra
);
3853 * 'ipp_send_document()' - Add an attached document to a job object created with
3858 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3860 _ipp_job_t
*job
; /* Job information */
3861 char filename
[1024], /* Filename buffer */
3862 buffer
[4096]; /* Copy buffer */
3863 ssize_t bytes
; /* Bytes read */
3864 ipp_attribute_t
*attr
; /* Current attribute */
3865 cups_array_t
*ra
; /* Attributes to send in response */
3872 if ((job
= find_job(client
)) == NULL
)
3874 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3875 httpFlush(client
->http
);
3880 * See if we already have a document for this job or the job has already
3881 * in a non-pending state...
3884 if (job
->state
> IPP_JSTATE_HELD
)
3886 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3887 "Job is not in a pending state.");
3888 httpFlush(client
->http
);
3891 else if (job
->filename
|| job
->fd
>= 0)
3893 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3894 "Multiple document jobs are not supported.");
3895 httpFlush(client
->http
);
3899 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3900 IPP_TAG_ZERO
)) == NULL
)
3902 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3903 "Missing required last-document attribute.");
3904 httpFlush(client
->http
);
3907 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3908 !ippGetBoolean(attr
, 0))
3910 respond_unsupported(client
, attr
);
3911 httpFlush(client
->http
);
3916 * Validate document attributes...
3919 if (!valid_doc_attributes(client
))
3921 httpFlush(client
->http
);
3925 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3928 * Get the document format for the job...
3931 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3933 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3934 job
->format
= ippGetString(attr
, 0, NULL
);
3935 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3936 job
->format
= ippGetString(attr
, 0, NULL
);
3938 job
->format
= "application/octet-stream";
3941 * Create a file for the request data...
3944 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3947 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3949 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3951 _cupsRWUnlock(&(client
->printer
->rwlock
));
3955 job
->state
= IPP_JSTATE_ABORTED
;
3957 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3958 "Unable to create print file: %s", strerror(errno
));
3962 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3964 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3966 int error
= errno
; /* Write error */
3968 job
->state
= IPP_JSTATE_ABORTED
;
3975 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3976 "Unable to write print file: %s", strerror(error
));
3984 * Got an error while reading the print data, so abort this job.
3987 job
->state
= IPP_JSTATE_ABORTED
;
3994 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3995 "Unable to read print file.");
4001 int error
= errno
; /* Write error */
4003 job
->state
= IPP_JSTATE_ABORTED
;
4008 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4009 "Unable to write print file: %s", strerror(error
));
4013 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4016 job
->filename
= strdup(filename
);
4017 job
->state
= IPP_JSTATE_PENDING
;
4019 _cupsRWUnlock(&(client
->printer
->rwlock
));
4022 * Process the job...
4026 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4028 job
->state
= IPP_JSTATE_ABORTED
;
4029 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4038 * Return the job info...
4041 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4043 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4044 cupsArrayAdd(ra
, "job-id");
4045 cupsArrayAdd(ra
, "job-state");
4046 cupsArrayAdd(ra
, "job-state-reasons");
4047 cupsArrayAdd(ra
, "job-uri");
4049 copy_job_attributes(client
, job
, ra
);
4050 cupsArrayDelete(ra
);
4055 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4060 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
4062 _ipp_job_t
*job
; /* Job information */
4063 ipp_attribute_t
*uri
; /* document-uri */
4064 char scheme
[256], /* URI scheme */
4065 userpass
[256], /* Username and password info */
4066 hostname
[256], /* Hostname */
4067 resource
[1024]; /* Resource path */
4068 int port
; /* Port number */
4069 http_uri_status_t uri_status
; /* URI decode status */
4070 http_encryption_t encryption
; /* Encryption to use, if any */
4071 http_t
*http
; /* Connection for http/https URIs */
4072 http_status_t status
; /* Access status for http/https URIs */
4073 int infile
; /* Input file for local file URIs */
4074 char filename
[1024], /* Filename buffer */
4075 buffer
[4096]; /* Copy buffer */
4076 ssize_t bytes
; /* Bytes read */
4077 ipp_attribute_t
*attr
; /* Current attribute */
4078 cups_array_t
*ra
; /* Attributes to send in response */
4079 static const char * const uri_status_strings
[] =
4080 { /* URI decode errors */
4082 "Bad arguments to function.",
4083 "Bad resource in URI.",
4084 "Bad port number in URI.",
4085 "Bad hostname in URI.",
4086 "Bad username in URI.",
4087 "Bad scheme in URI.",
4096 if ((job
= find_job(client
)) == NULL
)
4098 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4099 httpFlush(client
->http
);
4104 * See if we already have a document for this job or the job has already
4105 * in a non-pending state...
4108 if (job
->state
> IPP_JSTATE_HELD
)
4110 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4111 "Job is not in a pending state.");
4112 httpFlush(client
->http
);
4115 else if (job
->filename
|| job
->fd
>= 0)
4117 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4118 "Multiple document jobs are not supported.");
4119 httpFlush(client
->http
);
4123 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4124 IPP_TAG_ZERO
)) == NULL
)
4126 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4127 "Missing required last-document attribute.");
4128 httpFlush(client
->http
);
4131 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4132 !ippGetBoolean(attr
, 0))
4134 respond_unsupported(client
, attr
);
4135 httpFlush(client
->http
);
4140 * Validate document attributes...
4143 if (!valid_doc_attributes(client
))
4145 httpFlush(client
->http
);
4150 * Do we have a file to print?
4153 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4155 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4156 "Unexpected document data following request.");
4161 * Do we have a document URI?
4164 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4165 IPP_TAG_URI
)) == NULL
)
4167 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4171 if (ippGetCount(uri
) != 1)
4173 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4174 "Too many document-uri values.");
4178 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4179 scheme
, sizeof(scheme
), userpass
,
4180 sizeof(userpass
), hostname
, sizeof(hostname
),
4181 &port
, resource
, sizeof(resource
));
4182 if (uri_status
< HTTP_URI_STATUS_OK
)
4184 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4185 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4189 if (strcmp(scheme
, "file") &&
4191 strcmp(scheme
, "https") &&
4192 #endif /* HAVE_SSL */
4193 strcmp(scheme
, "http"))
4195 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4196 "URI scheme \"%s\" not supported.", scheme
);
4200 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4202 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4203 "Unable to access URI: %s", strerror(errno
));
4208 * Get the document format for the job...
4211 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4213 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4214 IPP_TAG_MIMETYPE
)) != NULL
)
4215 job
->format
= ippGetString(attr
, 0, NULL
);
4217 job
->format
= "application/octet-stream";
4220 * Create a file for the request data...
4223 if (!strcasecmp(job
->format
, "image/jpeg"))
4224 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4225 client
->printer
->directory
, job
->id
);
4226 else if (!strcasecmp(job
->format
, "image/png"))
4227 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4228 client
->printer
->directory
, job
->id
);
4229 else if (!strcasecmp(job
->format
, "application/pdf"))
4230 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4231 client
->printer
->directory
, job
->id
);
4232 else if (!strcasecmp(job
->format
, "application/postscript"))
4233 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4234 client
->printer
->directory
, job
->id
);
4236 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4237 client
->printer
->directory
, job
->id
);
4239 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4241 _cupsRWUnlock(&(client
->printer
->rwlock
));
4245 job
->state
= IPP_JSTATE_ABORTED
;
4247 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4248 "Unable to create print file: %s", strerror(errno
));
4252 if (!strcmp(scheme
, "file"))
4254 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4256 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4257 "Unable to access URI: %s", strerror(errno
));
4263 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4264 (errno
== EAGAIN
|| errno
== EINTR
))
4266 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4268 int error
= errno
; /* Write error */
4270 job
->state
= IPP_JSTATE_ABORTED
;
4278 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4279 "Unable to write print file: %s", strerror(error
));
4290 if (port
== 443 || !strcmp(scheme
, "https"))
4291 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4293 #endif /* HAVE_SSL */
4294 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4296 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4297 1, 30000, NULL
)) == NULL
)
4299 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4300 "Unable to connect to %s: %s", hostname
,
4301 cupsLastErrorString());
4302 job
->state
= IPP_JSTATE_ABORTED
;
4311 httpClearFields(http
);
4312 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4313 if (httpGet(http
, resource
))
4315 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4316 "Unable to GET URI: %s", strerror(errno
));
4318 job
->state
= IPP_JSTATE_ABORTED
;
4328 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4330 if (status
!= HTTP_STATUS_OK
)
4332 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4333 "Unable to GET URI: %s", httpStatus(status
));
4335 job
->state
= IPP_JSTATE_ABORTED
;
4345 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4347 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4349 int error
= errno
; /* Write error */
4351 job
->state
= IPP_JSTATE_ABORTED
;
4359 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4360 "Unable to write print file: %s", strerror(error
));
4370 int error
= errno
; /* Write error */
4372 job
->state
= IPP_JSTATE_ABORTED
;
4377 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4378 "Unable to write print file: %s", strerror(error
));
4382 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4385 job
->filename
= strdup(filename
);
4386 job
->state
= IPP_JSTATE_PENDING
;
4388 _cupsRWUnlock(&(client
->printer
->rwlock
));
4391 * Process the job...
4395 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4397 job
->state
= IPP_JSTATE_ABORTED
;
4398 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4407 * Return the job info...
4410 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4412 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4413 cupsArrayAdd(ra
, "job-id");
4414 cupsArrayAdd(ra
, "job-state");
4415 cupsArrayAdd(ra
, "job-state-reasons");
4416 cupsArrayAdd(ra
, "job-uri");
4418 copy_job_attributes(client
, job
, ra
);
4419 cupsArrayDelete(ra
);
4424 * 'ipp_validate_job()' - Validate job creation attributes.
4428 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4430 if (valid_job_attributes(client
))
4431 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4436 * 'parse_options()' - Parse URL options into CUPS options.
4438 * The client->options string is destroyed by this function.
4441 static int /* O - Number of options */
4442 parse_options(_ipp_client_t
*client
, /* I - Client */
4443 cups_option_t
**options
) /* O - Options */
4445 char *name
, /* Name */
4447 *next
; /* Next name=value pair */
4448 int num_options
= 0; /* Number of options */
4453 for (name
= client
->options
; name
&& *name
; name
= next
)
4455 if ((value
= strchr(name
, '=')) == NULL
)
4459 if ((next
= strchr(value
, '&')) != NULL
)
4462 num_options
= cupsAddOption(name
, value
, num_options
, options
);
4465 return (num_options
);
4470 * 'process_client()' - Process client requests on a thread.
4473 static void * /* O - Exit status */
4474 process_client(_ipp_client_t
*client
) /* I - Client */
4477 * Loop until we are out of requests or timeout (30 seconds)...
4481 int first_time
= 1; /* First time request? */
4482 #endif /* HAVE_SSL */
4484 while (httpWait(client
->http
, 30000))
4490 * See if we need to negotiate a TLS connection...
4493 char buf
[1]; /* First byte from client */
4495 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
4497 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
4499 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
4501 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4505 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4510 #endif /* HAVE_SSL */
4512 if (!process_http(client
))
4517 * Close the conection to the client and return...
4520 delete_client(client
);
4527 * 'process_http()' - Process a HTTP request.
4530 int /* O - 1 on success, 0 on failure */
4531 process_http(_ipp_client_t
*client
) /* I - Client connection */
4533 char uri
[1024]; /* URI */
4534 http_state_t http_state
; /* HTTP state */
4535 http_status_t http_status
; /* HTTP status */
4536 ipp_state_t ipp_state
; /* State of IPP transfer */
4537 char scheme
[32], /* Method/scheme */
4538 userpass
[128], /* Username:password */
4539 hostname
[HTTP_MAX_HOST
];
4541 int port
; /* Port number */
4542 const char *encoding
; /* Content-Encoding value */
4543 static const char * const http_states
[] =
4544 { /* Strings for logging HTTP method */
4565 * Clear state variables...
4568 ippDelete(client
->request
);
4569 ippDelete(client
->response
);
4571 client
->request
= NULL
;
4572 client
->response
= NULL
;
4573 client
->operation
= HTTP_STATE_WAITING
;
4576 * Read a request from the connection...
4579 while ((http_state
= httpReadRequest(client
->http
, uri
,
4580 sizeof(uri
))) == HTTP_STATE_WAITING
)
4584 * Parse the request line...
4587 if (http_state
== HTTP_STATE_ERROR
)
4589 if (httpError(client
->http
) == EPIPE
)
4590 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
4592 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
4593 strerror(httpError(client
->http
)));
4597 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
4599 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
4600 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4603 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
4605 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
4606 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4610 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
4614 * Separate the URI into its components...
4617 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4618 userpass
, sizeof(userpass
),
4619 hostname
, sizeof(hostname
), &port
,
4620 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
4621 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
4623 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
4624 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4628 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
4629 *(client
->options
)++ = '\0';
4632 * Process the request...
4635 client
->start
= time(NULL
);
4636 client
->operation
= httpGetState(client
->http
);
4639 * Parse incoming parameters until the status changes...
4642 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4644 if (http_status
!= HTTP_STATUS_OK
)
4646 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4650 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4651 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4654 * HTTP/1.1 and higher require the "Host:" field...
4657 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4662 * Handle HTTP Upgrade...
4665 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4669 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
4671 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
4674 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
4676 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
4678 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4682 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4685 #endif /* HAVE_SSL */
4687 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4692 * Handle HTTP Expect...
4695 if (httpGetExpect(client
->http
) &&
4696 (client
->operation
== HTTP_STATE_POST
||
4697 client
->operation
== HTTP_STATE_PUT
))
4699 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4702 * Send 100-continue header...
4705 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4711 * Send 417-expectation-failed header...
4714 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4720 * Handle new transfers...
4723 encoding
= httpGetContentEncoding(client
->http
);
4725 switch (client
->operation
)
4727 case HTTP_STATE_OPTIONS
:
4729 * Do OPTIONS command...
4732 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4734 case HTTP_STATE_HEAD
:
4735 if (!strcmp(client
->uri
, "/icon.png"))
4736 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4737 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
4738 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4740 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4742 case HTTP_STATE_GET
:
4743 if (!strcmp(client
->uri
, "/icon.png"))
4746 * Send PNG icon file.
4749 int fd
; /* Icon file */
4750 struct stat fileinfo
; /* Icon file information */
4751 char buffer
[4096]; /* Copy buffer */
4752 ssize_t bytes
; /* Bytes */
4754 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
4756 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4757 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4759 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
4760 (size_t)fileinfo
.st_size
))
4766 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4767 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
4769 httpFlushWrite(client
->http
);
4774 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4776 else if (!strcmp(client
->uri
, "/"))
4779 * Show web status page...
4782 _ipp_job_t
*job
; /* Current job */
4783 int i
; /* Looping var */
4784 _ipp_preason_t reason
; /* Current reason */
4785 static const char * const reasons
[] =
4786 { /* Reason strings */
4789 "Input Tray Missing",
4790 "Marker Supply Empty",
4791 "Marker Supply Low",
4792 "Marker Waste Almost Full",
4793 "Marker Waste Full",
4805 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4808 html_header(client
, client
->printer
->name
);
4810 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
4811 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
4812 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
4813 if (client
->printer
->state_reasons
& reason
)
4814 html_printf(client
, "\n<br> %s", reasons
[i
]);
4815 html_printf(client
, "</p>\n");
4817 if (cupsArrayCount(client
->printer
->jobs
) > 0)
4819 _cupsRWLockRead(&(client
->printer
->rwlock
));
4821 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");
4822 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
4824 char when
[256], /* When job queued/started/finished */
4825 hhmmss
[64]; /* Time HH:MM:SS */
4829 case IPP_JSTATE_PENDING
:
4830 case IPP_JSTATE_HELD
:
4831 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
4833 case IPP_JSTATE_PROCESSING
:
4834 case IPP_JSTATE_STOPPED
:
4835 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
4837 case IPP_JSTATE_ABORTED
:
4838 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4840 case IPP_JSTATE_CANCELED
:
4841 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4843 case IPP_JSTATE_COMPLETED
:
4844 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4848 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
);
4850 html_printf(client
, "</tbody></table>\n");
4852 _cupsRWUnlock(&(client
->printer
->rwlock
));
4854 html_footer(client
);
4858 else if (!strcmp(client
->uri
, "/media"))
4861 * Show web media page...
4864 int i
, /* Looping var */
4865 num_options
; /* Number of form options */
4866 cups_option_t
*options
; /* Form options */
4867 static const char * const sizes
[] =
4868 { /* Size strings */
4881 static const char * const types
[] =
4898 static const int sheets
[] = /* Number of sheets */
4907 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4910 html_header(client
, client
->printer
->name
);
4912 if ((num_options
= parse_options(client
, &options
)) > 0)
4915 * WARNING: A real printer/server implementation MUST NOT implement
4916 * media updates via a GET request - GET requests are supposed to be
4917 * idempotent (without side-effects) and we obviously are not
4918 * authenticating access here. This form is provided solely to
4919 * enable testing and development!
4922 const char *val
; /* Form value */
4924 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
4925 client
->printer
->main_size
= atoi(val
);
4926 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
4927 client
->printer
->main_type
= atoi(val
);
4928 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
4929 client
->printer
->main_level
= atoi(val
);
4931 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
4932 client
->printer
->envelope_size
= atoi(val
);
4933 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
4934 client
->printer
->envelope_level
= atoi(val
);
4936 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
4937 client
->printer
->photo_size
= atoi(val
);
4938 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
4939 client
->printer
->photo_type
= atoi(val
);
4940 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
4941 client
->printer
->photo_level
= atoi(val
);
4943 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))
4944 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
4946 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
4948 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
))
4950 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
4951 if (client
->printer
->active_job
)
4952 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
4955 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
4957 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
4960 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
4962 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
4963 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
4964 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4965 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
4966 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
4967 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
4968 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4969 if (!strstr(types
[i
], "Photo"))
4970 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
4971 html_printf(client
, "</select> <select name=\"main_level\">");
4972 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4973 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
4974 html_printf(client
, "</select></td></tr>\n");
4977 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
4978 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4979 if (strstr(sizes
[i
], "Envelope"))
4980 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
4981 html_printf(client
, "</select> <select name=\"envelope_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
->envelope_level
? " selected" : "", sheets
[i
]);
4984 html_printf(client
, "</select></td></tr>\n");
4987 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
4988 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4989 if (strstr(sizes
[i
], "Photo"))
4990 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
4991 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
4992 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4993 if (strstr(types
[i
], "Photo"))
4994 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
4995 html_printf(client
, "</select> <select name=\"photo_level\">");
4996 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4997 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
4998 html_printf(client
, "</select></td></tr>\n");
5000 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5001 html_footer(client
);
5005 else if (!strcmp(client
->uri
, "/supplies"))
5008 * Show web supplies page...
5011 int i
, j
, /* Looping vars */
5012 num_options
; /* Number of form options */
5013 cups_option_t
*options
; /* Form options */
5014 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5016 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5019 html_header(client
, client
->printer
->name
);
5021 if ((num_options
= parse_options(client
, &options
)) > 0)
5024 * WARNING: A real printer/server implementation MUST NOT implement
5025 * supply updates via a GET request - GET requests are supposed to be
5026 * idempotent (without side-effects) and we obviously are not
5027 * authenticating access here. This form is provided solely to
5028 * enable testing and development!
5031 char name
[64]; /* Form field */
5032 const char *val
; /* Form value */
5034 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
);
5036 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5038 snprintf(name
, sizeof(name
), "supply_%d", i
);
5039 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5041 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5047 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
5048 else if (level
< 10)
5049 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
5054 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
5055 else if (level
> 90)
5056 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
5061 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5064 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5066 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5067 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5069 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5070 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5071 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5072 html_printf(client
, "</select></td></tr>\n");
5074 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5075 html_footer(client
);
5080 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5083 case HTTP_STATE_POST
:
5084 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5088 * Not an IPP request...
5091 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5095 * Read the IPP request...
5098 client
->request
= ippNew();
5100 while ((ipp_state
= ippRead(client
->http
,
5101 client
->request
)) != IPP_STATE_DATA
)
5103 if (ipp_state
== IPP_STATE_ERROR
)
5105 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5106 cupsLastErrorString());
5107 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5113 * Now that we have the IPP request, process the request...
5116 return (process_ipp(client
));
5119 break; /* Anti-compiler-warning-code */
5127 * 'process_ipp()' - Process an IPP request.
5130 static int /* O - 1 on success, 0 on error */
5131 process_ipp(_ipp_client_t
*client
) /* I - Client */
5133 ipp_tag_t group
; /* Current group tag */
5134 ipp_attribute_t
*attr
; /* Current attribute */
5135 ipp_attribute_t
*charset
; /* Character set attribute */
5136 ipp_attribute_t
*language
; /* Language attribute */
5137 ipp_attribute_t
*uri
; /* Printer URI attribute */
5138 int major
, minor
; /* Version number */
5139 const char *name
; /* Name of attribute */
5142 debug_attributes("Request", client
->request
, 1);
5145 * First build an empty response message for this request...
5148 client
->operation_id
= ippGetOperation(client
->request
);
5149 client
->response
= ippNewResponse(client
->request
);
5152 * Then validate the request header and required attributes...
5155 major
= ippGetVersion(client
->request
, &minor
);
5157 if (major
< 1 || major
> 2)
5160 * Return an error, since we only support IPP 1.x and 2.x.
5163 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
5164 "Bad request version number %d.%d.", major
, minor
);
5166 else if (ippGetRequestId(client
->request
) <= 0)
5167 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
5168 ippGetRequestId(client
->request
));
5169 else if (!ippFirstAttribute(client
->request
))
5170 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5171 "No attributes in request.");
5175 * Make sure that the attributes are provided in the correct order and
5176 * don't repeat groups...
5179 for (attr
= ippFirstAttribute(client
->request
),
5180 group
= ippGetGroupTag(attr
);
5182 attr
= ippNextAttribute(client
->request
))
5184 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5187 * Out of order; return an error...
5190 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5191 "Attribute groups are out of order (%x < %x).",
5192 ippGetGroupTag(attr
), group
);
5196 group
= ippGetGroupTag(attr
);
5202 * Then make sure that the first three attributes are:
5204 * attributes-charset
5205 * attributes-natural-language
5206 * printer-uri/job-uri
5209 attr
= ippFirstAttribute(client
->request
);
5210 name
= ippGetName(attr
);
5211 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5212 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5217 attr
= ippNextAttribute(client
->request
);
5218 name
= ippGetName(attr
);
5220 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5221 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5226 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5227 IPP_TAG_URI
)) != NULL
)
5229 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5230 IPP_TAG_URI
)) != NULL
)
5236 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5237 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5240 * Bad character set...
5243 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5244 "Unsupported character set \"%s\".",
5245 ippGetString(charset
, 0, NULL
));
5247 else if (!charset
|| !language
|| !uri
)
5250 * Return an error, since attributes-charset,
5251 * attributes-natural-language, and printer-uri/job-uri are required
5252 * for all operations.
5255 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5256 "Missing required attributes.");
5260 char scheme
[32], /* URI scheme */
5261 userpass
[32], /* Username/password in URI */
5262 host
[256], /* Host name in URI */
5263 resource
[256]; /* Resource path in URI */
5264 int port
; /* Port number in URI */
5266 name
= ippGetName(uri
);
5268 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5269 scheme
, sizeof(scheme
),
5270 userpass
, sizeof(userpass
),
5271 host
, sizeof(host
), &port
,
5272 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5273 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5274 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5275 else if ((!strcmp(name
, "job-uri") &&
5276 strncmp(resource
, "/ipp/print/", 11)) ||
5277 (!strcmp(name
, "printer-uri") &&
5278 strcmp(resource
, "/ipp/print")))
5279 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5280 name
, ippGetString(uri
, 0, NULL
));
5284 * Try processing the operation...
5287 switch (ippGetOperation(client
->request
))
5289 case IPP_OP_PRINT_JOB
:
5290 ipp_print_job(client
);
5293 case IPP_OP_PRINT_URI
:
5294 ipp_print_uri(client
);
5297 case IPP_OP_VALIDATE_JOB
:
5298 ipp_validate_job(client
);
5301 case IPP_OP_CREATE_JOB
:
5302 ipp_create_job(client
);
5305 case IPP_OP_SEND_DOCUMENT
:
5306 ipp_send_document(client
);
5309 case IPP_OP_SEND_URI
:
5310 ipp_send_uri(client
);
5313 case IPP_OP_CANCEL_JOB
:
5314 ipp_cancel_job(client
);
5317 case IPP_OP_GET_JOB_ATTRIBUTES
:
5318 ipp_get_job_attributes(client
);
5321 case IPP_OP_GET_JOBS
:
5322 ipp_get_jobs(client
);
5325 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5326 ipp_get_printer_attributes(client
);
5329 case IPP_OP_CLOSE_JOB
:
5330 ipp_close_job(client
);
5333 case IPP_OP_IDENTIFY_PRINTER
:
5334 ipp_identify_printer(client
);
5338 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5339 "Operation not supported.");
5348 * Send the HTTP header and return...
5351 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5352 httpFlush(client
->http
); /* Flush trailing (junk) data */
5354 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5355 ippLength(client
->response
)));
5360 * 'process_job()' - Process a print job.
5363 static void * /* O - Thread exit status */
5364 process_job(_ipp_job_t
*job
) /* I - Job */
5366 job
->state
= IPP_JSTATE_PROCESSING
;
5367 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
5368 job
->processing
= time(NULL
);
5370 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
5372 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5377 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
5379 if (job
->printer
->command
)
5382 * Execute a command with the job spool file and wait for it to complete...
5385 int pid
, /* Process ID */
5386 status
; /* Exit status */
5387 time_t start
, /* Start time */
5389 char *myargv
[3], /* Command-line arguments */
5390 *myenvp
[200]; /* Environment variables */
5391 int myenvc
; /* Number of environment variables */
5392 ipp_attribute_t
*attr
; /* Job attribute */
5393 char val
[1280], /* IPP_NAME=value */
5394 *valptr
; /* Pointer into string */
5396 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
5401 * Setup the command-line arguments...
5404 myargv
[0] = job
->printer
->command
;
5405 myargv
[1] = job
->filename
;
5409 * Copy the current environment, then add ENV variables for every Job
5413 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
5414 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
5416 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
5419 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
5420 * value(s) from the attribute.
5423 const char *name
= ippGetName(attr
);
5432 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
5437 *valptr
++ = (char)toupper(*name
& 255);
5442 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
5444 myenvp
[myenvc
++] = strdup(val
);
5446 myenvp
[myenvc
] = NULL
;
5449 * Now run the program...
5453 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
5455 if ((pid
= fork()) == 0)
5458 * Child comes here...
5461 execve(job
->printer
->command
, myargv
, myenvp
);
5467 * Unable to fork process...
5470 perror("Unable to start job processing command");
5476 * Free memory used for environment...
5480 free(myenvp
[-- myenvc
]);
5483 * Wait for child to complete...
5486 # ifdef HAVE_WAITPID
5487 while (waitpid(pid
, &status
, 0) < 0);
5489 while (wait(&status
) < 0);
5490 # endif /* HAVE_WAITPID */
5497 if (WIFEXITED(status
))
5499 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
5500 job
->printer
->command
, WEXITSTATUS(status
));
5503 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
5504 job
->printer
->command
, WTERMSIG(status
));
5506 job
->state
= IPP_JSTATE_ABORTED
;
5508 else if (status
< 0)
5509 job
->state
= IPP_JSTATE_ABORTED
;
5511 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
5512 job
->printer
->command
);
5515 * Make sure processing takes at least 5 seconds...
5519 if ((end
- start
) < 5)
5525 * Sleep for a random amount of time to simulate job processing.
5528 sleep((unsigned)(5 + (rand() % 11)));
5532 job
->state
= IPP_JSTATE_CANCELED
;
5533 else if (job
->state
== IPP_JSTATE_PROCESSING
)
5534 job
->state
= IPP_JSTATE_COMPLETED
;
5536 job
->completed
= time(NULL
);
5537 job
->printer
->state
= IPP_PSTATE_IDLE
;
5538 job
->printer
->active_job
= NULL
;
5545 * 'register_printer()' - Register a printer object via Bonjour.
5548 static int /* O - 1 on success, 0 on error */
5550 _ipp_printer_t
*printer
, /* I - Printer */
5551 const char *location
, /* I - Location */
5552 const char *make
, /* I - Manufacturer */
5553 const char *model
, /* I - Model name */
5554 const char *formats
, /* I - Supported formats */
5555 const char *adminurl
, /* I - Web interface URL */
5556 const char *uuid
, /* I - Printer UUID */
5557 int color
, /* I - 1 = color, 0 = monochrome */
5558 int duplex
, /* I - 1 = duplex, 0 = simplex */
5559 const char *subtype
) /* I - Service subtype */
5561 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
5562 _ipp_txt_t ipp_txt
; /* Bonjour IPP TXT record */
5563 #endif /* HAVE_DNSSD || HAVE_AVAHI */
5565 DNSServiceErrorType error
; /* Error from Bonjour */
5566 char make_model
[256],/* Make and model together */
5567 product
[256], /* Product string */
5568 regtype
[256]; /* Bonjour service type */
5572 * Build the TXT record for IPP...
5575 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
5576 snprintf(product
, sizeof(product
), "(%s)", model
);
5578 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
5579 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
5580 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(make_model
),
5582 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(adminurl
),
5585 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(location
),
5587 TXTRecordSetValue(&ipp_txt
, "product", (uint8_t)strlen(product
),
5589 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
),
5591 TXTRecordSetValue(&ipp_txt
, "Color", 1, color
? "T" : "F");
5592 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, duplex
? "T" : "F");
5593 TXTRecordSetValue(&ipp_txt
, "usb_MFG", (uint8_t)strlen(make
),
5595 TXTRecordSetValue(&ipp_txt
, "usb_MDL", (uint8_t)strlen(model
),
5597 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(uuid
), uuid
);
5599 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
5600 # endif /* HAVE_SSL */
5601 if (strstr(formats
, "image/urf"))
5602 TXTRecordSetValue(&ipp_txt
, "URF", 66, "CP1,IS1-5-7,MT1-2-3-4-5-6-8-9-10-11-12-13,RS600,SRGB24,V1.4,W8,DM1");
5604 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
5605 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
5608 * Register the _printer._tcp (LPD) service type with a port number of 0 to
5609 * defend our service name but not actually support LPD...
5612 printer
->printer_ref
= DNSSDMaster
;
5614 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
5615 kDNSServiceFlagsShareConnection
,
5616 0 /* interfaceIndex */, printer
->dnssd_name
,
5617 "_printer._tcp", NULL
/* domain */,
5618 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
5619 NULL
/* txtRecord */,
5620 (DNSServiceRegisterReply
)dnssd_callback
,
5621 printer
)) != kDNSServiceErr_NoError
)
5623 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
5624 printer
->dnssd_name
, error
);
5629 * Then register the _ipp._tcp (IPP) service type with the real port number to
5630 * advertise our IPP printer...
5633 printer
->ipp_ref
= DNSSDMaster
;
5635 if (subtype
&& *subtype
)
5636 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
5638 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
5640 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
5641 kDNSServiceFlagsShareConnection
,
5642 0 /* interfaceIndex */, printer
->dnssd_name
,
5643 regtype
, NULL
/* domain */,
5644 NULL
/* host */, htons(printer
->port
),
5645 TXTRecordGetLength(&ipp_txt
),
5646 TXTRecordGetBytesPtr(&ipp_txt
),
5647 (DNSServiceRegisterReply
)dnssd_callback
,
5648 printer
)) != kDNSServiceErr_NoError
)
5650 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5651 printer
->dnssd_name
, regtype
, error
);
5657 * Then register the _ipps._tcp (IPP) service type with the real port number to
5658 * advertise our IPPS printer...
5661 printer
->ipps_ref
= DNSSDMaster
;
5663 if (subtype
&& *subtype
)
5664 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
5666 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
5668 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
5669 kDNSServiceFlagsShareConnection
,
5670 0 /* interfaceIndex */, printer
->dnssd_name
,
5671 regtype
, NULL
/* domain */,
5672 NULL
/* host */, htons(printer
->port
),
5673 TXTRecordGetLength(&ipp_txt
),
5674 TXTRecordGetBytesPtr(&ipp_txt
),
5675 (DNSServiceRegisterReply
)dnssd_callback
,
5676 printer
)) != kDNSServiceErr_NoError
)
5678 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5679 printer
->dnssd_name
, regtype
, error
);
5682 # endif /* HAVE_SSL */
5685 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
5686 * real port number to advertise our IPP printer...
5689 printer
->http_ref
= DNSSDMaster
;
5691 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
5692 kDNSServiceFlagsShareConnection
,
5693 0 /* interfaceIndex */, printer
->dnssd_name
,
5694 "_http._tcp,_printer", NULL
/* domain */,
5695 NULL
/* host */, htons(printer
->port
),
5696 0 /* txtLen */, NULL
, /* txtRecord */
5697 (DNSServiceRegisterReply
)dnssd_callback
,
5698 printer
)) != kDNSServiceErr_NoError
)
5700 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5701 printer
->dnssd_name
, regtype
, error
);
5705 TXTRecordDeallocate(&ipp_txt
);
5707 #elif defined(HAVE_AVAHI)
5708 char temp
[256]; /* Subtype service string */
5711 * Create the TXT record...
5715 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
5716 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s %s", make
, model
);
5717 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", adminurl
);
5719 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", location
);
5720 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "product=(%s)", model
);
5721 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
5722 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", color
? "T" : "F");
5723 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", duplex
? "T" : "F");
5724 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MFG=%s", make
);
5725 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MDL=%s", model
);
5726 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", uuid
);
5728 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
5729 # endif /* HAVE_SSL */
5732 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
5735 avahi_threaded_poll_lock(DNSSDMaster
);
5737 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
5739 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
);
5742 * Then register the _ipp._tcp (IPP)...
5745 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
);
5746 if (subtype
&& *subtype
)
5748 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
5749 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
5754 * _ipps._tcp (IPPS) for secure printing...
5757 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
);
5758 if (subtype
&& *subtype
)
5760 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
5761 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
5763 #endif /* HAVE_SSL */
5766 * Finally _http.tcp (HTTP) for the web interface...
5769 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
);
5770 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");
5776 avahi_entry_group_commit(printer
->ipp_ref
);
5777 avahi_threaded_poll_unlock(DNSSDMaster
);
5779 avahi_string_list_free(ipp_txt
);
5780 #endif /* HAVE_DNSSD */
5787 * 'respond_http()' - Send a HTTP response.
5790 int /* O - 1 on success, 0 on failure */
5792 _ipp_client_t
*client
, /* I - Client */
5793 http_status_t code
, /* I - HTTP status of response */
5794 const char *content_encoding
, /* I - Content-Encoding of response */
5795 const char *type
, /* I - MIME media type of response */
5796 size_t length
) /* I - Length of response */
5798 char message
[1024]; /* Text message */
5801 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
5803 if (code
== HTTP_STATUS_CONTINUE
)
5806 * 100-continue doesn't send any headers...
5809 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
5813 * Format an error message...
5816 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
5818 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
5820 type
= "text/plain";
5821 length
= strlen(message
);
5827 * Send the HTTP response header...
5830 httpClearFields(client
->http
);
5832 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
5833 client
->operation
== HTTP_STATE_OPTIONS
)
5834 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
5838 if (!strcmp(type
, "text/html"))
5839 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
5840 "text/html; charset=utf-8");
5842 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
5844 if (content_encoding
)
5845 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
5848 httpSetLength(client
->http
, length
);
5850 if (httpWriteResponse(client
->http
, code
) < 0)
5854 * Send the response data...
5860 * Send a plain text message.
5863 if (httpPrintf(client
->http
, "%s", message
) < 0)
5866 if (httpWrite2(client
->http
, "", 0) < 0)
5869 else if (client
->response
)
5872 * Send an IPP response...
5875 debug_attributes("Response", client
->response
, 2);
5877 ippSetState(client
->response
, IPP_STATE_IDLE
);
5879 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
5888 * 'respond_ipp()' - Send an IPP response.
5892 respond_ipp(_ipp_client_t
*client
, /* I - Client */
5893 ipp_status_t status
, /* I - status-code */
5894 const char *message
, /* I - printf-style status-message */
5895 ...) /* I - Additional args as needed */
5897 const char *formatted
= NULL
; /* Formatted message */
5900 ippSetStatusCode(client
->response
, status
);
5904 va_list ap
; /* Pointer to additional args */
5905 ipp_attribute_t
*attr
; /* New status-message attribute */
5907 va_start(ap
, message
);
5908 if ((attr
= ippFindAttribute(client
->response
, "status-message",
5909 IPP_TAG_TEXT
)) != NULL
)
5910 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
5912 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
5913 "status-message", NULL
, message
, ap
);
5916 formatted
= ippGetString(attr
, 0, NULL
);
5920 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
5921 ippOpString(client
->operation_id
), ippErrorString(status
),
5924 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
5925 ippOpString(client
->operation_id
), ippErrorString(status
));
5930 * 'respond_unsupported()' - Respond with an unsupported attribute.
5934 respond_unsupported(
5935 _ipp_client_t
*client
, /* I - Client */
5936 ipp_attribute_t
*attr
) /* I - Atribute */
5938 ipp_attribute_t
*temp
; /* Copy of attribute */
5941 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5942 "Unsupported %s %s%s value.", ippGetName(attr
),
5943 ippGetCount(attr
) > 1 ? "1setOf " : "",
5944 ippTagString(ippGetValueTag(attr
)));
5946 temp
= ippCopyAttribute(client
->response
, attr
, 0);
5947 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
5952 * 'run_printer()' - Run the printer service.
5956 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
5958 int num_fds
; /* Number of file descriptors */
5959 struct pollfd polldata
[3]; /* poll() data */
5960 int timeout
; /* Timeout for poll() */
5961 _ipp_client_t
*client
; /* New client */
5965 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5968 polldata
[0].fd
= printer
->ipv4
;
5969 polldata
[0].events
= POLLIN
;
5971 polldata
[1].fd
= printer
->ipv6
;
5972 polldata
[1].events
= POLLIN
;
5977 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
5978 polldata
[num_fds
++].events
= POLLIN
;
5979 #endif /* HAVE_DNSSD */
5982 * Loop until we are killed or have a hard error...
5987 if (cupsArrayCount(printer
->jobs
))
5992 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
5994 perror("poll() failed");
5998 if (polldata
[0].revents
& POLLIN
)
6000 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6002 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
6004 perror("Unable to create client thread");
6005 delete_client(client
);
6010 if (polldata
[1].revents
& POLLIN
)
6012 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6014 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
6016 perror("Unable to create client thread");
6017 delete_client(client
);
6023 if (polldata
[2].revents
& POLLIN
)
6024 DNSServiceProcessResult(DNSSDMaster
);
6025 #endif /* HAVE_DNSSD */
6028 * Clean out old jobs...
6031 clean_jobs(printer
);
6037 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6041 time_string(time_t tv
, /* I - Time value */
6042 char *buffer
, /* I - Buffer */
6043 size_t bufsize
) /* I - Size of buffer */
6045 struct tm
*curtime
= localtime(&tv
);
6048 strftime(buffer
, bufsize
, "%X", curtime
);
6054 * 'usage()' - Show program usage.
6058 usage(int status
) /* O - Exit status */
6062 puts(CUPS_SVERSION
" - Copyright 2010-2014 by Apple Inc. All rights "
6067 puts("Usage: ippserver [options] \"name\"");
6070 puts("-2 Supports 2-sided printing (default=1-sided)");
6071 puts("-M manufacturer Manufacturer name (default=Test)");
6072 puts("-P PIN printing mode");
6073 puts("-c command Run command for every print job");
6074 printf("-d spool-directory Spool directory "
6075 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6076 puts("-f type/subtype[,...] List of supported types "
6077 "(default=application/pdf,image/jpeg)");
6078 puts("-h Show program help");
6079 puts("-i iconfile.png PNG icon file (default=printer.png)");
6080 puts("-k Keep job spool files");
6081 puts("-l location Location of printer (default=empty string)");
6082 puts("-m model Model name (default=Printer)");
6083 puts("-n hostname Hostname for printer");
6084 puts("-p port Port number (default=auto)");
6085 puts("-r subtype Bonjour service subtype (default=_print)");
6086 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6087 puts("-v[vvv] Be (very) verbose");
6094 * 'valid_doc_attributes()' - Determine whether the document attributes are
6097 * When one or more document attributes are invalid, this function adds a
6098 * suitable response and attributes to the unsupported group.
6101 static int /* O - 1 if valid, 0 if not */
6102 valid_doc_attributes(
6103 _ipp_client_t
*client
) /* I - Client */
6105 int valid
= 1; /* Valid attributes? */
6106 ipp_op_t op
= ippGetOperation(client
->request
);
6108 const char *op_name
= ippOpString(op
);
6109 /* IPP operation name */
6110 ipp_attribute_t
*attr
, /* Current attribute */
6111 *supported
; /* xxx-supported attribute */
6112 const char *compression
= NULL
,
6113 /* compression value */
6114 *format
= NULL
; /* document-format value */
6118 * Check operation attributes...
6121 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6124 * If compression is specified, only accept a supported value in a Print-Job
6125 * or Send-Document request...
6128 compression
= ippGetString(attr
, 0, NULL
);
6129 supported
= ippFindAttribute(client
->printer
->attrs
,
6130 "compression-supported", IPP_TAG_KEYWORD
);
6132 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6133 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6134 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6135 op
!= IPP_OP_VALIDATE_JOB
) ||
6136 !ippContainsString(supported
, compression
))
6138 respond_unsupported(client
, attr
);
6143 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6145 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6147 if (strcmp(compression
, "none"))
6150 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6151 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
6157 * Is it a format we support?
6160 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
6162 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
6163 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
6165 respond_unsupported(client
, attr
);
6170 format
= ippGetString(attr
, 0, NULL
);
6172 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
6173 client
->hostname
, op_name
, format
);
6175 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
6180 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
6182 format
= "application/octet-stream"; /* Should never happen */
6184 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
6187 if (!strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
6190 * Auto-type the file using the first 8 bytes of the file...
6193 unsigned char header
[8]; /* First 8 bytes of file */
6195 memset(header
, 0, sizeof(header
));
6196 httpPeek(client
->http
, (char *)header
, sizeof(header
));
6198 if (!memcmp(header
, "%PDF", 4))
6199 format
= "application/pdf";
6200 else if (!memcmp(header
, "%!", 2))
6201 format
= "application/postscript";
6202 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
6203 format
= "image/jpeg";
6204 else if (!memcmp(header
, "\211PNG", 4))
6205 format
= "image/png";
6206 else if (!memcmp(header
, "RAS2", 4))
6207 format
= "image/pwg-raster";
6208 else if (!memcmp(header
, "UNIRAST", 8))
6209 format
= "image/urf";
6215 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
6216 client
->hostname
, op_name
, format
);
6218 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
6222 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
6224 respond_unsupported(client
, attr
);
6232 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
6233 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
6240 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6242 * When one or more job attributes are invalid, this function adds a suitable
6243 * response and attributes to the unsupported group.
6246 static int /* O - 1 if valid, 0 if not */
6247 valid_job_attributes(
6248 _ipp_client_t
*client
) /* I - Client */
6250 int i
, /* Looping var */
6251 valid
= 1; /* Valid attributes? */
6252 ipp_attribute_t
*attr
, /* Current attribute */
6253 *supported
; /* xxx-supported attribute */
6257 * Check operation attributes...
6260 valid
= valid_doc_attributes(client
);
6263 * Check the various job template attributes...
6266 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
6268 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6269 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
6271 respond_unsupported(client
, attr
);
6276 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
6278 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
6280 respond_unsupported(client
, attr
);
6285 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
6287 if (ippGetCount(attr
) != 1 ||
6288 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6289 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6290 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6291 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
6293 respond_unsupported(client
, attr
);
6298 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
6300 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
6302 respond_unsupported(client
, attr
);
6307 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
6309 if (ippGetCount(attr
) != 1 ||
6310 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6311 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
6313 respond_unsupported(client
, attr
);
6317 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
6320 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
6322 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
6324 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6325 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
6327 respond_unsupported(client
, attr
);
6332 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
6334 if (ippGetCount(attr
) != 1 ||
6335 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6336 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6337 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6338 strcmp(ippGetString(attr
, 0, NULL
), "none"))
6340 respond_unsupported(client
, attr
);
6345 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
6347 if (ippGetCount(attr
) != 1 ||
6348 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6349 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6350 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
6352 respond_unsupported(client
, attr
);
6358 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
6360 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
6363 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
6365 respond_unsupported(client
, attr
);
6371 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
6373 if (ippGetCount(attr
) != 1 ||
6374 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
6376 respond_unsupported(client
, attr
);
6379 /* TODO: check for valid media-col */
6382 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
6384 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6385 (strcmp(ippGetString(attr
, 0, NULL
),
6386 "separate-documents-uncollated-copies") &&
6387 strcmp(ippGetString(attr
, 0, NULL
),
6388 "separate-documents-collated-copies")))
6390 respond_unsupported(client
, attr
);
6395 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
6397 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6398 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
6399 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
6401 respond_unsupported(client
, attr
);
6406 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
6408 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
6410 respond_unsupported(client
, attr
);
6415 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
6417 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6418 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
6419 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
6421 respond_unsupported(client
, attr
);
6426 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
6428 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
6430 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
6433 respond_unsupported(client
, attr
);
6438 int count
, /* Number of supported values */
6439 xdpi
, /* Horizontal resolution for job template attribute */
6440 ydpi
, /* Vertical resolution for job template attribute */
6441 sydpi
; /* Vertical resolution for supported value */
6442 ipp_res_t units
, /* Units for job template attribute */
6443 sunits
; /* Units for supported value */
6445 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
6446 count
= ippGetCount(supported
);
6448 for (i
= 0; i
< count
; i
++)
6450 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
6456 respond_unsupported(client
, attr
);
6462 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
6464 const char *sides
= ippGetString(attr
, 0, NULL
);
6465 /* "sides" value... */
6467 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
6469 respond_unsupported(client
, attr
);
6472 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
6474 if (!ippContainsString(supported
, sides
))
6476 respond_unsupported(client
, attr
);
6480 else if (strcmp(sides
, "one-sided"))
6482 respond_unsupported(client
, attr
);