4 * Sample IPP Everywhere server for CUPS.
6 * Copyright 2010-2014 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * Disable private and deprecated stuff so we can verify that the public API
19 * is sufficient to implement a server.
22 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
23 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
27 * Include necessary headers...
35 #include <cups/cups.h> /* Public API */
36 #include <config.h> /* CUPS configuration header */
37 #include <cups/thread-private.h> /* For multithreading functions */
43 #endif /* HAVE_DNSSD */
46 #include <sys/fcntl.h>
48 #ifdef HAVE_SYS_MOUNT_H
49 # include <sys/mount.h>
50 #endif /* HAVE_SYS_MOUNT_H */
51 #ifdef HAVE_SYS_STATFS_H
52 # include <sys/statfs.h>
53 #endif /* HAVE_SYS_STATFS_H */
54 #ifdef HAVE_SYS_STATVFS_H
55 # include <sys/statvfs.h>
56 #endif /* HAVE_SYS_STATVFS_H */
59 #endif /* HAVE_SYS_VFS_H */
66 enum _ipp_preason_e
/* printer-state-reasons bit values */
68 _IPP_PREASON_NONE
= 0x0000, /* none */
69 _IPP_PREASON_OTHER
= 0x0001, /* other */
70 _IPP_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
71 _IPP_PREASON_INPUT_TRAY_MISSING
= 0x0004,
72 /* input-tray-missing */
73 _IPP_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
74 /* marker-supply-empty */
75 _IPP_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
76 /* marker-supply-low */
77 _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
78 /* marker-waste-almost-full */
79 _IPP_PREASON_MARKER_WASTE_FULL
= 0x0040,
80 /* marker-waste-full */
81 _IPP_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
82 _IPP_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
83 _IPP_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
84 _IPP_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
85 _IPP_PREASON_MOVING_TO_PAUSED
= 0x0800,
86 /* moving-to-paused */
87 _IPP_PREASON_PAUSED
= 0x1000, /* paused */
88 _IPP_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
89 _IPP_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
90 _IPP_PREASON_TONER_LOW
= 0x8000 /* toner-low */
92 typedef unsigned int _ipp_preason_t
; /* Bitfield for printer-state-reasons */
94 typedef enum _ipp_media_class_e
96 _IPP_GENERAL
, /* General-purpose size */
97 _IPP_PHOTO_ONLY
, /* Photo-only size */
98 _IPP_ENV_ONLY
/* Envelope-only size */
101 typedef enum _ipp_media_size_e
103 _IPP_MEDIA_SIZE_NONE
= -1,
108 _IPP_MEDIA_SIZE_LEGAL
,
109 _IPP_MEDIA_SIZE_LETTER
,
110 _IPP_MEDIA_SIZE_COM10
,
116 static const char * const media_supported
[] =
117 { /* media-supported values */
118 "iso_a4_210x297mm", /* A4 */
119 "iso_a5_148x210mm", /* A5 */
120 "iso_a6_105x148mm", /* A6 */
121 "iso_dl_110x220mm", /* DL */
122 "na_legal_8.5x14in", /* Legal */
123 "na_letter_8.5x11in", /* Letter */
124 "na_number-10_4.125x9.5in", /* #10 */
125 "na_index-3x5_3x5in", /* 3x5 */
126 "oe_photo-l_3.5x5in", /* L */
127 "na_index-4x6_4x6in", /* 4x6 */
128 "na_5x7_5x7in" /* 5x7 aka 2L */
130 static const int media_col_sizes
[][3] =
131 { /* media-col-database sizes */
132 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
133 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
134 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
135 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
136 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
137 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
138 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
139 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
140 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
141 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
142 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
145 typedef enum _ipp_media_source_e
147 _IPP_MEDIA_SOURCE_NONE
= -1,
148 _IPP_MEDIA_SOURCE_AUTO
,
149 _IPP_MEDIA_SOURCE_MAIN
,
150 _IPP_MEDIA_SOURCE_MANUAL
,
151 _IPP_MEDIA_SOURCE_ENVELOPE
,
152 _IPP_MEDIA_SOURCE_PHOTO
153 } _ipp_media_source_t
;
154 static const char * const media_source_supported
[] =
155 /* media-source-supported values */
164 typedef enum _ipp_media_type_e
166 _IPP_MEDIA_TYPE_NONE
= -1,
167 _IPP_MEDIA_TYPE_AUTO
,
168 _IPP_MEDIA_TYPE_CARDSTOCK
,
169 _IPP_MEDIA_TYPE_ENVELOPE
,
170 _IPP_MEDIA_TYPE_LABELS
,
171 _IPP_MEDIA_TYPE_OTHER
,
172 _IPP_MEDIA_TYPE_GLOSSY
,
173 _IPP_MEDIA_TYPE_HIGH_GLOSS
,
174 _IPP_MEDIA_TYPE_MATTE
,
175 _IPP_MEDIA_TYPE_SATIN
,
176 _IPP_MEDIA_TYPE_SEMI_GLOSS
,
177 _IPP_MEDIA_TYPE_STATIONERY
,
178 _IPP_MEDIA_TYPE_LETTERHEAD
,
179 _IPP_MEDIA_TYPE_TRANSPARENCY
181 static const char * const media_type_supported
[] =
182 /* media-type-supported values */
189 "photographic-glossy",
190 "photographic-high-gloss",
191 "photographic-matte",
192 "photographic-satin",
193 "photographic-semi-gloss",
195 "stationery-letterhead",
199 typedef enum _ipp_supply_e
201 _IPP_SUPPLY_CYAN
, /* Cyan Toner */
202 _IPP_SUPPLY_MAGENTA
, /* Magenta Toner */
203 _IPP_SUPPLY_YELLOW
, /* Yellow Toner */
204 _IPP_SUPPLY_BLACK
, /* Black Toner */
205 _IPP_SUPPLY_WASTE
/* Waste Toner */
207 static const char * const printer_supplies
[] =
208 { /* printer-supply-description values */
221 typedef struct _ipp_filter_s
/**** Attribute filter ****/
223 cups_array_t
*ra
; /* Requested attributes */
224 ipp_tag_t group_tag
; /* Group to copy */
227 typedef struct _ipp_job_s _ipp_job_t
;
229 typedef struct _ipp_printer_s
/**** Printer data ****/
231 int ipv4
, /* IPv4 listener */
232 ipv6
; /* IPv6 listener */
234 DNSServiceRef common_ref
, /* Shared service connection */
235 ipp_ref
, /* Bonjour IPP service */
237 ipps_ref
, /* Bonjour IPPS service */
238 # endif /* HAVE_SSL */
239 http_ref
, /* Bonjour HTTP service */
240 printer_ref
; /* Bonjour LPD service */
241 TXTRecordRef ipp_txt
; /* Bonjour IPP TXT record */
242 char *dnssd_name
; /* printer-dnssd-name */
243 #endif /* HAVE_DNSSD */
244 char *name
, /* printer-name */
245 *icon
, /* Icon filename */
246 *directory
, /* Spool directory */
247 *hostname
, /* Hostname */
248 *uri
, /* printer-uri-supported */
249 *command
; /* Command to run with job file */
251 size_t urilen
; /* Length of printer URI */
252 ipp_t
*attrs
; /* Static attributes */
253 time_t start_time
; /* Startup time */
254 time_t config_time
; /* printer-config-change-time */
255 ipp_pstate_t state
; /* printer-state value */
256 _ipp_preason_t state_reasons
; /* printer-state-reasons values */
257 time_t state_time
; /* printer-state-change-time */
258 cups_array_t
*jobs
; /* Jobs */
259 _ipp_job_t
*active_job
; /* Current active/pending job */
260 int next_job_id
; /* Next job-id value */
261 _cups_rwlock_t rwlock
; /* Printer lock */
262 _ipp_media_size_t main_size
; /* Ready media */
263 _ipp_media_type_t main_type
;
265 _ipp_media_size_t envelope_size
;
267 _ipp_media_size_t photo_size
;
268 _ipp_media_type_t photo_type
;
270 int supplies
[5]; /* Supply levels (0-100) */
273 struct _ipp_job_s
/**** Job data ****/
276 const char *name
, /* job-name */
277 *username
, /* job-originating-user-name */
278 *format
; /* document-format */
279 ipp_jstate_t state
; /* job-state value */
280 time_t created
, /* time-at-creation value */
281 processing
, /* time-at-processing value */
282 completed
; /* time-at-completed value */
283 int impressions
, /* job-impressions value */
284 impcompleted
; /* job-impressions-completed value */
285 ipp_t
*attrs
; /* Static attributes */
286 int cancel
; /* Non-zero when job canceled */
287 char *filename
; /* Print file name */
288 int fd
; /* Print file descriptor */
289 _ipp_printer_t
*printer
; /* Printer */
292 typedef struct _ipp_client_s
/**** Client data ****/
294 http_t
*http
; /* HTTP connection */
295 ipp_t
*request
, /* IPP request */
296 *response
; /* IPP response */
297 time_t start
; /* Request start time */
298 http_state_t operation
; /* Request operation */
299 ipp_op_t operation_id
; /* IPP operation-id */
300 char uri
[1024], /* Request URI */
301 *options
; /* URI options */
302 http_addr_t addr
; /* Client address */
303 char hostname
[256]; /* Client hostname */
304 _ipp_printer_t
*printer
; /* Printer */
305 _ipp_job_t
*job
; /* Current job, if any */
313 static void clean_jobs(_ipp_printer_t
*printer
);
314 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
315 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
316 ipp_tag_t group_tag
, int quickcopy
);
317 static void copy_job_attributes(_ipp_client_t
*client
,
318 _ipp_job_t
*job
, cups_array_t
*ra
);
319 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
320 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
321 static int create_listener(int family
, int *port
);
322 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
323 static ipp_t
*create_media_size(int width
, int length
);
324 static _ipp_printer_t
*create_printer(const char *servername
,
325 const char *name
, const char *location
,
326 const char *make
, const char *model
,
328 const char *docformats
, int ppm
,
329 int ppm_color
, int duplex
, int port
,
333 #endif /* HAVE_DNSSD */
334 const char *directory
,
335 const char *command
);
336 static void debug_attributes(const char *title
, ipp_t
*ipp
,
338 static void delete_client(_ipp_client_t
*client
);
339 static void delete_job(_ipp_job_t
*job
);
340 static void delete_printer(_ipp_printer_t
*printer
);
342 static void dnssd_callback(DNSServiceRef sdRef
,
343 DNSServiceFlags flags
,
344 DNSServiceErrorType errorCode
,
348 _ipp_printer_t
*printer
);
349 #endif /* HAVE_DNSSD */
350 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
351 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
352 static void html_escape(_ipp_client_t
*client
, const char *s
,
354 static void html_footer(_ipp_client_t
*client
);
355 static void html_header(_ipp_client_t
*client
, const char *title
);
356 static void html_printf(_ipp_client_t
*client
, const char *format
,
357 ...) __attribute__((__format__(__printf__
,
359 static void ipp_cancel_job(_ipp_client_t
*client
);
360 static void ipp_close_job(_ipp_client_t
*client
);
361 static void ipp_create_job(_ipp_client_t
*client
);
362 static void ipp_get_job_attributes(_ipp_client_t
*client
);
363 static void ipp_get_jobs(_ipp_client_t
*client
);
364 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
365 static void ipp_identify_printer(_ipp_client_t
*client
);
366 static void ipp_print_job(_ipp_client_t
*client
);
367 static void ipp_print_uri(_ipp_client_t
*client
);
368 static void ipp_send_document(_ipp_client_t
*client
);
369 static void ipp_send_uri(_ipp_client_t
*client
);
370 static void ipp_validate_job(_ipp_client_t
*client
);
371 static int parse_options(_ipp_client_t
*client
, cups_option_t
**options
);
372 static void *process_client(_ipp_client_t
*client
);
373 static int process_http(_ipp_client_t
*client
);
374 static int process_ipp(_ipp_client_t
*client
);
375 static void *process_job(_ipp_job_t
*job
);
377 static int register_printer(_ipp_printer_t
*printer
, const char *location
, const char *make
, const char *model
, const char *formats
, const char *adminurl
, const char *uuid
, int color
, int duplex
, const char *regtype
);
378 #endif /* HAVE_DNSSD */
379 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
380 const char *content_coding
,
381 const char *type
, size_t length
);
382 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
383 const char *message
, ...)
384 __attribute__ ((__format__ (__printf__
, 3, 4)));
385 static void respond_unsupported(_ipp_client_t
*client
,
386 ipp_attribute_t
*attr
);
387 static void run_printer(_ipp_printer_t
*printer
);
388 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
389 static void usage(int status
) __attribute__((noreturn
));
390 static int valid_doc_attributes(_ipp_client_t
*client
);
391 static int valid_job_attributes(_ipp_client_t
*client
);
398 static int KeepFiles
= 0,
403 * 'main()' - Main entry to the sample server.
406 int /* O - Exit status */
407 main(int argc
, /* I - Number of command-line args */
408 char *argv
[]) /* I - Command-line arguments */
410 int i
; /* Looping var */
411 const char *opt
, /* Current option character */
412 *command
= NULL
, /* Command to run with job files */
413 *servername
= NULL
, /* Server host name */
414 *name
= NULL
, /* Printer name */
415 *location
= "", /* Location of printer */
416 *make
= "Test", /* Manufacturer */
417 *model
= "Printer", /* Model */
418 *icon
= "printer.png", /* Icon file */
419 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
420 /* Supported formats */
422 const char *keypath
= NULL
; /* Keychain path */
423 #endif /* HAVE_SSL */
425 const char *subtype
= "_print"; /* Bonjour service subtype */
426 #endif /* HAVE_DNSSD */
427 int port
= 8631, /* Port number (0 = auto) */
428 duplex
= 0, /* Duplex mode */
429 ppm
= 10, /* Pages per minute for mono */
430 ppm_color
= 0, /* Pages per minute for color */
431 pin
= 0; /* PIN printing mode? */
432 char directory
[1024] = ""; /* Spool directory */
433 _ipp_printer_t
*printer
; /* Printer object */
437 * Parse command-line arguments...
440 for (i
= 1; i
< argc
; i
++)
441 if (argv
[i
][0] == '-')
443 for (opt
= argv
[i
] + 1; *opt
; opt
++)
447 case '2' : /* -2 (enable 2-sided printing) */
452 case 'K' : /* -K keypath */
458 #endif /* HAVE_SSL */
460 case 'M' : /* -M manufacturer */
467 case 'P' : /* -P (PIN printing mode) */
471 case 'c' : /* -c command */
479 case 'd' : /* -d spool-directory */
483 strncpy(directory
, argv
[i
], sizeof(directory
) - 1);
484 directory
[sizeof(directory
) - 1] = '\0';
487 case 'f' : /* -f type/subtype[,...] */
494 case 'h' : /* -h (show help) */
497 case 'i' : /* -i icon.png */
504 case 'k' : /* -k (keep files) */
508 case 'l' : /* -l location */
515 case 'm' : /* -m model */
522 case 'n' : /* -n hostname */
526 servername
= argv
[i
];
529 case 'p' : /* -p port */
531 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
533 port
= atoi(argv
[i
]);
537 case 'r' : /* -r subtype */
543 #endif /* HAVE_DNSSD */
545 case 's' : /* -s speed[,color-speed] */
549 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
553 case 'v' : /* -v (be verbose) */
557 default : /* Unknown */
558 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
569 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
577 * Apply defaults as needed...
582 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
584 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
586 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
587 directory
, strerror(errno
));
592 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
596 cupsSetServerCredentials(keypath
, servername
, 1);
597 #endif /* HAVE_SSL */
600 * Create the printer...
603 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
604 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
607 #endif /* HAVE_DNSSD */
608 directory
, command
)) == NULL
)
612 * Run the print service...
615 run_printer(printer
);
618 * Destroy the printer and exit...
621 delete_printer(printer
);
628 * 'clean_jobs()' - Clean out old (completed) jobs.
632 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
634 _ipp_job_t
*job
; /* Current job */
635 time_t cleantime
; /* Clean time */
638 if (cupsArrayCount(printer
->jobs
) == 0)
641 cleantime
= time(NULL
) - 60;
643 _cupsRWLockWrite(&(printer
->rwlock
));
644 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
646 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
647 if (job
->completed
&& job
->completed
< cleantime
)
649 cupsArrayRemove(printer
->jobs
, job
);
654 _cupsRWUnlock(&(printer
->rwlock
));
659 * 'compare_jobs()' - Compare two jobs.
662 static int /* O - Result of comparison */
663 compare_jobs(_ipp_job_t
*a
, /* I - First job */
664 _ipp_job_t
*b
) /* I - Second job */
666 return (b
->id
- a
->id
);
671 * 'copy_attributes()' - Copy attributes from one request to another.
675 copy_attributes(ipp_t
*to
, /* I - Destination request */
676 ipp_t
*from
, /* I - Source request */
677 cups_array_t
*ra
, /* I - Requested attributes */
678 ipp_tag_t group_tag
, /* I - Group to copy */
679 int quickcopy
) /* I - Do a quick copy? */
681 _ipp_filter_t filter
; /* Filter data */
685 filter
.group_tag
= group_tag
;
687 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
692 * 'copy_job_attrs()' - Copy job attributes to the response.
697 _ipp_client_t
*client
, /* I - Client */
698 _ipp_job_t
*job
, /* I - Job */
699 cups_array_t
*ra
) /* I - requested-attributes */
701 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
703 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
706 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
708 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
711 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
714 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
716 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
719 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
720 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
722 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
723 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
725 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
726 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
728 if (!ra
|| cupsArrayFind(ra
, "job-state"))
729 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
730 "job-state", job
->state
);
732 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
736 case IPP_JSTATE_PENDING
:
737 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
740 case IPP_JSTATE_HELD
:
742 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
743 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
744 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
746 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
749 case IPP_JSTATE_PROCESSING
:
751 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
753 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
756 case IPP_JSTATE_STOPPED
:
757 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
760 case IPP_JSTATE_CANCELED
:
761 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
764 case IPP_JSTATE_ABORTED
:
765 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
768 case IPP_JSTATE_COMPLETED
:
769 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
774 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
778 case IPP_JSTATE_PENDING
:
779 ippAddString(client
->response
, IPP_TAG_JOB
,
780 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
784 case IPP_JSTATE_HELD
:
786 ippAddString(client
->response
, IPP_TAG_JOB
,
787 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
788 "job-state-reasons", NULL
, "job-incoming");
789 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
790 ippAddString(client
->response
, IPP_TAG_JOB
,
791 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
792 "job-state-reasons", NULL
, "job-hold-until-specified");
794 ippAddString(client
->response
, IPP_TAG_JOB
,
795 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
796 "job-state-reasons", NULL
, "job-data-insufficient");
799 case IPP_JSTATE_PROCESSING
:
801 ippAddString(client
->response
, IPP_TAG_JOB
,
802 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
803 "job-state-reasons", NULL
, "processing-to-stop-point");
805 ippAddString(client
->response
, IPP_TAG_JOB
,
806 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
807 "job-state-reasons", NULL
, "job-printing");
810 case IPP_JSTATE_STOPPED
:
811 ippAddString(client
->response
, IPP_TAG_JOB
,
812 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
813 NULL
, "job-stopped");
816 case IPP_JSTATE_CANCELED
:
817 ippAddString(client
->response
, IPP_TAG_JOB
,
818 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
819 NULL
, "job-canceled-by-user");
822 case IPP_JSTATE_ABORTED
:
823 ippAddString(client
->response
, IPP_TAG_JOB
,
824 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
825 NULL
, "aborted-by-system");
828 case IPP_JSTATE_COMPLETED
:
829 ippAddString(client
->response
, IPP_TAG_JOB
,
830 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
831 NULL
, "job-completed-successfully");
836 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
837 ippAddInteger(client
->response
, IPP_TAG_JOB
,
838 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
839 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
841 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
842 ippAddInteger(client
->response
, IPP_TAG_JOB
,
843 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
844 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
849 * 'create_client()' - Accept a new network connection and create a client
853 static _ipp_client_t
* /* O - Client */
854 create_client(_ipp_printer_t
*printer
, /* I - Printer */
855 int sock
) /* I - Listen socket */
857 _ipp_client_t
*client
; /* Client */
860 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
862 perror("Unable to allocate memory for client");
866 client
->printer
= printer
;
869 * Accept the client and get the remote address...
872 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
874 perror("Unable to accept client connection");
881 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
884 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
891 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
895 static _ipp_job_t
* /* O - Job */
896 create_job(_ipp_client_t
*client
) /* I - Client */
898 _ipp_job_t
*job
; /* Job */
899 ipp_attribute_t
*attr
; /* Job attribute */
900 char uri
[1024], /* job-uri value */
901 uuid
[64]; /* job-uuid value */
904 _cupsRWLockWrite(&(client
->printer
->rwlock
));
905 if (client
->printer
->active_job
&&
906 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
909 * Only accept a single job at a time...
912 _cupsRWLockWrite(&(client
->printer
->rwlock
));
917 * Allocate and initialize the job object...
920 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
922 perror("Unable to allocate memory for job");
926 job
->printer
= client
->printer
;
927 job
->attrs
= ippNew();
928 job
->state
= IPP_JSTATE_HELD
;
932 * Copy all of the job attributes...
935 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
938 * Get the requesting-user-name, document format, and priority...
941 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
942 job
->username
= ippGetString(attr
, 0, NULL
);
944 job
->username
= "anonymous";
946 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
948 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
950 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
951 job
->format
= ippGetString(attr
, 0, NULL
);
952 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
953 job
->format
= ippGetString(attr
, 0, NULL
);
955 job
->format
= "application/octet-stream";
958 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
959 job
->impressions
= ippGetInteger(attr
, 0);
961 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
962 job
->name
= ippGetString(attr
, 0, NULL
);
965 * Add job description attributes and add to the jobs array...
968 job
->id
= client
->printer
->next_job_id
++;
970 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
971 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
973 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
974 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
975 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
976 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
977 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
978 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
980 cupsArrayAdd(client
->printer
->jobs
, job
);
981 client
->printer
->active_job
= job
;
983 _cupsRWUnlock(&(client
->printer
->rwlock
));
990 * 'create_job_filename()' - Create the filename for a document in a job.
993 static void create_job_filename(
994 _ipp_printer_t
*printer
, /* I - Printer */
995 _ipp_job_t
*job
, /* I - Job */
996 char *fname
, /* I - Filename buffer */
997 size_t fnamesize
) /* I - Size of filename buffer */
999 char name
[256], /* "Safe" filename */
1000 *nameptr
; /* Pointer into filename */
1001 const char *ext
, /* Filename extension */
1002 *job_name
; /* job-name value */
1003 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1007 * Make a name from the job-name attribute...
1010 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1011 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1013 job_name
= "untitled";
1015 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1016 if (isalnum(*job_name
& 255) || *job_name
== '-')
1017 *nameptr
++ = (char)tolower(*job_name
& 255);
1024 * Figure out the extension...
1027 if (!strcasecmp(job
->format
, "image/jpeg"))
1029 else if (!strcasecmp(job
->format
, "image/png"))
1031 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1033 else if (!strcasecmp(job
->format
, "image/urf"))
1035 else if (!strcasecmp(job
->format
, "application/pdf"))
1037 else if (!strcasecmp(job
->format
, "application/postscript"))
1043 * Create a filename with the job-id, job-name, and document-format (extension)...
1046 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1051 * 'create_listener()' - Create a listener socket.
1054 static int /* O - Listener socket or -1 on error */
1055 create_listener(int family
, /* I - Address family */
1056 int *port
) /* IO - Port number */
1058 int sock
; /* Listener socket */
1059 http_addrlist_t
*addrlist
; /* Listen address */
1060 char service
[255]; /* Service port */
1065 *port
= 8000 + ((int)getuid() % 1000);
1066 fprintf(stderr
, "Listening on port %d.\n", *port
);
1069 snprintf(service
, sizeof(service
), "%d", *port
);
1070 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1073 sock
= httpAddrListen(&(addrlist
->addr
), *port
);
1075 httpAddrFreeList(addrlist
);
1082 * 'create_media_col()' - Create a media-col value.
1085 static ipp_t
* /* O - media-col collection */
1086 create_media_col(const char *media
, /* I - Media name */
1087 const char *source
, /* I - Media source */
1088 const char *type
, /* I - Media type */
1089 int width
, /* I - x-dimension in 2540ths */
1090 int length
, /* I - y-dimension in 2540ths */
1091 int margins
) /* I - Value for margins */
1093 ipp_t
*media_col
= ippNew(), /* media-col value */
1094 *media_size
= create_media_size(width
, length
);
1095 /* media-size value */
1096 char media_key
[256]; /* media-key value */
1100 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1102 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1104 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1106 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1108 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1110 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1111 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1112 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1113 "media-bottom-margin", margins
);
1114 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1115 "media-left-margin", margins
);
1116 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1117 "media-right-margin", margins
);
1118 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1119 "media-top-margin", margins
);
1121 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1123 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1125 ippDelete(media_size
);
1132 * 'create_media_size()' - Create a media-size value.
1135 static ipp_t
* /* O - media-col collection */
1136 create_media_size(int width
, /* I - x-dimension in 2540ths */
1137 int length
) /* I - y-dimension in 2540ths */
1139 ipp_t
*media_size
= ippNew(); /* media-size value */
1142 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1144 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1147 return (media_size
);
1152 * 'create_printer()' - Create, register, and listen for connections to a
1156 static _ipp_printer_t
* /* O - Printer */
1157 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1158 const char *name
, /* I - printer-name */
1159 const char *location
, /* I - printer-location */
1160 const char *make
, /* I - printer-make-and-model */
1161 const char *model
, /* I - printer-make-and-model */
1162 const char *icon
, /* I - printer-icons */
1163 const char *docformats
, /* I - document-format-supported */
1164 int ppm
, /* I - Pages per minute in grayscale */
1165 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1166 int duplex
, /* I - 1 = duplex, 0 = simplex */
1167 int port
, /* I - Port for listeners or 0 for auto */
1168 int pin
, /* I - Require PIN printing */
1170 const char *subtype
, /* I - Bonjour service subtype */
1171 #endif /* HAVE_DNSSD */
1172 const char *directory
, /* I - Spool directory */
1173 const char *command
) /* I - Command to run on job files */
1175 int i
, j
; /* Looping vars */
1176 _ipp_printer_t
*printer
; /* Printer */
1177 char hostname
[256], /* Hostname */
1178 uri
[1024], /* Printer URI */
1179 icons
[1024], /* printer-icons URI */
1180 adminurl
[1024], /* printer-more-info URI */
1181 supplyurl
[1024],/* printer-supply-info-uri URI */
1182 device_id
[1024],/* printer-device-id */
1183 make_model
[128],/* printer-make-and-model */
1184 uuid
[128]; /* printer-uuid */
1185 int num_formats
; /* Number of document-format-supported values */
1186 char *defformat
, /* document-format-default value */
1187 *formats
[100], /* document-format-supported values */
1188 *ptr
; /* Pointer into string */
1189 const char *prefix
; /* Prefix string */
1190 int num_database
; /* Number of database values */
1191 ipp_attribute_t
*media_col_database
,
1192 /* media-col-database value */
1193 *media_size_supported
;
1194 /* media-size-supported value */
1195 ipp_t
*media_col_default
;
1196 /* media-col-default value */
1197 int media_col_index
;/* Current media-col-database value */
1198 int k_supported
; /* Maximum file size supported */
1200 struct statvfs spoolinfo
; /* FS info for spool directory */
1201 double spoolsize
; /* FS size */
1202 #elif defined(HAVE_STATFS)
1203 struct statfs spoolinfo
; /* FS info for spool directory */
1204 double spoolsize
; /* FS size */
1205 #endif /* HAVE_STATVFS */
1206 static const int orients
[4] = /* orientation-requested-supported values */
1208 IPP_ORIENT_PORTRAIT
,
1209 IPP_ORIENT_LANDSCAPE
,
1210 IPP_ORIENT_REVERSE_LANDSCAPE
,
1211 IPP_ORIENT_REVERSE_PORTRAIT
1213 static const char * const versions
[] =/* ipp-versions-supported values */
1219 static const char * const features
[] =/* ipp-features-supported values */
1223 static const int ops
[] = /* operations-supported values */
1227 IPP_OP_VALIDATE_JOB
,
1229 IPP_OP_SEND_DOCUMENT
,
1232 IPP_OP_GET_JOB_ATTRIBUTES
,
1234 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1235 IPP_OP_CANCEL_MY_JOBS
,
1237 IPP_OP_IDENTIFY_PRINTER
1239 static const char * const charsets
[] =/* charset-supported values */
1244 static const char * const compressions
[] =/* compression-supported values */
1249 #endif /* HAVE_LIBZ */
1252 static const char * const identify_actions
[] =
1257 static const char * const job_creation
[] =
1258 { /* job-creation-attributes-supported values */
1260 "ipp-attribute-fidelity",
1262 "job-accounting-user-id",
1268 "multiple-document-handling",
1269 "orientation-requested",
1273 static const char * const media_col_supported
[] =
1274 { /* media-col-supported values */
1275 "media-bottom-margin",
1276 "media-left-margin",
1277 "media-right-margin",
1283 static const int media_xxx_margin_supported
[] =
1284 { /* media-xxx-margin-supported values */
1288 static const char * const multiple_document_handling
[] =
1289 { /* multiple-document-handling-supported values */
1290 "separate-documents-uncollated-copies",
1291 "separate-documents-collated-copies"
1293 static const char * const overrides
[] =
1294 { /* overrides-supported */
1298 static const char * const print_color_mode_supported
[] =
1299 { /* print-color-mode-supported values */
1304 static const int print_quality_supported
[] =
1305 { /* print-quality-supported values */
1310 static const int pwg_raster_document_resolution_supported
[] =
1316 static const char * const pwg_raster_document_type_supported
[] =
1324 static const char * const reference_uri_schemes_supported
[] =
1325 { /* reference-uri-schemes-supported */
1331 #endif /* HAVE_SSL */
1333 static const char * const sides_supported
[] =
1334 { /* sides-supported values */
1336 "two-sided-long-edge",
1337 "two-sided-short-edge"
1339 static const char * const urf_supported
[] =
1340 { /* urf-supported values */
1343 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1350 static const char * const which_jobs
[] =
1351 { /* which-jobs-supported values */
1360 "processing-stopped"
1365 * Allocate memory for the printer...
1368 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1370 perror("Unable to allocate memory for printer");
1376 printer
->name
= strdup(name
);
1378 printer
->dnssd_name
= strdup(printer
->name
);
1379 #endif /* HAVE_DNSSD */
1380 printer
->command
= command
? strdup(command
) : NULL
;
1381 printer
->directory
= strdup(directory
);
1382 printer
->hostname
= strdup(servername
? servername
: httpGetHostname(NULL
, hostname
, sizeof(hostname
)));
1383 printer
->port
= port
;
1384 printer
->start_time
= time(NULL
);
1385 printer
->config_time
= printer
->start_time
;
1386 printer
->state
= IPP_PSTATE_IDLE
;
1387 printer
->state_reasons
= _IPP_PREASON_NONE
;
1388 printer
->state_time
= printer
->start_time
;
1389 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1390 printer
->next_job_id
= 1;
1392 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1393 printer
->hostname
, printer
->port
, "/ipp/print");
1394 printer
->uri
= strdup(uri
);
1395 printer
->urilen
= strlen(uri
);
1398 printer
->icon
= strdup(icon
);
1400 printer
->main_size
= _IPP_MEDIA_SIZE_A4
;
1401 printer
->main_type
= _IPP_MEDIA_TYPE_STATIONERY
;
1402 printer
->main_level
= 500;
1404 printer
->envelope_size
= _IPP_MEDIA_SIZE_NONE
;
1405 printer
->envelope_level
= 0;
1407 printer
->photo_size
= _IPP_MEDIA_SIZE_NONE
;
1408 printer
->photo_type
= _IPP_MEDIA_TYPE_NONE
;
1409 printer
->photo_level
= 0;
1411 printer
->supplies
[_IPP_SUPPLY_CYAN
] = 100;
1412 printer
->supplies
[_IPP_SUPPLY_MAGENTA
] = 100;
1413 printer
->supplies
[_IPP_SUPPLY_YELLOW
] = 100;
1414 printer
->supplies
[_IPP_SUPPLY_BLACK
] = 100;
1415 printer
->supplies
[_IPP_SUPPLY_WASTE
] = 0;
1417 _cupsRWInit(&(printer
->rwlock
));
1420 * Create the listener sockets...
1423 if ((printer
->ipv4
= create_listener(AF_INET
, &(printer
->port
))) < 0)
1425 perror("Unable to create IPv4 listener");
1429 if ((printer
->ipv6
= create_listener(AF_INET6
, &(printer
->port
))) < 0)
1431 perror("Unable to create IPv6 listener");
1436 * Prepare values for the printer attributes...
1439 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1440 printer
->hostname
, printer
->port
, "/icon.png");
1441 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/");
1442 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/supplies");
1446 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1447 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1448 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1451 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1454 formats
[0] = strdup(docformats
);
1455 defformat
= formats
[0];
1456 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1459 formats
[num_formats
++] = ptr
;
1461 if (!strcasecmp(ptr
, "application/octet-stream"))
1465 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1466 ptr
= device_id
+ strlen(device_id
);
1468 for (i
= 0; i
< num_formats
; i
++)
1470 if (!strcasecmp(formats
[i
], "application/pdf"))
1471 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1472 else if (!strcasecmp(formats
[i
], "application/postscript"))
1473 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1474 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1475 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1476 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1477 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1478 else if (!strcasecmp(formats
[i
], "image/png"))
1479 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1480 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1481 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1486 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
1493 * Get the maximum spool size based on the size of the filesystem used for
1494 * the spool directory. If the host OS doesn't support the statfs call
1495 * or the filesystem is larger than 2TiB, always report INT_MAX.
1499 if (statvfs(printer
->directory
, &spoolinfo
))
1500 k_supported
= INT_MAX
;
1501 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1502 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1503 k_supported
= INT_MAX
;
1505 k_supported
= (int)spoolsize
;
1507 #elif defined(HAVE_STATFS)
1508 if (statfs(printer
->directory
, &spoolinfo
))
1509 k_supported
= INT_MAX
;
1510 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1511 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1512 k_supported
= INT_MAX
;
1514 k_supported
= (int)spoolsize
;
1517 k_supported
= INT_MAX
;
1518 #endif /* HAVE_STATVFS */
1521 * Create the printer attributes. This list of attributes is sorted to improve
1522 * performance when the client provides a requested-attributes attribute...
1525 printer
->attrs
= ippNew();
1527 /* charset-configured */
1528 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1529 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1530 "charset-configured", NULL
, "utf-8");
1532 /* charset-supported */
1533 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1534 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1535 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1538 /* color-supported */
1539 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1542 /* compression-supported */
1543 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1544 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1545 "compression-supported",
1546 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1549 /* copies-default */
1550 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1551 "copies-default", 1);
1553 /* copies-supported */
1554 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1556 /* document-format-default */
1557 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1558 "document-format-default", NULL
, defformat
);
1560 /* document-format-supported */
1561 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1562 "document-format-supported", num_formats
, NULL
,
1563 (const char * const *)formats
);
1565 /* document-password-supported */
1566 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1568 /* finishings-default */
1569 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1570 "finishings-default", IPP_FINISHINGS_NONE
);
1572 /* finishings-supported */
1573 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1574 "finishings-supported", IPP_FINISHINGS_NONE
);
1576 /* generated-natural-language-supported */
1577 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1578 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1579 "generated-natural-language-supported", NULL
, "en");
1581 /* identify-actions-default */
1582 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1584 /* identify-actions-supported */
1585 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
);
1587 /* ipp-features-supported */
1588 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1590 /* ipp-versions-supported */
1591 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1593 /* job-account-id-default */
1594 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1596 /* job-account-id-supported */
1597 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1599 /* job-accounting-user-id-default */
1600 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1602 /* job-accounting-user-id-supported */
1603 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1605 /* job-creation-attributes-supported */
1606 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
);
1608 /* job-ids-supported */
1609 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1611 /* job-k-octets-supported */
1612 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1615 /* job-password-supported */
1616 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1617 "job-password-supported", 4);
1619 /* job-preferred-attributes-supported */
1620 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-preferred-attributes-supported", 0);
1622 /* job-priority-default */
1623 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1624 "job-priority-default", 50);
1626 /* job-priority-supported */
1627 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1628 "job-priority-supported", 100);
1630 /* job-sheets-default */
1631 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1632 IPP_CONST_TAG(IPP_TAG_NAME
),
1633 "job-sheets-default", NULL
, "none");
1635 /* job-sheets-supported */
1636 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1637 IPP_CONST_TAG(IPP_TAG_NAME
),
1638 "job-sheets-supported", NULL
, "none");
1640 /* media-bottom-margin-supported */
1641 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1642 "media-bottom-margin-supported",
1643 (int)(sizeof(media_xxx_margin_supported
) /
1644 sizeof(media_xxx_margin_supported
[0])),
1645 media_xxx_margin_supported
);
1647 /* media-col-database */
1648 for (num_database
= 0, i
= 0;
1649 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1652 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1653 num_database
+= 3; /* auto + manual + envelope */
1654 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1655 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1657 num_database
+= 2; /* Regular + borderless */
1660 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1661 "media-col-database", num_database
,
1663 for (media_col_index
= 0, i
= 0;
1664 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1667 switch (media_col_sizes
[i
][2])
1671 * Regular + borderless for the general class; no source/type
1675 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]));
1676 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]));
1679 case _IPP_ENV_ONLY
:
1681 * Regular margins for "auto", "manual", and "envelope" sources.
1684 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]));
1685 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]));
1686 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]));
1688 case _IPP_PHOTO_ONLY
:
1690 * Photos have specific media types and can only be printed via
1691 * the auto, manual, and photo sources...
1695 j
< (int)(sizeof(media_type_supported
) /
1696 sizeof(media_type_supported
[0]));
1699 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1702 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]));
1703 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]));
1704 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]));
1710 /* media-col-default */
1711 media_col_default
= create_media_col(media_supported
[0],
1712 media_source_supported
[0],
1713 media_type_supported
[0],
1714 media_col_sizes
[0][0],
1715 media_col_sizes
[0][1],
1716 media_xxx_margin_supported
[1]);
1718 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1720 ippDelete(media_col_default
);
1722 /* media-col-supported */
1723 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1724 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1725 "media-col-supported",
1726 (int)(sizeof(media_col_supported
) /
1727 sizeof(media_col_supported
[0])), NULL
,
1728 media_col_supported
);
1731 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1732 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1733 "media-default", NULL
, media_supported
[0]);
1735 /* media-left-margin-supported */
1736 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1737 "media-left-margin-supported",
1738 (int)(sizeof(media_xxx_margin_supported
) /
1739 sizeof(media_xxx_margin_supported
[0])),
1740 media_xxx_margin_supported
);
1742 /* media-right-margin-supported */
1743 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1744 "media-right-margin-supported",
1745 (int)(sizeof(media_xxx_margin_supported
) /
1746 sizeof(media_xxx_margin_supported
[0])),
1747 media_xxx_margin_supported
);
1749 /* media-supported */
1750 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1751 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1753 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1754 NULL
, media_supported
);
1756 /* media-size-supported */
1757 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1758 "media-size-supported",
1759 (int)(sizeof(media_col_sizes
) /
1760 sizeof(media_col_sizes
[0])),
1763 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1765 ippSetCollection(printer
->attrs
, &media_size_supported
, i
,
1766 create_media_size(media_col_sizes
[i
][0],
1767 media_col_sizes
[i
][1]));
1769 /* media-source-supported */
1770 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
);
1772 /* media-top-margin-supported */
1773 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1774 "media-top-margin-supported",
1775 (int)(sizeof(media_xxx_margin_supported
) /
1776 sizeof(media_xxx_margin_supported
[0])),
1777 media_xxx_margin_supported
);
1779 /* media-type-supported */
1780 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
);
1782 /* multiple-document-handling-supported */
1783 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
);
1785 /* multiple-document-jobs-supported */
1786 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1788 /* multiple-operation-time-out */
1789 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1791 /* multiple-operation-time-out-action */
1792 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1794 /* natural-language-configured */
1795 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1796 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1797 "natural-language-configured", NULL
, "en");
1799 /* number-up-default */
1800 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1801 "number-up-default", 1);
1803 /* number-up-supported */
1804 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1805 "number-up-supported", 1);
1807 /* operations-supported */
1808 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1809 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1811 /* orientation-requested-default */
1812 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1813 "orientation-requested-default", 0);
1815 /* orientation-requested-supported */
1816 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1817 "orientation-requested-supported", 4, orients
);
1819 /* output-bin-default */
1820 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1821 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1822 "output-bin-default", NULL
, "face-down");
1824 /* output-bin-supported */
1825 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1826 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1827 "output-bin-supported", NULL
, "face-down");
1829 /* overrides-supported */
1830 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1832 /* page-ranges-supported */
1833 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1835 /* pages-per-minute */
1836 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1837 "pages-per-minute", ppm
);
1839 /* pages-per-minute-color */
1841 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1842 "pages-per-minute-color", ppm_color
);
1844 /* pdl-override-supported */
1845 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1846 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1847 "pdl-override-supported", NULL
, "attempted");
1849 /* print-color-mode-default */
1850 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1852 /* print-color-mode-supported */
1853 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
);
1855 /* print-content-optimize-default */
1856 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1858 /* print-content-optimize-supported */
1859 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
1861 /* print-rendering-intent-default */
1862 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
1864 /* print-rendering-intent-supported */
1865 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
1867 /* print-quality-default */
1868 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
1870 /* print-quality-supported */
1871 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
);
1873 /* printer-device-id */
1874 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1875 "printer-device-id", NULL
, device_id
);
1877 /* printer-get-attributes-supported */
1878 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1880 /* printer-geo-location */
1881 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
1884 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1885 "printer-icons", NULL
, icons
);
1887 /* printer-is-accepting-jobs */
1888 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1892 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1895 /* printer-location */
1896 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1897 "printer-location", NULL
, location
);
1899 /* printer-make-and-model */
1900 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1901 "printer-make-and-model", NULL
, make_model
);
1903 /* printer-mandatory-job-attributes */
1906 static const char * const names
[] =
1908 "job-accounting-user-id",
1912 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1913 "printer-mandatory-job-attributes",
1914 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
1917 /* printer-more-info */
1918 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1921 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1924 /* printer-organization */
1925 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
1927 /* printer-organizational-unit */
1928 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
1930 /* printer-resolution-default */
1931 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1932 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1934 /* printer-resolution-supported */
1935 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1936 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1938 /* printer-supply-description */
1939 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
);
1941 /* printer-supply-info-uri */
1942 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
1944 /* printer-uri-supported */
1945 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
1948 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
1949 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
1951 /* pwg-raster-document-xxx-supported */
1952 for (i
= 0; i
< num_formats
; i
++)
1953 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
1956 if (i
< num_formats
)
1958 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
,
1959 "pwg-raster-document-resolution-supported",
1960 (int)(sizeof(pwg_raster_document_resolution_supported
) /
1961 sizeof(pwg_raster_document_resolution_supported
[0])),
1963 pwg_raster_document_resolution_supported
,
1964 pwg_raster_document_resolution_supported
);
1965 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1966 "pwg-raster-document-sheet-back", NULL
, "normal");
1967 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1968 "pwg-raster-document-type-supported",
1969 (int)(sizeof(pwg_raster_document_type_supported
) /
1970 sizeof(pwg_raster_document_type_supported
[0])), NULL
,
1971 pwg_raster_document_type_supported
);
1974 /* reference-uri-scheme-supported */
1975 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1976 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
1977 "reference-uri-schemes-supported",
1978 (int)(sizeof(reference_uri_schemes_supported
) /
1979 sizeof(reference_uri_schemes_supported
[0])),
1980 NULL
, reference_uri_schemes_supported
);
1983 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1984 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1985 "sides-default", NULL
, "one-sided");
1987 /* sides-supported */
1988 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1989 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1990 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
1993 for (i
= 0; i
< num_formats
; i
++)
1994 if (!strcasecmp(formats
[i
], "image/urf"))
1997 if (i
< num_formats
)
1998 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2000 /* uri-authentication-supported */
2001 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2002 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2003 "uri-authentication-supported", NULL
, "none");
2005 /* uri-security-supported */
2006 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
2007 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2008 "uri-security-supported", NULL
, "none");
2010 /* which-jobs-supported */
2011 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
2012 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2013 "which-jobs-supported",
2014 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
2018 debug_attributes("Printer", printer
->attrs
, 0);
2022 * Register the printer with Bonjour...
2025 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2027 #endif /* HAVE_DNSSD */
2037 * If we get here we were unable to create the printer...
2042 delete_printer(printer
);
2048 * 'debug_attributes()' - Print attributes in a request or response.
2052 debug_attributes(const char *title
, /* I - Title */
2053 ipp_t
*ipp
, /* I - Request/response */
2054 int type
) /* I - 0 = object, 1 = request, 2 = response */
2056 ipp_tag_t group_tag
; /* Current group */
2057 ipp_attribute_t
*attr
; /* Current attribute */
2058 char buffer
[2048]; /* String buffer for value */
2059 int major
, minor
; /* Version */
2065 fprintf(stderr
, "%s:\n", title
);
2066 major
= ippGetVersion(ipp
, &minor
);
2067 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2069 fprintf(stderr
, " operation-id=%s(%04x)\n",
2070 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2072 fprintf(stderr
, " status-code=%s(%04x)\n",
2073 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2074 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2076 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2078 attr
= ippNextAttribute(ipp
))
2080 if (ippGetGroupTag(attr
) != group_tag
)
2082 group_tag
= ippGetGroupTag(attr
);
2083 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2086 if (ippGetName(attr
))
2088 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2089 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2090 ippGetCount(attr
) > 1 ? "1setOf " : "",
2091 ippTagString(ippGetValueTag(attr
)), buffer
);
2098 * 'delete_client()' - Close the socket and free all memory used by a client
2103 delete_client(_ipp_client_t
*client
) /* I - Client */
2106 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2109 * Flush pending writes before closing...
2112 httpFlushWrite(client
->http
);
2118 httpClose(client
->http
);
2120 ippDelete(client
->request
);
2121 ippDelete(client
->response
);
2128 * 'delete_job()' - Remove from the printer and free all memory used by a job
2133 delete_job(_ipp_job_t
*job
) /* I - Job */
2136 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2138 ippDelete(job
->attrs
);
2143 unlink(job
->filename
);
2145 free(job
->filename
);
2153 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2154 * used by a printer object.
2158 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2160 if (printer
->ipv4
>= 0)
2161 close(printer
->ipv4
);
2163 if (printer
->ipv6
>= 0)
2164 close(printer
->ipv6
);
2167 if (printer
->printer_ref
)
2168 DNSServiceRefDeallocate(printer
->printer_ref
);
2170 if (printer
->ipp_ref
)
2171 DNSServiceRefDeallocate(printer
->ipp_ref
);
2174 if (printer
->ipps_ref
)
2175 DNSServiceRefDeallocate(printer
->ipps_ref
);
2176 # endif /* HAVE_SSL */
2177 if (printer
->http_ref
)
2178 DNSServiceRefDeallocate(printer
->http_ref
);
2180 if (printer
->common_ref
)
2181 DNSServiceRefDeallocate(printer
->common_ref
);
2183 TXTRecordDeallocate(&(printer
->ipp_txt
));
2185 if (printer
->dnssd_name
)
2186 free(printer
->dnssd_name
);
2187 #endif /* HAVE_DNSSD */
2190 free(printer
->name
);
2192 free(printer
->icon
);
2193 if (printer
->command
)
2194 free(printer
->command
);
2195 if (printer
->directory
)
2196 free(printer
->directory
);
2197 if (printer
->hostname
)
2198 free(printer
->hostname
);
2202 ippDelete(printer
->attrs
);
2203 cupsArrayDelete(printer
->jobs
);
2211 * 'dnssd_callback()' - Handle Bonjour registration events.
2216 DNSServiceRef sdRef
, /* I - Service reference */
2217 DNSServiceFlags flags
, /* I - Status flags */
2218 DNSServiceErrorType errorCode
, /* I - Error, if any */
2219 const char *name
, /* I - Service name */
2220 const char *regtype
, /* I - Service type */
2221 const char *domain
, /* I - Domain for service */
2222 _ipp_printer_t
*printer
) /* I - Printer */
2230 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2231 regtype
, (int)errorCode
);
2234 else if (strcasecmp(name
, printer
->dnssd_name
))
2237 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2239 /* No lock needed since only the main thread accesses/changes this */
2240 free(printer
->dnssd_name
);
2241 printer
->dnssd_name
= strdup(name
);
2244 #endif /* HAVE_DNSSD */
2248 * 'filter_cb()' - Filter printer attributes based on the requested array.
2251 static int /* O - 1 to copy, 0 to ignore */
2252 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2253 ipp_t
*dst
, /* I - Destination (unused) */
2254 ipp_attribute_t
*attr
) /* I - Source attribute */
2257 * Filter attributes as needed...
2262 ipp_tag_t group
= ippGetGroupTag(attr
);
2263 const char *name
= ippGetName(attr
);
2265 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
)))
2268 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2273 * 'find_job()' - Find a job specified in a request.
2276 static _ipp_job_t
* /* O - Job or NULL */
2277 find_job(_ipp_client_t
*client
) /* I - Client */
2279 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2280 _ipp_job_t key
, /* Job search key */
2281 *job
; /* Matching job, if any */
2284 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2286 const char *uri
= ippGetString(attr
, 0, NULL
);
2288 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2289 uri
[client
->printer
->urilen
] == '/')
2290 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2294 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2295 key
.id
= ippGetInteger(attr
, 0);
2297 _cupsRWLockRead(&(client
->printer
->rwlock
));
2298 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2299 _cupsRWUnlock(&(client
->printer
->rwlock
));
2306 * 'html_escape()' - Write a HTML-safe string.
2310 html_escape(_ipp_client_t
*client
, /* I - Client */
2311 const char *s
, /* I - String to write */
2312 size_t slen
) /* I - Number of characters to write */
2314 const char *start
, /* Start of segment */
2315 *end
; /* End of string */
2319 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2321 while (*s
&& s
< end
)
2323 if (*s
== '&' || *s
== '<')
2326 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2329 httpWrite2(client
->http
, "&", 5);
2331 httpWrite2(client
->http
, "<", 4);
2340 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2345 * 'html_footer()' - Show the web interface footer.
2347 * This function also writes the trailing 0-length chunk.
2351 html_footer(_ipp_client_t
*client
) /* I - Client */
2357 httpWrite2(client
->http
, "", 0);
2362 * 'html_header()' - Show the web interface header and title.
2366 html_header(_ipp_client_t
*client
, /* I - Client */
2367 const char *title
) /* I - Title */
2373 "<title>%s</title>\n"
2374 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2375 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2376 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2377 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2379 "body { font-family: sans-serif; margin: 0; }\n"
2380 "div.body { padding: 0px 10px 10px; }\n"
2381 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2382 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2383 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2384 "table.form th { text-align: right; }\n"
2385 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2386 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2387 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2388 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2389 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2390 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2391 "table.nav td { margin: 0; text-align: center; }\n"
2392 "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"
2393 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2394 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2395 "td.nav:hover { background: #666; color: #fff; }\n"
2396 "td.nav:active { background: #000; color: #ff0; }\n"
2400 "<table class=\"nav\"><tr>"
2401 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2402 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2403 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2405 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2410 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2414 html_printf(_ipp_client_t
*client
, /* I - Client */
2415 const char *format
, /* I - Printf-style format string */
2416 ...) /* I - Additional arguments as needed */
2418 va_list ap
; /* Pointer to arguments */
2419 const char *start
; /* Start of string */
2420 char size
, /* Size character (h, l, L) */
2421 type
; /* Format type character */
2422 int width
, /* Width of field */
2423 prec
; /* Number of characters of precision */
2424 char tformat
[100], /* Temporary format string for sprintf() */
2425 *tptr
, /* Pointer into temporary format */
2426 temp
[1024]; /* Buffer for formatted numbers */
2427 char *s
; /* Pointer to string */
2431 * Loop through the format string, formatting as needed...
2434 va_start(ap
, format
);
2442 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2445 *tptr
++ = *format
++;
2449 httpWrite2(client
->http
, "%", 1);
2454 else if (strchr(" -+#\'", *format
))
2455 *tptr
++ = *format
++;
2460 * Get width from argument...
2464 width
= va_arg(ap
, int);
2466 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2467 tptr
+= strlen(tptr
);
2473 while (isdigit(*format
& 255))
2475 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2478 width
= width
* 10 + *format
++ - '0';
2484 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2492 * Get precision from argument...
2496 prec
= va_arg(ap
, int);
2498 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2499 tptr
+= strlen(tptr
);
2505 while (isdigit(*format
& 255))
2507 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2510 prec
= prec
* 10 + *format
++ - '0';
2515 if (*format
== 'l' && format
[1] == 'l')
2519 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2527 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2529 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2544 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2553 case 'E' : /* Floating point formats */
2558 if ((size_t)(width
+ 2) > sizeof(temp
))
2561 sprintf(temp
, tformat
, va_arg(ap
, double));
2563 httpWrite2(client
->http
, temp
, strlen(temp
));
2566 case 'B' : /* Integer formats */
2574 if ((size_t)(width
+ 2) > sizeof(temp
))
2577 # ifdef HAVE_LONG_LONG
2579 sprintf(temp
, tformat
, va_arg(ap
, long long));
2581 # endif /* HAVE_LONG_LONG */
2583 sprintf(temp
, tformat
, va_arg(ap
, long));
2585 sprintf(temp
, tformat
, va_arg(ap
, int));
2587 httpWrite2(client
->http
, temp
, strlen(temp
));
2590 case 'p' : /* Pointer value */
2591 if ((size_t)(width
+ 2) > sizeof(temp
))
2594 sprintf(temp
, tformat
, va_arg(ap
, void *));
2596 httpWrite2(client
->http
, temp
, strlen(temp
));
2599 case 'c' : /* Character or character array */
2602 temp
[0] = (char)va_arg(ap
, int);
2604 html_escape(client
, temp
, 1);
2607 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2610 case 's' : /* String */
2611 if ((s
= va_arg(ap
, char *)) == NULL
)
2614 html_escape(client
, s
, strlen(s
));
2623 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2630 * 'ipp_cancel_job()' - Cancel a job.
2634 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2636 _ipp_job_t
*job
; /* Job information */
2643 if ((job
= find_job(client
)) == NULL
)
2645 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2650 * See if the job is already completed, canceled, or aborted; if so,
2651 * we can't cancel...
2656 case IPP_JSTATE_CANCELED
:
2657 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2658 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2661 case IPP_JSTATE_ABORTED
:
2662 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2663 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2666 case IPP_JSTATE_COMPLETED
:
2667 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2668 "Job #%d is already completed - can\'t cancel.", job
->id
);
2676 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2678 if (job
->state
== IPP_JSTATE_PROCESSING
||
2679 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2683 job
->state
= IPP_JSTATE_CANCELED
;
2684 job
->completed
= time(NULL
);
2687 _cupsRWUnlock(&(client
->printer
->rwlock
));
2689 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2696 * 'ipp_close_job()' - Close an open job.
2700 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
2702 _ipp_job_t
*job
; /* Job information */
2709 if ((job
= find_job(client
)) == NULL
)
2711 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2716 * See if the job is already completed, canceled, or aborted; if so,
2717 * we can't cancel...
2722 case IPP_JSTATE_CANCELED
:
2723 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2724 "Job #%d is canceled - can\'t close.", job
->id
);
2727 case IPP_JSTATE_ABORTED
:
2728 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2729 "Job #%d is aborted - can\'t close.", job
->id
);
2732 case IPP_JSTATE_COMPLETED
:
2733 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2734 "Job #%d is completed - can\'t close.", job
->id
);
2737 case IPP_JSTATE_PROCESSING
:
2738 case IPP_JSTATE_STOPPED
:
2739 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2740 "Job #%d is already closed.", job
->id
);
2744 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2751 * 'ipp_create_job()' - Create a job object.
2755 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2757 _ipp_job_t
*job
; /* New job */
2758 cups_array_t
*ra
; /* Attributes to send in response */
2762 * Validate print job attributes...
2765 if (!valid_job_attributes(client
))
2767 httpFlush(client
->http
);
2772 * Do we have a file to print?
2775 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2777 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2778 "Unexpected document data following request.");
2786 if ((job
= create_job(client
)) == NULL
)
2788 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2789 "Currently printing another job.");
2794 * Return the job info...
2797 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2799 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2800 cupsArrayAdd(ra
, "job-id");
2801 cupsArrayAdd(ra
, "job-state");
2802 cupsArrayAdd(ra
, "job-state-message");
2803 cupsArrayAdd(ra
, "job-state-reasons");
2804 cupsArrayAdd(ra
, "job-uri");
2806 copy_job_attributes(client
, job
, ra
);
2807 cupsArrayDelete(ra
);
2812 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2816 ipp_get_job_attributes(
2817 _ipp_client_t
*client
) /* I - Client */
2819 _ipp_job_t
*job
; /* Job */
2820 cups_array_t
*ra
; /* requested-attributes */
2823 if ((job
= find_job(client
)) == NULL
)
2825 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2829 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2831 ra
= ippCreateRequestedArray(client
->request
);
2832 copy_job_attributes(client
, job
, ra
);
2833 cupsArrayDelete(ra
);
2838 * 'ipp_get_jobs()' - Get a list of job objects.
2842 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2844 ipp_attribute_t
*attr
; /* Current attribute */
2845 const char *which_jobs
= NULL
;
2846 /* which-jobs values */
2847 int job_comparison
; /* Job comparison */
2848 ipp_jstate_t job_state
; /* job-state value */
2849 int first_job_id
, /* First job ID */
2850 limit
, /* Maximum number of jobs to return */
2851 count
; /* Number of jobs that match */
2852 const char *username
; /* Username */
2853 _ipp_job_t
*job
; /* Current job pointer */
2854 cups_array_t
*ra
; /* Requested attributes array */
2858 * See if the "which-jobs" attribute have been specified...
2861 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2862 IPP_TAG_KEYWORD
)) != NULL
)
2864 which_jobs
= ippGetString(attr
, 0, NULL
);
2865 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
2868 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
2870 job_comparison
= -1;
2871 job_state
= IPP_JSTATE_STOPPED
;
2873 else if (!strcmp(which_jobs
, "completed"))
2876 job_state
= IPP_JSTATE_CANCELED
;
2878 else if (!strcmp(which_jobs
, "aborted"))
2881 job_state
= IPP_JSTATE_ABORTED
;
2883 else if (!strcmp(which_jobs
, "all"))
2886 job_state
= IPP_JSTATE_PENDING
;
2888 else if (!strcmp(which_jobs
, "canceled"))
2891 job_state
= IPP_JSTATE_CANCELED
;
2893 else if (!strcmp(which_jobs
, "pending"))
2896 job_state
= IPP_JSTATE_PENDING
;
2898 else if (!strcmp(which_jobs
, "pending-held"))
2901 job_state
= IPP_JSTATE_HELD
;
2903 else if (!strcmp(which_jobs
, "processing"))
2906 job_state
= IPP_JSTATE_PROCESSING
;
2908 else if (!strcmp(which_jobs
, "processing-stopped"))
2911 job_state
= IPP_JSTATE_STOPPED
;
2915 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
2916 "The which-jobs value \"%s\" is not supported.", which_jobs
);
2917 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2918 "which-jobs", NULL
, which_jobs
);
2923 * See if they want to limit the number of jobs reported...
2926 if ((attr
= ippFindAttribute(client
->request
, "limit",
2927 IPP_TAG_INTEGER
)) != NULL
)
2929 limit
= ippGetInteger(attr
, 0);
2931 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
2936 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2937 IPP_TAG_INTEGER
)) != NULL
)
2939 first_job_id
= ippGetInteger(attr
, 0);
2941 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
2948 * See if we only want to see jobs for a specific user...
2953 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2954 IPP_TAG_BOOLEAN
)) != NULL
)
2956 int my_jobs
= ippGetBoolean(attr
, 0);
2958 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
2959 my_jobs
? "true" : "false");
2963 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
2964 IPP_TAG_NAME
)) == NULL
)
2966 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2967 "Need requesting-user-name with my-jobs.");
2971 username
= ippGetString(attr
, 0, NULL
);
2973 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2974 client
->hostname
, username
);
2979 * OK, build a list of jobs for this printer...
2982 ra
= ippCreateRequestedArray(client
->request
);
2984 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2986 _cupsRWLockRead(&(client
->printer
->rwlock
));
2988 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
2989 (limit
<= 0 || count
< limit
) && job
;
2990 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
2993 * Filter out jobs that don't match...
2996 if ((job_comparison
< 0 && job
->state
> job_state
) ||
2997 (job_comparison
== 0 && job
->state
!= job_state
) ||
2998 (job_comparison
> 0 && job
->state
< job_state
) ||
2999 job
->id
< first_job_id
||
3000 (username
&& job
->username
&&
3001 strcasecmp(username
, job
->username
)))
3005 ippAddSeparator(client
->response
);
3008 copy_job_attributes(client
, job
, ra
);
3011 cupsArrayDelete(ra
);
3013 _cupsRWUnlock(&(client
->printer
->rwlock
));
3018 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3022 ipp_get_printer_attributes(
3023 _ipp_client_t
*client
) /* I - Client */
3025 cups_array_t
*ra
; /* Requested attributes array */
3026 _ipp_printer_t
*printer
; /* Printer */
3030 * Send the attributes...
3033 ra
= ippCreateRequestedArray(client
->request
);
3034 printer
= client
->printer
;
3036 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3038 _cupsRWLockRead(&(printer
->rwlock
));
3040 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3041 IPP_TAG_CUPS_CONST
);
3043 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3045 int i
, /* Looping var */
3046 num_ready
= 0; /* Number of ready media */
3047 ipp_t
*ready
[3]; /* Ready media */
3049 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3051 if (printer
->main_type
!= _IPP_MEDIA_TYPE_NONE
)
3052 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);
3054 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);
3056 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3057 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);
3058 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3060 if (printer
->photo_type
!= _IPP_MEDIA_TYPE_NONE
)
3061 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);
3063 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);
3068 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3069 for (i
= 0; i
< num_ready
; i
++)
3070 ippDelete(ready
[i
]);
3073 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3076 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3078 int num_ready
= 0; /* Number of ready media */
3079 const char *ready
[3]; /* Ready media */
3081 if (printer
->main_size
!= _IPP_MEDIA_SIZE_NONE
)
3082 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3084 if (printer
->envelope_size
!= _IPP_MEDIA_SIZE_NONE
)
3085 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3087 if (printer
->photo_size
!= _IPP_MEDIA_SIZE_NONE
)
3088 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3091 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3093 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3096 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3097 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3099 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3100 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3102 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3103 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3106 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3107 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3108 "printer-state", printer
->state
);
3110 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3111 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3113 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3114 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3116 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3118 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3120 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3123 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3125 if (printer
->state_reasons
== _IPP_PREASON_NONE
)
3126 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3127 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3128 "printer-state-reasons", NULL
, "none");
3131 int num_reasons
= 0;/* Number of reasons */
3132 const char *reasons
[32]; /* Reason strings */
3134 if (printer
->state_reasons
& _IPP_PREASON_OTHER
)
3135 reasons
[num_reasons
++] = "other";
3136 if (printer
->state_reasons
& _IPP_PREASON_COVER_OPEN
)
3137 reasons
[num_reasons
++] = "cover-open";
3138 if (printer
->state_reasons
& _IPP_PREASON_INPUT_TRAY_MISSING
)
3139 reasons
[num_reasons
++] = "input-tray-missing";
3140 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_EMPTY
)
3141 reasons
[num_reasons
++] = "marker-supply-empty-warning";
3142 if (printer
->state_reasons
& _IPP_PREASON_MARKER_SUPPLY_LOW
)
3143 reasons
[num_reasons
++] = "marker-supply-low-report";
3144 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
)
3145 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
3146 if (printer
->state_reasons
& _IPP_PREASON_MARKER_WASTE_FULL
)
3147 reasons
[num_reasons
++] = "marker-waste-full-warning";
3148 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
3149 reasons
[num_reasons
++] = "media-empty-warning";
3150 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_JAM
)
3151 reasons
[num_reasons
++] = "media-jam-warning";
3152 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_LOW
)
3153 reasons
[num_reasons
++] = "media-low-report";
3154 if (printer
->state_reasons
& _IPP_PREASON_MEDIA_NEEDED
)
3155 reasons
[num_reasons
++] = "media-needed-report";
3156 if (printer
->state_reasons
& _IPP_PREASON_MOVING_TO_PAUSED
)
3157 reasons
[num_reasons
++] = "moving-to-paused";
3158 if (printer
->state_reasons
& _IPP_PREASON_PAUSED
)
3159 reasons
[num_reasons
++] = "paused";
3160 if (printer
->state_reasons
& _IPP_PREASON_SPOOL_AREA_FULL
)
3161 reasons
[num_reasons
++] = "spool-area-full";
3162 if (printer
->state_reasons
& _IPP_PREASON_TONER_EMPTY
)
3163 reasons
[num_reasons
++] = "toner-empty-warning";
3164 if (printer
->state_reasons
& _IPP_PREASON_TONER_LOW
)
3165 reasons
[num_reasons
++] = "toner-low-report";
3167 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
3168 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3169 "printer-state-reasons", num_reasons
, NULL
, reasons
);
3173 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3175 int i
; /* Looping var */
3176 char buffer
[256]; /* Supply value buffer */
3177 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3178 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3180 for (i
= 0; i
< 5; i
++)
3182 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
]);
3185 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3187 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3191 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3192 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3194 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3195 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3196 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3198 _cupsRWUnlock(&(printer
->rwlock
));
3200 cupsArrayDelete(ra
);
3205 * 'ipp_identify_printer()' - Beep or display a message.
3209 ipp_identify_printer(
3210 _ipp_client_t
*client
) /* I - Client */
3212 /* TODO: Do something */
3214 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3219 * 'ipp_print_job()' - Create a job object with an attached document.
3223 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3225 _ipp_job_t
*job
; /* New job */
3226 char filename
[1024], /* Filename buffer */
3227 buffer
[4096]; /* Copy buffer */
3228 ssize_t bytes
; /* Bytes read */
3229 cups_array_t
*ra
; /* Attributes to send in response */
3233 * Validate print job attributes...
3236 if (!valid_job_attributes(client
))
3238 httpFlush(client
->http
);
3243 * Do we have a file to print?
3246 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3248 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3256 if ((job
= create_job(client
)) == NULL
)
3258 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3259 "Currently printing another job.");
3264 * Create a file for the request data...
3267 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3270 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3272 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3274 job
->state
= IPP_JSTATE_ABORTED
;
3276 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3277 "Unable to create print file: %s", strerror(errno
));
3281 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3283 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3285 int error
= errno
; /* Write error */
3287 job
->state
= IPP_JSTATE_ABORTED
;
3294 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3295 "Unable to write print file: %s", strerror(error
));
3303 * Got an error while reading the print data, so abort this job.
3306 job
->state
= IPP_JSTATE_ABORTED
;
3313 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3314 "Unable to read print file.");
3320 int error
= errno
; /* Write error */
3322 job
->state
= IPP_JSTATE_ABORTED
;
3327 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3328 "Unable to write print file: %s", strerror(error
));
3333 job
->filename
= strdup(filename
);
3334 job
->state
= IPP_JSTATE_PENDING
;
3337 * Process the job...
3340 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3342 job
->state
= IPP_JSTATE_ABORTED
;
3343 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3348 * Return the job info...
3351 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3353 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3354 cupsArrayAdd(ra
, "job-id");
3355 cupsArrayAdd(ra
, "job-state");
3356 cupsArrayAdd(ra
, "job-state-message");
3357 cupsArrayAdd(ra
, "job-state-reasons");
3358 cupsArrayAdd(ra
, "job-uri");
3360 copy_job_attributes(client
, job
, ra
);
3361 cupsArrayDelete(ra
);
3366 * 'ipp_print_uri()' - Create a job object with a referenced document.
3370 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3372 _ipp_job_t
*job
; /* New job */
3373 ipp_attribute_t
*uri
; /* document-uri */
3374 char scheme
[256], /* URI scheme */
3375 userpass
[256], /* Username and password info */
3376 hostname
[256], /* Hostname */
3377 resource
[1024]; /* Resource path */
3378 int port
; /* Port number */
3379 http_uri_status_t uri_status
; /* URI decode status */
3380 http_encryption_t encryption
; /* Encryption to use, if any */
3381 http_t
*http
; /* Connection for http/https URIs */
3382 http_status_t status
; /* Access status for http/https URIs */
3383 int infile
; /* Input file for local file URIs */
3384 char filename
[1024], /* Filename buffer */
3385 buffer
[4096]; /* Copy buffer */
3386 ssize_t bytes
; /* Bytes read */
3387 cups_array_t
*ra
; /* Attributes to send in response */
3388 static const char * const uri_status_strings
[] =
3389 { /* URI decode errors */
3391 "Bad arguments to function.",
3392 "Bad resource in URI.",
3393 "Bad port number in URI.",
3394 "Bad hostname in URI.",
3395 "Bad username in URI.",
3396 "Bad scheme in URI.",
3402 * Validate print job attributes...
3405 if (!valid_job_attributes(client
))
3407 httpFlush(client
->http
);
3412 * Do we have a file to print?
3415 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3417 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3418 "Unexpected document data following request.");
3423 * Do we have a document URI?
3426 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3427 IPP_TAG_URI
)) == NULL
)
3429 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3433 if (ippGetCount(uri
) != 1)
3435 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3436 "Too many document-uri values.");
3440 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3441 scheme
, sizeof(scheme
), userpass
,
3442 sizeof(userpass
), hostname
, sizeof(hostname
),
3443 &port
, resource
, sizeof(resource
));
3444 if (uri_status
< HTTP_URI_STATUS_OK
)
3446 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3447 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3451 if (strcmp(scheme
, "file") &&
3453 strcmp(scheme
, "https") &&
3454 #endif /* HAVE_SSL */
3455 strcmp(scheme
, "http"))
3457 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3458 "URI scheme \"%s\" not supported.", scheme
);
3462 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3464 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3465 "Unable to access URI: %s", strerror(errno
));
3473 if ((job
= create_job(client
)) == NULL
)
3475 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3476 "Currently printing another job.");
3481 * Create a file for the request data...
3484 if (!strcasecmp(job
->format
, "image/jpeg"))
3485 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3486 client
->printer
->directory
, job
->id
);
3487 else if (!strcasecmp(job
->format
, "image/png"))
3488 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3489 client
->printer
->directory
, job
->id
);
3490 else if (!strcasecmp(job
->format
, "application/pdf"))
3491 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3492 client
->printer
->directory
, job
->id
);
3493 else if (!strcasecmp(job
->format
, "application/postscript"))
3494 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3495 client
->printer
->directory
, job
->id
);
3497 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3498 client
->printer
->directory
, job
->id
);
3500 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3502 job
->state
= IPP_JSTATE_ABORTED
;
3504 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3505 "Unable to create print file: %s", strerror(errno
));
3509 if (!strcmp(scheme
, "file"))
3511 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3513 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3514 "Unable to access URI: %s", strerror(errno
));
3520 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3521 (errno
== EAGAIN
|| errno
== EINTR
))
3523 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3525 int error
= errno
; /* Write error */
3527 job
->state
= IPP_JSTATE_ABORTED
;
3535 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3536 "Unable to write print file: %s", strerror(error
));
3547 if (port
== 443 || !strcmp(scheme
, "https"))
3548 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3550 #endif /* HAVE_SSL */
3551 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3553 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3554 1, 30000, NULL
)) == NULL
)
3556 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3557 "Unable to connect to %s: %s", hostname
,
3558 cupsLastErrorString());
3559 job
->state
= IPP_JSTATE_ABORTED
;
3568 httpClearFields(http
);
3569 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3570 if (httpGet(http
, resource
))
3572 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3573 "Unable to GET URI: %s", strerror(errno
));
3575 job
->state
= IPP_JSTATE_ABORTED
;
3585 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3587 if (status
!= HTTP_STATUS_OK
)
3589 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3590 "Unable to GET URI: %s", httpStatus(status
));
3592 job
->state
= IPP_JSTATE_ABORTED
;
3602 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3604 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3606 int error
= errno
; /* Write error */
3608 job
->state
= IPP_JSTATE_ABORTED
;
3616 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3617 "Unable to write print file: %s", strerror(error
));
3627 int error
= errno
; /* Write error */
3629 job
->state
= IPP_JSTATE_ABORTED
;
3634 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3635 "Unable to write print file: %s", strerror(error
));
3640 job
->filename
= strdup(filename
);
3641 job
->state
= IPP_JSTATE_PENDING
;
3644 * Process the job...
3648 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3650 job
->state
= IPP_JSTATE_ABORTED
;
3651 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3660 * Return the job info...
3663 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3665 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3666 cupsArrayAdd(ra
, "job-id");
3667 cupsArrayAdd(ra
, "job-state");
3668 cupsArrayAdd(ra
, "job-state-reasons");
3669 cupsArrayAdd(ra
, "job-uri");
3671 copy_job_attributes(client
, job
, ra
);
3672 cupsArrayDelete(ra
);
3677 * 'ipp_send_document()' - Add an attached document to a job object created with
3682 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3684 _ipp_job_t
*job
; /* Job information */
3685 char filename
[1024], /* Filename buffer */
3686 buffer
[4096]; /* Copy buffer */
3687 ssize_t bytes
; /* Bytes read */
3688 ipp_attribute_t
*attr
; /* Current attribute */
3689 cups_array_t
*ra
; /* Attributes to send in response */
3696 if ((job
= find_job(client
)) == NULL
)
3698 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3699 httpFlush(client
->http
);
3704 * See if we already have a document for this job or the job has already
3705 * in a non-pending state...
3708 if (job
->state
> IPP_JSTATE_HELD
)
3710 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3711 "Job is not in a pending state.");
3712 httpFlush(client
->http
);
3715 else if (job
->filename
|| job
->fd
>= 0)
3717 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3718 "Multiple document jobs are not supported.");
3719 httpFlush(client
->http
);
3723 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3724 IPP_TAG_ZERO
)) == NULL
)
3726 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3727 "Missing required last-document attribute.");
3728 httpFlush(client
->http
);
3731 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3732 !ippGetBoolean(attr
, 0))
3734 respond_unsupported(client
, attr
);
3735 httpFlush(client
->http
);
3740 * Validate document attributes...
3743 if (!valid_doc_attributes(client
))
3745 httpFlush(client
->http
);
3749 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3752 * Get the document format for the job...
3755 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3757 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3758 job
->format
= ippGetString(attr
, 0, NULL
);
3759 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3760 job
->format
= ippGetString(attr
, 0, NULL
);
3762 job
->format
= "application/octet-stream";
3765 * Create a file for the request data...
3768 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3771 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3773 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3775 _cupsRWUnlock(&(client
->printer
->rwlock
));
3779 job
->state
= IPP_JSTATE_ABORTED
;
3781 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3782 "Unable to create print file: %s", strerror(errno
));
3786 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3788 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3790 int error
= errno
; /* Write error */
3792 job
->state
= IPP_JSTATE_ABORTED
;
3799 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3800 "Unable to write print file: %s", strerror(error
));
3808 * Got an error while reading the print data, so abort this job.
3811 job
->state
= IPP_JSTATE_ABORTED
;
3818 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3819 "Unable to read print file.");
3825 int error
= errno
; /* Write error */
3827 job
->state
= IPP_JSTATE_ABORTED
;
3832 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3833 "Unable to write print file: %s", strerror(error
));
3837 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3840 job
->filename
= strdup(filename
);
3841 job
->state
= IPP_JSTATE_PENDING
;
3843 _cupsRWUnlock(&(client
->printer
->rwlock
));
3846 * Process the job...
3850 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3852 job
->state
= IPP_JSTATE_ABORTED
;
3853 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3862 * Return the job info...
3865 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3867 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3868 cupsArrayAdd(ra
, "job-id");
3869 cupsArrayAdd(ra
, "job-state");
3870 cupsArrayAdd(ra
, "job-state-reasons");
3871 cupsArrayAdd(ra
, "job-uri");
3873 copy_job_attributes(client
, job
, ra
);
3874 cupsArrayDelete(ra
);
3879 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3884 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
3886 _ipp_job_t
*job
; /* Job information */
3887 ipp_attribute_t
*uri
; /* document-uri */
3888 char scheme
[256], /* URI scheme */
3889 userpass
[256], /* Username and password info */
3890 hostname
[256], /* Hostname */
3891 resource
[1024]; /* Resource path */
3892 int port
; /* Port number */
3893 http_uri_status_t uri_status
; /* URI decode status */
3894 http_encryption_t encryption
; /* Encryption to use, if any */
3895 http_t
*http
; /* Connection for http/https URIs */
3896 http_status_t status
; /* Access status for http/https URIs */
3897 int infile
; /* Input file for local file URIs */
3898 char filename
[1024], /* Filename buffer */
3899 buffer
[4096]; /* Copy buffer */
3900 ssize_t bytes
; /* Bytes read */
3901 ipp_attribute_t
*attr
; /* Current attribute */
3902 cups_array_t
*ra
; /* Attributes to send in response */
3903 static const char * const uri_status_strings
[] =
3904 { /* URI decode errors */
3906 "Bad arguments to function.",
3907 "Bad resource in URI.",
3908 "Bad port number in URI.",
3909 "Bad hostname in URI.",
3910 "Bad username in URI.",
3911 "Bad scheme in URI.",
3920 if ((job
= find_job(client
)) == NULL
)
3922 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3923 httpFlush(client
->http
);
3928 * See if we already have a document for this job or the job has already
3929 * in a non-pending state...
3932 if (job
->state
> IPP_JSTATE_HELD
)
3934 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3935 "Job is not in a pending state.");
3936 httpFlush(client
->http
);
3939 else if (job
->filename
|| job
->fd
>= 0)
3941 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3942 "Multiple document jobs are not supported.");
3943 httpFlush(client
->http
);
3947 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3948 IPP_TAG_ZERO
)) == NULL
)
3950 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3951 "Missing required last-document attribute.");
3952 httpFlush(client
->http
);
3955 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3956 !ippGetBoolean(attr
, 0))
3958 respond_unsupported(client
, attr
);
3959 httpFlush(client
->http
);
3964 * Validate document attributes...
3967 if (!valid_doc_attributes(client
))
3969 httpFlush(client
->http
);
3974 * Do we have a file to print?
3977 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3979 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3980 "Unexpected document data following request.");
3985 * Do we have a document URI?
3988 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3989 IPP_TAG_URI
)) == NULL
)
3991 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3995 if (ippGetCount(uri
) != 1)
3997 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3998 "Too many document-uri values.");
4002 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4003 scheme
, sizeof(scheme
), userpass
,
4004 sizeof(userpass
), hostname
, sizeof(hostname
),
4005 &port
, resource
, sizeof(resource
));
4006 if (uri_status
< HTTP_URI_STATUS_OK
)
4008 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4009 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4013 if (strcmp(scheme
, "file") &&
4015 strcmp(scheme
, "https") &&
4016 #endif /* HAVE_SSL */
4017 strcmp(scheme
, "http"))
4019 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4020 "URI scheme \"%s\" not supported.", scheme
);
4024 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4026 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4027 "Unable to access URI: %s", strerror(errno
));
4032 * Get the document format for the job...
4035 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4037 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4038 IPP_TAG_MIMETYPE
)) != NULL
)
4039 job
->format
= ippGetString(attr
, 0, NULL
);
4041 job
->format
= "application/octet-stream";
4044 * Create a file for the request data...
4047 if (!strcasecmp(job
->format
, "image/jpeg"))
4048 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4049 client
->printer
->directory
, job
->id
);
4050 else if (!strcasecmp(job
->format
, "image/png"))
4051 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4052 client
->printer
->directory
, job
->id
);
4053 else if (!strcasecmp(job
->format
, "application/pdf"))
4054 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4055 client
->printer
->directory
, job
->id
);
4056 else if (!strcasecmp(job
->format
, "application/postscript"))
4057 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4058 client
->printer
->directory
, job
->id
);
4060 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4061 client
->printer
->directory
, job
->id
);
4063 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4065 _cupsRWUnlock(&(client
->printer
->rwlock
));
4069 job
->state
= IPP_JSTATE_ABORTED
;
4071 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4072 "Unable to create print file: %s", strerror(errno
));
4076 if (!strcmp(scheme
, "file"))
4078 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4080 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4081 "Unable to access URI: %s", strerror(errno
));
4087 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4088 (errno
== EAGAIN
|| errno
== EINTR
))
4090 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4092 int error
= errno
; /* Write error */
4094 job
->state
= IPP_JSTATE_ABORTED
;
4102 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4103 "Unable to write print file: %s", strerror(error
));
4114 if (port
== 443 || !strcmp(scheme
, "https"))
4115 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4117 #endif /* HAVE_SSL */
4118 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4120 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4121 1, 30000, NULL
)) == NULL
)
4123 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4124 "Unable to connect to %s: %s", hostname
,
4125 cupsLastErrorString());
4126 job
->state
= IPP_JSTATE_ABORTED
;
4135 httpClearFields(http
);
4136 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4137 if (httpGet(http
, resource
))
4139 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4140 "Unable to GET URI: %s", strerror(errno
));
4142 job
->state
= IPP_JSTATE_ABORTED
;
4152 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4154 if (status
!= HTTP_STATUS_OK
)
4156 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4157 "Unable to GET URI: %s", httpStatus(status
));
4159 job
->state
= IPP_JSTATE_ABORTED
;
4169 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4171 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4173 int error
= errno
; /* Write error */
4175 job
->state
= IPP_JSTATE_ABORTED
;
4183 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4184 "Unable to write print file: %s", strerror(error
));
4194 int error
= errno
; /* Write error */
4196 job
->state
= IPP_JSTATE_ABORTED
;
4201 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4202 "Unable to write print file: %s", strerror(error
));
4206 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4209 job
->filename
= strdup(filename
);
4210 job
->state
= IPP_JSTATE_PENDING
;
4212 _cupsRWUnlock(&(client
->printer
->rwlock
));
4215 * Process the job...
4219 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
4221 job
->state
= IPP_JSTATE_ABORTED
;
4222 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
4231 * Return the job info...
4234 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4236 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4237 cupsArrayAdd(ra
, "job-id");
4238 cupsArrayAdd(ra
, "job-state");
4239 cupsArrayAdd(ra
, "job-state-reasons");
4240 cupsArrayAdd(ra
, "job-uri");
4242 copy_job_attributes(client
, job
, ra
);
4243 cupsArrayDelete(ra
);
4248 * 'ipp_validate_job()' - Validate job creation attributes.
4252 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
4254 if (valid_job_attributes(client
))
4255 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4260 * 'parse_options()' - Parse URL options into CUPS options.
4262 * The client->options string is destroyed by this function.
4265 static int /* O - Number of options */
4266 parse_options(_ipp_client_t
*client
, /* I - Client */
4267 cups_option_t
**options
) /* O - Options */
4269 char *name
, /* Name */
4271 *next
; /* Next name=value pair */
4272 int num_options
= 0; /* Number of options */
4277 for (name
= client
->options
; name
&& *name
; name
= next
)
4279 if ((value
= strchr(name
, '=')) == NULL
)
4283 if ((next
= strchr(value
, '&')) != NULL
)
4286 num_options
= cupsAddOption(name
, value
, num_options
, options
);
4289 return (num_options
);
4294 * 'process_client()' - Process client requests on a thread.
4297 static void * /* O - Exit status */
4298 process_client(_ipp_client_t
*client
) /* I - Client */
4301 * Loop until we are out of requests or timeout (30 seconds)...
4305 int first_time
= 1; /* First time request? */
4306 #endif /* HAVE_SSL */
4308 while (httpWait(client
->http
, 30000))
4314 * See if we need to negotiate a TLS connection...
4317 char buf
[1]; /* First byte from client */
4319 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
4321 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
4323 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
4325 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4329 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4334 #endif /* HAVE_SSL */
4336 if (!process_http(client
))
4341 * Close the conection to the client and return...
4344 delete_client(client
);
4351 * 'process_http()' - Process a HTTP request.
4354 int /* O - 1 on success, 0 on failure */
4355 process_http(_ipp_client_t
*client
) /* I - Client connection */
4357 char uri
[1024]; /* URI */
4358 http_state_t http_state
; /* HTTP state */
4359 http_status_t http_status
; /* HTTP status */
4360 ipp_state_t ipp_state
; /* State of IPP transfer */
4361 char scheme
[32], /* Method/scheme */
4362 userpass
[128], /* Username:password */
4363 hostname
[HTTP_MAX_HOST
];
4365 int port
; /* Port number */
4366 const char *encoding
; /* Content-Encoding value */
4367 static const char * const http_states
[] =
4368 { /* Strings for logging HTTP method */
4389 * Clear state variables...
4392 ippDelete(client
->request
);
4393 ippDelete(client
->response
);
4395 client
->request
= NULL
;
4396 client
->response
= NULL
;
4397 client
->operation
= HTTP_STATE_WAITING
;
4400 * Read a request from the connection...
4403 while ((http_state
= httpReadRequest(client
->http
, uri
,
4404 sizeof(uri
))) == HTTP_STATE_WAITING
)
4408 * Parse the request line...
4411 if (http_state
== HTTP_STATE_ERROR
)
4413 if (httpError(client
->http
) == EPIPE
)
4414 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
4416 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
4417 strerror(httpError(client
->http
)));
4421 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
4423 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
4424 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4427 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
4429 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
4430 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4434 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
4438 * Separate the URI into its components...
4441 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4442 userpass
, sizeof(userpass
),
4443 hostname
, sizeof(hostname
), &port
,
4444 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
4445 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
4447 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
4448 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4452 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
4453 *(client
->options
)++ = '\0';
4456 * Process the request...
4459 client
->start
= time(NULL
);
4460 client
->operation
= httpGetState(client
->http
);
4463 * Parse incoming parameters until the status changes...
4466 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4468 if (http_status
!= HTTP_STATUS_OK
)
4470 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4474 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4475 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4478 * HTTP/1.1 and higher require the "Host:" field...
4481 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4486 * Handle HTTP Upgrade...
4489 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4493 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
4495 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
4498 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
4500 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
4502 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4506 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4509 #endif /* HAVE_SSL */
4511 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4516 * Handle HTTP Expect...
4519 if (httpGetExpect(client
->http
) &&
4520 (client
->operation
== HTTP_STATE_POST
||
4521 client
->operation
== HTTP_STATE_PUT
))
4523 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4526 * Send 100-continue header...
4529 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4535 * Send 417-expectation-failed header...
4538 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4544 * Handle new transfers...
4547 encoding
= httpGetContentEncoding(client
->http
);
4549 switch (client
->operation
)
4551 case HTTP_STATE_OPTIONS
:
4553 * Do OPTIONS command...
4556 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4558 case HTTP_STATE_HEAD
:
4559 if (!strcmp(client
->uri
, "/icon.png"))
4560 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4561 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
4562 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4564 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4566 case HTTP_STATE_GET
:
4567 if (!strcmp(client
->uri
, "/icon.png"))
4570 * Send PNG icon file.
4573 int fd
; /* Icon file */
4574 struct stat fileinfo
; /* Icon file information */
4575 char buffer
[4096]; /* Copy buffer */
4576 ssize_t bytes
; /* Bytes */
4578 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
4580 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4581 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4583 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
4584 (size_t)fileinfo
.st_size
))
4590 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4591 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
4593 httpFlushWrite(client
->http
);
4598 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4600 else if (!strcmp(client
->uri
, "/"))
4603 * Show web status page...
4606 _ipp_job_t
*job
; /* Current job */
4607 int i
; /* Looping var */
4608 _ipp_preason_t reason
; /* Current reason */
4609 static const char * const reasons
[] =
4610 { /* Reason strings */
4613 "Input Tray Missing",
4614 "Marker Supply Empty",
4615 "Marker Supply Low",
4616 "Marker Waste Almost Full",
4617 "Marker Waste Full",
4629 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4632 html_header(client
, client
->printer
->name
);
4634 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
4635 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
4636 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
4637 if (client
->printer
->state_reasons
& reason
)
4638 html_printf(client
, "\n<br> %s", reasons
[i
]);
4639 html_printf(client
, "</p>\n");
4641 if (cupsArrayCount(client
->printer
->jobs
) > 0)
4643 _cupsRWLockRead(&(client
->printer
->rwlock
));
4645 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");
4646 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
4648 char when
[256], /* When job queued/started/finished */
4649 hhmmss
[64]; /* Time HH:MM:SS */
4653 case IPP_JSTATE_PENDING
:
4654 case IPP_JSTATE_HELD
:
4655 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
4657 case IPP_JSTATE_PROCESSING
:
4658 case IPP_JSTATE_STOPPED
:
4659 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
4661 case IPP_JSTATE_ABORTED
:
4662 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4664 case IPP_JSTATE_CANCELED
:
4665 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4667 case IPP_JSTATE_COMPLETED
:
4668 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
4672 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
);
4674 html_printf(client
, "</tbody></table>\n");
4676 _cupsRWUnlock(&(client
->printer
->rwlock
));
4678 html_footer(client
);
4682 else if (!strcmp(client
->uri
, "/media"))
4685 * Show web media page...
4688 int i
, /* Looping var */
4689 num_options
; /* Number of form options */
4690 cups_option_t
*options
; /* Form options */
4691 static const char * const sizes
[] =
4692 { /* Size strings */
4705 static const char * const types
[] =
4722 static const int sheets
[] = /* Number of sheets */
4731 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4734 html_header(client
, client
->printer
->name
);
4736 if ((num_options
= parse_options(client
, &options
)) > 0)
4739 * WARNING: A real printer/server implementation MUST NOT implement
4740 * media updates via a GET request - GET requests are supposed to be
4741 * idempotent (without side-effects) and we obviously are not
4742 * authenticating access here. This form is provided solely to
4743 * enable testing and development!
4746 const char *val
; /* Form value */
4748 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
4749 client
->printer
->main_size
= atoi(val
);
4750 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
4751 client
->printer
->main_type
= atoi(val
);
4752 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
4753 client
->printer
->main_level
= atoi(val
);
4755 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
4756 client
->printer
->envelope_size
= atoi(val
);
4757 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
4758 client
->printer
->envelope_level
= atoi(val
);
4760 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
4761 client
->printer
->photo_size
= atoi(val
);
4762 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
4763 client
->printer
->photo_type
= atoi(val
);
4764 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
4765 client
->printer
->photo_level
= atoi(val
);
4767 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))
4768 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
4770 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
4772 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
))
4774 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
4775 if (client
->printer
->active_job
)
4776 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
4779 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
4781 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
4784 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
4786 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
4787 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
4788 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4789 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
4790 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
4791 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
4792 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4793 if (!strstr(types
[i
], "Photo"))
4794 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
4795 html_printf(client
, "</select> <select name=\"main_level\">");
4796 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4797 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
4798 html_printf(client
, "</select></td></tr>\n");
4801 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
4802 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4803 if (strstr(sizes
[i
], "Envelope"))
4804 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
4805 html_printf(client
, "</select> <select name=\"envelope_level\">");
4806 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4807 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
4808 html_printf(client
, "</select></td></tr>\n");
4811 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
4812 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
4813 if (strstr(sizes
[i
], "Photo"))
4814 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
4815 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
4816 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
4817 if (strstr(types
[i
], "Photo"))
4818 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
4819 html_printf(client
, "</select> <select name=\"photo_level\">");
4820 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
4821 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
4822 html_printf(client
, "</select></td></tr>\n");
4824 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
4825 html_footer(client
);
4829 else if (!strcmp(client
->uri
, "/supplies"))
4832 * Show web supplies page...
4835 int i
, j
, /* Looping vars */
4836 num_options
; /* Number of form options */
4837 cups_option_t
*options
; /* Form options */
4838 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
4840 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4843 html_header(client
, client
->printer
->name
);
4845 if ((num_options
= parse_options(client
, &options
)) > 0)
4848 * WARNING: A real printer/server implementation MUST NOT implement
4849 * supply updates via a GET request - GET requests are supposed to be
4850 * idempotent (without side-effects) and we obviously are not
4851 * authenticating access here. This form is provided solely to
4852 * enable testing and development!
4855 char name
[64]; /* Form field */
4856 const char *val
; /* Form value */
4858 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
);
4860 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
4862 snprintf(name
, sizeof(name
), "supply_%d", i
);
4863 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
4865 int level
= client
->printer
->supplies
[i
] = atoi(val
);
4871 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
4872 else if (level
< 10)
4873 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
4878 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
4879 else if (level
> 90)
4880 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
4885 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
4888 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
4890 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
4891 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
4893 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
4894 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
4895 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
4896 html_printf(client
, "</select></td></tr>\n");
4898 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
4899 html_footer(client
);
4904 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4907 case HTTP_STATE_POST
:
4908 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
4912 * Not an IPP request...
4915 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
4919 * Read the IPP request...
4922 client
->request
= ippNew();
4924 while ((ipp_state
= ippRead(client
->http
,
4925 client
->request
)) != IPP_STATE_DATA
)
4927 if (ipp_state
== IPP_STATE_ERROR
)
4929 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
4930 cupsLastErrorString());
4931 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4937 * Now that we have the IPP request, process the request...
4940 return (process_ipp(client
));
4943 break; /* Anti-compiler-warning-code */
4951 * 'process_ipp()' - Process an IPP request.
4954 static int /* O - 1 on success, 0 on error */
4955 process_ipp(_ipp_client_t
*client
) /* I - Client */
4957 ipp_tag_t group
; /* Current group tag */
4958 ipp_attribute_t
*attr
; /* Current attribute */
4959 ipp_attribute_t
*charset
; /* Character set attribute */
4960 ipp_attribute_t
*language
; /* Language attribute */
4961 ipp_attribute_t
*uri
; /* Printer URI attribute */
4962 int major
, minor
; /* Version number */
4963 const char *name
; /* Name of attribute */
4966 debug_attributes("Request", client
->request
, 1);
4969 * First build an empty response message for this request...
4972 client
->operation_id
= ippGetOperation(client
->request
);
4973 client
->response
= ippNewResponse(client
->request
);
4976 * Then validate the request header and required attributes...
4979 major
= ippGetVersion(client
->request
, &minor
);
4981 if (major
< 1 || major
> 2)
4984 * Return an error, since we only support IPP 1.x and 2.x.
4987 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
4988 "Bad request version number %d.%d.", major
, minor
);
4990 else if (ippGetRequestId(client
->request
) <= 0)
4991 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
4992 ippGetRequestId(client
->request
));
4993 else if (!ippFirstAttribute(client
->request
))
4994 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4995 "No attributes in request.");
4999 * Make sure that the attributes are provided in the correct order and
5000 * don't repeat groups...
5003 for (attr
= ippFirstAttribute(client
->request
),
5004 group
= ippGetGroupTag(attr
);
5006 attr
= ippNextAttribute(client
->request
))
5008 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5011 * Out of order; return an error...
5014 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5015 "Attribute groups are out of order (%x < %x).",
5016 ippGetGroupTag(attr
), group
);
5020 group
= ippGetGroupTag(attr
);
5026 * Then make sure that the first three attributes are:
5028 * attributes-charset
5029 * attributes-natural-language
5030 * printer-uri/job-uri
5033 attr
= ippFirstAttribute(client
->request
);
5034 name
= ippGetName(attr
);
5035 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5036 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5041 attr
= ippNextAttribute(client
->request
);
5042 name
= ippGetName(attr
);
5044 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5045 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5050 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5051 IPP_TAG_URI
)) != NULL
)
5053 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5054 IPP_TAG_URI
)) != NULL
)
5060 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5061 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5064 * Bad character set...
5067 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5068 "Unsupported character set \"%s\".",
5069 ippGetString(charset
, 0, NULL
));
5071 else if (!charset
|| !language
|| !uri
)
5074 * Return an error, since attributes-charset,
5075 * attributes-natural-language, and printer-uri/job-uri are required
5076 * for all operations.
5079 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5080 "Missing required attributes.");
5084 char scheme
[32], /* URI scheme */
5085 userpass
[32], /* Username/password in URI */
5086 host
[256], /* Host name in URI */
5087 resource
[256]; /* Resource path in URI */
5088 int port
; /* Port number in URI */
5090 name
= ippGetName(uri
);
5092 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5093 scheme
, sizeof(scheme
),
5094 userpass
, sizeof(userpass
),
5095 host
, sizeof(host
), &port
,
5096 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5097 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5098 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5099 else if ((!strcmp(name
, "job-uri") &&
5100 strncmp(resource
, "/ipp/print/", 11)) ||
5101 (!strcmp(name
, "printer-uri") &&
5102 strcmp(resource
, "/ipp/print")))
5103 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5104 name
, ippGetString(uri
, 0, NULL
));
5108 * Try processing the operation...
5111 switch (ippGetOperation(client
->request
))
5113 case IPP_OP_PRINT_JOB
:
5114 ipp_print_job(client
);
5117 case IPP_OP_PRINT_URI
:
5118 ipp_print_uri(client
);
5121 case IPP_OP_VALIDATE_JOB
:
5122 ipp_validate_job(client
);
5125 case IPP_OP_CREATE_JOB
:
5126 ipp_create_job(client
);
5129 case IPP_OP_SEND_DOCUMENT
:
5130 ipp_send_document(client
);
5133 case IPP_OP_SEND_URI
:
5134 ipp_send_uri(client
);
5137 case IPP_OP_CANCEL_JOB
:
5138 ipp_cancel_job(client
);
5141 case IPP_OP_GET_JOB_ATTRIBUTES
:
5142 ipp_get_job_attributes(client
);
5145 case IPP_OP_GET_JOBS
:
5146 ipp_get_jobs(client
);
5149 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5150 ipp_get_printer_attributes(client
);
5153 case IPP_OP_CLOSE_JOB
:
5154 ipp_close_job(client
);
5157 case IPP_OP_IDENTIFY_PRINTER
:
5158 ipp_identify_printer(client
);
5162 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5163 "Operation not supported.");
5172 * Send the HTTP header and return...
5175 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5176 httpFlush(client
->http
); /* Flush trailing (junk) data */
5178 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5179 ippLength(client
->response
)));
5184 * 'process_job()' - Process a print job.
5187 static void * /* O - Thread exit status */
5188 process_job(_ipp_job_t
*job
) /* I - Job */
5190 job
->state
= IPP_JSTATE_PROCESSING
;
5191 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
5192 job
->processing
= time(NULL
);
5194 while (job
->printer
->state_reasons
& _IPP_PREASON_MEDIA_EMPTY
)
5196 job
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5201 job
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_NEEDED
;
5203 if (job
->printer
->command
)
5206 * Execute a command with the job spool file and wait for it to complete...
5209 int pid
, /* Process ID */
5210 status
; /* Exit status */
5211 time_t start
, /* Start time */
5214 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
5218 if ((pid
= fork()) == 0)
5221 * Child comes here...
5224 execlp(job
->printer
->command
, job
->printer
->command
, job
->filename
,
5231 * Unable to fork process...
5234 perror("Unable to start job processing command");
5239 * Wait for child to complete...
5243 while (waitpid(pid
, &status
, 0) < 0);
5245 while (wait(&status
) < 0);
5246 #endif /* HAVE_WAITPID */
5250 if (WIFEXITED(status
))
5251 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
5252 job
->printer
->command
, WEXITSTATUS(status
));
5254 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
5255 job
->printer
->command
, WTERMSIG(status
));
5257 job
->state
= IPP_JSTATE_ABORTED
;
5260 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
5261 job
->printer
->command
);
5265 * Make sure processing takes at least 5 seconds...
5269 if ((end
- start
) < 5)
5275 * Sleep for a random amount of time to simulate job processing.
5278 sleep((unsigned)(5 + (rand() % 11)));
5282 job
->state
= IPP_JSTATE_CANCELED
;
5283 else if (job
->state
== IPP_JSTATE_PROCESSING
)
5284 job
->state
= IPP_JSTATE_COMPLETED
;
5286 job
->completed
= time(NULL
);
5287 job
->printer
->state
= IPP_PSTATE_IDLE
;
5288 job
->printer
->active_job
= NULL
;
5296 * 'register_printer()' - Register a printer object via Bonjour.
5299 static int /* O - 1 on success, 0 on error */
5301 _ipp_printer_t
*printer
, /* I - Printer */
5302 const char *location
, /* I - Location */
5303 const char *make
, /* I - Manufacturer */
5304 const char *model
, /* I - Model name */
5305 const char *formats
, /* I - Supported formats */
5306 const char *adminurl
, /* I - Web interface URL */
5307 const char *uuid
, /* I - Printer UUID */
5308 int color
, /* I - 1 = color, 0 = monochrome */
5309 int duplex
, /* I - 1 = duplex, 0 = simplex */
5310 const char *subtype
) /* I - Service subtype */
5312 DNSServiceErrorType error
; /* Error from Bonjour */
5313 char make_model
[256],/* Make and model together */
5314 product
[256], /* Product string */
5315 regtype
[256]; /* Bonjour service type */
5319 * Build the TXT record for IPP...
5322 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
5323 snprintf(product
, sizeof(product
), "(%s)", model
);
5325 TXTRecordCreate(&(printer
->ipp_txt
), 1024, NULL
);
5326 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 9, "ipp/print");
5327 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
5329 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
5332 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
5334 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
5336 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
5338 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
5339 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
5340 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
5342 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
5344 TXTRecordSetValue(&(printer
->ipp_txt
), "UUID", (uint8_t)strlen(uuid
), uuid
);
5346 TXTRecordSetValue(&(printer
->ipp_txt
), "TLS", 3, "1.2");
5347 # endif /* HAVE_SSL */
5350 * Create a shared service reference for Bonjour...
5353 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
5354 != kDNSServiceErr_NoError
)
5356 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
5361 * Register the _printer._tcp (LPD) service type with a port number of 0 to
5362 * defend our service name but not actually support LPD...
5365 printer
->printer_ref
= printer
->common_ref
;
5367 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
5368 kDNSServiceFlagsShareConnection
,
5369 0 /* interfaceIndex */, printer
->dnssd_name
,
5370 "_printer._tcp", NULL
/* domain */,
5371 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
5372 NULL
/* txtRecord */,
5373 (DNSServiceRegisterReply
)dnssd_callback
,
5374 printer
)) != kDNSServiceErr_NoError
)
5376 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
5377 printer
->dnssd_name
, error
);
5382 * Then register the _ipp._tcp (IPP) service type with the real port number to
5383 * advertise our IPP printer...
5386 printer
->ipp_ref
= printer
->common_ref
;
5388 if (subtype
&& *subtype
)
5389 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
5392 strncpy(regtype
, "_ipp._tcp", sizeof(regtype
) - 1);
5393 regtype
[sizeof(regtype
) - 1] = '\0';
5396 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
5397 kDNSServiceFlagsShareConnection
,
5398 0 /* interfaceIndex */, printer
->dnssd_name
,
5399 regtype
, NULL
/* domain */,
5400 NULL
/* host */, htons(printer
->port
),
5401 TXTRecordGetLength(&(printer
->ipp_txt
)),
5402 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
5403 (DNSServiceRegisterReply
)dnssd_callback
,
5404 printer
)) != kDNSServiceErr_NoError
)
5406 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5407 printer
->dnssd_name
, regtype
, error
);
5413 * Then register the _ipps._tcp (IPP) service type with the real port number to
5414 * advertise our IPP printer...
5417 printer
->ipps_ref
= printer
->common_ref
;
5419 if (subtype
&& *subtype
)
5420 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
5423 strncpy(regtype
, "_ipps._tcp", sizeof(regtype
) - 1);
5424 regtype
[sizeof(regtype
) - 1] = '\0';
5427 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
5428 kDNSServiceFlagsShareConnection
,
5429 0 /* interfaceIndex */, printer
->dnssd_name
,
5430 regtype
, NULL
/* domain */,
5431 NULL
/* host */, htons(printer
->port
),
5432 TXTRecordGetLength(&(printer
->ipp_txt
)),
5433 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
5434 (DNSServiceRegisterReply
)dnssd_callback
,
5435 printer
)) != kDNSServiceErr_NoError
)
5437 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5438 printer
->dnssd_name
, regtype
, error
);
5441 # endif /* HAVE_SSL */
5444 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
5445 * real port number to advertise our IPP printer...
5448 printer
->http_ref
= printer
->common_ref
;
5450 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
5451 kDNSServiceFlagsShareConnection
,
5452 0 /* interfaceIndex */, printer
->dnssd_name
,
5453 "_http._tcp,_printer", NULL
/* domain */,
5454 NULL
/* host */, htons(printer
->port
),
5455 0 /* txtLen */, NULL
, /* txtRecord */
5456 (DNSServiceRegisterReply
)dnssd_callback
,
5457 printer
)) != kDNSServiceErr_NoError
)
5459 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
5460 printer
->dnssd_name
, regtype
, error
);
5466 #endif /* HAVE_DNSSD */
5470 * 'respond_http()' - Send a HTTP response.
5473 int /* O - 1 on success, 0 on failure */
5475 _ipp_client_t
*client
, /* I - Client */
5476 http_status_t code
, /* I - HTTP status of response */
5477 const char *content_encoding
, /* I - Content-Encoding of response */
5478 const char *type
, /* I - MIME media type of response */
5479 size_t length
) /* I - Length of response */
5481 char message
[1024]; /* Text message */
5484 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
5486 if (code
== HTTP_STATUS_CONTINUE
)
5489 * 100-continue doesn't send any headers...
5492 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
5496 * Format an error message...
5499 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
5501 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
5503 type
= "text/plain";
5504 length
= strlen(message
);
5510 * Send the HTTP response header...
5513 httpClearFields(client
->http
);
5515 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
5516 client
->operation
== HTTP_STATE_OPTIONS
)
5517 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
5521 if (!strcmp(type
, "text/html"))
5522 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
5523 "text/html; charset=utf-8");
5525 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
5527 if (content_encoding
)
5528 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
5531 httpSetLength(client
->http
, length
);
5533 if (httpWriteResponse(client
->http
, code
) < 0)
5537 * Send the response data...
5543 * Send a plain text message.
5546 if (httpPrintf(client
->http
, "%s", message
) < 0)
5549 if (httpWrite2(client
->http
, "", 0) < 0)
5552 else if (client
->response
)
5555 * Send an IPP response...
5558 debug_attributes("Response", client
->response
, 2);
5560 ippSetState(client
->response
, IPP_STATE_IDLE
);
5562 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
5571 * 'respond_ipp()' - Send an IPP response.
5575 respond_ipp(_ipp_client_t
*client
, /* I - Client */
5576 ipp_status_t status
, /* I - status-code */
5577 const char *message
, /* I - printf-style status-message */
5578 ...) /* I - Additional args as needed */
5580 const char *formatted
= NULL
; /* Formatted message */
5583 ippSetStatusCode(client
->response
, status
);
5587 va_list ap
; /* Pointer to additional args */
5588 ipp_attribute_t
*attr
; /* New status-message attribute */
5590 va_start(ap
, message
);
5591 if ((attr
= ippFindAttribute(client
->response
, "status-message",
5592 IPP_TAG_TEXT
)) != NULL
)
5593 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
5595 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
5596 "status-message", NULL
, message
, ap
);
5599 formatted
= ippGetString(attr
, 0, NULL
);
5603 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
5604 ippOpString(client
->operation_id
), ippErrorString(status
),
5607 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
5608 ippOpString(client
->operation_id
), ippErrorString(status
));
5613 * 'respond_unsupported()' - Respond with an unsupported attribute.
5617 respond_unsupported(
5618 _ipp_client_t
*client
, /* I - Client */
5619 ipp_attribute_t
*attr
) /* I - Atribute */
5621 ipp_attribute_t
*temp
; /* Copy of attribute */
5624 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5625 "Unsupported %s %s%s value.", ippGetName(attr
),
5626 ippGetCount(attr
) > 1 ? "1setOf " : "",
5627 ippTagString(ippGetValueTag(attr
)));
5629 temp
= ippCopyAttribute(client
->response
, attr
, 0);
5630 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
5635 * 'run_printer()' - Run the printer service.
5639 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
5641 int num_fds
; /* Number of file descriptors */
5642 struct pollfd polldata
[3]; /* poll() data */
5643 int timeout
; /* Timeout for poll() */
5644 _ipp_client_t
*client
; /* New client */
5648 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5651 polldata
[0].fd
= printer
->ipv4
;
5652 polldata
[0].events
= POLLIN
;
5654 polldata
[1].fd
= printer
->ipv6
;
5655 polldata
[1].events
= POLLIN
;
5660 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
5661 polldata
[num_fds
++].events
= POLLIN
;
5662 #endif /* HAVE_DNSSD */
5665 * Loop until we are killed or have a hard error...
5670 if (cupsArrayCount(printer
->jobs
))
5675 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
5677 perror("poll() failed");
5681 if (polldata
[0].revents
& POLLIN
)
5683 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
5685 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5687 perror("Unable to create client thread");
5688 delete_client(client
);
5693 if (polldata
[1].revents
& POLLIN
)
5695 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
5697 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5699 perror("Unable to create client thread");
5700 delete_client(client
);
5706 if (polldata
[2].revents
& POLLIN
)
5707 DNSServiceProcessResult(printer
->common_ref
);
5708 #endif /* HAVE_DNSSD */
5711 * Clean out old jobs...
5714 clean_jobs(printer
);
5720 * 'time_string()' - Return the local time in hours, minutes, and seconds.
5724 time_string(time_t tv
, /* I - Time value */
5725 char *buffer
, /* I - Buffer */
5726 size_t bufsize
) /* I - Size of buffer */
5728 struct tm
*curtime
= localtime(&tv
);
5731 strftime(buffer
, bufsize
, "%X", curtime
);
5737 * 'usage()' - Show program usage.
5741 usage(int status
) /* O - Exit status */
5745 puts(CUPS_SVERSION
" - Copyright 2010-2013 by Apple Inc. All rights "
5750 puts("Usage: ippserver [options] \"name\"");
5753 puts("-2 Supports 2-sided printing (default=1-sided)");
5754 puts("-M manufacturer Manufacturer name (default=Test)");
5755 puts("-P PIN printing mode");
5756 puts("-c command Run command for every print job");
5757 printf("-d spool-directory Spool directory "
5758 "(default=/tmp/ippserver.%d)\n", (int)getpid());
5759 puts("-f type/subtype[,...] List of supported types "
5760 "(default=application/pdf,image/jpeg)");
5761 puts("-h Show program help");
5762 puts("-i iconfile.png PNG icon file (default=printer.png)");
5763 puts("-k Keep job spool files");
5764 puts("-l location Location of printer (default=empty string)");
5765 puts("-m model Model name (default=Printer)");
5766 puts("-n hostname Hostname for printer");
5767 puts("-p port Port number (default=auto)");
5769 puts("-r subtype Bonjour service subtype (default=_print)");
5770 #endif /* HAVE_DNSSD */
5771 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
5772 puts("-v[vvv] Be (very) verbose");
5779 * 'valid_doc_attributes()' - Determine whether the document attributes are
5782 * When one or more document attributes are invalid, this function adds a
5783 * suitable response and attributes to the unsupported group.
5786 static int /* O - 1 if valid, 0 if not */
5787 valid_doc_attributes(
5788 _ipp_client_t
*client
) /* I - Client */
5790 int valid
= 1; /* Valid attributes? */
5791 ipp_op_t op
= ippGetOperation(client
->request
);
5793 const char *op_name
= ippOpString(op
);
5794 /* IPP operation name */
5795 ipp_attribute_t
*attr
, /* Current attribute */
5796 *supported
; /* xxx-supported attribute */
5797 const char *compression
= NULL
,
5798 /* compression value */
5799 *format
= NULL
; /* document-format value */
5803 * Check operation attributes...
5806 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
5809 * If compression is specified, only accept a supported value in a Print-Job
5810 * or Send-Document request...
5813 compression
= ippGetString(attr
, 0, NULL
);
5814 supported
= ippFindAttribute(client
->printer
->attrs
,
5815 "compression-supported", IPP_TAG_KEYWORD
);
5817 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5818 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
5819 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
5820 op
!= IPP_OP_VALIDATE_JOB
) ||
5821 !ippContainsString(supported
, compression
))
5823 respond_unsupported(client
, attr
);
5828 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
5830 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
5832 if (strcmp(compression
, "none"))
5835 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
5836 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
5842 * Is it a format we support?
5845 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
5847 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
5848 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
5850 respond_unsupported(client
, attr
);
5855 format
= ippGetString(attr
, 0, NULL
);
5857 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
5858 client
->hostname
, op_name
, format
);
5860 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
5865 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
5867 format
= "application/octet-stream"; /* Should never happen */
5869 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
5872 if (!strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
5875 * Auto-type the file using the first 8 bytes of the file...
5878 unsigned char header
[8]; /* First 8 bytes of file */
5880 memset(header
, 0, sizeof(header
));
5881 httpPeek(client
->http
, (char *)header
, sizeof(header
));
5883 if (!memcmp(header
, "%PDF", 4))
5884 format
= "application/pdf";
5885 else if (!memcmp(header
, "%!", 2))
5886 format
= "application/postscript";
5887 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
5888 format
= "image/jpeg";
5889 else if (!memcmp(header
, "\211PNG", 4))
5890 format
= "image/png";
5891 else if (!memcmp(header
, "RAS2", 4))
5892 format
= "image/pwg-raster";
5893 else if (!memcmp(header
, "UNIRAST", 8))
5894 format
= "image/urf";
5900 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
5901 client
->hostname
, op_name
, format
);
5903 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
5907 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
5909 respond_unsupported(client
, attr
);
5917 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
5918 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
5925 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
5927 * When one or more job attributes are invalid, this function adds a suitable
5928 * response and attributes to the unsupported group.
5931 static int /* O - 1 if valid, 0 if not */
5932 valid_job_attributes(
5933 _ipp_client_t
*client
) /* I - Client */
5935 int i
, /* Looping var */
5936 valid
= 1; /* Valid attributes? */
5937 ipp_attribute_t
*attr
, /* Current attribute */
5938 *supported
; /* xxx-supported attribute */
5942 * Check operation attributes...
5945 valid
= valid_doc_attributes(client
);
5948 * Check the various job template attributes...
5951 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
5953 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5954 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
5956 respond_unsupported(client
, attr
);
5961 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
5963 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
5965 respond_unsupported(client
, attr
);
5970 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
5972 if (ippGetCount(attr
) != 1 ||
5973 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5974 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5975 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5976 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
5978 respond_unsupported(client
, attr
);
5983 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
5985 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
5987 respond_unsupported(client
, attr
);
5992 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
5994 if (ippGetCount(attr
) != 1 ||
5995 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5996 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
5998 respond_unsupported(client
, attr
);
6002 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
6005 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
6007 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
6009 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6010 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
6012 respond_unsupported(client
, attr
);
6017 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
6019 if (ippGetCount(attr
) != 1 ||
6020 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6021 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6022 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6023 strcmp(ippGetString(attr
, 0, NULL
), "none"))
6025 respond_unsupported(client
, attr
);
6030 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
6032 if (ippGetCount(attr
) != 1 ||
6033 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6034 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6035 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
6037 respond_unsupported(client
, attr
);
6043 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
6045 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
6048 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
6050 respond_unsupported(client
, attr
);
6056 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
6058 if (ippGetCount(attr
) != 1 ||
6059 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
6061 respond_unsupported(client
, attr
);
6064 /* TODO: check for valid media-col */
6067 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
6069 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6070 (strcmp(ippGetString(attr
, 0, NULL
),
6071 "separate-documents-uncollated-copies") &&
6072 strcmp(ippGetString(attr
, 0, NULL
),
6073 "separate-documents-collated-copies")))
6075 respond_unsupported(client
, attr
);
6080 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
6082 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6083 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
6084 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
6086 respond_unsupported(client
, attr
);
6091 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
6093 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
6095 respond_unsupported(client
, attr
);
6100 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
6102 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6103 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
6104 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
6106 respond_unsupported(client
, attr
);
6111 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
6113 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
6115 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
6118 respond_unsupported(client
, attr
);
6123 int count
, /* Number of supported values */
6124 xdpi
, /* Horizontal resolution for job template attribute */
6125 ydpi
, /* Vertical resolution for job template attribute */
6126 sydpi
; /* Vertical resolution for supported value */
6127 ipp_res_t units
, /* Units for job template attribute */
6128 sunits
; /* Units for supported value */
6130 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
6131 count
= ippGetCount(supported
);
6133 for (i
= 0; i
< count
; i
++)
6135 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
6141 respond_unsupported(client
, attr
);
6147 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
6149 const char *sides
= ippGetString(attr
, 0, NULL
);
6150 /* "sides" value... */
6152 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
6154 respond_unsupported(client
, attr
);
6157 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
6159 if (!ippContainsString(supported
, sides
))
6161 respond_unsupported(client
, attr
);
6165 else if (strcmp(sides
, "one-sided"))
6167 respond_unsupported(client
, attr
);