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 */
41 extern char **environ
;
45 #endif /* HAVE_DNSSD */
48 #include <sys/fcntl.h>
50 #ifdef HAVE_SYS_MOUNT_H
51 # include <sys/mount.h>
52 #endif /* HAVE_SYS_MOUNT_H */
53 #ifdef HAVE_SYS_STATFS_H
54 # include <sys/statfs.h>
55 #endif /* HAVE_SYS_STATFS_H */
56 #ifdef HAVE_SYS_STATVFS_H
57 # include <sys/statvfs.h>
58 #endif /* HAVE_SYS_STATVFS_H */
61 #endif /* HAVE_SYS_VFS_H */
68 enum _ipp_preason_e
/* printer-state-reasons bit values */
70 _IPP_PREASON_NONE
= 0x0000, /* none */
71 _IPP_PREASON_OTHER
= 0x0001, /* other */
72 _IPP_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
73 _IPP_PREASON_INPUT_TRAY_MISSING
= 0x0004,
74 /* input-tray-missing */
75 _IPP_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
76 /* marker-supply-empty */
77 _IPP_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
78 /* marker-supply-low */
79 _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
80 /* marker-waste-almost-full */
81 _IPP_PREASON_MARKER_WASTE_FULL
= 0x0040,
82 /* marker-waste-full */
83 _IPP_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
84 _IPP_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
85 _IPP_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
86 _IPP_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
87 _IPP_PREASON_MOVING_TO_PAUSED
= 0x0800,
88 /* moving-to-paused */
89 _IPP_PREASON_PAUSED
= 0x1000, /* paused */
90 _IPP_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
91 _IPP_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
92 _IPP_PREASON_TONER_LOW
= 0x8000 /* toner-low */
94 typedef unsigned int _ipp_preason_t
; /* Bitfield for printer-state-reasons */
96 typedef enum _ipp_media_class_e
98 _IPP_GENERAL
, /* General-purpose size */
99 _IPP_PHOTO_ONLY
, /* Photo-only size */
100 _IPP_ENV_ONLY
/* Envelope-only size */
101 } _ipp_media_class_t
;
103 typedef enum _ipp_media_size_e
105 _IPP_MEDIA_SIZE_NONE
= -1,
110 _IPP_MEDIA_SIZE_LEGAL
,
111 _IPP_MEDIA_SIZE_LETTER
,
112 _IPP_MEDIA_SIZE_COM10
,
118 static const char * const media_supported
[] =
119 { /* media-supported values */
120 "iso_a4_210x297mm", /* A4 */
121 "iso_a5_148x210mm", /* A5 */
122 "iso_a6_105x148mm", /* A6 */
123 "iso_dl_110x220mm", /* DL */
124 "na_legal_8.5x14in", /* Legal */
125 "na_letter_8.5x11in", /* Letter */
126 "na_number-10_4.125x9.5in", /* #10 */
127 "na_index-3x5_3x5in", /* 3x5 */
128 "oe_photo-l_3.5x5in", /* L */
129 "na_index-4x6_4x6in", /* 4x6 */
130 "na_5x7_5x7in" /* 5x7 aka 2L */
132 static const int media_col_sizes
[][3] =
133 { /* media-col-database sizes */
134 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
135 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
136 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
137 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
138 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
139 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
140 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
141 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
142 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
143 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
144 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
147 typedef enum _ipp_media_source_e
149 _IPP_MEDIA_SOURCE_NONE
= -1,
150 _IPP_MEDIA_SOURCE_AUTO
,
151 _IPP_MEDIA_SOURCE_MAIN
,
152 _IPP_MEDIA_SOURCE_MANUAL
,
153 _IPP_MEDIA_SOURCE_ENVELOPE
,
154 _IPP_MEDIA_SOURCE_PHOTO
155 } _ipp_media_source_t
;
156 static const char * const media_source_supported
[] =
157 /* media-source-supported values */
166 typedef enum _ipp_media_type_e
168 _IPP_MEDIA_TYPE_NONE
= -1,
169 _IPP_MEDIA_TYPE_AUTO
,
170 _IPP_MEDIA_TYPE_CARDSTOCK
,
171 _IPP_MEDIA_TYPE_ENVELOPE
,
172 _IPP_MEDIA_TYPE_LABELS
,
173 _IPP_MEDIA_TYPE_OTHER
,
174 _IPP_MEDIA_TYPE_GLOSSY
,
175 _IPP_MEDIA_TYPE_HIGH_GLOSS
,
176 _IPP_MEDIA_TYPE_MATTE
,
177 _IPP_MEDIA_TYPE_SATIN
,
178 _IPP_MEDIA_TYPE_SEMI_GLOSS
,
179 _IPP_MEDIA_TYPE_STATIONERY
,
180 _IPP_MEDIA_TYPE_LETTERHEAD
,
181 _IPP_MEDIA_TYPE_TRANSPARENCY
183 static const char * const media_type_supported
[] =
184 /* media-type-supported values */
191 "photographic-glossy",
192 "photographic-high-gloss",
193 "photographic-matte",
194 "photographic-satin",
195 "photographic-semi-gloss",
197 "stationery-letterhead",
201 typedef enum _ipp_supply_e
203 _IPP_SUPPLY_CYAN
, /* Cyan Toner */
204 _IPP_SUPPLY_MAGENTA
, /* Magenta Toner */
205 _IPP_SUPPLY_YELLOW
, /* Yellow Toner */
206 _IPP_SUPPLY_BLACK
, /* Black Toner */
207 _IPP_SUPPLY_WASTE
/* Waste Toner */
209 static const char * const printer_supplies
[] =
210 { /* printer-supply-description values */
223 typedef struct _ipp_filter_s
/**** Attribute filter ****/
225 cups_array_t
*ra
; /* Requested attributes */
226 ipp_tag_t group_tag
; /* Group to copy */
229 typedef struct _ipp_job_s _ipp_job_t
;
231 typedef struct _ipp_printer_s
/**** Printer data ****/
233 int ipv4
, /* IPv4 listener */
234 ipv6
; /* IPv6 listener */
236 DNSServiceRef common_ref
, /* Shared service connection */
237 ipp_ref
, /* Bonjour IPP service */
239 ipps_ref
, /* Bonjour IPPS service */
240 # endif /* HAVE_SSL */
241 http_ref
, /* Bonjour HTTP service */
242 printer_ref
; /* Bonjour LPD service */
243 TXTRecordRef ipp_txt
; /* Bonjour IPP TXT record */
244 char *dnssd_name
; /* printer-dnssd-name */
245 #endif /* HAVE_DNSSD */
246 char *name
, /* printer-name */
247 *icon
, /* Icon filename */
248 *directory
, /* Spool directory */
249 *hostname
, /* Hostname */
250 *uri
, /* printer-uri-supported */
251 *command
; /* Command to run with job file */
253 size_t urilen
; /* Length of printer URI */
254 ipp_t
*attrs
; /* Static attributes */
255 time_t start_time
; /* Startup time */
256 time_t config_time
; /* printer-config-change-time */
257 ipp_pstate_t state
; /* printer-state value */
258 _ipp_preason_t state_reasons
; /* printer-state-reasons values */
259 time_t state_time
; /* printer-state-change-time */
260 cups_array_t
*jobs
; /* Jobs */
261 _ipp_job_t
*active_job
; /* Current active/pending job */
262 int next_job_id
; /* Next job-id value */
263 _cups_rwlock_t rwlock
; /* Printer lock */
264 _ipp_media_size_t main_size
; /* Ready media */
265 _ipp_media_type_t main_type
;
267 _ipp_media_size_t envelope_size
;
269 _ipp_media_size_t photo_size
;
270 _ipp_media_type_t photo_type
;
272 int supplies
[5]; /* Supply levels (0-100) */
275 struct _ipp_job_s
/**** Job data ****/
278 const char *name
, /* job-name */
279 *username
, /* job-originating-user-name */
280 *format
; /* document-format */
281 ipp_jstate_t state
; /* job-state value */
282 time_t created
, /* time-at-creation value */
283 processing
, /* time-at-processing value */
284 completed
; /* time-at-completed value */
285 int impressions
, /* job-impressions value */
286 impcompleted
; /* job-impressions-completed value */
287 ipp_t
*attrs
; /* Static attributes */
288 int cancel
; /* Non-zero when job canceled */
289 char *filename
; /* Print file name */
290 int fd
; /* Print file descriptor */
291 _ipp_printer_t
*printer
; /* Printer */
294 typedef struct _ipp_client_s
/**** Client data ****/
296 http_t
*http
; /* HTTP connection */
297 ipp_t
*request
, /* IPP request */
298 *response
; /* IPP response */
299 time_t start
; /* Request start time */
300 http_state_t operation
; /* Request operation */
301 ipp_op_t operation_id
; /* IPP operation-id */
302 char uri
[1024], /* Request URI */
303 *options
; /* URI options */
304 http_addr_t addr
; /* Client address */
305 char hostname
[256]; /* Client hostname */
306 _ipp_printer_t
*printer
; /* Printer */
307 _ipp_job_t
*job
; /* Current job, if any */
315 static void clean_jobs(_ipp_printer_t
*printer
);
316 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
317 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
318 ipp_tag_t group_tag
, int quickcopy
);
319 static void copy_job_attributes(_ipp_client_t
*client
,
320 _ipp_job_t
*job
, cups_array_t
*ra
);
321 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
322 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
323 static int create_listener(int family
, int port
);
324 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
325 static ipp_t
*create_media_size(int width
, int length
);
326 static _ipp_printer_t
*create_printer(const char *servername
,
327 const char *name
, const char *location
,
328 const char *make
, const char *model
,
330 const char *docformats
, int ppm
,
331 int ppm_color
, int duplex
, int port
,
335 #endif /* HAVE_DNSSD */
336 const char *directory
,
337 const char *command
);
338 static void debug_attributes(const char *title
, ipp_t
*ipp
,
340 static void delete_client(_ipp_client_t
*client
);
341 static void delete_job(_ipp_job_t
*job
);
342 static void delete_printer(_ipp_printer_t
*printer
);
344 static void dnssd_callback(DNSServiceRef sdRef
,
345 DNSServiceFlags flags
,
346 DNSServiceErrorType errorCode
,
350 _ipp_printer_t
*printer
);
351 #endif /* HAVE_DNSSD */
352 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
353 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
354 static void html_escape(_ipp_client_t
*client
, const char *s
,
356 static void html_footer(_ipp_client_t
*client
);
357 static void html_header(_ipp_client_t
*client
, const char *title
);
358 static void html_printf(_ipp_client_t
*client
, const char *format
,
359 ...) __attribute__((__format__(__printf__
,
361 static void ipp_cancel_job(_ipp_client_t
*client
);
362 static void ipp_close_job(_ipp_client_t
*client
);
363 static void ipp_create_job(_ipp_client_t
*client
);
364 static void ipp_get_job_attributes(_ipp_client_t
*client
);
365 static void ipp_get_jobs(_ipp_client_t
*client
);
366 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
367 static void ipp_identify_printer(_ipp_client_t
*client
);
368 static void ipp_print_job(_ipp_client_t
*client
);
369 static void ipp_print_uri(_ipp_client_t
*client
);
370 static void ipp_send_document(_ipp_client_t
*client
);
371 static void ipp_send_uri(_ipp_client_t
*client
);
372 static void ipp_validate_job(_ipp_client_t
*client
);
373 static int parse_options(_ipp_client_t
*client
, cups_option_t
**options
);
374 static void *process_client(_ipp_client_t
*client
);
375 static int process_http(_ipp_client_t
*client
);
376 static int process_ipp(_ipp_client_t
*client
);
377 static void *process_job(_ipp_job_t
*job
);
379 static int register_printer(_ipp_printer_t
*printer
, const char *location
, const char *make
, const char *model
, const char *formats
, const char *adminurl
, const char *uuid
, int color
, int duplex
, const char *regtype
);
380 #endif /* HAVE_DNSSD */
381 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
382 const char *content_coding
,
383 const char *type
, size_t length
);
384 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
385 const char *message
, ...)
386 __attribute__ ((__format__ (__printf__
, 3, 4)));
387 static void respond_unsupported(_ipp_client_t
*client
,
388 ipp_attribute_t
*attr
);
389 static void run_printer(_ipp_printer_t
*printer
);
390 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
391 static void usage(int status
) __attribute__((noreturn
));
392 static int valid_doc_attributes(_ipp_client_t
*client
);
393 static int valid_job_attributes(_ipp_client_t
*client
);
400 static int KeepFiles
= 0,
405 * 'main()' - Main entry to the sample server.
408 int /* O - Exit status */
409 main(int argc
, /* I - Number of command-line args */
410 char *argv
[]) /* I - Command-line arguments */
412 int i
; /* Looping var */
413 const char *opt
, /* Current option character */
414 *command
= NULL
, /* Command to run with job files */
415 *servername
= NULL
, /* Server host name */
416 *name
= NULL
, /* Printer name */
417 *location
= "", /* Location of printer */
418 *make
= "Test", /* Manufacturer */
419 *model
= "Printer", /* Model */
420 *icon
= "printer.png", /* Icon file */
421 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
422 /* Supported formats */
424 const char *keypath
= NULL
; /* Keychain path */
425 #endif /* HAVE_SSL */
427 const char *subtype
= "_print"; /* Bonjour service subtype */
428 #endif /* HAVE_DNSSD */
429 int port
= 0, /* Port number (0 = auto) */
430 duplex
= 0, /* Duplex mode */
431 ppm
= 10, /* Pages per minute for mono */
432 ppm_color
= 0, /* Pages per minute for color */
433 pin
= 0; /* PIN printing mode? */
434 char directory
[1024] = "", /* Spool directory */
435 hostname
[1024]; /* Auto-detected hostname */
436 _ipp_printer_t
*printer
; /* Printer object */
440 * Parse command-line arguments...
443 for (i
= 1; i
< argc
; i
++)
444 if (argv
[i
][0] == '-')
446 for (opt
= argv
[i
] + 1; *opt
; opt
++)
450 case '2' : /* -2 (enable 2-sided printing) */
455 case 'K' : /* -K keypath */
461 #endif /* HAVE_SSL */
463 case 'M' : /* -M manufacturer */
470 case 'P' : /* -P (PIN printing mode) */
474 case 'c' : /* -c command */
482 case 'd' : /* -d spool-directory */
486 strncpy(directory
, argv
[i
], sizeof(directory
) - 1);
487 directory
[sizeof(directory
) - 1] = '\0';
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...
584 servername
= httpGetHostname(NULL
, hostname
, sizeof(hostname
));
588 port
= 8000 + ((int)getuid() % 1000);
589 fprintf(stderr
, "Listening on port %d.\n", port
);
594 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
596 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
598 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
599 directory
, strerror(errno
));
604 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
608 cupsSetServerCredentials(keypath
, servername
, 1);
609 #endif /* HAVE_SSL */
612 * Create the printer...
615 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
616 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
619 #endif /* HAVE_DNSSD */
620 directory
, command
)) == NULL
)
624 * Run the print service...
627 run_printer(printer
);
630 * Destroy the printer and exit...
633 delete_printer(printer
);
640 * 'clean_jobs()' - Clean out old (completed) jobs.
644 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
646 _ipp_job_t
*job
; /* Current job */
647 time_t cleantime
; /* Clean time */
650 if (cupsArrayCount(printer
->jobs
) == 0)
653 cleantime
= time(NULL
) - 60;
655 _cupsRWLockWrite(&(printer
->rwlock
));
656 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
658 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
659 if (job
->completed
&& job
->completed
< cleantime
)
661 cupsArrayRemove(printer
->jobs
, job
);
666 _cupsRWUnlock(&(printer
->rwlock
));
671 * 'compare_jobs()' - Compare two jobs.
674 static int /* O - Result of comparison */
675 compare_jobs(_ipp_job_t
*a
, /* I - First job */
676 _ipp_job_t
*b
) /* I - Second job */
678 return (b
->id
- a
->id
);
683 * 'copy_attributes()' - Copy attributes from one request to another.
687 copy_attributes(ipp_t
*to
, /* I - Destination request */
688 ipp_t
*from
, /* I - Source request */
689 cups_array_t
*ra
, /* I - Requested attributes */
690 ipp_tag_t group_tag
, /* I - Group to copy */
691 int quickcopy
) /* I - Do a quick copy? */
693 _ipp_filter_t filter
; /* Filter data */
697 filter
.group_tag
= group_tag
;
699 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
704 * 'copy_job_attrs()' - Copy job attributes to the response.
709 _ipp_client_t
*client
, /* I - Client */
710 _ipp_job_t
*job
, /* I - Job */
711 cups_array_t
*ra
) /* I - requested-attributes */
713 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
715 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
718 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
720 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
723 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
726 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
728 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
731 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
732 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
734 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
735 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
737 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
738 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
740 if (!ra
|| cupsArrayFind(ra
, "job-state"))
741 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
742 "job-state", job
->state
);
744 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
748 case IPP_JSTATE_PENDING
:
749 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
752 case IPP_JSTATE_HELD
:
754 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
755 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
756 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
758 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
761 case IPP_JSTATE_PROCESSING
:
763 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
765 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
768 case IPP_JSTATE_STOPPED
:
769 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
772 case IPP_JSTATE_CANCELED
:
773 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
776 case IPP_JSTATE_ABORTED
:
777 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
780 case IPP_JSTATE_COMPLETED
:
781 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
786 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
790 case IPP_JSTATE_PENDING
:
791 ippAddString(client
->response
, IPP_TAG_JOB
,
792 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
796 case IPP_JSTATE_HELD
:
798 ippAddString(client
->response
, IPP_TAG_JOB
,
799 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
800 "job-state-reasons", NULL
, "job-incoming");
801 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
802 ippAddString(client
->response
, IPP_TAG_JOB
,
803 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
804 "job-state-reasons", NULL
, "job-hold-until-specified");
806 ippAddString(client
->response
, IPP_TAG_JOB
,
807 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
808 "job-state-reasons", NULL
, "job-data-insufficient");
811 case IPP_JSTATE_PROCESSING
:
813 ippAddString(client
->response
, IPP_TAG_JOB
,
814 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
815 "job-state-reasons", NULL
, "processing-to-stop-point");
817 ippAddString(client
->response
, IPP_TAG_JOB
,
818 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
819 "job-state-reasons", NULL
, "job-printing");
822 case IPP_JSTATE_STOPPED
:
823 ippAddString(client
->response
, IPP_TAG_JOB
,
824 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
825 NULL
, "job-stopped");
828 case IPP_JSTATE_CANCELED
:
829 ippAddString(client
->response
, IPP_TAG_JOB
,
830 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
831 NULL
, "job-canceled-by-user");
834 case IPP_JSTATE_ABORTED
:
835 ippAddString(client
->response
, IPP_TAG_JOB
,
836 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
837 NULL
, "aborted-by-system");
840 case IPP_JSTATE_COMPLETED
:
841 ippAddString(client
->response
, IPP_TAG_JOB
,
842 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
843 NULL
, "job-completed-successfully");
848 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
849 ippAddInteger(client
->response
, IPP_TAG_JOB
,
850 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
851 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
853 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
854 ippAddInteger(client
->response
, IPP_TAG_JOB
,
855 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
856 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
861 * 'create_client()' - Accept a new network connection and create a client
865 static _ipp_client_t
* /* O - Client */
866 create_client(_ipp_printer_t
*printer
, /* I - Printer */
867 int sock
) /* I - Listen socket */
869 _ipp_client_t
*client
; /* Client */
872 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
874 perror("Unable to allocate memory for client");
878 client
->printer
= printer
;
881 * Accept the client and get the remote address...
884 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
886 perror("Unable to accept client connection");
893 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
896 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
903 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
907 static _ipp_job_t
* /* O - Job */
908 create_job(_ipp_client_t
*client
) /* I - Client */
910 _ipp_job_t
*job
; /* Job */
911 ipp_attribute_t
*attr
; /* Job attribute */
912 char uri
[1024], /* job-uri value */
913 uuid
[64]; /* job-uuid value */
916 _cupsRWLockWrite(&(client
->printer
->rwlock
));
917 if (client
->printer
->active_job
&&
918 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
921 * Only accept a single job at a time...
924 _cupsRWLockWrite(&(client
->printer
->rwlock
));
929 * Allocate and initialize the job object...
932 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
934 perror("Unable to allocate memory for job");
938 job
->printer
= client
->printer
;
939 job
->attrs
= ippNew();
940 job
->state
= IPP_JSTATE_HELD
;
944 * Copy all of the job attributes...
947 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
950 * Get the requesting-user-name, document format, and priority...
953 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
954 job
->username
= ippGetString(attr
, 0, NULL
);
956 job
->username
= "anonymous";
958 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
960 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
962 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
963 job
->format
= ippGetString(attr
, 0, NULL
);
964 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
965 job
->format
= ippGetString(attr
, 0, NULL
);
967 job
->format
= "application/octet-stream";
970 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
971 job
->impressions
= ippGetInteger(attr
, 0);
973 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
974 job
->name
= ippGetString(attr
, 0, NULL
);
977 * Add job description attributes and add to the jobs array...
980 job
->id
= client
->printer
->next_job_id
++;
982 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
983 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
985 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
986 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
987 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
988 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
989 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
990 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
992 cupsArrayAdd(client
->printer
->jobs
, job
);
993 client
->printer
->active_job
= job
;
995 _cupsRWUnlock(&(client
->printer
->rwlock
));
1002 * 'create_job_filename()' - Create the filename for a document in a job.
1005 static void create_job_filename(
1006 _ipp_printer_t
*printer
, /* I - Printer */
1007 _ipp_job_t
*job
, /* I - Job */
1008 char *fname
, /* I - Filename buffer */
1009 size_t fnamesize
) /* I - Size of filename buffer */
1011 char name
[256], /* "Safe" filename */
1012 *nameptr
; /* Pointer into filename */
1013 const char *ext
, /* Filename extension */
1014 *job_name
; /* job-name value */
1015 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1019 * Make a name from the job-name attribute...
1022 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1023 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1025 job_name
= "untitled";
1027 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1028 if (isalnum(*job_name
& 255) || *job_name
== '-')
1029 *nameptr
++ = (char)tolower(*job_name
& 255);
1036 * Figure out the extension...
1039 if (!strcasecmp(job
->format
, "image/jpeg"))
1041 else if (!strcasecmp(job
->format
, "image/png"))
1043 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1045 else if (!strcasecmp(job
->format
, "image/urf"))
1047 else if (!strcasecmp(job
->format
, "application/pdf"))
1049 else if (!strcasecmp(job
->format
, "application/postscript"))
1055 * Create a filename with the job-id, job-name, and document-format (extension)...
1058 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1063 * 'create_listener()' - Create a listener socket.
1066 static int /* O - Listener socket or -1 on error */
1067 create_listener(int family
, /* I - Address family */
1068 int port
) /* I - Port number */
1070 int sock
; /* Listener socket */
1071 http_addrlist_t
*addrlist
; /* Listen address */
1072 char service
[255]; /* Service port */
1075 snprintf(service
, sizeof(service
), "%d", port
);
1076 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1079 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1081 httpAddrFreeList(addrlist
);
1088 * 'create_media_col()' - Create a media-col value.
1091 static ipp_t
* /* O - media-col collection */
1092 create_media_col(const char *media
, /* I - Media name */
1093 const char *source
, /* I - Media source */
1094 const char *type
, /* I - Media type */
1095 int width
, /* I - x-dimension in 2540ths */
1096 int length
, /* I - y-dimension in 2540ths */
1097 int margins
) /* I - Value for margins */
1099 ipp_t
*media_col
= ippNew(), /* media-col value */
1100 *media_size
= create_media_size(width
, length
);
1101 /* media-size value */
1102 char media_key
[256]; /* media-key value */
1106 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1108 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1110 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1112 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1114 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1116 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1117 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1118 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1119 "media-bottom-margin", margins
);
1120 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1121 "media-left-margin", margins
);
1122 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1123 "media-right-margin", margins
);
1124 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1125 "media-top-margin", margins
);
1127 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1129 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1131 ippDelete(media_size
);
1138 * 'create_media_size()' - Create a media-size value.
1141 static ipp_t
* /* O - media-col collection */
1142 create_media_size(int width
, /* I - x-dimension in 2540ths */
1143 int length
) /* I - y-dimension in 2540ths */
1145 ipp_t
*media_size
= ippNew(); /* media-size value */
1148 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1150 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1153 return (media_size
);
1158 * 'create_printer()' - Create, register, and listen for connections to a
1162 static _ipp_printer_t
* /* O - Printer */
1163 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1164 const char *name
, /* I - printer-name */
1165 const char *location
, /* I - printer-location */
1166 const char *make
, /* I - printer-make-and-model */
1167 const char *model
, /* I - printer-make-and-model */
1168 const char *icon
, /* I - printer-icons */
1169 const char *docformats
, /* I - document-format-supported */
1170 int ppm
, /* I - Pages per minute in grayscale */
1171 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1172 int duplex
, /* I - 1 = duplex, 0 = simplex */
1173 int port
, /* I - Port for listeners or 0 for auto */
1174 int pin
, /* I - Require PIN printing */
1176 const char *subtype
, /* I - Bonjour service subtype */
1177 #endif /* HAVE_DNSSD */
1178 const char *directory
, /* I - Spool directory */
1179 const char *command
) /* I - Command to run on job files */
1181 int i
, j
; /* Looping vars */
1182 _ipp_printer_t
*printer
; /* Printer */
1184 char path
[1024]; /* Full path to command */
1186 char uri
[1024], /* Printer URI */
1187 icons
[1024], /* printer-icons URI */
1188 adminurl
[1024], /* printer-more-info URI */
1189 supplyurl
[1024],/* printer-supply-info-uri URI */
1190 device_id
[1024],/* printer-device-id */
1191 make_model
[128],/* printer-make-and-model */
1192 uuid
[128]; /* printer-uuid */
1193 int num_formats
; /* Number of document-format-supported values */
1194 char *defformat
, /* document-format-default value */
1195 *formats
[100], /* document-format-supported values */
1196 *ptr
; /* Pointer into string */
1197 const char *prefix
; /* Prefix string */
1198 int num_database
; /* Number of database values */
1199 ipp_attribute_t
*media_col_database
,
1200 /* media-col-database value */
1201 *media_size_supported
;
1202 /* media-size-supported value */
1203 ipp_t
*media_col_default
;
1204 /* media-col-default value */
1205 int media_col_index
;/* Current media-col-database value */
1206 int k_supported
; /* Maximum file size supported */
1208 struct statvfs spoolinfo
; /* FS info for spool directory */
1209 double spoolsize
; /* FS size */
1210 #elif defined(HAVE_STATFS)
1211 struct statfs spoolinfo
; /* FS info for spool directory */
1212 double spoolsize
; /* FS size */
1213 #endif /* HAVE_STATVFS */
1214 static const int orients
[4] = /* orientation-requested-supported values */
1216 IPP_ORIENT_PORTRAIT
,
1217 IPP_ORIENT_LANDSCAPE
,
1218 IPP_ORIENT_REVERSE_LANDSCAPE
,
1219 IPP_ORIENT_REVERSE_PORTRAIT
1221 static const char * const versions
[] =/* ipp-versions-supported values */
1227 static const char * const features
[] =/* ipp-features-supported values */
1231 static const int ops
[] = /* operations-supported values */
1235 IPP_OP_VALIDATE_JOB
,
1237 IPP_OP_SEND_DOCUMENT
,
1240 IPP_OP_GET_JOB_ATTRIBUTES
,
1242 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1243 IPP_OP_CANCEL_MY_JOBS
,
1245 IPP_OP_IDENTIFY_PRINTER
1247 static const char * const charsets
[] =/* charset-supported values */
1252 static const char * const compressions
[] =/* compression-supported values */
1257 #endif /* HAVE_LIBZ */
1260 static const char * const identify_actions
[] =
1265 static const char * const job_creation
[] =
1266 { /* job-creation-attributes-supported values */
1268 "ipp-attribute-fidelity",
1270 "job-accounting-user-id",
1276 "multiple-document-handling",
1277 "orientation-requested",
1281 static const char * const media_col_supported
[] =
1282 { /* media-col-supported values */
1283 "media-bottom-margin",
1284 "media-left-margin",
1285 "media-right-margin",
1291 static const int media_xxx_margin_supported
[] =
1292 { /* media-xxx-margin-supported values */
1296 static const char * const multiple_document_handling
[] =
1297 { /* multiple-document-handling-supported values */
1298 "separate-documents-uncollated-copies",
1299 "separate-documents-collated-copies"
1301 static const char * const overrides
[] =
1302 { /* overrides-supported */
1306 static const char * const print_color_mode_supported
[] =
1307 { /* print-color-mode-supported values */
1312 static const int print_quality_supported
[] =
1313 { /* print-quality-supported values */
1318 static const int pwg_raster_document_resolution_supported
[] =
1324 static const char * const pwg_raster_document_type_supported
[] =
1332 static const char * const reference_uri_schemes_supported
[] =
1333 { /* reference-uri-schemes-supported */
1339 #endif /* HAVE_SSL */
1341 static const char * const sides_supported
[] =
1342 { /* sides-supported values */
1344 "two-sided-long-edge",
1345 "two-sided-short-edge"
1347 static const char * const urf_supported
[] =
1348 { /* urf-supported values */
1351 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1358 static const char * const which_jobs
[] =
1359 { /* which-jobs-supported values */
1368 "processing-stopped"
1374 * If a command was specified, make sure it exists and is executable...
1379 if (*command
== '/' || !strncmp(command
, "./", 2))
1381 if (access(command
, X_OK
))
1383 fprintf(stderr
, "ippserver: Unable to execute command \"%s\": %s\n", command
, strerror(errno
));
1389 if (!cupsFileFind(command
, getenv("PATH"), 1, path
, sizeof(path
)))
1391 fprintf(stderr
, "ippserver: Unable to find command \"%s\".\n", command
);
1401 * Allocate memory for the printer...
1404 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1406 perror("ippserver: Unable to allocate memory for printer");
1412 printer
->name
= strdup(name
);
1414 printer
->dnssd_name
= strdup(printer
->name
);
1415 #endif /* HAVE_DNSSD */
1416 printer
->command
= command
? strdup(command
) : NULL
;
1417 printer
->directory
= strdup(directory
);
1418 printer
->hostname
= strdup(servername
);
1419 printer
->port
= port
;
1420 printer
->start_time
= time(NULL
);
1421 printer
->config_time
= printer
->start_time
;
1422 printer
->state
= IPP_PSTATE_IDLE
;
1423 printer
->state_reasons
= _IPP_PREASON_NONE
;
1424 printer
->state_time
= printer
->start_time
;
1425 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1426 printer
->next_job_id
= 1;
1428 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1429 printer
->hostname
, printer
->port
, "/ipp/print");
1430 printer
->uri
= strdup(uri
);
1431 printer
->urilen
= strlen(uri
);
1434 printer
->icon
= strdup(icon
);
1436 printer
->main_size
= _IPP_MEDIA_SIZE_A4
;
1437 printer
->main_type
= _IPP_MEDIA_TYPE_STATIONERY
;
1438 printer
->main_level
= 500;
1440 printer
->envelope_size
= _IPP_MEDIA_SIZE_NONE
;
1441 printer
->envelope_level
= 0;
1443 printer
->photo_size
= _IPP_MEDIA_SIZE_NONE
;
1444 printer
->photo_type
= _IPP_MEDIA_TYPE_NONE
;
1445 printer
->photo_level
= 0;
1447 printer
->supplies
[_IPP_SUPPLY_CYAN
] = 100;
1448 printer
->supplies
[_IPP_SUPPLY_MAGENTA
] = 100;
1449 printer
->supplies
[_IPP_SUPPLY_YELLOW
] = 100;
1450 printer
->supplies
[_IPP_SUPPLY_BLACK
] = 100;
1451 printer
->supplies
[_IPP_SUPPLY_WASTE
] = 0;
1453 _cupsRWInit(&(printer
->rwlock
));
1456 * Create the listener sockets...
1459 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1461 perror("Unable to create IPv4 listener");
1465 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1467 perror("Unable to create IPv6 listener");
1472 * Prepare values for the printer attributes...
1475 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1476 printer
->hostname
, printer
->port
, "/icon.png");
1477 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/");
1478 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/supplies");
1482 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1483 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1484 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1487 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1490 formats
[0] = strdup(docformats
);
1491 defformat
= formats
[0];
1492 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1495 formats
[num_formats
++] = ptr
;
1497 if (!strcasecmp(ptr
, "application/octet-stream"))
1501 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1502 ptr
= device_id
+ strlen(device_id
);
1504 for (i
= 0; i
< num_formats
; i
++)
1506 if (!strcasecmp(formats
[i
], "application/pdf"))
1507 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1508 else if (!strcasecmp(formats
[i
], "application/postscript"))
1509 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1510 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1511 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1512 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1513 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1514 else if (!strcasecmp(formats
[i
], "image/png"))
1515 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1516 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1517 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1522 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
1529 * Get the maximum spool size based on the size of the filesystem used for
1530 * the spool directory. If the host OS doesn't support the statfs call
1531 * or the filesystem is larger than 2TiB, always report INT_MAX.
1535 if (statvfs(printer
->directory
, &spoolinfo
))
1536 k_supported
= INT_MAX
;
1537 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1538 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1539 k_supported
= INT_MAX
;
1541 k_supported
= (int)spoolsize
;
1543 #elif defined(HAVE_STATFS)
1544 if (statfs(printer
->directory
, &spoolinfo
))
1545 k_supported
= INT_MAX
;
1546 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1547 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1548 k_supported
= INT_MAX
;
1550 k_supported
= (int)spoolsize
;
1553 k_supported
= INT_MAX
;
1554 #endif /* HAVE_STATVFS */
1557 * Create the printer attributes. This list of attributes is sorted to improve
1558 * performance when the client provides a requested-attributes attribute...
1561 printer
->attrs
= ippNew();
1563 /* charset-configured */
1564 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1565 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1566 "charset-configured", NULL
, "utf-8");
1568 /* charset-supported */
1569 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1570 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1571 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1574 /* color-supported */
1575 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1578 /* compression-supported */
1579 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1580 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1581 "compression-supported",
1582 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1585 /* copies-default */
1586 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1587 "copies-default", 1);
1589 /* copies-supported */
1590 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1592 /* document-format-default */
1593 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1594 "document-format-default", NULL
, defformat
);
1596 /* document-format-supported */
1597 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1598 "document-format-supported", num_formats
, NULL
,
1599 (const char * const *)formats
);
1601 /* document-password-supported */
1602 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1604 /* finishings-default */
1605 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1606 "finishings-default", IPP_FINISHINGS_NONE
);
1608 /* finishings-supported */
1609 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1610 "finishings-supported", IPP_FINISHINGS_NONE
);
1612 /* generated-natural-language-supported */
1613 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1614 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1615 "generated-natural-language-supported", NULL
, "en");
1617 /* identify-actions-default */
1618 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1620 /* identify-actions-supported */
1621 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
);
1623 /* ipp-features-supported */
1624 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1626 /* ipp-versions-supported */
1627 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1629 /* job-account-id-default */
1630 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1632 /* job-account-id-supported */
1633 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1635 /* job-accounting-user-id-default */
1636 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1638 /* job-accounting-user-id-supported */
1639 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1641 /* job-creation-attributes-supported */
1642 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
);
1644 /* job-ids-supported */
1645 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1647 /* job-k-octets-supported */
1648 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1651 /* job-password-supported */
1652 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1653 "job-password-supported", 4);
1655 /* job-preferred-attributes-supported */
1656 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-preferred-attributes-supported", 0);
1658 /* job-priority-default */
1659 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1660 "job-priority-default", 50);
1662 /* job-priority-supported */
1663 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1664 "job-priority-supported", 100);
1666 /* job-sheets-default */
1667 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1668 IPP_CONST_TAG(IPP_TAG_NAME
),
1669 "job-sheets-default", NULL
, "none");
1671 /* job-sheets-supported */
1672 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1673 IPP_CONST_TAG(IPP_TAG_NAME
),
1674 "job-sheets-supported", NULL
, "none");
1676 /* media-bottom-margin-supported */
1677 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1678 "media-bottom-margin-supported",
1679 (int)(sizeof(media_xxx_margin_supported
) /
1680 sizeof(media_xxx_margin_supported
[0])),
1681 media_xxx_margin_supported
);
1683 /* media-col-database */
1684 for (num_database
= 0, i
= 0;
1685 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1688 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1689 num_database
+= 3; /* auto + manual + envelope */
1690 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1691 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1693 num_database
+= 2; /* Regular + borderless */
1696 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1697 "media-col-database", num_database
,
1699 for (media_col_index
= 0, i
= 0;
1700 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1703 switch (media_col_sizes
[i
][2])
1707 * Regular + borderless for the general class; no source/type
1711 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]));
1712 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]));
1715 case _IPP_ENV_ONLY
:
1717 * Regular margins for "auto", "manual", and "envelope" sources.
1720 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]));
1721 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]));
1722 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]));
1724 case _IPP_PHOTO_ONLY
:
1726 * Photos have specific media types and can only be printed via
1727 * the auto, manual, and photo sources...
1731 j
< (int)(sizeof(media_type_supported
) /
1732 sizeof(media_type_supported
[0]));
1735 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1738 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]));
1739 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]));
1740 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]));
1746 /* media-col-default */
1747 media_col_default
= create_media_col(media_supported
[0],
1748 media_source_supported
[0],
1749 media_type_supported
[0],
1750 media_col_sizes
[0][0],
1751 media_col_sizes
[0][1],
1752 media_xxx_margin_supported
[1]);
1754 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1756 ippDelete(media_col_default
);
1758 /* media-col-supported */
1759 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1760 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1761 "media-col-supported",
1762 (int)(sizeof(media_col_supported
) /
1763 sizeof(media_col_supported
[0])), NULL
,
1764 media_col_supported
);
1767 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1768 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1769 "media-default", NULL
, media_supported
[0]);
1771 /* media-left-margin-supported */
1772 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1773 "media-left-margin-supported",
1774 (int)(sizeof(media_xxx_margin_supported
) /
1775 sizeof(media_xxx_margin_supported
[0])),
1776 media_xxx_margin_supported
);
1778 /* media-right-margin-supported */
1779 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1780 "media-right-margin-supported",
1781 (int)(sizeof(media_xxx_margin_supported
) /
1782 sizeof(media_xxx_margin_supported
[0])),
1783 media_xxx_margin_supported
);
1785 /* media-supported */
1786 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1787 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1789 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1790 NULL
, media_supported
);
1792 /* media-size-supported */
1793 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1794 "media-size-supported",
1795 (int)(sizeof(media_col_sizes
) /
1796 sizeof(media_col_sizes
[0])),
1799 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1801 ippSetCollection(printer
->attrs
, &media_size_supported
, i
,
1802 create_media_size(media_col_sizes
[i
][0],
1803 media_col_sizes
[i
][1]));
1805 /* media-source-supported */
1806 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
);
1808 /* media-top-margin-supported */
1809 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1810 "media-top-margin-supported",
1811 (int)(sizeof(media_xxx_margin_supported
) /
1812 sizeof(media_xxx_margin_supported
[0])),
1813 media_xxx_margin_supported
);
1815 /* media-type-supported */
1816 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
);
1818 /* multiple-document-handling-supported */
1819 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
);
1821 /* multiple-document-jobs-supported */
1822 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1824 /* multiple-operation-time-out */
1825 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1827 /* multiple-operation-time-out-action */
1828 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1830 /* natural-language-configured */
1831 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1832 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1833 "natural-language-configured", NULL
, "en");
1835 /* number-up-default */
1836 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1837 "number-up-default", 1);
1839 /* number-up-supported */
1840 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1841 "number-up-supported", 1);
1843 /* operations-supported */
1844 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1845 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1847 /* orientation-requested-default */
1848 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1849 "orientation-requested-default", 0);
1851 /* orientation-requested-supported */
1852 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1853 "orientation-requested-supported", 4, orients
);
1855 /* output-bin-default */
1856 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1857 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1858 "output-bin-default", NULL
, "face-down");
1860 /* output-bin-supported */
1861 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1862 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1863 "output-bin-supported", NULL
, "face-down");
1865 /* overrides-supported */
1866 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1868 /* page-ranges-supported */
1869 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1871 /* pages-per-minute */
1872 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1873 "pages-per-minute", ppm
);
1875 /* pages-per-minute-color */
1877 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1878 "pages-per-minute-color", ppm_color
);
1880 /* pdl-override-supported */
1881 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1882 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1883 "pdl-override-supported", NULL
, "attempted");
1885 /* print-color-mode-default */
1886 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1888 /* print-color-mode-supported */
1889 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
);
1891 /* print-content-optimize-default */
1892 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1894 /* print-content-optimize-supported */
1895 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
1897 /* print-rendering-intent-default */
1898 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
1900 /* print-rendering-intent-supported */
1901 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
1903 /* print-quality-default */
1904 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
1906 /* print-quality-supported */
1907 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
);
1909 /* printer-device-id */
1910 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1911 "printer-device-id", NULL
, device_id
);
1913 /* printer-get-attributes-supported */
1914 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1916 /* printer-geo-location */
1917 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
1920 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1921 "printer-icons", NULL
, icons
);
1923 /* printer-is-accepting-jobs */
1924 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1928 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1931 /* printer-location */
1932 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1933 "printer-location", NULL
, location
);
1935 /* printer-make-and-model */
1936 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1937 "printer-make-and-model", NULL
, make_model
);
1939 /* printer-mandatory-job-attributes */
1942 static const char * const names
[] =
1944 "job-accounting-user-id",
1948 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1949 "printer-mandatory-job-attributes",
1950 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
1953 /* printer-more-info */
1954 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1957 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1960 /* printer-organization */
1961 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
1963 /* printer-organizational-unit */
1964 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
1966 /* printer-resolution-default */
1967 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1968 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1970 /* printer-resolution-supported */
1971 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1972 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1974 /* printer-supply-description */
1975 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
);
1977 /* printer-supply-info-uri */
1978 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
1980 /* printer-uri-supported */
1981 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
1984 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
1985 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
1987 /* pwg-raster-document-xxx-supported */
1988 for (i
= 0; i
< num_formats
; i
++)
1989 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
1992 if (i
< num_formats
)
1994 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
,
1995 "pwg-raster-document-resolution-supported",
1996 (int)(sizeof(pwg_raster_document_resolution_supported
) /
1997 sizeof(pwg_raster_document_resolution_supported
[0])),
1999 pwg_raster_document_resolution_supported
,
2000 pwg_raster_document_resolution_supported
);
2001 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2002 "pwg-raster-document-sheet-back", NULL
, "normal");
2003 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2004 "pwg-raster-document-type-supported",
2005 (int)(sizeof(pwg_raster_document_type_supported
) /
2006 sizeof(pwg_raster_document_type_supported
[0])), NULL
,
2007 pwg_raster_document_type_supported
);
2010 /* reference-uri-scheme-supported */
2011 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2012 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
2013 "reference-uri-schemes-supported",
2014 (int)(sizeof(reference_uri_schemes_supported
) /
2015 sizeof(reference_uri_schemes_supported
[0])),
2016 NULL
, reference_uri_schemes_supported
);
2019 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2020 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2021 "sides-default", NULL
, "one-sided");
2023 /* sides-supported */
2024 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2025 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2026 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
2029 for (i
= 0; i
< num_formats
; i
++)
2030 if (!strcasecmp(formats
[i
], "image/urf"))
2033 if (i
< num_formats
)
2034 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2036 /* uri-authentication-supported */
2037 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2038 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2039 "uri-authentication-supported", NULL
, "none");
2041 /* uri-security-supported */
2042 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2043 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2044 "uri-security-supported", NULL
, "none");
2046 /* which-jobs-supported */
2047 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2048 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2049 "which-jobs-supported",
2050 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
2054 debug_attributes("Printer", printer
->attrs
, 0);
2058 * Register the printer with Bonjour...
2061 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2063 #endif /* HAVE_DNSSD */
2073 * If we get here we were unable to create the printer...
2078 delete_printer(printer
);
2084 * 'debug_attributes()' - Print attributes in a request or response.
2088 debug_attributes(const char *title
, /* I - Title */
2089 ipp_t
*ipp
, /* I - Request/response */
2090 int type
) /* I - 0 = object, 1 = request, 2 = response */
2092 ipp_tag_t group_tag
; /* Current group */
2093 ipp_attribute_t
*attr
; /* Current attribute */
2094 char buffer
[2048]; /* String buffer for value */
2095 int major
, minor
; /* Version */
2101 fprintf(stderr
, "%s:\n", title
);
2102 major
= ippGetVersion(ipp
, &minor
);
2103 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2105 fprintf(stderr
, " operation-id=%s(%04x)\n",
2106 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2108 fprintf(stderr
, " status-code=%s(%04x)\n",
2109 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2110 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2112 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2114 attr
= ippNextAttribute(ipp
))
2116 if (ippGetGroupTag(attr
) != group_tag
)
2118 group_tag
= ippGetGroupTag(attr
);
2119 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2122 if (ippGetName(attr
))
2124 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2125 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2126 ippGetCount(attr
) > 1 ? "1setOf " : "",
2127 ippTagString(ippGetValueTag(attr
)), buffer
);
2134 * 'delete_client()' - Close the socket and free all memory used by a client
2139 delete_client(_ipp_client_t
*client
) /* I - Client */
2142 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2145 * Flush pending writes before closing...
2148 httpFlushWrite(client
->http
);
2154 httpClose(client
->http
);
2156 ippDelete(client
->request
);
2157 ippDelete(client
->response
);
2164 * 'delete_job()' - Remove from the printer and free all memory used by a job
2169 delete_job(_ipp_job_t
*job
) /* I - Job */
2172 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2174 ippDelete(job
->attrs
);
2179 unlink(job
->filename
);
2181 free(job
->filename
);
2189 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2190 * used by a printer object.
2194 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2196 if (printer
->ipv4
>= 0)
2197 close(printer
->ipv4
);
2199 if (printer
->ipv6
>= 0)
2200 close(printer
->ipv6
);
2203 if (printer
->printer_ref
)
2204 DNSServiceRefDeallocate(printer
->printer_ref
);
2206 if (printer
->ipp_ref
)
2207 DNSServiceRefDeallocate(printer
->ipp_ref
);
2210 if (printer
->ipps_ref
)
2211 DNSServiceRefDeallocate(printer
->ipps_ref
);
2212 # endif /* HAVE_SSL */
2213 if (printer
->http_ref
)
2214 DNSServiceRefDeallocate(printer
->http_ref
);
2216 if (printer
->common_ref
)
2217 DNSServiceRefDeallocate(printer
->common_ref
);
2219 TXTRecordDeallocate(&(printer
->ipp_txt
));
2221 if (printer
->dnssd_name
)
2222 free(printer
->dnssd_name
);
2223 #endif /* HAVE_DNSSD */
2226 free(printer
->name
);
2228 free(printer
->icon
);
2229 if (printer
->command
)
2230 free(printer
->command
);
2231 if (printer
->directory
)
2232 free(printer
->directory
);
2233 if (printer
->hostname
)
2234 free(printer
->hostname
);
2238 ippDelete(printer
->attrs
);
2239 cupsArrayDelete(printer
->jobs
);
2247 * 'dnssd_callback()' - Handle Bonjour registration events.
2252 DNSServiceRef sdRef
, /* I - Service reference */
2253 DNSServiceFlags flags
, /* I - Status flags */
2254 DNSServiceErrorType errorCode
, /* I - Error, if any */
2255 const char *name
, /* I - Service name */
2256 const char *regtype
, /* I - Service type */
2257 const char *domain
, /* I - Domain for service */
2258 _ipp_printer_t
*printer
) /* I - Printer */
2266 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2267 regtype
, (int)errorCode
);
2270 else if (strcasecmp(name
, printer
->dnssd_name
))
2273 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2275 /* No lock needed since only the main thread accesses/changes this */
2276 free(printer
->dnssd_name
);
2277 printer
->dnssd_name
= strdup(name
);
2280 #endif /* HAVE_DNSSD */
2284 * 'filter_cb()' - Filter printer attributes based on the requested array.
2287 static int /* O - 1 to copy, 0 to ignore */
2288 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2289 ipp_t
*dst
, /* I - Destination (unused) */
2290 ipp_attribute_t
*attr
) /* I - Source attribute */
2293 * Filter attributes as needed...
2298 ipp_tag_t group
= ippGetGroupTag(attr
);
2299 const char *name
= ippGetName(attr
);
2301 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
)))
2304 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2309 * 'find_job()' - Find a job specified in a request.
2312 static _ipp_job_t
* /* O - Job or NULL */
2313 find_job(_ipp_client_t
*client
) /* I - Client */
2315 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2316 _ipp_job_t key
, /* Job search key */
2317 *job
; /* Matching job, if any */
2320 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2322 const char *uri
= ippGetString(attr
, 0, NULL
);
2324 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2325 uri
[client
->printer
->urilen
] == '/')
2326 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2330 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2331 key
.id
= ippGetInteger(attr
, 0);
2333 _cupsRWLockRead(&(client
->printer
->rwlock
));
2334 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2335 _cupsRWUnlock(&(client
->printer
->rwlock
));
2342 * 'html_escape()' - Write a HTML-safe string.
2346 html_escape(_ipp_client_t
*client
, /* I - Client */
2347 const char *s
, /* I - String to write */
2348 size_t slen
) /* I - Number of characters to write */
2350 const char *start
, /* Start of segment */
2351 *end
; /* End of string */
2355 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2357 while (*s
&& s
< end
)
2359 if (*s
== '&' || *s
== '<')
2362 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2365 httpWrite2(client
->http
, "&", 5);
2367 httpWrite2(client
->http
, "<", 4);
2376 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2381 * 'html_footer()' - Show the web interface footer.
2383 * This function also writes the trailing 0-length chunk.
2387 html_footer(_ipp_client_t
*client
) /* I - Client */
2393 httpWrite2(client
->http
, "", 0);
2398 * 'html_header()' - Show the web interface header and title.
2402 html_header(_ipp_client_t
*client
, /* I - Client */
2403 const char *title
) /* I - Title */
2409 "<title>%s</title>\n"
2410 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2411 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2412 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2413 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2415 "body { font-family: sans-serif; margin: 0; }\n"
2416 "div.body { padding: 0px 10px 10px; }\n"
2417 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2418 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2419 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2420 "table.form th { text-align: right; }\n"
2421 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2422 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2423 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2424 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2425 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2426 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2427 "table.nav td { margin: 0; text-align: center; }\n"
2428 "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"
2429 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2430 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2431 "td.nav:hover { background: #666; color: #fff; }\n"
2432 "td.nav:active { background: #000; color: #ff0; }\n"
2436 "<table class=\"nav\"><tr>"
2437 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2438 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2439 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2441 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2446 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2450 html_printf(_ipp_client_t
*client
, /* I - Client */
2451 const char *format
, /* I - Printf-style format string */
2452 ...) /* I - Additional arguments as needed */
2454 va_list ap
; /* Pointer to arguments */
2455 const char *start
; /* Start of string */
2456 char size
, /* Size character (h, l, L) */
2457 type
; /* Format type character */
2458 int width
, /* Width of field */
2459 prec
; /* Number of characters of precision */
2460 char tformat
[100], /* Temporary format string for sprintf() */
2461 *tptr
, /* Pointer into temporary format */
2462 temp
[1024]; /* Buffer for formatted numbers */
2463 char *s
; /* Pointer to string */
2467 * Loop through the format string, formatting as needed...
2470 va_start(ap
, format
);
2478 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2481 *tptr
++ = *format
++;
2485 httpWrite2(client
->http
, "%", 1);
2490 else if (strchr(" -+#\'", *format
))
2491 *tptr
++ = *format
++;
2496 * Get width from argument...
2500 width
= va_arg(ap
, int);
2502 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2503 tptr
+= strlen(tptr
);
2509 while (isdigit(*format
& 255))
2511 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2514 width
= width
* 10 + *format
++ - '0';
2520 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2528 * Get precision from argument...
2532 prec
= va_arg(ap
, int);
2534 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2535 tptr
+= strlen(tptr
);
2541 while (isdigit(*format
& 255))
2543 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2546 prec
= prec
* 10 + *format
++ - '0';
2551 if (*format
== 'l' && format
[1] == 'l')
2555 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2563 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2565 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2580 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2589 case 'E' : /* Floating point formats */
2594 if ((size_t)(width
+ 2) > sizeof(temp
))
2597 sprintf(temp
, tformat
, va_arg(ap
, double));
2599 httpWrite2(client
->http
, temp
, strlen(temp
));
2602 case 'B' : /* Integer formats */
2610 if ((size_t)(width
+ 2) > sizeof(temp
))
2613 # ifdef HAVE_LONG_LONG
2615 sprintf(temp
, tformat
, va_arg(ap
, long long));
2617 # endif /* HAVE_LONG_LONG */
2619 sprintf(temp
, tformat
, va_arg(ap
, long));
2621 sprintf(temp
, tformat
, va_arg(ap
, int));
2623 httpWrite2(client
->http
, temp
, strlen(temp
));
2626 case 'p' : /* Pointer value */
2627 if ((size_t)(width
+ 2) > sizeof(temp
))
2630 sprintf(temp
, tformat
, va_arg(ap
, void *));
2632 httpWrite2(client
->http
, temp
, strlen(temp
));
2635 case 'c' : /* Character or character array */
2638 temp
[0] = (char)va_arg(ap
, int);
2640 html_escape(client
, temp
, 1);
2643 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2646 case 's' : /* String */
2647 if ((s
= va_arg(ap
, char *)) == NULL
)
2650 html_escape(client
, s
, strlen(s
));
2659 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2666 * 'ipp_cancel_job()' - Cancel a job.
2670 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2672 _ipp_job_t
*job
; /* Job information */
2679 if ((job
= find_job(client
)) == NULL
)
2681 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2686 * See if the job is already completed, canceled, or aborted; if so,
2687 * we can't cancel...
2692 case IPP_JSTATE_CANCELED
:
2693 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2694 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2697 case IPP_JSTATE_ABORTED
:
2698 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2699 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2702 case IPP_JSTATE_COMPLETED
:
2703 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2704 "Job #%d is already completed - can\'t cancel.", job
->id
);
2712 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2714 if (job
->state
== IPP_JSTATE_PROCESSING
||
2715 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2719 job
->state
= IPP_JSTATE_CANCELED
;
2720 job
->completed
= time(NULL
);
2723 _cupsRWUnlock(&(client
->printer
->rwlock
));
2725 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2732 * 'ipp_close_job()' - Close an open job.
2736 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
2738 _ipp_job_t
*job
; /* Job information */
2745 if ((job
= find_job(client
)) == NULL
)
2747 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2752 * See if the job is already completed, canceled, or aborted; if so,
2753 * we can't cancel...
2758 case IPP_JSTATE_CANCELED
:
2759 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2760 "Job #%d is canceled - can\'t close.", job
->id
);
2763 case IPP_JSTATE_ABORTED
:
2764 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2765 "Job #%d is aborted - can\'t close.", job
->id
);
2768 case IPP_JSTATE_COMPLETED
:
2769 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2770 "Job #%d is completed - can\'t close.", job
->id
);
2773 case IPP_JSTATE_PROCESSING
:
2774 case IPP_JSTATE_STOPPED
:
2775 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2776 "Job #%d is already closed.", job
->id
);
2780 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2787 * 'ipp_create_job()' - Create a job object.
2791 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2793 _ipp_job_t
*job
; /* New job */
2794 cups_array_t
*ra
; /* Attributes to send in response */
2798 * Validate print job attributes...
2801 if (!valid_job_attributes(client
))
2803 httpFlush(client
->http
);
2808 * Do we have a file to print?
2811 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2813 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2814 "Unexpected document data following request.");
2822 if ((job
= create_job(client
)) == NULL
)
2824 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2825 "Currently printing another job.");
2830 * Return the job info...
2833 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2835 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2836 cupsArrayAdd(ra
, "job-id");
2837 cupsArrayAdd(ra
, "job-state");
2838 cupsArrayAdd(ra
, "job-state-message");
2839 cupsArrayAdd(ra
, "job-state-reasons");
2840 cupsArrayAdd(ra
, "job-uri");
2842 copy_job_attributes(client
, job
, ra
);
2843 cupsArrayDelete(ra
);
2848 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2852 ipp_get_job_attributes(
2853 _ipp_client_t
*client
) /* I - Client */
2855 _ipp_job_t
*job
; /* Job */
2856 cups_array_t
*ra
; /* requested-attributes */
2859 if ((job
= find_job(client
)) == NULL
)
2861 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2865 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2867 ra
= ippCreateRequestedArray(client
->request
);
2868 copy_job_attributes(client
, job
, ra
);
2869 cupsArrayDelete(ra
);
2874 * 'ipp_get_jobs()' - Get a list of job objects.
2878 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2880 ipp_attribute_t
*attr
; /* Current attribute */
2881 const char *which_jobs
= NULL
;
2882 /* which-jobs values */
2883 int job_comparison
; /* Job comparison */
2884 ipp_jstate_t job_state
; /* job-state value */
2885 int first_job_id
, /* First job ID */
2886 limit
, /* Maximum number of jobs to return */
2887 count
; /* Number of jobs that match */
2888 const char *username
; /* Username */
2889 _ipp_job_t
*job
; /* Current job pointer */
2890 cups_array_t
*ra
; /* Requested attributes array */
2894 * See if the "which-jobs" attribute have been specified...
2897 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2898 IPP_TAG_KEYWORD
)) != NULL
)
2900 which_jobs
= ippGetString(attr
, 0, NULL
);
2901 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
2904 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
2906 job_comparison
= -1;
2907 job_state
= IPP_JSTATE_STOPPED
;
2909 else if (!strcmp(which_jobs
, "completed"))
2912 job_state
= IPP_JSTATE_CANCELED
;
2914 else if (!strcmp(which_jobs
, "aborted"))
2917 job_state
= IPP_JSTATE_ABORTED
;
2919 else if (!strcmp(which_jobs
, "all"))
2922 job_state
= IPP_JSTATE_PENDING
;
2924 else if (!strcmp(which_jobs
, "canceled"))
2927 job_state
= IPP_JSTATE_CANCELED
;
2929 else if (!strcmp(which_jobs
, "pending"))
2932 job_state
= IPP_JSTATE_PENDING
;
2934 else if (!strcmp(which_jobs
, "pending-held"))
2937 job_state
= IPP_JSTATE_HELD
;
2939 else if (!strcmp(which_jobs
, "processing"))
2942 job_state
= IPP_JSTATE_PROCESSING
;
2944 else if (!strcmp(which_jobs
, "processing-stopped"))
2947 job_state
= IPP_JSTATE_STOPPED
;
2951 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
2952 "The which-jobs value \"%s\" is not supported.", which_jobs
);
2953 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2954 "which-jobs", NULL
, which_jobs
);
2959 * See if they want to limit the number of jobs reported...
2962 if ((attr
= ippFindAttribute(client
->request
, "limit",
2963 IPP_TAG_INTEGER
)) != NULL
)
2965 limit
= ippGetInteger(attr
, 0);
2967 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
2972 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2973 IPP_TAG_INTEGER
)) != NULL
)
2975 first_job_id
= ippGetInteger(attr
, 0);
2977 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
2984 * See if we only want to see jobs for a specific user...
2989 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2990 IPP_TAG_BOOLEAN
)) != NULL
)
2992 int my_jobs
= ippGetBoolean(attr
, 0);
2994 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
2995 my_jobs
? "true" : "false");
2999 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3000 IPP_TAG_NAME
)) == NULL
)
3002 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3003 "Need requesting-user-name with my-jobs.");
3007 username
= ippGetString(attr
, 0, NULL
);
3009 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3010 client
->hostname
, username
);
3015 * OK, build a list of jobs for this printer...
3018 ra
= ippCreateRequestedArray(client
->request
);
3020 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3022 _cupsRWLockRead(&(client
->printer
->rwlock
));
3024 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3025 (limit
<= 0 || count
< limit
) && job
;
3026 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3029 * Filter out jobs that don't match...
3032 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3033 (job_comparison
== 0 && job
->state
!= job_state
) ||
3034 (job_comparison
> 0 && job
->state
< job_state
) ||
3035 job
->id
< first_job_id
||
3036 (username
&& job
->username
&&
3037 strcasecmp(username
, job
->username
)))
3041 ippAddSeparator(client
->response
);
3044 copy_job_attributes(client
, job
, ra
);
3047 cupsArrayDelete(ra
);
3049 _cupsRWUnlock(&(client
->printer
->rwlock
));
3054 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3058 ipp_get_printer_attributes(
3059 _ipp_client_t
*client
) /* I - Client */
3061 cups_array_t
*ra
; /* Requested attributes array */
3062 _ipp_printer_t
*printer
; /* Printer */
3066 * Send the attributes...
3069 ra
= ippCreateRequestedArray(client
->request
);
3070 printer
= client
->printer
;
3072 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3074 _cupsRWLockRead(&(printer
->rwlock
));
3076 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3077 IPP_TAG_CUPS_CONST
);
3079 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3081 int i
, /* Looping var */
3082 num_ready
= 0; /* Number of ready media */
3083 ipp_t
*ready
[3]; /* Ready media */
3085 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3087 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
3088 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);
3090 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);
3092 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3093 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);
3094 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3096 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
3097 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);
3099 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);
3104 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3105 for (i
= 0; i
< num_ready
; i
++)
3106 ippDelete(ready
[i
]);
3109 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3112 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3114 int num_ready
= 0; /* Number of ready media */
3115 const char *ready
[3]; /* Ready media */
3117 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3118 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3120 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3121 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3123 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3124 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3127 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3129 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3132 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3133 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3135 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3136 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3138 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3139 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3142 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3143 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3144 "printer-state", printer
->state
);
3146 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3147 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3149 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3150 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3152 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3154 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3156 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3159 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3161 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3162 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3163 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3164 "printer-state-reasons", NULL
, "none");
3167 int num_reasons
= 0;/* Number of reasons */
3168 const char *reasons
[32]; /* Reason strings */
3170 if (printer
->state_reasons
& _IPP_PREASON_OTHER
)
3171 reasons
[num_reasons
++] = "other";
3172 if (printer
->state_reasons
& _IPP_PREASON_COVER_OPEN
)
3173 reasons
[num_reasons
++] = "cover-open";
3174 if (printer
->state_reasons
& _IPP_PREASON_INPUT_TRAY_MISSING
)
3175 reasons
[num_reasons
++] = "input-tray-missing";
3176 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_EMPTY
)
3177 reasons
[num_reasons
++] = "marker-supply-empty-warning";
3178 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_LOW
)
3179 reasons
[num_reasons
++] = "marker-supply-low-report";
3180 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
)
3181 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
3182 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_FULL
)
3183 reasons
[num_reasons
++] = "marker-waste-full-warning";
3184 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
3185 reasons
[num_reasons
++] = "media-empty-warning";
3186 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_JAM
)
3187 reasons
[num_reasons
++] = "media-jam-warning";
3188 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_LOW
)
3189 reasons
[num_reasons
++] = "media-low-report";
3190 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_NEEDED
)
3191 reasons
[num_reasons
++] = "media-needed-report";
3192 if (printer
->state_reasons
& _IPP_PREASON_MOVING_TO_PAUSED
)
3193 reasons
[num_reasons
++] = "moving-to-paused";
3194 if (printer
->state_reasons
& _IPP_PREASON_PAUSED
)
3195 reasons
[num_reasons
++] = "paused";
3196 if (printer
->state_reasons
& _IPP_PREASON_SPOOL_AREA_FULL
)
3197 reasons
[num_reasons
++] = "spool-area-full";
3198 if (printer
->state_reasons
& _IPP_PREASON_TONER_EMPTY
)
3199 reasons
[num_reasons
++] = "toner-empty-warning";
3200 if (printer
->state_reasons
& _IPP_PREASON_TONER_LOW
)
3201 reasons
[num_reasons
++] = "toner-low-report";
3203 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
3204 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3205 "printer-state-reasons", num_reasons
, NULL
, reasons
);
3209 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3211 int i
; /* Looping var */
3212 char buffer
[256]; /* Supply value buffer */
3213 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3214 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3216 for (i
= 0; i
< 5; i
++)
3218 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
]);
3221 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3223 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3227 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3228 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3230 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3231 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3232 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3234 _cupsRWUnlock(&(printer
->rwlock
));
3236 cupsArrayDelete(ra
);
3241 * 'ipp_identify_printer()' - Beep or display a message.
3245 ipp_identify_printer(
3246 _ipp_client_t
*client
) /* I - Client */
3248 /* TODO: Do something */
3250 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3255 * 'ipp_print_job()' - Create a job object with an attached document.
3259 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3261 _ipp_job_t
*job
; /* New job */
3262 char filename
[1024], /* Filename buffer */
3263 buffer
[4096]; /* Copy buffer */
3264 ssize_t bytes
; /* Bytes read */
3265 cups_array_t
*ra
; /* Attributes to send in response */
3269 * Validate print job attributes...
3272 if (!valid_job_attributes(client
))
3274 httpFlush(client
->http
);
3279 * Do we have a file to print?
3282 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3284 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3292 if ((job
= create_job(client
)) == NULL
)
3294 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3295 "Currently printing another job.");
3300 * Create a file for the request data...
3303 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3306 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3308 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3310 job
->state
= IPP_JSTATE_ABORTED
;
3312 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3313 "Unable to create print file: %s", strerror(errno
));
3317 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3319 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3321 int error
= errno
; /* Write error */
3323 job
->state
= IPP_JSTATE_ABORTED
;
3330 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3331 "Unable to write print file: %s", strerror(error
));
3339 * Got an error while reading the print data, so abort this job.
3342 job
->state
= IPP_JSTATE_ABORTED
;
3349 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3350 "Unable to read print file.");
3356 int error
= errno
; /* Write error */
3358 job
->state
= IPP_JSTATE_ABORTED
;
3363 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3364 "Unable to write print file: %s", strerror(error
));
3369 job
->filename
= strdup(filename
);
3370 job
->state
= IPP_JSTATE_PENDING
;
3373 * Process the job...
3376 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3378 job
->state
= IPP_JSTATE_ABORTED
;
3379 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3384 * Return the job info...
3387 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3389 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3390 cupsArrayAdd(ra
, "job-id");
3391 cupsArrayAdd(ra
, "job-state");
3392 cupsArrayAdd(ra
, "job-state-message");
3393 cupsArrayAdd(ra
, "job-state-reasons");
3394 cupsArrayAdd(ra
, "job-uri");
3396 copy_job_attributes(client
, job
, ra
);
3397 cupsArrayDelete(ra
);
3402 * 'ipp_print_uri()' - Create a job object with a referenced document.
3406 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3408 _ipp_job_t
*job
; /* New job */
3409 ipp_attribute_t
*uri
; /* document-uri */
3410 char scheme
[256], /* URI scheme */
3411 userpass
[256], /* Username and password info */
3412 hostname
[256], /* Hostname */
3413 resource
[1024]; /* Resource path */
3414 int port
; /* Port number */
3415 http_uri_status_t uri_status
; /* URI decode status */
3416 http_encryption_t encryption
; /* Encryption to use, if any */
3417 http_t
*http
; /* Connection for http/https URIs */
3418 http_status_t status
; /* Access status for http/https URIs */
3419 int infile
; /* Input file for local file URIs */
3420 char filename
[1024], /* Filename buffer */
3421 buffer
[4096]; /* Copy buffer */
3422 ssize_t bytes
; /* Bytes read */
3423 cups_array_t
*ra
; /* Attributes to send in response */
3424 static const char * const uri_status_strings
[] =
3425 { /* URI decode errors */
3427 "Bad arguments to function.",
3428 "Bad resource in URI.",
3429 "Bad port number in URI.",
3430 "Bad hostname in URI.",
3431 "Bad username in URI.",
3432 "Bad scheme in URI.",
3438 * Validate print job attributes...
3441 if (!valid_job_attributes(client
))
3443 httpFlush(client
->http
);
3448 * Do we have a file to print?
3451 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3453 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3454 "Unexpected document data following request.");
3459 * Do we have a document URI?
3462 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3463 IPP_TAG_URI
)) == NULL
)
3465 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3469 if (ippGetCount(uri
) != 1)
3471 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3472 "Too many document-uri values.");
3476 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3477 scheme
, sizeof(scheme
), userpass
,
3478 sizeof(userpass
), hostname
, sizeof(hostname
),
3479 &port
, resource
, sizeof(resource
));
3480 if (uri_status
< HTTP_URI_STATUS_OK
)
3482 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3483 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3487 if (strcmp(scheme
, "file") &&
3489 strcmp(scheme
, "https") &&
3490 #endif /* HAVE_SSL */
3491 strcmp(scheme
, "http"))
3493 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3494 "URI scheme \"%s\" not supported.", scheme
);
3498 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3500 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3501 "Unable to access URI: %s", strerror(errno
));
3509 if ((job
= create_job(client
)) == NULL
)
3511 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3512 "Currently printing another job.");
3517 * Create a file for the request data...
3520 if (!strcasecmp(job
->format
, "image/jpeg"))
3521 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3522 client
->printer
->directory
, job
->id
);
3523 else if (!strcasecmp(job
->format
, "image/png"))
3524 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3525 client
->printer
->directory
, job
->id
);
3526 else if (!strcasecmp(job
->format
, "application/pdf"))
3527 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3528 client
->printer
->directory
, job
->id
);
3529 else if (!strcasecmp(job
->format
, "application/postscript"))
3530 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3531 client
->printer
->directory
, job
->id
);
3533 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3534 client
->printer
->directory
, job
->id
);
3536 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3538 job
->state
= IPP_JSTATE_ABORTED
;
3540 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3541 "Unable to create print file: %s", strerror(errno
));
3545 if (!strcmp(scheme
, "file"))
3547 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3549 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3550 "Unable to access URI: %s", strerror(errno
));
3556 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3557 (errno
== EAGAIN
|| errno
== EINTR
))
3559 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3561 int error
= errno
; /* Write error */
3563 job
->state
= IPP_JSTATE_ABORTED
;
3571 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3572 "Unable to write print file: %s", strerror(error
));
3583 if (port
== 443 || !strcmp(scheme
, "https"))
3584 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3586 #endif /* HAVE_SSL */
3587 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3589 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3590 1, 30000, NULL
)) == NULL
)
3592 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3593 "Unable to connect to %s: %s", hostname
,
3594 cupsLastErrorString());
3595 job
->state
= IPP_JSTATE_ABORTED
;
3604 httpClearFields(http
);
3605 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3606 if (httpGet(http
, resource
))
3608 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3609 "Unable to GET URI: %s", strerror(errno
));
3611 job
->state
= IPP_JSTATE_ABORTED
;
3621 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3623 if (status
!= HTTP_STATUS_OK
)
3625 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3626 "Unable to GET URI: %s", httpStatus(status
));
3628 job
->state
= IPP_JSTATE_ABORTED
;
3638 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3640 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3642 int error
= errno
; /* Write error */
3644 job
->state
= IPP_JSTATE_ABORTED
;
3652 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3653 "Unable to write print file: %s", strerror(error
));
3663 int error
= errno
; /* Write error */
3665 job
->state
= IPP_JSTATE_ABORTED
;
3670 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3671 "Unable to write print file: %s", strerror(error
));
3676 job
->filename
= strdup(filename
);
3677 job
->state
= IPP_JSTATE_PENDING
;
3680 * Process the job...
3684 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3686 job
->state
= IPP_JSTATE_ABORTED
;
3687 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3696 * Return the job info...
3699 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3701 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3702 cupsArrayAdd(ra
, "job-id");
3703 cupsArrayAdd(ra
, "job-state");
3704 cupsArrayAdd(ra
, "job-state-reasons");
3705 cupsArrayAdd(ra
, "job-uri");
3707 copy_job_attributes(client
, job
, ra
);
3708 cupsArrayDelete(ra
);
3713 * 'ipp_send_document()' - Add an attached document to a job object created with
3718 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3720 _ipp_job_t
*job
; /* Job information */
3721 char filename
[1024], /* Filename buffer */
3722 buffer
[4096]; /* Copy buffer */
3723 ssize_t bytes
; /* Bytes read */
3724 ipp_attribute_t
*attr
; /* Current attribute */
3725 cups_array_t
*ra
; /* Attributes to send in response */
3732 if ((job
= find_job(client
)) == NULL
)
3734 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3735 httpFlush(client
->http
);
3740 * See if we already have a document for this job or the job has already
3741 * in a non-pending state...
3744 if (job
->state
> IPP_JSTATE_HELD
)
3746 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3747 "Job is not in a pending state.");
3748 httpFlush(client
->http
);
3751 else if (job
->filename
|| job
->fd
>= 0)
3753 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3754 "Multiple document jobs are not supported.");
3755 httpFlush(client
->http
);
3759 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3760 IPP_TAG_ZERO
)) == NULL
)
3762 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3763 "Missing required last-document attribute.");
3764 httpFlush(client
->http
);
3767 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3768 !ippGetBoolean(attr
, 0))
3770 respond_unsupported(client
, attr
);
3771 httpFlush(client
->http
);
3776 * Validate document attributes...
3779 if (!valid_doc_attributes(client
))
3781 httpFlush(client
->http
);
3785 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3788 * Get the document format for the job...
3791 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3793 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3794 job
->format
= ippGetString(attr
, 0, NULL
);
3795 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3796 job
->format
= ippGetString(attr
, 0, NULL
);
3798 job
->format
= "application/octet-stream";
3801 * Create a file for the request data...
3804 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3807 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3809 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3811 _cupsRWUnlock(&(client
->printer
->rwlock
));
3815 job
->state
= IPP_JSTATE_ABORTED
;
3817 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3818 "Unable to create print file: %s", strerror(errno
));
3822 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3824 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3826 int error
= errno
; /* Write error */
3828 job
->state
= IPP_JSTATE_ABORTED
;
3835 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3836 "Unable to write print file: %s", strerror(error
));
3844 * Got an error while reading the print data, so abort this job.
3847 job
->state
= IPP_JSTATE_ABORTED
;
3854 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3855 "Unable to read print file.");
3861 int error
= errno
; /* Write error */
3863 job
->state
= IPP_JSTATE_ABORTED
;
3868 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3869 "Unable to write print file: %s", strerror(error
));
3873 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3876 job
->filename
= strdup(filename
);
3877 job
->state
= IPP_JSTATE_PENDING
;
3879 _cupsRWUnlock(&(client
->printer
->rwlock
));
3882 * Process the job...
3886 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3888 job
->state
= IPP_JSTATE_ABORTED
;
3889 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3898 * Return the job info...
3901 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3903 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3904 cupsArrayAdd(ra
, "job-id");
3905 cupsArrayAdd(ra
, "job-state");
3906 cupsArrayAdd(ra
, "job-state-reasons");
3907 cupsArrayAdd(ra
, "job-uri");
3909 copy_job_attributes(client
, job
, ra
);
3910 cupsArrayDelete(ra
);
3915 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3920 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
3922 _ipp_job_t
*job
; /* Job information */
3923 ipp_attribute_t
*uri
; /* document-uri */
3924 char scheme
[256], /* URI scheme */
3925 userpass
[256], /* Username and password info */
3926 hostname
[256], /* Hostname */
3927 resource
[1024]; /* Resource path */
3928 int port
; /* Port number */
3929 http_uri_status_t uri_status
; /* URI decode status */
3930 http_encryption_t encryption
; /* Encryption to use, if any */
3931 http_t
*http
; /* Connection for http/https URIs */
3932 http_status_t status
; /* Access status for http/https URIs */
3933 int infile
; /* Input file for local file URIs */
3934 char filename
[1024], /* Filename buffer */
3935 buffer
[4096]; /* Copy buffer */
3936 ssize_t bytes
; /* Bytes read */
3937 ipp_attribute_t
*attr
; /* Current attribute */
3938 cups_array_t
*ra
; /* Attributes to send in response */
3939 static const char * const uri_status_strings
[] =
3940 { /* URI decode errors */
3942 "Bad arguments to function.",
3943 "Bad resource in URI.",
3944 "Bad port number in URI.",
3945 "Bad hostname in URI.",
3946 "Bad username in URI.",
3947 "Bad scheme in URI.",
3956 if ((job
= find_job(client
)) == NULL
)
3958 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3959 httpFlush(client
->http
);
3964 * See if we already have a document for this job or the job has already
3965 * in a non-pending state...
3968 if (job
->state
> IPP_JSTATE_HELD
)
3970 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3971 "Job is not in a pending state.");
3972 httpFlush(client
->http
);
3975 else if (job
->filename
|| job
->fd
>= 0)
3977 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3978 "Multiple document jobs are not supported.");
3979 httpFlush(client
->http
);
3983 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3984 IPP_TAG_ZERO
)) == NULL
)
3986 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3987 "Missing required last-document attribute.");
3988 httpFlush(client
->http
);
3991 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3992 !ippGetBoolean(attr
, 0))
3994 respond_unsupported(client
, attr
);
3995 httpFlush(client
->http
);
4000 * Validate document attributes...
4003 if (!valid_doc_attributes(client
))
4005 httpFlush(client
->http
);
4010 * Do we have a file to print?
4013 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4015 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4016 "Unexpected document data following request.");
4021 * Do we have a document URI?
4024 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4025 IPP_TAG_URI
)) == NULL
)
4027 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4031 if (ippGetCount(uri
) != 1)
4033 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4034 "Too many document-uri values.");
4038 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4039 scheme
, sizeof(scheme
), userpass
,
4040 sizeof(userpass
), hostname
, sizeof(hostname
),
4041 &port
, resource
, sizeof(resource
));
4042 if (uri_status
< HTTP_URI_STATUS_OK
)
4044 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4045 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4049 if (strcmp(scheme
, "file") &&
4051 strcmp(scheme
, "https") &&
4052 #endif /* HAVE_SSL */
4053 strcmp(scheme
, "http"))
4055 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4056 "URI scheme \"%s\" not supported.", scheme
);
4060 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4062 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4063 "Unable to access URI: %s", strerror(errno
));
4068 * Get the document format for the job...
4071 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4073 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4074 IPP_TAG_MIMETYPE
)) != NULL
)
4075 job
->format
= ippGetString(attr
, 0, NULL
);
4077 job
->format
= "application/octet-stream";
4080 * Create a file for the request data...
4083 if (!strcasecmp(job
->format
, "image/jpeg"))
4084 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4085 client
->printer
->directory
, job
->id
);
4086 else if (!strcasecmp(job
->format
, "image/png"))
4087 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4088 client
->printer
->directory
, job
->id
);
4089 else if (!strcasecmp(job
->format
, "application/pdf"))
4090 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4091 client
->printer
->directory
, job
->id
);
4092 else if (!strcasecmp(job
->format
, "application/postscript"))
4093 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4094 client
->printer
->directory
, job
->id
);
4096 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4097 client
->printer
->directory
, job
->id
);
4099 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4101 _cupsRWUnlock(&(client
->printer
->rwlock
));
4105 job
->state
= IPP_JSTATE_ABORTED
;
4107 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4108 "Unable to create print file: %s", strerror(errno
));
4112 if (!strcmp(scheme
, "file"))
4114 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4116 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4117 "Unable to access URI: %s", strerror(errno
));
4123 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4124 (errno
== EAGAIN
|| errno
== EINTR
))
4126 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4128 int error
= errno
; /* Write error */
4130 job
->state
= IPP_JSTATE_ABORTED
;
4138 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4139 "Unable to write print file: %s", strerror(error
));
4150 if (port
== 443 || !strcmp(scheme
, "https"))
4151 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4153 #endif /* HAVE_SSL */
4154 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4156 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4157 1, 30000, NULL
)) == NULL
)
4159 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4160 "Unable to connect to %s: %s", hostname
,
4161 cupsLastErrorString());
4162 job
->state
= IPP_JSTATE_ABORTED
;
4171 httpClearFields(http
);
4172 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4173 if (httpGet(http
, resource
))
4175 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4176 "Unable to GET URI: %s", strerror(errno
));
4178 job
->state
= IPP_JSTATE_ABORTED
;
4188 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4190 if (status
!= HTTP_STATUS_OK
)
4192 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4193 "Unable to GET URI: %s", httpStatus(status
));
4195 job
->state
= IPP_JSTATE_ABORTED
;
4205 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4207 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4209 int error
= errno
; /* Write error */
4211 job
->state
= IPP_JSTATE_ABORTED
;
4219 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4220 "Unable to write print file: %s", strerror(error
));
4230 int error
= errno
; /* Write error */
4232 job
->state
= IPP_JSTATE_ABORTED
;
4237 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4238 "Unable to write print file: %s", strerror(error
));
4242 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4245 job
->filename
= strdup(filename
);
4246 job
->state
= IPP_JSTATE_PENDING
;
4248 _cupsRWUnlock(&(client
->printer
->rwlock
));
4251 * Process the job...
4255 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4257 job
->state
= IPP_JSTATE_ABORTED
;
4258 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4267 * Return the job info...
4270 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4272 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4273 cupsArrayAdd(ra
, "job-id");
4274 cupsArrayAdd(ra
, "job-state");
4275 cupsArrayAdd(ra
, "job-state-reasons");
4276 cupsArrayAdd(ra
, "job-uri");
4278 copy_job_attributes(client
, job
, ra
);
4279 cupsArrayDelete(ra
);
4284 * 'ipp_validate_job()' - Validate job creation attributes.
4288 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4290 if (valid_job_attributes(client
))
4291 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4296 * 'parse_options()' - Parse URL options into CUPS options.
4298 * The client->options string is destroyed by this function.
4301 static int /* O - Number of options */
4302 parse_options(_ipp_client_t
*client
, /* I - Client */
4303 cups_option_t
**options
) /* O - Options */
4305 char *name
, /* Name */
4307 *next
; /* Next name=value pair */
4308 int num_options
= 0; /* Number of options */
4313 for (name
= client
->options
; name
&& *name
; name
= next
)
4315 if ((value
= strchr(name
, '=')) == NULL
)
4319 if ((next
= strchr(value
, '&')) != NULL
)
4322 num_options
= cupsAddOption(name
, value
, num_options
, options
);
4325 return (num_options
);
4330 * 'process_client()' - Process client requests on a thread.
4333 static void * /* O - Exit status */
4334 process_client(_ipp_client_t
*client
) /* I - Client */
4337 * Loop until we are out of requests or timeout (30 seconds)...
4341 int first_time
= 1; /* First time request? */
4342 #endif /* HAVE_SSL */
4344 while (httpWait(client
->http
, 30000))
4350 * See if we need to negotiate a TLS connection...
4353 char buf
[1]; /* First byte from client */
4355 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
4357 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
4359 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
4361 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4365 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4370 #endif /* HAVE_SSL */
4372 if (!process_http(client
))
4377 * Close the conection to the client and return...
4380 delete_client(client
);
4387 * 'process_http()' - Process a HTTP request.
4390 int /* O - 1 on success, 0 on failure */
4391 process_http(_ipp_client_t
*client
) /* I - Client connection */
4393 char uri
[1024]; /* URI */
4394 http_state_t http_state
; /* HTTP state */
4395 http_status_t http_status
; /* HTTP status */
4396 ipp_state_t ipp_state
; /* State of IPP transfer */
4397 char scheme
[32], /* Method/scheme */
4398 userpass
[128], /* Username:password */
4399 hostname
[HTTP_MAX_HOST
];
4401 int port
; /* Port number */
4402 const char *encoding
; /* Content-Encoding value */
4403 static const char * const http_states
[] =
4404 { /* Strings for logging HTTP method */
4425 * Clear state variables...
4428 ippDelete(client
->request
);
4429 ippDelete(client
->response
);
4431 client
->request
= NULL
;
4432 client
->response
= NULL
;
4433 client
->operation
= HTTP_STATE_WAITING
;
4436 * Read a request from the connection...
4439 while ((http_state
= httpReadRequest(client
->http
, uri
,
4440 sizeof(uri
))) == HTTP_STATE_WAITING
)
4444 * Parse the request line...
4447 if (http_state
== HTTP_STATE_ERROR
)
4449 if (httpError(client
->http
) == EPIPE
)
4450 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
4452 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
4453 strerror(httpError(client
->http
)));
4457 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
4459 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
4460 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4463 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
4465 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
4466 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4470 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
4474 * Separate the URI into its components...
4477 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4478 userpass
, sizeof(userpass
),
4479 hostname
, sizeof(hostname
), &port
,
4480 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
4481 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
4483 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
4484 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4488 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
4489 *(client
->options
)++ = '\0';
4492 * Process the request...
4495 client
->start
= time(NULL
);
4496 client
->operation
= httpGetState(client
->http
);
4499 * Parse incoming parameters until the status changes...
4502 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4504 if (http_status
!= HTTP_STATUS_OK
)
4506 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4510 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4511 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4514 * HTTP/1.1 and higher require the "Host:" field...
4517 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4522 * Handle HTTP Upgrade...
4525 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4529 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
4531 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
4534 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
4536 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
4538 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4542 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4545 #endif /* HAVE_SSL */
4547 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4552 * Handle HTTP Expect...
4555 if (httpGetExpect(client
->http
) &&
4556 (client
->operation
== HTTP_STATE_POST
||
4557 client
->operation
== HTTP_STATE_PUT
))
4559 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4562 * Send 100-continue header...
4565 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4571 * Send 417-expectation-failed header...
4574 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4580 * Handle new transfers...
4583 encoding
= httpGetContentEncoding(client
->http
);
4585 switch (client
->operation
)
4587 case HTTP_STATE_OPTIONS
:
4589 * Do OPTIONS command...
4592 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4594 case HTTP_STATE_HEAD
:
4595 if (!strcmp(client
->uri
, "/icon.png"))
4596 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4597 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
4598 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4600 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4602 case HTTP_STATE_GET
:
4603 if (!strcmp(client
->uri
, "/icon.png"))
4606 * Send PNG icon file.
4609 int fd
; /* Icon file */
4610 struct stat fileinfo
; /* Icon file information */
4611 char buffer
[4096]; /* Copy buffer */
4612 ssize_t bytes
; /* Bytes */
4614 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
4616 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4617 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4619 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
4620 (size_t)fileinfo
.st_size
))
4626 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4627 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
4629 httpFlushWrite(client
->http
);
4634 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4636 else if (!strcmp(client
->uri
, "/"))
4639 * Show web status page...
4642 _ipp_job_t
*job
; /* Current job */
4643 int i
; /* Looping var */
4644 _ipp_preason_t reason
; /* Current reason */
4645 static const char * const reasons
[] =
4646 { /* Reason strings */
4649 "Input Tray Missing",
4650 "Marker Supply Empty",
4651 "Marker Supply Low",
4652 "Marker Waste Almost Full",
4653 "Marker Waste Full",
4665 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4668 html_header(client
, client
->printer
->name
);
4670 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
4671 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
4672 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
4673 if (client
->printer
->state_reasons
& reason
)
4674 html_printf(client
, "\n<br> %s", reasons
[i
]);
4675 html_printf(client
, "</p>\n");
4677 if (cupsArrayCount(client
->printer
->jobs
) > 0)
4679 _cupsRWLockRead(&(client
->printer
->rwlock
));
4681 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");
4682 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
4684 char when
[256], /* When job queued/started/finished */
4685 hhmmss
[64]; /* Time HH:MM:SS */
4689 case IPP_JSTATE_PENDING
:
4690 case IPP_JSTATE_HELD
:
4691 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
4693 case IPP_JSTATE_PROCESSING
:
4694 case IPP_JSTATE_STOPPED
:
4695 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
4697 case IPP_JSTATE_ABORTED
:
4698 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4700 case IPP_JSTATE_CANCELED
:
4701 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4703 case IPP_JSTATE_COMPLETED
:
4704 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4708 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
);
4710 html_printf(client
, "</tbody></table>\n");
4712 _cupsRWUnlock(&(client
->printer
->rwlock
));
4714 html_footer(client
);
4718 else if (!strcmp(client
->uri
, "/media"))
4721 * Show web media page...
4724 int i
, /* Looping var */
4725 num_options
; /* Number of form options */
4726 cups_option_t
*options
; /* Form options */
4727 static const char * const sizes
[] =
4728 { /* Size strings */
4741 static const char * const types
[] =
4758 static const int sheets
[] = /* Number of sheets */
4767 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4770 html_header(client
, client
->printer
->name
);
4772 if ((num_options
= parse_options(client
, &options
)) > 0)
4775 * WARNING: A real printer/server implementation MUST NOT implement
4776 * media updates via a GET request - GET requests are supposed to be
4777 * idempotent (without side-effects) and we obviously are not
4778 * authenticating access here. This form is provided solely to
4779 * enable testing and development!
4782 const char *val
; /* Form value */
4784 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
4785 client
->printer
->main_size
= atoi(val
);
4786 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
4787 client
->printer
->main_type
= atoi(val
);
4788 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
4789 client
->printer
->main_level
= atoi(val
);
4791 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
4792 client
->printer
->envelope_size
= atoi(val
);
4793 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
4794 client
->printer
->envelope_level
= atoi(val
);
4796 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
4797 client
->printer
->photo_size
= atoi(val
);
4798 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
4799 client
->printer
->photo_type
= atoi(val
);
4800 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
4801 client
->printer
->photo_level
= atoi(val
);
4803 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))
4804 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
4806 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
4808 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
))
4810 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
4811 if (client
->printer
->active_job
)
4812 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
4815 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
4817 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
4820 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
4822 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
4823 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
4824 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4825 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
4826 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
4827 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
4828 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4829 if (!strstr(types
[i
], "Photo"))
4830 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
4831 html_printf(client
, "</select> <select name=\"main_level\">");
4832 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4833 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
4834 html_printf(client
, "</select></td></tr>\n");
4837 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
4838 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4839 if (strstr(sizes
[i
], "Envelope"))
4840 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
4841 html_printf(client
, "</select> <select name=\"envelope_level\">");
4842 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4843 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
4844 html_printf(client
, "</select></td></tr>\n");
4847 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
4848 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4849 if (strstr(sizes
[i
], "Photo"))
4850 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
4851 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
4852 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4853 if (strstr(types
[i
], "Photo"))
4854 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
4855 html_printf(client
, "</select> <select name=\"photo_level\">");
4856 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4857 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
4858 html_printf(client
, "</select></td></tr>\n");
4860 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
4861 html_footer(client
);
4865 else if (!strcmp(client
->uri
, "/supplies"))
4868 * Show web supplies page...
4871 int i
, j
, /* Looping vars */
4872 num_options
; /* Number of form options */
4873 cups_option_t
*options
; /* Form options */
4874 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
4876 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4879 html_header(client
, client
->printer
->name
);
4881 if ((num_options
= parse_options(client
, &options
)) > 0)
4884 * WARNING: A real printer/server implementation MUST NOT implement
4885 * supply updates via a GET request - GET requests are supposed to be
4886 * idempotent (without side-effects) and we obviously are not
4887 * authenticating access here. This form is provided solely to
4888 * enable testing and development!
4891 char name
[64]; /* Form field */
4892 const char *val
; /* Form value */
4894 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
);
4896 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
4898 snprintf(name
, sizeof(name
), "supply_%d", i
);
4899 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
4901 int level
= client
->printer
->supplies
[i
] = atoi(val
);
4907 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
4908 else if (level
< 10)
4909 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
4914 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
4915 else if (level
> 90)
4916 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
4921 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
4924 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
4926 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
4927 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
4929 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
4930 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
4931 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
4932 html_printf(client
, "</select></td></tr>\n");
4934 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
4935 html_footer(client
);
4940 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4943 case HTTP_STATE_POST
:
4944 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
4948 * Not an IPP request...
4951 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
4955 * Read the IPP request...
4958 client
->request
= ippNew();
4960 while ((ipp_state
= ippRead(client
->http
,
4961 client
->request
)) != IPP_STATE_DATA
)
4963 if (ipp_state
== IPP_STATE_ERROR
)
4965 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
4966 cupsLastErrorString());
4967 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4973 * Now that we have the IPP request, process the request...
4976 return (process_ipp(client
));
4979 break; /* Anti-compiler-warning-code */
4987 * 'process_ipp()' - Process an IPP request.
4990 static int /* O - 1 on success, 0 on error */
4991 process_ipp(_ipp_client_t
*client
) /* I - Client */
4993 ipp_tag_t group
; /* Current group tag */
4994 ipp_attribute_t
*attr
; /* Current attribute */
4995 ipp_attribute_t
*charset
; /* Character set attribute */
4996 ipp_attribute_t
*language
; /* Language attribute */
4997 ipp_attribute_t
*uri
; /* Printer URI attribute */
4998 int major
, minor
; /* Version number */
4999 const char *name
; /* Name of attribute */
5002 debug_attributes("Request", client
->request
, 1);
5005 * First build an empty response message for this request...
5008 client
->operation_id
= ippGetOperation(client
->request
);
5009 client
->response
= ippNewResponse(client
->request
);
5012 * Then validate the request header and required attributes...
5015 major
= ippGetVersion(client
->request
, &minor
);
5017 if (major
< 1 || major
> 2)
5020 * Return an error, since we only support IPP 1.x and 2.x.
5023 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
5024 "Bad request version number %d.%d.", major
, minor
);
5026 else if (ippGetRequestId(client
->request
) <= 0)
5027 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
5028 ippGetRequestId(client
->request
));
5029 else if (!ippFirstAttribute(client
->request
))
5030 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5031 "No attributes in request.");
5035 * Make sure that the attributes are provided in the correct order and
5036 * don't repeat groups...
5039 for (attr
= ippFirstAttribute(client
->request
),
5040 group
= ippGetGroupTag(attr
);
5042 attr
= ippNextAttribute(client
->request
))
5044 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5047 * Out of order; return an error...
5050 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5051 "Attribute groups are out of order (%x < %x).",
5052 ippGetGroupTag(attr
), group
);
5056 group
= ippGetGroupTag(attr
);
5062 * Then make sure that the first three attributes are:
5064 * attributes-charset
5065 * attributes-natural-language
5066 * printer-uri/job-uri
5069 attr
= ippFirstAttribute(client
->request
);
5070 name
= ippGetName(attr
);
5071 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5072 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5077 attr
= ippNextAttribute(client
->request
);
5078 name
= ippGetName(attr
);
5080 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5081 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5086 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5087 IPP_TAG_URI
)) != NULL
)
5089 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5090 IPP_TAG_URI
)) != NULL
)
5096 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5097 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5100 * Bad character set...
5103 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5104 "Unsupported character set \"%s\".",
5105 ippGetString(charset
, 0, NULL
));
5107 else if (!charset
|| !language
|| !uri
)
5110 * Return an error, since attributes-charset,
5111 * attributes-natural-language, and printer-uri/job-uri are required
5112 * for all operations.
5115 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5116 "Missing required attributes.");
5120 char scheme
[32], /* URI scheme */
5121 userpass
[32], /* Username/password in URI */
5122 host
[256], /* Host name in URI */
5123 resource
[256]; /* Resource path in URI */
5124 int port
; /* Port number in URI */
5126 name
= ippGetName(uri
);
5128 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5129 scheme
, sizeof(scheme
),
5130 userpass
, sizeof(userpass
),
5131 host
, sizeof(host
), &port
,
5132 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5133 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5134 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5135 else if ((!strcmp(name
, "job-uri") &&
5136 strncmp(resource
, "/ipp/print/", 11)) ||
5137 (!strcmp(name
, "printer-uri") &&
5138 strcmp(resource
, "/ipp/print")))
5139 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5140 name
, ippGetString(uri
, 0, NULL
));
5144 * Try processing the operation...
5147 switch (ippGetOperation(client
->request
))
5149 case IPP_OP_PRINT_JOB
:
5150 ipp_print_job(client
);
5153 case IPP_OP_PRINT_URI
:
5154 ipp_print_uri(client
);
5157 case IPP_OP_VALIDATE_JOB
:
5158 ipp_validate_job(client
);
5161 case IPP_OP_CREATE_JOB
:
5162 ipp_create_job(client
);
5165 case IPP_OP_SEND_DOCUMENT
:
5166 ipp_send_document(client
);
5169 case IPP_OP_SEND_URI
:
5170 ipp_send_uri(client
);
5173 case IPP_OP_CANCEL_JOB
:
5174 ipp_cancel_job(client
);
5177 case IPP_OP_GET_JOB_ATTRIBUTES
:
5178 ipp_get_job_attributes(client
);
5181 case IPP_OP_GET_JOBS
:
5182 ipp_get_jobs(client
);
5185 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5186 ipp_get_printer_attributes(client
);
5189 case IPP_OP_CLOSE_JOB
:
5190 ipp_close_job(client
);
5193 case IPP_OP_IDENTIFY_PRINTER
:
5194 ipp_identify_printer(client
);
5198 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5199 "Operation not supported.");
5208 * Send the HTTP header and return...
5211 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5212 httpFlush(client
->http
); /* Flush trailing (junk) data */
5214 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5215 ippLength(client
->response
)));
5220 * 'process_job()' - Process a print job.
5223 static void * /* O - Thread exit status */
5224 process_job(_ipp_job_t
*job
) /* I - Job */
5226 job
->state
= IPP_JSTATE_PROCESSING
;
5227 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
5228 job
->processing
= time(NULL
);
5230 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
5232 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5237 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
5239 if (job
->printer
->command
)
5242 * Execute a command with the job spool file and wait for it to complete...
5245 int pid
, /* Process ID */
5246 status
; /* Exit status */
5247 time_t start
, /* Start time */
5249 char *myargv
[3], /* Command-line arguments */
5250 *myenvp
[200]; /* Environment variables */
5251 int myenvc
; /* Number of environment variables */
5252 ipp_attribute_t
*attr
; /* Job attribute */
5253 char val
[1280], /* IPP_NAME=value */
5254 *valptr
; /* Pointer into string */
5256 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
5261 * Setup the command-line arguments...
5264 myargv
[0] = job
->printer
->command
;
5265 myargv
[1] = job
->filename
;
5269 * Copy the current environment, then add ENV variables for every Job
5273 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
5274 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
5276 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
5279 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
5280 * value(s) from the attribute.
5283 const char *name
= ippGetName(attr
);
5292 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
5297 *valptr
++ = (char)toupper(*name
& 255);
5302 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
5304 myenvp
[myenvc
++] = strdup(val
);
5306 myenvp
[myenvc
] = NULL
;
5309 * Now run the program...
5313 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
5315 if ((pid
= fork()) == 0)
5318 * Child comes here...
5321 execve(job
->printer
->command
, myargv
, myenvp
);
5327 * Unable to fork process...
5330 perror("Unable to start job processing command");
5336 * Free memory used for environment...
5340 free(myenvp
[-- myenvc
]);
5343 * Wait for child to complete...
5346 # ifdef HAVE_WAITPID
5347 while (waitpid(pid
, &status
, 0) < 0);
5349 while (wait(&status
) < 0);
5350 # endif /* HAVE_WAITPID */
5357 if (WIFEXITED(status
))
5359 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
5360 job
->printer
->command
, WEXITSTATUS(status
));
5363 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
5364 job
->printer
->command
, WTERMSIG(status
));
5366 job
->state
= IPP_JSTATE_ABORTED
;
5368 else if (status
< 0)
5369 job
->state
= IPP_JSTATE_ABORTED
;
5371 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
5372 job
->printer
->command
);
5375 * Make sure processing takes at least 5 seconds...
5379 if ((end
- start
) < 5)
5385 * Sleep for a random amount of time to simulate job processing.
5388 sleep((unsigned)(5 + (rand() % 11)));
5392 job
->state
= IPP_JSTATE_CANCELED
;
5393 else if (job
->state
== IPP_JSTATE_PROCESSING
)
5394 job
->state
= IPP_JSTATE_COMPLETED
;
5396 job
->completed
= time(NULL
);
5397 job
->printer
->state
= IPP_PSTATE_IDLE
;
5398 job
->printer
->active_job
= NULL
;
5406 * 'register_printer()' - Register a printer object via Bonjour.
5409 static int /* O - 1 on success, 0 on error */
5411 _ipp_printer_t
*printer
, /* I - Printer */
5412 const char *location
, /* I - Location */
5413 const char *make
, /* I - Manufacturer */
5414 const char *model
, /* I - Model name */
5415 const char *formats
, /* I - Supported formats */
5416 const char *adminurl
, /* I - Web interface URL */
5417 const char *uuid
, /* I - Printer UUID */
5418 int color
, /* I - 1 = color, 0 = monochrome */
5419 int duplex
, /* I - 1 = duplex, 0 = simplex */
5420 const char *subtype
) /* I - Service subtype */
5422 DNSServiceErrorType error
; /* Error from Bonjour */
5423 char make_model
[256],/* Make and model together */
5424 product
[256], /* Product string */
5425 regtype
[256]; /* Bonjour service type */
5429 * Build the TXT record for IPP...
5432 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
5433 snprintf(product
, sizeof(product
), "(%s)", model
);
5435 TXTRecordCreate(&(printer
->ipp_txt
), 1024, NULL
);
5436 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 9, "ipp/print");
5437 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
5439 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
5442 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
5444 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
5446 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
5448 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
5449 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
5450 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
5452 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
5454 TXTRecordSetValue(&(printer
->ipp_txt
), "UUID", (uint8_t)strlen(uuid
), uuid
);
5456 TXTRecordSetValue(&(printer
->ipp_txt
), "TLS", 3, "1.2");
5457 # endif /* HAVE_SSL */
5460 * Create a shared service reference for Bonjour...
5463 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
5464 != kDNSServiceErr_NoError
)
5466 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
5471 * Register the _printer._tcp (LPD) service type with a port number of 0 to
5472 * defend our service name but not actually support LPD...
5475 printer
->printer_ref
= printer
->common_ref
;
5477 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
5478 kDNSServiceFlagsShareConnection
,
5479 0 /* interfaceIndex */, printer
->dnssd_name
,
5480 "_printer._tcp", NULL
/* domain */,
5481 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
5482 NULL
/* txtRecord */,
5483 (DNSServiceRegisterReply
)dnssd_callback
,
5484 printer
)) != kDNSServiceErr_NoError
)
5486 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
5487 printer
->dnssd_name
, error
);
5492 * Then register the _ipp._tcp (IPP) service type with the real port number to
5493 * advertise our IPP printer...
5496 printer
->ipp_ref
= printer
->common_ref
;
5498 if (subtype
&& *subtype
)
5499 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
5502 strncpy(regtype
, "_ipp._tcp", sizeof(regtype
) - 1);
5503 regtype
[sizeof(regtype
) - 1] = '\0';
5506 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
5507 kDNSServiceFlagsShareConnection
,
5508 0 /* interfaceIndex */, printer
->dnssd_name
,
5509 regtype
, NULL
/* domain */,
5510 NULL
/* host */, htons(printer
->port
),
5511 TXTRecordGetLength(&(printer
->ipp_txt
)),
5512 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
5513 (DNSServiceRegisterReply
)dnssd_callback
,
5514 printer
)) != kDNSServiceErr_NoError
)
5516 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5517 printer
->dnssd_name
, regtype
, error
);
5523 * Then register the _ipps._tcp (IPP) service type with the real port number to
5524 * advertise our IPP printer...
5527 printer
->ipps_ref
= printer
->common_ref
;
5529 if (subtype
&& *subtype
)
5530 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
5533 strncpy(regtype
, "_ipps._tcp", sizeof(regtype
) - 1);
5534 regtype
[sizeof(regtype
) - 1] = '\0';
5537 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
5538 kDNSServiceFlagsShareConnection
,
5539 0 /* interfaceIndex */, printer
->dnssd_name
,
5540 regtype
, NULL
/* domain */,
5541 NULL
/* host */, htons(printer
->port
),
5542 TXTRecordGetLength(&(printer
->ipp_txt
)),
5543 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
5544 (DNSServiceRegisterReply
)dnssd_callback
,
5545 printer
)) != kDNSServiceErr_NoError
)
5547 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5548 printer
->dnssd_name
, regtype
, error
);
5551 # endif /* HAVE_SSL */
5554 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
5555 * real port number to advertise our IPP printer...
5558 printer
->http_ref
= printer
->common_ref
;
5560 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
5561 kDNSServiceFlagsShareConnection
,
5562 0 /* interfaceIndex */, printer
->dnssd_name
,
5563 "_http._tcp,_printer", NULL
/* domain */,
5564 NULL
/* host */, htons(printer
->port
),
5565 0 /* txtLen */, NULL
, /* txtRecord */
5566 (DNSServiceRegisterReply
)dnssd_callback
,
5567 printer
)) != kDNSServiceErr_NoError
)
5569 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5570 printer
->dnssd_name
, regtype
, error
);
5576 #endif /* HAVE_DNSSD */
5580 * 'respond_http()' - Send a HTTP response.
5583 int /* O - 1 on success, 0 on failure */
5585 _ipp_client_t
*client
, /* I - Client */
5586 http_status_t code
, /* I - HTTP status of response */
5587 const char *content_encoding
, /* I - Content-Encoding of response */
5588 const char *type
, /* I - MIME media type of response */
5589 size_t length
) /* I - Length of response */
5591 char message
[1024]; /* Text message */
5594 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
5596 if (code
== HTTP_STATUS_CONTINUE
)
5599 * 100-continue doesn't send any headers...
5602 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
5606 * Format an error message...
5609 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
5611 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
5613 type
= "text/plain";
5614 length
= strlen(message
);
5620 * Send the HTTP response header...
5623 httpClearFields(client
->http
);
5625 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
5626 client
->operation
== HTTP_STATE_OPTIONS
)
5627 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
5631 if (!strcmp(type
, "text/html"))
5632 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
5633 "text/html; charset=utf-8");
5635 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
5637 if (content_encoding
)
5638 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
5641 httpSetLength(client
->http
, length
);
5643 if (httpWriteResponse(client
->http
, code
) < 0)
5647 * Send the response data...
5653 * Send a plain text message.
5656 if (httpPrintf(client
->http
, "%s", message
) < 0)
5659 if (httpWrite2(client
->http
, "", 0) < 0)
5662 else if (client
->response
)
5665 * Send an IPP response...
5668 debug_attributes("Response", client
->response
, 2);
5670 ippSetState(client
->response
, IPP_STATE_IDLE
);
5672 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
5681 * 'respond_ipp()' - Send an IPP response.
5685 respond_ipp(_ipp_client_t
*client
, /* I - Client */
5686 ipp_status_t status
, /* I - status-code */
5687 const char *message
, /* I - printf-style status-message */
5688 ...) /* I - Additional args as needed */
5690 const char *formatted
= NULL
; /* Formatted message */
5693 ippSetStatusCode(client
->response
, status
);
5697 va_list ap
; /* Pointer to additional args */
5698 ipp_attribute_t
*attr
; /* New status-message attribute */
5700 va_start(ap
, message
);
5701 if ((attr
= ippFindAttribute(client
->response
, "status-message",
5702 IPP_TAG_TEXT
)) != NULL
)
5703 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
5705 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
5706 "status-message", NULL
, message
, ap
);
5709 formatted
= ippGetString(attr
, 0, NULL
);
5713 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
5714 ippOpString(client
->operation_id
), ippErrorString(status
),
5717 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
5718 ippOpString(client
->operation_id
), ippErrorString(status
));
5723 * 'respond_unsupported()' - Respond with an unsupported attribute.
5727 respond_unsupported(
5728 _ipp_client_t
*client
, /* I - Client */
5729 ipp_attribute_t
*attr
) /* I - Atribute */
5731 ipp_attribute_t
*temp
; /* Copy of attribute */
5734 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5735 "Unsupported %s %s%s value.", ippGetName(attr
),
5736 ippGetCount(attr
) > 1 ? "1setOf " : "",
5737 ippTagString(ippGetValueTag(attr
)));
5739 temp
= ippCopyAttribute(client
->response
, attr
, 0);
5740 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
5745 * 'run_printer()' - Run the printer service.
5749 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
5751 int num_fds
; /* Number of file descriptors */
5752 struct pollfd polldata
[3]; /* poll() data */
5753 int timeout
; /* Timeout for poll() */
5754 _ipp_client_t
*client
; /* New client */
5758 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5761 polldata
[0].fd
= printer
->ipv4
;
5762 polldata
[0].events
= POLLIN
;
5764 polldata
[1].fd
= printer
->ipv6
;
5765 polldata
[1].events
= POLLIN
;
5770 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
5771 polldata
[num_fds
++].events
= POLLIN
;
5772 #endif /* HAVE_DNSSD */
5775 * Loop until we are killed or have a hard error...
5780 if (cupsArrayCount(printer
->jobs
))
5785 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
5787 perror("poll() failed");
5791 if (polldata
[0].revents
& POLLIN
)
5793 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
5795 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5797 perror("Unable to create client thread");
5798 delete_client(client
);
5803 if (polldata
[1].revents
& POLLIN
)
5805 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
5807 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5809 perror("Unable to create client thread");
5810 delete_client(client
);
5816 if (polldata
[2].revents
& POLLIN
)
5817 DNSServiceProcessResult(printer
->common_ref
);
5818 #endif /* HAVE_DNSSD */
5821 * Clean out old jobs...
5824 clean_jobs(printer
);
5830 * 'time_string()' - Return the local time in hours, minutes, and seconds.
5834 time_string(time_t tv
, /* I - Time value */
5835 char *buffer
, /* I - Buffer */
5836 size_t bufsize
) /* I - Size of buffer */
5838 struct tm
*curtime
= localtime(&tv
);
5841 strftime(buffer
, bufsize
, "%X", curtime
);
5847 * 'usage()' - Show program usage.
5851 usage(int status
) /* O - Exit status */
5855 puts(CUPS_SVERSION
" - Copyright 2010-2013 by Apple Inc. All rights "
5860 puts("Usage: ippserver [options] \"name\"");
5863 puts("-2 Supports 2-sided printing (default=1-sided)");
5864 puts("-M manufacturer Manufacturer name (default=Test)");
5865 puts("-P PIN printing mode");
5866 puts("-c command Run command for every print job");
5867 printf("-d spool-directory Spool directory "
5868 "(default=/tmp/ippserver.%d)\n", (int)getpid());
5869 puts("-f type/subtype[,...] List of supported types "
5870 "(default=application/pdf,image/jpeg)");
5871 puts("-h Show program help");
5872 puts("-i iconfile.png PNG icon file (default=printer.png)");
5873 puts("-k Keep job spool files");
5874 puts("-l location Location of printer (default=empty string)");
5875 puts("-m model Model name (default=Printer)");
5876 puts("-n hostname Hostname for printer");
5877 puts("-p port Port number (default=auto)");
5879 puts("-r subtype Bonjour service subtype (default=_print)");
5880 #endif /* HAVE_DNSSD */
5881 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
5882 puts("-v[vvv] Be (very) verbose");
5889 * 'valid_doc_attributes()' - Determine whether the document attributes are
5892 * When one or more document attributes are invalid, this function adds a
5893 * suitable response and attributes to the unsupported group.
5896 static int /* O - 1 if valid, 0 if not */
5897 valid_doc_attributes(
5898 _ipp_client_t
*client
) /* I - Client */
5900 int valid
= 1; /* Valid attributes? */
5901 ipp_op_t op
= ippGetOperation(client
->request
);
5903 const char *op_name
= ippOpString(op
);
5904 /* IPP operation name */
5905 ipp_attribute_t
*attr
, /* Current attribute */
5906 *supported
; /* xxx-supported attribute */
5907 const char *compression
= NULL
,
5908 /* compression value */
5909 *format
= NULL
; /* document-format value */
5913 * Check operation attributes...
5916 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
5919 * If compression is specified, only accept a supported value in a Print-Job
5920 * or Send-Document request...
5923 compression
= ippGetString(attr
, 0, NULL
);
5924 supported
= ippFindAttribute(client
->printer
->attrs
,
5925 "compression-supported", IPP_TAG_KEYWORD
);
5927 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5928 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
5929 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
5930 op
!= IPP_OP_VALIDATE_JOB
) ||
5931 !ippContainsString(supported
, compression
))
5933 respond_unsupported(client
, attr
);
5938 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
5940 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
5942 if (strcmp(compression
, "none"))
5945 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
5946 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
5952 * Is it a format we support?
5955 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
5957 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
5958 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
5960 respond_unsupported(client
, attr
);
5965 format
= ippGetString(attr
, 0, NULL
);
5967 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
5968 client
->hostname
, op_name
, format
);
5970 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
5975 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
5977 format
= "application/octet-stream"; /* Should never happen */
5979 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
5982 if (!strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
5985 * Auto-type the file using the first 8 bytes of the file...
5988 unsigned char header
[8]; /* First 8 bytes of file */
5990 memset(header
, 0, sizeof(header
));
5991 httpPeek(client
->http
, (char *)header
, sizeof(header
));
5993 if (!memcmp(header
, "%PDF", 4))
5994 format
= "application/pdf";
5995 else if (!memcmp(header
, "%!", 2))
5996 format
= "application/postscript";
5997 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
5998 format
= "image/jpeg";
5999 else if (!memcmp(header
, "\211PNG", 4))
6000 format
= "image/png";
6001 else if (!memcmp(header
, "RAS2", 4))
6002 format
= "image/pwg-raster";
6003 else if (!memcmp(header
, "UNIRAST", 8))
6004 format
= "image/urf";
6010 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
6011 client
->hostname
, op_name
, format
);
6013 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
6017 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
6019 respond_unsupported(client
, attr
);
6027 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
6028 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
6035 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6037 * When one or more job attributes are invalid, this function adds a suitable
6038 * response and attributes to the unsupported group.
6041 static int /* O - 1 if valid, 0 if not */
6042 valid_job_attributes(
6043 _ipp_client_t
*client
) /* I - Client */
6045 int i
, /* Looping var */
6046 valid
= 1; /* Valid attributes? */
6047 ipp_attribute_t
*attr
, /* Current attribute */
6048 *supported
; /* xxx-supported attribute */
6052 * Check operation attributes...
6055 valid
= valid_doc_attributes(client
);
6058 * Check the various job template attributes...
6061 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
6063 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6064 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
6066 respond_unsupported(client
, attr
);
6071 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
6073 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
6075 respond_unsupported(client
, attr
);
6080 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
6082 if (ippGetCount(attr
) != 1 ||
6083 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6084 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6085 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6086 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
6088 respond_unsupported(client
, attr
);
6093 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
6095 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
6097 respond_unsupported(client
, attr
);
6102 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
6104 if (ippGetCount(attr
) != 1 ||
6105 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6106 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
6108 respond_unsupported(client
, attr
);
6112 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
6115 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
6117 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
6119 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6120 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
6122 respond_unsupported(client
, attr
);
6127 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
6129 if (ippGetCount(attr
) != 1 ||
6130 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6131 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6132 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6133 strcmp(ippGetString(attr
, 0, NULL
), "none"))
6135 respond_unsupported(client
, attr
);
6140 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
6142 if (ippGetCount(attr
) != 1 ||
6143 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6144 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6145 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
6147 respond_unsupported(client
, attr
);
6153 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
6155 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
6158 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
6160 respond_unsupported(client
, attr
);
6166 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
6168 if (ippGetCount(attr
) != 1 ||
6169 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
6171 respond_unsupported(client
, attr
);
6174 /* TODO: check for valid media-col */
6177 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
6179 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6180 (strcmp(ippGetString(attr
, 0, NULL
),
6181 "separate-documents-uncollated-copies") &&
6182 strcmp(ippGetString(attr
, 0, NULL
),
6183 "separate-documents-collated-copies")))
6185 respond_unsupported(client
, attr
);
6190 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
6192 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6193 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
6194 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
6196 respond_unsupported(client
, attr
);
6201 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
6203 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
6205 respond_unsupported(client
, attr
);
6210 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
6212 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6213 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
6214 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
6216 respond_unsupported(client
, attr
);
6221 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
6223 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
6225 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
6228 respond_unsupported(client
, attr
);
6233 int count
, /* Number of supported values */
6234 xdpi
, /* Horizontal resolution for job template attribute */
6235 ydpi
, /* Vertical resolution for job template attribute */
6236 sydpi
; /* Vertical resolution for supported value */
6237 ipp_res_t units
, /* Units for job template attribute */
6238 sunits
; /* Units for supported value */
6240 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
6241 count
= ippGetCount(supported
);
6243 for (i
= 0; i
< count
; i
++)
6245 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
6251 respond_unsupported(client
, attr
);
6257 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
6259 const char *sides
= ippGetString(attr
, 0, NULL
);
6260 /* "sides" value... */
6262 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
6264 respond_unsupported(client
, attr
);
6267 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
6269 if (!ippContainsString(supported
, sides
))
6271 respond_unsupported(client
, attr
);
6275 else if (strcmp(sides
, "one-sided"))
6277 respond_unsupported(client
, attr
);