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...
35 #include <cups/cups.h> /* Public API */
36 #include <config.h> /* CUPS configuration header */
37 #include <cups/thread-private.h> /* For multithreading functions */
43 #endif /* HAVE_DNSSD */
46 #include <sys/fcntl.h>
48 #ifdef HAVE_SYS_MOUNT_H
49 # include <sys/mount.h>
50 #endif /* HAVE_SYS_MOUNT_H */
51 #ifdef HAVE_SYS_STATFS_H
52 # include <sys/statfs.h>
53 #endif /* HAVE_SYS_STATFS_H */
54 #ifdef HAVE_SYS_STATVFS_H
55 # include <sys/statvfs.h>
56 #endif /* HAVE_SYS_STATVFS_H */
59 #endif /* HAVE_SYS_VFS_H */
66 enum _ipp_preason_e
/* printer-state-reasons bit values */
68 _IPP_PREASON_NONE
= 0x0000, /* none */
69 _IPP_PREASON_OTHER
= 0x0001, /* other */
70 _IPP_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
71 _IPP_PREASON_INPUT_TRAY_MISSING
= 0x0004,
72 /* input-tray-missing */
73 _IPP_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
74 /* marker-supply-empty */
75 _IPP_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
76 /* marker-supply-low */
77 _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
78 /* marker-waste-almost-full */
79 _IPP_PREASON_MARKER_WASTE_FULL
= 0x0040,
80 /* marker-waste-full */
81 _IPP_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
82 _IPP_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
83 _IPP_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
84 _IPP_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
85 _IPP_PREASON_MOVING_TO_PAUSED
= 0x0800,
86 /* moving-to-paused */
87 _IPP_PREASON_PAUSED
= 0x1000, /* paused */
88 _IPP_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
89 _IPP_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
90 _IPP_PREASON_TONER_LOW
= 0x8000 /* toner-low */
92 typedef unsigned int _ipp_preason_t
; /* Bitfield for printer-state-reasons */
94 typedef enum _ipp_media_class_e
96 _IPP_GENERAL
, /* General-purpose size */
97 _IPP_PHOTO_ONLY
, /* Photo-only size */
98 _IPP_ENV_ONLY
/* Envelope-only size */
101 typedef enum _ipp_media_size_e
103 _IPP_MEDIA_SIZE_NONE
= -1,
108 _IPP_MEDIA_SIZE_LEGAL
,
109 _IPP_MEDIA_SIZE_LETTER
,
110 _IPP_MEDIA_SIZE_COM10
,
116 static const char * const media_supported
[] =
117 { /* media-supported values */
118 "iso_a4_210x297mm", /* A4 */
119 "iso_a5_148x210mm", /* A5 */
120 "iso_a6_105x148mm", /* A6 */
121 "iso_dl_110x220mm", /* DL */
122 "na_legal_8.5x14in", /* Legal */
123 "na_letter_8.5x11in", /* Letter */
124 "na_number-10_4.125x9.5in", /* #10 */
125 "na_index-3x5_3x5in", /* 3x5 */
126 "oe_photo-l_3.5x5in", /* L */
127 "na_index-4x6_4x6in", /* 4x6 */
128 "na_5x7_5x7in" /* 5x7 aka 2L */
130 static const int media_col_sizes
[][3] =
131 { /* media-col-database sizes */
132 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
133 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
134 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
135 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
136 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
137 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
138 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
139 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
140 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
141 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
142 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
145 typedef enum _ipp_media_source_e
147 _IPP_MEDIA_SOURCE_NONE
= -1,
148 _IPP_MEDIA_SOURCE_AUTO
,
149 _IPP_MEDIA_SOURCE_MAIN
,
150 _IPP_MEDIA_SOURCE_MANUAL
,
151 _IPP_MEDIA_SOURCE_ENVELOPE
,
152 _IPP_MEDIA_SOURCE_PHOTO
153 } _ipp_media_source_t
;
154 static const char * const media_source_supported
[] =
155 /* media-source-supported values */
164 typedef enum _ipp_media_type_e
166 _IPP_MEDIA_TYPE_NONE
= -1,
167 _IPP_MEDIA_TYPE_AUTO
,
168 _IPP_MEDIA_TYPE_CARDSTOCK
,
169 _IPP_MEDIA_TYPE_ENVELOPE
,
170 _IPP_MEDIA_TYPE_LABELS
,
171 _IPP_MEDIA_TYPE_OTHER
,
172 _IPP_MEDIA_TYPE_GLOSSY
,
173 _IPP_MEDIA_TYPE_HIGH_GLOSS
,
174 _IPP_MEDIA_TYPE_MATTE
,
175 _IPP_MEDIA_TYPE_SATIN
,
176 _IPP_MEDIA_TYPE_SEMI_GLOSS
,
177 _IPP_MEDIA_TYPE_STATIONERY
,
178 _IPP_MEDIA_TYPE_LETTERHEAD
,
179 _IPP_MEDIA_TYPE_TRANSPARENCY
181 static const char * const media_type_supported
[] =
182 /* media-type-supported values */
189 "photographic-glossy",
190 "photographic-high-gloss",
191 "photographic-matte",
192 "photographic-satin",
193 "photographic-semi-gloss",
195 "stationery-letterhead",
199 typedef enum _ipp_supply_e
201 _IPP_SUPPLY_CYAN
, /* Cyan Toner */
202 _IPP_SUPPLY_MAGENTA
, /* Magenta Toner */
203 _IPP_SUPPLY_YELLOW
, /* Yellow Toner */
204 _IPP_SUPPLY_BLACK
, /* Black Toner */
205 _IPP_SUPPLY_WASTE
/* Waste Toner */
207 static const char * const printer_supplies
[] =
208 { /* printer-supply-description values */
221 typedef struct _ipp_filter_s
/**** Attribute filter ****/
223 cups_array_t
*ra
; /* Requested attributes */
224 ipp_tag_t group_tag
; /* Group to copy */
227 typedef struct _ipp_job_s _ipp_job_t
;
229 typedef struct _ipp_printer_s
/**** Printer data ****/
231 int ipv4
, /* IPv4 listener */
232 ipv6
; /* IPv6 listener */
234 DNSServiceRef common_ref
, /* Shared service connection */
235 ipp_ref
, /* Bonjour IPP service */
237 ipps_ref
, /* Bonjour IPPS service */
238 # endif /* HAVE_SSL */
239 http_ref
, /* Bonjour HTTP service */
240 printer_ref
; /* Bonjour LPD service */
241 TXTRecordRef ipp_txt
; /* Bonjour IPP TXT record */
242 char *dnssd_name
; /* printer-dnssd-name */
243 #endif /* HAVE_DNSSD */
244 char *name
, /* printer-name */
245 *icon
, /* Icon filename */
246 *directory
, /* Spool directory */
247 *hostname
, /* Hostname */
248 *uri
, /* printer-uri-supported */
249 *command
; /* Command to run with job file */
251 size_t urilen
; /* Length of printer URI */
252 ipp_t
*attrs
; /* Static attributes */
253 time_t start_time
; /* Startup time */
254 time_t config_time
; /* printer-config-change-time */
255 ipp_pstate_t state
; /* printer-state value */
256 _ipp_preason_t state_reasons
; /* printer-state-reasons values */
257 time_t state_time
; /* printer-state-change-time */
258 cups_array_t
*jobs
; /* Jobs */
259 _ipp_job_t
*active_job
; /* Current active/pending job */
260 int next_job_id
; /* Next job-id value */
261 _cups_rwlock_t rwlock
; /* Printer lock */
262 _ipp_media_size_t main_size
; /* Ready media */
263 _ipp_media_type_t main_type
;
265 _ipp_media_size_t envelope_size
;
267 _ipp_media_size_t photo_size
;
268 _ipp_media_type_t photo_type
;
270 int supplies
[5]; /* Supply levels (0-100) */
273 struct _ipp_job_s
/**** Job data ****/
276 const char *name
, /* job-name */
277 *username
, /* job-originating-user-name */
278 *format
; /* document-format */
279 ipp_jstate_t state
; /* job-state value */
280 time_t created
, /* time-at-creation value */
281 processing
, /* time-at-processing value */
282 completed
; /* time-at-completed value */
283 int impressions
, /* job-impressions value */
284 impcompleted
; /* job-impressions-completed value */
285 ipp_t
*attrs
; /* Static attributes */
286 int cancel
; /* Non-zero when job canceled */
287 char *filename
; /* Print file name */
288 int fd
; /* Print file descriptor */
289 _ipp_printer_t
*printer
; /* Printer */
292 typedef struct _ipp_client_s
/**** Client data ****/
294 http_t
*http
; /* HTTP connection */
295 ipp_t
*request
, /* IPP request */
296 *response
; /* IPP response */
297 time_t start
; /* Request start time */
298 http_state_t operation
; /* Request operation */
299 ipp_op_t operation_id
; /* IPP operation-id */
300 char uri
[1024], /* Request URI */
301 *options
; /* URI options */
302 http_addr_t addr
; /* Client address */
303 char hostname
[256]; /* Client hostname */
304 _ipp_printer_t
*printer
; /* Printer */
305 _ipp_job_t
*job
; /* Current job, if any */
313 static void clean_jobs(_ipp_printer_t
*printer
);
314 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
315 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
316 ipp_tag_t group_tag
, int quickcopy
);
317 static void copy_job_attributes(_ipp_client_t
*client
,
318 _ipp_job_t
*job
, cups_array_t
*ra
);
319 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
320 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
321 static int create_listener(int family
, int *port
);
322 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
323 static ipp_t
*create_media_size(int width
, int length
);
324 static _ipp_printer_t
*create_printer(const char *servername
,
325 const char *name
, const char *location
,
326 const char *make
, const char *model
,
328 const char *docformats
, int ppm
,
329 int ppm_color
, int duplex
, int port
,
333 #endif /* HAVE_DNSSD */
334 const char *directory
,
335 const char *command
);
336 static void debug_attributes(const char *title
, ipp_t
*ipp
,
338 static void delete_client(_ipp_client_t
*client
);
339 static void delete_job(_ipp_job_t
*job
);
340 static void delete_printer(_ipp_printer_t
*printer
);
342 static void dnssd_callback(DNSServiceRef sdRef
,
343 DNSServiceFlags flags
,
344 DNSServiceErrorType errorCode
,
348 _ipp_printer_t
*printer
);
349 #endif /* HAVE_DNSSD */
350 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
351 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
352 static void html_escape(_ipp_client_t
*client
, const char *s
,
354 static void html_footer(_ipp_client_t
*client
);
355 static void html_header(_ipp_client_t
*client
, const char *title
);
356 static void html_printf(_ipp_client_t
*client
, const char *format
,
357 ...) __attribute__((__format__(__printf__
,
359 static void ipp_cancel_job(_ipp_client_t
*client
);
360 static void ipp_close_job(_ipp_client_t
*client
);
361 static void ipp_create_job(_ipp_client_t
*client
);
362 static void ipp_get_job_attributes(_ipp_client_t
*client
);
363 static void ipp_get_jobs(_ipp_client_t
*client
);
364 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
365 static void ipp_identify_printer(_ipp_client_t
*client
);
366 static void ipp_print_job(_ipp_client_t
*client
);
367 static void ipp_print_uri(_ipp_client_t
*client
);
368 static void ipp_send_document(_ipp_client_t
*client
);
369 static void ipp_send_uri(_ipp_client_t
*client
);
370 static void ipp_validate_job(_ipp_client_t
*client
);
371 static int parse_options(_ipp_client_t
*client
, cups_option_t
**options
);
372 static void *process_client(_ipp_client_t
*client
);
373 static int process_http(_ipp_client_t
*client
);
374 static int process_ipp(_ipp_client_t
*client
);
375 static void *process_job(_ipp_job_t
*job
);
377 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
);
378 #endif /* HAVE_DNSSD */
379 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
380 const char *content_coding
,
381 const char *type
, size_t length
);
382 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
383 const char *message
, ...)
384 __attribute__ ((__format__ (__printf__
, 3, 4)));
385 static void respond_unsupported(_ipp_client_t
*client
,
386 ipp_attribute_t
*attr
);
387 static void run_printer(_ipp_printer_t
*printer
);
388 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
389 static void usage(int status
) __attribute__((noreturn
));
390 static int valid_doc_attributes(_ipp_client_t
*client
);
391 static int valid_job_attributes(_ipp_client_t
*client
);
398 static int KeepFiles
= 0,
403 * 'main()' - Main entry to the sample server.
406 int /* O - Exit status */
407 main(int argc
, /* I - Number of command-line args */
408 char *argv
[]) /* I - Command-line arguments */
410 int i
; /* Looping var */
411 const char *opt
, /* Current option character */
412 *command
= NULL
, /* Command to run with job files */
413 *servername
= NULL
, /* Server host name */
414 *name
= NULL
, /* Printer name */
415 *location
= "", /* Location of printer */
416 *make
= "Test", /* Manufacturer */
417 *model
= "Printer", /* Model */
418 *icon
= "printer.png", /* Icon file */
419 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
420 /* Supported formats */
422 const char *keypath
= NULL
; /* Keychain path */
423 #endif /* HAVE_SSL */
425 const char *subtype
= "_print"; /* Bonjour service subtype */
426 #endif /* HAVE_DNSSD */
427 int port
= 8631, /* Port number (0 = auto) */
428 duplex
= 0, /* Duplex mode */
429 ppm
= 10, /* Pages per minute for mono */
430 ppm_color
= 0, /* Pages per minute for color */
431 pin
= 0; /* PIN printing mode? */
432 char directory
[1024] = ""; /* Spool directory */
433 _ipp_printer_t
*printer
; /* Printer object */
437 * Parse command-line arguments...
440 for (i
= 1; i
< argc
; i
++)
441 if (argv
[i
][0] == '-')
443 for (opt
= argv
[i
] + 1; *opt
; opt
++)
447 case '2' : /* -2 (enable 2-sided printing) */
452 case 'K' : /* -K keypath */
458 #endif /* HAVE_SSL */
460 case 'M' : /* -M manufacturer */
467 case 'P' : /* -P (PIN printing mode) */
471 case 'c' : /* -c command */
479 case 'd' : /* -d spool-directory */
483 strlcpy(directory
, argv
[i
], sizeof(directory
));
486 case 'f' : /* -f type/subtype[,...] */
493 case 'h' : /* -h (show help) */
496 case 'i' : /* -i icon.png */
503 case 'k' : /* -k (keep files) */
507 case 'l' : /* -l location */
514 case 'm' : /* -m model */
521 case 'n' : /* -n hostname */
525 servername
= argv
[i
];
528 case 'p' : /* -p port */
530 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
532 port
= atoi(argv
[i
]);
536 case 'r' : /* -r subtype */
542 #endif /* HAVE_DNSSD */
544 case 's' : /* -s speed[,color-speed] */
548 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
552 case 'v' : /* -v (be verbose) */
556 default : /* Unknown */
557 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
568 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
576 * Apply defaults as needed...
581 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
583 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
585 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
586 directory
, strerror(errno
));
591 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
595 cupsSetServerCredentials(keypath
, servername
, 1);
596 #endif /* HAVE_SSL */
599 * Create the printer...
602 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
603 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
606 #endif /* HAVE_DNSSD */
607 directory
, command
)) == NULL
)
611 * Run the print service...
614 run_printer(printer
);
617 * Destroy the printer and exit...
620 delete_printer(printer
);
627 * 'clean_jobs()' - Clean out old (completed) jobs.
631 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
633 _ipp_job_t
*job
; /* Current job */
634 time_t cleantime
; /* Clean time */
637 if (cupsArrayCount(printer
->jobs
) == 0)
640 cleantime
= time(NULL
) - 60;
642 _cupsRWLockWrite(&(printer
->rwlock
));
643 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
645 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
646 if (job
->completed
&& job
->completed
< cleantime
)
648 cupsArrayRemove(printer
->jobs
, job
);
653 _cupsRWUnlock(&(printer
->rwlock
));
658 * 'compare_jobs()' - Compare two jobs.
661 static int /* O - Result of comparison */
662 compare_jobs(_ipp_job_t
*a
, /* I - First job */
663 _ipp_job_t
*b
) /* I - Second job */
665 return (b
->id
- a
->id
);
670 * 'copy_attributes()' - Copy attributes from one request to another.
674 copy_attributes(ipp_t
*to
, /* I - Destination request */
675 ipp_t
*from
, /* I - Source request */
676 cups_array_t
*ra
, /* I - Requested attributes */
677 ipp_tag_t group_tag
, /* I - Group to copy */
678 int quickcopy
) /* I - Do a quick copy? */
680 _ipp_filter_t filter
; /* Filter data */
684 filter
.group_tag
= group_tag
;
686 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
691 * 'copy_job_attrs()' - Copy job attributes to the response.
696 _ipp_client_t
*client
, /* I - Client */
697 _ipp_job_t
*job
, /* I - Job */
698 cups_array_t
*ra
) /* I - requested-attributes */
700 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
702 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
705 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
707 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
710 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
713 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
715 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
718 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
719 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
721 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
722 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
724 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
725 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
727 if (!ra
|| cupsArrayFind(ra
, "job-state"))
728 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
729 "job-state", job
->state
);
731 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
735 case IPP_JSTATE_PENDING
:
736 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
739 case IPP_JSTATE_HELD
:
741 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
742 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
743 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
745 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
748 case IPP_JSTATE_PROCESSING
:
750 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
752 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
755 case IPP_JSTATE_STOPPED
:
756 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
759 case IPP_JSTATE_CANCELED
:
760 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
763 case IPP_JSTATE_ABORTED
:
764 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
767 case IPP_JSTATE_COMPLETED
:
768 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
773 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
777 case IPP_JSTATE_PENDING
:
778 ippAddString(client
->response
, IPP_TAG_JOB
,
779 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
783 case IPP_JSTATE_HELD
:
785 ippAddString(client
->response
, IPP_TAG_JOB
,
786 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
787 "job-state-reasons", NULL
, "job-incoming");
788 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
789 ippAddString(client
->response
, IPP_TAG_JOB
,
790 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
791 "job-state-reasons", NULL
, "job-hold-until-specified");
793 ippAddString(client
->response
, IPP_TAG_JOB
,
794 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
795 "job-state-reasons", NULL
, "job-data-insufficient");
798 case IPP_JSTATE_PROCESSING
:
800 ippAddString(client
->response
, IPP_TAG_JOB
,
801 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
802 "job-state-reasons", NULL
, "processing-to-stop-point");
804 ippAddString(client
->response
, IPP_TAG_JOB
,
805 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
806 "job-state-reasons", NULL
, "job-printing");
809 case IPP_JSTATE_STOPPED
:
810 ippAddString(client
->response
, IPP_TAG_JOB
,
811 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
812 NULL
, "job-stopped");
815 case IPP_JSTATE_CANCELED
:
816 ippAddString(client
->response
, IPP_TAG_JOB
,
817 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
818 NULL
, "job-canceled-by-user");
821 case IPP_JSTATE_ABORTED
:
822 ippAddString(client
->response
, IPP_TAG_JOB
,
823 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
824 NULL
, "aborted-by-system");
827 case IPP_JSTATE_COMPLETED
:
828 ippAddString(client
->response
, IPP_TAG_JOB
,
829 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
830 NULL
, "job-completed-successfully");
835 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
836 ippAddInteger(client
->response
, IPP_TAG_JOB
,
837 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
838 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
840 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
841 ippAddInteger(client
->response
, IPP_TAG_JOB
,
842 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
843 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
848 * 'create_client()' - Accept a new network connection and create a client
852 static _ipp_client_t
* /* O - Client */
853 create_client(_ipp_printer_t
*printer
, /* I - Printer */
854 int sock
) /* I - Listen socket */
856 _ipp_client_t
*client
; /* Client */
859 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
861 perror("Unable to allocate memory for client");
865 client
->printer
= printer
;
868 * Accept the client and get the remote address...
871 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
873 perror("Unable to accept client connection");
880 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
883 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
890 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
894 static _ipp_job_t
* /* O - Job */
895 create_job(_ipp_client_t
*client
) /* I - Client */
897 _ipp_job_t
*job
; /* Job */
898 ipp_attribute_t
*attr
; /* Job attribute */
899 char uri
[1024], /* job-uri value */
900 uuid
[64]; /* job-uuid value */
903 _cupsRWLockWrite(&(client
->printer
->rwlock
));
904 if (client
->printer
->active_job
&&
905 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
908 * Only accept a single job at a time...
911 _cupsRWLockWrite(&(client
->printer
->rwlock
));
916 * Allocate and initialize the job object...
919 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
921 perror("Unable to allocate memory for job");
925 job
->printer
= client
->printer
;
926 job
->attrs
= ippNew();
927 job
->state
= IPP_JSTATE_HELD
;
931 * Copy all of the job attributes...
934 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
937 * Get the requesting-user-name, document format, and priority...
940 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
941 job
->username
= ippGetString(attr
, 0, NULL
);
943 job
->username
= "anonymous";
945 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
947 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
949 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
950 job
->format
= ippGetString(attr
, 0, NULL
);
951 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
952 job
->format
= ippGetString(attr
, 0, NULL
);
954 job
->format
= "application/octet-stream";
957 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
958 job
->impressions
= ippGetInteger(attr
, 0);
960 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
961 job
->name
= ippGetString(attr
, 0, NULL
);
964 * Add job description attributes and add to the jobs array...
967 job
->id
= client
->printer
->next_job_id
++;
969 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
970 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
972 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
973 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
974 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
975 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
976 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
977 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
979 cupsArrayAdd(client
->printer
->jobs
, job
);
980 client
->printer
->active_job
= job
;
982 _cupsRWUnlock(&(client
->printer
->rwlock
));
989 * 'create_job_filename()' - Create the filename for a document in a job.
992 static void create_job_filename(
993 _ipp_printer_t
*printer
, /* I - Printer */
994 _ipp_job_t
*job
, /* I - Job */
995 char *fname
, /* I - Filename buffer */
996 size_t fnamesize
) /* I - Size of filename buffer */
998 char name
[256], /* "Safe" filename */
999 *nameptr
; /* Pointer into filename */
1000 const char *ext
, /* Filename extension */
1001 *job_name
; /* job-name value */
1002 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1006 * Make a name from the job-name attribute...
1009 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1010 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1012 job_name
= "untitled";
1014 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1015 if (isalnum(*job_name
& 255) || *job_name
== '-')
1016 *nameptr
++ = (char)tolower(*job_name
& 255);
1023 * Figure out the extension...
1026 if (!strcasecmp(job
->format
, "image/jpeg"))
1028 else if (!strcasecmp(job
->format
, "image/png"))
1030 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1032 else if (!strcasecmp(job
->format
, "image/urf"))
1034 else if (!strcasecmp(job
->format
, "application/pdf"))
1036 else if (!strcasecmp(job
->format
, "application/postscript"))
1042 * Create a filename with the job-id, job-name, and document-format (extension)...
1045 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1050 * 'create_listener()' - Create a listener socket.
1053 static int /* O - Listener socket or -1 on error */
1054 create_listener(int family
, /* I - Address family */
1055 int *port
) /* IO - Port number */
1057 int sock
; /* Listener socket */
1058 http_addrlist_t
*addrlist
; /* Listen address */
1059 char service
[255]; /* Service port */
1064 *port
= 8000 + (getuid() % 1000);
1065 fprintf(stderr
, "Listening on port %d.\n", *port
);
1068 snprintf(service
, sizeof(service
), "%d", *port
);
1069 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1072 sock
= httpAddrListen(&(addrlist
->addr
), *port
);
1074 httpAddrFreeList(addrlist
);
1081 * 'create_media_col()' - Create a media-col value.
1084 static ipp_t
* /* O - media-col collection */
1085 create_media_col(const char *media
, /* I - Media name */
1086 const char *source
, /* I - Media source */
1087 const char *type
, /* I - Media type */
1088 int width
, /* I - x-dimension in 2540ths */
1089 int length
, /* I - y-dimension in 2540ths */
1090 int margins
) /* I - Value for margins */
1092 ipp_t
*media_col
= ippNew(), /* media-col value */
1093 *media_size
= create_media_size(width
, length
);
1094 /* media-size value */
1095 char media_key
[256]; /* media-key value */
1099 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1101 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1103 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1105 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1107 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1109 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1110 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1111 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1112 "media-bottom-margin", margins
);
1113 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1114 "media-left-margin", margins
);
1115 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1116 "media-right-margin", margins
);
1117 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1118 "media-top-margin", margins
);
1120 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1122 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1124 ippDelete(media_size
);
1131 * 'create_media_size()' - Create a media-size value.
1134 static ipp_t
* /* O - media-col collection */
1135 create_media_size(int width
, /* I - x-dimension in 2540ths */
1136 int length
) /* I - y-dimension in 2540ths */
1138 ipp_t
*media_size
= ippNew(); /* media-size value */
1141 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1143 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1146 return (media_size
);
1151 * 'create_printer()' - Create, register, and listen for connections to a
1155 static _ipp_printer_t
* /* O - Printer */
1156 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1157 const char *name
, /* I - printer-name */
1158 const char *location
, /* I - printer-location */
1159 const char *make
, /* I - printer-make-and-model */
1160 const char *model
, /* I - printer-make-and-model */
1161 const char *icon
, /* I - printer-icons */
1162 const char *docformats
, /* I - document-format-supported */
1163 int ppm
, /* I - Pages per minute in grayscale */
1164 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1165 int duplex
, /* I - 1 = duplex, 0 = simplex */
1166 int port
, /* I - Port for listeners or 0 for auto */
1167 int pin
, /* I - Require PIN printing */
1169 const char *subtype
, /* I - Bonjour service subtype */
1170 #endif /* HAVE_DNSSD */
1171 const char *directory
, /* I - Spool directory */
1172 const char *command
) /* I - Command to run on job files */
1174 int i
, j
; /* Looping vars */
1175 _ipp_printer_t
*printer
; /* Printer */
1176 char hostname
[256], /* Hostname */
1177 uri
[1024], /* Printer URI */
1178 icons
[1024], /* printer-icons URI */
1179 adminurl
[1024], /* printer-more-info URI */
1180 supplyurl
[1024],/* printer-supply-info-uri URI */
1181 device_id
[1024],/* printer-device-id */
1182 make_model
[128],/* printer-make-and-model */
1183 uuid
[128]; /* printer-uuid */
1184 int num_formats
; /* Number of document-format-supported values */
1185 char *defformat
, /* document-format-default value */
1186 *formats
[100], /* document-format-supported values */
1187 *ptr
; /* Pointer into string */
1188 const char *prefix
; /* Prefix string */
1189 int num_database
; /* Number of database values */
1190 ipp_attribute_t
*media_col_database
,
1191 /* media-col-database value */
1192 *media_size_supported
;
1193 /* media-size-supported value */
1194 ipp_t
*media_col_default
;
1195 /* media-col-default value */
1196 int media_col_index
;/* Current media-col-database value */
1197 int k_supported
; /* Maximum file size supported */
1199 struct statvfs spoolinfo
; /* FS info for spool directory */
1200 double spoolsize
; /* FS size */
1201 #elif defined(HAVE_STATFS)
1202 struct statfs spoolinfo
; /* FS info for spool directory */
1203 double spoolsize
; /* FS size */
1204 #endif /* HAVE_STATVFS */
1205 static const int orients
[4] = /* orientation-requested-supported values */
1207 IPP_ORIENT_PORTRAIT
,
1208 IPP_ORIENT_LANDSCAPE
,
1209 IPP_ORIENT_REVERSE_LANDSCAPE
,
1210 IPP_ORIENT_REVERSE_PORTRAIT
1212 static const char * const versions
[] =/* ipp-versions-supported values */
1218 static const char * const features
[] =/* ipp-features-supported values */
1222 static const int ops
[] = /* operations-supported values */
1226 IPP_OP_VALIDATE_JOB
,
1228 IPP_OP_SEND_DOCUMENT
,
1231 IPP_OP_GET_JOB_ATTRIBUTES
,
1233 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1234 IPP_OP_CANCEL_MY_JOBS
,
1236 IPP_OP_IDENTIFY_PRINTER
1238 static const char * const charsets
[] =/* charset-supported values */
1243 static const char * const compressions
[] =/* compression-supported values */
1248 #endif /* HAVE_LIBZ */
1251 static const char * const identify_actions
[] =
1256 static const char * const job_creation
[] =
1257 { /* job-creation-attributes-supported values */
1259 "ipp-attribute-fidelity",
1261 "job-accounting-user-id",
1267 "multiple-document-handling",
1268 "orientation-requested",
1272 static const char * const media_col_supported
[] =
1273 { /* media-col-supported values */
1274 "media-bottom-margin",
1275 "media-left-margin",
1276 "media-right-margin",
1282 static const int media_xxx_margin_supported
[] =
1283 { /* media-xxx-margin-supported values */
1287 static const char * const multiple_document_handling
[] =
1288 { /* multiple-document-handling-supported values */
1289 "separate-documents-uncollated-copies",
1290 "separate-documents-collated-copies"
1292 static const char * const overrides
[] =
1293 { /* overrides-supported */
1297 static const char * const print_color_mode_supported
[] =
1298 { /* print-color-mode-supported values */
1303 static const int print_quality_supported
[] =
1304 { /* print-quality-supported values */
1309 static const int pwg_raster_document_resolution_supported
[] =
1315 static const char * const pwg_raster_document_type_supported
[] =
1323 static const char * const reference_uri_schemes_supported
[] =
1324 { /* reference-uri-schemes-supported */
1330 #endif /* HAVE_SSL */
1332 static const char * const sides_supported
[] =
1333 { /* sides-supported values */
1335 "two-sided-long-edge",
1336 "two-sided-short-edge"
1338 static const char * const urf_supported
[] =
1339 { /* urf-supported values */
1342 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1349 static const char * const which_jobs
[] =
1350 { /* which-jobs-supported values */
1359 "processing-stopped"
1364 * Allocate memory for the printer...
1367 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1369 perror("Unable to allocate memory for printer");
1375 printer
->name
= strdup(name
);
1377 printer
->dnssd_name
= strdup(printer
->name
);
1378 #endif /* HAVE_DNSSD */
1379 printer
->command
= command
? strdup(command
) : NULL
;
1380 printer
->directory
= strdup(directory
);
1381 printer
->hostname
= strdup(servername
? servername
: httpGetHostname(NULL
, hostname
, sizeof(hostname
)));
1382 printer
->port
= port
;
1383 printer
->start_time
= time(NULL
);
1384 printer
->config_time
= printer
->start_time
;
1385 printer
->state
= IPP_PSTATE_IDLE
;
1386 printer
->state_reasons
= _IPP_PREASON_NONE
;
1387 printer
->state_time
= printer
->start_time
;
1388 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1389 printer
->next_job_id
= 1;
1391 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1392 printer
->hostname
, printer
->port
, "/ipp/print");
1393 printer
->uri
= strdup(uri
);
1394 printer
->urilen
= strlen(uri
);
1397 printer
->icon
= strdup(icon
);
1399 printer
->main_size
= _IPP_MEDIA_SIZE_A4
;
1400 printer
->main_type
= _IPP_MEDIA_TYPE_STATIONERY
;
1401 printer
->main_level
= 500;
1403 printer
->envelope_size
= _IPP_MEDIA_SIZE_NONE
;
1404 printer
->envelope_level
= 0;
1406 printer
->photo_size
= _IPP_MEDIA_SIZE_NONE
;
1407 printer
->photo_type
= _IPP_MEDIA_TYPE_NONE
;
1408 printer
->photo_level
= 0;
1410 printer
->supplies
[_IPP_SUPPLY_CYAN
] = 100;
1411 printer
->supplies
[_IPP_SUPPLY_MAGENTA
] = 100;
1412 printer
->supplies
[_IPP_SUPPLY_YELLOW
] = 100;
1413 printer
->supplies
[_IPP_SUPPLY_BLACK
] = 100;
1414 printer
->supplies
[_IPP_SUPPLY_WASTE
] = 0;
1416 _cupsRWInit(&(printer
->rwlock
));
1419 * Create the listener sockets...
1422 if ((printer
->ipv4
= create_listener(AF_INET
, &(printer
->port
))) < 0)
1424 perror("Unable to create IPv4 listener");
1428 if ((printer
->ipv6
= create_listener(AF_INET6
, &(printer
->port
))) < 0)
1430 perror("Unable to create IPv6 listener");
1435 * Prepare values for the printer attributes...
1438 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1439 printer
->hostname
, printer
->port
, "/icon.png");
1440 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/");
1441 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/supplies");
1445 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1446 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1447 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1450 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1453 formats
[0] = strdup(docformats
);
1454 defformat
= formats
[0];
1455 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1458 formats
[num_formats
++] = ptr
;
1460 if (!strcasecmp(ptr
, "application/octet-stream"))
1464 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1465 ptr
= device_id
+ strlen(device_id
);
1467 for (i
= 0; i
< num_formats
; i
++)
1469 if (!strcasecmp(formats
[i
], "application/pdf"))
1470 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1471 else if (!strcasecmp(formats
[i
], "application/postscript"))
1472 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1473 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1474 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1475 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1476 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1477 else if (!strcasecmp(formats
[i
], "image/png"))
1478 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1479 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1480 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1485 strlcat(device_id
, ";", sizeof(device_id
));
1488 * Get the maximum spool size based on the size of the filesystem used for
1489 * the spool directory. If the host OS doesn't support the statfs call
1490 * or the filesystem is larger than 2TiB, always report INT_MAX.
1494 if (statvfs(printer
->directory
, &spoolinfo
))
1495 k_supported
= INT_MAX
;
1496 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1497 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1498 k_supported
= INT_MAX
;
1500 k_supported
= (int)spoolsize
;
1502 #elif defined(HAVE_STATFS)
1503 if (statfs(printer
->directory
, &spoolinfo
))
1504 k_supported
= INT_MAX
;
1505 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1506 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1507 k_supported
= INT_MAX
;
1509 k_supported
= (int)spoolsize
;
1512 k_supported
= INT_MAX
;
1513 #endif /* HAVE_STATVFS */
1516 * Create the printer attributes. This list of attributes is sorted to improve
1517 * performance when the client provides a requested-attributes attribute...
1520 printer
->attrs
= ippNew();
1522 /* charset-configured */
1523 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1524 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1525 "charset-configured", NULL
, "utf-8");
1527 /* charset-supported */
1528 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1529 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1530 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1533 /* color-supported */
1534 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1537 /* compression-supported */
1538 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1539 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1540 "compression-supported",
1541 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1544 /* copies-default */
1545 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1546 "copies-default", 1);
1548 /* copies-supported */
1549 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1551 /* document-format-default */
1552 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1553 "document-format-default", NULL
, defformat
);
1555 /* document-format-supported */
1556 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1557 "document-format-supported", num_formats
, NULL
,
1558 (const char * const *)formats
);
1560 /* document-password-supported */
1561 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1563 /* finishings-default */
1564 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1565 "finishings-default", IPP_FINISHINGS_NONE
);
1567 /* finishings-supported */
1568 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1569 "finishings-supported", IPP_FINISHINGS_NONE
);
1571 /* generated-natural-language-supported */
1572 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1573 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1574 "generated-natural-language-supported", NULL
, "en");
1576 /* identify-actions-default */
1577 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1579 /* identify-actions-supported */
1580 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
);
1582 /* ipp-features-supported */
1583 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1585 /* ipp-versions-supported */
1586 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1588 /* job-account-id-default */
1589 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1591 /* job-account-id-supported */
1592 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1594 /* job-accounting-user-id-default */
1595 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1597 /* job-accounting-user-id-supported */
1598 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1600 /* job-creation-attributes-supported */
1601 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
);
1603 /* job-ids-supported */
1604 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1606 /* job-k-octets-supported */
1607 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1610 /* job-password-supported */
1611 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1612 "job-password-supported", 4);
1614 /* job-preferred-attributes-supported */
1615 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-preferred-attributes-supported", 0);
1617 /* job-priority-default */
1618 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1619 "job-priority-default", 50);
1621 /* job-priority-supported */
1622 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1623 "job-priority-supported", 100);
1625 /* job-sheets-default */
1626 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1627 IPP_CONST_TAG(IPP_TAG_NAME
),
1628 "job-sheets-default", NULL
, "none");
1630 /* job-sheets-supported */
1631 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1632 IPP_CONST_TAG(IPP_TAG_NAME
),
1633 "job-sheets-supported", NULL
, "none");
1635 /* media-bottom-margin-supported */
1636 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1637 "media-bottom-margin-supported",
1638 (int)(sizeof(media_xxx_margin_supported
) /
1639 sizeof(media_xxx_margin_supported
[0])),
1640 media_xxx_margin_supported
);
1642 /* media-col-database */
1643 for (num_database
= 0, i
= 0;
1644 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1647 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1648 num_database
+= 3; /* auto + manual + envelope */
1649 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1650 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1652 num_database
+= 2; /* Regular + borderless */
1655 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1656 "media-col-database", num_database
,
1658 for (media_col_index
= 0, i
= 0;
1659 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1662 switch (media_col_sizes
[i
][2])
1666 * Regular + borderless for the general class; no source/type
1670 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]));
1671 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]));
1674 case _IPP_ENV_ONLY
:
1676 * Regular margins for "auto", "manual", and "envelope" sources.
1679 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]));
1680 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]));
1681 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]));
1683 case _IPP_PHOTO_ONLY
:
1685 * Photos have specific media types and can only be printed via
1686 * the auto, manual, and photo sources...
1690 j
< (int)(sizeof(media_type_supported
) /
1691 sizeof(media_type_supported
[0]));
1694 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1697 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]));
1698 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]));
1699 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]));
1705 /* media-col-default */
1706 media_col_default
= create_media_col(media_supported
[0],
1707 media_source_supported
[0],
1708 media_type_supported
[0],
1709 media_col_sizes
[0][0],
1710 media_col_sizes
[0][1],
1711 media_xxx_margin_supported
[1]);
1713 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1715 ippDelete(media_col_default
);
1717 /* media-col-supported */
1718 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1719 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1720 "media-col-supported",
1721 (int)(sizeof(media_col_supported
) /
1722 sizeof(media_col_supported
[0])), NULL
,
1723 media_col_supported
);
1726 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1727 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1728 "media-default", NULL
, media_supported
[0]);
1730 /* media-left-margin-supported */
1731 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1732 "media-left-margin-supported",
1733 (int)(sizeof(media_xxx_margin_supported
) /
1734 sizeof(media_xxx_margin_supported
[0])),
1735 media_xxx_margin_supported
);
1737 /* media-right-margin-supported */
1738 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1739 "media-right-margin-supported",
1740 (int)(sizeof(media_xxx_margin_supported
) /
1741 sizeof(media_xxx_margin_supported
[0])),
1742 media_xxx_margin_supported
);
1744 /* media-supported */
1745 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1746 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1748 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1749 NULL
, media_supported
);
1751 /* media-size-supported */
1752 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1753 "media-size-supported",
1754 (int)(sizeof(media_col_sizes
) /
1755 sizeof(media_col_sizes
[0])),
1758 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1760 ippSetCollection(printer
->attrs
, &media_size_supported
, i
,
1761 create_media_size(media_col_sizes
[i
][0],
1762 media_col_sizes
[i
][1]));
1764 /* media-source-supported */
1765 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
);
1767 /* media-top-margin-supported */
1768 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1769 "media-top-margin-supported",
1770 (int)(sizeof(media_xxx_margin_supported
) /
1771 sizeof(media_xxx_margin_supported
[0])),
1772 media_xxx_margin_supported
);
1774 /* media-type-supported */
1775 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
);
1777 /* multiple-document-handling-supported */
1778 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
);
1780 /* multiple-document-jobs-supported */
1781 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1783 /* multiple-operation-time-out */
1784 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1786 /* multiple-operation-time-out-action */
1787 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1789 /* natural-language-configured */
1790 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1791 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1792 "natural-language-configured", NULL
, "en");
1794 /* number-up-default */
1795 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1796 "number-up-default", 1);
1798 /* number-up-supported */
1799 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1800 "number-up-supported", 1);
1802 /* operations-supported */
1803 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1804 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1806 /* orientation-requested-default */
1807 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1808 "orientation-requested-default", 0);
1810 /* orientation-requested-supported */
1811 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1812 "orientation-requested-supported", 4, orients
);
1814 /* output-bin-default */
1815 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1816 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1817 "output-bin-default", NULL
, "face-down");
1819 /* output-bin-supported */
1820 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1821 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1822 "output-bin-supported", NULL
, "face-down");
1824 /* overrides-supported */
1825 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1827 /* page-ranges-supported */
1828 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1830 /* pages-per-minute */
1831 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1832 "pages-per-minute", ppm
);
1834 /* pages-per-minute-color */
1836 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1837 "pages-per-minute-color", ppm_color
);
1839 /* pdl-override-supported */
1840 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1841 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1842 "pdl-override-supported", NULL
, "attempted");
1844 /* print-color-mode-default */
1845 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1847 /* print-color-mode-supported */
1848 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
);
1850 /* print-content-optimize-default */
1851 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1853 /* print-content-optimize-supported */
1854 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
1856 /* print-rendering-intent-default */
1857 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
1859 /* print-rendering-intent-supported */
1860 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
1862 /* print-quality-default */
1863 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
1865 /* print-quality-supported */
1866 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
);
1868 /* printer-device-id */
1869 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1870 "printer-device-id", NULL
, device_id
);
1872 /* printer-get-attributes-supported */
1873 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1875 /* printer-geo-location */
1876 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
1879 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1880 "printer-icons", NULL
, icons
);
1882 /* printer-is-accepting-jobs */
1883 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1887 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1890 /* printer-location */
1891 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1892 "printer-location", NULL
, location
);
1894 /* printer-make-and-model */
1895 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1896 "printer-make-and-model", NULL
, make_model
);
1898 /* printer-mandatory-job-attributes */
1901 static const char * const names
[] =
1903 "job-accounting-user-id",
1907 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1908 "printer-mandatory-job-attributes",
1909 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
1912 /* printer-more-info */
1913 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1916 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1919 /* printer-organization */
1920 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
1922 /* printer-organizational-unit */
1923 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
1925 /* printer-resolution-default */
1926 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1927 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1929 /* printer-resolution-supported */
1930 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1931 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1933 /* printer-supply-description */
1934 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
);
1936 /* printer-supply-info-uri */
1937 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
1939 /* printer-uri-supported */
1940 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
1943 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
1944 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
1946 /* pwg-raster-document-xxx-supported */
1947 for (i
= 0; i
< num_formats
; i
++)
1948 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
1951 if (i
< num_formats
)
1953 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
,
1954 "pwg-raster-document-resolution-supported",
1955 (int)(sizeof(pwg_raster_document_resolution_supported
) /
1956 sizeof(pwg_raster_document_resolution_supported
[0])),
1958 pwg_raster_document_resolution_supported
,
1959 pwg_raster_document_resolution_supported
);
1960 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1961 "pwg-raster-document-sheet-back", NULL
, "normal");
1962 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1963 "pwg-raster-document-type-supported",
1964 (int)(sizeof(pwg_raster_document_type_supported
) /
1965 sizeof(pwg_raster_document_type_supported
[0])), NULL
,
1966 pwg_raster_document_type_supported
);
1969 /* reference-uri-scheme-supported */
1970 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1971 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
1972 "reference-uri-schemes-supported",
1973 (int)(sizeof(reference_uri_schemes_supported
) /
1974 sizeof(reference_uri_schemes_supported
[0])),
1975 NULL
, reference_uri_schemes_supported
);
1978 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1979 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1980 "sides-default", NULL
, "one-sided");
1982 /* sides-supported */
1983 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1984 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1985 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
1988 for (i
= 0; i
< num_formats
; i
++)
1989 if (!strcasecmp(formats
[i
], "image/urf"))
1992 if (i
< num_formats
)
1993 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
1995 /* uri-authentication-supported */
1996 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1997 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1998 "uri-authentication-supported", NULL
, "none");
2000 /* uri-security-supported */
2001 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2002 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2003 "uri-security-supported", NULL
, "none");
2005 /* which-jobs-supported */
2006 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2007 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2008 "which-jobs-supported",
2009 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
2013 debug_attributes("Printer", printer
->attrs
, 0);
2017 * Register the printer with Bonjour...
2020 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2022 #endif /* HAVE_DNSSD */
2032 * If we get here we were unable to create the printer...
2037 delete_printer(printer
);
2043 * 'debug_attributes()' - Print attributes in a request or response.
2047 debug_attributes(const char *title
, /* I - Title */
2048 ipp_t
*ipp
, /* I - Request/response */
2049 int type
) /* I - 0 = object, 1 = request, 2 = response */
2051 ipp_tag_t group_tag
; /* Current group */
2052 ipp_attribute_t
*attr
; /* Current attribute */
2053 char buffer
[2048]; /* String buffer for value */
2054 int major
, minor
; /* Version */
2060 fprintf(stderr
, "%s:\n", title
);
2061 major
= ippGetVersion(ipp
, &minor
);
2062 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2064 fprintf(stderr
, " operation-id=%s(%04x)\n",
2065 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2067 fprintf(stderr
, " status-code=%s(%04x)\n",
2068 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2069 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2071 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2073 attr
= ippNextAttribute(ipp
))
2075 if (ippGetGroupTag(attr
) != group_tag
)
2077 group_tag
= ippGetGroupTag(attr
);
2078 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2081 if (ippGetName(attr
))
2083 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2084 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2085 ippGetCount(attr
) > 1 ? "1setOf " : "",
2086 ippTagString(ippGetValueTag(attr
)), buffer
);
2093 * 'delete_client()' - Close the socket and free all memory used by a client
2098 delete_client(_ipp_client_t
*client
) /* I - Client */
2101 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2104 * Flush pending writes before closing...
2107 httpFlushWrite(client
->http
);
2113 httpClose(client
->http
);
2115 ippDelete(client
->request
);
2116 ippDelete(client
->response
);
2123 * 'delete_job()' - Remove from the printer and free all memory used by a job
2128 delete_job(_ipp_job_t
*job
) /* I - Job */
2131 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2133 ippDelete(job
->attrs
);
2138 unlink(job
->filename
);
2140 free(job
->filename
);
2148 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2149 * used by a printer object.
2153 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2155 if (printer
->ipv4
>= 0)
2156 close(printer
->ipv4
);
2158 if (printer
->ipv6
>= 0)
2159 close(printer
->ipv6
);
2162 if (printer
->printer_ref
)
2163 DNSServiceRefDeallocate(printer
->printer_ref
);
2165 if (printer
->ipp_ref
)
2166 DNSServiceRefDeallocate(printer
->ipp_ref
);
2169 if (printer
->ipps_ref
)
2170 DNSServiceRefDeallocate(printer
->ipps_ref
);
2171 # endif /* HAVE_SSL */
2172 if (printer
->http_ref
)
2173 DNSServiceRefDeallocate(printer
->http_ref
);
2175 if (printer
->common_ref
)
2176 DNSServiceRefDeallocate(printer
->common_ref
);
2178 TXTRecordDeallocate(&(printer
->ipp_txt
));
2180 if (printer
->dnssd_name
)
2181 free(printer
->dnssd_name
);
2182 #endif /* HAVE_DNSSD */
2185 free(printer
->name
);
2187 free(printer
->icon
);
2188 if (printer
->command
)
2189 free(printer
->command
);
2190 if (printer
->directory
)
2191 free(printer
->directory
);
2192 if (printer
->hostname
)
2193 free(printer
->hostname
);
2197 ippDelete(printer
->attrs
);
2198 cupsArrayDelete(printer
->jobs
);
2206 * 'dnssd_callback()' - Handle Bonjour registration events.
2211 DNSServiceRef sdRef
, /* I - Service reference */
2212 DNSServiceFlags flags
, /* I - Status flags */
2213 DNSServiceErrorType errorCode
, /* I - Error, if any */
2214 const char *name
, /* I - Service name */
2215 const char *regtype
, /* I - Service type */
2216 const char *domain
, /* I - Domain for service */
2217 _ipp_printer_t
*printer
) /* I - Printer */
2225 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2226 regtype
, (int)errorCode
);
2229 else if (strcasecmp(name
, printer
->dnssd_name
))
2232 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2234 /* No lock needed since only the main thread accesses/changes this */
2235 free(printer
->dnssd_name
);
2236 printer
->dnssd_name
= strdup(name
);
2239 #endif /* HAVE_DNSSD */
2243 * 'filter_cb()' - Filter printer attributes based on the requested array.
2246 static int /* O - 1 to copy, 0 to ignore */
2247 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2248 ipp_t
*dst
, /* I - Destination (unused) */
2249 ipp_attribute_t
*attr
) /* I - Source attribute */
2252 * Filter attributes as needed...
2257 ipp_tag_t group
= ippGetGroupTag(attr
);
2258 const char *name
= ippGetName(attr
);
2260 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
)))
2263 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2268 * 'find_job()' - Find a job specified in a request.
2271 static _ipp_job_t
* /* O - Job or NULL */
2272 find_job(_ipp_client_t
*client
) /* I - Client */
2274 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2275 _ipp_job_t key
, /* Job search key */
2276 *job
; /* Matching job, if any */
2279 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2281 const char *uri
= ippGetString(attr
, 0, NULL
);
2283 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2284 uri
[client
->printer
->urilen
] == '/')
2285 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2289 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2290 key
.id
= ippGetInteger(attr
, 0);
2292 _cupsRWLockRead(&(client
->printer
->rwlock
));
2293 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2294 _cupsRWUnlock(&(client
->printer
->rwlock
));
2301 * 'html_escape()' - Write a HTML-safe string.
2305 html_escape(_ipp_client_t
*client
, /* I - Client */
2306 const char *s
, /* I - String to write */
2307 size_t slen
) /* I - Number of characters to write */
2309 const char *start
, /* Start of segment */
2310 *end
; /* End of string */
2314 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2316 while (*s
&& s
< end
)
2318 if (*s
== '&' || *s
== '<')
2321 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2324 httpWrite2(client
->http
, "&", 5);
2326 httpWrite2(client
->http
, "<", 4);
2335 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2340 * 'html_footer()' - Show the web interface footer.
2342 * This function also writes the trailing 0-length chunk.
2346 html_footer(_ipp_client_t
*client
) /* I - Client */
2352 httpWrite2(client
->http
, "", 0);
2357 * 'html_header()' - Show the web interface header and title.
2361 html_header(_ipp_client_t
*client
, /* I - Client */
2362 const char *title
) /* I - Title */
2368 "<title>%s</title>\n"
2369 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2370 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2371 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2372 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2374 "body { font-family: sans-serif; margin: 0; }\n"
2375 "div.body { padding: 0px 10px 10px; }\n"
2376 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2377 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2378 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2379 "table.form th { text-align: right; }\n"
2380 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2381 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2382 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2383 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2384 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2385 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2386 "table.nav td { margin: 0; text-align: center; }\n"
2387 "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"
2388 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2389 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2390 "td.nav:hover { background: #666; color: #fff; }\n"
2391 "td.nav:active { background: #000; color: #ff0; }\n"
2395 "<table class=\"nav\"><tr>"
2396 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2397 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2398 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2400 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2405 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2409 html_printf(_ipp_client_t
*client
, /* I - Client */
2410 const char *format
, /* I - Printf-style format string */
2411 ...) /* I - Additional arguments as needed */
2413 va_list ap
; /* Pointer to arguments */
2414 const char *start
; /* Start of string */
2415 char size
, /* Size character (h, l, L) */
2416 type
; /* Format type character */
2417 int width
, /* Width of field */
2418 prec
; /* Number of characters of precision */
2419 char tformat
[100], /* Temporary format string for sprintf() */
2420 *tptr
, /* Pointer into temporary format */
2421 temp
[1024]; /* Buffer for formatted numbers */
2422 char *s
; /* Pointer to string */
2426 * Loop through the format string, formatting as needed...
2429 va_start(ap
, format
);
2437 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2440 *tptr
++ = *format
++;
2444 httpWrite2(client
->http
, "%", 1);
2449 else if (strchr(" -+#\'", *format
))
2450 *tptr
++ = *format
++;
2455 * Get width from argument...
2459 width
= va_arg(ap
, int);
2461 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2462 tptr
+= strlen(tptr
);
2468 while (isdigit(*format
& 255))
2470 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2473 width
= width
* 10 + *format
++ - '0';
2479 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2487 * Get precision from argument...
2491 prec
= va_arg(ap
, int);
2493 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2494 tptr
+= strlen(tptr
);
2500 while (isdigit(*format
& 255))
2502 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2505 prec
= prec
* 10 + *format
++ - '0';
2510 if (*format
== 'l' && format
[1] == 'l')
2514 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2522 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2524 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2539 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2548 case 'E' : /* Floating point formats */
2553 if ((size_t)(width
+ 2) > sizeof(temp
))
2556 sprintf(temp
, tformat
, va_arg(ap
, double));
2558 httpWrite2(client
->http
, temp
, strlen(temp
));
2561 case 'B' : /* Integer formats */
2569 if ((size_t)(width
+ 2) > sizeof(temp
))
2572 # ifdef HAVE_LONG_LONG
2574 sprintf(temp
, tformat
, va_arg(ap
, long long));
2576 # endif /* HAVE_LONG_LONG */
2578 sprintf(temp
, tformat
, va_arg(ap
, long));
2580 sprintf(temp
, tformat
, va_arg(ap
, int));
2582 httpWrite2(client
->http
, temp
, strlen(temp
));
2585 case 'p' : /* Pointer value */
2586 if ((size_t)(width
+ 2) > sizeof(temp
))
2589 sprintf(temp
, tformat
, va_arg(ap
, void *));
2591 httpWrite2(client
->http
, temp
, strlen(temp
));
2594 case 'c' : /* Character or character array */
2597 temp
[0] = (char)va_arg(ap
, int);
2599 html_escape(client
, temp
, 1);
2602 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2605 case 's' : /* String */
2606 if ((s
= va_arg(ap
, char *)) == NULL
)
2609 html_escape(client
, s
, strlen(s
));
2618 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2625 * 'ipp_cancel_job()' - Cancel a job.
2629 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2631 _ipp_job_t
*job
; /* Job information */
2638 if ((job
= find_job(client
)) == NULL
)
2640 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2645 * See if the job is already completed, canceled, or aborted; if so,
2646 * we can't cancel...
2651 case IPP_JSTATE_CANCELED
:
2652 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2653 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2656 case IPP_JSTATE_ABORTED
:
2657 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2658 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2661 case IPP_JSTATE_COMPLETED
:
2662 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2663 "Job #%d is already completed - can\'t cancel.", job
->id
);
2671 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2673 if (job
->state
== IPP_JSTATE_PROCESSING
||
2674 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2678 job
->state
= IPP_JSTATE_CANCELED
;
2679 job
->completed
= time(NULL
);
2682 _cupsRWUnlock(&(client
->printer
->rwlock
));
2684 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2691 * 'ipp_close_job()' - Close an open job.
2695 ipp_close_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 canceled - can\'t close.", job
->id
);
2722 case IPP_JSTATE_ABORTED
:
2723 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2724 "Job #%d is aborted - can\'t close.", job
->id
);
2727 case IPP_JSTATE_COMPLETED
:
2728 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2729 "Job #%d is completed - can\'t close.", job
->id
);
2732 case IPP_JSTATE_PROCESSING
:
2733 case IPP_JSTATE_STOPPED
:
2734 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2735 "Job #%d is already closed.", job
->id
);
2739 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2746 * 'ipp_create_job()' - Create a job object.
2750 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2752 _ipp_job_t
*job
; /* New job */
2753 cups_array_t
*ra
; /* Attributes to send in response */
2757 * Validate print job attributes...
2760 if (!valid_job_attributes(client
))
2762 httpFlush(client
->http
);
2767 * Do we have a file to print?
2770 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2772 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2773 "Unexpected document data following request.");
2781 if ((job
= create_job(client
)) == NULL
)
2783 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2784 "Currently printing another job.");
2789 * Return the job info...
2792 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2794 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2795 cupsArrayAdd(ra
, "job-id");
2796 cupsArrayAdd(ra
, "job-state");
2797 cupsArrayAdd(ra
, "job-state-message");
2798 cupsArrayAdd(ra
, "job-state-reasons");
2799 cupsArrayAdd(ra
, "job-uri");
2801 copy_job_attributes(client
, job
, ra
);
2802 cupsArrayDelete(ra
);
2807 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2811 ipp_get_job_attributes(
2812 _ipp_client_t
*client
) /* I - Client */
2814 _ipp_job_t
*job
; /* Job */
2815 cups_array_t
*ra
; /* requested-attributes */
2818 if ((job
= find_job(client
)) == NULL
)
2820 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2824 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2826 ra
= ippCreateRequestedArray(client
->request
);
2827 copy_job_attributes(client
, job
, ra
);
2828 cupsArrayDelete(ra
);
2833 * 'ipp_get_jobs()' - Get a list of job objects.
2837 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2839 ipp_attribute_t
*attr
; /* Current attribute */
2840 const char *which_jobs
= NULL
;
2841 /* which-jobs values */
2842 int job_comparison
; /* Job comparison */
2843 ipp_jstate_t job_state
; /* job-state value */
2844 int first_job_id
, /* First job ID */
2845 limit
, /* Maximum number of jobs to return */
2846 count
; /* Number of jobs that match */
2847 const char *username
; /* Username */
2848 _ipp_job_t
*job
; /* Current job pointer */
2849 cups_array_t
*ra
; /* Requested attributes array */
2853 * See if the "which-jobs" attribute have been specified...
2856 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2857 IPP_TAG_KEYWORD
)) != NULL
)
2859 which_jobs
= ippGetString(attr
, 0, NULL
);
2860 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
2863 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
2865 job_comparison
= -1;
2866 job_state
= IPP_JSTATE_STOPPED
;
2868 else if (!strcmp(which_jobs
, "completed"))
2871 job_state
= IPP_JSTATE_CANCELED
;
2873 else if (!strcmp(which_jobs
, "aborted"))
2876 job_state
= IPP_JSTATE_ABORTED
;
2878 else if (!strcmp(which_jobs
, "all"))
2881 job_state
= IPP_JSTATE_PENDING
;
2883 else if (!strcmp(which_jobs
, "canceled"))
2886 job_state
= IPP_JSTATE_CANCELED
;
2888 else if (!strcmp(which_jobs
, "pending"))
2891 job_state
= IPP_JSTATE_PENDING
;
2893 else if (!strcmp(which_jobs
, "pending-held"))
2896 job_state
= IPP_JSTATE_HELD
;
2898 else if (!strcmp(which_jobs
, "processing"))
2901 job_state
= IPP_JSTATE_PROCESSING
;
2903 else if (!strcmp(which_jobs
, "processing-stopped"))
2906 job_state
= IPP_JSTATE_STOPPED
;
2910 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
2911 "The which-jobs value \"%s\" is not supported.", which_jobs
);
2912 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2913 "which-jobs", NULL
, which_jobs
);
2918 * See if they want to limit the number of jobs reported...
2921 if ((attr
= ippFindAttribute(client
->request
, "limit",
2922 IPP_TAG_INTEGER
)) != NULL
)
2924 limit
= ippGetInteger(attr
, 0);
2926 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
2931 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2932 IPP_TAG_INTEGER
)) != NULL
)
2934 first_job_id
= ippGetInteger(attr
, 0);
2936 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
2943 * See if we only want to see jobs for a specific user...
2948 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2949 IPP_TAG_BOOLEAN
)) != NULL
)
2951 int my_jobs
= ippGetBoolean(attr
, 0);
2953 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
2954 my_jobs
? "true" : "false");
2958 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
2959 IPP_TAG_NAME
)) == NULL
)
2961 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2962 "Need requesting-user-name with my-jobs.");
2966 username
= ippGetString(attr
, 0, NULL
);
2968 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2969 client
->hostname
, username
);
2974 * OK, build a list of jobs for this printer...
2977 ra
= ippCreateRequestedArray(client
->request
);
2979 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2981 _cupsRWLockRead(&(client
->printer
->rwlock
));
2983 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
2984 (limit
<= 0 || count
< limit
) && job
;
2985 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
2988 * Filter out jobs that don't match...
2991 if ((job_comparison
< 0 && job
->state
> job_state
) ||
2992 (job_comparison
== 0 && job
->state
!= job_state
) ||
2993 (job_comparison
> 0 && job
->state
< job_state
) ||
2994 job
->id
< first_job_id
||
2995 (username
&& job
->username
&&
2996 strcasecmp(username
, job
->username
)))
3000 ippAddSeparator(client
->response
);
3003 copy_job_attributes(client
, job
, ra
);
3006 cupsArrayDelete(ra
);
3008 _cupsRWUnlock(&(client
->printer
->rwlock
));
3013 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3017 ipp_get_printer_attributes(
3018 _ipp_client_t
*client
) /* I - Client */
3020 cups_array_t
*ra
; /* Requested attributes array */
3021 _ipp_printer_t
*printer
; /* Printer */
3025 * Send the attributes...
3028 ra
= ippCreateRequestedArray(client
->request
);
3029 printer
= client
->printer
;
3031 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3033 _cupsRWLockRead(&(printer
->rwlock
));
3035 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3036 IPP_TAG_CUPS_CONST
);
3038 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3040 int i
, /* Looping var */
3041 num_ready
= 0; /* Number of ready media */
3042 ipp_t
*ready
[3]; /* Ready media */
3044 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3046 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
3047 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);
3049 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);
3051 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3052 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);
3053 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3055 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
3056 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);
3058 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);
3063 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3064 for (i
= 0; i
< num_ready
; i
++)
3065 ippDelete(ready
[i
]);
3068 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3071 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3073 int num_ready
= 0; /* Number of ready media */
3074 const char *ready
[3]; /* Ready media */
3076 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3077 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3079 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3080 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3082 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3083 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3086 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3088 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3091 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3092 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3094 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3095 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3097 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3098 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3101 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3102 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3103 "printer-state", printer
->state
);
3105 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3106 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3108 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3109 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3111 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3113 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3115 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3118 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3120 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3121 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3122 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3123 "printer-state-reasons", NULL
, "none");
3126 int num_reasons
= 0;/* Number of reasons */
3127 const char *reasons
[32]; /* Reason strings */
3129 if (printer
->state_reasons
& _IPP_PREASON_OTHER
)
3130 reasons
[num_reasons
++] = "other";
3131 if (printer
->state_reasons
& _IPP_PREASON_COVER_OPEN
)
3132 reasons
[num_reasons
++] = "cover-open";
3133 if (printer
->state_reasons
& _IPP_PREASON_INPUT_TRAY_MISSING
)
3134 reasons
[num_reasons
++] = "input-tray-missing";
3135 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_EMPTY
)
3136 reasons
[num_reasons
++] = "marker-supply-empty-warning";
3137 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_LOW
)
3138 reasons
[num_reasons
++] = "marker-supply-low-report";
3139 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
)
3140 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
3141 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_FULL
)
3142 reasons
[num_reasons
++] = "marker-waste-full-warning";
3143 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
3144 reasons
[num_reasons
++] = "media-empty-warning";
3145 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_JAM
)
3146 reasons
[num_reasons
++] = "media-jam-warning";
3147 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_LOW
)
3148 reasons
[num_reasons
++] = "media-low-report";
3149 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_NEEDED
)
3150 reasons
[num_reasons
++] = "media-needed-report";
3151 if (printer
->state_reasons
& _IPP_PREASON_MOVING_TO_PAUSED
)
3152 reasons
[num_reasons
++] = "moving-to-paused";
3153 if (printer
->state_reasons
& _IPP_PREASON_PAUSED
)
3154 reasons
[num_reasons
++] = "paused";
3155 if (printer
->state_reasons
& _IPP_PREASON_SPOOL_AREA_FULL
)
3156 reasons
[num_reasons
++] = "spool-area-full";
3157 if (printer
->state_reasons
& _IPP_PREASON_TONER_EMPTY
)
3158 reasons
[num_reasons
++] = "toner-empty-warning";
3159 if (printer
->state_reasons
& _IPP_PREASON_TONER_LOW
)
3160 reasons
[num_reasons
++] = "toner-low-report";
3162 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
3163 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3164 "printer-state-reasons", num_reasons
, NULL
, reasons
);
3168 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3170 int i
; /* Looping var */
3171 char buffer
[256]; /* Supply value buffer */
3172 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3173 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3175 for (i
= 0; i
< 5; i
++)
3177 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
]);
3180 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3182 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3186 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3187 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3189 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3190 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3191 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3193 _cupsRWUnlock(&(printer
->rwlock
));
3195 cupsArrayDelete(ra
);
3200 * 'ipp_identify_printer()' - Beep or display a message.
3204 ipp_identify_printer(
3205 _ipp_client_t
*client
) /* I - Client */
3207 /* TODO: Do something */
3209 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3214 * 'ipp_print_job()' - Create a job object with an attached document.
3218 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3220 _ipp_job_t
*job
; /* New job */
3221 char filename
[1024], /* Filename buffer */
3222 buffer
[4096]; /* Copy buffer */
3223 ssize_t bytes
; /* Bytes read */
3224 cups_array_t
*ra
; /* Attributes to send in response */
3228 * Validate print job attributes...
3231 if (!valid_job_attributes(client
))
3233 httpFlush(client
->http
);
3238 * Do we have a file to print?
3241 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3243 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3251 if ((job
= create_job(client
)) == NULL
)
3253 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3254 "Currently printing another job.");
3259 * Create a file for the request data...
3262 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3265 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3267 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3269 job
->state
= IPP_JSTATE_ABORTED
;
3271 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3272 "Unable to create print file: %s", strerror(errno
));
3276 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3278 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3280 int error
= errno
; /* Write error */
3282 job
->state
= IPP_JSTATE_ABORTED
;
3289 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3290 "Unable to write print file: %s", strerror(error
));
3298 * Got an error while reading the print data, so abort this job.
3301 job
->state
= IPP_JSTATE_ABORTED
;
3308 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3309 "Unable to read print file.");
3315 int error
= errno
; /* Write error */
3317 job
->state
= IPP_JSTATE_ABORTED
;
3322 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3323 "Unable to write print file: %s", strerror(error
));
3328 job
->filename
= strdup(filename
);
3329 job
->state
= IPP_JSTATE_PENDING
;
3332 * Process the job...
3335 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3337 job
->state
= IPP_JSTATE_ABORTED
;
3338 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3343 * Return the job info...
3346 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3348 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3349 cupsArrayAdd(ra
, "job-id");
3350 cupsArrayAdd(ra
, "job-state");
3351 cupsArrayAdd(ra
, "job-state-message");
3352 cupsArrayAdd(ra
, "job-state-reasons");
3353 cupsArrayAdd(ra
, "job-uri");
3355 copy_job_attributes(client
, job
, ra
);
3356 cupsArrayDelete(ra
);
3361 * 'ipp_print_uri()' - Create a job object with a referenced document.
3365 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3367 _ipp_job_t
*job
; /* New job */
3368 ipp_attribute_t
*uri
; /* document-uri */
3369 char scheme
[256], /* URI scheme */
3370 userpass
[256], /* Username and password info */
3371 hostname
[256], /* Hostname */
3372 resource
[1024]; /* Resource path */
3373 int port
; /* Port number */
3374 http_uri_status_t uri_status
; /* URI decode status */
3375 http_encryption_t encryption
; /* Encryption to use, if any */
3376 http_t
*http
; /* Connection for http/https URIs */
3377 http_status_t status
; /* Access status for http/https URIs */
3378 int infile
; /* Input file for local file URIs */
3379 char filename
[1024], /* Filename buffer */
3380 buffer
[4096]; /* Copy buffer */
3381 ssize_t bytes
; /* Bytes read */
3382 cups_array_t
*ra
; /* Attributes to send in response */
3383 static const char * const uri_status_strings
[] =
3384 { /* URI decode errors */
3386 "Bad arguments to function.",
3387 "Bad resource in URI.",
3388 "Bad port number in URI.",
3389 "Bad hostname in URI.",
3390 "Bad username in URI.",
3391 "Bad scheme in URI.",
3397 * Validate print job attributes...
3400 if (!valid_job_attributes(client
))
3402 httpFlush(client
->http
);
3407 * Do we have a file to print?
3410 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3412 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3413 "Unexpected document data following request.");
3418 * Do we have a document URI?
3421 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3422 IPP_TAG_URI
)) == NULL
)
3424 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3428 if (ippGetCount(uri
) != 1)
3430 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3431 "Too many document-uri values.");
3435 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3436 scheme
, sizeof(scheme
), userpass
,
3437 sizeof(userpass
), hostname
, sizeof(hostname
),
3438 &port
, resource
, sizeof(resource
));
3439 if (uri_status
< HTTP_URI_STATUS_OK
)
3441 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3442 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3446 if (strcmp(scheme
, "file") &&
3448 strcmp(scheme
, "https") &&
3449 #endif /* HAVE_SSL */
3450 strcmp(scheme
, "http"))
3452 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3453 "URI scheme \"%s\" not supported.", scheme
);
3457 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3459 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3460 "Unable to access URI: %s", strerror(errno
));
3468 if ((job
= create_job(client
)) == NULL
)
3470 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3471 "Currently printing another job.");
3476 * Create a file for the request data...
3479 if (!strcasecmp(job
->format
, "image/jpeg"))
3480 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3481 client
->printer
->directory
, job
->id
);
3482 else if (!strcasecmp(job
->format
, "image/png"))
3483 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3484 client
->printer
->directory
, job
->id
);
3485 else if (!strcasecmp(job
->format
, "application/pdf"))
3486 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3487 client
->printer
->directory
, job
->id
);
3488 else if (!strcasecmp(job
->format
, "application/postscript"))
3489 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3490 client
->printer
->directory
, job
->id
);
3492 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3493 client
->printer
->directory
, job
->id
);
3495 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3497 job
->state
= IPP_JSTATE_ABORTED
;
3499 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3500 "Unable to create print file: %s", strerror(errno
));
3504 if (!strcmp(scheme
, "file"))
3506 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3508 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3509 "Unable to access URI: %s", strerror(errno
));
3515 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3516 (errno
== EAGAIN
|| errno
== EINTR
))
3518 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3520 int error
= errno
; /* Write error */
3522 job
->state
= IPP_JSTATE_ABORTED
;
3530 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3531 "Unable to write print file: %s", strerror(error
));
3542 if (port
== 443 || !strcmp(scheme
, "https"))
3543 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3545 #endif /* HAVE_SSL */
3546 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3548 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3549 1, 30000, NULL
)) == NULL
)
3551 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3552 "Unable to connect to %s: %s", hostname
,
3553 cupsLastErrorString());
3554 job
->state
= IPP_JSTATE_ABORTED
;
3563 httpClearFields(http
);
3564 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3565 if (httpGet(http
, resource
))
3567 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3568 "Unable to GET URI: %s", strerror(errno
));
3570 job
->state
= IPP_JSTATE_ABORTED
;
3580 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3582 if (status
!= HTTP_STATUS_OK
)
3584 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3585 "Unable to GET URI: %s", httpStatus(status
));
3587 job
->state
= IPP_JSTATE_ABORTED
;
3597 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3599 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3601 int error
= errno
; /* Write error */
3603 job
->state
= IPP_JSTATE_ABORTED
;
3611 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3612 "Unable to write print file: %s", strerror(error
));
3622 int error
= errno
; /* Write error */
3624 job
->state
= IPP_JSTATE_ABORTED
;
3629 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3630 "Unable to write print file: %s", strerror(error
));
3635 job
->filename
= strdup(filename
);
3636 job
->state
= IPP_JSTATE_PENDING
;
3639 * Process the job...
3643 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3645 job
->state
= IPP_JSTATE_ABORTED
;
3646 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3655 * Return the job info...
3658 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3660 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3661 cupsArrayAdd(ra
, "job-id");
3662 cupsArrayAdd(ra
, "job-state");
3663 cupsArrayAdd(ra
, "job-state-reasons");
3664 cupsArrayAdd(ra
, "job-uri");
3666 copy_job_attributes(client
, job
, ra
);
3667 cupsArrayDelete(ra
);
3672 * 'ipp_send_document()' - Add an attached document to a job object created with
3677 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3679 _ipp_job_t
*job
; /* Job information */
3680 char filename
[1024], /* Filename buffer */
3681 buffer
[4096]; /* Copy buffer */
3682 ssize_t bytes
; /* Bytes read */
3683 ipp_attribute_t
*attr
; /* Current attribute */
3684 cups_array_t
*ra
; /* Attributes to send in response */
3691 if ((job
= find_job(client
)) == NULL
)
3693 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3694 httpFlush(client
->http
);
3699 * See if we already have a document for this job or the job has already
3700 * in a non-pending state...
3703 if (job
->state
> IPP_JSTATE_HELD
)
3705 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3706 "Job is not in a pending state.");
3707 httpFlush(client
->http
);
3710 else if (job
->filename
|| job
->fd
>= 0)
3712 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3713 "Multiple document jobs are not supported.");
3714 httpFlush(client
->http
);
3718 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3719 IPP_TAG_ZERO
)) == NULL
)
3721 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3722 "Missing required last-document attribute.");
3723 httpFlush(client
->http
);
3726 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3727 !ippGetBoolean(attr
, 0))
3729 respond_unsupported(client
, attr
);
3730 httpFlush(client
->http
);
3735 * Validate document attributes...
3738 if (!valid_doc_attributes(client
))
3740 httpFlush(client
->http
);
3745 * Get the document format for the job...
3748 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3750 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3751 job
->format
= ippGetString(attr
, 0, NULL
);
3752 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3753 job
->format
= ippGetString(attr
, 0, NULL
);
3755 job
->format
= "application/octet-stream";
3758 * Create a file for the request data...
3761 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3764 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3766 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3768 _cupsRWUnlock(&(client
->printer
->rwlock
));
3772 job
->state
= IPP_JSTATE_ABORTED
;
3774 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3775 "Unable to create print file: %s", strerror(errno
));
3779 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3781 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3783 int error
= errno
; /* Write error */
3785 job
->state
= IPP_JSTATE_ABORTED
;
3792 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3793 "Unable to write print file: %s", strerror(error
));
3801 * Got an error while reading the print data, so abort this job.
3804 job
->state
= IPP_JSTATE_ABORTED
;
3811 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3812 "Unable to read print file.");
3818 int error
= errno
; /* Write error */
3820 job
->state
= IPP_JSTATE_ABORTED
;
3825 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3826 "Unable to write print file: %s", strerror(error
));
3830 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3833 job
->filename
= strdup(filename
);
3834 job
->state
= IPP_JSTATE_PENDING
;
3836 _cupsRWUnlock(&(client
->printer
->rwlock
));
3839 * Process the job...
3843 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3845 job
->state
= IPP_JSTATE_ABORTED
;
3846 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3855 * Return the job info...
3858 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3860 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3861 cupsArrayAdd(ra
, "job-id");
3862 cupsArrayAdd(ra
, "job-state");
3863 cupsArrayAdd(ra
, "job-state-reasons");
3864 cupsArrayAdd(ra
, "job-uri");
3866 copy_job_attributes(client
, job
, ra
);
3867 cupsArrayDelete(ra
);
3872 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3877 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
3879 _ipp_job_t
*job
; /* Job information */
3880 ipp_attribute_t
*uri
; /* document-uri */
3881 char scheme
[256], /* URI scheme */
3882 userpass
[256], /* Username and password info */
3883 hostname
[256], /* Hostname */
3884 resource
[1024]; /* Resource path */
3885 int port
; /* Port number */
3886 http_uri_status_t uri_status
; /* URI decode status */
3887 http_encryption_t encryption
; /* Encryption to use, if any */
3888 http_t
*http
; /* Connection for http/https URIs */
3889 http_status_t status
; /* Access status for http/https URIs */
3890 int infile
; /* Input file for local file URIs */
3891 char filename
[1024], /* Filename buffer */
3892 buffer
[4096]; /* Copy buffer */
3893 ssize_t bytes
; /* Bytes read */
3894 ipp_attribute_t
*attr
; /* Current attribute */
3895 cups_array_t
*ra
; /* Attributes to send in response */
3896 static const char * const uri_status_strings
[] =
3897 { /* URI decode errors */
3899 "Bad arguments to function.",
3900 "Bad resource in URI.",
3901 "Bad port number in URI.",
3902 "Bad hostname in URI.",
3903 "Bad username in URI.",
3904 "Bad scheme in URI.",
3913 if ((job
= find_job(client
)) == NULL
)
3915 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3916 httpFlush(client
->http
);
3921 * See if we already have a document for this job or the job has already
3922 * in a non-pending state...
3925 if (job
->state
> IPP_JSTATE_HELD
)
3927 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3928 "Job is not in a pending state.");
3929 httpFlush(client
->http
);
3932 else if (job
->filename
|| job
->fd
>= 0)
3934 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3935 "Multiple document jobs are not supported.");
3936 httpFlush(client
->http
);
3940 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3941 IPP_TAG_ZERO
)) == NULL
)
3943 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3944 "Missing required last-document attribute.");
3945 httpFlush(client
->http
);
3948 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3949 !ippGetBoolean(attr
, 0))
3951 respond_unsupported(client
, attr
);
3952 httpFlush(client
->http
);
3957 * Validate document attributes...
3960 if (!valid_doc_attributes(client
))
3962 httpFlush(client
->http
);
3967 * Do we have a file to print?
3970 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3972 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3973 "Unexpected document data following request.");
3978 * Do we have a document URI?
3981 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3982 IPP_TAG_URI
)) == NULL
)
3984 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3988 if (ippGetCount(uri
) != 1)
3990 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3991 "Too many document-uri values.");
3995 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3996 scheme
, sizeof(scheme
), userpass
,
3997 sizeof(userpass
), hostname
, sizeof(hostname
),
3998 &port
, resource
, sizeof(resource
));
3999 if (uri_status
< HTTP_URI_STATUS_OK
)
4001 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4002 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4006 if (strcmp(scheme
, "file") &&
4008 strcmp(scheme
, "https") &&
4009 #endif /* HAVE_SSL */
4010 strcmp(scheme
, "http"))
4012 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4013 "URI scheme \"%s\" not supported.", scheme
);
4017 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4019 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4020 "Unable to access URI: %s", strerror(errno
));
4025 * Get the document format for the job...
4028 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4030 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4031 IPP_TAG_MIMETYPE
)) != NULL
)
4032 job
->format
= ippGetString(attr
, 0, NULL
);
4034 job
->format
= "application/octet-stream";
4037 * Create a file for the request data...
4040 if (!strcasecmp(job
->format
, "image/jpeg"))
4041 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4042 client
->printer
->directory
, job
->id
);
4043 else if (!strcasecmp(job
->format
, "image/png"))
4044 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4045 client
->printer
->directory
, job
->id
);
4046 else if (!strcasecmp(job
->format
, "application/pdf"))
4047 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4048 client
->printer
->directory
, job
->id
);
4049 else if (!strcasecmp(job
->format
, "application/postscript"))
4050 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4051 client
->printer
->directory
, job
->id
);
4053 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4054 client
->printer
->directory
, job
->id
);
4056 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4058 _cupsRWUnlock(&(client
->printer
->rwlock
));
4062 job
->state
= IPP_JSTATE_ABORTED
;
4064 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4065 "Unable to create print file: %s", strerror(errno
));
4069 if (!strcmp(scheme
, "file"))
4071 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4073 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4074 "Unable to access URI: %s", strerror(errno
));
4080 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4081 (errno
== EAGAIN
|| errno
== EINTR
))
4083 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4085 int error
= errno
; /* Write error */
4087 job
->state
= IPP_JSTATE_ABORTED
;
4095 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4096 "Unable to write print file: %s", strerror(error
));
4107 if (port
== 443 || !strcmp(scheme
, "https"))
4108 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4110 #endif /* HAVE_SSL */
4111 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4113 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4114 1, 30000, NULL
)) == NULL
)
4116 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4117 "Unable to connect to %s: %s", hostname
,
4118 cupsLastErrorString());
4119 job
->state
= IPP_JSTATE_ABORTED
;
4128 httpClearFields(http
);
4129 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4130 if (httpGet(http
, resource
))
4132 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4133 "Unable to GET URI: %s", strerror(errno
));
4135 job
->state
= IPP_JSTATE_ABORTED
;
4145 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4147 if (status
!= HTTP_STATUS_OK
)
4149 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4150 "Unable to GET URI: %s", httpStatus(status
));
4152 job
->state
= IPP_JSTATE_ABORTED
;
4162 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4164 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4166 int error
= errno
; /* Write error */
4168 job
->state
= IPP_JSTATE_ABORTED
;
4176 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4177 "Unable to write print file: %s", strerror(error
));
4187 int error
= errno
; /* Write error */
4189 job
->state
= IPP_JSTATE_ABORTED
;
4194 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4195 "Unable to write print file: %s", strerror(error
));
4199 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4202 job
->filename
= strdup(filename
);
4203 job
->state
= IPP_JSTATE_PENDING
;
4205 _cupsRWUnlock(&(client
->printer
->rwlock
));
4208 * Process the job...
4212 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4214 job
->state
= IPP_JSTATE_ABORTED
;
4215 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4224 * Return the job info...
4227 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4229 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4230 cupsArrayAdd(ra
, "job-id");
4231 cupsArrayAdd(ra
, "job-state");
4232 cupsArrayAdd(ra
, "job-state-reasons");
4233 cupsArrayAdd(ra
, "job-uri");
4235 copy_job_attributes(client
, job
, ra
);
4236 cupsArrayDelete(ra
);
4241 * 'ipp_validate_job()' - Validate job creation attributes.
4245 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4247 if (valid_job_attributes(client
))
4248 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4253 * 'parse_options()' - Parse URL options into CUPS options.
4255 * The client->options string is destroyed by this function.
4258 static int /* O - Number of options */
4259 parse_options(_ipp_client_t
*client
, /* I - Client */
4260 cups_option_t
**options
) /* O - Options */
4262 char *name
, /* Name */
4264 *next
; /* Next name=value pair */
4265 int num_options
= 0; /* Number of options */
4270 for (name
= client
->options
; name
&& *name
; name
= next
)
4272 if ((value
= strchr(name
, '=')) == NULL
)
4276 if ((next
= strchr(value
, '&')) != NULL
)
4279 num_options
= cupsAddOption(name
, value
, num_options
, options
);
4282 return (num_options
);
4287 * 'process_client()' - Process client requests on a thread.
4290 static void * /* O - Exit status */
4291 process_client(_ipp_client_t
*client
) /* I - Client */
4294 * Loop until we are out of requests or timeout (30 seconds)...
4298 int first_time
= 1; /* First time request? */
4299 #endif /* HAVE_SSL */
4301 while (httpWait(client
->http
, 30000))
4307 * See if we need to negotiate a TLS connection...
4310 char buf
[1]; /* First byte from client */
4312 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
4314 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
4316 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
4318 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4322 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4327 #endif /* HAVE_SSL */
4329 if (!process_http(client
))
4334 * Close the conection to the client and return...
4337 delete_client(client
);
4344 * 'process_http()' - Process a HTTP request.
4347 int /* O - 1 on success, 0 on failure */
4348 process_http(_ipp_client_t
*client
) /* I - Client connection */
4350 char uri
[1024]; /* URI */
4351 http_state_t http_state
; /* HTTP state */
4352 http_status_t http_status
; /* HTTP status */
4353 ipp_state_t ipp_state
; /* State of IPP transfer */
4354 char scheme
[32], /* Method/scheme */
4355 userpass
[128], /* Username:password */
4356 hostname
[HTTP_MAX_HOST
];
4358 int port
; /* Port number */
4359 const char *encoding
; /* Content-Encoding value */
4360 static const char * const http_states
[] =
4361 { /* Strings for logging HTTP method */
4382 * Clear state variables...
4385 ippDelete(client
->request
);
4386 ippDelete(client
->response
);
4388 client
->request
= NULL
;
4389 client
->response
= NULL
;
4390 client
->operation
= HTTP_STATE_WAITING
;
4393 * Read a request from the connection...
4396 while ((http_state
= httpReadRequest(client
->http
, uri
,
4397 sizeof(uri
))) == HTTP_STATE_WAITING
)
4401 * Parse the request line...
4404 if (http_state
== HTTP_STATE_ERROR
)
4406 if (httpError(client
->http
) == EPIPE
)
4407 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
4409 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
4410 strerror(httpError(client
->http
)));
4414 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
4416 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
4417 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4420 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
4422 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
4423 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4427 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
4431 * Separate the URI into its components...
4434 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4435 userpass
, sizeof(userpass
),
4436 hostname
, sizeof(hostname
), &port
,
4437 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
4438 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
4440 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
4441 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4445 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
4446 *(client
->options
)++ = '\0';
4449 * Process the request...
4452 client
->start
= time(NULL
);
4453 client
->operation
= httpGetState(client
->http
);
4456 * Parse incoming parameters until the status changes...
4459 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4461 if (http_status
!= HTTP_STATUS_OK
)
4463 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4467 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4468 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4471 * HTTP/1.1 and higher require the "Host:" field...
4474 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4479 * Handle HTTP Upgrade...
4482 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4486 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
4488 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
4491 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
4493 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
4495 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4499 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4502 #endif /* HAVE_SSL */
4504 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4509 * Handle HTTP Expect...
4512 if (httpGetExpect(client
->http
) &&
4513 (client
->operation
== HTTP_STATE_POST
||
4514 client
->operation
== HTTP_STATE_PUT
))
4516 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4519 * Send 100-continue header...
4522 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4528 * Send 417-expectation-failed header...
4531 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4537 * Handle new transfers...
4540 encoding
= httpGetContentEncoding(client
->http
);
4542 switch (client
->operation
)
4544 case HTTP_STATE_OPTIONS
:
4546 * Do OPTIONS command...
4549 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4551 case HTTP_STATE_HEAD
:
4552 if (!strcmp(client
->uri
, "/icon.png"))
4553 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4554 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
4555 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4557 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4559 case HTTP_STATE_GET
:
4560 if (!strcmp(client
->uri
, "/icon.png"))
4563 * Send PNG icon file.
4566 int fd
; /* Icon file */
4567 struct stat fileinfo
; /* Icon file information */
4568 char buffer
[4096]; /* Copy buffer */
4569 ssize_t bytes
; /* Bytes */
4571 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
4573 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4574 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4576 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
4577 (size_t)fileinfo
.st_size
))
4583 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4584 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
4586 httpFlushWrite(client
->http
);
4591 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4593 else if (!strcmp(client
->uri
, "/"))
4596 * Show web status page...
4599 _ipp_job_t
*job
; /* Current job */
4600 int i
; /* Looping var */
4601 _ipp_preason_t reason
; /* Current reason */
4602 static const char * const reasons
[] =
4603 { /* Reason strings */
4606 "Input Tray Missing",
4607 "Marker Supply Empty",
4608 "Marker Supply Low",
4609 "Marker Waste Almost Full",
4610 "Marker Waste Full",
4622 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4625 html_header(client
, client
->printer
->name
);
4627 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
4628 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
4629 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
4630 if (client
->printer
->state_reasons
& reason
)
4631 html_printf(client
, "\n<br> %s", reasons
[i
]);
4632 html_printf(client
, "</p>\n");
4634 if (cupsArrayCount(client
->printer
->jobs
) > 0)
4636 _cupsRWLockRead(&(client
->printer
->rwlock
));
4638 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");
4639 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
4641 char when
[256], /* When job queued/started/finished */
4642 hhmmss
[64]; /* Time HH:MM:SS */
4646 case IPP_JSTATE_PENDING
:
4647 case IPP_JSTATE_HELD
:
4648 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
4650 case IPP_JSTATE_PROCESSING
:
4651 case IPP_JSTATE_STOPPED
:
4652 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
4654 case IPP_JSTATE_ABORTED
:
4655 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4657 case IPP_JSTATE_CANCELED
:
4658 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4660 case IPP_JSTATE_COMPLETED
:
4661 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4665 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
);
4667 html_printf(client
, "</tbody></table>\n");
4669 _cupsRWUnlock(&(client
->printer
->rwlock
));
4671 html_footer(client
);
4675 else if (!strcmp(client
->uri
, "/media"))
4678 * Show web media page...
4681 int i
, /* Looping var */
4682 num_options
; /* Number of form options */
4683 cups_option_t
*options
; /* Form options */
4684 static const char * const sizes
[] =
4685 { /* Size strings */
4698 static const char * const types
[] =
4715 static const int sheets
[] = /* Number of sheets */
4724 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4727 html_header(client
, client
->printer
->name
);
4729 if ((num_options
= parse_options(client
, &options
)) > 0)
4732 * WARNING: A real printer/server implementation MUST NOT implement
4733 * media updates via a GET request - GET requests are supposed to be
4734 * idempotent (without side-effects) and we obviously are not
4735 * authenticating access here. This form is provided solely to
4736 * enable testing and development!
4739 const char *val
; /* Form value */
4741 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
4742 client
->printer
->main_size
= atoi(val
);
4743 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
4744 client
->printer
->main_type
= atoi(val
);
4745 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
4746 client
->printer
->main_level
= atoi(val
);
4748 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
4749 client
->printer
->envelope_size
= atoi(val
);
4750 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
4751 client
->printer
->envelope_level
= atoi(val
);
4753 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
4754 client
->printer
->photo_size
= atoi(val
);
4755 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
4756 client
->printer
->photo_type
= atoi(val
);
4757 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
4758 client
->printer
->photo_level
= atoi(val
);
4760 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))
4761 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
4763 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
4765 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
))
4767 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
4768 if (client
->printer
->active_job
)
4769 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
4772 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
4774 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
4777 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
4779 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
4780 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
4781 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4782 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
4783 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
4784 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
4785 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4786 if (!strstr(types
[i
], "Photo"))
4787 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
4788 html_printf(client
, "</select> <select name=\"main_level\">");
4789 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4790 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
4791 html_printf(client
, "</select></td></tr>\n");
4794 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
4795 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4796 if (strstr(sizes
[i
], "Envelope"))
4797 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
4798 html_printf(client
, "</select> <select name=\"envelope_level\">");
4799 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4800 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
4801 html_printf(client
, "</select></td></tr>\n");
4804 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
4805 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4806 if (strstr(sizes
[i
], "Photo"))
4807 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
4808 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
4809 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4810 if (strstr(types
[i
], "Photo"))
4811 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
4812 html_printf(client
, "</select> <select name=\"photo_level\">");
4813 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4814 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
4815 html_printf(client
, "</select></td></tr>\n");
4817 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
4818 html_footer(client
);
4822 else if (!strcmp(client
->uri
, "/supplies"))
4825 * Show web supplies page...
4828 int i
, j
, /* Looping vars */
4829 num_options
; /* Number of form options */
4830 cups_option_t
*options
; /* Form options */
4831 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
4833 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4836 html_header(client
, client
->printer
->name
);
4838 if ((num_options
= parse_options(client
, &options
)) > 0)
4841 * WARNING: A real printer/server implementation MUST NOT implement
4842 * supply updates via a GET request - GET requests are supposed to be
4843 * idempotent (without side-effects) and we obviously are not
4844 * authenticating access here. This form is provided solely to
4845 * enable testing and development!
4848 char name
[64]; /* Form field */
4849 const char *val
; /* Form value */
4851 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
);
4853 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
4855 snprintf(name
, sizeof(name
), "supply_%d", i
);
4856 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
4858 int level
= client
->printer
->supplies
[i
] = atoi(val
);
4864 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
4865 else if (level
< 10)
4866 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
4871 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
4872 else if (level
> 90)
4873 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
4878 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
4881 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
4883 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
4884 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
4886 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
4887 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
4888 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
4889 html_printf(client
, "</select></td></tr>\n");
4891 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
4892 html_footer(client
);
4897 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4900 case HTTP_STATE_POST
:
4901 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
4905 * Not an IPP request...
4908 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
4912 * Read the IPP request...
4915 client
->request
= ippNew();
4917 while ((ipp_state
= ippRead(client
->http
,
4918 client
->request
)) != IPP_STATE_DATA
)
4920 if (ipp_state
== IPP_STATE_ERROR
)
4922 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
4923 cupsLastErrorString());
4924 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4930 * Now that we have the IPP request, process the request...
4933 return (process_ipp(client
));
4936 break; /* Anti-compiler-warning-code */
4944 * 'process_ipp()' - Process an IPP request.
4947 static int /* O - 1 on success, 0 on error */
4948 process_ipp(_ipp_client_t
*client
) /* I - Client */
4950 ipp_tag_t group
; /* Current group tag */
4951 ipp_attribute_t
*attr
; /* Current attribute */
4952 ipp_attribute_t
*charset
; /* Character set attribute */
4953 ipp_attribute_t
*language
; /* Language attribute */
4954 ipp_attribute_t
*uri
; /* Printer URI attribute */
4955 int major
, minor
; /* Version number */
4956 const char *name
; /* Name of attribute */
4959 debug_attributes("Request", client
->request
, 1);
4962 * First build an empty response message for this request...
4965 client
->operation_id
= ippGetOperation(client
->request
);
4966 client
->response
= ippNewResponse(client
->request
);
4969 * Then validate the request header and required attributes...
4972 major
= ippGetVersion(client
->request
, &minor
);
4974 if (major
< 1 || major
> 2)
4977 * Return an error, since we only support IPP 1.x and 2.x.
4980 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
4981 "Bad request version number %d.%d.", major
, minor
);
4983 else if (ippGetRequestId(client
->request
) <= 0)
4984 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
4985 ippGetRequestId(client
->request
));
4986 else if (!ippFirstAttribute(client
->request
))
4987 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4988 "No attributes in request.");
4992 * Make sure that the attributes are provided in the correct order and
4993 * don't repeat groups...
4996 for (attr
= ippFirstAttribute(client
->request
),
4997 group
= ippGetGroupTag(attr
);
4999 attr
= ippNextAttribute(client
->request
))
5001 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5004 * Out of order; return an error...
5007 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5008 "Attribute groups are out of order (%x < %x).",
5009 ippGetGroupTag(attr
), group
);
5013 group
= ippGetGroupTag(attr
);
5019 * Then make sure that the first three attributes are:
5021 * attributes-charset
5022 * attributes-natural-language
5023 * printer-uri/job-uri
5026 attr
= ippFirstAttribute(client
->request
);
5027 name
= ippGetName(attr
);
5028 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5029 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5034 attr
= ippNextAttribute(client
->request
);
5035 name
= ippGetName(attr
);
5037 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5038 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5043 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5044 IPP_TAG_URI
)) != NULL
)
5046 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5047 IPP_TAG_URI
)) != NULL
)
5053 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5054 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5057 * Bad character set...
5060 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5061 "Unsupported character set \"%s\".",
5062 ippGetString(charset
, 0, NULL
));
5064 else if (!charset
|| !language
|| !uri
)
5067 * Return an error, since attributes-charset,
5068 * attributes-natural-language, and printer-uri/job-uri are required
5069 * for all operations.
5072 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5073 "Missing required attributes.");
5077 char scheme
[32], /* URI scheme */
5078 userpass
[32], /* Username/password in URI */
5079 host
[256], /* Host name in URI */
5080 resource
[256]; /* Resource path in URI */
5081 int port
; /* Port number in URI */
5083 name
= ippGetName(uri
);
5085 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5086 scheme
, sizeof(scheme
),
5087 userpass
, sizeof(userpass
),
5088 host
, sizeof(host
), &port
,
5089 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5090 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5091 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5092 else if ((!strcmp(name
, "job-uri") &&
5093 strncmp(resource
, "/ipp/print/", 11)) ||
5094 (!strcmp(name
, "printer-uri") &&
5095 strcmp(resource
, "/ipp/print")))
5096 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5097 name
, ippGetString(uri
, 0, NULL
));
5101 * Try processing the operation...
5104 switch (ippGetOperation(client
->request
))
5106 case IPP_OP_PRINT_JOB
:
5107 ipp_print_job(client
);
5110 case IPP_OP_PRINT_URI
:
5111 ipp_print_uri(client
);
5114 case IPP_OP_VALIDATE_JOB
:
5115 ipp_validate_job(client
);
5118 case IPP_OP_CREATE_JOB
:
5119 ipp_create_job(client
);
5122 case IPP_OP_SEND_DOCUMENT
:
5123 ipp_send_document(client
);
5126 case IPP_OP_SEND_URI
:
5127 ipp_send_uri(client
);
5130 case IPP_OP_CANCEL_JOB
:
5131 ipp_cancel_job(client
);
5134 case IPP_OP_GET_JOB_ATTRIBUTES
:
5135 ipp_get_job_attributes(client
);
5138 case IPP_OP_GET_JOBS
:
5139 ipp_get_jobs(client
);
5142 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5143 ipp_get_printer_attributes(client
);
5146 case IPP_OP_CLOSE_JOB
:
5147 ipp_close_job(client
);
5150 case IPP_OP_IDENTIFY_PRINTER
:
5151 ipp_identify_printer(client
);
5155 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5156 "Operation not supported.");
5165 * Send the HTTP header and return...
5168 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5169 httpFlush(client
->http
); /* Flush trailing (junk) data */
5171 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5172 ippLength(client
->response
)));
5177 * 'process_job()' - Process a print job.
5180 static void * /* O - Thread exit status */
5181 process_job(_ipp_job_t
*job
) /* I - Job */
5183 job
->state
= IPP_JSTATE_PROCESSING
;
5184 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
5185 job
->processing
= time(NULL
);
5187 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
5189 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5194 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
5196 if (job
->printer
->command
)
5199 * Execute a command with the job spool file and wait for it to complete...
5202 int pid
, /* Process ID */
5203 status
; /* Exit status */
5204 time_t start
, /* Start time */
5207 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
5211 if ((pid
= fork()) == 0)
5214 * Child comes here...
5217 execlp(job
->printer
->command
, job
->printer
->command
, job
->filename
,
5224 * Unable to fork process...
5227 perror("Unable to start job processing command");
5232 * Wait for child to complete...
5236 while (waitpid(pid
, &status
, 0) < 0);
5238 while (wait(&status
) < 0);
5239 #endif /* HAVE_WAITPID */
5243 if (WIFEXITED(status
))
5244 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
5245 job
->printer
->command
, WEXITSTATUS(status
));
5247 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
5248 job
->printer
->command
, WTERMSIG(status
));
5250 job
->state
= IPP_JSTATE_ABORTED
;
5253 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
5254 job
->printer
->command
);
5258 * Make sure processing takes at least 5 seconds...
5262 if ((end
- start
) < 5)
5268 * Sleep for a random amount of time to simulate job processing.
5271 sleep(5 + (rand() % 11));
5275 job
->state
= IPP_JSTATE_CANCELED
;
5276 else if (job
->state
== IPP_JSTATE_PROCESSING
)
5277 job
->state
= IPP_JSTATE_COMPLETED
;
5279 job
->completed
= time(NULL
);
5280 job
->printer
->state
= IPP_PSTATE_IDLE
;
5281 job
->printer
->active_job
= NULL
;
5289 * 'register_printer()' - Register a printer object via Bonjour.
5292 static int /* O - 1 on success, 0 on error */
5294 _ipp_printer_t
*printer
, /* I - Printer */
5295 const char *location
, /* I - Location */
5296 const char *make
, /* I - Manufacturer */
5297 const char *model
, /* I - Model name */
5298 const char *formats
, /* I - Supported formats */
5299 const char *adminurl
, /* I - Web interface URL */
5300 const char *uuid
, /* I - Printer UUID */
5301 int color
, /* I - 1 = color, 0 = monochrome */
5302 int duplex
, /* I - 1 = duplex, 0 = simplex */
5303 const char *subtype
) /* I - Service subtype */
5305 DNSServiceErrorType error
; /* Error from Bonjour */
5306 char make_model
[256],/* Make and model together */
5307 product
[256], /* Product string */
5308 regtype
[256]; /* Bonjour service type */
5312 * Build the TXT record for IPP...
5315 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
5316 snprintf(product
, sizeof(product
), "(%s)", model
);
5318 TXTRecordCreate(&(printer
->ipp_txt
), 1024, NULL
);
5319 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 9, "ipp/print");
5320 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
5322 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
5325 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
5327 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
5329 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
5331 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
5332 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
5333 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
5335 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
5337 TXTRecordSetValue(&(printer
->ipp_txt
), "UUID", (uint8_t)strlen(uuid
), uuid
);
5339 TXTRecordSetValue(&(printer
->ipp_txt
), "TLS", 3, "1.2");
5340 # endif /* HAVE_SSL */
5343 * Create a shared service reference for Bonjour...
5346 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
5347 != kDNSServiceErr_NoError
)
5349 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
5354 * Register the _printer._tcp (LPD) service type with a port number of 0 to
5355 * defend our service name but not actually support LPD...
5358 printer
->printer_ref
= printer
->common_ref
;
5360 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
5361 kDNSServiceFlagsShareConnection
,
5362 0 /* interfaceIndex */, printer
->dnssd_name
,
5363 "_printer._tcp", NULL
/* domain */,
5364 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
5365 NULL
/* txtRecord */,
5366 (DNSServiceRegisterReply
)dnssd_callback
,
5367 printer
)) != kDNSServiceErr_NoError
)
5369 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
5370 printer
->dnssd_name
, error
);
5375 * Then register the _ipp._tcp (IPP) service type with the real port number to
5376 * advertise our IPP printer...
5379 printer
->ipp_ref
= printer
->common_ref
;
5381 if (subtype
&& *subtype
)
5382 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
5384 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
5386 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
5387 kDNSServiceFlagsShareConnection
,
5388 0 /* interfaceIndex */, printer
->dnssd_name
,
5389 regtype
, NULL
/* domain */,
5390 NULL
/* host */, htons(printer
->port
),
5391 TXTRecordGetLength(&(printer
->ipp_txt
)),
5392 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
5393 (DNSServiceRegisterReply
)dnssd_callback
,
5394 printer
)) != kDNSServiceErr_NoError
)
5396 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5397 printer
->dnssd_name
, regtype
, error
);
5403 * Then register the _ipps._tcp (IPP) service type with the real port number to
5404 * advertise our IPP printer...
5407 printer
->ipps_ref
= printer
->common_ref
;
5409 if (subtype
&& *subtype
)
5410 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
5412 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
5414 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
5415 kDNSServiceFlagsShareConnection
,
5416 0 /* interfaceIndex */, printer
->dnssd_name
,
5417 regtype
, NULL
/* domain */,
5418 NULL
/* host */, htons(printer
->port
),
5419 TXTRecordGetLength(&(printer
->ipp_txt
)),
5420 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
5421 (DNSServiceRegisterReply
)dnssd_callback
,
5422 printer
)) != kDNSServiceErr_NoError
)
5424 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5425 printer
->dnssd_name
, regtype
, error
);
5428 # endif /* HAVE_SSL */
5431 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
5432 * real port number to advertise our IPP printer...
5435 printer
->http_ref
= printer
->common_ref
;
5437 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
5438 kDNSServiceFlagsShareConnection
,
5439 0 /* interfaceIndex */, printer
->dnssd_name
,
5440 "_http._tcp,_printer", NULL
/* domain */,
5441 NULL
/* host */, htons(printer
->port
),
5442 0 /* txtLen */, NULL
, /* txtRecord */
5443 (DNSServiceRegisterReply
)dnssd_callback
,
5444 printer
)) != kDNSServiceErr_NoError
)
5446 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5447 printer
->dnssd_name
, regtype
, error
);
5453 #endif /* HAVE_DNSSD */
5457 * 'respond_http()' - Send a HTTP response.
5460 int /* O - 1 on success, 0 on failure */
5462 _ipp_client_t
*client
, /* I - Client */
5463 http_status_t code
, /* I - HTTP status of response */
5464 const char *content_encoding
, /* I - Content-Encoding of response */
5465 const char *type
, /* I - MIME media type of response */
5466 size_t length
) /* I - Length of response */
5468 char message
[1024]; /* Text message */
5471 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
5473 if (code
== HTTP_STATUS_CONTINUE
)
5476 * 100-continue doesn't send any headers...
5479 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
5483 * Format an error message...
5486 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
5488 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
5490 type
= "text/plain";
5491 length
= strlen(message
);
5497 * Send the HTTP response header...
5500 httpClearFields(client
->http
);
5502 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
5503 client
->operation
== HTTP_STATE_OPTIONS
)
5504 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
5508 if (!strcmp(type
, "text/html"))
5509 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
5510 "text/html; charset=utf-8");
5512 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
5514 if (content_encoding
)
5515 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
5518 httpSetLength(client
->http
, length
);
5520 if (httpWriteResponse(client
->http
, code
) < 0)
5524 * Send the response data...
5530 * Send a plain text message.
5533 if (httpPrintf(client
->http
, "%s", message
) < 0)
5536 if (httpWrite2(client
->http
, "", 0) < 0)
5539 else if (client
->response
)
5542 * Send an IPP response...
5545 debug_attributes("Response", client
->response
, 2);
5547 ippSetState(client
->response
, IPP_STATE_IDLE
);
5549 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
5558 * 'respond_ipp()' - Send an IPP response.
5562 respond_ipp(_ipp_client_t
*client
, /* I - Client */
5563 ipp_status_t status
, /* I - status-code */
5564 const char *message
, /* I - printf-style status-message */
5565 ...) /* I - Additional args as needed */
5567 const char *formatted
= NULL
; /* Formatted message */
5570 ippSetStatusCode(client
->response
, status
);
5574 va_list ap
; /* Pointer to additional args */
5575 ipp_attribute_t
*attr
; /* New status-message attribute */
5577 va_start(ap
, message
);
5578 if ((attr
= ippFindAttribute(client
->response
, "status-message",
5579 IPP_TAG_TEXT
)) != NULL
)
5580 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
5582 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
5583 "status-message", NULL
, message
, ap
);
5586 formatted
= ippGetString(attr
, 0, NULL
);
5590 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
5591 ippOpString(client
->operation_id
), ippErrorString(status
),
5594 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
5595 ippOpString(client
->operation_id
), ippErrorString(status
));
5600 * 'respond_unsupported()' - Respond with an unsupported attribute.
5604 respond_unsupported(
5605 _ipp_client_t
*client
, /* I - Client */
5606 ipp_attribute_t
*attr
) /* I - Atribute */
5608 ipp_attribute_t
*temp
; /* Copy of attribute */
5611 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5612 "Unsupported %s %s%s value.", ippGetName(attr
),
5613 ippGetCount(attr
) > 1 ? "1setOf " : "",
5614 ippTagString(ippGetValueTag(attr
)));
5616 temp
= ippCopyAttribute(client
->response
, attr
, 0);
5617 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
5622 * 'run_printer()' - Run the printer service.
5626 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
5628 int num_fds
; /* Number of file descriptors */
5629 struct pollfd polldata
[3]; /* poll() data */
5630 int timeout
; /* Timeout for poll() */
5631 _ipp_client_t
*client
; /* New client */
5635 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5638 polldata
[0].fd
= printer
->ipv4
;
5639 polldata
[0].events
= POLLIN
;
5641 polldata
[1].fd
= printer
->ipv6
;
5642 polldata
[1].events
= POLLIN
;
5647 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
5648 polldata
[num_fds
++].events
= POLLIN
;
5649 #endif /* HAVE_DNSSD */
5652 * Loop until we are killed or have a hard error...
5657 if (cupsArrayCount(printer
->jobs
))
5662 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
5664 perror("poll() failed");
5668 if (polldata
[0].revents
& POLLIN
)
5670 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
5672 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5674 perror("Unable to create client thread");
5675 delete_client(client
);
5680 if (polldata
[1].revents
& POLLIN
)
5682 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
5684 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5686 perror("Unable to create client thread");
5687 delete_client(client
);
5693 if (polldata
[2].revents
& POLLIN
)
5694 DNSServiceProcessResult(printer
->common_ref
);
5695 #endif /* HAVE_DNSSD */
5698 * Clean out old jobs...
5701 clean_jobs(printer
);
5707 * 'time_string()' - Return the local time in hours, minutes, and seconds.
5711 time_string(time_t tv
, /* I - Time value */
5712 char *buffer
, /* I - Buffer */
5713 size_t bufsize
) /* I - Size of buffer */
5715 struct tm
*curtime
= localtime(&tv
);
5718 strftime(buffer
, bufsize
, "%X", curtime
);
5724 * 'usage()' - Show program usage.
5728 usage(int status
) /* O - Exit status */
5732 puts(CUPS_SVERSION
" - Copyright 2010-2013 by Apple Inc. All rights "
5737 puts("Usage: ippserver [options] \"name\"");
5740 puts("-2 Supports 2-sided printing (default=1-sided)");
5741 puts("-M manufacturer Manufacturer name (default=Test)");
5742 puts("-P PIN printing mode");
5743 puts("-c command Run command for every print job");
5744 printf("-d spool-directory Spool directory "
5745 "(default=/tmp/ippserver.%d)\n", (int)getpid());
5746 puts("-f type/subtype[,...] List of supported types "
5747 "(default=application/pdf,image/jpeg)");
5748 puts("-h Show program help");
5749 puts("-i iconfile.png PNG icon file (default=printer.png)");
5750 puts("-k Keep job spool files");
5751 puts("-l location Location of printer (default=empty string)");
5752 puts("-m model Model name (default=Printer)");
5753 puts("-n hostname Hostname for printer");
5754 puts("-p port Port number (default=auto)");
5756 puts("-r subtype Bonjour service subtype (default=_print)");
5757 #endif /* HAVE_DNSSD */
5758 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
5759 puts("-v[vvv] Be (very) verbose");
5766 * 'valid_doc_attributes()' - Determine whether the document attributes are
5769 * When one or more document attributes are invalid, this function adds a
5770 * suitable response and attributes to the unsupported group.
5773 static int /* O - 1 if valid, 0 if not */
5774 valid_doc_attributes(
5775 _ipp_client_t
*client
) /* I - Client */
5777 int valid
= 1; /* Valid attributes? */
5778 ipp_op_t op
= ippGetOperation(client
->request
);
5780 const char *op_name
= ippOpString(op
);
5781 /* IPP operation name */
5782 ipp_attribute_t
*attr
, /* Current attribute */
5783 *supported
; /* xxx-supported attribute */
5784 const char *compression
= NULL
,
5785 /* compression value */
5786 *format
= NULL
; /* document-format value */
5790 * Check operation attributes...
5793 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
5796 * If compression is specified, only accept a supported value in a Print-Job
5797 * or Send-Document request...
5800 compression
= ippGetString(attr
, 0, NULL
);
5801 supported
= ippFindAttribute(client
->printer
->attrs
,
5802 "compression-supported", IPP_TAG_KEYWORD
);
5804 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5805 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
5806 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
5807 op
!= IPP_OP_VALIDATE_JOB
) ||
5808 !ippContainsString(supported
, compression
))
5810 respond_unsupported(client
, attr
);
5815 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
5817 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
5819 if (strcmp(compression
, "none"))
5822 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
5823 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
5829 * Is it a format we support?
5832 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
5834 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
5835 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
5837 respond_unsupported(client
, attr
);
5842 format
= ippGetString(attr
, 0, NULL
);
5844 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
5845 client
->hostname
, op_name
, format
);
5847 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
5852 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
5854 format
= "application/octet-stream"; /* Should never happen */
5856 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
5859 if (!strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
5862 * Auto-type the file using the first 8 bytes of the file...
5865 unsigned char header
[8]; /* First 8 bytes of file */
5867 memset(header
, 0, sizeof(header
));
5868 httpPeek(client
->http
, (char *)header
, sizeof(header
));
5870 if (!memcmp(header
, "%PDF", 4))
5871 format
= "application/pdf";
5872 else if (!memcmp(header
, "%!", 2))
5873 format
= "application/postscript";
5874 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
5875 format
= "image/jpeg";
5876 else if (!memcmp(header
, "\211PNG", 4))
5877 format
= "image/png";
5878 else if (!memcmp(header
, "RAS2", 4))
5879 format
= "image/pwg-raster";
5880 else if (!memcmp(header
, "UNIRAST", 8))
5881 format
= "image/urf";
5887 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
5888 client
->hostname
, op_name
, format
);
5890 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
5894 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
5896 respond_unsupported(client
, attr
);
5904 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
5905 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
5912 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
5914 * When one or more job attributes are invalid, this function adds a suitable
5915 * response and attributes to the unsupported group.
5918 static int /* O - 1 if valid, 0 if not */
5919 valid_job_attributes(
5920 _ipp_client_t
*client
) /* I - Client */
5922 int i
, /* Looping var */
5923 valid
= 1; /* Valid attributes? */
5924 ipp_attribute_t
*attr
, /* Current attribute */
5925 *supported
; /* xxx-supported attribute */
5929 * Check operation attributes...
5932 valid
= valid_doc_attributes(client
);
5935 * Check the various job template attributes...
5938 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
5940 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5941 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
5943 respond_unsupported(client
, attr
);
5948 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
5950 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
5952 respond_unsupported(client
, attr
);
5957 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
5959 if (ippGetCount(attr
) != 1 ||
5960 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5961 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5962 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5963 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
5965 respond_unsupported(client
, attr
);
5970 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
5972 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
5974 respond_unsupported(client
, attr
);
5979 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
5981 if (ippGetCount(attr
) != 1 ||
5982 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5983 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
5985 respond_unsupported(client
, attr
);
5989 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
5992 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
5994 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
5996 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5997 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
5999 respond_unsupported(client
, attr
);
6004 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
6006 if (ippGetCount(attr
) != 1 ||
6007 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6008 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6009 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6010 strcmp(ippGetString(attr
, 0, NULL
), "none"))
6012 respond_unsupported(client
, attr
);
6017 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
6019 if (ippGetCount(attr
) != 1 ||
6020 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6021 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6022 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
6024 respond_unsupported(client
, attr
);
6030 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
6032 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
6035 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
6037 respond_unsupported(client
, attr
);
6043 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
6045 if (ippGetCount(attr
) != 1 ||
6046 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
6048 respond_unsupported(client
, attr
);
6051 /* TODO: check for valid media-col */
6054 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
6056 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6057 (strcmp(ippGetString(attr
, 0, NULL
),
6058 "separate-documents-uncollated-copies") &&
6059 strcmp(ippGetString(attr
, 0, NULL
),
6060 "separate-documents-collated-copies")))
6062 respond_unsupported(client
, attr
);
6067 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
6069 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6070 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
6071 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
6073 respond_unsupported(client
, attr
);
6078 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
6080 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
6082 respond_unsupported(client
, attr
);
6087 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
6089 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6090 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
6091 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
6093 respond_unsupported(client
, attr
);
6098 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
6100 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
6102 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
6105 respond_unsupported(client
, attr
);
6110 int count
, /* Number of supported values */
6111 xdpi
, /* Horizontal resolution for job template attribute */
6112 ydpi
, /* Vertical resolution for job template attribute */
6113 sydpi
; /* Vertical resolution for supported value */
6114 ipp_res_t units
, /* Units for job template attribute */
6115 sunits
; /* Units for supported value */
6117 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
6118 count
= ippGetCount(supported
);
6120 for (i
= 0; i
< count
; i
++)
6122 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
6128 respond_unsupported(client
, attr
);
6134 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
6136 const char *sides
= ippGetString(attr
, 0, NULL
);
6137 /* "sides" value... */
6139 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
6141 respond_unsupported(client
, attr
);
6144 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
6146 if (!ippContainsString(supported
, sides
))
6148 respond_unsupported(client
, attr
);
6152 else if (strcmp(sides
, "one-sided"))
6154 respond_unsupported(client
, attr
);