4 * Sample IPP/2.0 server for CUPS.
6 * Copyright 2010-2013 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 * main() - Main entry to the sample server.
19 * clean_jobs() - Clean out old (completed) jobs.
20 * compare_jobs() - Compare two jobs.
21 * copy_attributes() - Copy attributes from one request to
23 * copy_job_attrs() - Copy job attributes to the response.
24 * create_client() - Accept a new network connection and create
26 * create_job() - Create a new job object from a Print-Job or
28 * create_listener() - Create a listener socket.
29 * create_media_col() - Create a media-col value.
30 * create_printer() - Create, register, and listen for
31 * connections to a printer object.
32 * create_requested_array() - Create an array for requested-attributes.
33 * debug_attributes() - Print attributes in a request or response.
34 * delete_client() - Close the socket and free all memory used
36 * delete_job() - Remove from the printer and free all memory
37 * used by a job object.
38 * delete_printer() - Unregister, close listen sockets, and free
39 * all memory used by a printer object.
40 * dnssd_callback() - Handle Bonjour registration events.
41 * find_job() - Find a job specified in a request.
42 * html_escape() - Write a HTML-safe string.
43 * html_printf() - Send formatted text to the client, quoting
45 * ipp_cancel_job() - Cancel a job.
46 * ipp_create_job() - Create a job object.
47 * ipp_get_job_attributes() - Get the attributes for a job object.
48 * ipp_get_jobs() - Get a list of job objects.
49 * ipp_get_printer_attributes() - Get the attributes for a printer object.
50 * ipp_print_job() - Create a job object with an attached
52 * ipp_print_uri() - Create a job object with a referenced
54 * ipp_send_document() - Add an attached document to a job object
55 * created with Create-Job.
56 * ipp_send_uri() - Add a referenced document to a job object
57 * created with Create-Job.
58 * ipp_validate_job() - Validate job creation attributes.
59 * process_client() - Process client requests on a thread.
60 * process_http() - Process a HTTP request.
61 * process_ipp() - Process an IPP request.
62 * process_job() - Process a print job.
63 * register_printer() - Register a printer object via Bonjour.
64 * respond_http() - Send a HTTP response.
65 * respond_ipp() - Send an IPP response.
66 * respond_unsupported() - Respond with an unsupported attribute.
67 * run_printer() - Run the printer service.
68 * usage() - Show program usage.
69 * valid_doc_attributes() - Determine whether the document attributes
71 * valid_job_attributes() - Determine whether the job attributes are
76 * Disable private and deprecated stuff so we can verify that the public API
77 * is sufficient to implement a server.
80 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
81 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
85 * Include necessary headers...
88 #include <cups/cups.h> /* Public API */
89 #include <config.h> /* CUPS configuration header */
90 #include <cups/string-private.h> /* For string functions */
91 #include <cups/thread-private.h> /* For multithreading functions */
97 #endif /* HAVE_DNSSD */
100 #include <sys/fcntl.h>
102 #ifdef HAVE_SYS_MOUNT_H
103 # include <sys/mount.h>
104 #endif /* HAVE_SYS_MOUNT_H */
105 #ifdef HAVE_SYS_STATFS_H
106 # include <sys/statfs.h>
107 #endif /* HAVE_SYS_STATFS_H */
108 #ifdef HAVE_SYS_STATVFS_H
109 # include <sys/statvfs.h>
110 #endif /* HAVE_SYS_STATVFS_H */
111 #ifdef HAVE_SYS_VFS_H
112 # include <sys/vfs.h>
113 #endif /* HAVE_SYS_VFS_H */
120 enum _ipp_preasons_e
/* printer-state-reasons bit values */
122 _IPP_PSTATE_NONE
= 0x0000, /* none */
123 _IPP_PSTATE_OTHER
= 0x0001, /* other */
124 _IPP_PSTATE_COVER_OPEN
= 0x0002, /* cover-open */
125 _IPP_PSTATE_INPUT_TRAY_MISSING
= 0x0004,
126 /* input-tray-missing */
127 _IPP_PSTATE_MARKER_SUPPLY_EMPTY
= 0x0008,
128 /* marker-supply-empty */
129 _IPP_PSTATE_MARKER_SUPPLY_LOW
= 0x0010,
130 /* marker-suply-low */
131 _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL
= 0x0020,
132 /* marker-waste-almost-full */
133 _IPP_PSTATE_MARKER_WASTE_FULL
= 0x0040,
134 /* marker-waste-full */
135 _IPP_PSTATE_MEDIA_EMPTY
= 0x0080, /* media-empty */
136 _IPP_PSTATE_MEDIA_JAM
= 0x0100, /* media-jam */
137 _IPP_PSTATE_MEDIA_LOW
= 0x0200, /* media-low */
138 _IPP_PSTATE_MEDIA_NEEDED
= 0x0400, /* media-needed */
139 _IPP_PSTATE_MOVING_TO_PAUSED
= 0x0800,
140 /* moving-to-paused */
141 _IPP_PSTATE_PAUSED
= 0x1000, /* paused */
142 _IPP_PSTATE_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
143 _IPP_PSTATE_TONER_EMPTY
= 0x4000, /* toner-empty */
144 _IPP_PSTATE_TONER_LOW
= 0x8000 /* toner-low */
146 typedef unsigned int _ipp_preasons_t
; /* Bitfield for printer-state-reasons */
148 typedef enum _ipp_media_class_e
150 _IPP_GENERAL
, /* General-purpose size */
151 _IPP_PHOTO_ONLY
, /* Photo-only size */
152 _IPP_ENV_ONLY
/* Envelope-only size */
153 } _ipp_media_class_t
;
155 static const char * const media_supported
[] =
156 { /* media-supported values */
157 "iso_a4_210x297mm", /* A4 */
158 "iso_a5_148x210mm", /* A5 */
159 "iso_a6_105x148mm", /* A6 */
160 "iso_dl_110x220mm", /* DL */
161 "na_legal_8.5x14in", /* Legal */
162 "na_letter_8.5x11in", /* Letter */
163 "na_number-10_4.125x9.5in", /* #10 */
164 "na_index-3x5_3x5in", /* 3x5 */
165 "oe_photo-l_3.5x5in", /* L */
166 "na_index-4x6_4x6in", /* 4x6 */
167 "na_5x7_5x7in" /* 5x7 aka 2L */
169 static const int media_col_sizes
[][3] =
170 { /* media-col-database sizes */
171 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
172 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
173 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
174 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
175 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
176 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
177 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
178 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
179 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
180 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
181 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
183 static const char * const media_type_supported
[] =
184 /* media-type-supported values */
191 "photographic-glossy",
192 "photographic-high-gloss",
193 "photographic-matte",
194 "photographic-satin",
195 "photographic-semi-gloss",
197 "stationery-letterhead",
206 typedef struct _ipp_job_s _ipp_job_t
;
208 typedef struct _ipp_printer_s
/**** Printer data ****/
210 int ipv4
, /* IPv4 listener */
211 ipv6
; /* IPv6 listener */
213 DNSServiceRef common_ref
, /* Shared service connection */
214 ipp_ref
, /* Bonjour IPP service */
216 ipps_ref
, /* Bonjour IPPS service */
217 # endif /* HAVE_SSL */
218 http_ref
, /* Bonjour HTTP service */
219 printer_ref
; /* Bonjour LPD service */
220 TXTRecordRef ipp_txt
; /* Bonjour IPP TXT record */
221 char *dnssd_name
; /* printer-dnssd-name */
222 #endif /* HAVE_DNSSD */
223 char *name
, /* printer-name */
224 *icon
, /* Icon filename */
225 *directory
, /* Spool directory */
226 *hostname
, /* Hostname */
227 *uri
, /* printer-uri-supported */
228 *command
; /* Command to run with job file */
230 size_t urilen
; /* Length of printer URI */
231 ipp_t
*attrs
; /* Static attributes */
232 ipp_pstate_t state
; /* printer-state value */
233 _ipp_preasons_t state_reasons
; /* printer-state-reasons values */
234 cups_array_t
*jobs
; /* Jobs */
235 _ipp_job_t
*active_job
; /* Current active/pending job */
236 int next_job_id
; /* Next job-id value */
237 _cups_rwlock_t rwlock
; /* Printer lock */
240 struct _ipp_job_s
/**** Job data ****/
243 const char *name
, /* job-name */
244 *username
, /* job-originating-user-name */
245 *format
; /* document-format */
246 ipp_jstate_t state
; /* job-state value */
247 time_t processing
, /* time-at-processing value */
248 completed
; /* time-at-completed value */
249 ipp_t
*attrs
; /* Static attributes */
250 int cancel
; /* Non-zero when job canceled */
251 char *filename
; /* Print file name */
252 int fd
; /* Print file descriptor */
253 _ipp_printer_t
*printer
; /* Printer */
256 typedef struct _ipp_client_s
/**** Client data ****/
258 http_t
*http
; /* HTTP connection */
259 ipp_t
*request
, /* IPP request */
260 *response
; /* IPP response */
261 time_t start
; /* Request start time */
262 http_state_t operation
; /* Request operation */
263 ipp_op_t operation_id
; /* IPP operation-id */
264 char uri
[1024]; /* Request URI */
265 http_addr_t addr
; /* Client address */
266 char hostname
[256]; /* Client hostname */
267 _ipp_printer_t
*printer
; /* Printer */
268 _ipp_job_t
*job
; /* Current job, if any */
276 static void clean_jobs(_ipp_printer_t
*printer
);
277 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
278 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
279 ipp_tag_t group_tag
, int quickcopy
);
280 static void copy_job_attributes(_ipp_client_t
*client
,
281 _ipp_job_t
*job
, cups_array_t
*ra
);
282 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
283 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
284 static int create_listener(int family
, int *port
);
285 static ipp_t
*create_media_col(const char *media
, const char *type
,
286 int width
, int length
, int margins
);
287 static ipp_t
*create_media_size(int width
, int length
);
288 static _ipp_printer_t
*create_printer(const char *servername
,
289 const char *name
, const char *location
,
290 const char *make
, const char *model
,
292 const char *docformats
, int ppm
,
293 int ppm_color
, int duplex
, int port
,
297 #endif /* HAVE_DNSSD */
298 const char *directory
,
299 const char *command
);
300 static void debug_attributes(const char *title
, ipp_t
*ipp
,
302 static void delete_client(_ipp_client_t
*client
);
303 static void delete_job(_ipp_job_t
*job
);
304 static void delete_printer(_ipp_printer_t
*printer
);
306 static void dnssd_callback(DNSServiceRef sdRef
,
307 DNSServiceFlags flags
,
308 DNSServiceErrorType errorCode
,
312 _ipp_printer_t
*printer
);
313 #endif /* HAVE_DNSSD */
314 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
315 static void html_escape(_ipp_client_t
*client
, const char *s
,
317 static void html_printf(_ipp_client_t
*client
, const char *format
,
318 ...) __attribute__((__format__(__printf__
,
320 static void ipp_cancel_job(_ipp_client_t
*client
);
321 static void ipp_create_job(_ipp_client_t
*client
);
322 static void ipp_get_job_attributes(_ipp_client_t
*client
);
323 static void ipp_get_jobs(_ipp_client_t
*client
);
324 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
325 static void ipp_print_job(_ipp_client_t
*client
);
326 static void ipp_print_uri(_ipp_client_t
*client
);
327 static void ipp_send_document(_ipp_client_t
*client
);
328 static void ipp_send_uri(_ipp_client_t
*client
);
329 static void ipp_validate_job(_ipp_client_t
*client
);
330 static void *process_client(_ipp_client_t
*client
);
331 static int process_http(_ipp_client_t
*client
);
332 static int process_ipp(_ipp_client_t
*client
);
333 static void *process_job(_ipp_job_t
*job
);
335 static int register_printer(_ipp_printer_t
*printer
,
336 const char *location
, const char *make
,
337 const char *model
, const char *formats
,
338 const char *adminurl
, int color
,
339 int duplex
, const char *regtype
);
340 #endif /* HAVE_DNSSD */
341 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
342 const char *content_coding
,
343 const char *type
, size_t length
);
344 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
345 const char *message
, ...)
346 __attribute__ ((__format__ (__printf__
, 3, 4)));
347 static void respond_unsupported(_ipp_client_t
*client
,
348 ipp_attribute_t
*attr
);
349 static void run_printer(_ipp_printer_t
*printer
);
350 static void usage(int status
) __attribute__((noreturn
));
351 static int valid_doc_attributes(_ipp_client_t
*client
);
352 static int valid_job_attributes(_ipp_client_t
*client
);
359 static int KeepFiles
= 0,
364 * 'main()' - Main entry to the sample server.
367 int /* O - Exit status */
368 main(int argc
, /* I - Number of command-line args */
369 char *argv
[]) /* I - Command-line arguments */
371 int i
; /* Looping var */
372 const char *opt
, /* Current option character */
373 *command
= NULL
, /* Command to run with job files */
374 *servername
= NULL
, /* Server host name */
375 *name
= NULL
, /* Printer name */
376 *location
= "", /* Location of printer */
377 *make
= "Test", /* Manufacturer */
378 *model
= "Printer", /* Model */
379 *icon
= "printer.png", /* Icon file */
380 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
381 /* Supported formats */
383 const char *subtype
= "_print"; /* Bonjour service subtype */
384 #endif /* HAVE_DNSSD */
385 int port
= 8631, /* Port number (0 = auto) */
386 duplex
= 0, /* Duplex mode */
387 ppm
= 10, /* Pages per minute for mono */
388 ppm_color
= 0, /* Pages per minute for color */
389 pin
= 0; /* PIN printing mode? */
390 char directory
[1024] = ""; /* Spool directory */
391 _ipp_printer_t
*printer
; /* Printer object */
395 * Parse command-line arguments...
398 for (i
= 1; i
< argc
; i
++)
399 if (argv
[i
][0] == '-')
401 for (opt
= argv
[i
] + 1; *opt
; opt
++)
404 case '2' : /* -2 (enable 2-sided printing) */
408 case 'M' : /* -M manufacturer */
415 case 'P' : /* -P (PIN printing mode) */
419 case 'c' : /* -c command */
427 case 'd' : /* -d spool-directory */
431 strlcpy(directory
, argv
[i
], sizeof(directory
));
434 case 'f' : /* -f type/subtype[,...] */
441 case 'h' : /* -h (show help) */
445 case 'i' : /* -i icon.png */
452 case 'k' : /* -k (keep files) */
456 case 'l' : /* -l location */
463 case 'm' : /* -m model */
470 case 'n' : /* -n hostname */
474 servername
= argv
[i
];
477 case 'p' : /* -p port */
479 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
481 port
= atoi(argv
[i
]);
485 case 'r' : /* -r subtype */
491 #endif /* HAVE_DNSSD */
493 case 's' : /* -s speed[,color-speed] */
497 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
501 case 'v' : /* -v (be verbose) */
505 default : /* Unknown */
506 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
517 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
525 * Apply defaults as needed...
530 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
532 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
534 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
535 directory
, strerror(errno
));
540 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
544 * Create the printer...
547 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
548 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
551 #endif /* HAVE_DNSSD */
552 directory
, command
)) == NULL
)
556 * Run the print service...
559 run_printer(printer
);
562 * Destroy the printer and exit...
565 delete_printer(printer
);
572 * 'clean_jobs()' - Clean out old (completed) jobs.
576 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
578 _ipp_job_t
*job
; /* Current job */
579 time_t cleantime
; /* Clean time */
582 if (cupsArrayCount(printer
->jobs
) == 0)
585 cleantime
= time(NULL
) - 60;
587 _cupsRWLockWrite(&(printer
->rwlock
));
588 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
590 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
591 if (job
->completed
&& job
->completed
< cleantime
)
593 cupsArrayRemove(printer
->jobs
, job
);
598 _cupsRWUnlock(&(printer
->rwlock
));
603 * 'compare_jobs()' - Compare two jobs.
606 static int /* O - Result of comparison */
607 compare_jobs(_ipp_job_t
*a
, /* I - First job */
608 _ipp_job_t
*b
) /* I - Second job */
610 return (b
->id
- a
->id
);
615 * 'copy_attributes()' - Copy attributes from one request to another.
619 copy_attributes(ipp_t
*to
, /* I - Destination request */
620 ipp_t
*from
, /* I - Source request */
621 cups_array_t
*ra
, /* I - Requested attributes */
622 ipp_tag_t group_tag
, /* I - Group to copy */
623 int quickcopy
) /* I - Do a quick copy? */
625 ipp_attribute_t
*fromattr
; /* Source attribute */
631 for (fromattr
= ippFirstAttribute(from
);
633 fromattr
= ippNextAttribute(from
))
636 * Filter attributes as needed...
639 ipp_tag_t fromgroup
= ippGetGroupTag(fromattr
);
640 const char *fromname
= ippGetName(fromattr
);
642 if ((group_tag
!= IPP_TAG_ZERO
&& fromgroup
!= group_tag
&&
643 fromgroup
!= IPP_TAG_ZERO
) || !fromname
)
646 if (!ra
|| cupsArrayFind(ra
, (void *)fromname
))
647 ippCopyAttribute(to
, fromattr
, quickcopy
);
653 * 'copy_job_attrs()' - Copy job attributes to the response.
658 _ipp_client_t
*client
, /* I - Client */
659 _ipp_job_t
*job
, /* I - Job */
660 cups_array_t
*ra
) /* I - requested-attributes */
662 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
664 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
665 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
666 "job-printer-up-time", (int)time(NULL
));
668 if (!ra
|| cupsArrayFind(ra
, "job-state"))
669 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
670 "job-state", job
->state
);
672 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
676 case IPP_JSTATE_PENDING
:
677 ippAddString(client
->response
, IPP_TAG_JOB
,
678 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
, "job-state-reasons",
682 case IPP_JSTATE_HELD
:
684 ippAddString(client
->response
, IPP_TAG_JOB
,
685 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
686 "job-state-reasons", NULL
, "job-incoming");
687 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
688 ippAddString(client
->response
, IPP_TAG_JOB
,
689 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
690 "job-state-reasons", NULL
, "job-hold-until-specified");
692 ippAddString(client
->response
, IPP_TAG_JOB
,
693 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
694 "job-state-reasons", NULL
, "job-data-insufficient");
697 case IPP_JSTATE_PROCESSING
:
699 ippAddString(client
->response
, IPP_TAG_JOB
,
700 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
701 "job-state-reasons", NULL
, "processing-to-stop-point");
703 ippAddString(client
->response
, IPP_TAG_JOB
,
704 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
705 "job-state-reasons", NULL
, "job-printing");
708 case IPP_JSTATE_STOPPED
:
709 ippAddString(client
->response
, IPP_TAG_JOB
,
710 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
, "job-state-reasons",
711 NULL
, "job-stopped");
714 case IPP_JSTATE_CANCELED
:
715 ippAddString(client
->response
, IPP_TAG_JOB
,
716 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
, "job-state-reasons",
717 NULL
, "job-canceled-by-user");
720 case IPP_JSTATE_ABORTED
:
721 ippAddString(client
->response
, IPP_TAG_JOB
,
722 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
, "job-state-reasons",
723 NULL
, "aborted-by-system");
726 case IPP_JSTATE_COMPLETED
:
727 ippAddString(client
->response
, IPP_TAG_JOB
,
728 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
, "job-state-reasons",
729 NULL
, "job-completed-successfully");
734 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
735 ippAddInteger(client
->response
, IPP_TAG_JOB
,
736 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
737 "time-at-completed", job
->completed
);
739 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
740 ippAddInteger(client
->response
, IPP_TAG_JOB
,
741 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
742 "time-at-processing", job
->processing
);
747 * 'create_client()' - Accept a new network connection and create a client
751 static _ipp_client_t
* /* O - Client */
752 create_client(_ipp_printer_t
*printer
, /* I - Printer */
753 int sock
) /* I - Listen socket */
755 _ipp_client_t
*client
; /* Client */
758 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
760 perror("Unable to allocate memory for client");
764 client
->printer
= printer
;
767 * Accept the client and get the remote address...
770 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
772 perror("Unable to accept client connection");
779 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
782 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
789 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
793 static _ipp_job_t
* /* O - Job */
794 create_job(_ipp_client_t
*client
) /* I - Client */
796 _ipp_job_t
*job
; /* Job */
797 ipp_attribute_t
*attr
; /* Job attribute */
798 char uri
[1024]; /* job-uri value */
801 _cupsRWLockWrite(&(client
->printer
->rwlock
));
802 if (client
->printer
->active_job
&&
803 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
806 * Only accept a single job at a time...
809 _cupsRWLockWrite(&(client
->printer
->rwlock
));
814 * Allocate and initialize the job object...
817 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
819 perror("Unable to allocate memory for job");
823 job
->printer
= client
->printer
;
824 job
->attrs
= client
->request
;
825 job
->state
= IPP_JSTATE_HELD
;
827 client
->request
= NULL
;
830 * Set all but the first two attributes to the job attributes group...
833 for (ippFirstAttribute(job
->attrs
),
834 ippNextAttribute(job
->attrs
),
835 attr
= ippNextAttribute(job
->attrs
);
837 attr
= ippNextAttribute(job
->attrs
))
838 ippSetGroupTag(job
->attrs
, &attr
, IPP_TAG_JOB
);
841 * Get the requesting-user-name, document format, and priority...
844 if ((attr
= ippFindAttribute(job
->attrs
, "requesting-user-name",
845 IPP_TAG_NAME
)) != NULL
)
846 ippSetName(job
->attrs
, &attr
, "job-originating-user-name");
848 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
,
849 IPP_TAG_NAME
| IPP_TAG_CUPS_CONST
,
850 "job-originating-user-name", NULL
, "anonymous");
853 job
->username
= ippGetString(attr
, 0, NULL
);
855 job
->username
= "anonymous";
857 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
858 IPP_TAG_MIMETYPE
)) != NULL
)
859 job
->format
= ippGetString(attr
, 0, NULL
);
861 job
->format
= "application/octet-stream";
864 * Add job description attributes and add to the jobs array...
867 job
->id
= client
->printer
->next_job_id
++;
869 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
871 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
872 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
873 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
874 client
->printer
->uri
);
875 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
878 cupsArrayAdd(client
->printer
->jobs
, job
);
879 client
->printer
->active_job
= job
;
881 _cupsRWUnlock(&(client
->printer
->rwlock
));
888 * 'create_listener()' - Create a listener socket.
891 static int /* O - Listener socket or -1 on error */
892 create_listener(int family
, /* I - Address family */
893 int *port
) /* IO - Port number */
895 int sock
; /* Listener socket */
896 http_addrlist_t
*addrlist
; /* Listen address */
897 char service
[255]; /* Service port */
902 *port
= 8000 + (getuid() % 1000);
903 fprintf(stderr
, "Listening on port %d.\n", *port
);
906 snprintf(service
, sizeof(service
), "%d", *port
);
907 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
910 sock
= httpAddrListen(&(addrlist
->addr
), *port
);
912 httpAddrFreeList(addrlist
);
919 * 'create_media_col()' - Create a media-col value.
922 static ipp_t
* /* O - media-col collection */
923 create_media_col(const char *media
, /* I - Media name */
924 const char *type
, /* I - Nedua type */
925 int width
, /* I - x-dimension in 2540ths */
926 int length
, /* I - y-dimension in 2540ths */
927 int margins
) /* I - Value for margins */
929 ipp_t
*media_col
= ippNew(), /* media-col value */
930 *media_size
= create_media_size(width
, length
);
931 /* media-size value */
932 char media_key
[256]; /* media-key value */
935 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, type
,
936 margins
== 0 ? "_borderless" : "");
938 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
940 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
941 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
942 "media-bottom-margin", margins
);
943 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
944 "media-left-margin", margins
);
945 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
946 "media-right-margin", margins
);
947 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
948 "media-top-margin", margins
);
949 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type",
952 ippDelete(media_size
);
959 * 'create_media_size()' - Create a media-size value.
962 static ipp_t
* /* O - media-col collection */
963 create_media_size(int width
, /* I - x-dimension in 2540ths */
964 int length
) /* I - y-dimension in 2540ths */
966 ipp_t
*media_size
= ippNew(); /* media-size value */
969 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
971 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
979 * 'create_printer()' - Create, register, and listen for connections to a
983 static _ipp_printer_t
* /* O - Printer */
984 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
985 const char *name
, /* I - printer-name */
986 const char *location
, /* I - printer-location */
987 const char *make
, /* I - printer-make-and-model */
988 const char *model
, /* I - printer-make-and-model */
989 const char *icon
, /* I - printer-icons */
990 const char *docformats
, /* I - document-format-supported */
991 int ppm
, /* I - Pages per minute in grayscale */
992 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
993 int duplex
, /* I - 1 = duplex, 0 = simplex */
994 int port
, /* I - Port for listeners or 0 for auto */
995 int pin
, /* I - Require PIN printing */
997 const char *subtype
, /* I - Bonjour service subtype */
998 #endif /* HAVE_DNSSD */
999 const char *directory
, /* I - Spool directory */
1000 const char *command
) /* I - Command to run on job files */
1002 int i
, j
; /* Looping vars */
1003 _ipp_printer_t
*printer
; /* Printer */
1004 char hostname
[256], /* Hostname */
1005 uri
[1024], /* Printer URI */
1006 icons
[1024], /* printer-icons URI */
1007 adminurl
[1024], /* printer-more-info URI */
1008 device_id
[1024],/* printer-device-id */
1009 make_model
[128];/* printer-make-and-model */
1010 int num_formats
; /* Number of document-format-supported values */
1011 char *defformat
, /* document-format-default value */
1012 *formats
[100], /* document-format-supported values */
1013 *ptr
; /* Pointer into string */
1014 const char *prefix
; /* Prefix string */
1015 int num_database
; /* Number of database values */
1016 ipp_attribute_t
*media_col_database
,
1017 /* media-col-database value */
1018 *media_size_supported
;
1019 /* media-size-supported value */
1020 ipp_t
*media_col_default
;
1021 /* media-col-default value */
1022 int media_col_index
;/* Current media-col-database value */
1023 int k_supported
; /* Maximum file size supported */
1025 struct statvfs spoolinfo
; /* FS info for spool directory */
1026 double spoolsize
; /* FS size */
1027 #elif defined(HAVE_STATFS)
1028 struct statfs spoolinfo
; /* FS info for spool directory */
1029 double spoolsize
; /* FS size */
1030 #endif /* HAVE_STATVFS */
1031 static const int orients
[4] = /* orientation-requested-supported values */
1033 IPP_ORIENT_PORTRAIT
,
1034 IPP_ORIENT_LANDSCAPE
,
1035 IPP_ORIENT_REVERSE_LANDSCAPE
,
1036 IPP_ORIENT_REVERSE_PORTRAIT
1038 static const char * const versions
[] =/* ipp-versions-supported values */
1044 static const int ops
[] = /* operations-supported values */
1048 IPP_OP_VALIDATE_JOB
,
1050 IPP_OP_SEND_DOCUMENT
,
1053 IPP_OP_GET_JOB_ATTRIBUTES
,
1055 IPP_OP_GET_PRINTER_ATTRIBUTES
1057 static const char * const charsets
[] =/* charset-supported values */
1062 static const char * const compressions
[] =/* compression-supported values */
1067 #endif /* HAVE_LIBZ */
1070 static const char * const job_creation
[] =
1071 { /* job-creation-attributes-supported values */
1073 "ipp-attribute-fidelity",
1075 "job-accounting-user-id",
1081 "multiple-document-handling",
1082 "orientation-requested",
1086 static const char * const media_col_supported
[] =
1087 { /* media-col-supported values */
1088 "media-bottom-margin",
1089 "media-left-margin",
1090 "media-right-margin",
1095 static const int media_xxx_margin_supported
[] =
1096 { /* media-xxx-margin-supported values */
1100 static const char * const multiple_document_handling
[] =
1101 { /* multiple-document-handling-supported values */
1102 "separate-documents-uncollated-copies",
1103 "separate-documents-collated-copies"
1105 static const int print_quality_supported
[] =
1106 { /* print-quality-supported values */
1111 static const int pwg_raster_document_resolution_supported
[] =
1117 static const char * const pwg_raster_document_type_supported
[] =
1125 static const char * const reference_uri_schemes_supported
[] =
1126 { /* reference-uri-schemes-supported */
1132 #endif /* HAVE_SSL */
1134 static const char * const sides_supported
[] =
1135 { /* sides-supported values */
1137 "two-sided-long-edge",
1138 "two-sided-short-edge"
1140 static const char * const which_jobs
[] =
1141 { /* which-jobs-supported values */
1150 "processing-stopped"
1155 * Allocate memory for the printer...
1158 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1160 perror("Unable to allocate memory for printer");
1166 printer
->name
= strdup(name
);
1168 printer
->dnssd_name
= strdup(printer
->name
);
1169 #endif /* HAVE_DNSSD */
1170 printer
->command
= command
? strdup(command
) : NULL
;
1171 printer
->directory
= strdup(directory
);
1172 printer
->hostname
= strdup(servername
? servername
:
1173 httpGetHostname(NULL
, hostname
,
1175 printer
->port
= port
;
1176 printer
->state
= IPP_PSTATE_IDLE
;
1177 printer
->state_reasons
= _IPP_PSTATE_NONE
;
1178 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1179 printer
->next_job_id
= 1;
1181 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1182 printer
->hostname
, printer
->port
, "/ipp/print");
1183 printer
->uri
= strdup(uri
);
1184 printer
->urilen
= strlen(uri
);
1187 printer
->icon
= strdup(icon
);
1189 _cupsRWInit(&(printer
->rwlock
));
1192 * Create the listener sockets...
1195 if ((printer
->ipv4
= create_listener(AF_INET
, &(printer
->port
))) < 0)
1197 perror("Unable to create IPv4 listener");
1201 if ((printer
->ipv6
= create_listener(AF_INET6
, &(printer
->port
))) < 0)
1203 perror("Unable to create IPv6 listener");
1208 * Prepare values for the printer attributes...
1211 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1212 printer
->hostname
, printer
->port
, "/icon.png");
1213 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
,
1214 printer
->hostname
, printer
->port
, "/");
1218 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1219 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1222 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1225 formats
[0] = strdup(docformats
);
1226 defformat
= formats
[0];
1227 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1230 formats
[num_formats
++] = ptr
;
1232 if (!_cups_strcasecmp(ptr
, "application/octet-stream"))
1236 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1237 ptr
= device_id
+ strlen(device_id
);
1239 for (i
= 0; i
< num_formats
; i
++)
1241 if (!_cups_strcasecmp(formats
[i
], "application/pdf"))
1242 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPDF", prefix
);
1243 else if (!_cups_strcasecmp(formats
[i
], "application/postscript"))
1244 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPS", prefix
);
1245 else if (!_cups_strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1246 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPCL", prefix
);
1247 else if (!_cups_strcasecmp(formats
[i
], "image/jpeg"))
1248 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sJPEG", prefix
);
1249 else if (!_cups_strcasecmp(formats
[i
], "image/png"))
1250 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPNG", prefix
);
1251 else if (_cups_strcasecmp(formats
[i
], "application/octet-stream"))
1252 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%s%s", prefix
,
1258 strlcat(device_id
, ";", sizeof(device_id
));
1261 * Get the maximum spool size based on the size of the filesystem used for
1262 * the spool directory. If the host OS doesn't support the statfs call
1263 * or the filesystem is larger than 2TiB, always report INT_MAX.
1267 if (statvfs(printer
->directory
, &spoolinfo
))
1268 k_supported
= INT_MAX
;
1269 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1270 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1271 k_supported
= INT_MAX
;
1273 k_supported
= (int)spoolsize
;
1275 #elif defined(HAVE_STATFS)
1276 if (statfs(printer
->directory
, &spoolinfo
))
1277 k_supported
= INT_MAX
;
1278 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1279 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1280 k_supported
= INT_MAX
;
1282 k_supported
= (int)spoolsize
;
1285 k_supported
= INT_MAX
;
1286 #endif /* HAVE_STATVFS */
1289 * Create the printer attributes. This list of attributes is sorted to improve
1290 * performance when the client provides a requested-attributes attribute...
1293 printer
->attrs
= ippNew();
1295 /* charset-configured */
1296 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1297 IPP_TAG_CHARSET
| IPP_TAG_CUPS_CONST
,
1298 "charset-configured", NULL
, "utf-8");
1300 /* charset-supported */
1301 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1302 IPP_TAG_CHARSET
| IPP_TAG_CUPS_CONST
,
1303 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1306 /* color-supported */
1307 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1310 /* compression-supported */
1311 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1312 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1313 "compression-supported",
1314 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1317 /* copies-default */
1318 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1319 "copies-default", 1);
1321 /* copies-supported */
1322 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1324 /* document-format-default */
1325 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1326 "document-format-default", NULL
, defformat
);
1328 /* document-format-supported */
1329 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1330 "document-format-supported", num_formats
, NULL
,
1331 (const char * const *)formats
);
1333 /* finishings-default */
1334 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1335 "finishings-default", IPP_FINISHINGS_NONE
);
1337 /* finishings-supported */
1338 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1339 "finishings-supported", IPP_FINISHINGS_NONE
);
1341 /* generated-natural-language-supported */
1342 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1343 IPP_TAG_LANGUAGE
| IPP_TAG_CUPS_CONST
,
1344 "generated-natural-language-supported", NULL
, "en");
1346 /* ipp-versions-supported */
1347 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1348 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1349 "ipp-versions-supported",
1350 sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1352 /* job-account-id-supported */
1353 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1355 /* job-accounting-user-id-supported */
1356 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
,
1357 "job-accounting-user-id-supported", 1);
1359 /* job-creation-attributes-supported */
1360 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1361 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1362 "job-creation-attributes-supported",
1363 sizeof(job_creation
) / sizeof(job_creation
[0]),
1364 NULL
, job_creation
);
1366 /* job-k-octets-supported */
1367 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1370 /* job-password-supported */
1371 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1372 "job-password-supported", 4);
1374 /* job-priority-default */
1375 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1376 "job-priority-default", 50);
1378 /* job-priority-supported */
1379 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1380 "job-priority-supported", 100);
1382 /* job-sheets-default */
1383 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1384 IPP_TAG_NAME
| IPP_TAG_CUPS_CONST
,
1385 "job-sheets-default", NULL
, "none");
1387 /* job-sheets-supported */
1388 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1389 IPP_TAG_NAME
| IPP_TAG_CUPS_CONST
,
1390 "job-sheets-supported", NULL
, "none");
1392 /* media-bottom-margin-supported */
1393 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1394 "media-bottom-margin-supported",
1395 (int)(sizeof(media_xxx_margin_supported
) /
1396 sizeof(media_xxx_margin_supported
[0])),
1397 media_xxx_margin_supported
);
1399 /* media-col-database */
1400 for (num_database
= 0, i
= 0;
1401 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1404 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1405 num_database
+= 2; /* auto + envelope */
1406 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1407 num_database
+= 12; /* auto + photographic-* + borderless */
1409 num_database
+= (int)(sizeof(media_type_supported
) /
1410 sizeof(media_type_supported
[0])) + 6;
1411 /* All types + borderless */
1414 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1415 "media-col-database", num_database
,
1417 for (media_col_index
= 0, i
= 0;
1418 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1422 j
< (int)(sizeof(media_type_supported
) /
1423 sizeof(media_type_supported
[0]));
1426 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
&&
1427 strcmp(media_type_supported
[j
], "auto") &&
1428 strcmp(media_type_supported
[j
], "envelope"))
1430 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
&&
1431 strcmp(media_type_supported
[j
], "auto") &&
1432 strncmp(media_type_supported
[j
], "photographic-", 13))
1435 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
,
1436 create_media_col(media_supported
[i
],
1437 media_type_supported
[j
],
1438 media_col_sizes
[i
][0],
1439 media_col_sizes
[i
][1],
1440 media_xxx_margin_supported
[1]));
1443 if (media_col_sizes
[i
][2] != _IPP_ENV_ONLY
&&
1444 (!strcmp(media_type_supported
[j
], "auto") ||
1445 !strncmp(media_type_supported
[j
], "photographic-", 13)))
1448 * Add borderless version for this combination...
1451 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
,
1452 create_media_col(media_supported
[i
],
1453 media_type_supported
[j
],
1454 media_col_sizes
[i
][0],
1455 media_col_sizes
[i
][1],
1456 media_xxx_margin_supported
[0]));
1462 /* media-col-default */
1463 media_col_default
= create_media_col(media_supported
[0],
1464 media_type_supported
[0],
1465 media_col_sizes
[0][0],
1466 media_col_sizes
[0][1],
1467 media_xxx_margin_supported
[1]);
1469 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1471 ippDelete(media_col_default
);
1473 /* media-col-supported */
1474 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1475 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1476 "media-col-supported",
1477 (int)(sizeof(media_col_supported
) /
1478 sizeof(media_col_supported
[0])), NULL
,
1479 media_col_supported
);
1482 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1483 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1484 "media-default", NULL
, media_supported
[0]);
1486 /* media-left-margin-supported */
1487 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1488 "media-left-margin-supported",
1489 (int)(sizeof(media_xxx_margin_supported
) /
1490 sizeof(media_xxx_margin_supported
[0])),
1491 media_xxx_margin_supported
);
1493 /* media-right-margin-supported */
1494 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1495 "media-right-margin-supported",
1496 (int)(sizeof(media_xxx_margin_supported
) /
1497 sizeof(media_xxx_margin_supported
[0])),
1498 media_xxx_margin_supported
);
1500 /* media-supported */
1501 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1502 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1504 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1505 NULL
, media_supported
);
1507 /* media-size-supported */
1508 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1509 "media-size-supported",
1510 (int)(sizeof(media_col_sizes
) /
1511 sizeof(media_col_sizes
[0])),
1514 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1516 ippSetCollection(printer
->attrs
, &media_size_supported
, i
,
1517 create_media_size(media_col_sizes
[i
][0],
1518 media_col_sizes
[i
][1]));
1520 /* media-top-margin-supported */
1521 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1522 "media-top-margin-supported",
1523 (int)(sizeof(media_xxx_margin_supported
) /
1524 sizeof(media_xxx_margin_supported
[0])),
1525 media_xxx_margin_supported
);
1527 /* media-type-supported */
1528 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1529 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1530 "media-type-supported",
1531 (int)(sizeof(media_type_supported
) /
1532 sizeof(media_type_supported
[0])),
1533 NULL
, media_type_supported
);
1535 /* multiple-document-handling-supported */
1536 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1537 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1538 "multiple-document-handling-supported",
1539 sizeof(multiple_document_handling
) /
1540 sizeof(multiple_document_handling
[0]), NULL
,
1541 multiple_document_handling
);
1543 /* multiple-document-jobs-supported */
1544 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
,
1545 "multiple-document-jobs-supported", 0);
1547 /* natural-language-configured */
1548 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1549 IPP_TAG_LANGUAGE
| IPP_TAG_CUPS_CONST
,
1550 "natural-language-configured", NULL
, "en");
1552 /* number-up-default */
1553 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1554 "number-up-default", 1);
1556 /* number-up-supported */
1557 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1558 "number-up-supported", 1);
1560 /* operations-supported */
1561 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1562 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1564 /* orientation-requested-default */
1565 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1566 "orientation-requested-default", 0);
1568 /* orientation-requested-supported */
1569 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1570 "orientation-requested-supported", 4, orients
);
1572 /* output-bin-default */
1573 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1574 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1575 "output-bin-default", NULL
, "face-down");
1577 /* output-bin-supported */
1578 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1579 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1580 "output-bin-supported", NULL
, "face-down");
1582 /* pages-per-minute */
1583 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1584 "pages-per-minute", ppm
);
1586 /* pages-per-minute-color */
1588 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1589 "pages-per-minute-color", ppm_color
);
1591 /* pdl-override-supported */
1592 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1593 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1594 "pdl-override-supported", NULL
, "attempted");
1596 /* print-quality-default */
1597 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1598 "print-quality-default", IPP_QUALITY_NORMAL
);
1600 /* print-quality-supported */
1601 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1602 "print-quality-supported",
1603 (int)(sizeof(print_quality_supported
) /
1604 sizeof(print_quality_supported
[0])),
1605 print_quality_supported
);
1607 /* printer-device-id */
1608 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1609 "printer-device-id", NULL
, device_id
);
1612 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1613 "printer-icons", NULL
, icons
);
1615 /* printer-is-accepting-jobs */
1616 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1620 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1623 /* printer-location */
1624 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1625 "printer-location", NULL
, location
);
1627 /* printer-make-and-model */
1628 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1629 "printer-make-and-model", NULL
, make_model
);
1631 /* printer-mandatory-job-attributes */
1634 static const char * const names
[] =
1636 "job-accounting-user-id",
1640 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1641 "printer-mandatory-job-attributes",
1642 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
1645 /* printer-more-info */
1646 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1647 "printer-more-info", NULL
, adminurl
);
1650 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1653 /* printer-resolution-default */
1654 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1655 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1657 /* printer-resolution-supported */
1658 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1659 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1661 /* printer-uri-supported */
1662 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1663 "printer-uri-supported", NULL
, uri
);
1665 /* pwg-raster-document-xxx-supported */
1666 for (i
= 0; i
< num_formats
; i
++)
1667 if (!_cups_strcasecmp(formats
[i
], "image/pwg-raster"))
1670 if (i
< num_formats
)
1672 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
,
1673 "pwg-raster-document-resolution-supported",
1674 (int)(sizeof(pwg_raster_document_resolution_supported
) /
1675 sizeof(pwg_raster_document_resolution_supported
[0])),
1677 pwg_raster_document_resolution_supported
,
1678 pwg_raster_document_resolution_supported
);
1679 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1680 "pwg-raster-document-sheet-back", NULL
, "normal");
1681 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1682 "pwg-raster-document-type-supported",
1683 (int)(sizeof(pwg_raster_document_type_supported
) /
1684 sizeof(pwg_raster_document_type_supported
[0])), NULL
,
1685 pwg_raster_document_type_supported
);
1688 /* reference-uri-scheme-supported */
1689 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1690 IPP_TAG_URISCHEME
| IPP_TAG_CUPS_CONST
,
1691 "reference-uri-schemes-supported",
1692 (int)(sizeof(reference_uri_schemes_supported
) /
1693 sizeof(reference_uri_schemes_supported
[0])),
1694 NULL
, reference_uri_schemes_supported
);
1697 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1698 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1699 "sides-default", NULL
, "one-sided");
1701 /* sides-supported */
1702 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1703 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1704 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
1706 /* uri-authentication-supported */
1707 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1708 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1709 "uri-authentication-supported", NULL
, "none");
1711 /* uri-security-supported */
1712 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1713 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1714 "uri-security-supported", NULL
, "none");
1716 /* which-jobs-supported */
1717 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1718 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1719 "which-jobs-supported",
1720 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
1724 debug_attributes("Printer", printer
->attrs
, 0);
1728 * Register the printer with Bonjour...
1731 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
,
1732 ppm_color
> 0, duplex
, subtype
))
1734 #endif /* HAVE_DNSSD */
1744 * If we get here we were unable to create the printer...
1749 delete_printer(printer
);
1755 * 'debug_attributes()' - Print attributes in a request or response.
1759 debug_attributes(const char *title
, /* I - Title */
1760 ipp_t
*ipp
, /* I - Request/response */
1761 int type
) /* I - 0 = object, 1 = request, 2 = response */
1763 ipp_tag_t group_tag
; /* Current group */
1764 ipp_attribute_t
*attr
; /* Current attribute */
1765 char buffer
[2048]; /* String buffer for value */
1766 int major
, minor
; /* Version */
1772 fprintf(stderr
, "%s:\n", title
);
1773 major
= ippGetVersion(ipp
, &minor
);
1774 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1776 fprintf(stderr
, " operation-id=%s(%04x)\n",
1777 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1779 fprintf(stderr
, " status-code=%s(%04x)\n",
1780 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1781 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1783 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1785 attr
= ippNextAttribute(ipp
))
1787 if (ippGetGroupTag(attr
) != group_tag
)
1789 group_tag
= ippGetGroupTag(attr
);
1790 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1793 if (ippGetName(attr
))
1795 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1796 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
1797 ippGetCount(attr
) > 1 ? "1setOf " : "",
1798 ippTagString(ippGetValueTag(attr
)), buffer
);
1805 * 'delete_client()' - Close the socket and free all memory used by a client
1810 delete_client(_ipp_client_t
*client
) /* I - Client */
1813 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
1816 * Flush pending writes before closing...
1819 httpFlushWrite(client
->http
);
1825 httpClose(client
->http
);
1827 ippDelete(client
->request
);
1828 ippDelete(client
->response
);
1835 * 'delete_job()' - Remove from the printer and free all memory used by a job
1840 delete_job(_ipp_job_t
*job
) /* I - Job */
1843 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
1845 ippDelete(job
->attrs
);
1850 unlink(job
->filename
);
1852 free(job
->filename
);
1860 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
1861 * used by a printer object.
1865 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
1867 if (printer
->ipv4
>= 0)
1868 close(printer
->ipv4
);
1870 if (printer
->ipv6
>= 0)
1871 close(printer
->ipv6
);
1874 if (printer
->printer_ref
)
1875 DNSServiceRefDeallocate(printer
->printer_ref
);
1877 if (printer
->ipp_ref
)
1878 DNSServiceRefDeallocate(printer
->ipp_ref
);
1881 if (printer
->ipps_ref
)
1882 DNSServiceRefDeallocate(printer
->ipps_ref
);
1883 # endif /* HAVE_SSL */
1884 if (printer
->http_ref
)
1885 DNSServiceRefDeallocate(printer
->http_ref
);
1887 if (printer
->common_ref
)
1888 DNSServiceRefDeallocate(printer
->common_ref
);
1890 TXTRecordDeallocate(&(printer
->ipp_txt
));
1892 if (printer
->dnssd_name
)
1893 free(printer
->dnssd_name
);
1894 #endif /* HAVE_DNSSD */
1897 free(printer
->name
);
1899 free(printer
->icon
);
1900 if (printer
->command
)
1901 free(printer
->command
);
1902 if (printer
->directory
)
1903 free(printer
->directory
);
1904 if (printer
->hostname
)
1905 free(printer
->hostname
);
1909 ippDelete(printer
->attrs
);
1910 cupsArrayDelete(printer
->jobs
);
1918 * 'dnssd_callback()' - Handle Bonjour registration events.
1923 DNSServiceRef sdRef
, /* I - Service reference */
1924 DNSServiceFlags flags
, /* I - Status flags */
1925 DNSServiceErrorType errorCode
, /* I - Error, if any */
1926 const char *name
, /* I - Service name */
1927 const char *regtype
, /* I - Service type */
1928 const char *domain
, /* I - Domain for service */
1929 _ipp_printer_t
*printer
) /* I - Printer */
1933 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
1934 regtype
, (int)errorCode
);
1937 else if (_cups_strcasecmp(name
, printer
->dnssd_name
))
1940 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
1942 /* No lock needed since only the main thread accesses/changes this */
1943 free(printer
->dnssd_name
);
1944 printer
->dnssd_name
= strdup(name
);
1947 #endif /* HAVE_DNSSD */
1951 * 'find_job()' - Find a job specified in a request.
1954 static _ipp_job_t
* /* O - Job or NULL */
1955 find_job(_ipp_client_t
*client
) /* I - Client */
1957 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
1958 _ipp_job_t key
, /* Job search key */
1959 *job
; /* Matching job, if any */
1964 if ((attr
= ippFindAttribute(client
->request
, "job-uri",
1965 IPP_TAG_URI
)) != NULL
)
1967 const char *uri
= ippGetString(attr
, 0, NULL
);
1969 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
1970 uri
[client
->printer
->urilen
] == '/')
1971 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
1973 else if ((attr
= ippFindAttribute(client
->request
, "job-id",
1974 IPP_TAG_INTEGER
)) != NULL
)
1975 key
.id
= ippGetInteger(attr
, 0);
1977 _cupsRWLockRead(&(client
->printer
->rwlock
));
1978 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
1979 _cupsRWUnlock(&(client
->printer
->rwlock
));
1986 * 'html_escape()' - Write a HTML-safe string.
1990 html_escape(_ipp_client_t
*client
, /* I - Client */
1991 const char *s
, /* I - String to write */
1992 size_t slen
) /* I - Number of characters to write */
1994 const char *start
, /* Start of segment */
1995 *end
; /* End of string */
1999 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2001 while (*s
&& s
< end
)
2003 if (*s
== '&' || *s
== '<')
2006 httpWrite2(client
->http
, start
, s
- start
);
2009 httpWrite2(client
->http
, "&", 5);
2011 httpWrite2(client
->http
, "<", 4);
2020 httpWrite2(client
->http
, start
, s
- start
);
2025 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2029 html_printf(_ipp_client_t
*client
, /* I - Client */
2030 const char *format
, /* I - Printf-style format string */
2031 ...) /* I - Additional arguments as needed */
2033 va_list ap
; /* Pointer to arguments */
2034 const char *start
; /* Start of string */
2035 char size
, /* Size character (h, l, L) */
2036 type
; /* Format type character */
2037 int width
, /* Width of field */
2038 prec
; /* Number of characters of precision */
2039 char tformat
[100], /* Temporary format string for sprintf() */
2040 *tptr
, /* Pointer into temporary format */
2041 temp
[1024]; /* Buffer for formatted numbers */
2042 char *s
; /* Pointer to string */
2046 * Loop through the format string, formatting as needed...
2049 va_start(ap
, format
);
2057 httpWrite2(client
->http
, start
, format
- start
);
2060 *tptr
++ = *format
++;
2064 httpWrite2(client
->http
, "%", 1);
2068 else if (strchr(" -+#\'", *format
))
2069 *tptr
++ = *format
++;
2074 * Get width from argument...
2078 width
= va_arg(ap
, int);
2080 snprintf(tptr
, sizeof(tformat
) - (tptr
- tformat
), "%d", width
);
2081 tptr
+= strlen(tptr
);
2087 while (isdigit(*format
& 255))
2089 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2092 width
= width
* 10 + *format
++ - '0';
2098 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2106 * Get precision from argument...
2110 prec
= va_arg(ap
, int);
2112 snprintf(tptr
, sizeof(tformat
) - (tptr
- tformat
), "%d", prec
);
2113 tptr
+= strlen(tptr
);
2119 while (isdigit(*format
& 255))
2121 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2124 prec
= prec
* 10 + *format
++ - '0';
2129 if (*format
== 'l' && format
[1] == 'l')
2133 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2141 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2143 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2158 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2167 case 'E' : /* Floating point formats */
2172 if ((width
+ 2) > sizeof(temp
))
2175 sprintf(temp
, tformat
, va_arg(ap
, double));
2177 httpWrite2(client
->http
, temp
, strlen(temp
));
2180 case 'B' : /* Integer formats */
2188 if ((width
+ 2) > sizeof(temp
))
2191 # ifdef HAVE_LONG_LONG
2193 sprintf(temp
, tformat
, va_arg(ap
, long long));
2195 # endif /* HAVE_LONG_LONG */
2197 sprintf(temp
, tformat
, va_arg(ap
, long));
2199 sprintf(temp
, tformat
, va_arg(ap
, int));
2201 httpWrite2(client
->http
, temp
, strlen(temp
));
2204 case 'p' : /* Pointer value */
2205 if ((width
+ 2) > sizeof(temp
))
2208 sprintf(temp
, tformat
, va_arg(ap
, void *));
2210 httpWrite2(client
->http
, temp
, strlen(temp
));
2213 case 'c' : /* Character or character array */
2216 temp
[0] = va_arg(ap
, int);
2218 html_escape(client
, temp
, 1);
2221 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2224 case 's' : /* String */
2225 if ((s
= va_arg(ap
, char *)) == NULL
)
2228 html_escape(client
, s
, strlen(s
));
2237 httpWrite2(client
->http
, start
, format
- start
);
2244 * 'ipp_cancel_job()' - Cancel a job.
2248 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2250 _ipp_job_t
*job
; /* Job information */
2257 if ((job
= find_job(client
)) == NULL
)
2259 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2264 * See if the job is already completed, canceled, or aborted; if so,
2265 * we can't cancel...
2270 case IPP_JSTATE_CANCELED
:
2271 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2272 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2275 case IPP_JSTATE_ABORTED
:
2276 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2277 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2280 case IPP_JSTATE_COMPLETED
:
2281 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2282 "Job #%d is already completed - can\'t cancel.", job
->id
);
2290 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2292 if (job
->state
== IPP_JSTATE_PROCESSING
||
2293 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2297 job
->state
= IPP_JSTATE_CANCELED
;
2298 job
->completed
= time(NULL
);
2301 _cupsRWUnlock(&(client
->printer
->rwlock
));
2303 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2310 * 'ipp_create_job()' - Create a job object.
2314 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2316 _ipp_job_t
*job
; /* New job */
2317 cups_array_t
*ra
; /* Attributes to send in response */
2321 * Validate print job attributes...
2324 if (!valid_job_attributes(client
))
2326 httpFlush(client
->http
);
2331 * Do we have a file to print?
2334 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2336 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2337 "Unexpected document data following request.");
2345 if ((job
= create_job(client
)) == NULL
)
2347 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2348 "Currently printing another job.");
2353 * Return the job info...
2356 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2358 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2359 cupsArrayAdd(ra
, "job-id");
2360 cupsArrayAdd(ra
, "job-state");
2361 cupsArrayAdd(ra
, "job-state-reasons");
2362 cupsArrayAdd(ra
, "job-uri");
2364 copy_job_attributes(client
, job
, ra
);
2365 cupsArrayDelete(ra
);
2370 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2374 ipp_get_job_attributes(
2375 _ipp_client_t
*client
) /* I - Client */
2377 _ipp_job_t
*job
; /* Job */
2378 cups_array_t
*ra
; /* requested-attributes */
2381 if ((job
= find_job(client
)) == NULL
)
2383 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2387 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2389 ra
= ippCreateRequestedArray(client
->request
);
2390 copy_job_attributes(client
, job
, ra
);
2391 cupsArrayDelete(ra
);
2396 * 'ipp_get_jobs()' - Get a list of job objects.
2400 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2402 ipp_attribute_t
*attr
; /* Current attribute */
2403 const char *which_jobs
= NULL
;
2404 /* which-jobs values */
2405 int job_comparison
; /* Job comparison */
2406 ipp_jstate_t job_state
; /* job-state value */
2407 int first_job_id
, /* First job ID */
2408 limit
, /* Maximum number of jobs to return */
2409 count
; /* Number of jobs that match */
2410 const char *username
; /* Username */
2411 _ipp_job_t
*job
; /* Current job pointer */
2412 cups_array_t
*ra
; /* Requested attributes array */
2416 * See if the "which-jobs" attribute have been specified...
2419 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2420 IPP_TAG_KEYWORD
)) != NULL
)
2422 which_jobs
= ippGetString(attr
, 0, NULL
);
2423 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
2426 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
2428 job_comparison
= -1;
2429 job_state
= IPP_JSTATE_STOPPED
;
2431 else if (!strcmp(which_jobs
, "completed"))
2434 job_state
= IPP_JSTATE_CANCELED
;
2436 else if (!strcmp(which_jobs
, "aborted"))
2439 job_state
= IPP_JSTATE_ABORTED
;
2441 else if (!strcmp(which_jobs
, "all"))
2444 job_state
= IPP_JSTATE_PENDING
;
2446 else if (!strcmp(which_jobs
, "canceled"))
2449 job_state
= IPP_JSTATE_CANCELED
;
2451 else if (!strcmp(which_jobs
, "pending"))
2454 job_state
= IPP_JSTATE_PENDING
;
2456 else if (!strcmp(which_jobs
, "pending-held"))
2459 job_state
= IPP_JSTATE_HELD
;
2461 else if (!strcmp(which_jobs
, "processing"))
2464 job_state
= IPP_JSTATE_PROCESSING
;
2466 else if (!strcmp(which_jobs
, "processing-stopped"))
2469 job_state
= IPP_JSTATE_STOPPED
;
2473 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
2474 "The which-jobs value \"%s\" is not supported.", which_jobs
);
2475 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2476 "which-jobs", NULL
, which_jobs
);
2481 * See if they want to limit the number of jobs reported...
2484 if ((attr
= ippFindAttribute(client
->request
, "limit",
2485 IPP_TAG_INTEGER
)) != NULL
)
2487 limit
= ippGetInteger(attr
, 0);
2489 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
2494 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2495 IPP_TAG_INTEGER
)) != NULL
)
2497 first_job_id
= ippGetInteger(attr
, 0);
2499 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
2506 * See if we only want to see jobs for a specific user...
2511 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2512 IPP_TAG_BOOLEAN
)) != NULL
)
2514 int my_jobs
= ippGetBoolean(attr
, 0);
2516 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
2517 my_jobs
? "true" : "false");
2521 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
2522 IPP_TAG_NAME
)) == NULL
)
2524 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2525 "Need requesting-user-name with my-jobs.");
2529 username
= ippGetString(attr
, 0, NULL
);
2531 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2532 client
->hostname
, username
);
2537 * OK, build a list of jobs for this printer...
2540 ra
= ippCreateRequestedArray(client
->request
);
2542 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2544 _cupsRWLockRead(&(client
->printer
->rwlock
));
2546 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
2547 (limit
<= 0 || count
< limit
) && job
;
2548 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
2551 * Filter out jobs that don't match...
2554 if ((job_comparison
< 0 && job
->state
> job_state
) ||
2555 (job_comparison
== 0 && job
->state
!= job_state
) ||
2556 (job_comparison
> 0 && job
->state
< job_state
) ||
2557 job
->id
< first_job_id
||
2558 (username
&& job
->username
&&
2559 _cups_strcasecmp(username
, job
->username
)))
2563 ippAddSeparator(client
->response
);
2566 copy_job_attributes(client
, job
, ra
);
2569 cupsArrayDelete(ra
);
2571 _cupsRWUnlock(&(client
->printer
->rwlock
));
2576 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2580 ipp_get_printer_attributes(
2581 _ipp_client_t
*client
) /* I - Client */
2583 cups_array_t
*ra
; /* Requested attributes array */
2584 _ipp_printer_t
*printer
; /* Printer */
2588 * Send the attributes...
2591 ra
= ippCreateRequestedArray(client
->request
);
2592 printer
= client
->printer
;
2594 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2596 _cupsRWLockRead(&(printer
->rwlock
));
2598 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
2599 IPP_TAG_CUPS_CONST
);
2601 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
2602 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
2603 "printer-state", printer
->state
);
2605 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
2607 if (printer
->state_reasons
== _IPP_PSTATE_NONE
)
2608 ippAddString(client
->response
, IPP_TAG_PRINTER
,
2609 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
2610 "printer-state-reasons", NULL
, "none");
2613 int num_reasons
= 0;/* Number of reasons */
2614 const char *reasons
[32]; /* Reason strings */
2616 if (printer
->state_reasons
& _IPP_PSTATE_OTHER
)
2617 reasons
[num_reasons
++] = "other";
2618 if (printer
->state_reasons
& _IPP_PSTATE_COVER_OPEN
)
2619 reasons
[num_reasons
++] = "cover-open";
2620 if (printer
->state_reasons
& _IPP_PSTATE_INPUT_TRAY_MISSING
)
2621 reasons
[num_reasons
++] = "input-tray-missing";
2622 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_SUPPLY_EMPTY
)
2623 reasons
[num_reasons
++] = "marker-supply-empty-warning";
2624 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_SUPPLY_LOW
)
2625 reasons
[num_reasons
++] = "marker-supply-low-report";
2626 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL
)
2627 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
2628 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_WASTE_FULL
)
2629 reasons
[num_reasons
++] = "marker-waste-full-warning";
2630 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_EMPTY
)
2631 reasons
[num_reasons
++] = "media-empty-warning";
2632 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_JAM
)
2633 reasons
[num_reasons
++] = "media-jam-warning";
2634 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_LOW
)
2635 reasons
[num_reasons
++] = "media-low-report";
2636 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_NEEDED
)
2637 reasons
[num_reasons
++] = "media-needed-report";
2638 if (printer
->state_reasons
& _IPP_PSTATE_MOVING_TO_PAUSED
)
2639 reasons
[num_reasons
++] = "moving-to-paused";
2640 if (printer
->state_reasons
& _IPP_PSTATE_PAUSED
)
2641 reasons
[num_reasons
++] = "paused";
2642 if (printer
->state_reasons
& _IPP_PSTATE_SPOOL_AREA_FULL
)
2643 reasons
[num_reasons
++] = "spool-area-full";
2644 if (printer
->state_reasons
& _IPP_PSTATE_TONER_EMPTY
)
2645 reasons
[num_reasons
++] = "toner-empty-warning";
2646 if (printer
->state_reasons
& _IPP_PSTATE_TONER_LOW
)
2647 reasons
[num_reasons
++] = "toner-low-report";
2649 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
2650 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
2651 "printer-state-reasons", num_reasons
, NULL
, reasons
);
2655 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
2656 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2657 "printer-up-time", (int)time(NULL
));
2659 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
2660 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2662 printer
->active_job
&&
2663 printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
2665 _cupsRWUnlock(&(printer
->rwlock
));
2667 cupsArrayDelete(ra
);
2672 * 'ipp_print_job()' - Create a job object with an attached document.
2676 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
2678 _ipp_job_t
*job
; /* New job */
2679 char filename
[1024], /* Filename buffer */
2680 buffer
[4096]; /* Copy buffer */
2681 ssize_t bytes
; /* Bytes read */
2682 cups_array_t
*ra
; /* Attributes to send in response */
2686 * Validate print job attributes...
2689 if (!valid_job_attributes(client
))
2691 httpFlush(client
->http
);
2696 * Do we have a file to print?
2699 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
2701 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
2709 if ((job
= create_job(client
)) == NULL
)
2711 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2712 "Currently printing another job.");
2717 * Create a file for the request data...
2720 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
2721 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
2722 client
->printer
->directory
, job
->id
);
2723 else if (!_cups_strcasecmp(job
->format
, "image/png"))
2724 snprintf(filename
, sizeof(filename
), "%s/%d.png",
2725 client
->printer
->directory
, job
->id
);
2726 else if (!_cups_strcasecmp(job
->format
, "image/pwg-raster"))
2727 snprintf(filename
, sizeof(filename
), "%s/%d.ras",
2728 client
->printer
->directory
, job
->id
);
2729 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
2730 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
2731 client
->printer
->directory
, job
->id
);
2732 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
2733 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
2734 client
->printer
->directory
, job
->id
);
2736 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
2737 client
->printer
->directory
, job
->id
);
2739 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
2741 job
->state
= IPP_JSTATE_ABORTED
;
2743 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2744 "Unable to create print file: %s", strerror(errno
));
2748 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
2750 if (write(job
->fd
, buffer
, bytes
) < bytes
)
2752 int error
= errno
; /* Write error */
2754 job
->state
= IPP_JSTATE_ABORTED
;
2761 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2762 "Unable to write print file: %s", strerror(error
));
2770 * Got an error while reading the print data, so abort this job.
2773 job
->state
= IPP_JSTATE_ABORTED
;
2780 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2781 "Unable to read print file.");
2787 int error
= errno
; /* Write error */
2789 job
->state
= IPP_JSTATE_ABORTED
;
2794 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2795 "Unable to write print file: %s", strerror(error
));
2800 job
->filename
= strdup(filename
);
2801 job
->state
= IPP_JSTATE_PENDING
;
2804 * Process the job...
2808 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
2810 job
->state
= IPP_JSTATE_ABORTED
;
2811 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
2820 * Return the job info...
2823 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2825 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2826 cupsArrayAdd(ra
, "job-id");
2827 cupsArrayAdd(ra
, "job-state");
2828 cupsArrayAdd(ra
, "job-state-reasons");
2829 cupsArrayAdd(ra
, "job-uri");
2831 copy_job_attributes(client
, job
, ra
);
2832 cupsArrayDelete(ra
);
2837 * 'ipp_print_uri()' - Create a job object with a referenced document.
2841 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
2843 _ipp_job_t
*job
; /* New job */
2844 ipp_attribute_t
*uri
; /* document-uri */
2845 char scheme
[256], /* URI scheme */
2846 userpass
[256], /* Username and password info */
2847 hostname
[256], /* Hostname */
2848 resource
[1024]; /* Resource path */
2849 int port
; /* Port number */
2850 http_uri_status_t uri_status
; /* URI decode status */
2851 http_encryption_t encryption
; /* Encryption to use, if any */
2852 http_t
*http
; /* Connection for http/https URIs */
2853 http_status_t status
; /* Access status for http/https URIs */
2854 int infile
; /* Input file for local file URIs */
2855 char filename
[1024], /* Filename buffer */
2856 buffer
[4096]; /* Copy buffer */
2857 ssize_t bytes
; /* Bytes read */
2858 cups_array_t
*ra
; /* Attributes to send in response */
2859 static const char * const uri_status_strings
[] =
2860 { /* URI decode errors */
2862 "Bad arguments to function.",
2863 "Bad resource in URI.",
2864 "Bad port number in URI.",
2865 "Bad hostname in URI.",
2866 "Bad username in URI.",
2867 "Bad scheme in URI.",
2873 * Validate print job attributes...
2876 if (!valid_job_attributes(client
))
2878 httpFlush(client
->http
);
2883 * Do we have a file to print?
2886 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2888 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2889 "Unexpected document data following request.");
2894 * Do we have a document URI?
2897 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
2898 IPP_TAG_URI
)) == NULL
)
2900 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
2904 if (ippGetCount(uri
) != 1)
2906 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2907 "Too many document-uri values.");
2911 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
2912 scheme
, sizeof(scheme
), userpass
,
2913 sizeof(userpass
), hostname
, sizeof(hostname
),
2914 &port
, resource
, sizeof(resource
));
2915 if (uri_status
< HTTP_URI_STATUS_OK
)
2917 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
2918 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
2922 if (strcmp(scheme
, "file") &&
2924 strcmp(scheme
, "https") &&
2925 #endif /* HAVE_SSL */
2926 strcmp(scheme
, "http"))
2928 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
2929 "URI scheme \"%s\" not supported.", scheme
);
2933 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
2935 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
2936 "Unable to access URI: %s", strerror(errno
));
2944 if ((job
= create_job(client
)) == NULL
)
2946 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2947 "Currently printing another job.");
2952 * Create a file for the request data...
2955 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
2956 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
2957 client
->printer
->directory
, job
->id
);
2958 else if (!_cups_strcasecmp(job
->format
, "image/png"))
2959 snprintf(filename
, sizeof(filename
), "%s/%d.png",
2960 client
->printer
->directory
, job
->id
);
2961 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
2962 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
2963 client
->printer
->directory
, job
->id
);
2964 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
2965 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
2966 client
->printer
->directory
, job
->id
);
2968 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
2969 client
->printer
->directory
, job
->id
);
2971 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
2973 job
->state
= IPP_JSTATE_ABORTED
;
2975 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2976 "Unable to create print file: %s", strerror(errno
));
2980 if (!strcmp(scheme
, "file"))
2982 if ((infile
= open(resource
, O_RDONLY
)) < 0)
2984 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
2985 "Unable to access URI: %s", strerror(errno
));
2991 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
2992 (errno
== EAGAIN
|| errno
== EINTR
))
2994 else if (bytes
> 0 && write(job
->fd
, buffer
, bytes
) < bytes
)
2996 int error
= errno
; /* Write error */
2998 job
->state
= IPP_JSTATE_ABORTED
;
3006 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3007 "Unable to write print file: %s", strerror(error
));
3018 if (port
== 443 || !strcmp(scheme
, "https"))
3019 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3021 #endif /* HAVE_SSL */
3022 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3024 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3025 1, 30000, NULL
)) == NULL
)
3027 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3028 "Unable to connect to %s: %s", hostname
,
3029 cupsLastErrorString());
3030 job
->state
= IPP_JSTATE_ABORTED
;
3039 httpClearFields(http
);
3040 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3041 if (httpGet(http
, resource
))
3043 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3044 "Unable to GET URI: %s", strerror(errno
));
3046 job
->state
= IPP_JSTATE_ABORTED
;
3056 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3058 if (status
!= HTTP_STATUS_OK
)
3060 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3061 "Unable to GET URI: %s", httpStatus(status
));
3063 job
->state
= IPP_JSTATE_ABORTED
;
3073 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3075 if (write(job
->fd
, buffer
, bytes
) < bytes
)
3077 int error
= errno
; /* Write error */
3079 job
->state
= IPP_JSTATE_ABORTED
;
3087 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3088 "Unable to write print file: %s", strerror(error
));
3098 int error
= errno
; /* Write error */
3100 job
->state
= IPP_JSTATE_ABORTED
;
3105 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3106 "Unable to write print file: %s", strerror(error
));
3111 job
->filename
= strdup(filename
);
3112 job
->state
= IPP_JSTATE_PENDING
;
3115 * Process the job...
3119 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3121 job
->state
= IPP_JSTATE_ABORTED
;
3122 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3131 * Return the job info...
3134 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3136 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3137 cupsArrayAdd(ra
, "job-id");
3138 cupsArrayAdd(ra
, "job-state");
3139 cupsArrayAdd(ra
, "job-state-reasons");
3140 cupsArrayAdd(ra
, "job-uri");
3142 copy_job_attributes(client
, job
, ra
);
3143 cupsArrayDelete(ra
);
3148 * 'ipp_send_document()' - Add an attached document to a job object created with
3153 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3155 _ipp_job_t
*job
; /* Job information */
3156 char filename
[1024], /* Filename buffer */
3157 buffer
[4096]; /* Copy buffer */
3158 ssize_t bytes
; /* Bytes read */
3159 ipp_attribute_t
*attr
; /* Current attribute */
3160 cups_array_t
*ra
; /* Attributes to send in response */
3167 if ((job
= find_job(client
)) == NULL
)
3169 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3170 httpFlush(client
->http
);
3175 * See if we already have a document for this job or the job has already
3176 * in a non-pending state...
3179 if (job
->state
> IPP_JSTATE_HELD
)
3181 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3182 "Job is not in a pending state.");
3183 httpFlush(client
->http
);
3186 else if (job
->filename
|| job
->fd
>= 0)
3188 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3189 "Multiple document jobs are not supported.");
3190 httpFlush(client
->http
);
3194 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3195 IPP_TAG_ZERO
)) == NULL
)
3197 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3198 "Missing required last-document attribute.");
3199 httpFlush(client
->http
);
3202 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3203 !ippGetBoolean(attr
, 0))
3205 respond_unsupported(client
, attr
);
3206 httpFlush(client
->http
);
3211 * Validate document attributes...
3214 if (!valid_doc_attributes(client
))
3216 httpFlush(client
->http
);
3221 * Get the document format for the job...
3224 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3226 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3227 IPP_TAG_MIMETYPE
)) != NULL
)
3228 job
->format
= ippGetString(attr
, 0, NULL
);
3230 job
->format
= "application/octet-stream";
3233 * Create a file for the request data...
3236 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3237 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3238 client
->printer
->directory
, job
->id
);
3239 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3240 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3241 client
->printer
->directory
, job
->id
);
3242 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3243 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3244 client
->printer
->directory
, job
->id
);
3245 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3246 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3247 client
->printer
->directory
, job
->id
);
3249 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3250 client
->printer
->directory
, job
->id
);
3252 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3254 _cupsRWUnlock(&(client
->printer
->rwlock
));
3258 job
->state
= IPP_JSTATE_ABORTED
;
3260 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3261 "Unable to create print file: %s", strerror(errno
));
3265 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3267 if (write(job
->fd
, buffer
, bytes
) < bytes
)
3269 int error
= errno
; /* Write error */
3271 job
->state
= IPP_JSTATE_ABORTED
;
3278 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3279 "Unable to write print file: %s", strerror(error
));
3287 * Got an error while reading the print data, so abort this job.
3290 job
->state
= IPP_JSTATE_ABORTED
;
3297 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3298 "Unable to read print file.");
3304 int error
= errno
; /* Write error */
3306 job
->state
= IPP_JSTATE_ABORTED
;
3311 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3312 "Unable to write print file: %s", strerror(error
));
3316 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3319 job
->filename
= strdup(filename
);
3320 job
->state
= IPP_JSTATE_PENDING
;
3322 _cupsRWUnlock(&(client
->printer
->rwlock
));
3325 * Process the job...
3329 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3331 job
->state
= IPP_JSTATE_ABORTED
;
3332 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3341 * Return the job info...
3344 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3346 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3347 cupsArrayAdd(ra
, "job-id");
3348 cupsArrayAdd(ra
, "job-state");
3349 cupsArrayAdd(ra
, "job-state-reasons");
3350 cupsArrayAdd(ra
, "job-uri");
3352 copy_job_attributes(client
, job
, ra
);
3353 cupsArrayDelete(ra
);
3358 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3363 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
3365 _ipp_job_t
*job
; /* Job information */
3366 ipp_attribute_t
*uri
; /* document-uri */
3367 char scheme
[256], /* URI scheme */
3368 userpass
[256], /* Username and password info */
3369 hostname
[256], /* Hostname */
3370 resource
[1024]; /* Resource path */
3371 int port
; /* Port number */
3372 http_uri_status_t uri_status
; /* URI decode status */
3373 http_encryption_t encryption
; /* Encryption to use, if any */
3374 http_t
*http
; /* Connection for http/https URIs */
3375 http_status_t status
; /* Access status for http/https URIs */
3376 int infile
; /* Input file for local file URIs */
3377 char filename
[1024], /* Filename buffer */
3378 buffer
[4096]; /* Copy buffer */
3379 ssize_t bytes
; /* Bytes read */
3380 ipp_attribute_t
*attr
; /* Current attribute */
3381 cups_array_t
*ra
; /* Attributes to send in response */
3382 static const char * const uri_status_strings
[] =
3383 { /* URI decode errors */
3385 "Bad arguments to function.",
3386 "Bad resource in URI.",
3387 "Bad port number in URI.",
3388 "Bad hostname in URI.",
3389 "Bad username in URI.",
3390 "Bad scheme in URI.",
3399 if ((job
= find_job(client
)) == NULL
)
3401 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3402 httpFlush(client
->http
);
3407 * See if we already have a document for this job or the job has already
3408 * in a non-pending state...
3411 if (job
->state
> IPP_JSTATE_HELD
)
3413 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3414 "Job is not in a pending state.");
3415 httpFlush(client
->http
);
3418 else if (job
->filename
|| job
->fd
>= 0)
3420 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3421 "Multiple document jobs are not supported.");
3422 httpFlush(client
->http
);
3426 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3427 IPP_TAG_ZERO
)) == NULL
)
3429 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3430 "Missing required last-document attribute.");
3431 httpFlush(client
->http
);
3434 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3435 !ippGetBoolean(attr
, 0))
3437 respond_unsupported(client
, attr
);
3438 httpFlush(client
->http
);
3443 * Validate document attributes...
3446 if (!valid_doc_attributes(client
))
3448 httpFlush(client
->http
);
3453 * Do we have a file to print?
3456 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3458 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3459 "Unexpected document data following request.");
3464 * Do we have a document URI?
3467 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3468 IPP_TAG_URI
)) == NULL
)
3470 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3474 if (ippGetCount(uri
) != 1)
3476 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3477 "Too many document-uri values.");
3481 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3482 scheme
, sizeof(scheme
), userpass
,
3483 sizeof(userpass
), hostname
, sizeof(hostname
),
3484 &port
, resource
, sizeof(resource
));
3485 if (uri_status
< HTTP_URI_STATUS_OK
)
3487 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3488 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3492 if (strcmp(scheme
, "file") &&
3494 strcmp(scheme
, "https") &&
3495 #endif /* HAVE_SSL */
3496 strcmp(scheme
, "http"))
3498 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3499 "URI scheme \"%s\" not supported.", scheme
);
3503 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3505 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3506 "Unable to access URI: %s", strerror(errno
));
3511 * Get the document format for the job...
3514 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3516 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3517 IPP_TAG_MIMETYPE
)) != NULL
)
3518 job
->format
= ippGetString(attr
, 0, NULL
);
3520 job
->format
= "application/octet-stream";
3523 * Create a file for the request data...
3526 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3527 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3528 client
->printer
->directory
, job
->id
);
3529 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3530 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3531 client
->printer
->directory
, job
->id
);
3532 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3533 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3534 client
->printer
->directory
, job
->id
);
3535 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3536 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3537 client
->printer
->directory
, job
->id
);
3539 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3540 client
->printer
->directory
, job
->id
);
3542 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3544 _cupsRWUnlock(&(client
->printer
->rwlock
));
3548 job
->state
= IPP_JSTATE_ABORTED
;
3550 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3551 "Unable to create print file: %s", strerror(errno
));
3555 if (!strcmp(scheme
, "file"))
3557 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3559 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3560 "Unable to access URI: %s", strerror(errno
));
3566 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3567 (errno
== EAGAIN
|| errno
== EINTR
))
3569 else if (bytes
> 0 && write(job
->fd
, buffer
, bytes
) < bytes
)
3571 int error
= errno
; /* Write error */
3573 job
->state
= IPP_JSTATE_ABORTED
;
3581 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3582 "Unable to write print file: %s", strerror(error
));
3593 if (port
== 443 || !strcmp(scheme
, "https"))
3594 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3596 #endif /* HAVE_SSL */
3597 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3599 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3600 1, 30000, NULL
)) == NULL
)
3602 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3603 "Unable to connect to %s: %s", hostname
,
3604 cupsLastErrorString());
3605 job
->state
= IPP_JSTATE_ABORTED
;
3614 httpClearFields(http
);
3615 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3616 if (httpGet(http
, resource
))
3618 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3619 "Unable to GET URI: %s", strerror(errno
));
3621 job
->state
= IPP_JSTATE_ABORTED
;
3631 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3633 if (status
!= HTTP_STATUS_OK
)
3635 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3636 "Unable to GET URI: %s", httpStatus(status
));
3638 job
->state
= IPP_JSTATE_ABORTED
;
3648 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3650 if (write(job
->fd
, buffer
, bytes
) < bytes
)
3652 int error
= errno
; /* Write error */
3654 job
->state
= IPP_JSTATE_ABORTED
;
3662 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3663 "Unable to write print file: %s", strerror(error
));
3673 int error
= errno
; /* Write error */
3675 job
->state
= IPP_JSTATE_ABORTED
;
3680 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3681 "Unable to write print file: %s", strerror(error
));
3685 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3688 job
->filename
= strdup(filename
);
3689 job
->state
= IPP_JSTATE_PENDING
;
3691 _cupsRWUnlock(&(client
->printer
->rwlock
));
3694 * Process the job...
3698 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3700 job
->state
= IPP_JSTATE_ABORTED
;
3701 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3710 * Return the job info...
3713 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3715 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3716 cupsArrayAdd(ra
, "job-id");
3717 cupsArrayAdd(ra
, "job-state");
3718 cupsArrayAdd(ra
, "job-state-reasons");
3719 cupsArrayAdd(ra
, "job-uri");
3721 copy_job_attributes(client
, job
, ra
);
3722 cupsArrayDelete(ra
);
3727 * 'ipp_validate_job()' - Validate job creation attributes.
3731 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
3733 if (valid_job_attributes(client
))
3734 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3739 * 'process_client()' - Process client requests on a thread.
3742 static void * /* O - Exit status */
3743 process_client(_ipp_client_t
*client
) /* I - Client */
3746 * Loop until we are out of requests or timeout (30 seconds)...
3749 while (httpWait(client
->http
, 30000))
3750 if (!process_http(client
))
3754 * Close the conection to the client and return...
3757 delete_client(client
);
3764 * 'process_http()' - Process a HTTP request.
3767 int /* O - 1 on success, 0 on failure */
3768 process_http(_ipp_client_t
*client
) /* I - Client connection */
3770 char uri
[1024]; /* URI */
3771 http_state_t http_state
; /* HTTP state */
3772 http_status_t http_status
; /* HTTP status */
3773 ipp_state_t ipp_state
; /* State of IPP transfer */
3774 char scheme
[32], /* Method/scheme */
3775 userpass
[128], /* Username:password */
3776 hostname
[HTTP_MAX_HOST
];
3778 int port
; /* Port number */
3779 const char *encoding
; /* Content-Encoding value */
3780 static const char * const http_states
[] =
3781 { /* Strings for logging HTTP method */
3802 * Clear state variables...
3805 ippDelete(client
->request
);
3806 ippDelete(client
->response
);
3808 client
->request
= NULL
;
3809 client
->response
= NULL
;
3810 client
->operation
= HTTP_STATE_WAITING
;
3813 * Read a request from the connection...
3816 while ((http_state
= httpReadRequest(client
->http
, uri
,
3817 sizeof(uri
))) == HTTP_STATE_WAITING
)
3821 * Parse the request line...
3824 if (http_state
== HTTP_STATE_ERROR
)
3826 if (httpError(client
->http
) == EPIPE
)
3827 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
3829 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
3830 strerror(httpError(client
->http
)));
3834 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
3836 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
3837 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3840 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
3842 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
3843 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3847 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
3851 * Separate the URI into its components...
3854 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
3855 userpass
, sizeof(userpass
),
3856 hostname
, sizeof(hostname
), &port
,
3857 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
)
3859 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
3860 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3865 * Process the request...
3868 client
->start
= time(NULL
);
3869 client
->operation
= httpGetState(client
->http
);
3872 * Parse incoming parameters until the status changes...
3875 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
3877 if (http_status
!= HTTP_STATUS_OK
)
3879 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3883 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
3884 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
3887 * HTTP/1.1 and higher require the "Host:" field...
3890 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3895 * Handle HTTP Upgrade...
3898 if (!_cups_strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
3901 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
3906 * Handle HTTP Expect...
3909 if (httpGetExpect(client
->http
) &&
3910 (client
->operation
== HTTP_STATE_POST
||
3911 client
->operation
== HTTP_STATE_PUT
))
3913 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
3916 * Send 100-continue header...
3919 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
3925 * Send 417-expectation-failed header...
3928 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
3934 * Handle new transfers...
3937 encoding
= httpGetContentEncoding(client
->http
);
3939 switch (client
->operation
)
3941 case HTTP_STATE_OPTIONS
:
3943 * Do HEAD/OPTIONS command...
3946 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
3948 case HTTP_STATE_HEAD
:
3949 if (!strcmp(client
->uri
, "/icon.png"))
3950 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
3951 else if (!strcmp(client
->uri
, "/"))
3952 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
3954 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
3957 case HTTP_STATE_GET
:
3958 if (!strcmp(client
->uri
, "/icon.png"))
3961 * Send PNG icon file.
3964 int fd
; /* Icon file */
3965 struct stat fileinfo
; /* Icon file information */
3966 char buffer
[4096]; /* Copy buffer */
3967 ssize_t bytes
; /* Bytes */
3969 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
3971 if (!stat(client
->printer
->icon
, &fileinfo
) &&
3972 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
3974 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
3981 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
3982 httpWrite2(client
->http
, buffer
, bytes
);
3984 httpFlushWrite(client
->http
);
3989 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
3991 else if (!strcmp(client
->uri
, "/"))
3994 * Show web status page...
3997 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4001 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
4002 "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
4005 "<title>%s</title>\n"
4006 "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
4007 "type=\"image/png\">\n"
4011 "<h1><img align=\"right\" src=\"/icon.png\">%s</h1>\n"
4012 "<p>%s, %d job(s).</p>\n"
4015 client
->printer
->name
, client
->printer
->name
,
4016 client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" :
4017 client
->printer
->state
== IPP_PSTATE_PROCESSING
?
4018 "Printing" : "Stopped",
4019 cupsArrayCount(client
->printer
->jobs
));
4020 httpWrite2(client
->http
, "", 0);
4025 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4028 case HTTP_STATE_POST
:
4029 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
4033 * Not an IPP request...
4036 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
4040 * Read the IPP request...
4043 client
->request
= ippNew();
4045 while ((ipp_state
= ippRead(client
->http
,
4046 client
->request
)) != IPP_STATE_DATA
)
4048 if (ipp_state
== IPP_STATE_ERROR
)
4050 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
4051 cupsLastErrorString());
4052 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4058 * Now that we have the IPP request, process the request...
4061 return (process_ipp(client
));
4064 break; /* Anti-compiler-warning-code */
4072 * 'process_ipp()' - Process an IPP request.
4075 static int /* O - 1 on success, 0 on error */
4076 process_ipp(_ipp_client_t
*client
) /* I - Client */
4078 ipp_tag_t group
; /* Current group tag */
4079 ipp_attribute_t
*attr
; /* Current attribute */
4080 ipp_attribute_t
*charset
; /* Character set attribute */
4081 ipp_attribute_t
*language
; /* Language attribute */
4082 ipp_attribute_t
*uri
; /* Printer URI attribute */
4083 int major
, minor
; /* Version number */
4084 const char *name
; /* Name of attribute */
4087 debug_attributes("Request", client
->request
, 1);
4090 * First build an empty response message for this request...
4093 client
->operation_id
= ippGetOperation(client
->request
);
4094 client
->response
= ippNewResponse(client
->request
);
4097 * Then validate the request header and required attributes...
4100 major
= ippGetVersion(client
->request
, &minor
);
4102 if (major
< 1 || major
> 2)
4105 * Return an error, since we only support IPP 1.x and 2.x.
4108 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
4109 "Bad request version number %d.%d.", major
, minor
);
4111 else if (ippGetRequestId(client
->request
) <= 0)
4112 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
4113 ippGetRequestId(client
->request
));
4114 else if (!ippFirstAttribute(client
->request
))
4115 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4116 "No attributes in request.");
4120 * Make sure that the attributes are provided in the correct order and
4121 * don't repeat groups...
4124 for (attr
= ippFirstAttribute(client
->request
),
4125 group
= ippGetGroupTag(attr
);
4127 attr
= ippNextAttribute(client
->request
))
4129 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
4132 * Out of order; return an error...
4135 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4136 "Attribute groups are out of order (%x < %x).",
4137 ippGetGroupTag(attr
), group
);
4141 group
= ippGetGroupTag(attr
);
4147 * Then make sure that the first three attributes are:
4149 * attributes-charset
4150 * attributes-natural-language
4151 * printer-uri/job-uri
4154 attr
= ippFirstAttribute(client
->request
);
4155 name
= ippGetName(attr
);
4156 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
4157 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
4162 attr
= ippNextAttribute(client
->request
);
4163 name
= ippGetName(attr
);
4165 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
4166 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
4171 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
4172 IPP_TAG_URI
)) != NULL
)
4174 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
4175 IPP_TAG_URI
)) != NULL
)
4181 _cups_strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
4182 _cups_strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
4185 * Bad character set...
4188 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4189 "Unsupported character set \"%s\".",
4190 ippGetString(charset
, 0, NULL
));
4192 else if (!charset
|| !language
|| !uri
)
4195 * Return an error, since attributes-charset,
4196 * attributes-natural-language, and printer-uri/job-uri are required
4197 * for all operations.
4200 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4201 "Missing required attributes.");
4205 char scheme
[32], /* URI scheme */
4206 userpass
[32], /* Username/password in URI */
4207 host
[256], /* Host name in URI */
4208 resource
[256]; /* Resource path in URI */
4209 int port
; /* Port number in URI */
4211 name
= ippGetName(uri
);
4213 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4214 scheme
, sizeof(scheme
),
4215 userpass
, sizeof(userpass
),
4216 host
, sizeof(host
), &port
,
4217 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
4218 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
4219 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
4220 else if ((!strcmp(name
, "job-uri") &&
4221 strncmp(resource
, "/ipp/print/", 11)) ||
4222 (!strcmp(name
, "printer-uri") &&
4223 strcmp(resource
, "/ipp/print")))
4224 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
4225 name
, ippGetString(uri
, 0, NULL
));
4229 * Try processing the operation...
4232 switch (ippGetOperation(client
->request
))
4234 case IPP_OP_PRINT_JOB
:
4235 ipp_print_job(client
);
4238 case IPP_OP_PRINT_URI
:
4239 ipp_print_uri(client
);
4242 case IPP_OP_VALIDATE_JOB
:
4243 ipp_validate_job(client
);
4246 case IPP_OP_CREATE_JOB
:
4247 ipp_create_job(client
);
4250 case IPP_OP_SEND_DOCUMENT
:
4251 ipp_send_document(client
);
4254 case IPP_OP_SEND_URI
:
4255 ipp_send_uri(client
);
4258 case IPP_OP_CANCEL_JOB
:
4259 ipp_cancel_job(client
);
4262 case IPP_OP_GET_JOB_ATTRIBUTES
:
4263 ipp_get_job_attributes(client
);
4266 case IPP_OP_GET_JOBS
:
4267 ipp_get_jobs(client
);
4270 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
4271 ipp_get_printer_attributes(client
);
4275 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
4276 "Operation not supported.");
4285 * Send the HTTP header and return...
4288 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
4289 httpFlush(client
->http
); /* Flush trailing (junk) data */
4291 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
4292 ippLength(client
->response
)));
4297 * 'process_job()' - Process a print job.
4300 static void * /* O - Thread exit status */
4301 process_job(_ipp_job_t
*job
) /* I - Job */
4303 job
->state
= IPP_JSTATE_PROCESSING
;
4304 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
4306 if (job
->printer
->command
)
4309 * Execute a command with the job spool file and wait for it to complete...
4312 int pid
, /* Process ID */
4313 status
; /* Exit status */
4314 time_t start
, /* Start time */
4317 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
4321 if ((pid
= fork()) == 0)
4324 * Child comes here...
4327 execlp(job
->printer
->command
, job
->printer
->command
, job
->filename
,
4334 * Unable to fork process...
4337 perror("Unable to start job processing command");
4342 * Wait for child to complete...
4346 while (waitpid(pid
, &status
, 0) < 0);
4348 while (wait(&status
) < 0);
4349 #endif /* HAVE_WAITPID */
4353 if (WIFEXITED(status
))
4354 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
4355 job
->printer
->command
, WEXITSTATUS(status
));
4357 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
4358 job
->printer
->command
, WTERMSIG(status
));
4361 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
4362 job
->printer
->command
);
4366 * Make sure processing takes at least 5 seconds...
4370 if ((end
- start
) < 5)
4376 * Sleep for a random amount of time to simulate job processing.
4379 sleep(5 + (rand() % 11));
4383 job
->state
= IPP_JSTATE_CANCELED
;
4385 job
->state
= IPP_JSTATE_COMPLETED
;
4387 job
->completed
= time(NULL
);
4388 job
->printer
->state
= IPP_PSTATE_IDLE
;
4389 job
->printer
->active_job
= NULL
;
4397 * 'register_printer()' - Register a printer object via Bonjour.
4400 static int /* O - 1 on success, 0 on error */
4402 _ipp_printer_t
*printer
, /* I - Printer */
4403 const char *location
, /* I - Location */
4404 const char *make
, /* I - Manufacturer */
4405 const char *model
, /* I - Model name */
4406 const char *formats
, /* I - Supported formats */
4407 const char *adminurl
, /* I - Web interface URL */
4408 int color
, /* I - 1 = color, 0 = monochrome */
4409 int duplex
, /* I - 1 = duplex, 0 = simplex */
4410 const char *subtype
) /* I - Service subtype */
4412 DNSServiceErrorType error
; /* Error from Bonjour */
4413 char make_model
[256],/* Make and model together */
4414 product
[256], /* Product string */
4415 regtype
[256]; /* Bonjour service type */
4419 * Build the TXT record for IPP...
4422 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
4423 snprintf(product
, sizeof(product
), "(%s)", model
);
4425 TXTRecordCreate(&(printer
->ipp_txt
), 1024, NULL
);
4426 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 9, "ipp/print");
4427 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
4429 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
4432 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
4434 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
4436 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
4438 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
4439 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
4440 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
4442 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
4446 * Create a shared service reference for Bonjour...
4449 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
4450 != kDNSServiceErr_NoError
)
4452 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
4457 * Register the _printer._tcp (LPD) service type with a port number of 0 to
4458 * defend our service name but not actually support LPD...
4461 printer
->printer_ref
= printer
->common_ref
;
4463 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
4464 kDNSServiceFlagsShareConnection
,
4465 0 /* interfaceIndex */, printer
->dnssd_name
,
4466 "_printer._tcp", NULL
/* domain */,
4467 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
4468 NULL
/* txtRecord */,
4469 (DNSServiceRegisterReply
)dnssd_callback
,
4470 printer
)) != kDNSServiceErr_NoError
)
4472 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
4473 printer
->dnssd_name
, error
);
4478 * Then register the _ipp._tcp (IPP) service type with the real port number to
4479 * advertise our IPP printer...
4482 printer
->ipp_ref
= printer
->common_ref
;
4484 if (subtype
&& *subtype
)
4485 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
4487 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
4489 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
4490 kDNSServiceFlagsShareConnection
,
4491 0 /* interfaceIndex */, printer
->dnssd_name
,
4492 regtype
, NULL
/* domain */,
4493 NULL
/* host */, htons(printer
->port
),
4494 TXTRecordGetLength(&(printer
->ipp_txt
)),
4495 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4496 (DNSServiceRegisterReply
)dnssd_callback
,
4497 printer
)) != kDNSServiceErr_NoError
)
4499 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4500 printer
->dnssd_name
, regtype
, error
);
4504 # if 0 /* ifdef HAVE_SSL */
4506 * Then register the _ipps._tcp (IPP) service type with the real port number to
4507 * advertise our IPP printer...
4510 printer
->ipps_ref
= printer
->common_ref
;
4512 if (subtype
&& *subtype
)
4513 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
4515 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
4517 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
4518 kDNSServiceFlagsShareConnection
,
4519 0 /* interfaceIndex */, printer
->dnssd_name
,
4520 regtype
, NULL
/* domain */,
4521 NULL
/* host */, htons(printer
->port
),
4522 TXTRecordGetLength(&(printer
->ipp_txt
)),
4523 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4524 (DNSServiceRegisterReply
)dnssd_callback
,
4525 printer
)) != kDNSServiceErr_NoError
)
4527 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4528 printer
->dnssd_name
, regtype
, error
);
4531 # endif /* HAVE_SSL */
4534 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
4535 * real port number to advertise our IPP printer...
4538 printer
->http_ref
= printer
->common_ref
;
4540 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
4541 kDNSServiceFlagsShareConnection
,
4542 0 /* interfaceIndex */, printer
->dnssd_name
,
4543 "_http._tcp,_printer", NULL
/* domain */,
4544 NULL
/* host */, htons(printer
->port
),
4545 0 /* txtLen */, NULL
, /* txtRecord */
4546 (DNSServiceRegisterReply
)dnssd_callback
,
4547 printer
)) != kDNSServiceErr_NoError
)
4549 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4550 printer
->dnssd_name
, regtype
, error
);
4556 #endif /* HAVE_DNSSD */
4560 * 'respond_http()' - Send a HTTP response.
4563 int /* O - 1 on success, 0 on failure */
4565 _ipp_client_t
*client
, /* I - Client */
4566 http_status_t code
, /* I - HTTP status of response */
4567 const char *content_encoding
, /* I - Content-Encoding of response */
4568 const char *type
, /* I - MIME media type of response */
4569 size_t length
) /* I - Length of response */
4571 char message
[1024]; /* Text message */
4574 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
4576 if (code
== HTTP_STATUS_CONTINUE
)
4579 * 100-continue doesn't send any headers...
4582 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
4586 * Format an error message...
4589 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
)
4591 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
4593 type
= "text/plain";
4594 length
= strlen(message
);
4600 * Send the HTTP response header...
4603 httpClearFields(client
->http
);
4605 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
4606 client
->operation
== HTTP_STATE_OPTIONS
)
4607 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
4611 if (!strcmp(type
, "text/html"))
4612 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
4613 "text/html; charset=utf-8");
4615 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
4617 if (content_encoding
)
4618 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
4621 httpSetLength(client
->http
, length
);
4623 if (httpWriteResponse(client
->http
, code
) < 0)
4627 * Send the response data...
4633 * Send a plain text message.
4636 if (httpPrintf(client
->http
, "%s", message
) < 0)
4639 if (httpWrite2(client
->http
, "", 0) < 0)
4642 else if (client
->response
)
4645 * Send an IPP response...
4648 debug_attributes("Response", client
->response
, 2);
4650 ippSetState(client
->response
, IPP_STATE_IDLE
);
4652 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
4661 * 'respond_ipp()' - Send an IPP response.
4665 respond_ipp(_ipp_client_t
*client
, /* I - Client */
4666 ipp_status_t status
, /* I - status-code */
4667 const char *message
, /* I - printf-style status-message */
4668 ...) /* I - Additional args as needed */
4670 const char *formatted
= NULL
; /* Formatted message */
4673 ippSetStatusCode(client
->response
, status
);
4677 va_list ap
; /* Pointer to additional args */
4678 ipp_attribute_t
*attr
; /* New status-message attribute */
4680 va_start(ap
, message
);
4681 if ((attr
= ippFindAttribute(client
->response
, "status-message",
4682 IPP_TAG_TEXT
)) != NULL
)
4683 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
4685 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
4686 "status-message", NULL
, message
, ap
);
4689 formatted
= ippGetString(attr
, 0, NULL
);
4693 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
4694 ippOpString(client
->operation_id
), ippErrorString(status
),
4697 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
4698 ippOpString(client
->operation_id
), ippErrorString(status
));
4703 * 'respond_unsupported()' - Respond with an unsupported attribute.
4707 respond_unsupported(
4708 _ipp_client_t
*client
, /* I - Client */
4709 ipp_attribute_t
*attr
) /* I - Atribute */
4711 ipp_attribute_t
*temp
; /* Copy of attribute */
4714 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
4715 "Unsupported %s %s%s value.", ippGetName(attr
),
4716 ippGetCount(attr
) > 1 ? "1setOf " : "",
4717 ippTagString(ippGetValueTag(attr
)));
4719 temp
= ippCopyAttribute(client
->response
, attr
, 0);
4720 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
4725 * 'run_printer()' - Run the printer service.
4729 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
4731 int num_fds
; /* Number of file descriptors */
4732 struct pollfd polldata
[3]; /* poll() data */
4733 int timeout
; /* Timeout for poll() */
4734 _ipp_client_t
*client
; /* New client */
4738 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
4741 polldata
[0].fd
= printer
->ipv4
;
4742 polldata
[0].events
= POLLIN
;
4744 polldata
[1].fd
= printer
->ipv6
;
4745 polldata
[1].events
= POLLIN
;
4750 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
4751 polldata
[num_fds
++].events
= POLLIN
;
4752 #endif /* HAVE_DNSSD */
4755 * Loop until we are killed or have a hard error...
4760 if (cupsArrayCount(printer
->jobs
))
4765 if (poll(polldata
, num_fds
, timeout
) < 0 && errno
!= EINTR
)
4767 perror("poll() failed");
4771 if (polldata
[0].revents
& POLLIN
)
4773 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
4775 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4777 perror("Unable to create client thread");
4778 delete_client(client
);
4783 if (polldata
[1].revents
& POLLIN
)
4785 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
4787 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4789 perror("Unable to create client thread");
4790 delete_client(client
);
4796 if (polldata
[2].revents
& POLLIN
)
4797 DNSServiceProcessResult(printer
->common_ref
);
4798 #endif /* HAVE_DNSSD */
4801 * Clean out old jobs...
4804 clean_jobs(printer
);
4810 * 'usage()' - Show program usage.
4814 usage(int status
) /* O - Exit status */
4818 puts(CUPS_SVERSION
" - Copyright 2010-2013 by Apple Inc. All rights "
4823 puts("Usage: ippserver [options] \"name\"");
4826 puts("-2 Supports 2-sided printing (default=1-sided)");
4827 puts("-M manufacturer Manufacturer name (default=Test)");
4828 puts("-P PIN printing mode");
4829 puts("-c command Run command for every print job");
4830 printf("-d spool-directory Spool directory "
4831 "(default=/tmp/ippserver.%d)\n", (int)getpid());
4832 puts("-f type/subtype[,...] List of supported types "
4833 "(default=application/pdf,image/jpeg)");
4834 puts("-h Show program help");
4835 puts("-i iconfile.png PNG icon file (default=printer.png)");
4836 puts("-k Keep job spool files");
4837 puts("-l location Location of printer (default=empty string)");
4838 puts("-m model Model name (default=Printer)");
4839 puts("-n hostname Hostname for printer");
4840 puts("-p port Port number (default=auto)");
4842 puts("-r subtype Bonjour service subtype (default=_print)");
4843 #endif /* HAVE_DNSSD */
4844 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
4845 puts("-v[vvv] Be (very) verbose");
4852 * 'valid_doc_attributes()' - Determine whether the document attributes are
4855 * When one or more document attributes are invalid, this function adds a
4856 * suitable response and attributes to the unsupported group.
4859 static int /* O - 1 if valid, 0 if not */
4860 valid_doc_attributes(
4861 _ipp_client_t
*client
) /* I - Client */
4863 int valid
= 1; /* Valid attributes? */
4864 ipp_op_t op
= ippGetOperation(client
->request
);
4866 const char *op_name
= ippOpString(op
);
4867 /* IPP operation name */
4868 ipp_attribute_t
*attr
, /* Current attribute */
4869 *supported
; /* xxx-supported attribute */
4870 const char *compression
= NULL
,
4871 /* compression value */
4872 *format
= NULL
; /* document-format value */
4876 * Check operation attributes...
4879 if ((attr
= ippFindAttribute(client
->request
, "compression",
4880 IPP_TAG_ZERO
)) != NULL
)
4883 * If compression is specified, only accept a supported value in a Print-Job
4884 * or Send-Document request...
4887 compression
= ippGetString(attr
, 0, NULL
);
4888 supported
= ippFindAttribute(client
->printer
->attrs
,
4889 "compression-supported", IPP_TAG_KEYWORD
);
4891 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
4892 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
4893 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
4894 op
!= IPP_OP_VALIDATE_JOB
) ||
4895 !ippContainsString(supported
, compression
))
4897 respond_unsupported(client
, attr
);
4902 fprintf(stderr
, "%s %s compression=\"%s\"\n",
4903 client
->hostname
, op_name
, compression
);
4905 if (strcmp(compression
, "none"))
4906 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
4911 * Is it a format we support?
4914 if ((attr
= ippFindAttribute(client
->request
, "document-format",
4915 IPP_TAG_ZERO
)) != NULL
)
4917 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
4918 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
4920 respond_unsupported(client
, attr
);
4925 format
= ippGetString(attr
, 0, NULL
);
4927 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
4928 client
->hostname
, op_name
, format
);
4933 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
,
4934 "document-format-default",
4935 IPP_TAG_MIMETYPE
), 0, NULL
);
4937 format
= "application/octet-stream"; /* Should never happen */
4939 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
4940 "document-format", NULL
, format
);
4943 if (!strcmp(format
, "application/octet-stream") &&
4944 (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
||
4945 ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
4948 * Auto-type the file using the first 4 bytes of the file...
4951 unsigned char header
[4]; /* First 4 bytes of file */
4953 memset(header
, 0, sizeof(header
));
4954 httpPeek(client
->http
, (char *)header
, sizeof(header
));
4956 if (!memcmp(header
, "%PDF", 4))
4957 format
= "application/pdf";
4958 else if (!memcmp(header
, "%!", 2))
4959 format
= "application/postscript";
4960 else if (!memcmp(header
, "\377\330\377", 3) &&
4961 header
[3] >= 0xe0 && header
[3] <= 0xef)
4962 format
= "image/jpeg";
4963 else if (!memcmp(header
, "\211PNG", 4))
4964 format
= "image/png";
4967 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
4968 client
->hostname
, op_name
, format
);
4971 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
4972 "document-format", NULL
, format
);
4974 ippSetString(client
->request
, &attr
, 0, format
);
4977 if (op
!= IPP_OP_CREATE_JOB
&&
4978 (supported
= ippFindAttribute(client
->printer
->attrs
,
4979 "document-format-supported",
4980 IPP_TAG_MIMETYPE
)) != NULL
&&
4981 !ippContainsString(supported
, format
))
4983 respond_unsupported(client
, attr
);
4992 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
4994 * When one or more job attributes are invalid, this function adds a suitable
4995 * response and attributes to the unsupported group.
4998 static int /* O - 1 if valid, 0 if not */
4999 valid_job_attributes(
5000 _ipp_client_t
*client
) /* I - Client */
5002 int i
, /* Looping var */
5003 valid
= 1; /* Valid attributes? */
5004 ipp_attribute_t
*attr
, /* Current attribute */
5005 *supported
; /* xxx-supported attribute */
5009 * Check operation attributes...
5012 valid
= valid_doc_attributes(client
);
5015 * Check the various job template attributes...
5018 if ((attr
= ippFindAttribute(client
->request
, "copies",
5019 IPP_TAG_ZERO
)) != NULL
)
5021 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5022 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
5024 respond_unsupported(client
, attr
);
5029 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity",
5030 IPP_TAG_ZERO
)) != NULL
)
5032 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
5034 respond_unsupported(client
, attr
);
5039 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until",
5040 IPP_TAG_ZERO
)) != NULL
)
5042 if (ippGetCount(attr
) != 1 ||
5043 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5044 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5045 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5046 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
5048 respond_unsupported(client
, attr
);
5053 if ((attr
= ippFindAttribute(client
->request
, "job-name",
5054 IPP_TAG_ZERO
)) != NULL
)
5056 if (ippGetCount(attr
) != 1 ||
5057 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5058 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
5060 respond_unsupported(client
, attr
);
5065 if ((attr
= ippFindAttribute(client
->request
, "job-priority",
5066 IPP_TAG_ZERO
)) != NULL
)
5068 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5069 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
5071 respond_unsupported(client
, attr
);
5076 if ((attr
= ippFindAttribute(client
->request
, "job-sheets",
5077 IPP_TAG_ZERO
)) != NULL
)
5079 if (ippGetCount(attr
) != 1 ||
5080 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5081 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5082 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5083 strcmp(ippGetString(attr
, 0, NULL
), "none"))
5085 respond_unsupported(client
, attr
);
5090 if ((attr
= ippFindAttribute(client
->request
, "media",
5091 IPP_TAG_ZERO
)) != NULL
)
5093 if (ippGetCount(attr
) != 1 ||
5094 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5095 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5096 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
5098 respond_unsupported(client
, attr
);
5104 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
5106 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
5109 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
5111 respond_unsupported(client
, attr
);
5117 if ((attr
= ippFindAttribute(client
->request
, "media-col",
5118 IPP_TAG_ZERO
)) != NULL
)
5120 if (ippGetCount(attr
) != 1 ||
5121 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
5123 respond_unsupported(client
, attr
);
5126 /* TODO: check for valid media-col */
5129 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling",
5130 IPP_TAG_ZERO
)) != NULL
)
5132 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5133 (strcmp(ippGetString(attr
, 0, NULL
),
5134 "separate-documents-uncollated-copies") &&
5135 strcmp(ippGetString(attr
, 0, NULL
),
5136 "separate-documents-collated-copies")))
5138 respond_unsupported(client
, attr
);
5143 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested",
5144 IPP_TAG_ZERO
)) != NULL
)
5146 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
5147 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
5148 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
5150 respond_unsupported(client
, attr
);
5155 if ((attr
= ippFindAttribute(client
->request
, "page-ranges",
5156 IPP_TAG_ZERO
)) != NULL
)
5158 respond_unsupported(client
, attr
);
5162 if ((attr
= ippFindAttribute(client
->request
, "print-quality",
5163 IPP_TAG_ZERO
)) != NULL
)
5165 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
5166 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
5167 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
5169 respond_unsupported(client
, attr
);
5174 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution",
5175 IPP_TAG_ZERO
)) != NULL
)
5177 respond_unsupported(client
, attr
);
5181 if ((attr
= ippFindAttribute(client
->request
, "sides",
5182 IPP_TAG_ZERO
)) != NULL
)
5184 const char *sides
= NULL
; /* "sides" value... */
5186 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
5188 respond_unsupported(client
, attr
);
5192 sides
= ippGetString(attr
, 0, NULL
);
5194 if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported",
5195 IPP_TAG_KEYWORD
)) != NULL
)
5197 if (!ippContainsString(supported
, sides
))
5199 respond_unsupported(client
, attr
);
5203 else if (strcmp(sides
, "one-sided"))
5205 respond_unsupported(client
, attr
);