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
,
378 const char *location
, const char *make
,
379 const char *model
, const char *formats
,
380 const char *adminurl
, int color
,
381 int duplex
, const char *regtype
);
382 #endif /* HAVE_DNSSD */
383 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
384 const char *content_coding
,
385 const char *type
, size_t length
);
386 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
387 const char *message
, ...)
388 __attribute__ ((__format__ (__printf__
, 3, 4)));
389 static void respond_unsupported(_ipp_client_t
*client
,
390 ipp_attribute_t
*attr
);
391 static void run_printer(_ipp_printer_t
*printer
);
392 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
393 static void usage(int status
) __attribute__((noreturn
));
394 static int valid_doc_attributes(_ipp_client_t
*client
);
395 static int valid_job_attributes(_ipp_client_t
*client
);
402 static int KeepFiles
= 0,
407 * 'main()' - Main entry to the sample server.
410 int /* O - Exit status */
411 main(int argc
, /* I - Number of command-line args */
412 char *argv
[]) /* I - Command-line arguments */
414 int i
; /* Looping var */
415 const char *opt
, /* Current option character */
416 *command
= NULL
, /* Command to run with job files */
417 *servername
= NULL
, /* Server host name */
418 *name
= NULL
, /* Printer name */
419 *location
= "", /* Location of printer */
420 *make
= "Test", /* Manufacturer */
421 *model
= "Printer", /* Model */
422 *icon
= "printer.png", /* Icon file */
423 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
424 /* Supported formats */
426 const char *keypath
= NULL
; /* Keychain path */
427 #endif /* HAVE_SSL */
429 const char *subtype
= "_print"; /* Bonjour service subtype */
430 #endif /* HAVE_DNSSD */
431 int port
= 8631, /* Port number (0 = auto) */
432 duplex
= 0, /* Duplex mode */
433 ppm
= 10, /* Pages per minute for mono */
434 ppm_color
= 0, /* Pages per minute for color */
435 pin
= 0; /* PIN printing mode? */
436 char directory
[1024] = ""; /* Spool directory */
437 _ipp_printer_t
*printer
; /* Printer object */
441 * Parse command-line arguments...
444 for (i
= 1; i
< argc
; i
++)
445 if (argv
[i
][0] == '-')
447 for (opt
= argv
[i
] + 1; *opt
; opt
++)
451 case '2' : /* -2 (enable 2-sided printing) */
456 case 'K' : /* -K keypath */
462 #endif /* HAVE_SSL */
464 case 'M' : /* -M manufacturer */
471 case 'P' : /* -P (PIN printing mode) */
475 case 'c' : /* -c command */
483 case 'd' : /* -d spool-directory */
487 strlcpy(directory
, argv
[i
], sizeof(directory
));
490 case 'f' : /* -f type/subtype[,...] */
497 case 'h' : /* -h (show help) */
500 case 'i' : /* -i icon.png */
507 case 'k' : /* -k (keep files) */
511 case 'l' : /* -l location */
518 case 'm' : /* -m model */
525 case 'n' : /* -n hostname */
529 servername
= argv
[i
];
532 case 'p' : /* -p port */
534 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
536 port
= atoi(argv
[i
]);
540 case 'r' : /* -r subtype */
546 #endif /* HAVE_DNSSD */
548 case 's' : /* -s speed[,color-speed] */
552 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
556 case 'v' : /* -v (be verbose) */
560 default : /* Unknown */
561 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
572 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
580 * Apply defaults as needed...
585 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
587 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
589 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
590 directory
, strerror(errno
));
595 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
599 cupsSetServerCredentials(keypath
, servername
, 1);
600 #endif /* HAVE_SSL */
603 * Create the printer...
606 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
607 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
610 #endif /* HAVE_DNSSD */
611 directory
, command
)) == NULL
)
615 * Run the print service...
618 run_printer(printer
);
621 * Destroy the printer and exit...
624 delete_printer(printer
);
631 * 'clean_jobs()' - Clean out old (completed) jobs.
635 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
637 _ipp_job_t
*job
; /* Current job */
638 time_t cleantime
; /* Clean time */
641 if (cupsArrayCount(printer
->jobs
) == 0)
644 cleantime
= time(NULL
) - 60;
646 _cupsRWLockWrite(&(printer
->rwlock
));
647 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
649 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
650 if (job
->completed
&& job
->completed
< cleantime
)
652 cupsArrayRemove(printer
->jobs
, job
);
657 _cupsRWUnlock(&(printer
->rwlock
));
662 * 'compare_jobs()' - Compare two jobs.
665 static int /* O - Result of comparison */
666 compare_jobs(_ipp_job_t
*a
, /* I - First job */
667 _ipp_job_t
*b
) /* I - Second job */
669 return (b
->id
- a
->id
);
674 * 'copy_attributes()' - Copy attributes from one request to another.
678 copy_attributes(ipp_t
*to
, /* I - Destination request */
679 ipp_t
*from
, /* I - Source request */
680 cups_array_t
*ra
, /* I - Requested attributes */
681 ipp_tag_t group_tag
, /* I - Group to copy */
682 int quickcopy
) /* I - Do a quick copy? */
684 _ipp_filter_t filter
; /* Filter data */
688 filter
.group_tag
= group_tag
;
690 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
695 * 'copy_job_attrs()' - Copy job attributes to the response.
700 _ipp_client_t
*client
, /* I - Client */
701 _ipp_job_t
*job
, /* I - Job */
702 cups_array_t
*ra
) /* I - requested-attributes */
704 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
706 if (job
->completed
&& (!ra
|| cupsArrayFind(ra
, "date-time-at-completed")))
707 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
709 if (job
->processing
&& (!ra
|| cupsArrayFind(ra
, "date-time-at-processing")))
710 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
712 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
713 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
715 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
716 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
718 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
719 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
721 if (!ra
|| cupsArrayFind(ra
, "job-state"))
722 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
723 "job-state", job
->state
);
725 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
729 case IPP_JSTATE_PENDING
:
730 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
733 case IPP_JSTATE_HELD
:
735 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
736 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
737 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
739 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
742 case IPP_JSTATE_PROCESSING
:
744 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
746 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
749 case IPP_JSTATE_STOPPED
:
750 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
753 case IPP_JSTATE_CANCELED
:
754 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
757 case IPP_JSTATE_ABORTED
:
758 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
761 case IPP_JSTATE_COMPLETED
:
762 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
767 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
771 case IPP_JSTATE_PENDING
:
772 ippAddString(client
->response
, IPP_TAG_JOB
,
773 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
777 case IPP_JSTATE_HELD
:
779 ippAddString(client
->response
, IPP_TAG_JOB
,
780 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
781 "job-state-reasons", NULL
, "job-incoming");
782 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
783 ippAddString(client
->response
, IPP_TAG_JOB
,
784 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
785 "job-state-reasons", NULL
, "job-hold-until-specified");
787 ippAddString(client
->response
, IPP_TAG_JOB
,
788 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
789 "job-state-reasons", NULL
, "job-data-insufficient");
792 case IPP_JSTATE_PROCESSING
:
794 ippAddString(client
->response
, IPP_TAG_JOB
,
795 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
796 "job-state-reasons", NULL
, "processing-to-stop-point");
798 ippAddString(client
->response
, IPP_TAG_JOB
,
799 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
800 "job-state-reasons", NULL
, "job-printing");
803 case IPP_JSTATE_STOPPED
:
804 ippAddString(client
->response
, IPP_TAG_JOB
,
805 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
806 NULL
, "job-stopped");
809 case IPP_JSTATE_CANCELED
:
810 ippAddString(client
->response
, IPP_TAG_JOB
,
811 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
812 NULL
, "job-canceled-by-user");
815 case IPP_JSTATE_ABORTED
:
816 ippAddString(client
->response
, IPP_TAG_JOB
,
817 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
818 NULL
, "aborted-by-system");
821 case IPP_JSTATE_COMPLETED
:
822 ippAddString(client
->response
, IPP_TAG_JOB
,
823 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
824 NULL
, "job-completed-successfully");
829 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
830 ippAddInteger(client
->response
, IPP_TAG_JOB
,
831 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
832 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
834 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
835 ippAddInteger(client
->response
, IPP_TAG_JOB
,
836 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
837 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
842 * 'create_client()' - Accept a new network connection and create a client
846 static _ipp_client_t
* /* O - Client */
847 create_client(_ipp_printer_t
*printer
, /* I - Printer */
848 int sock
) /* I - Listen socket */
850 _ipp_client_t
*client
; /* Client */
853 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
855 perror("Unable to allocate memory for client");
859 client
->printer
= printer
;
862 * Accept the client and get the remote address...
865 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
867 perror("Unable to accept client connection");
874 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
877 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
884 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
888 static _ipp_job_t
* /* O - Job */
889 create_job(_ipp_client_t
*client
) /* I - Client */
891 _ipp_job_t
*job
; /* Job */
892 ipp_attribute_t
*attr
; /* Job attribute */
893 char uri
[1024], /* job-uri value */
894 uuid
[64]; /* job-uuid value */
897 _cupsRWLockWrite(&(client
->printer
->rwlock
));
898 if (client
->printer
->active_job
&&
899 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
902 * Only accept a single job at a time...
905 _cupsRWLockWrite(&(client
->printer
->rwlock
));
910 * Allocate and initialize the job object...
913 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
915 perror("Unable to allocate memory for job");
919 job
->printer
= client
->printer
;
920 job
->attrs
= ippNew();
921 job
->state
= IPP_JSTATE_HELD
;
925 * Copy all of the job attributes...
928 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
931 * Get the requesting-user-name, document format, and priority...
934 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
935 job
->username
= ippGetString(attr
, 0, NULL
);
937 job
->username
= "anonymous";
939 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
941 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
943 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
)
944 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, ippGetString(attr
, 0, NULL
));
946 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
947 job
->format
= ippGetString(attr
, 0, NULL
);
949 job
->format
= "application/octet-stream";
951 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
952 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
955 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
956 job
->name
= ippGetString(attr
, 0, NULL
);
958 job
->name
= "Untitled";
960 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, job
->name
);
962 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
963 job
->impressions
= ippGetInteger(attr
, 0);
966 * Add job description attributes and add to the jobs array...
969 job
->id
= client
->printer
->next_job_id
++;
971 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
972 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
974 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
975 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
976 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
977 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
978 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
979 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
981 cupsArrayAdd(client
->printer
->jobs
, job
);
982 client
->printer
->active_job
= job
;
984 _cupsRWUnlock(&(client
->printer
->rwlock
));
991 * 'create_listener()' - Create a listener socket.
994 static int /* O - Listener socket or -1 on error */
995 create_listener(int family
, /* I - Address family */
996 int *port
) /* IO - Port number */
998 int sock
; /* Listener socket */
999 http_addrlist_t
*addrlist
; /* Listen address */
1000 char service
[255]; /* Service port */
1005 *port
= 8000 + (getuid() % 1000);
1006 fprintf(stderr
, "Listening on port %d.\n", *port
);
1009 snprintf(service
, sizeof(service
), "%d", *port
);
1010 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1013 sock
= httpAddrListen(&(addrlist
->addr
), *port
);
1015 httpAddrFreeList(addrlist
);
1022 * 'create_media_col()' - Create a media-col value.
1025 static ipp_t
* /* O - media-col collection */
1026 create_media_col(const char *media
, /* I - Media name */
1027 const char *source
, /* I - Media source */
1028 const char *type
, /* I - Media type */
1029 int width
, /* I - x-dimension in 2540ths */
1030 int length
, /* I - y-dimension in 2540ths */
1031 int margins
) /* I - Value for margins */
1033 ipp_t
*media_col
= ippNew(), /* media-col value */
1034 *media_size
= create_media_size(width
, length
);
1035 /* media-size value */
1036 char media_key
[256]; /* media-key value */
1040 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1042 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1044 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1046 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1048 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1050 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1051 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1052 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1053 "media-bottom-margin", margins
);
1054 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1055 "media-left-margin", margins
);
1056 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1057 "media-right-margin", margins
);
1058 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1059 "media-top-margin", margins
);
1061 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1063 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1065 ippDelete(media_size
);
1072 * 'create_media_size()' - Create a media-size value.
1075 static ipp_t
* /* O - media-col collection */
1076 create_media_size(int width
, /* I - x-dimension in 2540ths */
1077 int length
) /* I - y-dimension in 2540ths */
1079 ipp_t
*media_size
= ippNew(); /* media-size value */
1082 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1084 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1087 return (media_size
);
1092 * 'create_printer()' - Create, register, and listen for connections to a
1096 static _ipp_printer_t
* /* O - Printer */
1097 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1098 const char *name
, /* I - printer-name */
1099 const char *location
, /* I - printer-location */
1100 const char *make
, /* I - printer-make-and-model */
1101 const char *model
, /* I - printer-make-and-model */
1102 const char *icon
, /* I - printer-icons */
1103 const char *docformats
, /* I - document-format-supported */
1104 int ppm
, /* I - Pages per minute in grayscale */
1105 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1106 int duplex
, /* I - 1 = duplex, 0 = simplex */
1107 int port
, /* I - Port for listeners or 0 for auto */
1108 int pin
, /* I - Require PIN printing */
1110 const char *subtype
, /* I - Bonjour service subtype */
1111 #endif /* HAVE_DNSSD */
1112 const char *directory
, /* I - Spool directory */
1113 const char *command
) /* I - Command to run on job files */
1115 int i
, j
; /* Looping vars */
1116 _ipp_printer_t
*printer
; /* Printer */
1117 char hostname
[256], /* Hostname */
1118 uri
[1024], /* Printer URI */
1119 icons
[1024], /* printer-icons URI */
1120 adminurl
[1024], /* printer-more-info URI */
1121 supplyurl
[1024],/* printer-supply-info-uri URI */
1122 device_id
[1024],/* printer-device-id */
1123 make_model
[128],/* printer-make-and-model */
1124 uuid
[128]; /* printer-uuid */
1125 int num_formats
; /* Number of document-format-supported values */
1126 char *defformat
, /* document-format-default value */
1127 *formats
[100], /* document-format-supported values */
1128 *ptr
; /* Pointer into string */
1129 const char *prefix
; /* Prefix string */
1130 int num_database
; /* Number of database values */
1131 ipp_attribute_t
*media_col_database
,
1132 /* media-col-database value */
1133 *media_size_supported
;
1134 /* media-size-supported value */
1135 ipp_t
*media_col_default
;
1136 /* media-col-default value */
1137 int media_col_index
;/* Current media-col-database value */
1138 int k_supported
; /* Maximum file size supported */
1140 struct statvfs spoolinfo
; /* FS info for spool directory */
1141 double spoolsize
; /* FS size */
1142 #elif defined(HAVE_STATFS)
1143 struct statfs spoolinfo
; /* FS info for spool directory */
1144 double spoolsize
; /* FS size */
1145 #endif /* HAVE_STATVFS */
1146 static const int orients
[4] = /* orientation-requested-supported values */
1148 IPP_ORIENT_PORTRAIT
,
1149 IPP_ORIENT_LANDSCAPE
,
1150 IPP_ORIENT_REVERSE_LANDSCAPE
,
1151 IPP_ORIENT_REVERSE_PORTRAIT
1153 static const char * const versions
[] =/* ipp-versions-supported values */
1159 static const char * const features
[] =/* ipp-features-supported values */
1163 static const int ops
[] = /* operations-supported values */
1167 IPP_OP_VALIDATE_JOB
,
1169 IPP_OP_SEND_DOCUMENT
,
1172 IPP_OP_GET_JOB_ATTRIBUTES
,
1174 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1175 IPP_OP_CANCEL_MY_JOBS
,
1177 IPP_OP_IDENTIFY_PRINTER
1179 static const char * const charsets
[] =/* charset-supported values */
1184 static const char * const compressions
[] =/* compression-supported values */
1189 #endif /* HAVE_LIBZ */
1192 static const char * const identify_actions
[] =
1197 static const char * const job_creation
[] =
1198 { /* job-creation-attributes-supported values */
1200 "ipp-attribute-fidelity",
1202 "job-accounting-user-id",
1208 "multiple-document-handling",
1209 "orientation-requested",
1213 static const char * const media_col_supported
[] =
1214 { /* media-col-supported values */
1215 "media-bottom-margin",
1216 "media-left-margin",
1217 "media-right-margin",
1223 static const int media_xxx_margin_supported
[] =
1224 { /* media-xxx-margin-supported values */
1228 static const char * const multiple_document_handling
[] =
1229 { /* multiple-document-handling-supported values */
1230 "separate-documents-uncollated-copies",
1231 "separate-documents-collated-copies"
1233 static const char * const overrides
[] =
1234 { /* overrides-supported */
1238 static const char * const print_color_mode_supported
[] =
1239 { /* print-color-mode-supported values */
1244 static const int print_quality_supported
[] =
1245 { /* print-quality-supported values */
1250 static const int pwg_raster_document_resolution_supported
[] =
1256 static const char * const pwg_raster_document_type_supported
[] =
1264 static const char * const reference_uri_schemes_supported
[] =
1265 { /* reference-uri-schemes-supported */
1271 #endif /* HAVE_SSL */
1273 static const char * const sides_supported
[] =
1274 { /* sides-supported values */
1276 "two-sided-long-edge",
1277 "two-sided-short-edge"
1279 static const char * const which_jobs
[] =
1280 { /* which-jobs-supported values */
1289 "processing-stopped"
1294 * Allocate memory for the printer...
1297 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1299 perror("Unable to allocate memory for printer");
1305 printer
->name
= strdup(name
);
1307 printer
->dnssd_name
= strdup(printer
->name
);
1308 #endif /* HAVE_DNSSD */
1309 printer
->command
= command
? strdup(command
) : NULL
;
1310 printer
->directory
= strdup(directory
);
1311 printer
->hostname
= strdup(servername
? servername
: httpGetHostname(NULL
, hostname
, sizeof(hostname
)));
1312 printer
->port
= port
;
1313 printer
->start_time
= time(NULL
);
1314 printer
->config_time
= printer
->start_time
;
1315 printer
->state
= IPP_PSTATE_IDLE
;
1316 printer
->state_reasons
= _IPP_PREASON_NONE
;
1317 printer
->state_time
= printer
->start_time
;
1318 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1319 printer
->next_job_id
= 1;
1321 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1322 printer
->hostname
, printer
->port
, "/ipp/print");
1323 printer
->uri
= strdup(uri
);
1324 printer
->urilen
= strlen(uri
);
1327 printer
->icon
= strdup(icon
);
1329 printer
->main_size
= _IPP_MEDIA_SIZE_A4
;
1330 printer
->main_type
= _IPP_MEDIA_TYPE_STATIONERY
;
1331 printer
->main_level
= 500;
1333 printer
->envelope_size
= _IPP_MEDIA_SIZE_NONE
;
1334 printer
->envelope_level
= 0;
1336 printer
->photo_size
= _IPP_MEDIA_SIZE_NONE
;
1337 printer
->photo_type
= _IPP_MEDIA_TYPE_NONE
;
1338 printer
->photo_level
= 0;
1340 printer
->supplies
[_IPP_SUPPLY_CYAN
] = 100;
1341 printer
->supplies
[_IPP_SUPPLY_MAGENTA
] = 100;
1342 printer
->supplies
[_IPP_SUPPLY_YELLOW
] = 100;
1343 printer
->supplies
[_IPP_SUPPLY_BLACK
] = 100;
1344 printer
->supplies
[_IPP_SUPPLY_WASTE
] = 0;
1346 _cupsRWInit(&(printer
->rwlock
));
1349 * Create the listener sockets...
1352 if ((printer
->ipv4
= create_listener(AF_INET
, &(printer
->port
))) < 0)
1354 perror("Unable to create IPv4 listener");
1358 if ((printer
->ipv6
= create_listener(AF_INET6
, &(printer
->port
))) < 0)
1360 perror("Unable to create IPv6 listener");
1365 * Prepare values for the printer attributes...
1368 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1369 printer
->hostname
, printer
->port
, "/icon.png");
1370 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/");
1371 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/supplies");
1375 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1376 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1377 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1380 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1383 formats
[0] = strdup(docformats
);
1384 defformat
= formats
[0];
1385 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1388 formats
[num_formats
++] = ptr
;
1390 if (!strcasecmp(ptr
, "application/octet-stream"))
1394 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1395 ptr
= device_id
+ strlen(device_id
);
1397 for (i
= 0; i
< num_formats
; i
++)
1399 if (!strcasecmp(formats
[i
], "application/pdf"))
1400 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1401 else if (!strcasecmp(formats
[i
], "application/postscript"))
1402 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1403 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1404 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1405 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1406 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1407 else if (!strcasecmp(formats
[i
], "image/png"))
1408 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1409 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1410 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1415 strlcat(device_id
, ";", sizeof(device_id
));
1418 * Get the maximum spool size based on the size of the filesystem used for
1419 * the spool directory. If the host OS doesn't support the statfs call
1420 * or the filesystem is larger than 2TiB, always report INT_MAX.
1424 if (statvfs(printer
->directory
, &spoolinfo
))
1425 k_supported
= INT_MAX
;
1426 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1427 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1428 k_supported
= INT_MAX
;
1430 k_supported
= (int)spoolsize
;
1432 #elif defined(HAVE_STATFS)
1433 if (statfs(printer
->directory
, &spoolinfo
))
1434 k_supported
= INT_MAX
;
1435 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1436 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1437 k_supported
= INT_MAX
;
1439 k_supported
= (int)spoolsize
;
1442 k_supported
= INT_MAX
;
1443 #endif /* HAVE_STATVFS */
1446 * Create the printer attributes. This list of attributes is sorted to improve
1447 * performance when the client provides a requested-attributes attribute...
1450 printer
->attrs
= ippNew();
1452 /* charset-configured */
1453 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1454 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1455 "charset-configured", NULL
, "utf-8");
1457 /* charset-supported */
1458 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1459 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1460 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1463 /* color-supported */
1464 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1467 /* compression-supported */
1468 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1469 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1470 "compression-supported",
1471 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1474 /* copies-default */
1475 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1476 "copies-default", 1);
1478 /* copies-supported */
1479 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1481 /* document-format-default */
1482 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1483 "document-format-default", NULL
, defformat
);
1485 /* document-format-supported */
1486 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1487 "document-format-supported", num_formats
, NULL
,
1488 (const char * const *)formats
);
1490 /* document-password-supported */
1491 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1493 /* finishings-default */
1494 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1495 "finishings-default", IPP_FINISHINGS_NONE
);
1497 /* finishings-supported */
1498 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1499 "finishings-supported", IPP_FINISHINGS_NONE
);
1501 /* generated-natural-language-supported */
1502 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1503 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1504 "generated-natural-language-supported", NULL
, "en");
1506 /* identify-actions-default */
1507 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1509 /* identify-actions-supported */
1510 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
);
1512 /* ipp-features-supported */
1513 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1515 /* ipp-versions-supported */
1516 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1518 /* job-account-id-default */
1519 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1521 /* job-account-id-supported */
1522 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1524 /* job-accounting-user-id-default */
1525 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1527 /* job-accounting-user-id-supported */
1528 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1530 /* job-creation-attributes-supported */
1531 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
);
1533 /* job-ids-supported */
1534 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1536 /* job-k-octets-supported */
1537 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1540 /* job-password-supported */
1541 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1542 "job-password-supported", 4);
1544 /* job-preferred-attributes-supported */
1545 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-preferred-attributes-supported", 0);
1547 /* job-priority-default */
1548 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1549 "job-priority-default", 50);
1551 /* job-priority-supported */
1552 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1553 "job-priority-supported", 100);
1555 /* job-sheets-default */
1556 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1557 IPP_CONST_TAG(IPP_TAG_NAME
),
1558 "job-sheets-default", NULL
, "none");
1560 /* job-sheets-supported */
1561 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1562 IPP_CONST_TAG(IPP_TAG_NAME
),
1563 "job-sheets-supported", NULL
, "none");
1565 /* media-bottom-margin-supported */
1566 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1567 "media-bottom-margin-supported",
1568 (int)(sizeof(media_xxx_margin_supported
) /
1569 sizeof(media_xxx_margin_supported
[0])),
1570 media_xxx_margin_supported
);
1572 /* media-col-database */
1573 for (num_database
= 0, i
= 0;
1574 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1577 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1578 num_database
+= 3; /* auto + manual + envelope */
1579 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1580 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1582 num_database
+= 2; /* Regular + borderless */
1585 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1586 "media-col-database", num_database
,
1588 for (media_col_index
= 0, i
= 0;
1589 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1592 switch (media_col_sizes
[i
][2])
1596 * Regular + borderless for the general class; no source/type
1600 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]));
1601 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]));
1604 case _IPP_ENV_ONLY
:
1606 * Regular margins for "auto", "manual", and "envelope" sources.
1609 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]));
1610 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]));
1611 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]));
1613 case _IPP_PHOTO_ONLY
:
1615 * Photos have specific media types and can only be printed via
1616 * the auto, manual, and photo sources...
1620 j
< (int)(sizeof(media_type_supported
) /
1621 sizeof(media_type_supported
[0]));
1624 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1627 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]));
1628 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]));
1629 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]));
1635 /* media-col-default */
1636 media_col_default
= create_media_col(media_supported
[0],
1637 media_source_supported
[0],
1638 media_type_supported
[0],
1639 media_col_sizes
[0][0],
1640 media_col_sizes
[0][1],
1641 media_xxx_margin_supported
[1]);
1643 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1645 ippDelete(media_col_default
);
1647 /* media-col-supported */
1648 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1649 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1650 "media-col-supported",
1651 (int)(sizeof(media_col_supported
) /
1652 sizeof(media_col_supported
[0])), NULL
,
1653 media_col_supported
);
1656 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1657 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1658 "media-default", NULL
, media_supported
[0]);
1660 /* media-left-margin-supported */
1661 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1662 "media-left-margin-supported",
1663 (int)(sizeof(media_xxx_margin_supported
) /
1664 sizeof(media_xxx_margin_supported
[0])),
1665 media_xxx_margin_supported
);
1667 /* media-right-margin-supported */
1668 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1669 "media-right-margin-supported",
1670 (int)(sizeof(media_xxx_margin_supported
) /
1671 sizeof(media_xxx_margin_supported
[0])),
1672 media_xxx_margin_supported
);
1674 /* media-supported */
1675 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1676 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1678 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1679 NULL
, media_supported
);
1681 /* media-size-supported */
1682 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1683 "media-size-supported",
1684 (int)(sizeof(media_col_sizes
) /
1685 sizeof(media_col_sizes
[0])),
1688 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1690 ippSetCollection(printer
->attrs
, &media_size_supported
, i
,
1691 create_media_size(media_col_sizes
[i
][0],
1692 media_col_sizes
[i
][1]));
1694 /* media-source-supported */
1695 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
);
1697 /* media-top-margin-supported */
1698 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1699 "media-top-margin-supported",
1700 (int)(sizeof(media_xxx_margin_supported
) /
1701 sizeof(media_xxx_margin_supported
[0])),
1702 media_xxx_margin_supported
);
1704 /* media-type-supported */
1705 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
);
1707 /* multiple-document-handling-supported */
1708 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
);
1710 /* multiple-document-jobs-supported */
1711 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1713 /* multiple-operation-time-out */
1714 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1716 /* multiple-operation-time-out-action */
1717 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1719 /* natural-language-configured */
1720 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1721 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1722 "natural-language-configured", NULL
, "en");
1724 /* number-up-default */
1725 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1726 "number-up-default", 1);
1728 /* number-up-supported */
1729 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1730 "number-up-supported", 1);
1732 /* operations-supported */
1733 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1734 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1736 /* orientation-requested-default */
1737 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1738 "orientation-requested-default", 0);
1740 /* orientation-requested-supported */
1741 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1742 "orientation-requested-supported", 4, orients
);
1744 /* output-bin-default */
1745 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1746 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1747 "output-bin-default", NULL
, "face-down");
1749 /* output-bin-supported */
1750 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1751 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1752 "output-bin-supported", NULL
, "face-down");
1754 /* overrides-supported */
1755 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1757 /* page-ranges-supported */
1758 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1760 /* pages-per-minute */
1761 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1762 "pages-per-minute", ppm
);
1764 /* pages-per-minute-color */
1766 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1767 "pages-per-minute-color", ppm_color
);
1769 /* pdl-override-supported */
1770 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1771 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1772 "pdl-override-supported", NULL
, "attempted");
1774 /* print-color-mode-default */
1775 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1777 /* print-color-mode-supported */
1778 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
);
1780 /* print-content-optimize-default */
1781 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1783 /* print-content-optimize-supported */
1784 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
1786 /* print-rendering-intent-default */
1787 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
1789 /* print-rendering-intent-supported */
1790 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
1792 /* print-quality-default */
1793 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
1795 /* print-quality-supported */
1796 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
);
1798 /* printer-device-id */
1799 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1800 "printer-device-id", NULL
, device_id
);
1802 /* printer-get-attributes-supported */
1803 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1805 /* printer-geo-location */
1806 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
1809 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1810 "printer-icons", NULL
, icons
);
1812 /* printer-is-accepting-jobs */
1813 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1817 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1820 /* printer-location */
1821 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1822 "printer-location", NULL
, location
);
1824 /* printer-make-and-model */
1825 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1826 "printer-make-and-model", NULL
, make_model
);
1828 /* printer-mandatory-job-attributes */
1831 static const char * const names
[] =
1833 "job-accounting-user-id",
1837 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1838 "printer-mandatory-job-attributes",
1839 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
1842 /* printer-more-info */
1843 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1846 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1849 /* printer-organization */
1850 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
1852 /* printer-organizational-unit */
1853 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
1855 /* printer-resolution-default */
1856 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1857 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1859 /* printer-resolution-supported */
1860 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1861 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1863 /* printer-supply-description */
1864 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
);
1866 /* printer-supply-info-uri */
1867 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
1869 /* printer-uri-supported */
1870 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
1873 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
1874 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
1876 /* pwg-raster-document-xxx-supported */
1877 for (i
= 0; i
< num_formats
; i
++)
1878 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
1881 if (i
< num_formats
)
1883 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
,
1884 "pwg-raster-document-resolution-supported",
1885 (int)(sizeof(pwg_raster_document_resolution_supported
) /
1886 sizeof(pwg_raster_document_resolution_supported
[0])),
1888 pwg_raster_document_resolution_supported
,
1889 pwg_raster_document_resolution_supported
);
1890 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1891 "pwg-raster-document-sheet-back", NULL
, "normal");
1892 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1893 "pwg-raster-document-type-supported",
1894 (int)(sizeof(pwg_raster_document_type_supported
) /
1895 sizeof(pwg_raster_document_type_supported
[0])), NULL
,
1896 pwg_raster_document_type_supported
);
1899 /* reference-uri-scheme-supported */
1900 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1901 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
1902 "reference-uri-schemes-supported",
1903 (int)(sizeof(reference_uri_schemes_supported
) /
1904 sizeof(reference_uri_schemes_supported
[0])),
1905 NULL
, reference_uri_schemes_supported
);
1908 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1909 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1910 "sides-default", NULL
, "one-sided");
1912 /* sides-supported */
1913 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1914 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1915 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
1917 /* uri-authentication-supported */
1918 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1919 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1920 "uri-authentication-supported", NULL
, "none");
1922 /* uri-security-supported */
1923 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1924 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1925 "uri-security-supported", NULL
, "none");
1927 /* which-jobs-supported */
1928 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1929 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1930 "which-jobs-supported",
1931 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
1935 debug_attributes("Printer", printer
->attrs
, 0);
1939 * Register the printer with Bonjour...
1942 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
,
1943 ppm_color
> 0, duplex
, subtype
))
1945 #endif /* HAVE_DNSSD */
1955 * If we get here we were unable to create the printer...
1960 delete_printer(printer
);
1966 * 'debug_attributes()' - Print attributes in a request or response.
1970 debug_attributes(const char *title
, /* I - Title */
1971 ipp_t
*ipp
, /* I - Request/response */
1972 int type
) /* I - 0 = object, 1 = request, 2 = response */
1974 ipp_tag_t group_tag
; /* Current group */
1975 ipp_attribute_t
*attr
; /* Current attribute */
1976 char buffer
[2048]; /* String buffer for value */
1977 int major
, minor
; /* Version */
1983 fprintf(stderr
, "%s:\n", title
);
1984 major
= ippGetVersion(ipp
, &minor
);
1985 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1987 fprintf(stderr
, " operation-id=%s(%04x)\n",
1988 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1990 fprintf(stderr
, " status-code=%s(%04x)\n",
1991 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1992 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1994 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1996 attr
= ippNextAttribute(ipp
))
1998 if (ippGetGroupTag(attr
) != group_tag
)
2000 group_tag
= ippGetGroupTag(attr
);
2001 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2004 if (ippGetName(attr
))
2006 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2007 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2008 ippGetCount(attr
) > 1 ? "1setOf " : "",
2009 ippTagString(ippGetValueTag(attr
)), buffer
);
2016 * 'delete_client()' - Close the socket and free all memory used by a client
2021 delete_client(_ipp_client_t
*client
) /* I - Client */
2024 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2027 * Flush pending writes before closing...
2030 httpFlushWrite(client
->http
);
2036 httpClose(client
->http
);
2038 ippDelete(client
->request
);
2039 ippDelete(client
->response
);
2046 * 'delete_job()' - Remove from the printer and free all memory used by a job
2051 delete_job(_ipp_job_t
*job
) /* I - Job */
2054 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2056 ippDelete(job
->attrs
);
2061 unlink(job
->filename
);
2063 free(job
->filename
);
2071 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2072 * used by a printer object.
2076 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2078 if (printer
->ipv4
>= 0)
2079 close(printer
->ipv4
);
2081 if (printer
->ipv6
>= 0)
2082 close(printer
->ipv6
);
2085 if (printer
->printer_ref
)
2086 DNSServiceRefDeallocate(printer
->printer_ref
);
2088 if (printer
->ipp_ref
)
2089 DNSServiceRefDeallocate(printer
->ipp_ref
);
2092 if (printer
->ipps_ref
)
2093 DNSServiceRefDeallocate(printer
->ipps_ref
);
2094 # endif /* HAVE_SSL */
2095 if (printer
->http_ref
)
2096 DNSServiceRefDeallocate(printer
->http_ref
);
2098 if (printer
->common_ref
)
2099 DNSServiceRefDeallocate(printer
->common_ref
);
2101 TXTRecordDeallocate(&(printer
->ipp_txt
));
2103 if (printer
->dnssd_name
)
2104 free(printer
->dnssd_name
);
2105 #endif /* HAVE_DNSSD */
2108 free(printer
->name
);
2110 free(printer
->icon
);
2111 if (printer
->command
)
2112 free(printer
->command
);
2113 if (printer
->directory
)
2114 free(printer
->directory
);
2115 if (printer
->hostname
)
2116 free(printer
->hostname
);
2120 ippDelete(printer
->attrs
);
2121 cupsArrayDelete(printer
->jobs
);
2129 * 'dnssd_callback()' - Handle Bonjour registration events.
2134 DNSServiceRef sdRef
, /* I - Service reference */
2135 DNSServiceFlags flags
, /* I - Status flags */
2136 DNSServiceErrorType errorCode
, /* I - Error, if any */
2137 const char *name
, /* I - Service name */
2138 const char *regtype
, /* I - Service type */
2139 const char *domain
, /* I - Domain for service */
2140 _ipp_printer_t
*printer
) /* I - Printer */
2148 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2149 regtype
, (int)errorCode
);
2152 else if (strcasecmp(name
, printer
->dnssd_name
))
2155 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2157 /* No lock needed since only the main thread accesses/changes this */
2158 free(printer
->dnssd_name
);
2159 printer
->dnssd_name
= strdup(name
);
2162 #endif /* HAVE_DNSSD */
2166 * 'filter_cb()' - Filter printer attributes based on the requested array.
2169 static int /* O - 1 to copy, 0 to ignore */
2170 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2171 ipp_t
*dst
, /* I - Destination (unused) */
2172 ipp_attribute_t
*attr
) /* I - Source attribute */
2175 * Filter attributes as needed...
2180 ipp_tag_t group
= ippGetGroupTag(attr
);
2181 const char *name
= ippGetName(attr
);
2183 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
)))
2186 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2191 * 'find_job()' - Find a job specified in a request.
2194 static _ipp_job_t
* /* O - Job or NULL */
2195 find_job(_ipp_client_t
*client
) /* I - Client */
2197 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2198 _ipp_job_t key
, /* Job search key */
2199 *job
; /* Matching job, if any */
2202 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2204 const char *uri
= ippGetString(attr
, 0, NULL
);
2206 fprintf(stderr
, "find_job: job-uri=\"%s\"\n", uri
);
2208 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2209 uri
[client
->printer
->urilen
] == '/')
2210 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2214 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2215 key
.id
= ippGetInteger(attr
, 0);
2217 fprintf(stderr
, "find_job: key.id=%d\n", key
.id
);
2219 _cupsRWLockRead(&(client
->printer
->rwlock
));
2220 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2221 _cupsRWUnlock(&(client
->printer
->rwlock
));
2223 fprintf(stderr
, "find_job: Got job %d (%p)\n", job
? job
->id
: 0, job
);
2230 * 'html_escape()' - Write a HTML-safe string.
2234 html_escape(_ipp_client_t
*client
, /* I - Client */
2235 const char *s
, /* I - String to write */
2236 size_t slen
) /* I - Number of characters to write */
2238 const char *start
, /* Start of segment */
2239 *end
; /* End of string */
2243 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2245 while (*s
&& s
< end
)
2247 if (*s
== '&' || *s
== '<')
2250 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2253 httpWrite2(client
->http
, "&", 5);
2255 httpWrite2(client
->http
, "<", 4);
2264 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2269 * 'html_footer()' - Show the web interface footer.
2271 * This function also writes the trailing 0-length chunk.
2275 html_footer(_ipp_client_t
*client
) /* I - Client */
2281 httpWrite2(client
->http
, "", 0);
2286 * 'html_header()' - Show the web interface header and title.
2290 html_header(_ipp_client_t
*client
, /* I - Client */
2291 const char *title
) /* I - Title */
2297 "<title>%s</title>\n"
2298 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2299 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2300 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2302 "body { font-family: sans-serif; margin: 0; }\n"
2303 "div.body { padding: 0px 10px 10px; }\n"
2304 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2305 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2306 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2307 "table.form th { text-align: right; }\n"
2308 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2309 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2310 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2311 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2312 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2313 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2314 "table.nav td { margin: 0; text-align: center; }\n"
2315 "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"
2316 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2317 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2318 "td.nav:hover { background: #666; color: #fff; }\n"
2319 "td.nav:active { background: #000; color: #ff0; }\n"
2323 "<table class=\"nav\"><tr>"
2324 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2325 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2326 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2328 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2333 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2337 html_printf(_ipp_client_t
*client
, /* I - Client */
2338 const char *format
, /* I - Printf-style format string */
2339 ...) /* I - Additional arguments as needed */
2341 va_list ap
; /* Pointer to arguments */
2342 const char *start
; /* Start of string */
2343 char size
, /* Size character (h, l, L) */
2344 type
; /* Format type character */
2345 int width
, /* Width of field */
2346 prec
; /* Number of characters of precision */
2347 char tformat
[100], /* Temporary format string for sprintf() */
2348 *tptr
, /* Pointer into temporary format */
2349 temp
[1024]; /* Buffer for formatted numbers */
2350 char *s
; /* Pointer to string */
2354 * Loop through the format string, formatting as needed...
2357 va_start(ap
, format
);
2365 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2368 *tptr
++ = *format
++;
2372 httpWrite2(client
->http
, "%", 1);
2377 else if (strchr(" -+#\'", *format
))
2378 *tptr
++ = *format
++;
2383 * Get width from argument...
2387 width
= va_arg(ap
, int);
2389 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2390 tptr
+= strlen(tptr
);
2396 while (isdigit(*format
& 255))
2398 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2401 width
= width
* 10 + *format
++ - '0';
2407 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2415 * Get precision from argument...
2419 prec
= va_arg(ap
, int);
2421 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2422 tptr
+= strlen(tptr
);
2428 while (isdigit(*format
& 255))
2430 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2433 prec
= prec
* 10 + *format
++ - '0';
2438 if (*format
== 'l' && format
[1] == 'l')
2442 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2450 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2452 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2467 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2476 case 'E' : /* Floating point formats */
2481 if ((size_t)(width
+ 2) > sizeof(temp
))
2484 sprintf(temp
, tformat
, va_arg(ap
, double));
2486 httpWrite2(client
->http
, temp
, strlen(temp
));
2489 case 'B' : /* Integer formats */
2497 if ((size_t)(width
+ 2) > sizeof(temp
))
2500 # ifdef HAVE_LONG_LONG
2502 sprintf(temp
, tformat
, va_arg(ap
, long long));
2504 # endif /* HAVE_LONG_LONG */
2506 sprintf(temp
, tformat
, va_arg(ap
, long));
2508 sprintf(temp
, tformat
, va_arg(ap
, int));
2510 httpWrite2(client
->http
, temp
, strlen(temp
));
2513 case 'p' : /* Pointer value */
2514 if ((size_t)(width
+ 2) > sizeof(temp
))
2517 sprintf(temp
, tformat
, va_arg(ap
, void *));
2519 httpWrite2(client
->http
, temp
, strlen(temp
));
2522 case 'c' : /* Character or character array */
2525 temp
[0] = (char)va_arg(ap
, int);
2527 html_escape(client
, temp
, 1);
2530 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2533 case 's' : /* String */
2534 if ((s
= va_arg(ap
, char *)) == NULL
)
2537 html_escape(client
, s
, strlen(s
));
2546 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2553 * 'ipp_cancel_job()' - Cancel a job.
2557 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2559 _ipp_job_t
*job
; /* Job information */
2566 if ((job
= find_job(client
)) == NULL
)
2568 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2573 * See if the job is already completed, canceled, or aborted; if so,
2574 * we can't cancel...
2579 case IPP_JSTATE_CANCELED
:
2580 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2581 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2584 case IPP_JSTATE_ABORTED
:
2585 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2586 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2589 case IPP_JSTATE_COMPLETED
:
2590 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2591 "Job #%d is already completed - can\'t cancel.", job
->id
);
2599 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2601 if (job
->state
== IPP_JSTATE_PROCESSING
||
2602 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2606 job
->state
= IPP_JSTATE_CANCELED
;
2607 job
->completed
= time(NULL
);
2610 _cupsRWUnlock(&(client
->printer
->rwlock
));
2612 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2619 * 'ipp_close_job()' - Close an open job.
2623 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
2625 _ipp_job_t
*job
; /* Job information */
2632 if ((job
= find_job(client
)) == NULL
)
2634 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2639 * See if the job is already completed, canceled, or aborted; if so,
2640 * we can't cancel...
2645 case IPP_JSTATE_CANCELED
:
2646 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2647 "Job #%d is canceled - can\'t close.", job
->id
);
2650 case IPP_JSTATE_ABORTED
:
2651 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2652 "Job #%d is aborted - can\'t close.", job
->id
);
2655 case IPP_JSTATE_COMPLETED
:
2656 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2657 "Job #%d is completed - can\'t close.", job
->id
);
2660 case IPP_JSTATE_PROCESSING
:
2661 case IPP_JSTATE_STOPPED
:
2662 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2663 "Job #%d is already closed.", job
->id
);
2667 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2674 * 'ipp_create_job()' - Create a job object.
2678 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2680 _ipp_job_t
*job
; /* New job */
2681 cups_array_t
*ra
; /* Attributes to send in response */
2685 * Validate print job attributes...
2688 if (!valid_job_attributes(client
))
2690 httpFlush(client
->http
);
2695 * Do we have a file to print?
2698 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2700 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2701 "Unexpected document data following request.");
2709 if ((job
= create_job(client
)) == NULL
)
2711 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2712 "Currently printing another job.");
2717 * Return the job info...
2720 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2722 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2723 cupsArrayAdd(ra
, "job-id");
2724 cupsArrayAdd(ra
, "job-state");
2725 cupsArrayAdd(ra
, "job-state-message");
2726 cupsArrayAdd(ra
, "job-state-reasons");
2727 cupsArrayAdd(ra
, "job-uri");
2729 copy_job_attributes(client
, job
, ra
);
2730 cupsArrayDelete(ra
);
2735 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2739 ipp_get_job_attributes(
2740 _ipp_client_t
*client
) /* I - Client */
2742 _ipp_job_t
*job
; /* Job */
2743 cups_array_t
*ra
; /* requested-attributes */
2746 if ((job
= find_job(client
)) == NULL
)
2748 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2752 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2754 ra
= ippCreateRequestedArray(client
->request
);
2755 copy_job_attributes(client
, job
, ra
);
2756 cupsArrayDelete(ra
);
2761 * 'ipp_get_jobs()' - Get a list of job objects.
2765 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2767 ipp_attribute_t
*attr
; /* Current attribute */
2768 const char *which_jobs
= NULL
;
2769 /* which-jobs values */
2770 int job_comparison
; /* Job comparison */
2771 ipp_jstate_t job_state
; /* job-state value */
2772 int first_job_id
, /* First job ID */
2773 limit
, /* Maximum number of jobs to return */
2774 count
; /* Number of jobs that match */
2775 const char *username
; /* Username */
2776 _ipp_job_t
*job
; /* Current job pointer */
2777 cups_array_t
*ra
; /* Requested attributes array */
2781 * See if the "which-jobs" attribute have been specified...
2784 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2785 IPP_TAG_KEYWORD
)) != NULL
)
2787 which_jobs
= ippGetString(attr
, 0, NULL
);
2788 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
2791 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
2793 job_comparison
= -1;
2794 job_state
= IPP_JSTATE_STOPPED
;
2796 else if (!strcmp(which_jobs
, "completed"))
2799 job_state
= IPP_JSTATE_CANCELED
;
2801 else if (!strcmp(which_jobs
, "aborted"))
2804 job_state
= IPP_JSTATE_ABORTED
;
2806 else if (!strcmp(which_jobs
, "all"))
2809 job_state
= IPP_JSTATE_PENDING
;
2811 else if (!strcmp(which_jobs
, "canceled"))
2814 job_state
= IPP_JSTATE_CANCELED
;
2816 else if (!strcmp(which_jobs
, "pending"))
2819 job_state
= IPP_JSTATE_PENDING
;
2821 else if (!strcmp(which_jobs
, "pending-held"))
2824 job_state
= IPP_JSTATE_HELD
;
2826 else if (!strcmp(which_jobs
, "processing"))
2829 job_state
= IPP_JSTATE_PROCESSING
;
2831 else if (!strcmp(which_jobs
, "processing-stopped"))
2834 job_state
= IPP_JSTATE_STOPPED
;
2838 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
2839 "The which-jobs value \"%s\" is not supported.", which_jobs
);
2840 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2841 "which-jobs", NULL
, which_jobs
);
2846 * See if they want to limit the number of jobs reported...
2849 if ((attr
= ippFindAttribute(client
->request
, "limit",
2850 IPP_TAG_INTEGER
)) != NULL
)
2852 limit
= ippGetInteger(attr
, 0);
2854 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
2859 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2860 IPP_TAG_INTEGER
)) != NULL
)
2862 first_job_id
= ippGetInteger(attr
, 0);
2864 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
2871 * See if we only want to see jobs for a specific user...
2876 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2877 IPP_TAG_BOOLEAN
)) != NULL
)
2879 int my_jobs
= ippGetBoolean(attr
, 0);
2881 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
2882 my_jobs
? "true" : "false");
2886 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
2887 IPP_TAG_NAME
)) == NULL
)
2889 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2890 "Need requesting-user-name with my-jobs.");
2894 username
= ippGetString(attr
, 0, NULL
);
2896 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2897 client
->hostname
, username
);
2902 * OK, build a list of jobs for this printer...
2905 ra
= ippCreateRequestedArray(client
->request
);
2907 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2909 _cupsRWLockRead(&(client
->printer
->rwlock
));
2911 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
2912 (limit
<= 0 || count
< limit
) && job
;
2913 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
2916 * Filter out jobs that don't match...
2919 if ((job_comparison
< 0 && job
->state
> job_state
) ||
2920 (job_comparison
== 0 && job
->state
!= job_state
) ||
2921 (job_comparison
> 0 && job
->state
< job_state
) ||
2922 job
->id
< first_job_id
||
2923 (username
&& job
->username
&&
2924 strcasecmp(username
, job
->username
)))
2928 ippAddSeparator(client
->response
);
2931 copy_job_attributes(client
, job
, ra
);
2934 cupsArrayDelete(ra
);
2936 _cupsRWUnlock(&(client
->printer
->rwlock
));
2941 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2945 ipp_get_printer_attributes(
2946 _ipp_client_t
*client
) /* I - Client */
2948 cups_array_t
*ra
; /* Requested attributes array */
2949 _ipp_printer_t
*printer
; /* Printer */
2953 * Send the attributes...
2956 ra
= ippCreateRequestedArray(client
->request
);
2957 printer
= client
->printer
;
2959 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2961 _cupsRWLockRead(&(printer
->rwlock
));
2963 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
2964 IPP_TAG_CUPS_CONST
);
2966 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
2968 int i
, /* Looping var */
2969 num_ready
= 0; /* Number of ready media */
2970 ipp_t
*ready
[3]; /* Ready media */
2972 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
2974 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
2975 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);
2977 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);
2979 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
2980 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);
2981 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
2983 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
2984 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);
2986 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);
2991 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
2992 for (i
= 0; i
< num_ready
; i
++)
2993 ippDelete(ready
[i
]);
2996 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
2999 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3001 int num_ready
= 0; /* Number of ready media */
3002 const char *ready
[3]; /* Ready media */
3004 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3005 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3007 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3008 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3010 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3011 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3014 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3016 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3019 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3020 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3022 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3023 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3025 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3026 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3029 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3030 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3031 "printer-state", printer
->state
);
3033 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3034 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3036 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3037 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3039 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3041 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3043 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3046 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3048 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3049 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3050 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3051 "printer-state-reasons", NULL
, "none");
3054 int num_reasons
= 0;/* Number of reasons */
3055 const char *reasons
[32]; /* Reason strings */
3057 if (printer
->state_reasons
& _IPP_PREASON_OTHER
)
3058 reasons
[num_reasons
++] = "other";
3059 if (printer
->state_reasons
& _IPP_PREASON_COVER_OPEN
)
3060 reasons
[num_reasons
++] = "cover-open";
3061 if (printer
->state_reasons
& _IPP_PREASON_INPUT_TRAY_MISSING
)
3062 reasons
[num_reasons
++] = "input-tray-missing";
3063 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_EMPTY
)
3064 reasons
[num_reasons
++] = "marker-supply-empty-warning";
3065 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_LOW
)
3066 reasons
[num_reasons
++] = "marker-supply-low-report";
3067 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
)
3068 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
3069 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_FULL
)
3070 reasons
[num_reasons
++] = "marker-waste-full-warning";
3071 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
3072 reasons
[num_reasons
++] = "media-empty-warning";
3073 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_JAM
)
3074 reasons
[num_reasons
++] = "media-jam-warning";
3075 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_LOW
)
3076 reasons
[num_reasons
++] = "media-low-report";
3077 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_NEEDED
)
3078 reasons
[num_reasons
++] = "media-needed-report";
3079 if (printer
->state_reasons
& _IPP_PREASON_MOVING_TO_PAUSED
)
3080 reasons
[num_reasons
++] = "moving-to-paused";
3081 if (printer
->state_reasons
& _IPP_PREASON_PAUSED
)
3082 reasons
[num_reasons
++] = "paused";
3083 if (printer
->state_reasons
& _IPP_PREASON_SPOOL_AREA_FULL
)
3084 reasons
[num_reasons
++] = "spool-area-full";
3085 if (printer
->state_reasons
& _IPP_PREASON_TONER_EMPTY
)
3086 reasons
[num_reasons
++] = "toner-empty-warning";
3087 if (printer
->state_reasons
& _IPP_PREASON_TONER_LOW
)
3088 reasons
[num_reasons
++] = "toner-low-report";
3090 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
3091 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3092 "printer-state-reasons", num_reasons
, NULL
, reasons
);
3096 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3098 int i
; /* Looping var */
3099 char buffer
[256]; /* Supply value buffer */
3100 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3101 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3103 for (i
= 0; i
< 5; i
++)
3105 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
]);
3108 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3110 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3114 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3115 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3117 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3118 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3119 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3121 _cupsRWUnlock(&(printer
->rwlock
));
3123 cupsArrayDelete(ra
);
3128 * 'ipp_identify_printer()' - Beep or display a message.
3132 ipp_identify_printer(
3133 _ipp_client_t
*client
) /* I - Client */
3135 /* TODO: Do something */
3137 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3142 * 'ipp_print_job()' - Create a job object with an attached document.
3146 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3148 _ipp_job_t
*job
; /* New job */
3149 char filename
[1024], /* Filename buffer */
3150 buffer
[4096]; /* Copy buffer */
3151 ssize_t bytes
; /* Bytes read */
3152 cups_array_t
*ra
; /* Attributes to send in response */
3156 * Validate print job attributes...
3159 if (!valid_job_attributes(client
))
3161 httpFlush(client
->http
);
3166 * Do we have a file to print?
3169 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3171 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3179 if ((job
= create_job(client
)) == NULL
)
3181 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3182 "Currently printing another job.");
3187 * Create a file for the request data...
3190 if (!strcasecmp(job
->format
, "image/jpeg"))
3191 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3192 client
->printer
->directory
, job
->id
);
3193 else if (!strcasecmp(job
->format
, "image/png"))
3194 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3195 client
->printer
->directory
, job
->id
);
3196 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
3197 snprintf(filename
, sizeof(filename
), "%s/%d.ras",
3198 client
->printer
->directory
, job
->id
);
3199 else if (!strcasecmp(job
->format
, "application/pdf"))
3200 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3201 client
->printer
->directory
, job
->id
);
3202 else if (!strcasecmp(job
->format
, "application/postscript"))
3203 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3204 client
->printer
->directory
, job
->id
);
3206 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3207 client
->printer
->directory
, job
->id
);
3210 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3212 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3214 job
->state
= IPP_JSTATE_ABORTED
;
3216 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3217 "Unable to create print file: %s", strerror(errno
));
3221 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3223 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3225 int error
= errno
; /* Write error */
3227 job
->state
= IPP_JSTATE_ABORTED
;
3234 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3235 "Unable to write print file: %s", strerror(error
));
3243 * Got an error while reading the print data, so abort this job.
3246 job
->state
= IPP_JSTATE_ABORTED
;
3253 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3254 "Unable to read print file.");
3260 int error
= errno
; /* Write error */
3262 job
->state
= IPP_JSTATE_ABORTED
;
3267 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3268 "Unable to write print file: %s", strerror(error
));
3273 job
->filename
= strdup(filename
);
3274 job
->state
= IPP_JSTATE_PENDING
;
3277 * Process the job...
3280 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3282 job
->state
= IPP_JSTATE_ABORTED
;
3283 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3288 * Return the job info...
3291 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3293 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3294 cupsArrayAdd(ra
, "job-id");
3295 cupsArrayAdd(ra
, "job-state");
3296 cupsArrayAdd(ra
, "job-state-message");
3297 cupsArrayAdd(ra
, "job-state-reasons");
3298 cupsArrayAdd(ra
, "job-uri");
3300 copy_job_attributes(client
, job
, ra
);
3301 cupsArrayDelete(ra
);
3306 * 'ipp_print_uri()' - Create a job object with a referenced document.
3310 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3312 _ipp_job_t
*job
; /* New job */
3313 ipp_attribute_t
*uri
; /* document-uri */
3314 char scheme
[256], /* URI scheme */
3315 userpass
[256], /* Username and password info */
3316 hostname
[256], /* Hostname */
3317 resource
[1024]; /* Resource path */
3318 int port
; /* Port number */
3319 http_uri_status_t uri_status
; /* URI decode status */
3320 http_encryption_t encryption
; /* Encryption to use, if any */
3321 http_t
*http
; /* Connection for http/https URIs */
3322 http_status_t status
; /* Access status for http/https URIs */
3323 int infile
; /* Input file for local file URIs */
3324 char filename
[1024], /* Filename buffer */
3325 buffer
[4096]; /* Copy buffer */
3326 ssize_t bytes
; /* Bytes read */
3327 cups_array_t
*ra
; /* Attributes to send in response */
3328 static const char * const uri_status_strings
[] =
3329 { /* URI decode errors */
3331 "Bad arguments to function.",
3332 "Bad resource in URI.",
3333 "Bad port number in URI.",
3334 "Bad hostname in URI.",
3335 "Bad username in URI.",
3336 "Bad scheme in URI.",
3342 * Validate print job attributes...
3345 if (!valid_job_attributes(client
))
3347 httpFlush(client
->http
);
3352 * Do we have a file to print?
3355 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3357 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3358 "Unexpected document data following request.");
3363 * Do we have a document URI?
3366 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3367 IPP_TAG_URI
)) == NULL
)
3369 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3373 if (ippGetCount(uri
) != 1)
3375 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3376 "Too many document-uri values.");
3380 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3381 scheme
, sizeof(scheme
), userpass
,
3382 sizeof(userpass
), hostname
, sizeof(hostname
),
3383 &port
, resource
, sizeof(resource
));
3384 if (uri_status
< HTTP_URI_STATUS_OK
)
3386 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3387 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3391 if (strcmp(scheme
, "file") &&
3393 strcmp(scheme
, "https") &&
3394 #endif /* HAVE_SSL */
3395 strcmp(scheme
, "http"))
3397 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3398 "URI scheme \"%s\" not supported.", scheme
);
3402 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3404 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3405 "Unable to access URI: %s", strerror(errno
));
3413 if ((job
= create_job(client
)) == NULL
)
3415 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3416 "Currently printing another job.");
3421 * Create a file for the request data...
3424 if (!strcasecmp(job
->format
, "image/jpeg"))
3425 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3426 client
->printer
->directory
, job
->id
);
3427 else if (!strcasecmp(job
->format
, "image/png"))
3428 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3429 client
->printer
->directory
, job
->id
);
3430 else if (!strcasecmp(job
->format
, "application/pdf"))
3431 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3432 client
->printer
->directory
, job
->id
);
3433 else if (!strcasecmp(job
->format
, "application/postscript"))
3434 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3435 client
->printer
->directory
, job
->id
);
3437 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3438 client
->printer
->directory
, job
->id
);
3440 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3442 job
->state
= IPP_JSTATE_ABORTED
;
3444 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3445 "Unable to create print file: %s", strerror(errno
));
3449 if (!strcmp(scheme
, "file"))
3451 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3453 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3454 "Unable to access URI: %s", strerror(errno
));
3460 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3461 (errno
== EAGAIN
|| errno
== EINTR
))
3463 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3465 int error
= errno
; /* Write error */
3467 job
->state
= IPP_JSTATE_ABORTED
;
3475 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3476 "Unable to write print file: %s", strerror(error
));
3487 if (port
== 443 || !strcmp(scheme
, "https"))
3488 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3490 #endif /* HAVE_SSL */
3491 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3493 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3494 1, 30000, NULL
)) == NULL
)
3496 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3497 "Unable to connect to %s: %s", hostname
,
3498 cupsLastErrorString());
3499 job
->state
= IPP_JSTATE_ABORTED
;
3508 httpClearFields(http
);
3509 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3510 if (httpGet(http
, resource
))
3512 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3513 "Unable to GET URI: %s", strerror(errno
));
3515 job
->state
= IPP_JSTATE_ABORTED
;
3525 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3527 if (status
!= HTTP_STATUS_OK
)
3529 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3530 "Unable to GET URI: %s", httpStatus(status
));
3532 job
->state
= IPP_JSTATE_ABORTED
;
3542 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3544 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3546 int error
= errno
; /* Write error */
3548 job
->state
= IPP_JSTATE_ABORTED
;
3556 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3557 "Unable to write print file: %s", strerror(error
));
3567 int error
= errno
; /* Write error */
3569 job
->state
= IPP_JSTATE_ABORTED
;
3574 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3575 "Unable to write print file: %s", strerror(error
));
3580 job
->filename
= strdup(filename
);
3581 job
->state
= IPP_JSTATE_PENDING
;
3584 * Process the job...
3588 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3590 job
->state
= IPP_JSTATE_ABORTED
;
3591 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3600 * Return the job info...
3603 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3605 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3606 cupsArrayAdd(ra
, "job-id");
3607 cupsArrayAdd(ra
, "job-state");
3608 cupsArrayAdd(ra
, "job-state-reasons");
3609 cupsArrayAdd(ra
, "job-uri");
3611 copy_job_attributes(client
, job
, ra
);
3612 cupsArrayDelete(ra
);
3617 * 'ipp_send_document()' - Add an attached document to a job object created with
3622 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3624 _ipp_job_t
*job
; /* Job information */
3625 char filename
[1024], /* Filename buffer */
3626 buffer
[4096]; /* Copy buffer */
3627 ssize_t bytes
; /* Bytes read */
3628 ipp_attribute_t
*attr
; /* Current attribute */
3629 cups_array_t
*ra
; /* Attributes to send in response */
3636 if ((job
= find_job(client
)) == NULL
)
3638 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3639 httpFlush(client
->http
);
3644 * See if we already have a document for this job or the job has already
3645 * in a non-pending state...
3648 if (job
->state
> IPP_JSTATE_HELD
)
3650 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3651 "Job is not in a pending state.");
3652 httpFlush(client
->http
);
3655 else if (job
->filename
|| job
->fd
>= 0)
3657 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3658 "Multiple document jobs are not supported.");
3659 httpFlush(client
->http
);
3663 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3664 IPP_TAG_ZERO
)) == NULL
)
3666 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3667 "Missing required last-document attribute.");
3668 httpFlush(client
->http
);
3671 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3672 !ippGetBoolean(attr
, 0))
3674 respond_unsupported(client
, attr
);
3675 httpFlush(client
->http
);
3680 * Validate document attributes...
3683 if (!valid_doc_attributes(client
))
3685 httpFlush(client
->http
);
3690 * Get the document format for the job...
3693 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3695 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
)
3696 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, ippGetString(attr
, 0, NULL
));
3698 if ((attr
= ippFindAttribute(client
->request
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3699 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, ippGetString(attr
, 0, NULL
));
3701 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
3702 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
3704 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
3705 job
->format
= ippGetString(attr
, 0, NULL
);
3707 job
->format
= "application/octet-stream";
3709 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format", NULL
, job
->format
);
3712 * Create a file for the request data...
3715 if (!strcasecmp(job
->format
, "image/jpeg"))
3716 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3717 client
->printer
->directory
, job
->id
);
3718 else if (!strcasecmp(job
->format
, "image/png"))
3719 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3720 client
->printer
->directory
, job
->id
);
3721 else if (!strcasecmp(job
->format
, "application/pdf"))
3722 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3723 client
->printer
->directory
, job
->id
);
3724 else if (!strcasecmp(job
->format
, "application/postscript"))
3725 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3726 client
->printer
->directory
, job
->id
);
3728 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3729 client
->printer
->directory
, job
->id
);
3732 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3734 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3736 _cupsRWUnlock(&(client
->printer
->rwlock
));
3740 job
->state
= IPP_JSTATE_ABORTED
;
3742 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3743 "Unable to create print file: %s", strerror(errno
));
3747 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3749 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3751 int error
= errno
; /* Write error */
3753 job
->state
= IPP_JSTATE_ABORTED
;
3760 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3761 "Unable to write print file: %s", strerror(error
));
3769 * Got an error while reading the print data, so abort this job.
3772 job
->state
= IPP_JSTATE_ABORTED
;
3779 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3780 "Unable to read print file.");
3786 int error
= errno
; /* Write error */
3788 job
->state
= IPP_JSTATE_ABORTED
;
3793 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3794 "Unable to write print file: %s", strerror(error
));
3798 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3801 job
->filename
= strdup(filename
);
3802 job
->state
= IPP_JSTATE_PENDING
;
3804 _cupsRWUnlock(&(client
->printer
->rwlock
));
3807 * Process the job...
3811 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3813 job
->state
= IPP_JSTATE_ABORTED
;
3814 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3823 * Return the job info...
3826 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3828 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3829 cupsArrayAdd(ra
, "job-id");
3830 cupsArrayAdd(ra
, "job-state");
3831 cupsArrayAdd(ra
, "job-state-reasons");
3832 cupsArrayAdd(ra
, "job-uri");
3834 copy_job_attributes(client
, job
, ra
);
3835 cupsArrayDelete(ra
);
3840 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3845 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
3847 _ipp_job_t
*job
; /* Job information */
3848 ipp_attribute_t
*uri
; /* document-uri */
3849 char scheme
[256], /* URI scheme */
3850 userpass
[256], /* Username and password info */
3851 hostname
[256], /* Hostname */
3852 resource
[1024]; /* Resource path */
3853 int port
; /* Port number */
3854 http_uri_status_t uri_status
; /* URI decode status */
3855 http_encryption_t encryption
; /* Encryption to use, if any */
3856 http_t
*http
; /* Connection for http/https URIs */
3857 http_status_t status
; /* Access status for http/https URIs */
3858 int infile
; /* Input file for local file URIs */
3859 char filename
[1024], /* Filename buffer */
3860 buffer
[4096]; /* Copy buffer */
3861 ssize_t bytes
; /* Bytes read */
3862 ipp_attribute_t
*attr
; /* Current attribute */
3863 cups_array_t
*ra
; /* Attributes to send in response */
3864 static const char * const uri_status_strings
[] =
3865 { /* URI decode errors */
3867 "Bad arguments to function.",
3868 "Bad resource in URI.",
3869 "Bad port number in URI.",
3870 "Bad hostname in URI.",
3871 "Bad username in URI.",
3872 "Bad scheme in URI.",
3881 if ((job
= find_job(client
)) == NULL
)
3883 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3884 httpFlush(client
->http
);
3889 * See if we already have a document for this job or the job has already
3890 * in a non-pending state...
3893 if (job
->state
> IPP_JSTATE_HELD
)
3895 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3896 "Job is not in a pending state.");
3897 httpFlush(client
->http
);
3900 else if (job
->filename
|| job
->fd
>= 0)
3902 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3903 "Multiple document jobs are not supported.");
3904 httpFlush(client
->http
);
3908 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3909 IPP_TAG_ZERO
)) == NULL
)
3911 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3912 "Missing required last-document attribute.");
3913 httpFlush(client
->http
);
3916 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3917 !ippGetBoolean(attr
, 0))
3919 respond_unsupported(client
, attr
);
3920 httpFlush(client
->http
);
3925 * Validate document attributes...
3928 if (!valid_doc_attributes(client
))
3930 httpFlush(client
->http
);
3935 * Do we have a file to print?
3938 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3940 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3941 "Unexpected document data following request.");
3946 * Do we have a document URI?
3949 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3950 IPP_TAG_URI
)) == NULL
)
3952 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3956 if (ippGetCount(uri
) != 1)
3958 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3959 "Too many document-uri values.");
3963 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3964 scheme
, sizeof(scheme
), userpass
,
3965 sizeof(userpass
), hostname
, sizeof(hostname
),
3966 &port
, resource
, sizeof(resource
));
3967 if (uri_status
< HTTP_URI_STATUS_OK
)
3969 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3970 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3974 if (strcmp(scheme
, "file") &&
3976 strcmp(scheme
, "https") &&
3977 #endif /* HAVE_SSL */
3978 strcmp(scheme
, "http"))
3980 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3981 "URI scheme \"%s\" not supported.", scheme
);
3985 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3987 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3988 "Unable to access URI: %s", strerror(errno
));
3993 * Get the document format for the job...
3996 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3998 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3999 IPP_TAG_MIMETYPE
)) != NULL
)
4000 job
->format
= ippGetString(attr
, 0, NULL
);
4002 job
->format
= "application/octet-stream";
4005 * Create a file for the request data...
4008 if (!strcasecmp(job
->format
, "image/jpeg"))
4009 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4010 client
->printer
->directory
, job
->id
);
4011 else if (!strcasecmp(job
->format
, "image/png"))
4012 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4013 client
->printer
->directory
, job
->id
);
4014 else if (!strcasecmp(job
->format
, "application/pdf"))
4015 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4016 client
->printer
->directory
, job
->id
);
4017 else if (!strcasecmp(job
->format
, "application/postscript"))
4018 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4019 client
->printer
->directory
, job
->id
);
4021 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4022 client
->printer
->directory
, job
->id
);
4024 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4026 _cupsRWUnlock(&(client
->printer
->rwlock
));
4030 job
->state
= IPP_JSTATE_ABORTED
;
4032 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4033 "Unable to create print file: %s", strerror(errno
));
4037 if (!strcmp(scheme
, "file"))
4039 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4041 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4042 "Unable to access URI: %s", strerror(errno
));
4048 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4049 (errno
== EAGAIN
|| errno
== EINTR
))
4051 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4053 int error
= errno
; /* Write error */
4055 job
->state
= IPP_JSTATE_ABORTED
;
4063 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4064 "Unable to write print file: %s", strerror(error
));
4075 if (port
== 443 || !strcmp(scheme
, "https"))
4076 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4078 #endif /* HAVE_SSL */
4079 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4081 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4082 1, 30000, NULL
)) == NULL
)
4084 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4085 "Unable to connect to %s: %s", hostname
,
4086 cupsLastErrorString());
4087 job
->state
= IPP_JSTATE_ABORTED
;
4096 httpClearFields(http
);
4097 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4098 if (httpGet(http
, resource
))
4100 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4101 "Unable to GET URI: %s", strerror(errno
));
4103 job
->state
= IPP_JSTATE_ABORTED
;
4113 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4115 if (status
!= HTTP_STATUS_OK
)
4117 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4118 "Unable to GET URI: %s", httpStatus(status
));
4120 job
->state
= IPP_JSTATE_ABORTED
;
4130 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4132 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4134 int error
= errno
; /* Write error */
4136 job
->state
= IPP_JSTATE_ABORTED
;
4144 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4145 "Unable to write print file: %s", strerror(error
));
4155 int error
= errno
; /* Write error */
4157 job
->state
= IPP_JSTATE_ABORTED
;
4162 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4163 "Unable to write print file: %s", strerror(error
));
4167 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4170 job
->filename
= strdup(filename
);
4171 job
->state
= IPP_JSTATE_PENDING
;
4173 _cupsRWUnlock(&(client
->printer
->rwlock
));
4176 * Process the job...
4180 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4182 job
->state
= IPP_JSTATE_ABORTED
;
4183 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4192 * Return the job info...
4195 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4197 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4198 cupsArrayAdd(ra
, "job-id");
4199 cupsArrayAdd(ra
, "job-state");
4200 cupsArrayAdd(ra
, "job-state-reasons");
4201 cupsArrayAdd(ra
, "job-uri");
4203 copy_job_attributes(client
, job
, ra
);
4204 cupsArrayDelete(ra
);
4209 * 'ipp_validate_job()' - Validate job creation attributes.
4213 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4215 if (valid_job_attributes(client
))
4216 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4221 * 'parse_options()' - Parse URL options into CUPS options.
4223 * The client->options string is destroyed by this function.
4226 static int /* O - Number of options */
4227 parse_options(_ipp_client_t
*client
, /* I - Client */
4228 cups_option_t
**options
) /* O - Options */
4230 char *name
, /* Name */
4232 *next
; /* Next name=value pair */
4233 int num_options
= 0; /* Number of options */
4238 for (name
= client
->options
; name
&& *name
; name
= next
)
4240 if ((value
= strchr(name
, '=')) == NULL
)
4244 if ((next
= strchr(value
, '&')) != NULL
)
4247 num_options
= cupsAddOption(name
, value
, num_options
, options
);
4250 return (num_options
);
4255 * 'process_client()' - Process client requests on a thread.
4258 static void * /* O - Exit status */
4259 process_client(_ipp_client_t
*client
) /* I - Client */
4262 * Loop until we are out of requests or timeout (30 seconds)...
4266 int first_time
= 1; /* First time request? */
4267 #endif /* HAVE_SSL */
4269 while (httpWait(client
->http
, 30000))
4275 * See if we need to negotiate a TLS connection...
4278 char buf
[1]; /* First byte from client */
4280 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
4282 fprintf(stderr
, "%s Negotiating TLS session.\n", client
->hostname
);
4284 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
4286 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4293 #endif /* HAVE_SSL */
4295 if (!process_http(client
))
4300 * Close the conection to the client and return...
4303 delete_client(client
);
4310 * 'process_http()' - Process a HTTP request.
4313 int /* O - 1 on success, 0 on failure */
4314 process_http(_ipp_client_t
*client
) /* I - Client connection */
4316 char uri
[1024]; /* URI */
4317 http_state_t http_state
; /* HTTP state */
4318 http_status_t http_status
; /* HTTP status */
4319 ipp_state_t ipp_state
; /* State of IPP transfer */
4320 char scheme
[32], /* Method/scheme */
4321 userpass
[128], /* Username:password */
4322 hostname
[HTTP_MAX_HOST
];
4324 int port
; /* Port number */
4325 const char *encoding
; /* Content-Encoding value */
4326 static const char * const http_states
[] =
4327 { /* Strings for logging HTTP method */
4348 * Clear state variables...
4351 ippDelete(client
->request
);
4352 ippDelete(client
->response
);
4354 client
->request
= NULL
;
4355 client
->response
= NULL
;
4356 client
->operation
= HTTP_STATE_WAITING
;
4359 * Read a request from the connection...
4362 while ((http_state
= httpReadRequest(client
->http
, uri
,
4363 sizeof(uri
))) == HTTP_STATE_WAITING
)
4367 * Parse the request line...
4370 if (http_state
== HTTP_STATE_ERROR
)
4372 if (httpError(client
->http
) == EPIPE
)
4373 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
4375 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
4376 strerror(httpError(client
->http
)));
4380 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
4382 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
4383 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4386 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
4388 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
4389 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4393 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
4397 * Separate the URI into its components...
4400 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4401 userpass
, sizeof(userpass
),
4402 hostname
, sizeof(hostname
), &port
,
4403 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
)
4405 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
4406 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4410 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
4411 *(client
->options
)++ = '\0';
4414 * Process the request...
4417 client
->start
= time(NULL
);
4418 client
->operation
= httpGetState(client
->http
);
4421 * Parse incoming parameters until the status changes...
4424 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4426 if (http_status
!= HTTP_STATUS_OK
)
4428 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4432 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4433 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4436 * HTTP/1.1 and higher require the "Host:" field...
4439 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4444 * Handle HTTP Upgrade...
4447 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4450 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4455 * Handle HTTP Expect...
4458 if (httpGetExpect(client
->http
) &&
4459 (client
->operation
== HTTP_STATE_POST
||
4460 client
->operation
== HTTP_STATE_PUT
))
4462 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4465 * Send 100-continue header...
4468 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4474 * Send 417-expectation-failed header...
4477 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4483 * Handle new transfers...
4486 encoding
= httpGetContentEncoding(client
->http
);
4488 switch (client
->operation
)
4490 case HTTP_STATE_OPTIONS
:
4492 * Do HEAD/OPTIONS command...
4495 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4497 case HTTP_STATE_HEAD
:
4498 if (!strcmp(client
->uri
, "/icon.png"))
4499 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4500 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
4501 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4503 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4505 case HTTP_STATE_GET
:
4506 if (!strcmp(client
->uri
, "/icon.png"))
4509 * Send PNG icon file.
4512 int fd
; /* Icon file */
4513 struct stat fileinfo
; /* Icon file information */
4514 char buffer
[4096]; /* Copy buffer */
4515 ssize_t bytes
; /* Bytes */
4517 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
4519 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4520 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4522 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
4523 (size_t)fileinfo
.st_size
))
4529 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4530 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
4532 httpFlushWrite(client
->http
);
4537 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4539 else if (!strcmp(client
->uri
, "/"))
4542 * Show web status page...
4545 _ipp_job_t
*job
; /* Current job */
4546 int i
; /* Looping var */
4547 _ipp_preason_t reason
; /* Current reason */
4548 static const char * const reasons
[] =
4549 { /* Reason strings */
4552 "Input Tray Missing",
4553 "Marker Supply Empty",
4554 "Marker Supply Low",
4555 "Marker Waste Almost Full",
4556 "Marker Waste Full",
4568 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4571 html_header(client
, client
->printer
->name
);
4573 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
4574 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
4575 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
4576 if (client
->printer
->state_reasons
& reason
)
4577 html_printf(client
, "\n<br> %s", reasons
[i
]);
4578 html_printf(client
, "</p>\n");
4580 if (cupsArrayCount(client
->printer
->jobs
) > 0)
4582 _cupsRWLockRead(&(client
->printer
->rwlock
));
4584 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");
4585 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
4587 char when
[256], /* When job queued/started/finished */
4588 hhmmss
[64]; /* Time HH:MM:SS */
4592 case IPP_JSTATE_PENDING
:
4593 case IPP_JSTATE_HELD
:
4594 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
4596 case IPP_JSTATE_PROCESSING
:
4597 case IPP_JSTATE_STOPPED
:
4598 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
4600 case IPP_JSTATE_ABORTED
:
4601 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4603 case IPP_JSTATE_CANCELED
:
4604 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4606 case IPP_JSTATE_COMPLETED
:
4607 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4611 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
);
4613 html_printf(client
, "</tbody></table>\n");
4615 _cupsRWUnlock(&(client
->printer
->rwlock
));
4617 html_footer(client
);
4621 else if (!strcmp(client
->uri
, "/media"))
4624 * Show web media page...
4627 int i
, /* Looping var */
4628 num_options
; /* Number of form options */
4629 cups_option_t
*options
; /* Form options */
4630 static const char * const sizes
[] =
4631 { /* Size strings */
4644 static const char * const types
[] =
4661 static const int sheets
[] = /* Number of sheets */
4670 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4673 html_header(client
, client
->printer
->name
);
4675 if ((num_options
= parse_options(client
, &options
)) > 0)
4677 const char *val
; /* Form value */
4679 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
4680 client
->printer
->main_size
= atoi(val
);
4681 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
4682 client
->printer
->main_type
= atoi(val
);
4683 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
4684 client
->printer
->main_level
= atoi(val
);
4686 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
4687 client
->printer
->envelope_size
= atoi(val
);
4688 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
4689 client
->printer
->envelope_level
= atoi(val
);
4691 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
4692 client
->printer
->photo_size
= atoi(val
);
4693 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
4694 client
->printer
->photo_type
= atoi(val
);
4695 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
4696 client
->printer
->photo_level
= atoi(val
);
4698 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))
4699 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
4701 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
4703 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
))
4705 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
4706 if (client
->printer
->active_job
)
4707 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
4710 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
4712 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
4715 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
4717 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
4718 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
4719 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4720 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
4721 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
4722 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
4723 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4724 if (!strstr(types
[i
], "Photo"))
4725 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
4726 html_printf(client
, "</select> <select name=\"main_level\">");
4727 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4728 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
4729 html_printf(client
, "</select></td></tr>\n");
4732 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
4733 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4734 if (strstr(sizes
[i
], "Envelope"))
4735 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
4736 html_printf(client
, "</select> <select name=\"envelope_level\">");
4737 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4738 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
4739 html_printf(client
, "</select></td></tr>\n");
4742 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
4743 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4744 if (strstr(sizes
[i
], "Photo"))
4745 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
4746 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
4747 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4748 if (strstr(types
[i
], "Photo"))
4749 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
4750 html_printf(client
, "</select> <select name=\"photo_level\">");
4751 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4752 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
4753 html_printf(client
, "</select></td></tr>\n");
4755 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
4756 html_footer(client
);
4760 else if (!strcmp(client
->uri
, "/supplies"))
4763 * Show web supplies page...
4766 int i
, j
, /* Looping vars */
4767 num_options
; /* Number of form options */
4768 cups_option_t
*options
; /* Form options */
4769 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
4771 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4774 html_header(client
, client
->printer
->name
);
4776 if ((num_options
= parse_options(client
, &options
)) > 0)
4778 char name
[64]; /* Form field */
4779 const char *val
; /* Form value */
4781 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
);
4783 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
4785 snprintf(name
, sizeof(name
), "supply_%d", i
);
4786 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
4788 int level
= client
->printer
->supplies
[i
] = atoi(val
);
4794 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
4795 else if (level
< 10)
4796 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
4801 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
4802 else if (level
> 90)
4803 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
4808 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
4811 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
4813 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
4814 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
4816 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
4817 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
4818 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
4819 html_printf(client
, "</select></td></tr>\n");
4821 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
4822 html_footer(client
);
4827 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4830 case HTTP_STATE_POST
:
4831 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
4835 * Not an IPP request...
4838 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
4842 * Read the IPP request...
4845 client
->request
= ippNew();
4847 while ((ipp_state
= ippRead(client
->http
,
4848 client
->request
)) != IPP_STATE_DATA
)
4850 if (ipp_state
== IPP_STATE_ERROR
)
4852 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
4853 cupsLastErrorString());
4854 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4860 * Now that we have the IPP request, process the request...
4863 return (process_ipp(client
));
4866 break; /* Anti-compiler-warning-code */
4874 * 'process_ipp()' - Process an IPP request.
4877 static int /* O - 1 on success, 0 on error */
4878 process_ipp(_ipp_client_t
*client
) /* I - Client */
4880 ipp_tag_t group
; /* Current group tag */
4881 ipp_attribute_t
*attr
; /* Current attribute */
4882 ipp_attribute_t
*charset
; /* Character set attribute */
4883 ipp_attribute_t
*language
; /* Language attribute */
4884 ipp_attribute_t
*uri
; /* Printer URI attribute */
4885 int major
, minor
; /* Version number */
4886 const char *name
; /* Name of attribute */
4889 debug_attributes("Request", client
->request
, 1);
4892 * First build an empty response message for this request...
4895 client
->operation_id
= ippGetOperation(client
->request
);
4896 client
->response
= ippNewResponse(client
->request
);
4899 * Then validate the request header and required attributes...
4902 major
= ippGetVersion(client
->request
, &minor
);
4904 if (major
< 1 || major
> 2)
4907 * Return an error, since we only support IPP 1.x and 2.x.
4910 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
4911 "Bad request version number %d.%d.", major
, minor
);
4913 else if (ippGetRequestId(client
->request
) <= 0)
4914 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
4915 ippGetRequestId(client
->request
));
4916 else if (!ippFirstAttribute(client
->request
))
4917 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4918 "No attributes in request.");
4922 * Make sure that the attributes are provided in the correct order and
4923 * don't repeat groups...
4926 for (attr
= ippFirstAttribute(client
->request
),
4927 group
= ippGetGroupTag(attr
);
4929 attr
= ippNextAttribute(client
->request
))
4931 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
4934 * Out of order; return an error...
4937 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4938 "Attribute groups are out of order (%x < %x).",
4939 ippGetGroupTag(attr
), group
);
4943 group
= ippGetGroupTag(attr
);
4949 * Then make sure that the first three attributes are:
4951 * attributes-charset
4952 * attributes-natural-language
4953 * printer-uri/job-uri
4956 attr
= ippFirstAttribute(client
->request
);
4957 name
= ippGetName(attr
);
4958 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
4959 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
4964 attr
= ippNextAttribute(client
->request
);
4965 name
= ippGetName(attr
);
4967 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
4968 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
4973 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
4974 IPP_TAG_URI
)) != NULL
)
4976 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
4977 IPP_TAG_URI
)) != NULL
)
4983 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
4984 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
4987 * Bad character set...
4990 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4991 "Unsupported character set \"%s\".",
4992 ippGetString(charset
, 0, NULL
));
4994 else if (!charset
|| !language
|| !uri
)
4997 * Return an error, since attributes-charset,
4998 * attributes-natural-language, and printer-uri/job-uri are required
4999 * for all operations.
5002 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5003 "Missing required attributes.");
5007 char scheme
[32], /* URI scheme */
5008 userpass
[32], /* Username/password in URI */
5009 host
[256], /* Host name in URI */
5010 resource
[256]; /* Resource path in URI */
5011 int port
; /* Port number in URI */
5013 name
= ippGetName(uri
);
5015 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5016 scheme
, sizeof(scheme
),
5017 userpass
, sizeof(userpass
),
5018 host
, sizeof(host
), &port
,
5019 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5020 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5021 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5022 else if ((!strcmp(name
, "job-uri") &&
5023 strncmp(resource
, "/ipp/print/", 11)) ||
5024 (!strcmp(name
, "printer-uri") &&
5025 strcmp(resource
, "/ipp/print")))
5026 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5027 name
, ippGetString(uri
, 0, NULL
));
5031 * Try processing the operation...
5034 switch (ippGetOperation(client
->request
))
5036 case IPP_OP_PRINT_JOB
:
5037 ipp_print_job(client
);
5040 case IPP_OP_PRINT_URI
:
5041 ipp_print_uri(client
);
5044 case IPP_OP_VALIDATE_JOB
:
5045 ipp_validate_job(client
);
5048 case IPP_OP_CREATE_JOB
:
5049 ipp_create_job(client
);
5052 case IPP_OP_SEND_DOCUMENT
:
5053 ipp_send_document(client
);
5056 case IPP_OP_SEND_URI
:
5057 ipp_send_uri(client
);
5060 case IPP_OP_CANCEL_JOB
:
5061 ipp_cancel_job(client
);
5064 case IPP_OP_GET_JOB_ATTRIBUTES
:
5065 ipp_get_job_attributes(client
);
5068 case IPP_OP_GET_JOBS
:
5069 ipp_get_jobs(client
);
5072 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5073 ipp_get_printer_attributes(client
);
5076 case IPP_OP_CLOSE_JOB
:
5077 ipp_close_job(client
);
5080 case IPP_OP_IDENTIFY_PRINTER
:
5081 ipp_identify_printer(client
);
5085 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5086 "Operation not supported.");
5095 * Send the HTTP header and return...
5098 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5099 httpFlush(client
->http
); /* Flush trailing (junk) data */
5101 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5102 ippLength(client
->response
)));
5107 * 'process_job()' - Process a print job.
5110 static void * /* O - Thread exit status */
5111 process_job(_ipp_job_t
*job
) /* I - Job */
5113 job
->state
= IPP_JSTATE_PROCESSING
;
5114 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
5115 job
->processing
= time(NULL
);
5117 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
5119 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5124 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
5126 if (job
->printer
->command
)
5129 * Execute a command with the job spool file and wait for it to complete...
5132 int pid
, /* Process ID */
5133 status
; /* Exit status */
5134 time_t start
, /* Start time */
5137 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
5141 if ((pid
= fork()) == 0)
5144 * Child comes here...
5147 execlp(job
->printer
->command
, job
->printer
->command
, job
->filename
,
5154 * Unable to fork process...
5157 perror("Unable to start job processing command");
5162 * Wait for child to complete...
5166 while (waitpid(pid
, &status
, 0) < 0);
5168 while (wait(&status
) < 0);
5169 #endif /* HAVE_WAITPID */
5173 if (WIFEXITED(status
))
5174 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
5175 job
->printer
->command
, WEXITSTATUS(status
));
5177 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
5178 job
->printer
->command
, WTERMSIG(status
));
5180 job
->state
= IPP_JSTATE_ABORTED
;
5183 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
5184 job
->printer
->command
);
5188 * Make sure processing takes at least 5 seconds...
5192 if ((end
- start
) < 5)
5198 * Sleep for a random amount of time to simulate job processing.
5201 sleep(5 + (rand() % 11));
5205 job
->state
= IPP_JSTATE_CANCELED
;
5206 else if (job
->state
== IPP_JSTATE_PROCESSING
)
5207 job
->state
= IPP_JSTATE_COMPLETED
;
5209 job
->completed
= time(NULL
);
5210 job
->printer
->state
= IPP_PSTATE_IDLE
;
5211 job
->printer
->active_job
= NULL
;
5219 * 'register_printer()' - Register a printer object via Bonjour.
5222 static int /* O - 1 on success, 0 on error */
5224 _ipp_printer_t
*printer
, /* I - Printer */
5225 const char *location
, /* I - Location */
5226 const char *make
, /* I - Manufacturer */
5227 const char *model
, /* I - Model name */
5228 const char *formats
, /* I - Supported formats */
5229 const char *adminurl
, /* I - Web interface URL */
5230 int color
, /* I - 1 = color, 0 = monochrome */
5231 int duplex
, /* I - 1 = duplex, 0 = simplex */
5232 const char *subtype
) /* I - Service subtype */
5234 DNSServiceErrorType error
; /* Error from Bonjour */
5235 char make_model
[256],/* Make and model together */
5236 product
[256], /* Product string */
5237 regtype
[256]; /* Bonjour service type */
5241 * Build the TXT record for IPP...
5244 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
5245 snprintf(product
, sizeof(product
), "(%s)", model
);
5247 TXTRecordCreate(&(printer
->ipp_txt
), 1024, NULL
);
5248 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 9, "ipp/print");
5249 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
5251 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
5254 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
5256 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
5258 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
5260 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
5261 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
5262 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
5264 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
5268 * Create a shared service reference for Bonjour...
5271 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
5272 != kDNSServiceErr_NoError
)
5274 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
5279 * Register the _printer._tcp (LPD) service type with a port number of 0 to
5280 * defend our service name but not actually support LPD...
5283 printer
->printer_ref
= printer
->common_ref
;
5285 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
5286 kDNSServiceFlagsShareConnection
,
5287 0 /* interfaceIndex */, printer
->dnssd_name
,
5288 "_printer._tcp", NULL
/* domain */,
5289 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
5290 NULL
/* txtRecord */,
5291 (DNSServiceRegisterReply
)dnssd_callback
,
5292 printer
)) != kDNSServiceErr_NoError
)
5294 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
5295 printer
->dnssd_name
, error
);
5300 * Then register the _ipp._tcp (IPP) service type with the real port number to
5301 * advertise our IPP printer...
5304 printer
->ipp_ref
= printer
->common_ref
;
5306 if (subtype
&& *subtype
)
5307 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
5309 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
5311 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
5312 kDNSServiceFlagsShareConnection
,
5313 0 /* interfaceIndex */, printer
->dnssd_name
,
5314 regtype
, NULL
/* domain */,
5315 NULL
/* host */, htons(printer
->port
),
5316 TXTRecordGetLength(&(printer
->ipp_txt
)),
5317 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
5318 (DNSServiceRegisterReply
)dnssd_callback
,
5319 printer
)) != kDNSServiceErr_NoError
)
5321 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5322 printer
->dnssd_name
, regtype
, error
);
5328 * Then register the _ipps._tcp (IPP) service type with the real port number to
5329 * advertise our IPP printer...
5332 printer
->ipps_ref
= printer
->common_ref
;
5334 if (subtype
&& *subtype
)
5335 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
5337 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
5339 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
5340 kDNSServiceFlagsShareConnection
,
5341 0 /* interfaceIndex */, printer
->dnssd_name
,
5342 regtype
, NULL
/* domain */,
5343 NULL
/* host */, htons(printer
->port
),
5344 TXTRecordGetLength(&(printer
->ipp_txt
)),
5345 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
5346 (DNSServiceRegisterReply
)dnssd_callback
,
5347 printer
)) != kDNSServiceErr_NoError
)
5349 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5350 printer
->dnssd_name
, regtype
, error
);
5353 # endif /* HAVE_SSL */
5356 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
5357 * real port number to advertise our IPP printer...
5360 printer
->http_ref
= printer
->common_ref
;
5362 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
5363 kDNSServiceFlagsShareConnection
,
5364 0 /* interfaceIndex */, printer
->dnssd_name
,
5365 "_http._tcp,_printer", NULL
/* domain */,
5366 NULL
/* host */, htons(printer
->port
),
5367 0 /* txtLen */, NULL
, /* txtRecord */
5368 (DNSServiceRegisterReply
)dnssd_callback
,
5369 printer
)) != kDNSServiceErr_NoError
)
5371 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5372 printer
->dnssd_name
, regtype
, error
);
5378 #endif /* HAVE_DNSSD */
5382 * 'respond_http()' - Send a HTTP response.
5385 int /* O - 1 on success, 0 on failure */
5387 _ipp_client_t
*client
, /* I - Client */
5388 http_status_t code
, /* I - HTTP status of response */
5389 const char *content_encoding
, /* I - Content-Encoding of response */
5390 const char *type
, /* I - MIME media type of response */
5391 size_t length
) /* I - Length of response */
5393 char message
[1024]; /* Text message */
5396 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
5398 if (code
== HTTP_STATUS_CONTINUE
)
5401 * 100-continue doesn't send any headers...
5404 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
5408 * Format an error message...
5411 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
)
5413 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
5415 type
= "text/plain";
5416 length
= strlen(message
);
5422 * Send the HTTP response header...
5425 httpClearFields(client
->http
);
5427 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
5428 client
->operation
== HTTP_STATE_OPTIONS
)
5429 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
5433 if (!strcmp(type
, "text/html"))
5434 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
5435 "text/html; charset=utf-8");
5437 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
5439 if (content_encoding
)
5440 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
5443 httpSetLength(client
->http
, length
);
5445 if (httpWriteResponse(client
->http
, code
) < 0)
5449 * Send the response data...
5455 * Send a plain text message.
5458 if (httpPrintf(client
->http
, "%s", message
) < 0)
5461 if (httpWrite2(client
->http
, "", 0) < 0)
5464 else if (client
->response
)
5467 * Send an IPP response...
5470 debug_attributes("Response", client
->response
, 2);
5472 ippSetState(client
->response
, IPP_STATE_IDLE
);
5474 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
5483 * 'respond_ipp()' - Send an IPP response.
5487 respond_ipp(_ipp_client_t
*client
, /* I - Client */
5488 ipp_status_t status
, /* I - status-code */
5489 const char *message
, /* I - printf-style status-message */
5490 ...) /* I - Additional args as needed */
5492 const char *formatted
= NULL
; /* Formatted message */
5495 ippSetStatusCode(client
->response
, status
);
5499 va_list ap
; /* Pointer to additional args */
5500 ipp_attribute_t
*attr
; /* New status-message attribute */
5502 va_start(ap
, message
);
5503 if ((attr
= ippFindAttribute(client
->response
, "status-message",
5504 IPP_TAG_TEXT
)) != NULL
)
5505 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
5507 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
5508 "status-message", NULL
, message
, ap
);
5511 formatted
= ippGetString(attr
, 0, NULL
);
5515 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
5516 ippOpString(client
->operation_id
), ippErrorString(status
),
5519 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
5520 ippOpString(client
->operation_id
), ippErrorString(status
));
5525 * 'respond_unsupported()' - Respond with an unsupported attribute.
5529 respond_unsupported(
5530 _ipp_client_t
*client
, /* I - Client */
5531 ipp_attribute_t
*attr
) /* I - Atribute */
5533 ipp_attribute_t
*temp
; /* Copy of attribute */
5536 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5537 "Unsupported %s %s%s value.", ippGetName(attr
),
5538 ippGetCount(attr
) > 1 ? "1setOf " : "",
5539 ippTagString(ippGetValueTag(attr
)));
5541 temp
= ippCopyAttribute(client
->response
, attr
, 0);
5542 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
5547 * 'run_printer()' - Run the printer service.
5551 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
5553 int num_fds
; /* Number of file descriptors */
5554 struct pollfd polldata
[3]; /* poll() data */
5555 int timeout
; /* Timeout for poll() */
5556 _ipp_client_t
*client
; /* New client */
5560 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5563 polldata
[0].fd
= printer
->ipv4
;
5564 polldata
[0].events
= POLLIN
;
5566 polldata
[1].fd
= printer
->ipv6
;
5567 polldata
[1].events
= POLLIN
;
5572 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
5573 polldata
[num_fds
++].events
= POLLIN
;
5574 #endif /* HAVE_DNSSD */
5577 * Loop until we are killed or have a hard error...
5582 if (cupsArrayCount(printer
->jobs
))
5587 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
5589 perror("poll() failed");
5593 if (polldata
[0].revents
& POLLIN
)
5595 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
5597 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5599 perror("Unable to create client thread");
5600 delete_client(client
);
5605 if (polldata
[1].revents
& POLLIN
)
5607 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
5609 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5611 perror("Unable to create client thread");
5612 delete_client(client
);
5618 if (polldata
[2].revents
& POLLIN
)
5619 DNSServiceProcessResult(printer
->common_ref
);
5620 #endif /* HAVE_DNSSD */
5623 * Clean out old jobs...
5626 clean_jobs(printer
);
5632 * 'time_string()' - Return the local time in hours, minutes, and seconds.
5636 time_string(time_t tv
, /* I - Time value */
5637 char *buffer
, /* I - Buffer */
5638 size_t bufsize
) /* I - Size of buffer */
5640 struct tm
*curtime
= localtime(&tv
);
5643 strftime(buffer
, bufsize
, "%X", curtime
);
5649 * 'usage()' - Show program usage.
5653 usage(int status
) /* O - Exit status */
5657 puts(CUPS_SVERSION
" - Copyright 2010-2013 by Apple Inc. All rights "
5662 puts("Usage: ippserver [options] \"name\"");
5665 puts("-2 Supports 2-sided printing (default=1-sided)");
5666 puts("-M manufacturer Manufacturer name (default=Test)");
5667 puts("-P PIN printing mode");
5668 puts("-c command Run command for every print job");
5669 printf("-d spool-directory Spool directory "
5670 "(default=/tmp/ippserver.%d)\n", (int)getpid());
5671 puts("-f type/subtype[,...] List of supported types "
5672 "(default=application/pdf,image/jpeg)");
5673 puts("-h Show program help");
5674 puts("-i iconfile.png PNG icon file (default=printer.png)");
5675 puts("-k Keep job spool files");
5676 puts("-l location Location of printer (default=empty string)");
5677 puts("-m model Model name (default=Printer)");
5678 puts("-n hostname Hostname for printer");
5679 puts("-p port Port number (default=auto)");
5681 puts("-r subtype Bonjour service subtype (default=_print)");
5682 #endif /* HAVE_DNSSD */
5683 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
5684 puts("-v[vvv] Be (very) verbose");
5691 * 'valid_doc_attributes()' - Determine whether the document attributes are
5694 * When one or more document attributes are invalid, this function adds a
5695 * suitable response and attributes to the unsupported group.
5698 static int /* O - 1 if valid, 0 if not */
5699 valid_doc_attributes(
5700 _ipp_client_t
*client
) /* I - Client */
5702 int valid
= 1; /* Valid attributes? */
5703 ipp_op_t op
= ippGetOperation(client
->request
);
5705 const char *op_name
= ippOpString(op
);
5706 /* IPP operation name */
5707 ipp_attribute_t
*attr
, /* Current attribute */
5708 *supported
; /* xxx-supported attribute */
5709 const char *compression
= NULL
,
5710 /* compression value */
5711 *format
= NULL
; /* document-format value */
5715 * Check operation attributes...
5718 if ((attr
= ippFindAttribute(client
->request
, "compression",
5719 IPP_TAG_ZERO
)) != NULL
)
5722 * If compression is specified, only accept a supported value in a Print-Job
5723 * or Send-Document request...
5726 compression
= ippGetString(attr
, 0, NULL
);
5727 supported
= ippFindAttribute(client
->printer
->attrs
,
5728 "compression-supported", IPP_TAG_KEYWORD
);
5730 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5731 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
5732 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
5733 op
!= IPP_OP_VALIDATE_JOB
) ||
5734 !ippContainsString(supported
, compression
))
5736 respond_unsupported(client
, attr
);
5741 fprintf(stderr
, "%s %s compression=\"%s\"\n",
5742 client
->hostname
, op_name
, compression
);
5744 if (strcmp(compression
, "none"))
5745 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
5750 * Is it a format we support?
5753 if ((attr
= ippFindAttribute(client
->request
, "document-format",
5754 IPP_TAG_ZERO
)) != NULL
)
5756 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
5757 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
5759 respond_unsupported(client
, attr
);
5764 format
= ippGetString(attr
, 0, NULL
);
5766 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
5767 client
->hostname
, op_name
, format
);
5769 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
5774 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
,
5775 "document-format-default",
5776 IPP_TAG_MIMETYPE
), 0, NULL
);
5778 format
= "application/octet-stream"; /* Should never happen */
5780 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
5783 if (!strcmp(format
, "application/octet-stream") &&
5784 (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
||
5785 ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
5788 * Auto-type the file using the first 4 bytes of the file...
5791 unsigned char header
[4]; /* First 4 bytes of file */
5793 memset(header
, 0, sizeof(header
));
5794 httpPeek(client
->http
, (char *)header
, sizeof(header
));
5796 if (!memcmp(header
, "%PDF", 4))
5797 format
= "application/pdf";
5798 else if (!memcmp(header
, "%!", 2))
5799 format
= "application/postscript";
5800 else if (!memcmp(header
, "\377\330\377", 3) &&
5801 header
[3] >= 0xe0 && header
[3] <= 0xef)
5802 format
= "image/jpeg";
5803 else if (!memcmp(header
, "\211PNG", 4))
5804 format
= "image/png";
5807 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
5808 client
->hostname
, op_name
, format
);
5811 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
5812 "document-format", NULL
, format
);
5814 ippSetString(client
->request
, &attr
, 0, format
);
5817 if (op
!= IPP_OP_CREATE_JOB
&&
5818 (supported
= ippFindAttribute(client
->printer
->attrs
,
5819 "document-format-supported",
5820 IPP_TAG_MIMETYPE
)) != NULL
&&
5821 !ippContainsString(supported
, format
))
5823 respond_unsupported(client
, attr
);
5832 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
5834 * When one or more job attributes are invalid, this function adds a suitable
5835 * response and attributes to the unsupported group.
5838 static int /* O - 1 if valid, 0 if not */
5839 valid_job_attributes(
5840 _ipp_client_t
*client
) /* I - Client */
5842 int i
, /* Looping var */
5843 valid
= 1; /* Valid attributes? */
5844 ipp_attribute_t
*attr
, /* Current attribute */
5845 *supported
; /* xxx-supported attribute */
5849 * Check operation attributes...
5852 valid
= valid_doc_attributes(client
);
5855 * Check the various job template attributes...
5858 if ((attr
= ippFindAttribute(client
->request
, "copies",
5859 IPP_TAG_ZERO
)) != NULL
)
5861 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5862 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
5864 respond_unsupported(client
, attr
);
5869 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity",
5870 IPP_TAG_ZERO
)) != NULL
)
5872 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
5874 respond_unsupported(client
, attr
);
5879 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until",
5880 IPP_TAG_ZERO
)) != NULL
)
5882 if (ippGetCount(attr
) != 1 ||
5883 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5884 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5885 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5886 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
5888 respond_unsupported(client
, attr
);
5893 if ((attr
= ippFindAttribute(client
->request
, "job-name",
5894 IPP_TAG_ZERO
)) != NULL
)
5896 if (ippGetCount(attr
) != 1 ||
5897 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5898 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
5900 respond_unsupported(client
, attr
);
5905 if ((attr
= ippFindAttribute(client
->request
, "job-priority",
5906 IPP_TAG_ZERO
)) != NULL
)
5908 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5909 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
5911 respond_unsupported(client
, attr
);
5916 if ((attr
= ippFindAttribute(client
->request
, "job-sheets",
5917 IPP_TAG_ZERO
)) != NULL
)
5919 if (ippGetCount(attr
) != 1 ||
5920 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5921 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5922 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5923 strcmp(ippGetString(attr
, 0, NULL
), "none"))
5925 respond_unsupported(client
, attr
);
5930 if ((attr
= ippFindAttribute(client
->request
, "media",
5931 IPP_TAG_ZERO
)) != NULL
)
5933 if (ippGetCount(attr
) != 1 ||
5934 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5935 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5936 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
5938 respond_unsupported(client
, attr
);
5944 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
5946 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
5949 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
5951 respond_unsupported(client
, attr
);
5957 if ((attr
= ippFindAttribute(client
->request
, "media-col",
5958 IPP_TAG_ZERO
)) != NULL
)
5960 if (ippGetCount(attr
) != 1 ||
5961 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
5963 respond_unsupported(client
, attr
);
5966 /* TODO: check for valid media-col */
5969 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling",
5970 IPP_TAG_ZERO
)) != NULL
)
5972 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5973 (strcmp(ippGetString(attr
, 0, NULL
),
5974 "separate-documents-uncollated-copies") &&
5975 strcmp(ippGetString(attr
, 0, NULL
),
5976 "separate-documents-collated-copies")))
5978 respond_unsupported(client
, attr
);
5983 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested",
5984 IPP_TAG_ZERO
)) != NULL
)
5986 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
5987 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
5988 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
5990 respond_unsupported(client
, attr
);
5995 if ((attr
= ippFindAttribute(client
->request
, "page-ranges",
5996 IPP_TAG_ZERO
)) != NULL
)
5998 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
6000 respond_unsupported(client
, attr
);
6005 if ((attr
= ippFindAttribute(client
->request
, "print-quality",
6006 IPP_TAG_ZERO
)) != NULL
)
6008 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6009 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
6010 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
6012 respond_unsupported(client
, attr
);
6017 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution",
6018 IPP_TAG_ZERO
)) != NULL
)
6020 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
6022 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
6025 respond_unsupported(client
, attr
);
6030 int count
, /* Number of supported values */
6031 xdpi
, /* Horizontal resolution for job template attribute */
6032 ydpi
, /* Vertical resolution for job template attribute */
6033 sydpi
; /* Vertical resolution for supported value */
6034 ipp_res_t units
, /* Units for job template attribute */
6035 sunits
; /* Units for supported value */
6037 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
6038 count
= ippGetCount(supported
);
6040 for (i
= 0; i
< count
; i
++)
6042 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
6048 respond_unsupported(client
, attr
);
6054 if ((attr
= ippFindAttribute(client
->request
, "sides",
6055 IPP_TAG_ZERO
)) != NULL
)
6057 const char *sides
= NULL
; /* "sides" value... */
6059 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
6061 respond_unsupported(client
, attr
);
6065 sides
= ippGetString(attr
, 0, NULL
);
6067 if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported",
6068 IPP_TAG_KEYWORD
)) != NULL
)
6070 if (!ippContainsString(supported
, sides
))
6072 respond_unsupported(client
, attr
);
6076 else if (strcmp(sides
, "one-sided"))
6078 respond_unsupported(client
, attr
);