2 * "$Id: ippserver.c 12136 2014-08-29 15:19:40Z msweet $"
4 * Sample IPP Everywhere server for CUPS.
6 * Copyright 2010-2014 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * Disable private and deprecated stuff so we can verify that the public API
19 * is sufficient to implement a server.
22 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
23 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
27 * Include necessary headers...
30 #include <config.h> /* CUPS configuration header */
31 #include <cups/cups.h> /* Public API */
32 #include <cups/string-private.h> /* CUPS string functions */
33 #include <cups/thread-private.h> /* For multithreading functions */
46 # define WEXITSTATUS(s) (s)
47 # include <winsock2.h>
51 extern char **environ
;
53 # include <sys/fcntl.h>
54 # include <sys/wait.h>
60 #endif /* HAVE_DNSSD */
61 #ifdef HAVE_SYS_MOUNT_H
62 # include <sys/mount.h>
63 #endif /* HAVE_SYS_MOUNT_H */
64 #ifdef HAVE_SYS_STATFS_H
65 # include <sys/statfs.h>
66 #endif /* HAVE_SYS_STATFS_H */
67 #ifdef HAVE_SYS_STATVFS_H
68 # include <sys/statvfs.h>
69 #endif /* HAVE_SYS_STATVFS_H */
72 #endif /* HAVE_SYS_VFS_H */
79 enum _ipp_preason_e
/* printer-state-reasons bit values */
81 _IPP_PREASON_NONE
= 0x0000, /* none */
82 _IPP_PREASON_OTHER
= 0x0001, /* other */
83 _IPP_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
84 _IPP_PREASON_INPUT_TRAY_MISSING
= 0x0004,
85 /* input-tray-missing */
86 _IPP_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
87 /* marker-supply-empty */
88 _IPP_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
89 /* marker-supply-low */
90 _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
91 /* marker-waste-almost-full */
92 _IPP_PREASON_MARKER_WASTE_FULL
= 0x0040,
93 /* marker-waste-full */
94 _IPP_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
95 _IPP_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
96 _IPP_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
97 _IPP_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
98 _IPP_PREASON_MOVING_TO_PAUSED
= 0x0800,
99 /* moving-to-paused */
100 _IPP_PREASON_PAUSED
= 0x1000, /* paused */
101 _IPP_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
102 _IPP_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
103 _IPP_PREASON_TONER_LOW
= 0x8000 /* toner-low */
105 typedef unsigned int _ipp_preason_t
; /* Bitfield for printer-state-reasons */
107 typedef enum _ipp_media_class_e
109 _IPP_GENERAL
, /* General-purpose size */
110 _IPP_PHOTO_ONLY
, /* Photo-only size */
111 _IPP_ENV_ONLY
/* Envelope-only size */
112 } _ipp_media_class_t
;
114 typedef enum _ipp_media_size_e
116 _IPP_MEDIA_SIZE_NONE
= -1,
121 _IPP_MEDIA_SIZE_LEGAL
,
122 _IPP_MEDIA_SIZE_LETTER
,
123 _IPP_MEDIA_SIZE_COM10
,
129 static const char * const media_supported
[] =
130 { /* media-supported values */
131 "iso_a4_210x297mm", /* A4 */
132 "iso_a5_148x210mm", /* A5 */
133 "iso_a6_105x148mm", /* A6 */
134 "iso_dl_110x220mm", /* DL */
135 "na_legal_8.5x14in", /* Legal */
136 "na_letter_8.5x11in", /* Letter */
137 "na_number-10_4.125x9.5in", /* #10 */
138 "na_index-3x5_3x5in", /* 3x5 */
139 "oe_photo-l_3.5x5in", /* L */
140 "na_index-4x6_4x6in", /* 4x6 */
141 "na_5x7_5x7in" /* 5x7 aka 2L */
143 static const int media_col_sizes
[][3] =
144 { /* media-col-database sizes */
145 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
146 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
147 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
148 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
149 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
150 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
151 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
152 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
153 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
154 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
155 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
158 typedef enum _ipp_media_source_e
160 _IPP_MEDIA_SOURCE_NONE
= -1,
161 _IPP_MEDIA_SOURCE_AUTO
,
162 _IPP_MEDIA_SOURCE_MAIN
,
163 _IPP_MEDIA_SOURCE_MANUAL
,
164 _IPP_MEDIA_SOURCE_ENVELOPE
,
165 _IPP_MEDIA_SOURCE_PHOTO
166 } _ipp_media_source_t
;
167 static const char * const media_source_supported
[] =
168 /* media-source-supported values */
177 typedef enum _ipp_media_type_e
179 _IPP_MEDIA_TYPE_NONE
= -1,
180 _IPP_MEDIA_TYPE_AUTO
,
181 _IPP_MEDIA_TYPE_CARDSTOCK
,
182 _IPP_MEDIA_TYPE_ENVELOPE
,
183 _IPP_MEDIA_TYPE_LABELS
,
184 _IPP_MEDIA_TYPE_OTHER
,
185 _IPP_MEDIA_TYPE_GLOSSY
,
186 _IPP_MEDIA_TYPE_HIGH_GLOSS
,
187 _IPP_MEDIA_TYPE_MATTE
,
188 _IPP_MEDIA_TYPE_SATIN
,
189 _IPP_MEDIA_TYPE_SEMI_GLOSS
,
190 _IPP_MEDIA_TYPE_STATIONERY
,
191 _IPP_MEDIA_TYPE_LETTERHEAD
,
192 _IPP_MEDIA_TYPE_TRANSPARENCY
194 static const char * const media_type_supported
[] =
195 /* media-type-supported values */
202 "photographic-glossy",
203 "photographic-high-gloss",
204 "photographic-matte",
205 "photographic-satin",
206 "photographic-semi-gloss",
208 "stationery-letterhead",
212 typedef enum _ipp_supply_e
214 _IPP_SUPPLY_CYAN
, /* Cyan Toner */
215 _IPP_SUPPLY_MAGENTA
, /* Magenta Toner */
216 _IPP_SUPPLY_YELLOW
, /* Yellow Toner */
217 _IPP_SUPPLY_BLACK
, /* Black Toner */
218 _IPP_SUPPLY_WASTE
/* Waste Toner */
220 static const char * const printer_supplies
[] =
221 { /* printer-supply-description values */
234 typedef struct _ipp_filter_s
/**** Attribute filter ****/
236 cups_array_t
*ra
; /* Requested attributes */
237 ipp_tag_t group_tag
; /* Group to copy */
240 typedef struct _ipp_job_s _ipp_job_t
;
242 typedef struct _ipp_printer_s
/**** Printer data ****/
244 int ipv4
, /* IPv4 listener */
245 ipv6
; /* IPv6 listener */
247 DNSServiceRef common_ref
, /* Shared service connection */
248 ipp_ref
, /* Bonjour IPP service */
250 ipps_ref
, /* Bonjour IPPS service */
251 # endif /* HAVE_SSL */
252 http_ref
, /* Bonjour HTTP service */
253 printer_ref
; /* Bonjour LPD service */
254 TXTRecordRef ipp_txt
; /* Bonjour IPP TXT record */
255 char *dnssd_name
; /* printer-dnssd-name */
256 #endif /* HAVE_DNSSD */
257 char *name
, /* printer-name */
258 *icon
, /* Icon filename */
259 *directory
, /* Spool directory */
260 *hostname
, /* Hostname */
261 *uri
, /* printer-uri-supported */
262 *command
; /* Command to run with job file */
264 size_t urilen
; /* Length of printer URI */
265 ipp_t
*attrs
; /* Static attributes */
266 time_t start_time
; /* Startup time */
267 time_t config_time
; /* printer-config-change-time */
268 ipp_pstate_t state
; /* printer-state value */
269 _ipp_preason_t state_reasons
; /* printer-state-reasons values */
270 time_t state_time
; /* printer-state-change-time */
271 cups_array_t
*jobs
; /* Jobs */
272 _ipp_job_t
*active_job
; /* Current active/pending job */
273 int next_job_id
; /* Next job-id value */
274 _cups_rwlock_t rwlock
; /* Printer lock */
275 _ipp_media_size_t main_size
; /* Ready media */
276 _ipp_media_type_t main_type
;
278 _ipp_media_size_t envelope_size
;
280 _ipp_media_size_t photo_size
;
281 _ipp_media_type_t photo_type
;
283 int supplies
[5]; /* Supply levels (0-100) */
286 struct _ipp_job_s
/**** Job data ****/
289 const char *name
, /* job-name */
290 *username
, /* job-originating-user-name */
291 *format
; /* document-format */
292 ipp_jstate_t state
; /* job-state value */
293 time_t created
, /* time-at-creation value */
294 processing
, /* time-at-processing value */
295 completed
; /* time-at-completed value */
296 int impressions
, /* job-impressions value */
297 impcompleted
; /* job-impressions-completed value */
298 ipp_t
*attrs
; /* Static attributes */
299 int cancel
; /* Non-zero when job canceled */
300 char *filename
; /* Print file name */
301 int fd
; /* Print file descriptor */
302 _ipp_printer_t
*printer
; /* Printer */
305 typedef struct _ipp_client_s
/**** Client data ****/
307 http_t
*http
; /* HTTP connection */
308 ipp_t
*request
, /* IPP request */
309 *response
; /* IPP response */
310 time_t start
; /* Request start time */
311 http_state_t operation
; /* Request operation */
312 ipp_op_t operation_id
; /* IPP operation-id */
313 char uri
[1024], /* Request URI */
314 *options
; /* URI options */
315 http_addr_t addr
; /* Client address */
316 char hostname
[256]; /* Client hostname */
317 _ipp_printer_t
*printer
; /* Printer */
318 _ipp_job_t
*job
; /* Current job, if any */
326 static void clean_jobs(_ipp_printer_t
*printer
);
327 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
328 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
329 ipp_tag_t group_tag
, int quickcopy
);
330 static void copy_job_attributes(_ipp_client_t
*client
,
331 _ipp_job_t
*job
, cups_array_t
*ra
);
332 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
333 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
334 static int create_listener(int family
, int port
);
335 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
336 static ipp_t
*create_media_size(int width
, int length
);
337 static _ipp_printer_t
*create_printer(const char *servername
,
338 const char *name
, const char *location
,
339 const char *make
, const char *model
,
341 const char *docformats
, int ppm
,
342 int ppm_color
, int duplex
, int port
,
346 #endif /* HAVE_DNSSD */
347 const char *directory
,
348 const char *command
);
349 static void debug_attributes(const char *title
, ipp_t
*ipp
,
351 static void delete_client(_ipp_client_t
*client
);
352 static void delete_job(_ipp_job_t
*job
);
353 static void delete_printer(_ipp_printer_t
*printer
);
355 static void dnssd_callback(DNSServiceRef sdRef
,
356 DNSServiceFlags flags
,
357 DNSServiceErrorType errorCode
,
361 _ipp_printer_t
*printer
);
362 #endif /* HAVE_DNSSD */
363 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
364 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
365 static void html_escape(_ipp_client_t
*client
, const char *s
,
367 static void html_footer(_ipp_client_t
*client
);
368 static void html_header(_ipp_client_t
*client
, const char *title
);
369 static void html_printf(_ipp_client_t
*client
, const char *format
,
370 ...) __attribute__((__format__(__printf__
,
372 static void ipp_cancel_job(_ipp_client_t
*client
);
373 static void ipp_close_job(_ipp_client_t
*client
);
374 static void ipp_create_job(_ipp_client_t
*client
);
375 static void ipp_get_job_attributes(_ipp_client_t
*client
);
376 static void ipp_get_jobs(_ipp_client_t
*client
);
377 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
378 static void ipp_identify_printer(_ipp_client_t
*client
);
379 static void ipp_print_job(_ipp_client_t
*client
);
380 static void ipp_print_uri(_ipp_client_t
*client
);
381 static void ipp_send_document(_ipp_client_t
*client
);
382 static void ipp_send_uri(_ipp_client_t
*client
);
383 static void ipp_validate_job(_ipp_client_t
*client
);
384 static int parse_options(_ipp_client_t
*client
, cups_option_t
**options
);
385 static void *process_client(_ipp_client_t
*client
);
386 static int process_http(_ipp_client_t
*client
);
387 static int process_ipp(_ipp_client_t
*client
);
388 static void *process_job(_ipp_job_t
*job
);
390 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
);
391 #endif /* HAVE_DNSSD */
392 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
393 const char *content_coding
,
394 const char *type
, size_t length
);
395 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
396 const char *message
, ...)
397 __attribute__ ((__format__ (__printf__
, 3, 4)));
398 static void respond_unsupported(_ipp_client_t
*client
,
399 ipp_attribute_t
*attr
);
400 static void run_printer(_ipp_printer_t
*printer
);
401 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
402 static void usage(int status
) __attribute__((noreturn
));
403 static int valid_doc_attributes(_ipp_client_t
*client
);
404 static int valid_job_attributes(_ipp_client_t
*client
);
411 static int KeepFiles
= 0,
416 * 'main()' - Main entry to the sample server.
419 int /* O - Exit status */
420 main(int argc
, /* I - Number of command-line args */
421 char *argv
[]) /* I - Command-line arguments */
423 int i
; /* Looping var */
424 const char *opt
, /* Current option character */
425 *command
= NULL
, /* Command to run with job files */
426 *servername
= NULL
, /* Server host name */
427 *name
= NULL
, /* Printer name */
428 *location
= "", /* Location of printer */
429 *make
= "Test", /* Manufacturer */
430 *model
= "Printer", /* Model */
431 *icon
= "printer.png", /* Icon file */
432 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
433 /* Supported formats */
435 const char *keypath
= NULL
; /* Keychain path */
436 #endif /* HAVE_SSL */
438 const char *subtype
= "_print"; /* Bonjour service subtype */
439 #endif /* HAVE_DNSSD */
440 int port
= 0, /* Port number (0 = auto) */
441 duplex
= 0, /* Duplex mode */
442 ppm
= 10, /* Pages per minute for mono */
443 ppm_color
= 0, /* Pages per minute for color */
444 pin
= 0; /* PIN printing mode? */
445 char directory
[1024] = "", /* Spool directory */
446 hostname
[1024]; /* Auto-detected hostname */
447 _ipp_printer_t
*printer
; /* Printer object */
451 * Parse command-line arguments...
454 for (i
= 1; i
< argc
; i
++)
455 if (argv
[i
][0] == '-')
457 for (opt
= argv
[i
] + 1; *opt
; opt
++)
461 case '2' : /* -2 (enable 2-sided printing) */
466 case 'K' : /* -K keypath */
472 #endif /* HAVE_SSL */
474 case 'M' : /* -M manufacturer */
481 case 'P' : /* -P (PIN printing mode) */
485 case 'c' : /* -c command */
493 case 'd' : /* -d spool-directory */
497 strlcpy(directory
, argv
[i
], sizeof(directory
));
500 case 'f' : /* -f type/subtype[,...] */
507 case 'h' : /* -h (show help) */
510 case 'i' : /* -i icon.png */
517 case 'k' : /* -k (keep files) */
521 case 'l' : /* -l location */
528 case 'm' : /* -m model */
535 case 'n' : /* -n hostname */
539 servername
= argv
[i
];
542 case 'p' : /* -p port */
544 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
546 port
= atoi(argv
[i
]);
550 case 'r' : /* -r subtype */
556 #endif /* HAVE_DNSSD */
558 case 's' : /* -s speed[,color-speed] */
562 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
566 case 'v' : /* -v (be verbose) */
570 default : /* Unknown */
571 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
582 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
590 * Apply defaults as needed...
594 servername
= httpGetHostname(NULL
, hostname
, sizeof(hostname
));
600 * Windows is almost always used as a single user system, so use a default port
608 * Use 8000 + UID mod 1000 for the default port number...
611 port
= 8000 + ((int)getuid() % 1000);
614 fprintf(stderr
, "Listening on port %d.\n", port
);
619 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
621 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
623 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
624 directory
, strerror(errno
));
629 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
633 cupsSetServerCredentials(keypath
, servername
, 1);
634 #endif /* HAVE_SSL */
637 * Create the printer...
640 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
641 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
644 #endif /* HAVE_DNSSD */
645 directory
, command
)) == NULL
)
649 * Run the print service...
652 run_printer(printer
);
655 * Destroy the printer and exit...
658 delete_printer(printer
);
665 * 'clean_jobs()' - Clean out old (completed) jobs.
669 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
671 _ipp_job_t
*job
; /* Current job */
672 time_t cleantime
; /* Clean time */
675 if (cupsArrayCount(printer
->jobs
) == 0)
678 cleantime
= time(NULL
) - 60;
680 _cupsRWLockWrite(&(printer
->rwlock
));
681 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
683 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
684 if (job
->completed
&& job
->completed
< cleantime
)
686 cupsArrayRemove(printer
->jobs
, job
);
691 _cupsRWUnlock(&(printer
->rwlock
));
696 * 'compare_jobs()' - Compare two jobs.
699 static int /* O - Result of comparison */
700 compare_jobs(_ipp_job_t
*a
, /* I - First job */
701 _ipp_job_t
*b
) /* I - Second job */
703 return (b
->id
- a
->id
);
708 * 'copy_attributes()' - Copy attributes from one request to another.
712 copy_attributes(ipp_t
*to
, /* I - Destination request */
713 ipp_t
*from
, /* I - Source request */
714 cups_array_t
*ra
, /* I - Requested attributes */
715 ipp_tag_t group_tag
, /* I - Group to copy */
716 int quickcopy
) /* I - Do a quick copy? */
718 _ipp_filter_t filter
; /* Filter data */
722 filter
.group_tag
= group_tag
;
724 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
729 * 'copy_job_attrs()' - Copy job attributes to the response.
734 _ipp_client_t
*client
, /* I - Client */
735 _ipp_job_t
*job
, /* I - Job */
736 cups_array_t
*ra
) /* I - requested-attributes */
738 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
740 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
743 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
745 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
748 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
751 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
753 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
756 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
757 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
759 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
760 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
762 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
763 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
765 if (!ra
|| cupsArrayFind(ra
, "job-state"))
766 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
767 "job-state", job
->state
);
769 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
773 case IPP_JSTATE_PENDING
:
774 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
777 case IPP_JSTATE_HELD
:
779 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
780 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
781 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
783 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
786 case IPP_JSTATE_PROCESSING
:
788 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
790 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
793 case IPP_JSTATE_STOPPED
:
794 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
797 case IPP_JSTATE_CANCELED
:
798 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
801 case IPP_JSTATE_ABORTED
:
802 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
805 case IPP_JSTATE_COMPLETED
:
806 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
811 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
815 case IPP_JSTATE_PENDING
:
816 ippAddString(client
->response
, IPP_TAG_JOB
,
817 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
821 case IPP_JSTATE_HELD
:
823 ippAddString(client
->response
, IPP_TAG_JOB
,
824 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
825 "job-state-reasons", NULL
, "job-incoming");
826 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
827 ippAddString(client
->response
, IPP_TAG_JOB
,
828 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
829 "job-state-reasons", NULL
, "job-hold-until-specified");
831 ippAddString(client
->response
, IPP_TAG_JOB
,
832 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
833 "job-state-reasons", NULL
, "job-data-insufficient");
836 case IPP_JSTATE_PROCESSING
:
838 ippAddString(client
->response
, IPP_TAG_JOB
,
839 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
840 "job-state-reasons", NULL
, "processing-to-stop-point");
842 ippAddString(client
->response
, IPP_TAG_JOB
,
843 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
844 "job-state-reasons", NULL
, "job-printing");
847 case IPP_JSTATE_STOPPED
:
848 ippAddString(client
->response
, IPP_TAG_JOB
,
849 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
850 NULL
, "job-stopped");
853 case IPP_JSTATE_CANCELED
:
854 ippAddString(client
->response
, IPP_TAG_JOB
,
855 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
856 NULL
, "job-canceled-by-user");
859 case IPP_JSTATE_ABORTED
:
860 ippAddString(client
->response
, IPP_TAG_JOB
,
861 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
862 NULL
, "aborted-by-system");
865 case IPP_JSTATE_COMPLETED
:
866 ippAddString(client
->response
, IPP_TAG_JOB
,
867 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
868 NULL
, "job-completed-successfully");
873 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
874 ippAddInteger(client
->response
, IPP_TAG_JOB
,
875 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
876 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
878 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
879 ippAddInteger(client
->response
, IPP_TAG_JOB
,
880 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
881 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
886 * 'create_client()' - Accept a new network connection and create a client
890 static _ipp_client_t
* /* O - Client */
891 create_client(_ipp_printer_t
*printer
, /* I - Printer */
892 int sock
) /* I - Listen socket */
894 _ipp_client_t
*client
; /* Client */
897 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
899 perror("Unable to allocate memory for client");
903 client
->printer
= printer
;
906 * Accept the client and get the remote address...
909 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
911 perror("Unable to accept client connection");
918 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
921 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
928 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
932 static _ipp_job_t
* /* O - Job */
933 create_job(_ipp_client_t
*client
) /* I - Client */
935 _ipp_job_t
*job
; /* Job */
936 ipp_attribute_t
*attr
; /* Job attribute */
937 char uri
[1024], /* job-uri value */
938 uuid
[64]; /* job-uuid value */
941 _cupsRWLockWrite(&(client
->printer
->rwlock
));
942 if (client
->printer
->active_job
&&
943 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
946 * Only accept a single job at a time...
949 _cupsRWLockWrite(&(client
->printer
->rwlock
));
954 * Allocate and initialize the job object...
957 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
959 perror("Unable to allocate memory for job");
963 job
->printer
= client
->printer
;
964 job
->attrs
= ippNew();
965 job
->state
= IPP_JSTATE_HELD
;
969 * Copy all of the job attributes...
972 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
975 * Get the requesting-user-name, document format, and priority...
978 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
979 job
->username
= ippGetString(attr
, 0, NULL
);
981 job
->username
= "anonymous";
983 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
985 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
987 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
988 job
->format
= ippGetString(attr
, 0, NULL
);
989 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
990 job
->format
= ippGetString(attr
, 0, NULL
);
992 job
->format
= "application/octet-stream";
995 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
996 job
->impressions
= ippGetInteger(attr
, 0);
998 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
999 job
->name
= ippGetString(attr
, 0, NULL
);
1002 * Add job description attributes and add to the jobs array...
1005 job
->id
= client
->printer
->next_job_id
++;
1007 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1008 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1010 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1011 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1012 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1013 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1014 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1015 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1017 cupsArrayAdd(client
->printer
->jobs
, job
);
1018 client
->printer
->active_job
= job
;
1020 _cupsRWUnlock(&(client
->printer
->rwlock
));
1027 * 'create_job_filename()' - Create the filename for a document in a job.
1030 static void create_job_filename(
1031 _ipp_printer_t
*printer
, /* I - Printer */
1032 _ipp_job_t
*job
, /* I - Job */
1033 char *fname
, /* I - Filename buffer */
1034 size_t fnamesize
) /* I - Size of filename buffer */
1036 char name
[256], /* "Safe" filename */
1037 *nameptr
; /* Pointer into filename */
1038 const char *ext
, /* Filename extension */
1039 *job_name
; /* job-name value */
1040 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1044 * Make a name from the job-name attribute...
1047 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1048 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1050 job_name
= "untitled";
1052 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1053 if (isalnum(*job_name
& 255) || *job_name
== '-')
1054 *nameptr
++ = (char)tolower(*job_name
& 255);
1061 * Figure out the extension...
1064 if (!strcasecmp(job
->format
, "image/jpeg"))
1066 else if (!strcasecmp(job
->format
, "image/png"))
1068 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1070 else if (!strcasecmp(job
->format
, "image/urf"))
1072 else if (!strcasecmp(job
->format
, "application/pdf"))
1074 else if (!strcasecmp(job
->format
, "application/postscript"))
1080 * Create a filename with the job-id, job-name, and document-format (extension)...
1083 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1088 * 'create_listener()' - Create a listener socket.
1091 static int /* O - Listener socket or -1 on error */
1092 create_listener(int family
, /* I - Address family */
1093 int port
) /* I - Port number */
1095 int sock
; /* Listener socket */
1096 http_addrlist_t
*addrlist
; /* Listen address */
1097 char service
[255]; /* Service port */
1100 snprintf(service
, sizeof(service
), "%d", port
);
1101 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1104 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1106 httpAddrFreeList(addrlist
);
1113 * 'create_media_col()' - Create a media-col value.
1116 static ipp_t
* /* O - media-col collection */
1117 create_media_col(const char *media
, /* I - Media name */
1118 const char *source
, /* I - Media source */
1119 const char *type
, /* I - Media type */
1120 int width
, /* I - x-dimension in 2540ths */
1121 int length
, /* I - y-dimension in 2540ths */
1122 int margins
) /* I - Value for margins */
1124 ipp_t
*media_col
= ippNew(), /* media-col value */
1125 *media_size
= create_media_size(width
, length
);
1126 /* media-size value */
1127 char media_key
[256]; /* media-key value */
1131 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1133 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1135 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1137 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1139 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1141 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1142 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1143 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1144 "media-bottom-margin", margins
);
1145 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1146 "media-left-margin", margins
);
1147 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1148 "media-right-margin", margins
);
1149 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1150 "media-top-margin", margins
);
1152 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1154 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1156 ippDelete(media_size
);
1163 * 'create_media_size()' - Create a media-size value.
1166 static ipp_t
* /* O - media-col collection */
1167 create_media_size(int width
, /* I - x-dimension in 2540ths */
1168 int length
) /* I - y-dimension in 2540ths */
1170 ipp_t
*media_size
= ippNew(); /* media-size value */
1173 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1175 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1178 return (media_size
);
1183 * 'create_printer()' - Create, register, and listen for connections to a
1187 static _ipp_printer_t
* /* O - Printer */
1188 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1189 const char *name
, /* I - printer-name */
1190 const char *location
, /* I - printer-location */
1191 const char *make
, /* I - printer-make-and-model */
1192 const char *model
, /* I - printer-make-and-model */
1193 const char *icon
, /* I - printer-icons */
1194 const char *docformats
, /* I - document-format-supported */
1195 int ppm
, /* I - Pages per minute in grayscale */
1196 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1197 int duplex
, /* I - 1 = duplex, 0 = simplex */
1198 int port
, /* I - Port for listeners or 0 for auto */
1199 int pin
, /* I - Require PIN printing */
1201 const char *subtype
, /* I - Bonjour service subtype */
1202 #endif /* HAVE_DNSSD */
1203 const char *directory
, /* I - Spool directory */
1204 const char *command
) /* I - Command to run on job files */
1206 int i
, j
; /* Looping vars */
1207 _ipp_printer_t
*printer
; /* Printer */
1209 char path
[1024]; /* Full path to command */
1211 char uri
[1024], /* Printer URI */
1212 icons
[1024], /* printer-icons URI */
1213 adminurl
[1024], /* printer-more-info URI */
1214 supplyurl
[1024],/* printer-supply-info-uri URI */
1215 device_id
[1024],/* printer-device-id */
1216 make_model
[128],/* printer-make-and-model */
1217 uuid
[128]; /* printer-uuid */
1218 int num_formats
; /* Number of document-format-supported values */
1219 char *defformat
, /* document-format-default value */
1220 *formats
[100], /* document-format-supported values */
1221 *ptr
; /* Pointer into string */
1222 const char *prefix
; /* Prefix string */
1223 int num_database
; /* Number of database values */
1224 ipp_attribute_t
*media_col_database
,
1225 /* media-col-database value */
1226 *media_size_supported
;
1227 /* media-size-supported value */
1228 ipp_t
*media_col_default
;
1229 /* media-col-default value */
1230 int media_col_index
;/* Current media-col-database value */
1231 int k_supported
; /* Maximum file size supported */
1233 struct statvfs spoolinfo
; /* FS info for spool directory */
1234 double spoolsize
; /* FS size */
1235 #elif defined(HAVE_STATFS)
1236 struct statfs spoolinfo
; /* FS info for spool directory */
1237 double spoolsize
; /* FS size */
1238 #endif /* HAVE_STATVFS */
1239 static const int orients
[4] = /* orientation-requested-supported values */
1241 IPP_ORIENT_PORTRAIT
,
1242 IPP_ORIENT_LANDSCAPE
,
1243 IPP_ORIENT_REVERSE_LANDSCAPE
,
1244 IPP_ORIENT_REVERSE_PORTRAIT
1246 static const char * const versions
[] =/* ipp-versions-supported values */
1252 static const char * const features
[] =/* ipp-features-supported values */
1256 static const int ops
[] = /* operations-supported values */
1260 IPP_OP_VALIDATE_JOB
,
1262 IPP_OP_SEND_DOCUMENT
,
1265 IPP_OP_GET_JOB_ATTRIBUTES
,
1267 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1268 IPP_OP_CANCEL_MY_JOBS
,
1270 IPP_OP_IDENTIFY_PRINTER
1272 static const char * const charsets
[] =/* charset-supported values */
1277 static const char * const compressions
[] =/* compression-supported values */
1282 #endif /* HAVE_LIBZ */
1285 static const char * const identify_actions
[] =
1290 static const char * const job_creation
[] =
1291 { /* job-creation-attributes-supported values */
1293 "ipp-attribute-fidelity",
1295 "job-accounting-user-id",
1301 "multiple-document-handling",
1302 "orientation-requested",
1306 static const char * const media_col_supported
[] =
1307 { /* media-col-supported values */
1308 "media-bottom-margin",
1309 "media-left-margin",
1310 "media-right-margin",
1316 static const int media_xxx_margin_supported
[] =
1317 { /* media-xxx-margin-supported values */
1321 static const char * const multiple_document_handling
[] =
1322 { /* multiple-document-handling-supported values */
1323 "separate-documents-uncollated-copies",
1324 "separate-documents-collated-copies"
1326 static const char * const overrides
[] =
1327 { /* overrides-supported */
1331 static const char * const print_color_mode_supported
[] =
1332 { /* print-color-mode-supported values */
1337 static const int print_quality_supported
[] =
1338 { /* print-quality-supported values */
1343 static const int pwg_raster_document_resolution_supported
[] =
1349 static const char * const pwg_raster_document_type_supported
[] =
1357 static const char * const reference_uri_schemes_supported
[] =
1358 { /* reference-uri-schemes-supported */
1364 #endif /* HAVE_SSL */
1366 static const char * const sides_supported
[] =
1367 { /* sides-supported values */
1369 "two-sided-long-edge",
1370 "two-sided-short-edge"
1372 static const char * const urf_supported
[] =
1373 { /* urf-supported values */
1376 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1383 static const char * const which_jobs
[] =
1384 { /* which-jobs-supported values */
1393 "processing-stopped"
1399 * If a command was specified, make sure it exists and is executable...
1404 if (*command
== '/' || !strncmp(command
, "./", 2))
1406 if (access(command
, X_OK
))
1408 fprintf(stderr
, "ippserver: Unable to execute command \"%s\": %s\n", command
, strerror(errno
));
1414 if (!cupsFileFind(command
, getenv("PATH"), 1, path
, sizeof(path
)))
1416 fprintf(stderr
, "ippserver: Unable to find command \"%s\".\n", command
);
1426 * Allocate memory for the printer...
1429 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1431 perror("ippserver: Unable to allocate memory for printer");
1437 printer
->name
= strdup(name
);
1439 printer
->dnssd_name
= strdup(printer
->name
);
1440 #endif /* HAVE_DNSSD */
1441 printer
->command
= command
? strdup(command
) : NULL
;
1442 printer
->directory
= strdup(directory
);
1443 printer
->hostname
= strdup(servername
);
1444 printer
->port
= port
;
1445 printer
->start_time
= time(NULL
);
1446 printer
->config_time
= printer
->start_time
;
1447 printer
->state
= IPP_PSTATE_IDLE
;
1448 printer
->state_reasons
= _IPP_PREASON_NONE
;
1449 printer
->state_time
= printer
->start_time
;
1450 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1451 printer
->next_job_id
= 1;
1453 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1454 printer
->hostname
, printer
->port
, "/ipp/print");
1455 printer
->uri
= strdup(uri
);
1456 printer
->urilen
= strlen(uri
);
1459 printer
->icon
= strdup(icon
);
1461 printer
->main_size
= _IPP_MEDIA_SIZE_A4
;
1462 printer
->main_type
= _IPP_MEDIA_TYPE_STATIONERY
;
1463 printer
->main_level
= 500;
1465 printer
->envelope_size
= _IPP_MEDIA_SIZE_NONE
;
1466 printer
->envelope_level
= 0;
1468 printer
->photo_size
= _IPP_MEDIA_SIZE_NONE
;
1469 printer
->photo_type
= _IPP_MEDIA_TYPE_NONE
;
1470 printer
->photo_level
= 0;
1472 printer
->supplies
[_IPP_SUPPLY_CYAN
] = 100;
1473 printer
->supplies
[_IPP_SUPPLY_MAGENTA
] = 100;
1474 printer
->supplies
[_IPP_SUPPLY_YELLOW
] = 100;
1475 printer
->supplies
[_IPP_SUPPLY_BLACK
] = 100;
1476 printer
->supplies
[_IPP_SUPPLY_WASTE
] = 0;
1478 _cupsRWInit(&(printer
->rwlock
));
1481 * Create the listener sockets...
1484 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1486 perror("Unable to create IPv4 listener");
1490 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1492 perror("Unable to create IPv6 listener");
1497 * Prepare values for the printer attributes...
1500 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1501 printer
->hostname
, printer
->port
, "/icon.png");
1502 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/");
1503 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/supplies");
1507 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1508 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1509 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1512 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1515 formats
[0] = strdup(docformats
);
1516 defformat
= formats
[0];
1517 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1520 formats
[num_formats
++] = ptr
;
1522 if (!strcasecmp(ptr
, "application/octet-stream"))
1526 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1527 ptr
= device_id
+ strlen(device_id
);
1529 for (i
= 0; i
< num_formats
; i
++)
1531 if (!strcasecmp(formats
[i
], "application/pdf"))
1532 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1533 else if (!strcasecmp(formats
[i
], "application/postscript"))
1534 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1535 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1536 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1537 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1538 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1539 else if (!strcasecmp(formats
[i
], "image/png"))
1540 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1541 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1542 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1547 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
1554 * Get the maximum spool size based on the size of the filesystem used for
1555 * the spool directory. If the host OS doesn't support the statfs call
1556 * or the filesystem is larger than 2TiB, always report INT_MAX.
1560 if (statvfs(printer
->directory
, &spoolinfo
))
1561 k_supported
= INT_MAX
;
1562 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1563 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1564 k_supported
= INT_MAX
;
1566 k_supported
= (int)spoolsize
;
1568 #elif defined(HAVE_STATFS)
1569 if (statfs(printer
->directory
, &spoolinfo
))
1570 k_supported
= INT_MAX
;
1571 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1572 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1573 k_supported
= INT_MAX
;
1575 k_supported
= (int)spoolsize
;
1578 k_supported
= INT_MAX
;
1579 #endif /* HAVE_STATVFS */
1582 * Create the printer attributes. This list of attributes is sorted to improve
1583 * performance when the client provides a requested-attributes attribute...
1586 printer
->attrs
= ippNew();
1588 /* charset-configured */
1589 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1590 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1591 "charset-configured", NULL
, "utf-8");
1593 /* charset-supported */
1594 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1595 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1596 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1599 /* color-supported */
1600 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1603 /* compression-supported */
1604 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1605 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1606 "compression-supported",
1607 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1610 /* copies-default */
1611 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1612 "copies-default", 1);
1614 /* copies-supported */
1615 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1617 /* document-format-default */
1618 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1619 "document-format-default", NULL
, defformat
);
1621 /* document-format-supported */
1622 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1623 "document-format-supported", num_formats
, NULL
,
1624 (const char * const *)formats
);
1626 /* document-password-supported */
1627 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1629 /* finishings-default */
1630 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1631 "finishings-default", IPP_FINISHINGS_NONE
);
1633 /* finishings-supported */
1634 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1635 "finishings-supported", IPP_FINISHINGS_NONE
);
1637 /* generated-natural-language-supported */
1638 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1639 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1640 "generated-natural-language-supported", NULL
, "en");
1642 /* identify-actions-default */
1643 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1645 /* identify-actions-supported */
1646 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
);
1648 /* ipp-features-supported */
1649 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1651 /* ipp-versions-supported */
1652 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1654 /* job-account-id-default */
1655 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1657 /* job-account-id-supported */
1658 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1660 /* job-accounting-user-id-default */
1661 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1663 /* job-accounting-user-id-supported */
1664 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1666 /* job-creation-attributes-supported */
1667 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
);
1669 /* job-ids-supported */
1670 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1672 /* job-k-octets-supported */
1673 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1676 /* job-password-supported */
1677 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1678 "job-password-supported", 4);
1680 /* job-preferred-attributes-supported */
1681 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-preferred-attributes-supported", 0);
1683 /* job-priority-default */
1684 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1685 "job-priority-default", 50);
1687 /* job-priority-supported */
1688 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1689 "job-priority-supported", 100);
1691 /* job-sheets-default */
1692 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1693 IPP_CONST_TAG(IPP_TAG_NAME
),
1694 "job-sheets-default", NULL
, "none");
1696 /* job-sheets-supported */
1697 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1698 IPP_CONST_TAG(IPP_TAG_NAME
),
1699 "job-sheets-supported", NULL
, "none");
1701 /* media-bottom-margin-supported */
1702 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1703 "media-bottom-margin-supported",
1704 (int)(sizeof(media_xxx_margin_supported
) /
1705 sizeof(media_xxx_margin_supported
[0])),
1706 media_xxx_margin_supported
);
1708 /* media-col-database */
1709 for (num_database
= 0, i
= 0;
1710 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1713 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1714 num_database
+= 3; /* auto + manual + envelope */
1715 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1716 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1718 num_database
+= 2; /* Regular + borderless */
1721 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1722 "media-col-database", num_database
,
1724 for (media_col_index
= 0, i
= 0;
1725 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1728 switch (media_col_sizes
[i
][2])
1732 * Regular + borderless for the general class; no source/type
1736 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]));
1737 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]));
1740 case _IPP_ENV_ONLY
:
1742 * Regular margins for "auto", "manual", and "envelope" sources.
1745 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]));
1746 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]));
1747 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]));
1749 case _IPP_PHOTO_ONLY
:
1751 * Photos have specific media types and can only be printed via
1752 * the auto, manual, and photo sources...
1756 j
< (int)(sizeof(media_type_supported
) /
1757 sizeof(media_type_supported
[0]));
1760 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1763 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]));
1764 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]));
1765 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]));
1771 /* media-col-default */
1772 media_col_default
= create_media_col(media_supported
[0],
1773 media_source_supported
[0],
1774 media_type_supported
[0],
1775 media_col_sizes
[0][0],
1776 media_col_sizes
[0][1],
1777 media_xxx_margin_supported
[1]);
1779 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1781 ippDelete(media_col_default
);
1783 /* media-col-supported */
1784 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1785 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1786 "media-col-supported",
1787 (int)(sizeof(media_col_supported
) /
1788 sizeof(media_col_supported
[0])), NULL
,
1789 media_col_supported
);
1792 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1793 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1794 "media-default", NULL
, media_supported
[0]);
1796 /* media-left-margin-supported */
1797 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1798 "media-left-margin-supported",
1799 (int)(sizeof(media_xxx_margin_supported
) /
1800 sizeof(media_xxx_margin_supported
[0])),
1801 media_xxx_margin_supported
);
1803 /* media-right-margin-supported */
1804 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1805 "media-right-margin-supported",
1806 (int)(sizeof(media_xxx_margin_supported
) /
1807 sizeof(media_xxx_margin_supported
[0])),
1808 media_xxx_margin_supported
);
1810 /* media-supported */
1811 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1812 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1814 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1815 NULL
, media_supported
);
1817 /* media-size-supported */
1818 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1819 "media-size-supported",
1820 (int)(sizeof(media_col_sizes
) /
1821 sizeof(media_col_sizes
[0])),
1824 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1826 ippSetCollection(printer
->attrs
, &media_size_supported
, i
,
1827 create_media_size(media_col_sizes
[i
][0],
1828 media_col_sizes
[i
][1]));
1830 /* media-source-supported */
1831 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
);
1833 /* media-top-margin-supported */
1834 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1835 "media-top-margin-supported",
1836 (int)(sizeof(media_xxx_margin_supported
) /
1837 sizeof(media_xxx_margin_supported
[0])),
1838 media_xxx_margin_supported
);
1840 /* media-type-supported */
1841 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
);
1843 /* multiple-document-handling-supported */
1844 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
);
1846 /* multiple-document-jobs-supported */
1847 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1849 /* multiple-operation-time-out */
1850 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1852 /* multiple-operation-time-out-action */
1853 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1855 /* natural-language-configured */
1856 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1857 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1858 "natural-language-configured", NULL
, "en");
1860 /* number-up-default */
1861 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1862 "number-up-default", 1);
1864 /* number-up-supported */
1865 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1866 "number-up-supported", 1);
1868 /* operations-supported */
1869 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1870 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1872 /* orientation-requested-default */
1873 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1874 "orientation-requested-default", 0);
1876 /* orientation-requested-supported */
1877 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1878 "orientation-requested-supported", 4, orients
);
1880 /* output-bin-default */
1881 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1882 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1883 "output-bin-default", NULL
, "face-down");
1885 /* output-bin-supported */
1886 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1887 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1888 "output-bin-supported", NULL
, "face-down");
1890 /* overrides-supported */
1891 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1893 /* page-ranges-supported */
1894 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1896 /* pages-per-minute */
1897 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1898 "pages-per-minute", ppm
);
1900 /* pages-per-minute-color */
1902 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1903 "pages-per-minute-color", ppm_color
);
1905 /* pdl-override-supported */
1906 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1907 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1908 "pdl-override-supported", NULL
, "attempted");
1910 /* print-color-mode-default */
1911 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1913 /* print-color-mode-supported */
1914 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
);
1916 /* print-content-optimize-default */
1917 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1919 /* print-content-optimize-supported */
1920 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
1922 /* print-rendering-intent-default */
1923 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
1925 /* print-rendering-intent-supported */
1926 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
1928 /* print-quality-default */
1929 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
1931 /* print-quality-supported */
1932 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
);
1934 /* printer-device-id */
1935 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1936 "printer-device-id", NULL
, device_id
);
1938 /* printer-get-attributes-supported */
1939 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1941 /* printer-geo-location */
1942 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
1945 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1946 "printer-icons", NULL
, icons
);
1948 /* printer-is-accepting-jobs */
1949 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1953 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1956 /* printer-location */
1957 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1958 "printer-location", NULL
, location
);
1960 /* printer-make-and-model */
1961 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1962 "printer-make-and-model", NULL
, make_model
);
1964 /* printer-mandatory-job-attributes */
1967 static const char * const names
[] =
1969 "job-accounting-user-id",
1973 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1974 "printer-mandatory-job-attributes",
1975 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
1978 /* printer-more-info */
1979 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1982 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1985 /* printer-organization */
1986 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
1988 /* printer-organizational-unit */
1989 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
1991 /* printer-resolution-default */
1992 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1993 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1995 /* printer-resolution-supported */
1996 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1997 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1999 /* printer-supply-description */
2000 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
);
2002 /* printer-supply-info-uri */
2003 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
2005 /* printer-uri-supported */
2006 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
2009 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
2010 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
2012 /* pwg-raster-document-xxx-supported */
2013 for (i
= 0; i
< num_formats
; i
++)
2014 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
2017 if (i
< num_formats
)
2019 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
,
2020 "pwg-raster-document-resolution-supported",
2021 (int)(sizeof(pwg_raster_document_resolution_supported
) /
2022 sizeof(pwg_raster_document_resolution_supported
[0])),
2024 pwg_raster_document_resolution_supported
,
2025 pwg_raster_document_resolution_supported
);
2026 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2027 "pwg-raster-document-sheet-back", NULL
, "normal");
2028 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2029 "pwg-raster-document-type-supported",
2030 (int)(sizeof(pwg_raster_document_type_supported
) /
2031 sizeof(pwg_raster_document_type_supported
[0])), NULL
,
2032 pwg_raster_document_type_supported
);
2035 /* reference-uri-scheme-supported */
2036 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2037 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
2038 "reference-uri-schemes-supported",
2039 (int)(sizeof(reference_uri_schemes_supported
) /
2040 sizeof(reference_uri_schemes_supported
[0])),
2041 NULL
, reference_uri_schemes_supported
);
2044 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2045 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2046 "sides-default", NULL
, "one-sided");
2048 /* sides-supported */
2049 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2050 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2051 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
2054 for (i
= 0; i
< num_formats
; i
++)
2055 if (!strcasecmp(formats
[i
], "image/urf"))
2058 if (i
< num_formats
)
2059 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2061 /* uri-authentication-supported */
2062 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2063 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2064 "uri-authentication-supported", NULL
, "none");
2066 /* uri-security-supported */
2067 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2068 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2069 "uri-security-supported", NULL
, "none");
2071 /* which-jobs-supported */
2072 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2073 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2074 "which-jobs-supported",
2075 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
2079 debug_attributes("Printer", printer
->attrs
, 0);
2083 * Register the printer with Bonjour...
2086 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2088 #endif /* HAVE_DNSSD */
2098 * If we get here we were unable to create the printer...
2103 delete_printer(printer
);
2109 * 'debug_attributes()' - Print attributes in a request or response.
2113 debug_attributes(const char *title
, /* I - Title */
2114 ipp_t
*ipp
, /* I - Request/response */
2115 int type
) /* I - 0 = object, 1 = request, 2 = response */
2117 ipp_tag_t group_tag
; /* Current group */
2118 ipp_attribute_t
*attr
; /* Current attribute */
2119 char buffer
[2048]; /* String buffer for value */
2120 int major
, minor
; /* Version */
2126 fprintf(stderr
, "%s:\n", title
);
2127 major
= ippGetVersion(ipp
, &minor
);
2128 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2130 fprintf(stderr
, " operation-id=%s(%04x)\n",
2131 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2133 fprintf(stderr
, " status-code=%s(%04x)\n",
2134 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2135 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2137 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2139 attr
= ippNextAttribute(ipp
))
2141 if (ippGetGroupTag(attr
) != group_tag
)
2143 group_tag
= ippGetGroupTag(attr
);
2144 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2147 if (ippGetName(attr
))
2149 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2150 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2151 ippGetCount(attr
) > 1 ? "1setOf " : "",
2152 ippTagString(ippGetValueTag(attr
)), buffer
);
2159 * 'delete_client()' - Close the socket and free all memory used by a client
2164 delete_client(_ipp_client_t
*client
) /* I - Client */
2167 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2170 * Flush pending writes before closing...
2173 httpFlushWrite(client
->http
);
2179 httpClose(client
->http
);
2181 ippDelete(client
->request
);
2182 ippDelete(client
->response
);
2189 * 'delete_job()' - Remove from the printer and free all memory used by a job
2194 delete_job(_ipp_job_t
*job
) /* I - Job */
2197 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2199 ippDelete(job
->attrs
);
2204 unlink(job
->filename
);
2206 free(job
->filename
);
2214 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2215 * used by a printer object.
2219 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2221 if (printer
->ipv4
>= 0)
2222 close(printer
->ipv4
);
2224 if (printer
->ipv6
>= 0)
2225 close(printer
->ipv6
);
2228 if (printer
->printer_ref
)
2229 DNSServiceRefDeallocate(printer
->printer_ref
);
2231 if (printer
->ipp_ref
)
2232 DNSServiceRefDeallocate(printer
->ipp_ref
);
2235 if (printer
->ipps_ref
)
2236 DNSServiceRefDeallocate(printer
->ipps_ref
);
2237 # endif /* HAVE_SSL */
2238 if (printer
->http_ref
)
2239 DNSServiceRefDeallocate(printer
->http_ref
);
2241 if (printer
->common_ref
)
2242 DNSServiceRefDeallocate(printer
->common_ref
);
2244 TXTRecordDeallocate(&(printer
->ipp_txt
));
2246 if (printer
->dnssd_name
)
2247 free(printer
->dnssd_name
);
2248 #endif /* HAVE_DNSSD */
2251 free(printer
->name
);
2253 free(printer
->icon
);
2254 if (printer
->command
)
2255 free(printer
->command
);
2256 if (printer
->directory
)
2257 free(printer
->directory
);
2258 if (printer
->hostname
)
2259 free(printer
->hostname
);
2263 ippDelete(printer
->attrs
);
2264 cupsArrayDelete(printer
->jobs
);
2272 * 'dnssd_callback()' - Handle Bonjour registration events.
2277 DNSServiceRef sdRef
, /* I - Service reference */
2278 DNSServiceFlags flags
, /* I - Status flags */
2279 DNSServiceErrorType errorCode
, /* I - Error, if any */
2280 const char *name
, /* I - Service name */
2281 const char *regtype
, /* I - Service type */
2282 const char *domain
, /* I - Domain for service */
2283 _ipp_printer_t
*printer
) /* I - Printer */
2291 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2292 regtype
, (int)errorCode
);
2295 else if (strcasecmp(name
, printer
->dnssd_name
))
2298 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2300 /* No lock needed since only the main thread accesses/changes this */
2301 free(printer
->dnssd_name
);
2302 printer
->dnssd_name
= strdup(name
);
2305 #endif /* HAVE_DNSSD */
2309 * 'filter_cb()' - Filter printer attributes based on the requested array.
2312 static int /* O - 1 to copy, 0 to ignore */
2313 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2314 ipp_t
*dst
, /* I - Destination (unused) */
2315 ipp_attribute_t
*attr
) /* I - Source attribute */
2318 * Filter attributes as needed...
2323 ipp_tag_t group
= ippGetGroupTag(attr
);
2324 const char *name
= ippGetName(attr
);
2326 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
)))
2329 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2334 * 'find_job()' - Find a job specified in a request.
2337 static _ipp_job_t
* /* O - Job or NULL */
2338 find_job(_ipp_client_t
*client
) /* I - Client */
2340 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2341 _ipp_job_t key
, /* Job search key */
2342 *job
; /* Matching job, if any */
2345 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2347 const char *uri
= ippGetString(attr
, 0, NULL
);
2349 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2350 uri
[client
->printer
->urilen
] == '/')
2351 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2355 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2356 key
.id
= ippGetInteger(attr
, 0);
2358 _cupsRWLockRead(&(client
->printer
->rwlock
));
2359 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2360 _cupsRWUnlock(&(client
->printer
->rwlock
));
2367 * 'html_escape()' - Write a HTML-safe string.
2371 html_escape(_ipp_client_t
*client
, /* I - Client */
2372 const char *s
, /* I - String to write */
2373 size_t slen
) /* I - Number of characters to write */
2375 const char *start
, /* Start of segment */
2376 *end
; /* End of string */
2380 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2382 while (*s
&& s
< end
)
2384 if (*s
== '&' || *s
== '<')
2387 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2390 httpWrite2(client
->http
, "&", 5);
2392 httpWrite2(client
->http
, "<", 4);
2401 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2406 * 'html_footer()' - Show the web interface footer.
2408 * This function also writes the trailing 0-length chunk.
2412 html_footer(_ipp_client_t
*client
) /* I - Client */
2418 httpWrite2(client
->http
, "", 0);
2423 * 'html_header()' - Show the web interface header and title.
2427 html_header(_ipp_client_t
*client
, /* I - Client */
2428 const char *title
) /* I - Title */
2434 "<title>%s</title>\n"
2435 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2436 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2437 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2438 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2440 "body { font-family: sans-serif; margin: 0; }\n"
2441 "div.body { padding: 0px 10px 10px; }\n"
2442 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2443 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2444 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2445 "table.form th { text-align: right; }\n"
2446 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2447 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2448 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2449 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2450 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2451 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2452 "table.nav td { margin: 0; text-align: center; }\n"
2453 "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"
2454 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2455 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2456 "td.nav:hover { background: #666; color: #fff; }\n"
2457 "td.nav:active { background: #000; color: #ff0; }\n"
2461 "<table class=\"nav\"><tr>"
2462 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2463 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2464 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2466 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2471 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2475 html_printf(_ipp_client_t
*client
, /* I - Client */
2476 const char *format
, /* I - Printf-style format string */
2477 ...) /* I - Additional arguments as needed */
2479 va_list ap
; /* Pointer to arguments */
2480 const char *start
; /* Start of string */
2481 char size
, /* Size character (h, l, L) */
2482 type
; /* Format type character */
2483 int width
, /* Width of field */
2484 prec
; /* Number of characters of precision */
2485 char tformat
[100], /* Temporary format string for sprintf() */
2486 *tptr
, /* Pointer into temporary format */
2487 temp
[1024]; /* Buffer for formatted numbers */
2488 char *s
; /* Pointer to string */
2492 * Loop through the format string, formatting as needed...
2495 va_start(ap
, format
);
2503 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2506 *tptr
++ = *format
++;
2510 httpWrite2(client
->http
, "%", 1);
2515 else if (strchr(" -+#\'", *format
))
2516 *tptr
++ = *format
++;
2521 * Get width from argument...
2525 width
= va_arg(ap
, int);
2527 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2528 tptr
+= strlen(tptr
);
2534 while (isdigit(*format
& 255))
2536 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2539 width
= width
* 10 + *format
++ - '0';
2545 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2553 * Get precision from argument...
2557 prec
= va_arg(ap
, int);
2559 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2560 tptr
+= strlen(tptr
);
2566 while (isdigit(*format
& 255))
2568 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2571 prec
= prec
* 10 + *format
++ - '0';
2576 if (*format
== 'l' && format
[1] == 'l')
2580 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2588 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2590 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2605 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2614 case 'E' : /* Floating point formats */
2619 if ((size_t)(width
+ 2) > sizeof(temp
))
2622 sprintf(temp
, tformat
, va_arg(ap
, double));
2624 httpWrite2(client
->http
, temp
, strlen(temp
));
2627 case 'B' : /* Integer formats */
2635 if ((size_t)(width
+ 2) > sizeof(temp
))
2638 # ifdef HAVE_LONG_LONG
2640 sprintf(temp
, tformat
, va_arg(ap
, long long));
2642 # endif /* HAVE_LONG_LONG */
2644 sprintf(temp
, tformat
, va_arg(ap
, long));
2646 sprintf(temp
, tformat
, va_arg(ap
, int));
2648 httpWrite2(client
->http
, temp
, strlen(temp
));
2651 case 'p' : /* Pointer value */
2652 if ((size_t)(width
+ 2) > sizeof(temp
))
2655 sprintf(temp
, tformat
, va_arg(ap
, void *));
2657 httpWrite2(client
->http
, temp
, strlen(temp
));
2660 case 'c' : /* Character or character array */
2663 temp
[0] = (char)va_arg(ap
, int);
2665 html_escape(client
, temp
, 1);
2668 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2671 case 's' : /* String */
2672 if ((s
= va_arg(ap
, char *)) == NULL
)
2675 html_escape(client
, s
, strlen(s
));
2684 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2691 * 'ipp_cancel_job()' - Cancel a job.
2695 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2697 _ipp_job_t
*job
; /* Job information */
2704 if ((job
= find_job(client
)) == NULL
)
2706 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2711 * See if the job is already completed, canceled, or aborted; if so,
2712 * we can't cancel...
2717 case IPP_JSTATE_CANCELED
:
2718 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2719 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2722 case IPP_JSTATE_ABORTED
:
2723 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2724 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2727 case IPP_JSTATE_COMPLETED
:
2728 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2729 "Job #%d is already completed - can\'t cancel.", job
->id
);
2737 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2739 if (job
->state
== IPP_JSTATE_PROCESSING
||
2740 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2744 job
->state
= IPP_JSTATE_CANCELED
;
2745 job
->completed
= time(NULL
);
2748 _cupsRWUnlock(&(client
->printer
->rwlock
));
2750 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2757 * 'ipp_close_job()' - Close an open job.
2761 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
2763 _ipp_job_t
*job
; /* Job information */
2770 if ((job
= find_job(client
)) == NULL
)
2772 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2777 * See if the job is already completed, canceled, or aborted; if so,
2778 * we can't cancel...
2783 case IPP_JSTATE_CANCELED
:
2784 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2785 "Job #%d is canceled - can\'t close.", job
->id
);
2788 case IPP_JSTATE_ABORTED
:
2789 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2790 "Job #%d is aborted - can\'t close.", job
->id
);
2793 case IPP_JSTATE_COMPLETED
:
2794 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2795 "Job #%d is completed - can\'t close.", job
->id
);
2798 case IPP_JSTATE_PROCESSING
:
2799 case IPP_JSTATE_STOPPED
:
2800 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2801 "Job #%d is already closed.", job
->id
);
2805 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2812 * 'ipp_create_job()' - Create a job object.
2816 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2818 _ipp_job_t
*job
; /* New job */
2819 cups_array_t
*ra
; /* Attributes to send in response */
2823 * Validate print job attributes...
2826 if (!valid_job_attributes(client
))
2828 httpFlush(client
->http
);
2833 * Do we have a file to print?
2836 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2838 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2839 "Unexpected document data following request.");
2847 if ((job
= create_job(client
)) == NULL
)
2849 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2850 "Currently printing another job.");
2855 * Return the job info...
2858 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2860 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2861 cupsArrayAdd(ra
, "job-id");
2862 cupsArrayAdd(ra
, "job-state");
2863 cupsArrayAdd(ra
, "job-state-message");
2864 cupsArrayAdd(ra
, "job-state-reasons");
2865 cupsArrayAdd(ra
, "job-uri");
2867 copy_job_attributes(client
, job
, ra
);
2868 cupsArrayDelete(ra
);
2873 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2877 ipp_get_job_attributes(
2878 _ipp_client_t
*client
) /* I - Client */
2880 _ipp_job_t
*job
; /* Job */
2881 cups_array_t
*ra
; /* requested-attributes */
2884 if ((job
= find_job(client
)) == NULL
)
2886 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2890 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2892 ra
= ippCreateRequestedArray(client
->request
);
2893 copy_job_attributes(client
, job
, ra
);
2894 cupsArrayDelete(ra
);
2899 * 'ipp_get_jobs()' - Get a list of job objects.
2903 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2905 ipp_attribute_t
*attr
; /* Current attribute */
2906 const char *which_jobs
= NULL
;
2907 /* which-jobs values */
2908 int job_comparison
; /* Job comparison */
2909 ipp_jstate_t job_state
; /* job-state value */
2910 int first_job_id
, /* First job ID */
2911 limit
, /* Maximum number of jobs to return */
2912 count
; /* Number of jobs that match */
2913 const char *username
; /* Username */
2914 _ipp_job_t
*job
; /* Current job pointer */
2915 cups_array_t
*ra
; /* Requested attributes array */
2919 * See if the "which-jobs" attribute have been specified...
2922 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2923 IPP_TAG_KEYWORD
)) != NULL
)
2925 which_jobs
= ippGetString(attr
, 0, NULL
);
2926 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
2929 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
2931 job_comparison
= -1;
2932 job_state
= IPP_JSTATE_STOPPED
;
2934 else if (!strcmp(which_jobs
, "completed"))
2937 job_state
= IPP_JSTATE_CANCELED
;
2939 else if (!strcmp(which_jobs
, "aborted"))
2942 job_state
= IPP_JSTATE_ABORTED
;
2944 else if (!strcmp(which_jobs
, "all"))
2947 job_state
= IPP_JSTATE_PENDING
;
2949 else if (!strcmp(which_jobs
, "canceled"))
2952 job_state
= IPP_JSTATE_CANCELED
;
2954 else if (!strcmp(which_jobs
, "pending"))
2957 job_state
= IPP_JSTATE_PENDING
;
2959 else if (!strcmp(which_jobs
, "pending-held"))
2962 job_state
= IPP_JSTATE_HELD
;
2964 else if (!strcmp(which_jobs
, "processing"))
2967 job_state
= IPP_JSTATE_PROCESSING
;
2969 else if (!strcmp(which_jobs
, "processing-stopped"))
2972 job_state
= IPP_JSTATE_STOPPED
;
2976 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
2977 "The which-jobs value \"%s\" is not supported.", which_jobs
);
2978 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2979 "which-jobs", NULL
, which_jobs
);
2984 * See if they want to limit the number of jobs reported...
2987 if ((attr
= ippFindAttribute(client
->request
, "limit",
2988 IPP_TAG_INTEGER
)) != NULL
)
2990 limit
= ippGetInteger(attr
, 0);
2992 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
2997 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2998 IPP_TAG_INTEGER
)) != NULL
)
3000 first_job_id
= ippGetInteger(attr
, 0);
3002 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3009 * See if we only want to see jobs for a specific user...
3014 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3015 IPP_TAG_BOOLEAN
)) != NULL
)
3017 int my_jobs
= ippGetBoolean(attr
, 0);
3019 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3020 my_jobs
? "true" : "false");
3024 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3025 IPP_TAG_NAME
)) == NULL
)
3027 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3028 "Need requesting-user-name with my-jobs.");
3032 username
= ippGetString(attr
, 0, NULL
);
3034 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3035 client
->hostname
, username
);
3040 * OK, build a list of jobs for this printer...
3043 ra
= ippCreateRequestedArray(client
->request
);
3045 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3047 _cupsRWLockRead(&(client
->printer
->rwlock
));
3049 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3050 (limit
<= 0 || count
< limit
) && job
;
3051 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3054 * Filter out jobs that don't match...
3057 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3058 (job_comparison
== 0 && job
->state
!= job_state
) ||
3059 (job_comparison
> 0 && job
->state
< job_state
) ||
3060 job
->id
< first_job_id
||
3061 (username
&& job
->username
&&
3062 strcasecmp(username
, job
->username
)))
3066 ippAddSeparator(client
->response
);
3069 copy_job_attributes(client
, job
, ra
);
3072 cupsArrayDelete(ra
);
3074 _cupsRWUnlock(&(client
->printer
->rwlock
));
3079 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3083 ipp_get_printer_attributes(
3084 _ipp_client_t
*client
) /* I - Client */
3086 cups_array_t
*ra
; /* Requested attributes array */
3087 _ipp_printer_t
*printer
; /* Printer */
3091 * Send the attributes...
3094 ra
= ippCreateRequestedArray(client
->request
);
3095 printer
= client
->printer
;
3097 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3099 _cupsRWLockRead(&(printer
->rwlock
));
3101 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3102 IPP_TAG_CUPS_CONST
);
3104 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3106 int i
, /* Looping var */
3107 num_ready
= 0; /* Number of ready media */
3108 ipp_t
*ready
[3]; /* Ready media */
3110 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3112 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
3113 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);
3115 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);
3117 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3118 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);
3119 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3121 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
3122 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);
3124 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);
3129 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3130 for (i
= 0; i
< num_ready
; i
++)
3131 ippDelete(ready
[i
]);
3134 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3137 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3139 int num_ready
= 0; /* Number of ready media */
3140 const char *ready
[3]; /* Ready media */
3142 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3143 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3145 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3146 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3148 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3149 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3152 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3154 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3157 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3158 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3160 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3161 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3163 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3164 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3167 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3168 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3169 "printer-state", printer
->state
);
3171 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3172 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3174 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3175 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3177 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3179 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3181 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3184 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3186 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3187 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3188 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3189 "printer-state-reasons", NULL
, "none");
3192 int num_reasons
= 0;/* Number of reasons */
3193 const char *reasons
[32]; /* Reason strings */
3195 if (printer
->state_reasons
& _IPP_PREASON_OTHER
)
3196 reasons
[num_reasons
++] = "other";
3197 if (printer
->state_reasons
& _IPP_PREASON_COVER_OPEN
)
3198 reasons
[num_reasons
++] = "cover-open";
3199 if (printer
->state_reasons
& _IPP_PREASON_INPUT_TRAY_MISSING
)
3200 reasons
[num_reasons
++] = "input-tray-missing";
3201 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_EMPTY
)
3202 reasons
[num_reasons
++] = "marker-supply-empty-warning";
3203 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_LOW
)
3204 reasons
[num_reasons
++] = "marker-supply-low-report";
3205 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
)
3206 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
3207 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_FULL
)
3208 reasons
[num_reasons
++] = "marker-waste-full-warning";
3209 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
3210 reasons
[num_reasons
++] = "media-empty-warning";
3211 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_JAM
)
3212 reasons
[num_reasons
++] = "media-jam-warning";
3213 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_LOW
)
3214 reasons
[num_reasons
++] = "media-low-report";
3215 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_NEEDED
)
3216 reasons
[num_reasons
++] = "media-needed-report";
3217 if (printer
->state_reasons
& _IPP_PREASON_MOVING_TO_PAUSED
)
3218 reasons
[num_reasons
++] = "moving-to-paused";
3219 if (printer
->state_reasons
& _IPP_PREASON_PAUSED
)
3220 reasons
[num_reasons
++] = "paused";
3221 if (printer
->state_reasons
& _IPP_PREASON_SPOOL_AREA_FULL
)
3222 reasons
[num_reasons
++] = "spool-area-full";
3223 if (printer
->state_reasons
& _IPP_PREASON_TONER_EMPTY
)
3224 reasons
[num_reasons
++] = "toner-empty-warning";
3225 if (printer
->state_reasons
& _IPP_PREASON_TONER_LOW
)
3226 reasons
[num_reasons
++] = "toner-low-report";
3228 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
3229 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3230 "printer-state-reasons", num_reasons
, NULL
, reasons
);
3234 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3236 int i
; /* Looping var */
3237 char buffer
[256]; /* Supply value buffer */
3238 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3239 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3241 for (i
= 0; i
< 5; i
++)
3243 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
]);
3246 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3248 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3252 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3253 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3255 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3256 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3257 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3259 _cupsRWUnlock(&(printer
->rwlock
));
3261 cupsArrayDelete(ra
);
3266 * 'ipp_identify_printer()' - Beep or display a message.
3270 ipp_identify_printer(
3271 _ipp_client_t
*client
) /* I - Client */
3273 /* TODO: Do something */
3275 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3280 * 'ipp_print_job()' - Create a job object with an attached document.
3284 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3286 _ipp_job_t
*job
; /* New job */
3287 char filename
[1024], /* Filename buffer */
3288 buffer
[4096]; /* Copy buffer */
3289 ssize_t bytes
; /* Bytes read */
3290 cups_array_t
*ra
; /* Attributes to send in response */
3294 * Validate print job attributes...
3297 if (!valid_job_attributes(client
))
3299 httpFlush(client
->http
);
3304 * Do we have a file to print?
3307 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3309 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3317 if ((job
= create_job(client
)) == NULL
)
3319 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3320 "Currently printing another job.");
3325 * Create a file for the request data...
3328 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3331 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3333 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3335 job
->state
= IPP_JSTATE_ABORTED
;
3337 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3338 "Unable to create print file: %s", strerror(errno
));
3342 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3344 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3346 int error
= errno
; /* Write error */
3348 job
->state
= IPP_JSTATE_ABORTED
;
3355 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3356 "Unable to write print file: %s", strerror(error
));
3364 * Got an error while reading the print data, so abort this job.
3367 job
->state
= IPP_JSTATE_ABORTED
;
3374 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3375 "Unable to read print file.");
3381 int error
= errno
; /* Write error */
3383 job
->state
= IPP_JSTATE_ABORTED
;
3388 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3389 "Unable to write print file: %s", strerror(error
));
3394 job
->filename
= strdup(filename
);
3395 job
->state
= IPP_JSTATE_PENDING
;
3398 * Process the job...
3401 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3403 job
->state
= IPP_JSTATE_ABORTED
;
3404 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3409 * Return the job info...
3412 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3414 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3415 cupsArrayAdd(ra
, "job-id");
3416 cupsArrayAdd(ra
, "job-state");
3417 cupsArrayAdd(ra
, "job-state-message");
3418 cupsArrayAdd(ra
, "job-state-reasons");
3419 cupsArrayAdd(ra
, "job-uri");
3421 copy_job_attributes(client
, job
, ra
);
3422 cupsArrayDelete(ra
);
3427 * 'ipp_print_uri()' - Create a job object with a referenced document.
3431 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3433 _ipp_job_t
*job
; /* New job */
3434 ipp_attribute_t
*uri
; /* document-uri */
3435 char scheme
[256], /* URI scheme */
3436 userpass
[256], /* Username and password info */
3437 hostname
[256], /* Hostname */
3438 resource
[1024]; /* Resource path */
3439 int port
; /* Port number */
3440 http_uri_status_t uri_status
; /* URI decode status */
3441 http_encryption_t encryption
; /* Encryption to use, if any */
3442 http_t
*http
; /* Connection for http/https URIs */
3443 http_status_t status
; /* Access status for http/https URIs */
3444 int infile
; /* Input file for local file URIs */
3445 char filename
[1024], /* Filename buffer */
3446 buffer
[4096]; /* Copy buffer */
3447 ssize_t bytes
; /* Bytes read */
3448 cups_array_t
*ra
; /* Attributes to send in response */
3449 static const char * const uri_status_strings
[] =
3450 { /* URI decode errors */
3452 "Bad arguments to function.",
3453 "Bad resource in URI.",
3454 "Bad port number in URI.",
3455 "Bad hostname in URI.",
3456 "Bad username in URI.",
3457 "Bad scheme in URI.",
3463 * Validate print job attributes...
3466 if (!valid_job_attributes(client
))
3468 httpFlush(client
->http
);
3473 * Do we have a file to print?
3476 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3478 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3479 "Unexpected document data following request.");
3484 * Do we have a document URI?
3487 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3488 IPP_TAG_URI
)) == NULL
)
3490 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3494 if (ippGetCount(uri
) != 1)
3496 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3497 "Too many document-uri values.");
3501 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3502 scheme
, sizeof(scheme
), userpass
,
3503 sizeof(userpass
), hostname
, sizeof(hostname
),
3504 &port
, resource
, sizeof(resource
));
3505 if (uri_status
< HTTP_URI_STATUS_OK
)
3507 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3508 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3512 if (strcmp(scheme
, "file") &&
3514 strcmp(scheme
, "https") &&
3515 #endif /* HAVE_SSL */
3516 strcmp(scheme
, "http"))
3518 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3519 "URI scheme \"%s\" not supported.", scheme
);
3523 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3525 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3526 "Unable to access URI: %s", strerror(errno
));
3534 if ((job
= create_job(client
)) == NULL
)
3536 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3537 "Currently printing another job.");
3542 * Create a file for the request data...
3545 if (!strcasecmp(job
->format
, "image/jpeg"))
3546 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3547 client
->printer
->directory
, job
->id
);
3548 else if (!strcasecmp(job
->format
, "image/png"))
3549 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3550 client
->printer
->directory
, job
->id
);
3551 else if (!strcasecmp(job
->format
, "application/pdf"))
3552 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3553 client
->printer
->directory
, job
->id
);
3554 else if (!strcasecmp(job
->format
, "application/postscript"))
3555 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3556 client
->printer
->directory
, job
->id
);
3558 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3559 client
->printer
->directory
, job
->id
);
3561 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3563 job
->state
= IPP_JSTATE_ABORTED
;
3565 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3566 "Unable to create print file: %s", strerror(errno
));
3570 if (!strcmp(scheme
, "file"))
3572 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3574 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3575 "Unable to access URI: %s", strerror(errno
));
3581 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3582 (errno
== EAGAIN
|| errno
== EINTR
))
3584 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3586 int error
= errno
; /* Write error */
3588 job
->state
= IPP_JSTATE_ABORTED
;
3596 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3597 "Unable to write print file: %s", strerror(error
));
3608 if (port
== 443 || !strcmp(scheme
, "https"))
3609 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3611 #endif /* HAVE_SSL */
3612 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3614 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3615 1, 30000, NULL
)) == NULL
)
3617 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3618 "Unable to connect to %s: %s", hostname
,
3619 cupsLastErrorString());
3620 job
->state
= IPP_JSTATE_ABORTED
;
3629 httpClearFields(http
);
3630 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3631 if (httpGet(http
, resource
))
3633 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3634 "Unable to GET URI: %s", strerror(errno
));
3636 job
->state
= IPP_JSTATE_ABORTED
;
3646 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3648 if (status
!= HTTP_STATUS_OK
)
3650 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3651 "Unable to GET URI: %s", httpStatus(status
));
3653 job
->state
= IPP_JSTATE_ABORTED
;
3663 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3665 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3667 int error
= errno
; /* Write error */
3669 job
->state
= IPP_JSTATE_ABORTED
;
3677 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3678 "Unable to write print file: %s", strerror(error
));
3688 int error
= errno
; /* Write error */
3690 job
->state
= IPP_JSTATE_ABORTED
;
3695 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3696 "Unable to write print file: %s", strerror(error
));
3701 job
->filename
= strdup(filename
);
3702 job
->state
= IPP_JSTATE_PENDING
;
3705 * Process the job...
3709 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3711 job
->state
= IPP_JSTATE_ABORTED
;
3712 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3721 * Return the job info...
3724 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3726 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3727 cupsArrayAdd(ra
, "job-id");
3728 cupsArrayAdd(ra
, "job-state");
3729 cupsArrayAdd(ra
, "job-state-reasons");
3730 cupsArrayAdd(ra
, "job-uri");
3732 copy_job_attributes(client
, job
, ra
);
3733 cupsArrayDelete(ra
);
3738 * 'ipp_send_document()' - Add an attached document to a job object created with
3743 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3745 _ipp_job_t
*job
; /* Job information */
3746 char filename
[1024], /* Filename buffer */
3747 buffer
[4096]; /* Copy buffer */
3748 ssize_t bytes
; /* Bytes read */
3749 ipp_attribute_t
*attr
; /* Current attribute */
3750 cups_array_t
*ra
; /* Attributes to send in response */
3757 if ((job
= find_job(client
)) == NULL
)
3759 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3760 httpFlush(client
->http
);
3765 * See if we already have a document for this job or the job has already
3766 * in a non-pending state...
3769 if (job
->state
> IPP_JSTATE_HELD
)
3771 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3772 "Job is not in a pending state.");
3773 httpFlush(client
->http
);
3776 else if (job
->filename
|| job
->fd
>= 0)
3778 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3779 "Multiple document jobs are not supported.");
3780 httpFlush(client
->http
);
3784 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3785 IPP_TAG_ZERO
)) == NULL
)
3787 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3788 "Missing required last-document attribute.");
3789 httpFlush(client
->http
);
3792 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3793 !ippGetBoolean(attr
, 0))
3795 respond_unsupported(client
, attr
);
3796 httpFlush(client
->http
);
3801 * Validate document attributes...
3804 if (!valid_doc_attributes(client
))
3806 httpFlush(client
->http
);
3810 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3813 * Get the document format for the job...
3816 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3818 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3819 job
->format
= ippGetString(attr
, 0, NULL
);
3820 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3821 job
->format
= ippGetString(attr
, 0, NULL
);
3823 job
->format
= "application/octet-stream";
3826 * Create a file for the request data...
3829 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3832 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3834 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3836 _cupsRWUnlock(&(client
->printer
->rwlock
));
3840 job
->state
= IPP_JSTATE_ABORTED
;
3842 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3843 "Unable to create print file: %s", strerror(errno
));
3847 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3849 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3851 int error
= errno
; /* Write error */
3853 job
->state
= IPP_JSTATE_ABORTED
;
3860 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3861 "Unable to write print file: %s", strerror(error
));
3869 * Got an error while reading the print data, so abort this job.
3872 job
->state
= IPP_JSTATE_ABORTED
;
3879 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3880 "Unable to read print file.");
3886 int error
= errno
; /* Write error */
3888 job
->state
= IPP_JSTATE_ABORTED
;
3893 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3894 "Unable to write print file: %s", strerror(error
));
3898 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3901 job
->filename
= strdup(filename
);
3902 job
->state
= IPP_JSTATE_PENDING
;
3904 _cupsRWUnlock(&(client
->printer
->rwlock
));
3907 * Process the job...
3911 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3913 job
->state
= IPP_JSTATE_ABORTED
;
3914 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3923 * Return the job info...
3926 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3928 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3929 cupsArrayAdd(ra
, "job-id");
3930 cupsArrayAdd(ra
, "job-state");
3931 cupsArrayAdd(ra
, "job-state-reasons");
3932 cupsArrayAdd(ra
, "job-uri");
3934 copy_job_attributes(client
, job
, ra
);
3935 cupsArrayDelete(ra
);
3940 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3945 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
3947 _ipp_job_t
*job
; /* Job information */
3948 ipp_attribute_t
*uri
; /* document-uri */
3949 char scheme
[256], /* URI scheme */
3950 userpass
[256], /* Username and password info */
3951 hostname
[256], /* Hostname */
3952 resource
[1024]; /* Resource path */
3953 int port
; /* Port number */
3954 http_uri_status_t uri_status
; /* URI decode status */
3955 http_encryption_t encryption
; /* Encryption to use, if any */
3956 http_t
*http
; /* Connection for http/https URIs */
3957 http_status_t status
; /* Access status for http/https URIs */
3958 int infile
; /* Input file for local file URIs */
3959 char filename
[1024], /* Filename buffer */
3960 buffer
[4096]; /* Copy buffer */
3961 ssize_t bytes
; /* Bytes read */
3962 ipp_attribute_t
*attr
; /* Current attribute */
3963 cups_array_t
*ra
; /* Attributes to send in response */
3964 static const char * const uri_status_strings
[] =
3965 { /* URI decode errors */
3967 "Bad arguments to function.",
3968 "Bad resource in URI.",
3969 "Bad port number in URI.",
3970 "Bad hostname in URI.",
3971 "Bad username in URI.",
3972 "Bad scheme in URI.",
3981 if ((job
= find_job(client
)) == NULL
)
3983 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3984 httpFlush(client
->http
);
3989 * See if we already have a document for this job or the job has already
3990 * in a non-pending state...
3993 if (job
->state
> IPP_JSTATE_HELD
)
3995 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3996 "Job is not in a pending state.");
3997 httpFlush(client
->http
);
4000 else if (job
->filename
|| job
->fd
>= 0)
4002 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4003 "Multiple document jobs are not supported.");
4004 httpFlush(client
->http
);
4008 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4009 IPP_TAG_ZERO
)) == NULL
)
4011 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4012 "Missing required last-document attribute.");
4013 httpFlush(client
->http
);
4016 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4017 !ippGetBoolean(attr
, 0))
4019 respond_unsupported(client
, attr
);
4020 httpFlush(client
->http
);
4025 * Validate document attributes...
4028 if (!valid_doc_attributes(client
))
4030 httpFlush(client
->http
);
4035 * Do we have a file to print?
4038 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4040 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4041 "Unexpected document data following request.");
4046 * Do we have a document URI?
4049 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4050 IPP_TAG_URI
)) == NULL
)
4052 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4056 if (ippGetCount(uri
) != 1)
4058 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4059 "Too many document-uri values.");
4063 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4064 scheme
, sizeof(scheme
), userpass
,
4065 sizeof(userpass
), hostname
, sizeof(hostname
),
4066 &port
, resource
, sizeof(resource
));
4067 if (uri_status
< HTTP_URI_STATUS_OK
)
4069 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4070 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4074 if (strcmp(scheme
, "file") &&
4076 strcmp(scheme
, "https") &&
4077 #endif /* HAVE_SSL */
4078 strcmp(scheme
, "http"))
4080 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4081 "URI scheme \"%s\" not supported.", scheme
);
4085 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4087 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4088 "Unable to access URI: %s", strerror(errno
));
4093 * Get the document format for the job...
4096 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4098 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4099 IPP_TAG_MIMETYPE
)) != NULL
)
4100 job
->format
= ippGetString(attr
, 0, NULL
);
4102 job
->format
= "application/octet-stream";
4105 * Create a file for the request data...
4108 if (!strcasecmp(job
->format
, "image/jpeg"))
4109 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4110 client
->printer
->directory
, job
->id
);
4111 else if (!strcasecmp(job
->format
, "image/png"))
4112 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4113 client
->printer
->directory
, job
->id
);
4114 else if (!strcasecmp(job
->format
, "application/pdf"))
4115 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4116 client
->printer
->directory
, job
->id
);
4117 else if (!strcasecmp(job
->format
, "application/postscript"))
4118 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4119 client
->printer
->directory
, job
->id
);
4121 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4122 client
->printer
->directory
, job
->id
);
4124 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4126 _cupsRWUnlock(&(client
->printer
->rwlock
));
4130 job
->state
= IPP_JSTATE_ABORTED
;
4132 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4133 "Unable to create print file: %s", strerror(errno
));
4137 if (!strcmp(scheme
, "file"))
4139 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4141 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4142 "Unable to access URI: %s", strerror(errno
));
4148 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4149 (errno
== EAGAIN
|| errno
== EINTR
))
4151 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4153 int error
= errno
; /* Write error */
4155 job
->state
= IPP_JSTATE_ABORTED
;
4163 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4164 "Unable to write print file: %s", strerror(error
));
4175 if (port
== 443 || !strcmp(scheme
, "https"))
4176 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4178 #endif /* HAVE_SSL */
4179 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4181 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4182 1, 30000, NULL
)) == NULL
)
4184 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4185 "Unable to connect to %s: %s", hostname
,
4186 cupsLastErrorString());
4187 job
->state
= IPP_JSTATE_ABORTED
;
4196 httpClearFields(http
);
4197 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4198 if (httpGet(http
, resource
))
4200 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4201 "Unable to GET URI: %s", strerror(errno
));
4203 job
->state
= IPP_JSTATE_ABORTED
;
4213 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4215 if (status
!= HTTP_STATUS_OK
)
4217 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4218 "Unable to GET URI: %s", httpStatus(status
));
4220 job
->state
= IPP_JSTATE_ABORTED
;
4230 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4232 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4234 int error
= errno
; /* Write error */
4236 job
->state
= IPP_JSTATE_ABORTED
;
4244 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4245 "Unable to write print file: %s", strerror(error
));
4255 int error
= errno
; /* Write error */
4257 job
->state
= IPP_JSTATE_ABORTED
;
4262 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4263 "Unable to write print file: %s", strerror(error
));
4267 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4270 job
->filename
= strdup(filename
);
4271 job
->state
= IPP_JSTATE_PENDING
;
4273 _cupsRWUnlock(&(client
->printer
->rwlock
));
4276 * Process the job...
4280 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4282 job
->state
= IPP_JSTATE_ABORTED
;
4283 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4292 * Return the job info...
4295 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4297 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4298 cupsArrayAdd(ra
, "job-id");
4299 cupsArrayAdd(ra
, "job-state");
4300 cupsArrayAdd(ra
, "job-state-reasons");
4301 cupsArrayAdd(ra
, "job-uri");
4303 copy_job_attributes(client
, job
, ra
);
4304 cupsArrayDelete(ra
);
4309 * 'ipp_validate_job()' - Validate job creation attributes.
4313 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4315 if (valid_job_attributes(client
))
4316 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4321 * 'parse_options()' - Parse URL options into CUPS options.
4323 * The client->options string is destroyed by this function.
4326 static int /* O - Number of options */
4327 parse_options(_ipp_client_t
*client
, /* I - Client */
4328 cups_option_t
**options
) /* O - Options */
4330 char *name
, /* Name */
4332 *next
; /* Next name=value pair */
4333 int num_options
= 0; /* Number of options */
4338 for (name
= client
->options
; name
&& *name
; name
= next
)
4340 if ((value
= strchr(name
, '=')) == NULL
)
4344 if ((next
= strchr(value
, '&')) != NULL
)
4347 num_options
= cupsAddOption(name
, value
, num_options
, options
);
4350 return (num_options
);
4355 * 'process_client()' - Process client requests on a thread.
4358 static void * /* O - Exit status */
4359 process_client(_ipp_client_t
*client
) /* I - Client */
4362 * Loop until we are out of requests or timeout (30 seconds)...
4366 int first_time
= 1; /* First time request? */
4367 #endif /* HAVE_SSL */
4369 while (httpWait(client
->http
, 30000))
4375 * See if we need to negotiate a TLS connection...
4378 char buf
[1]; /* First byte from client */
4380 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
4382 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
4384 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
4386 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4390 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4395 #endif /* HAVE_SSL */
4397 if (!process_http(client
))
4402 * Close the conection to the client and return...
4405 delete_client(client
);
4412 * 'process_http()' - Process a HTTP request.
4415 int /* O - 1 on success, 0 on failure */
4416 process_http(_ipp_client_t
*client
) /* I - Client connection */
4418 char uri
[1024]; /* URI */
4419 http_state_t http_state
; /* HTTP state */
4420 http_status_t http_status
; /* HTTP status */
4421 ipp_state_t ipp_state
; /* State of IPP transfer */
4422 char scheme
[32], /* Method/scheme */
4423 userpass
[128], /* Username:password */
4424 hostname
[HTTP_MAX_HOST
];
4426 int port
; /* Port number */
4427 const char *encoding
; /* Content-Encoding value */
4428 static const char * const http_states
[] =
4429 { /* Strings for logging HTTP method */
4450 * Clear state variables...
4453 ippDelete(client
->request
);
4454 ippDelete(client
->response
);
4456 client
->request
= NULL
;
4457 client
->response
= NULL
;
4458 client
->operation
= HTTP_STATE_WAITING
;
4461 * Read a request from the connection...
4464 while ((http_state
= httpReadRequest(client
->http
, uri
,
4465 sizeof(uri
))) == HTTP_STATE_WAITING
)
4469 * Parse the request line...
4472 if (http_state
== HTTP_STATE_ERROR
)
4474 if (httpError(client
->http
) == EPIPE
)
4475 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
4477 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
4478 strerror(httpError(client
->http
)));
4482 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
4484 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
4485 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4488 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
4490 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
4491 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4495 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
4499 * Separate the URI into its components...
4502 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4503 userpass
, sizeof(userpass
),
4504 hostname
, sizeof(hostname
), &port
,
4505 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
4506 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
4508 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
4509 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4513 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
4514 *(client
->options
)++ = '\0';
4517 * Process the request...
4520 client
->start
= time(NULL
);
4521 client
->operation
= httpGetState(client
->http
);
4524 * Parse incoming parameters until the status changes...
4527 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4529 if (http_status
!= HTTP_STATUS_OK
)
4531 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4535 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4536 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4539 * HTTP/1.1 and higher require the "Host:" field...
4542 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4547 * Handle HTTP Upgrade...
4550 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4554 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
4556 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
4559 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
4561 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
4563 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4567 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4570 #endif /* HAVE_SSL */
4572 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4577 * Handle HTTP Expect...
4580 if (httpGetExpect(client
->http
) &&
4581 (client
->operation
== HTTP_STATE_POST
||
4582 client
->operation
== HTTP_STATE_PUT
))
4584 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4587 * Send 100-continue header...
4590 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4596 * Send 417-expectation-failed header...
4599 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4605 * Handle new transfers...
4608 encoding
= httpGetContentEncoding(client
->http
);
4610 switch (client
->operation
)
4612 case HTTP_STATE_OPTIONS
:
4614 * Do OPTIONS command...
4617 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4619 case HTTP_STATE_HEAD
:
4620 if (!strcmp(client
->uri
, "/icon.png"))
4621 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4622 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
4623 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4625 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4627 case HTTP_STATE_GET
:
4628 if (!strcmp(client
->uri
, "/icon.png"))
4631 * Send PNG icon file.
4634 int fd
; /* Icon file */
4635 struct stat fileinfo
; /* Icon file information */
4636 char buffer
[4096]; /* Copy buffer */
4637 ssize_t bytes
; /* Bytes */
4639 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
4641 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4642 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4644 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
4645 (size_t)fileinfo
.st_size
))
4651 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4652 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
4654 httpFlushWrite(client
->http
);
4659 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4661 else if (!strcmp(client
->uri
, "/"))
4664 * Show web status page...
4667 _ipp_job_t
*job
; /* Current job */
4668 int i
; /* Looping var */
4669 _ipp_preason_t reason
; /* Current reason */
4670 static const char * const reasons
[] =
4671 { /* Reason strings */
4674 "Input Tray Missing",
4675 "Marker Supply Empty",
4676 "Marker Supply Low",
4677 "Marker Waste Almost Full",
4678 "Marker Waste Full",
4690 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4693 html_header(client
, client
->printer
->name
);
4695 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
4696 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
4697 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
4698 if (client
->printer
->state_reasons
& reason
)
4699 html_printf(client
, "\n<br> %s", reasons
[i
]);
4700 html_printf(client
, "</p>\n");
4702 if (cupsArrayCount(client
->printer
->jobs
) > 0)
4704 _cupsRWLockRead(&(client
->printer
->rwlock
));
4706 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");
4707 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
4709 char when
[256], /* When job queued/started/finished */
4710 hhmmss
[64]; /* Time HH:MM:SS */
4714 case IPP_JSTATE_PENDING
:
4715 case IPP_JSTATE_HELD
:
4716 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
4718 case IPP_JSTATE_PROCESSING
:
4719 case IPP_JSTATE_STOPPED
:
4720 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
4722 case IPP_JSTATE_ABORTED
:
4723 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4725 case IPP_JSTATE_CANCELED
:
4726 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4728 case IPP_JSTATE_COMPLETED
:
4729 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4733 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
);
4735 html_printf(client
, "</tbody></table>\n");
4737 _cupsRWUnlock(&(client
->printer
->rwlock
));
4739 html_footer(client
);
4743 else if (!strcmp(client
->uri
, "/media"))
4746 * Show web media page...
4749 int i
, /* Looping var */
4750 num_options
; /* Number of form options */
4751 cups_option_t
*options
; /* Form options */
4752 static const char * const sizes
[] =
4753 { /* Size strings */
4766 static const char * const types
[] =
4783 static const int sheets
[] = /* Number of sheets */
4792 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4795 html_header(client
, client
->printer
->name
);
4797 if ((num_options
= parse_options(client
, &options
)) > 0)
4800 * WARNING: A real printer/server implementation MUST NOT implement
4801 * media updates via a GET request - GET requests are supposed to be
4802 * idempotent (without side-effects) and we obviously are not
4803 * authenticating access here. This form is provided solely to
4804 * enable testing and development!
4807 const char *val
; /* Form value */
4809 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
4810 client
->printer
->main_size
= atoi(val
);
4811 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
4812 client
->printer
->main_type
= atoi(val
);
4813 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
4814 client
->printer
->main_level
= atoi(val
);
4816 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
4817 client
->printer
->envelope_size
= atoi(val
);
4818 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
4819 client
->printer
->envelope_level
= atoi(val
);
4821 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
4822 client
->printer
->photo_size
= atoi(val
);
4823 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
4824 client
->printer
->photo_type
= atoi(val
);
4825 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
4826 client
->printer
->photo_level
= atoi(val
);
4828 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))
4829 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
4831 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
4833 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
))
4835 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
4836 if (client
->printer
->active_job
)
4837 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
4840 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
4842 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
4845 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
4847 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
4848 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
4849 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4850 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
4851 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
4852 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
4853 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4854 if (!strstr(types
[i
], "Photo"))
4855 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
4856 html_printf(client
, "</select> <select name=\"main_level\">");
4857 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4858 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
4859 html_printf(client
, "</select></td></tr>\n");
4862 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
4863 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4864 if (strstr(sizes
[i
], "Envelope"))
4865 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
4866 html_printf(client
, "</select> <select name=\"envelope_level\">");
4867 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4868 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
4869 html_printf(client
, "</select></td></tr>\n");
4872 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
4873 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4874 if (strstr(sizes
[i
], "Photo"))
4875 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
4876 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
4877 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4878 if (strstr(types
[i
], "Photo"))
4879 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
4880 html_printf(client
, "</select> <select name=\"photo_level\">");
4881 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4882 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
4883 html_printf(client
, "</select></td></tr>\n");
4885 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
4886 html_footer(client
);
4890 else if (!strcmp(client
->uri
, "/supplies"))
4893 * Show web supplies page...
4896 int i
, j
, /* Looping vars */
4897 num_options
; /* Number of form options */
4898 cups_option_t
*options
; /* Form options */
4899 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
4901 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4904 html_header(client
, client
->printer
->name
);
4906 if ((num_options
= parse_options(client
, &options
)) > 0)
4909 * WARNING: A real printer/server implementation MUST NOT implement
4910 * supply updates via a GET request - GET requests are supposed to be
4911 * idempotent (without side-effects) and we obviously are not
4912 * authenticating access here. This form is provided solely to
4913 * enable testing and development!
4916 char name
[64]; /* Form field */
4917 const char *val
; /* Form value */
4919 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
);
4921 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
4923 snprintf(name
, sizeof(name
), "supply_%d", i
);
4924 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
4926 int level
= client
->printer
->supplies
[i
] = atoi(val
);
4932 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
4933 else if (level
< 10)
4934 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
4939 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
4940 else if (level
> 90)
4941 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
4946 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
4949 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
4951 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
4952 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
4954 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
4955 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
4956 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
4957 html_printf(client
, "</select></td></tr>\n");
4959 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
4960 html_footer(client
);
4965 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4968 case HTTP_STATE_POST
:
4969 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
4973 * Not an IPP request...
4976 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
4980 * Read the IPP request...
4983 client
->request
= ippNew();
4985 while ((ipp_state
= ippRead(client
->http
,
4986 client
->request
)) != IPP_STATE_DATA
)
4988 if (ipp_state
== IPP_STATE_ERROR
)
4990 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
4991 cupsLastErrorString());
4992 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4998 * Now that we have the IPP request, process the request...
5001 return (process_ipp(client
));
5004 break; /* Anti-compiler-warning-code */
5012 * 'process_ipp()' - Process an IPP request.
5015 static int /* O - 1 on success, 0 on error */
5016 process_ipp(_ipp_client_t
*client
) /* I - Client */
5018 ipp_tag_t group
; /* Current group tag */
5019 ipp_attribute_t
*attr
; /* Current attribute */
5020 ipp_attribute_t
*charset
; /* Character set attribute */
5021 ipp_attribute_t
*language
; /* Language attribute */
5022 ipp_attribute_t
*uri
; /* Printer URI attribute */
5023 int major
, minor
; /* Version number */
5024 const char *name
; /* Name of attribute */
5027 debug_attributes("Request", client
->request
, 1);
5030 * First build an empty response message for this request...
5033 client
->operation_id
= ippGetOperation(client
->request
);
5034 client
->response
= ippNewResponse(client
->request
);
5037 * Then validate the request header and required attributes...
5040 major
= ippGetVersion(client
->request
, &minor
);
5042 if (major
< 1 || major
> 2)
5045 * Return an error, since we only support IPP 1.x and 2.x.
5048 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
5049 "Bad request version number %d.%d.", major
, minor
);
5051 else if (ippGetRequestId(client
->request
) <= 0)
5052 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
5053 ippGetRequestId(client
->request
));
5054 else if (!ippFirstAttribute(client
->request
))
5055 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5056 "No attributes in request.");
5060 * Make sure that the attributes are provided in the correct order and
5061 * don't repeat groups...
5064 for (attr
= ippFirstAttribute(client
->request
),
5065 group
= ippGetGroupTag(attr
);
5067 attr
= ippNextAttribute(client
->request
))
5069 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5072 * Out of order; return an error...
5075 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5076 "Attribute groups are out of order (%x < %x).",
5077 ippGetGroupTag(attr
), group
);
5081 group
= ippGetGroupTag(attr
);
5087 * Then make sure that the first three attributes are:
5089 * attributes-charset
5090 * attributes-natural-language
5091 * printer-uri/job-uri
5094 attr
= ippFirstAttribute(client
->request
);
5095 name
= ippGetName(attr
);
5096 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5097 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5102 attr
= ippNextAttribute(client
->request
);
5103 name
= ippGetName(attr
);
5105 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5106 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5111 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5112 IPP_TAG_URI
)) != NULL
)
5114 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5115 IPP_TAG_URI
)) != NULL
)
5121 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5122 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5125 * Bad character set...
5128 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5129 "Unsupported character set \"%s\".",
5130 ippGetString(charset
, 0, NULL
));
5132 else if (!charset
|| !language
|| !uri
)
5135 * Return an error, since attributes-charset,
5136 * attributes-natural-language, and printer-uri/job-uri are required
5137 * for all operations.
5140 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5141 "Missing required attributes.");
5145 char scheme
[32], /* URI scheme */
5146 userpass
[32], /* Username/password in URI */
5147 host
[256], /* Host name in URI */
5148 resource
[256]; /* Resource path in URI */
5149 int port
; /* Port number in URI */
5151 name
= ippGetName(uri
);
5153 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5154 scheme
, sizeof(scheme
),
5155 userpass
, sizeof(userpass
),
5156 host
, sizeof(host
), &port
,
5157 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5158 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5159 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5160 else if ((!strcmp(name
, "job-uri") &&
5161 strncmp(resource
, "/ipp/print/", 11)) ||
5162 (!strcmp(name
, "printer-uri") &&
5163 strcmp(resource
, "/ipp/print")))
5164 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5165 name
, ippGetString(uri
, 0, NULL
));
5169 * Try processing the operation...
5172 switch (ippGetOperation(client
->request
))
5174 case IPP_OP_PRINT_JOB
:
5175 ipp_print_job(client
);
5178 case IPP_OP_PRINT_URI
:
5179 ipp_print_uri(client
);
5182 case IPP_OP_VALIDATE_JOB
:
5183 ipp_validate_job(client
);
5186 case IPP_OP_CREATE_JOB
:
5187 ipp_create_job(client
);
5190 case IPP_OP_SEND_DOCUMENT
:
5191 ipp_send_document(client
);
5194 case IPP_OP_SEND_URI
:
5195 ipp_send_uri(client
);
5198 case IPP_OP_CANCEL_JOB
:
5199 ipp_cancel_job(client
);
5202 case IPP_OP_GET_JOB_ATTRIBUTES
:
5203 ipp_get_job_attributes(client
);
5206 case IPP_OP_GET_JOBS
:
5207 ipp_get_jobs(client
);
5210 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5211 ipp_get_printer_attributes(client
);
5214 case IPP_OP_CLOSE_JOB
:
5215 ipp_close_job(client
);
5218 case IPP_OP_IDENTIFY_PRINTER
:
5219 ipp_identify_printer(client
);
5223 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5224 "Operation not supported.");
5233 * Send the HTTP header and return...
5236 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5237 httpFlush(client
->http
); /* Flush trailing (junk) data */
5239 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5240 ippLength(client
->response
)));
5245 * 'process_job()' - Process a print job.
5248 static void * /* O - Thread exit status */
5249 process_job(_ipp_job_t
*job
) /* I - Job */
5251 job
->state
= IPP_JSTATE_PROCESSING
;
5252 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
5253 job
->processing
= time(NULL
);
5255 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
5257 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5262 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
5264 if (job
->printer
->command
)
5267 * Execute a command with the job spool file and wait for it to complete...
5270 int pid
, /* Process ID */
5271 status
; /* Exit status */
5272 time_t start
, /* Start time */
5274 char *myargv
[3], /* Command-line arguments */
5275 *myenvp
[200]; /* Environment variables */
5276 int myenvc
; /* Number of environment variables */
5277 ipp_attribute_t
*attr
; /* Job attribute */
5278 char val
[1280], /* IPP_NAME=value */
5279 *valptr
; /* Pointer into string */
5281 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
5286 * Setup the command-line arguments...
5289 myargv
[0] = job
->printer
->command
;
5290 myargv
[1] = job
->filename
;
5294 * Copy the current environment, then add ENV variables for every Job
5298 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
5299 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
5301 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
5304 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
5305 * value(s) from the attribute.
5308 const char *name
= ippGetName(attr
);
5317 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
5322 *valptr
++ = (char)toupper(*name
& 255);
5327 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
5329 myenvp
[myenvc
++] = strdup(val
);
5331 myenvp
[myenvc
] = NULL
;
5334 * Now run the program...
5338 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
5340 if ((pid
= fork()) == 0)
5343 * Child comes here...
5346 execve(job
->printer
->command
, myargv
, myenvp
);
5352 * Unable to fork process...
5355 perror("Unable to start job processing command");
5361 * Free memory used for environment...
5365 free(myenvp
[-- myenvc
]);
5368 * Wait for child to complete...
5371 # ifdef HAVE_WAITPID
5372 while (waitpid(pid
, &status
, 0) < 0);
5374 while (wait(&status
) < 0);
5375 # endif /* HAVE_WAITPID */
5382 if (WIFEXITED(status
))
5384 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
5385 job
->printer
->command
, WEXITSTATUS(status
));
5388 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
5389 job
->printer
->command
, WTERMSIG(status
));
5391 job
->state
= IPP_JSTATE_ABORTED
;
5393 else if (status
< 0)
5394 job
->state
= IPP_JSTATE_ABORTED
;
5396 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
5397 job
->printer
->command
);
5400 * Make sure processing takes at least 5 seconds...
5404 if ((end
- start
) < 5)
5410 * Sleep for a random amount of time to simulate job processing.
5413 sleep((unsigned)(5 + (rand() % 11)));
5417 job
->state
= IPP_JSTATE_CANCELED
;
5418 else if (job
->state
== IPP_JSTATE_PROCESSING
)
5419 job
->state
= IPP_JSTATE_COMPLETED
;
5421 job
->completed
= time(NULL
);
5422 job
->printer
->state
= IPP_PSTATE_IDLE
;
5423 job
->printer
->active_job
= NULL
;
5431 * 'register_printer()' - Register a printer object via Bonjour.
5434 static int /* O - 1 on success, 0 on error */
5436 _ipp_printer_t
*printer
, /* I - Printer */
5437 const char *location
, /* I - Location */
5438 const char *make
, /* I - Manufacturer */
5439 const char *model
, /* I - Model name */
5440 const char *formats
, /* I - Supported formats */
5441 const char *adminurl
, /* I - Web interface URL */
5442 const char *uuid
, /* I - Printer UUID */
5443 int color
, /* I - 1 = color, 0 = monochrome */
5444 int duplex
, /* I - 1 = duplex, 0 = simplex */
5445 const char *subtype
) /* I - Service subtype */
5447 DNSServiceErrorType error
; /* Error from Bonjour */
5448 char make_model
[256],/* Make and model together */
5449 product
[256], /* Product string */
5450 regtype
[256]; /* Bonjour service type */
5454 * Build the TXT record for IPP...
5457 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
5458 snprintf(product
, sizeof(product
), "(%s)", model
);
5460 TXTRecordCreate(&(printer
->ipp_txt
), 1024, NULL
);
5461 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 9, "ipp/print");
5462 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
5464 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
5467 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
5469 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
5471 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
5473 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
5474 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
5475 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
5477 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
5479 TXTRecordSetValue(&(printer
->ipp_txt
), "UUID", (uint8_t)strlen(uuid
), uuid
);
5481 TXTRecordSetValue(&(printer
->ipp_txt
), "TLS", 3, "1.2");
5482 # endif /* HAVE_SSL */
5485 * Create a shared service reference for Bonjour...
5488 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
5489 != kDNSServiceErr_NoError
)
5491 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
5496 * Register the _printer._tcp (LPD) service type with a port number of 0 to
5497 * defend our service name but not actually support LPD...
5500 printer
->printer_ref
= printer
->common_ref
;
5502 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
5503 kDNSServiceFlagsShareConnection
,
5504 0 /* interfaceIndex */, printer
->dnssd_name
,
5505 "_printer._tcp", NULL
/* domain */,
5506 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
5507 NULL
/* txtRecord */,
5508 (DNSServiceRegisterReply
)dnssd_callback
,
5509 printer
)) != kDNSServiceErr_NoError
)
5511 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
5512 printer
->dnssd_name
, error
);
5517 * Then register the _ipp._tcp (IPP) service type with the real port number to
5518 * advertise our IPP printer...
5521 printer
->ipp_ref
= printer
->common_ref
;
5523 if (subtype
&& *subtype
)
5524 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
5526 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
5528 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
5529 kDNSServiceFlagsShareConnection
,
5530 0 /* interfaceIndex */, printer
->dnssd_name
,
5531 regtype
, NULL
/* domain */,
5532 NULL
/* host */, htons(printer
->port
),
5533 TXTRecordGetLength(&(printer
->ipp_txt
)),
5534 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
5535 (DNSServiceRegisterReply
)dnssd_callback
,
5536 printer
)) != kDNSServiceErr_NoError
)
5538 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5539 printer
->dnssd_name
, regtype
, error
);
5545 * Then register the _ipps._tcp (IPP) service type with the real port number to
5546 * advertise our IPP printer...
5549 printer
->ipps_ref
= printer
->common_ref
;
5551 if (subtype
&& *subtype
)
5552 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
5554 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
5556 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
5557 kDNSServiceFlagsShareConnection
,
5558 0 /* interfaceIndex */, printer
->dnssd_name
,
5559 regtype
, NULL
/* domain */,
5560 NULL
/* host */, htons(printer
->port
),
5561 TXTRecordGetLength(&(printer
->ipp_txt
)),
5562 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
5563 (DNSServiceRegisterReply
)dnssd_callback
,
5564 printer
)) != kDNSServiceErr_NoError
)
5566 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5567 printer
->dnssd_name
, regtype
, error
);
5570 # endif /* HAVE_SSL */
5573 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
5574 * real port number to advertise our IPP printer...
5577 printer
->http_ref
= printer
->common_ref
;
5579 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
5580 kDNSServiceFlagsShareConnection
,
5581 0 /* interfaceIndex */, printer
->dnssd_name
,
5582 "_http._tcp,_printer", NULL
/* domain */,
5583 NULL
/* host */, htons(printer
->port
),
5584 0 /* txtLen */, NULL
, /* txtRecord */
5585 (DNSServiceRegisterReply
)dnssd_callback
,
5586 printer
)) != kDNSServiceErr_NoError
)
5588 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5589 printer
->dnssd_name
, regtype
, error
);
5595 #endif /* HAVE_DNSSD */
5599 * 'respond_http()' - Send a HTTP response.
5602 int /* O - 1 on success, 0 on failure */
5604 _ipp_client_t
*client
, /* I - Client */
5605 http_status_t code
, /* I - HTTP status of response */
5606 const char *content_encoding
, /* I - Content-Encoding of response */
5607 const char *type
, /* I - MIME media type of response */
5608 size_t length
) /* I - Length of response */
5610 char message
[1024]; /* Text message */
5613 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
5615 if (code
== HTTP_STATUS_CONTINUE
)
5618 * 100-continue doesn't send any headers...
5621 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
5625 * Format an error message...
5628 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
5630 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
5632 type
= "text/plain";
5633 length
= strlen(message
);
5639 * Send the HTTP response header...
5642 httpClearFields(client
->http
);
5644 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
5645 client
->operation
== HTTP_STATE_OPTIONS
)
5646 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
5650 if (!strcmp(type
, "text/html"))
5651 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
5652 "text/html; charset=utf-8");
5654 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
5656 if (content_encoding
)
5657 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
5660 httpSetLength(client
->http
, length
);
5662 if (httpWriteResponse(client
->http
, code
) < 0)
5666 * Send the response data...
5672 * Send a plain text message.
5675 if (httpPrintf(client
->http
, "%s", message
) < 0)
5678 if (httpWrite2(client
->http
, "", 0) < 0)
5681 else if (client
->response
)
5684 * Send an IPP response...
5687 debug_attributes("Response", client
->response
, 2);
5689 ippSetState(client
->response
, IPP_STATE_IDLE
);
5691 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
5700 * 'respond_ipp()' - Send an IPP response.
5704 respond_ipp(_ipp_client_t
*client
, /* I - Client */
5705 ipp_status_t status
, /* I - status-code */
5706 const char *message
, /* I - printf-style status-message */
5707 ...) /* I - Additional args as needed */
5709 const char *formatted
= NULL
; /* Formatted message */
5712 ippSetStatusCode(client
->response
, status
);
5716 va_list ap
; /* Pointer to additional args */
5717 ipp_attribute_t
*attr
; /* New status-message attribute */
5719 va_start(ap
, message
);
5720 if ((attr
= ippFindAttribute(client
->response
, "status-message",
5721 IPP_TAG_TEXT
)) != NULL
)
5722 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
5724 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
5725 "status-message", NULL
, message
, ap
);
5728 formatted
= ippGetString(attr
, 0, NULL
);
5732 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
5733 ippOpString(client
->operation_id
), ippErrorString(status
),
5736 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
5737 ippOpString(client
->operation_id
), ippErrorString(status
));
5742 * 'respond_unsupported()' - Respond with an unsupported attribute.
5746 respond_unsupported(
5747 _ipp_client_t
*client
, /* I - Client */
5748 ipp_attribute_t
*attr
) /* I - Atribute */
5750 ipp_attribute_t
*temp
; /* Copy of attribute */
5753 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5754 "Unsupported %s %s%s value.", ippGetName(attr
),
5755 ippGetCount(attr
) > 1 ? "1setOf " : "",
5756 ippTagString(ippGetValueTag(attr
)));
5758 temp
= ippCopyAttribute(client
->response
, attr
, 0);
5759 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
5764 * 'run_printer()' - Run the printer service.
5768 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
5770 int num_fds
; /* Number of file descriptors */
5771 struct pollfd polldata
[3]; /* poll() data */
5772 int timeout
; /* Timeout for poll() */
5773 _ipp_client_t
*client
; /* New client */
5777 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5780 polldata
[0].fd
= printer
->ipv4
;
5781 polldata
[0].events
= POLLIN
;
5783 polldata
[1].fd
= printer
->ipv6
;
5784 polldata
[1].events
= POLLIN
;
5789 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
5790 polldata
[num_fds
++].events
= POLLIN
;
5791 #endif /* HAVE_DNSSD */
5794 * Loop until we are killed or have a hard error...
5799 if (cupsArrayCount(printer
->jobs
))
5804 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
5806 perror("poll() failed");
5810 if (polldata
[0].revents
& POLLIN
)
5812 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
5814 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5816 perror("Unable to create client thread");
5817 delete_client(client
);
5822 if (polldata
[1].revents
& POLLIN
)
5824 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
5826 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5828 perror("Unable to create client thread");
5829 delete_client(client
);
5835 if (polldata
[2].revents
& POLLIN
)
5836 DNSServiceProcessResult(printer
->common_ref
);
5837 #endif /* HAVE_DNSSD */
5840 * Clean out old jobs...
5843 clean_jobs(printer
);
5849 * 'time_string()' - Return the local time in hours, minutes, and seconds.
5853 time_string(time_t tv
, /* I - Time value */
5854 char *buffer
, /* I - Buffer */
5855 size_t bufsize
) /* I - Size of buffer */
5857 struct tm
*curtime
= localtime(&tv
);
5860 strftime(buffer
, bufsize
, "%X", curtime
);
5866 * 'usage()' - Show program usage.
5870 usage(int status
) /* O - Exit status */
5874 puts(CUPS_SVERSION
" - Copyright 2010-2013 by Apple Inc. All rights "
5879 puts("Usage: ippserver [options] \"name\"");
5882 puts("-2 Supports 2-sided printing (default=1-sided)");
5883 puts("-M manufacturer Manufacturer name (default=Test)");
5884 puts("-P PIN printing mode");
5885 puts("-c command Run command for every print job");
5886 printf("-d spool-directory Spool directory "
5887 "(default=/tmp/ippserver.%d)\n", (int)getpid());
5888 puts("-f type/subtype[,...] List of supported types "
5889 "(default=application/pdf,image/jpeg)");
5890 puts("-h Show program help");
5891 puts("-i iconfile.png PNG icon file (default=printer.png)");
5892 puts("-k Keep job spool files");
5893 puts("-l location Location of printer (default=empty string)");
5894 puts("-m model Model name (default=Printer)");
5895 puts("-n hostname Hostname for printer");
5896 puts("-p port Port number (default=auto)");
5898 puts("-r subtype Bonjour service subtype (default=_print)");
5899 #endif /* HAVE_DNSSD */
5900 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
5901 puts("-v[vvv] Be (very) verbose");
5908 * 'valid_doc_attributes()' - Determine whether the document attributes are
5911 * When one or more document attributes are invalid, this function adds a
5912 * suitable response and attributes to the unsupported group.
5915 static int /* O - 1 if valid, 0 if not */
5916 valid_doc_attributes(
5917 _ipp_client_t
*client
) /* I - Client */
5919 int valid
= 1; /* Valid attributes? */
5920 ipp_op_t op
= ippGetOperation(client
->request
);
5922 const char *op_name
= ippOpString(op
);
5923 /* IPP operation name */
5924 ipp_attribute_t
*attr
, /* Current attribute */
5925 *supported
; /* xxx-supported attribute */
5926 const char *compression
= NULL
,
5927 /* compression value */
5928 *format
= NULL
; /* document-format value */
5932 * Check operation attributes...
5935 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
5938 * If compression is specified, only accept a supported value in a Print-Job
5939 * or Send-Document request...
5942 compression
= ippGetString(attr
, 0, NULL
);
5943 supported
= ippFindAttribute(client
->printer
->attrs
,
5944 "compression-supported", IPP_TAG_KEYWORD
);
5946 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5947 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
5948 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
5949 op
!= IPP_OP_VALIDATE_JOB
) ||
5950 !ippContainsString(supported
, compression
))
5952 respond_unsupported(client
, attr
);
5957 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
5959 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
5961 if (strcmp(compression
, "none"))
5964 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
5965 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
5971 * Is it a format we support?
5974 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
5976 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
5977 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
5979 respond_unsupported(client
, attr
);
5984 format
= ippGetString(attr
, 0, NULL
);
5986 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
5987 client
->hostname
, op_name
, format
);
5989 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
5994 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
5996 format
= "application/octet-stream"; /* Should never happen */
5998 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
6001 if (!strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
6004 * Auto-type the file using the first 8 bytes of the file...
6007 unsigned char header
[8]; /* First 8 bytes of file */
6009 memset(header
, 0, sizeof(header
));
6010 httpPeek(client
->http
, (char *)header
, sizeof(header
));
6012 if (!memcmp(header
, "%PDF", 4))
6013 format
= "application/pdf";
6014 else if (!memcmp(header
, "%!", 2))
6015 format
= "application/postscript";
6016 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
6017 format
= "image/jpeg";
6018 else if (!memcmp(header
, "\211PNG", 4))
6019 format
= "image/png";
6020 else if (!memcmp(header
, "RAS2", 4))
6021 format
= "image/pwg-raster";
6022 else if (!memcmp(header
, "UNIRAST", 8))
6023 format
= "image/urf";
6029 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
6030 client
->hostname
, op_name
, format
);
6032 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
6036 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
6038 respond_unsupported(client
, attr
);
6046 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
6047 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
6054 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6056 * When one or more job attributes are invalid, this function adds a suitable
6057 * response and attributes to the unsupported group.
6060 static int /* O - 1 if valid, 0 if not */
6061 valid_job_attributes(
6062 _ipp_client_t
*client
) /* I - Client */
6064 int i
, /* Looping var */
6065 valid
= 1; /* Valid attributes? */
6066 ipp_attribute_t
*attr
, /* Current attribute */
6067 *supported
; /* xxx-supported attribute */
6071 * Check operation attributes...
6074 valid
= valid_doc_attributes(client
);
6077 * Check the various job template attributes...
6080 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
6082 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6083 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
6085 respond_unsupported(client
, attr
);
6090 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
6092 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
6094 respond_unsupported(client
, attr
);
6099 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
6101 if (ippGetCount(attr
) != 1 ||
6102 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6103 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6104 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6105 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
6107 respond_unsupported(client
, attr
);
6112 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
6114 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
6116 respond_unsupported(client
, attr
);
6121 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
6123 if (ippGetCount(attr
) != 1 ||
6124 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6125 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
6127 respond_unsupported(client
, attr
);
6131 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
6134 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
6136 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
6138 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6139 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
6141 respond_unsupported(client
, attr
);
6146 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
6148 if (ippGetCount(attr
) != 1 ||
6149 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6150 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6151 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6152 strcmp(ippGetString(attr
, 0, NULL
), "none"))
6154 respond_unsupported(client
, attr
);
6159 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
6161 if (ippGetCount(attr
) != 1 ||
6162 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6163 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6164 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
6166 respond_unsupported(client
, attr
);
6172 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
6174 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
6177 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
6179 respond_unsupported(client
, attr
);
6185 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
6187 if (ippGetCount(attr
) != 1 ||
6188 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
6190 respond_unsupported(client
, attr
);
6193 /* TODO: check for valid media-col */
6196 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
6198 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6199 (strcmp(ippGetString(attr
, 0, NULL
),
6200 "separate-documents-uncollated-copies") &&
6201 strcmp(ippGetString(attr
, 0, NULL
),
6202 "separate-documents-collated-copies")))
6204 respond_unsupported(client
, attr
);
6209 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
6211 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6212 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
6213 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
6215 respond_unsupported(client
, attr
);
6220 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
6222 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
6224 respond_unsupported(client
, attr
);
6229 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
6231 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6232 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
6233 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
6235 respond_unsupported(client
, attr
);
6240 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
6242 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
6244 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
6247 respond_unsupported(client
, attr
);
6252 int count
, /* Number of supported values */
6253 xdpi
, /* Horizontal resolution for job template attribute */
6254 ydpi
, /* Vertical resolution for job template attribute */
6255 sydpi
; /* Vertical resolution for supported value */
6256 ipp_res_t units
, /* Units for job template attribute */
6257 sunits
; /* Units for supported value */
6259 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
6260 count
= ippGetCount(supported
);
6262 for (i
= 0; i
< count
; i
++)
6264 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
6270 respond_unsupported(client
, attr
);
6276 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
6278 const char *sides
= ippGetString(attr
, 0, NULL
);
6279 /* "sides" value... */
6281 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
6283 respond_unsupported(client
, attr
);
6286 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
6288 if (!ippContainsString(supported
, sides
))
6290 respond_unsupported(client
, attr
);
6294 else if (strcmp(sides
, "one-sided"))
6296 respond_unsupported(client
, attr
);
6306 * End of "$Id: ippserver.c 12136 2014-08-29 15:19:40Z msweet $".