4 * Sample IPP/2.0 server for CUPS.
6 * Copyright 2010-2012 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 */
95 #endif /* HAVE_DNSSD */
98 #ifdef HAVE_SYS_MOUNT_H
99 # include <sys/mount.h>
100 #endif /* HAVE_SYS_MOUNT_H */
101 #ifdef HAVE_SYS_STATFS_H
102 # include <sys/statfs.h>
103 #endif /* HAVE_SYS_STATFS_H */
104 #ifdef HAVE_SYS_STATVFS_H
105 # include <sys/statvfs.h>
106 #endif /* HAVE_SYS_STATVFS_H */
107 #ifdef HAVE_SYS_VFS_H
108 # include <sys/vfs.h>
109 #endif /* HAVE_SYS_VFS_H */
116 enum _ipp_preasons_e
/* printer-state-reasons bit values */
118 _IPP_PSTATE_NONE
= 0x0000, /* none */
119 _IPP_PSTATE_OTHER
= 0x0001, /* other */
120 _IPP_PSTATE_COVER_OPEN
= 0x0002, /* cover-open */
121 _IPP_PSTATE_INPUT_TRAY_MISSING
= 0x0004,
122 /* input-tray-missing */
123 _IPP_PSTATE_MARKER_SUPPLY_EMPTY
= 0x0008,
124 /* marker-supply-empty */
125 _IPP_PSTATE_MARKER_SUPPLY_LOW
= 0x0010,
126 /* marker-suply-low */
127 _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL
= 0x0020,
128 /* marker-waste-almost-full */
129 _IPP_PSTATE_MARKER_WASTE_FULL
= 0x0040,
130 /* marker-waste-full */
131 _IPP_PSTATE_MEDIA_EMPTY
= 0x0080, /* media-empty */
132 _IPP_PSTATE_MEDIA_JAM
= 0x0100, /* media-jam */
133 _IPP_PSTATE_MEDIA_LOW
= 0x0200, /* media-low */
134 _IPP_PSTATE_MEDIA_NEEDED
= 0x0400, /* media-needed */
135 _IPP_PSTATE_MOVING_TO_PAUSED
= 0x0800,
136 /* moving-to-paused */
137 _IPP_PSTATE_PAUSED
= 0x1000, /* paused */
138 _IPP_PSTATE_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
139 _IPP_PSTATE_TONER_EMPTY
= 0x4000, /* toner-empty */
140 _IPP_PSTATE_TONER_LOW
= 0x8000 /* toner-low */
142 typedef unsigned int _ipp_preasons_t
; /* Bitfield for printer-state-reasons */
144 typedef enum _ipp_media_class_e
146 _IPP_GENERAL
, /* General-purpose size */
147 _IPP_PHOTO_ONLY
, /* Photo-only size */
148 _IPP_ENV_ONLY
/* Envelope-only size */
149 } _ipp_media_class_t
;
151 static const char * const media_supported
[] =
152 { /* media-supported values */
153 "iso_a4_210x297mm", /* A4 */
154 "iso_a5_148x210mm", /* A5 */
155 "iso_a6_105x148mm", /* A6 */
156 "iso_dl_110x220mm", /* DL */
157 "na_legal_8.5x14in", /* Legal */
158 "na_letter_8.5x11in", /* Letter */
159 "na_number-10_4.125x9.5in", /* #10 */
160 "na_index-3x5_3x5in", /* 3x5 */
161 "oe_photo-l_3.5x5in", /* L */
162 "na_index-4x6_4x6in", /* 4x6 */
163 "na_5x7_5x7in" /* 5x7 aka 2L */
165 static const int media_col_sizes
[][3] =
166 { /* media-col-database sizes */
167 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
168 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
169 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
170 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
171 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
172 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
173 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
174 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
175 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
176 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
177 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
179 static const char * const media_type_supported
[] =
180 /* media-type-supported values */
187 "photographic-glossy",
188 "photographic-high-gloss",
189 "photographic-matte",
190 "photographic-satin",
191 "photographic-semi-gloss",
193 "stationery-letterhead",
202 typedef struct _ipp_job_s _ipp_job_t
;
204 typedef struct _ipp_printer_s
/**** Printer data ****/
206 int ipv4
, /* IPv4 listener */
207 ipv6
; /* IPv6 listener */
209 DNSServiceRef common_ref
, /* Shared service connection */
210 ipp_ref
, /* Bonjour IPP service */
212 ipps_ref
, /* Bonjour IPPS service */
213 # endif /* HAVE_SSL */
214 http_ref
, /* Bonjour HTTP service */
215 printer_ref
; /* Bonjour LPD service */
216 TXTRecordRef ipp_txt
; /* Bonjour IPP TXT record */
217 char *dnssd_name
; /* printer-dnssd-name */
218 #endif /* HAVE_DNSSD */
219 char *name
, /* printer-name */
220 *icon
, /* Icon filename */
221 *directory
, /* Spool directory */
222 *hostname
, /* Hostname */
223 *uri
; /* printer-uri-supported */
225 size_t urilen
; /* Length of printer URI */
226 ipp_t
*attrs
; /* Static attributes */
227 ipp_pstate_t state
; /* printer-state value */
228 _ipp_preasons_t state_reasons
; /* printer-state-reasons values */
229 cups_array_t
*jobs
; /* Jobs */
230 _ipp_job_t
*active_job
; /* Current active/pending job */
231 int next_job_id
; /* Next job-id value */
232 _cups_rwlock_t rwlock
; /* Printer lock */
235 struct _ipp_job_s
/**** Job data ****/
238 const char *name
, /* job-name */
239 *username
, /* job-originating-user-name */
240 *format
; /* document-format */
241 ipp_jstate_t state
; /* job-state value */
242 time_t processing
, /* time-at-processing value */
243 completed
; /* time-at-completed value */
244 ipp_t
*attrs
; /* Static attributes */
245 int cancel
; /* Non-zero when job canceled */
246 char *filename
; /* Print file name */
247 int fd
; /* Print file descriptor */
248 _ipp_printer_t
*printer
; /* Printer */
251 typedef struct _ipp_client_s
/**** Client data ****/
253 http_t
*http
; /* HTTP connection */
254 ipp_t
*request
, /* IPP request */
255 *response
; /* IPP response */
256 time_t start
; /* Request start time */
257 http_state_t operation
; /* Request operation */
258 ipp_op_t operation_id
; /* IPP operation-id */
259 char uri
[1024]; /* Request URI */
260 http_addr_t addr
; /* Client address */
261 char hostname
[256]; /* Client hostname */
262 _ipp_printer_t
*printer
; /* Printer */
263 _ipp_job_t
*job
; /* Current job, if any */
271 static void clean_jobs(_ipp_printer_t
*printer
);
272 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
273 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
274 ipp_tag_t group_tag
, int quickcopy
);
275 static void copy_job_attributes(_ipp_client_t
*client
,
276 _ipp_job_t
*job
, cups_array_t
*ra
);
277 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
278 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
279 static int create_listener(int family
, int *port
);
280 static ipp_t
*create_media_col(const char *media
, const char *type
,
281 int width
, int length
, int margins
);
282 static ipp_t
*create_media_size(int width
, int length
);
283 static _ipp_printer_t
*create_printer(const char *servername
,
284 const char *name
, const char *location
,
285 const char *make
, const char *model
,
287 const char *docformats
, int ppm
,
288 int ppm_color
, int duplex
, int port
,
292 #endif /* HAVE_DNSSD */
293 const char *directory
);
294 static cups_array_t
*create_requested_array(_ipp_client_t
*client
);
295 static void debug_attributes(const char *title
, ipp_t
*ipp
,
297 static void delete_client(_ipp_client_t
*client
);
298 static void delete_job(_ipp_job_t
*job
);
299 static void delete_printer(_ipp_printer_t
*printer
);
301 static void dnssd_callback(DNSServiceRef sdRef
,
302 DNSServiceFlags flags
,
303 DNSServiceErrorType errorCode
,
307 _ipp_printer_t
*printer
);
308 #endif /* HAVE_DNSSD */
309 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
310 static void html_escape(_ipp_client_t
*client
, const char *s
,
312 static void html_printf(_ipp_client_t
*client
, const char *format
,
313 ...) __attribute__((__format__(__printf__
,
315 static void ipp_cancel_job(_ipp_client_t
*client
);
316 static void ipp_create_job(_ipp_client_t
*client
);
317 static void ipp_get_job_attributes(_ipp_client_t
*client
);
318 static void ipp_get_jobs(_ipp_client_t
*client
);
319 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
320 static void ipp_print_job(_ipp_client_t
*client
);
321 static void ipp_print_uri(_ipp_client_t
*client
);
322 static void ipp_send_document(_ipp_client_t
*client
);
323 static void ipp_send_uri(_ipp_client_t
*client
);
324 static void ipp_validate_job(_ipp_client_t
*client
);
325 static void *process_client(_ipp_client_t
*client
);
326 static int process_http(_ipp_client_t
*client
);
327 static int process_ipp(_ipp_client_t
*client
);
328 static void *process_job(_ipp_job_t
*job
);
330 static int register_printer(_ipp_printer_t
*printer
,
331 const char *location
, const char *make
,
332 const char *model
, const char *formats
,
333 const char *adminurl
, int color
,
334 int duplex
, const char *regtype
);
335 #endif /* HAVE_DNSSD */
336 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
337 const char *content_coding
,
338 const char *type
, size_t length
);
339 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
340 const char *message
, ...)
341 __attribute__ ((__format__ (__printf__
, 3, 4)));
342 static void respond_unsupported(_ipp_client_t
*client
,
343 ipp_attribute_t
*attr
);
344 static void run_printer(_ipp_printer_t
*printer
);
345 static void usage(int status
) __attribute__((noreturn
));
346 static int valid_doc_attributes(_ipp_client_t
*client
);
347 static int valid_job_attributes(_ipp_client_t
*client
);
354 static int KeepFiles
= 0,
359 * 'main()' - Main entry to the sample server.
362 int /* O - Exit status */
363 main(int argc
, /* I - Number of command-line args */
364 char *argv
[]) /* I - Command-line arguments */
366 int i
; /* Looping var */
367 const char *opt
, /* Current option character */
368 *servername
= NULL
, /* Server host name */
369 *name
= NULL
, /* Printer name */
370 *location
= "", /* Location of printer */
371 *make
= "Test", /* Manufacturer */
372 *model
= "Printer", /* Model */
373 *icon
= "printer.png", /* Icon file */
374 *formats
= "application/pdf,image/jpeg";
375 /* Supported formats */
377 const char *subtype
= "_print"; /* Bonjour service subtype */
378 #endif /* HAVE_DNSSD */
379 int port
= 8631, /* Port number (0 = auto) */
380 duplex
= 0, /* Duplex mode */
381 ppm
= 10, /* Pages per minute for mono */
382 ppm_color
= 0, /* Pages per minute for color */
383 pin
= 0; /* PIN printing mode? */
384 char directory
[1024] = ""; /* Spool directory */
385 _ipp_printer_t
*printer
; /* Printer object */
389 * Parse command-line arguments...
392 for (i
= 1; i
< argc
; i
++)
393 if (argv
[i
][0] == '-')
395 for (opt
= argv
[i
] + 1; *opt
; opt
++)
398 case '2' : /* -2 (enable 2-sided printing) */
402 case 'M' : /* -M manufacturer */
409 case 'P' : /* -P (PIN printing mode) */
413 case 'd' : /* -d spool-directory */
417 strlcpy(directory
, argv
[i
], sizeof(directory
));
420 case 'f' : /* -f type/subtype[,...] */
427 case 'h' : /* -h (show help) */
431 case 'i' : /* -i icon.png */
438 case 'k' : /* -k (keep files) */
442 case 'l' : /* -l location */
449 case 'm' : /* -m model */
456 case 'n' : /* -n hostname */
460 servername
= argv
[i
];
463 case 'p' : /* -p port */
465 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
467 port
= atoi(argv
[i
]);
471 case 'r' : /* -r subtype */
477 #endif /* HAVE_DNSSD */
479 case 's' : /* -s speed[,color-speed] */
483 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
487 case 'v' : /* -v (be verbose) */
491 default : /* Unknown */
492 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
503 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
511 * Apply defaults as needed...
516 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
518 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
520 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
521 directory
, strerror(errno
));
526 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
530 * Create the printer...
533 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
534 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
537 #endif /* HAVE_DNSSD */
542 * Run the print service...
545 run_printer(printer
);
548 * Destroy the printer and exit...
551 delete_printer(printer
);
558 * 'clean_jobs()' - Clean out old (completed) jobs.
562 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
564 _ipp_job_t
*job
; /* Current job */
565 time_t cleantime
; /* Clean time */
568 if (cupsArrayCount(printer
->jobs
) == 0)
571 cleantime
= time(NULL
) - 60;
573 _cupsRWLockWrite(&(printer
->rwlock
));
574 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
576 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
577 if (job
->completed
&& job
->completed
< cleantime
)
579 cupsArrayRemove(printer
->jobs
, job
);
584 _cupsRWUnlock(&(printer
->rwlock
));
589 * 'compare_jobs()' - Compare two jobs.
592 static int /* O - Result of comparison */
593 compare_jobs(_ipp_job_t
*a
, /* I - First job */
594 _ipp_job_t
*b
) /* I - Second job */
596 return (b
->id
- a
->id
);
601 * 'copy_attributes()' - Copy attributes from one request to another.
605 copy_attributes(ipp_t
*to
, /* I - Destination request */
606 ipp_t
*from
, /* I - Source request */
607 cups_array_t
*ra
, /* I - Requested attributes */
608 ipp_tag_t group_tag
, /* I - Group to copy */
609 int quickcopy
) /* I - Do a quick copy? */
611 ipp_attribute_t
*fromattr
; /* Source attribute */
617 for (fromattr
= ippFirstAttribute(from
);
619 fromattr
= ippNextAttribute(from
))
622 * Filter attributes as needed...
625 ipp_tag_t fromgroup
= ippGetGroupTag(fromattr
);
626 const char *fromname
= ippGetName(fromattr
);
628 if ((group_tag
!= IPP_TAG_ZERO
&& fromgroup
!= group_tag
&&
629 fromgroup
!= IPP_TAG_ZERO
) || !fromname
)
632 if (!ra
|| cupsArrayFind(ra
, (void *)fromname
))
633 ippCopyAttribute(to
, fromattr
, quickcopy
);
639 * 'copy_job_attrs()' - Copy job attributes to the response.
644 _ipp_client_t
*client
, /* I - Client */
645 _ipp_job_t
*job
, /* I - Job */
646 cups_array_t
*ra
) /* I - requested-attributes */
648 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
650 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
651 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
652 "job-printer-up-time", (int)time(NULL
));
654 if (!ra
|| cupsArrayFind(ra
, "job-state"))
655 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
656 "job-state", job
->state
);
658 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
662 case IPP_JSTATE_PENDING
:
663 ippAddString(client
->response
, IPP_TAG_JOB
,
664 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
, "job-state-reasons",
668 case IPP_JSTATE_HELD
:
670 ippAddString(client
->response
, IPP_TAG_JOB
,
671 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
672 "job-state-reasons", NULL
, "job-incoming");
673 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
674 ippAddString(client
->response
, IPP_TAG_JOB
,
675 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
676 "job-state-reasons", NULL
, "job-hold-until-specified");
678 ippAddString(client
->response
, IPP_TAG_JOB
,
679 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
680 "job-state-reasons", NULL
, "job-data-insufficient");
683 case IPP_JSTATE_PROCESSING
:
685 ippAddString(client
->response
, IPP_TAG_JOB
,
686 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
687 "job-state-reasons", NULL
, "processing-to-stop-point");
689 ippAddString(client
->response
, IPP_TAG_JOB
,
690 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
691 "job-state-reasons", NULL
, "job-printing");
694 case IPP_JSTATE_STOPPED
:
695 ippAddString(client
->response
, IPP_TAG_JOB
,
696 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
, "job-state-reasons",
697 NULL
, "job-stopped");
700 case IPP_JSTATE_CANCELED
:
701 ippAddString(client
->response
, IPP_TAG_JOB
,
702 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
, "job-state-reasons",
703 NULL
, "job-canceled-by-user");
706 case IPP_JSTATE_ABORTED
:
707 ippAddString(client
->response
, IPP_TAG_JOB
,
708 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
, "job-state-reasons",
709 NULL
, "aborted-by-system");
712 case IPP_JSTATE_COMPLETED
:
713 ippAddString(client
->response
, IPP_TAG_JOB
,
714 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
, "job-state-reasons",
715 NULL
, "job-completed-successfully");
720 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
721 ippAddInteger(client
->response
, IPP_TAG_JOB
,
722 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
723 "time-at-completed", job
->completed
);
725 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
726 ippAddInteger(client
->response
, IPP_TAG_JOB
,
727 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
728 "time-at-processing", job
->processing
);
733 * 'create_client()' - Accept a new network connection and create a client
737 static _ipp_client_t
* /* O - Client */
738 create_client(_ipp_printer_t
*printer
, /* I - Printer */
739 int sock
) /* I - Listen socket */
741 _ipp_client_t
*client
; /* Client */
744 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
746 perror("Unable to allocate memory for client");
750 client
->printer
= printer
;
753 * Accept the client and get the remote address...
756 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
758 perror("Unable to accept client connection");
765 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
768 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
775 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
779 static _ipp_job_t
* /* O - Job */
780 create_job(_ipp_client_t
*client
) /* I - Client */
782 _ipp_job_t
*job
; /* Job */
783 ipp_attribute_t
*attr
; /* Job attribute */
784 char uri
[1024]; /* job-uri value */
787 _cupsRWLockWrite(&(client
->printer
->rwlock
));
788 if (client
->printer
->active_job
&&
789 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
792 * Only accept a single job at a time...
795 _cupsRWLockWrite(&(client
->printer
->rwlock
));
800 * Allocate and initialize the job object...
803 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
805 perror("Unable to allocate memory for job");
809 job
->printer
= client
->printer
;
810 job
->attrs
= client
->request
;
811 job
->state
= IPP_JSTATE_HELD
;
813 client
->request
= NULL
;
816 * Set all but the first two attributes to the job attributes group...
819 for (ippFirstAttribute(job
->attrs
),
820 ippNextAttribute(job
->attrs
),
821 attr
= ippNextAttribute(job
->attrs
);
823 attr
= ippNextAttribute(job
->attrs
))
824 ippSetGroupTag(job
->attrs
, &attr
, IPP_TAG_JOB
);
827 * Get the requesting-user-name, document format, and priority...
830 if ((attr
= ippFindAttribute(job
->attrs
, "requesting-user-name",
831 IPP_TAG_NAME
)) != NULL
)
832 ippSetName(job
->attrs
, &attr
, "job-originating-user-name");
834 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
,
835 IPP_TAG_NAME
| IPP_TAG_CUPS_CONST
,
836 "job-originating-user-name", NULL
, "anonymous");
839 job
->username
= ippGetString(attr
, 0, NULL
);
841 job
->username
= "anonymous";
843 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
844 IPP_TAG_MIMETYPE
)) != NULL
)
845 job
->format
= ippGetString(attr
, 0, NULL
);
847 job
->format
= "application/octet-stream";
850 * Add job description attributes and add to the jobs array...
853 job
->id
= client
->printer
->next_job_id
++;
855 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
857 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
858 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
859 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
860 client
->printer
->uri
);
861 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
864 cupsArrayAdd(client
->printer
->jobs
, job
);
865 client
->printer
->active_job
= job
;
867 _cupsRWUnlock(&(client
->printer
->rwlock
));
874 * 'create_listener()' - Create a listener socket.
877 static int /* O - Listener socket or -1 on error */
878 create_listener(int family
, /* I - Address family */
879 int *port
) /* IO - Port number */
881 int sock
; /* Listener socket */
882 http_addrlist_t
*addrlist
; /* Listen address */
883 char service
[255]; /* Service port */
888 *port
= 8000 + (getuid() % 1000);
889 fprintf(stderr
, "Listening on port %d.\n", *port
);
892 snprintf(service
, sizeof(service
), "%d", *port
);
893 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
896 sock
= httpAddrListen(&(addrlist
->addr
), *port
);
898 httpAddrFreeList(addrlist
);
905 * 'create_media_col()' - Create a media-col value.
908 static ipp_t
* /* O - media-col collection */
909 create_media_col(const char *media
, /* I - Media name */
910 const char *type
, /* I - Nedua type */
911 int width
, /* I - x-dimension in 2540ths */
912 int length
, /* I - y-dimension in 2540ths */
913 int margins
) /* I - Value for margins */
915 ipp_t
*media_col
= ippNew(), /* media-col value */
916 *media_size
= create_media_size(width
, length
);
917 /* media-size value */
918 char media_key
[256]; /* media-key value */
921 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, type
,
922 margins
== 0 ? "_borderless" : "");
924 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
926 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
927 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
928 "media-bottom-margin", margins
);
929 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
930 "media-left-margin", margins
);
931 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
932 "media-right-margin", margins
);
933 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
934 "media-top-margin", margins
);
935 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type",
938 ippDelete(media_size
);
945 * 'create_media_size()' - Create a media-size value.
948 static ipp_t
* /* O - media-col collection */
949 create_media_size(int width
, /* I - x-dimension in 2540ths */
950 int length
) /* I - y-dimension in 2540ths */
952 ipp_t
*media_size
= ippNew(); /* media-size value */
955 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
957 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
965 * 'create_printer()' - Create, register, and listen for connections to a
969 static _ipp_printer_t
* /* O - Printer */
970 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
971 const char *name
, /* I - printer-name */
972 const char *location
, /* I - printer-location */
973 const char *make
, /* I - printer-make-and-model */
974 const char *model
, /* I - printer-make-and-model */
975 const char *icon
, /* I - printer-icons */
976 const char *docformats
, /* I - document-format-supported */
977 int ppm
, /* I - Pages per minute in grayscale */
978 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
979 int duplex
, /* I - 1 = duplex, 0 = simplex */
980 int port
, /* I - Port for listeners or 0 for auto */
981 int pin
, /* I - Require PIN printing */
983 const char *subtype
, /* I - Bonjour service subtype */
984 #endif /* HAVE_DNSSD */
985 const char *directory
) /* I - Spool directory */
987 int i
, j
; /* Looping vars */
988 _ipp_printer_t
*printer
; /* Printer */
989 char hostname
[256], /* Hostname */
990 uri
[1024], /* Printer URI */
991 icons
[1024], /* printer-icons URI */
992 adminurl
[1024], /* printer-more-info URI */
993 device_id
[1024],/* printer-device-id */
994 make_model
[128];/* printer-make-and-model */
995 int num_formats
; /* Number of document-format-supported values */
996 char *defformat
, /* document-format-default value */
997 *formats
[100], /* document-format-supported values */
998 *ptr
; /* Pointer into string */
999 const char *prefix
; /* Prefix string */
1000 int num_database
; /* Number of database values */
1001 ipp_attribute_t
*media_col_database
,
1002 /* media-col-database value */
1003 *media_size_supported
;
1004 /* media-size-supported value */
1005 ipp_t
*media_col_default
;
1006 /* media-col-default value */
1007 int media_col_index
;/* Current media-col-database value */
1008 int k_supported
; /* Maximum file size supported */
1010 struct statvfs spoolinfo
; /* FS info for spool directory */
1011 double spoolsize
; /* FS size */
1012 #elif defined(HAVE_STATFS)
1013 struct statfs spoolinfo
; /* FS info for spool directory */
1014 double spoolsize
; /* FS size */
1015 #endif /* HAVE_STATVFS */
1016 static const int orients
[4] = /* orientation-requested-supported values */
1018 IPP_ORIENT_PORTRAIT
,
1019 IPP_ORIENT_LANDSCAPE
,
1020 IPP_ORIENT_REVERSE_LANDSCAPE
,
1021 IPP_ORIENT_REVERSE_PORTRAIT
1023 static const char * const versions
[] =/* ipp-versions-supported values */
1029 static const int ops
[] = /* operations-supported values */
1033 IPP_OP_VALIDATE_JOB
,
1035 IPP_OP_SEND_DOCUMENT
,
1038 IPP_OP_GET_JOB_ATTRIBUTES
,
1040 IPP_OP_GET_PRINTER_ATTRIBUTES
1042 static const char * const charsets
[] =/* charset-supported values */
1047 static const char * const compressions
[] =/* compression-supported values */
1052 #endif /* HAVE_LIBZ */
1055 static const char * const job_creation
[] =
1056 { /* job-creation-attributes-supported values */
1058 "ipp-attribute-fidelity",
1060 "job-accounting-user-id",
1066 "multiple-document-handling",
1067 "orientation-requested",
1071 static const char * const media_col_supported
[] =
1072 { /* media-col-supported values */
1073 "media-bottom-margin",
1074 "media-left-margin",
1075 "media-right-margin",
1080 static const int media_xxx_margin_supported
[] =
1081 { /* media-xxx-margin-supported values */
1085 static const char * const multiple_document_handling
[] =
1086 { /* multiple-document-handling-supported values */
1087 "separate-documents-uncollated-copies",
1088 "separate-documents-collated-copies"
1090 static const int print_quality_supported
[] =
1091 { /* print-quality-supported values */
1096 static const char * const reference_uri_schemes_supported
[] =
1097 { /* reference-uri-schemes-supported */
1103 #endif /* HAVE_SSL */
1105 static const char * const sides_supported
[] =
1106 { /* sides-supported values */
1108 "two-sided-long-edge",
1109 "two-sided-short-edge"
1111 static const char * const which_jobs
[] =
1112 { /* which-jobs-supported values */
1121 "processing-stopped"
1126 * Allocate memory for the printer...
1129 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1131 perror("Unable to allocate memory for printer");
1137 printer
->name
= strdup(name
);
1139 printer
->dnssd_name
= strdup(printer
->name
);
1140 #endif /* HAVE_DNSSD */
1141 printer
->directory
= strdup(directory
);
1142 printer
->hostname
= strdup(servername
? servername
:
1143 httpGetHostname(NULL
, hostname
,
1145 printer
->port
= port
;
1146 printer
->state
= IPP_PSTATE_IDLE
;
1147 printer
->state_reasons
= _IPP_PSTATE_NONE
;
1148 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1149 printer
->next_job_id
= 1;
1151 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1152 printer
->hostname
, printer
->port
, "/ipp");
1153 printer
->uri
= strdup(uri
);
1154 printer
->urilen
= strlen(uri
);
1157 printer
->icon
= strdup(icon
);
1159 _cupsRWInit(&(printer
->rwlock
));
1162 * Create the listener sockets...
1165 if ((printer
->ipv4
= create_listener(AF_INET
, &(printer
->port
))) < 0)
1167 perror("Unable to create IPv4 listener");
1171 if ((printer
->ipv6
= create_listener(AF_INET6
, &(printer
->port
))) < 0)
1173 perror("Unable to create IPv6 listener");
1178 * Prepare values for the printer attributes...
1181 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1182 printer
->hostname
, printer
->port
, "/icon.png");
1183 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
,
1184 printer
->hostname
, printer
->port
, "/");
1188 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1189 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1192 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1195 formats
[0] = strdup(docformats
);
1196 defformat
= formats
[0];
1197 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1200 formats
[num_formats
++] = ptr
;
1202 if (!_cups_strcasecmp(ptr
, "application/octet-stream"))
1206 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1207 ptr
= device_id
+ strlen(device_id
);
1209 for (i
= 0; i
< num_formats
; i
++)
1211 if (!_cups_strcasecmp(formats
[i
], "application/pdf"))
1212 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPDF", prefix
);
1213 else if (!_cups_strcasecmp(formats
[i
], "application/postscript"))
1214 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPS", prefix
);
1215 else if (!_cups_strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1216 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPCL", prefix
);
1217 else if (!_cups_strcasecmp(formats
[i
], "image/jpeg"))
1218 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sJPEG", prefix
);
1219 else if (!_cups_strcasecmp(formats
[i
], "image/png"))
1220 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPNG", prefix
);
1221 else if (_cups_strcasecmp(formats
[i
], "application/octet-stream"))
1222 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%s%s", prefix
,
1228 strlcat(device_id
, ";", sizeof(device_id
));
1231 * Get the maximum spool size based on the size of the filesystem used for
1232 * the spool directory. If the host OS doesn't support the statfs call
1233 * or the filesystem is larger than 2TiB, always report INT_MAX.
1237 if (statvfs(printer
->directory
, &spoolinfo
))
1238 k_supported
= INT_MAX
;
1239 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1240 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1241 k_supported
= INT_MAX
;
1243 k_supported
= (int)spoolsize
;
1245 #elif defined(HAVE_STATFS)
1246 if (statfs(printer
->directory
, &spoolinfo
))
1247 k_supported
= INT_MAX
;
1248 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1249 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1250 k_supported
= INT_MAX
;
1252 k_supported
= (int)spoolsize
;
1255 k_supported
= INT_MAX
;
1256 #endif /* HAVE_STATVFS */
1259 * Create the printer attributes. This list of attributes is sorted to improve
1260 * performance when the client provides a requested-attributes attribute...
1263 printer
->attrs
= ippNew();
1265 /* charset-configured */
1266 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1267 IPP_TAG_CHARSET
| IPP_TAG_CUPS_CONST
,
1268 "charset-configured", NULL
, "utf-8");
1270 /* charset-supported */
1271 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1272 IPP_TAG_CHARSET
| IPP_TAG_CUPS_CONST
,
1273 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1276 /* color-supported */
1277 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1280 /* compression-supported */
1281 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1282 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1283 "compression-supported",
1284 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1287 /* copies-default */
1288 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1289 "copies-default", 1);
1291 /* copies-supported */
1292 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1294 /* document-format-default */
1295 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1296 "document-format-default", NULL
, defformat
);
1298 /* document-format-supported */
1299 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1300 "document-format-supported", num_formats
, NULL
,
1301 (const char * const *)formats
);
1303 /* finishings-default */
1304 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1305 "finishings-default", IPP_FINISHINGS_NONE
);
1307 /* finishings-supported */
1308 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1309 "finishings-supported", IPP_FINISHINGS_NONE
);
1311 /* generated-natural-language-supported */
1312 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1313 IPP_TAG_LANGUAGE
| IPP_TAG_CUPS_CONST
,
1314 "generated-natural-language-supported", NULL
, "en");
1316 /* ipp-versions-supported */
1317 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1318 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1319 "ipp-versions-supported",
1320 sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1322 /* job-account-id-supported */
1323 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1325 /* job-accounting-user-id-supported */
1326 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
,
1327 "job-accounting-user-id-supported", 1);
1329 /* job-creation-attributes-supported */
1330 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1331 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1332 "job-creation-attributes-supported",
1333 sizeof(job_creation
) / sizeof(job_creation
[0]),
1334 NULL
, job_creation
);
1336 /* job-k-octets-supported */
1337 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1340 /* job-password-supported */
1341 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1342 "job-password-supported", 4);
1344 /* job-priority-default */
1345 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1346 "job-priority-default", 50);
1348 /* job-priority-supported */
1349 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1350 "job-priority-supported", 100);
1352 /* job-sheets-default */
1353 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1354 IPP_TAG_NAME
| IPP_TAG_CUPS_CONST
,
1355 "job-sheets-default", NULL
, "none");
1357 /* job-sheets-supported */
1358 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1359 IPP_TAG_NAME
| IPP_TAG_CUPS_CONST
,
1360 "job-sheets-supported", NULL
, "none");
1362 /* media-bottom-margin-supported */
1363 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1364 "media-bottom-margin-supported",
1365 (int)(sizeof(media_xxx_margin_supported
) /
1366 sizeof(media_xxx_margin_supported
[0])),
1367 media_xxx_margin_supported
);
1369 /* media-col-database */
1370 for (num_database
= 0, i
= 0;
1371 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1374 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1375 num_database
+= 2; /* auto + envelope */
1376 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1377 num_database
+= 12; /* auto + photographic-* + borderless */
1379 num_database
+= (int)(sizeof(media_type_supported
) /
1380 sizeof(media_type_supported
[0])) + 6;
1381 /* All types + borderless */
1384 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1385 "media-col-database", num_database
,
1387 for (media_col_index
= 0, i
= 0;
1388 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1392 j
< (int)(sizeof(media_type_supported
) /
1393 sizeof(media_type_supported
[0]));
1396 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
&&
1397 strcmp(media_type_supported
[j
], "auto") &&
1398 strcmp(media_type_supported
[j
], "envelope"))
1400 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
&&
1401 strcmp(media_type_supported
[j
], "auto") &&
1402 strncmp(media_type_supported
[j
], "photographic-", 13))
1405 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
,
1406 create_media_col(media_supported
[i
],
1407 media_type_supported
[j
],
1408 media_col_sizes
[i
][0],
1409 media_col_sizes
[i
][1],
1410 media_xxx_margin_supported
[1]));
1413 if (media_col_sizes
[i
][2] != _IPP_ENV_ONLY
&&
1414 (!strcmp(media_type_supported
[j
], "auto") ||
1415 !strncmp(media_type_supported
[j
], "photographic-", 13)))
1418 * Add borderless version for this combination...
1421 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
,
1422 create_media_col(media_supported
[i
],
1423 media_type_supported
[j
],
1424 media_col_sizes
[i
][0],
1425 media_col_sizes
[i
][1],
1426 media_xxx_margin_supported
[0]));
1432 /* media-col-default */
1433 media_col_default
= create_media_col(media_supported
[0],
1434 media_type_supported
[0],
1435 media_col_sizes
[0][0],
1436 media_col_sizes
[0][1],
1437 media_xxx_margin_supported
[1]);
1439 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1441 ippDelete(media_col_default
);
1443 /* media-col-supported */
1444 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1445 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1446 "media-col-supported",
1447 (int)(sizeof(media_col_supported
) /
1448 sizeof(media_col_supported
[0])), NULL
,
1449 media_col_supported
);
1452 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1453 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1454 "media-default", NULL
, media_supported
[0]);
1456 /* media-left-margin-supported */
1457 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1458 "media-left-margin-supported",
1459 (int)(sizeof(media_xxx_margin_supported
) /
1460 sizeof(media_xxx_margin_supported
[0])),
1461 media_xxx_margin_supported
);
1463 /* media-right-margin-supported */
1464 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1465 "media-right-margin-supported",
1466 (int)(sizeof(media_xxx_margin_supported
) /
1467 sizeof(media_xxx_margin_supported
[0])),
1468 media_xxx_margin_supported
);
1470 /* media-supported */
1471 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1472 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1474 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1475 NULL
, media_supported
);
1477 /* media-size-supported */
1478 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1479 "media-size-supported",
1480 (int)(sizeof(media_col_sizes
) /
1481 sizeof(media_col_sizes
[0])),
1484 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1486 ippSetCollection(printer
->attrs
, &media_size_supported
, i
,
1487 create_media_size(media_col_sizes
[i
][0],
1488 media_col_sizes
[i
][1]));
1490 /* media-top-margin-supported */
1491 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1492 "media-top-margin-supported",
1493 (int)(sizeof(media_xxx_margin_supported
) /
1494 sizeof(media_xxx_margin_supported
[0])),
1495 media_xxx_margin_supported
);
1497 /* media-type-supported */
1498 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1499 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1500 "media-type-supported",
1501 (int)(sizeof(media_type_supported
) /
1502 sizeof(media_type_supported
[0])),
1503 NULL
, media_type_supported
);
1505 /* multiple-document-handling-supported */
1506 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1507 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1508 "multiple-document-handling-supported",
1509 sizeof(multiple_document_handling
) /
1510 sizeof(multiple_document_handling
[0]), NULL
,
1511 multiple_document_handling
);
1513 /* multiple-document-jobs-supported */
1514 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
,
1515 "multiple-document-jobs-supported", 0);
1517 /* natural-language-configured */
1518 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1519 IPP_TAG_LANGUAGE
| IPP_TAG_CUPS_CONST
,
1520 "natural-language-configured", NULL
, "en");
1522 /* number-up-default */
1523 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1524 "number-up-default", 1);
1526 /* number-up-supported */
1527 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1528 "number-up-supported", 1);
1530 /* operations-supported */
1531 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1532 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1534 /* orientation-requested-default */
1535 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1536 "orientation-requested-default", 0);
1538 /* orientation-requested-supported */
1539 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1540 "orientation-requested-supported", 4, orients
);
1542 /* output-bin-default */
1543 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1544 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1545 "output-bin-default", NULL
, "face-down");
1547 /* output-bin-supported */
1548 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1549 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1550 "output-bin-supported", NULL
, "face-down");
1552 /* pages-per-minute */
1553 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1554 "pages-per-minute", ppm
);
1556 /* pages-per-minute-color */
1558 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1559 "pages-per-minute-color", ppm_color
);
1561 /* pdl-override-supported */
1562 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1563 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1564 "pdl-override-supported", NULL
, "attempted");
1566 /* print-quality-default */
1567 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1568 "print-quality-default", IPP_QUALITY_NORMAL
);
1570 /* print-quality-supported */
1571 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1572 "print-quality-supported",
1573 (int)(sizeof(print_quality_supported
) /
1574 sizeof(print_quality_supported
[0])),
1575 print_quality_supported
);
1577 /* printer-device-id */
1578 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1579 "printer-device-id", NULL
, device_id
);
1582 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1583 "printer-icons", NULL
, icons
);
1585 /* printer-is-accepting-jobs */
1586 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1590 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1593 /* printer-location */
1594 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1595 "printer-location", NULL
, location
);
1597 /* printer-make-and-model */
1598 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1599 "printer-make-and-model", NULL
, make_model
);
1601 /* printer-mandatory-job-attributes */
1604 static const char * const names
[] =
1606 "job-accounting-user-id",
1610 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1611 "printer-mandatory-job-attributes",
1612 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
1615 /* printer-more-info */
1616 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1617 "printer-more-info", NULL
, adminurl
);
1620 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1623 /* printer-resolution-default */
1624 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1625 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1627 /* printer-resolution-supported */
1628 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1629 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1631 /* printer-uri-supported */
1632 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1633 "printer-uri-supported", NULL
, uri
);
1635 /* reference-uri-scheme-supported */
1636 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1637 IPP_TAG_URISCHEME
| IPP_TAG_CUPS_CONST
,
1638 "reference-uri-schemes-supported",
1639 (int)(sizeof(reference_uri_schemes_supported
) /
1640 sizeof(reference_uri_schemes_supported
[0])),
1641 NULL
, reference_uri_schemes_supported
);
1644 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1645 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1646 "sides-default", NULL
, "one-sided");
1648 /* sides-supported */
1649 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1650 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1651 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
1653 /* uri-authentication-supported */
1654 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1655 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1656 "uri-authentication-supported", NULL
, "none");
1658 /* uri-security-supported */
1659 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1660 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1661 "uri-security-supported", NULL
, "none");
1663 /* which-jobs-supported */
1664 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1665 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
1666 "which-jobs-supported",
1667 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
1671 debug_attributes("Printer", printer
->attrs
, 0);
1675 * Register the printer with Bonjour...
1678 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
,
1679 ppm_color
> 0, duplex
, subtype
))
1681 #endif /* HAVE_DNSSD */
1691 * If we get here we were unable to create the printer...
1696 delete_printer(printer
);
1702 * 'create_requested_array()' - Create an array for requested-attributes.
1705 static cups_array_t
* /* O - requested-attributes array */
1706 create_requested_array(
1707 _ipp_client_t
*client
) /* I - Client */
1709 int i
, /* Looping var */
1710 count
; /* Number of values */
1711 ipp_attribute_t
*requested
; /* requested-attributes attribute */
1712 cups_array_t
*ra
; /* Requested attributes array */
1713 const char *value
; /* Current value */
1717 * Get the requested-attributes attribute, and return NULL if we don't
1721 if ((requested
= ippFindAttribute(client
->request
, "requested-attributes",
1722 IPP_TAG_KEYWORD
)) == NULL
)
1726 * If the attribute contains a single "all" keyword, return NULL...
1729 count
= ippGetCount(requested
);
1730 if (count
== 1 && !strcmp(ippGetString(requested
, 0, NULL
), "all"))
1734 * Create an array using "strcmp" as the comparison function...
1737 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
1739 for (i
= 0; i
< count
; i
++)
1741 value
= ippGetString(requested
, i
, NULL
);
1743 if (!strcmp(value
, "job-template"))
1745 cupsArrayAdd(ra
, "copies");
1746 cupsArrayAdd(ra
, "copies-default");
1747 cupsArrayAdd(ra
, "copies-supported");
1748 cupsArrayAdd(ra
, "finishings");
1749 cupsArrayAdd(ra
, "finishings-default");
1750 cupsArrayAdd(ra
, "finishings-supported");
1751 cupsArrayAdd(ra
, "job-hold-until");
1752 cupsArrayAdd(ra
, "job-hold-until-default");
1753 cupsArrayAdd(ra
, "job-hold-until-supported");
1754 cupsArrayAdd(ra
, "job-priority");
1755 cupsArrayAdd(ra
, "job-priority-default");
1756 cupsArrayAdd(ra
, "job-priority-supported");
1757 cupsArrayAdd(ra
, "job-sheets");
1758 cupsArrayAdd(ra
, "job-sheets-default");
1759 cupsArrayAdd(ra
, "job-sheets-supported");
1760 cupsArrayAdd(ra
, "media");
1761 cupsArrayAdd(ra
, "media-col");
1762 cupsArrayAdd(ra
, "media-col-default");
1763 cupsArrayAdd(ra
, "media-col-supported");
1764 cupsArrayAdd(ra
, "media-default");
1765 cupsArrayAdd(ra
, "media-source-supported");
1766 cupsArrayAdd(ra
, "media-supported");
1767 cupsArrayAdd(ra
, "media-type-supported");
1768 cupsArrayAdd(ra
, "multiple-document-handling");
1769 cupsArrayAdd(ra
, "multiple-document-handling-default");
1770 cupsArrayAdd(ra
, "multiple-document-handling-supported");
1771 cupsArrayAdd(ra
, "number-up");
1772 cupsArrayAdd(ra
, "number-up-default");
1773 cupsArrayAdd(ra
, "number-up-supported");
1774 cupsArrayAdd(ra
, "orientation-requested");
1775 cupsArrayAdd(ra
, "orientation-requested-default");
1776 cupsArrayAdd(ra
, "orientation-requested-supported");
1777 cupsArrayAdd(ra
, "page-ranges");
1778 cupsArrayAdd(ra
, "page-ranges-supported");
1779 cupsArrayAdd(ra
, "printer-resolution");
1780 cupsArrayAdd(ra
, "printer-resolution-default");
1781 cupsArrayAdd(ra
, "printer-resolution-supported");
1782 cupsArrayAdd(ra
, "print-quality");
1783 cupsArrayAdd(ra
, "print-quality-default");
1784 cupsArrayAdd(ra
, "print-quality-supported");
1785 cupsArrayAdd(ra
, "sides");
1786 cupsArrayAdd(ra
, "sides-default");
1787 cupsArrayAdd(ra
, "sides-supported");
1789 else if (!strcmp(value
, "job-description"))
1791 cupsArrayAdd(ra
, "date-time-at-completed");
1792 cupsArrayAdd(ra
, "date-time-at-creation");
1793 cupsArrayAdd(ra
, "date-time-at-processing");
1794 cupsArrayAdd(ra
, "job-detailed-status-message");
1795 cupsArrayAdd(ra
, "job-document-access-errors");
1796 cupsArrayAdd(ra
, "job-id");
1797 cupsArrayAdd(ra
, "job-impressions");
1798 cupsArrayAdd(ra
, "job-impressions-completed");
1799 cupsArrayAdd(ra
, "job-k-octets");
1800 cupsArrayAdd(ra
, "job-k-octets-processed");
1801 cupsArrayAdd(ra
, "job-media-sheets");
1802 cupsArrayAdd(ra
, "job-media-sheets-completed");
1803 cupsArrayAdd(ra
, "job-message-from-operator");
1804 cupsArrayAdd(ra
, "job-more-info");
1805 cupsArrayAdd(ra
, "job-name");
1806 cupsArrayAdd(ra
, "job-originating-user-name");
1807 cupsArrayAdd(ra
, "job-printer-up-time");
1808 cupsArrayAdd(ra
, "job-printer-uri");
1809 cupsArrayAdd(ra
, "job-state");
1810 cupsArrayAdd(ra
, "job-state-message");
1811 cupsArrayAdd(ra
, "job-state-reasons");
1812 cupsArrayAdd(ra
, "job-uri");
1813 cupsArrayAdd(ra
, "number-of-documents");
1814 cupsArrayAdd(ra
, "number-of-intervening-jobs");
1815 cupsArrayAdd(ra
, "output-device-assigned");
1816 cupsArrayAdd(ra
, "time-at-completed");
1817 cupsArrayAdd(ra
, "time-at-creation");
1818 cupsArrayAdd(ra
, "time-at-processing");
1820 else if (!strcmp(value
, "printer-description"))
1822 cupsArrayAdd(ra
, "charset-configured");
1823 cupsArrayAdd(ra
, "charset-supported");
1824 cupsArrayAdd(ra
, "color-supported");
1825 cupsArrayAdd(ra
, "compression-supported");
1826 cupsArrayAdd(ra
, "document-format-default");
1827 cupsArrayAdd(ra
, "document-format-supported");
1828 cupsArrayAdd(ra
, "generated-natural-language-supported");
1829 cupsArrayAdd(ra
, "ipp-versions-supported");
1830 cupsArrayAdd(ra
, "job-impressions-supported");
1831 cupsArrayAdd(ra
, "job-k-octets-supported");
1832 cupsArrayAdd(ra
, "job-media-sheets-supported");
1833 cupsArrayAdd(ra
, "multiple-document-jobs-supported");
1834 cupsArrayAdd(ra
, "multiple-operation-time-out");
1835 cupsArrayAdd(ra
, "natural-language-configured");
1836 cupsArrayAdd(ra
, "notify-attributes-supported");
1837 cupsArrayAdd(ra
, "notify-lease-duration-default");
1838 cupsArrayAdd(ra
, "notify-lease-duration-supported");
1839 cupsArrayAdd(ra
, "notify-max-events-supported");
1840 cupsArrayAdd(ra
, "notify-events-default");
1841 cupsArrayAdd(ra
, "notify-events-supported");
1842 cupsArrayAdd(ra
, "notify-pull-method-supported");
1843 cupsArrayAdd(ra
, "notify-schemes-supported");
1844 cupsArrayAdd(ra
, "operations-supported");
1845 cupsArrayAdd(ra
, "pages-per-minute");
1846 cupsArrayAdd(ra
, "pages-per-minute-color");
1847 cupsArrayAdd(ra
, "pdl-override-supported");
1848 cupsArrayAdd(ra
, "printer-alert");
1849 cupsArrayAdd(ra
, "printer-alert-description");
1850 cupsArrayAdd(ra
, "printer-current-time");
1851 cupsArrayAdd(ra
, "printer-driver-installer");
1852 cupsArrayAdd(ra
, "printer-info");
1853 cupsArrayAdd(ra
, "printer-is-accepting-jobs");
1854 cupsArrayAdd(ra
, "printer-location");
1855 cupsArrayAdd(ra
, "printer-make-and-model");
1856 cupsArrayAdd(ra
, "printer-message-from-operator");
1857 cupsArrayAdd(ra
, "printer-more-info");
1858 cupsArrayAdd(ra
, "printer-more-info-manufacturer");
1859 cupsArrayAdd(ra
, "printer-name");
1860 cupsArrayAdd(ra
, "printer-state");
1861 cupsArrayAdd(ra
, "printer-state-message");
1862 cupsArrayAdd(ra
, "printer-state-reasons");
1863 cupsArrayAdd(ra
, "printer-up-time");
1864 cupsArrayAdd(ra
, "printer-uri-supported");
1865 cupsArrayAdd(ra
, "queued-job-count");
1866 cupsArrayAdd(ra
, "reference-uri-schemes-supported");
1867 cupsArrayAdd(ra
, "uri-authentication-supported");
1868 cupsArrayAdd(ra
, "uri-security-supported");
1870 else if (!strcmp(value
, "printer-defaults"))
1872 cupsArrayAdd(ra
, "copies-default");
1873 cupsArrayAdd(ra
, "document-format-default");
1874 cupsArrayAdd(ra
, "finishings-default");
1875 cupsArrayAdd(ra
, "job-hold-until-default");
1876 cupsArrayAdd(ra
, "job-priority-default");
1877 cupsArrayAdd(ra
, "job-sheets-default");
1878 cupsArrayAdd(ra
, "media-default");
1879 cupsArrayAdd(ra
, "media-col-default");
1880 cupsArrayAdd(ra
, "number-up-default");
1881 cupsArrayAdd(ra
, "orientation-requested-default");
1882 cupsArrayAdd(ra
, "sides-default");
1884 else if (!strcmp(value
, "subscription-template"))
1886 cupsArrayAdd(ra
, "notify-attributes");
1887 cupsArrayAdd(ra
, "notify-charset");
1888 cupsArrayAdd(ra
, "notify-events");
1889 cupsArrayAdd(ra
, "notify-lease-duration");
1890 cupsArrayAdd(ra
, "notify-natural-language");
1891 cupsArrayAdd(ra
, "notify-pull-method");
1892 cupsArrayAdd(ra
, "notify-recipient-uri");
1893 cupsArrayAdd(ra
, "notify-time-interval");
1894 cupsArrayAdd(ra
, "notify-user-data");
1897 cupsArrayAdd(ra
, (void *)value
);
1905 * 'debug_attributes()' - Print attributes in a request or response.
1909 debug_attributes(const char *title
, /* I - Title */
1910 ipp_t
*ipp
, /* I - Request/response */
1911 int type
) /* I - 0 = object, 1 = request, 2 = response */
1913 ipp_tag_t group_tag
; /* Current group */
1914 ipp_attribute_t
*attr
; /* Current attribute */
1915 char buffer
[2048]; /* String buffer for value */
1916 int major
, minor
; /* Version */
1922 fprintf(stderr
, "%s:\n", title
);
1923 major
= ippGetVersion(ipp
, &minor
);
1924 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1926 fprintf(stderr
, " operation-id=%s(%04x)\n",
1927 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1929 fprintf(stderr
, " status-code=%s(%04x)\n",
1930 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1931 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1933 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1935 attr
= ippNextAttribute(ipp
))
1937 if (ippGetGroupTag(attr
) != group_tag
)
1939 group_tag
= ippGetGroupTag(attr
);
1940 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1943 if (ippGetName(attr
))
1945 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1946 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
1947 ippGetCount(attr
) > 1 ? "1setOf " : "",
1948 ippTagString(ippGetValueTag(attr
)), buffer
);
1955 * 'delete_client()' - Close the socket and free all memory used by a client
1960 delete_client(_ipp_client_t
*client
) /* I - Client */
1963 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
1966 * Flush pending writes before closing...
1969 httpFlushWrite(client
->http
);
1975 httpClose(client
->http
);
1977 ippDelete(client
->request
);
1978 ippDelete(client
->response
);
1985 * 'delete_job()' - Remove from the printer and free all memory used by a job
1990 delete_job(_ipp_job_t
*job
) /* I - Job */
1993 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
1995 ippDelete(job
->attrs
);
2000 unlink(job
->filename
);
2002 free(job
->filename
);
2010 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2011 * used by a printer object.
2015 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2017 if (printer
->ipv4
>= 0)
2018 close(printer
->ipv4
);
2020 if (printer
->ipv6
>= 0)
2021 close(printer
->ipv6
);
2024 if (printer
->printer_ref
)
2025 DNSServiceRefDeallocate(printer
->printer_ref
);
2027 if (printer
->ipp_ref
)
2028 DNSServiceRefDeallocate(printer
->ipp_ref
);
2031 if (printer
->ipps_ref
)
2032 DNSServiceRefDeallocate(printer
->ipps_ref
);
2033 # endif /* HAVE_SSL */
2034 if (printer
->http_ref
)
2035 DNSServiceRefDeallocate(printer
->http_ref
);
2037 if (printer
->common_ref
)
2038 DNSServiceRefDeallocate(printer
->common_ref
);
2040 TXTRecordDeallocate(&(printer
->ipp_txt
));
2042 if (printer
->dnssd_name
)
2043 free(printer
->dnssd_name
);
2044 #endif /* HAVE_DNSSD */
2047 free(printer
->name
);
2049 free(printer
->icon
);
2050 if (printer
->directory
)
2051 free(printer
->directory
);
2052 if (printer
->hostname
)
2053 free(printer
->hostname
);
2057 ippDelete(printer
->attrs
);
2058 cupsArrayDelete(printer
->jobs
);
2066 * 'dnssd_callback()' - Handle Bonjour registration events.
2071 DNSServiceRef sdRef
, /* I - Service reference */
2072 DNSServiceFlags flags
, /* I - Status flags */
2073 DNSServiceErrorType errorCode
, /* I - Error, if any */
2074 const char *name
, /* I - Service name */
2075 const char *regtype
, /* I - Service type */
2076 const char *domain
, /* I - Domain for service */
2077 _ipp_printer_t
*printer
) /* I - Printer */
2081 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2082 regtype
, (int)errorCode
);
2085 else if (_cups_strcasecmp(name
, printer
->dnssd_name
))
2088 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2090 /* No lock needed since only the main thread accesses/changes this */
2091 free(printer
->dnssd_name
);
2092 printer
->dnssd_name
= strdup(name
);
2095 #endif /* HAVE_DNSSD */
2099 * 'find_job()' - Find a job specified in a request.
2102 static _ipp_job_t
* /* O - Job or NULL */
2103 find_job(_ipp_client_t
*client
) /* I - Client */
2105 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2106 _ipp_job_t key
, /* Job search key */
2107 *job
; /* Matching job, if any */
2112 if ((attr
= ippFindAttribute(client
->request
, "job-uri",
2113 IPP_TAG_URI
)) != NULL
)
2115 const char *uri
= ippGetString(attr
, 0, NULL
);
2117 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2118 uri
[client
->printer
->urilen
] == '/')
2119 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2121 else if ((attr
= ippFindAttribute(client
->request
, "job-id",
2122 IPP_TAG_INTEGER
)) != NULL
)
2123 key
.id
= ippGetInteger(attr
, 0);
2125 _cupsRWLockRead(&(client
->printer
->rwlock
));
2126 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2127 _cupsRWUnlock(&(client
->printer
->rwlock
));
2134 * 'html_escape()' - Write a HTML-safe string.
2138 html_escape(_ipp_client_t
*client
, /* I - Client */
2139 const char *s
, /* I - String to write */
2140 size_t slen
) /* I - Number of characters to write */
2142 const char *start
, /* Start of segment */
2143 *end
; /* End of string */
2147 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2149 while (*s
&& s
< end
)
2151 if (*s
== '&' || *s
== '<')
2154 httpWrite2(client
->http
, start
, s
- start
);
2157 httpWrite2(client
->http
, "&", 5);
2159 httpWrite2(client
->http
, "<", 4);
2168 httpWrite2(client
->http
, start
, s
- start
);
2173 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2177 html_printf(_ipp_client_t
*client
, /* I - Client */
2178 const char *format
, /* I - Printf-style format string */
2179 ...) /* I - Additional arguments as needed */
2181 va_list ap
; /* Pointer to arguments */
2182 const char *start
; /* Start of string */
2183 char size
, /* Size character (h, l, L) */
2184 type
; /* Format type character */
2185 int width
, /* Width of field */
2186 prec
; /* Number of characters of precision */
2187 char tformat
[100], /* Temporary format string for sprintf() */
2188 *tptr
, /* Pointer into temporary format */
2189 temp
[1024]; /* Buffer for formatted numbers */
2190 char *s
; /* Pointer to string */
2194 * Loop through the format string, formatting as needed...
2197 va_start(ap
, format
);
2205 httpWrite2(client
->http
, start
, format
- start
);
2208 *tptr
++ = *format
++;
2212 httpWrite2(client
->http
, "%", 1);
2216 else if (strchr(" -+#\'", *format
))
2217 *tptr
++ = *format
++;
2222 * Get width from argument...
2226 width
= va_arg(ap
, int);
2228 snprintf(tptr
, sizeof(tformat
) - (tptr
- tformat
), "%d", width
);
2229 tptr
+= strlen(tptr
);
2235 while (isdigit(*format
& 255))
2237 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2240 width
= width
* 10 + *format
++ - '0';
2246 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2254 * Get precision from argument...
2258 prec
= va_arg(ap
, int);
2260 snprintf(tptr
, sizeof(tformat
) - (tptr
- tformat
), "%d", prec
);
2261 tptr
+= strlen(tptr
);
2267 while (isdigit(*format
& 255))
2269 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2272 prec
= prec
* 10 + *format
++ - '0';
2277 if (*format
== 'l' && format
[1] == 'l')
2281 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2289 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2291 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2306 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2315 case 'E' : /* Floating point formats */
2320 if ((width
+ 2) > sizeof(temp
))
2323 sprintf(temp
, tformat
, va_arg(ap
, double));
2325 httpWrite2(client
->http
, temp
, strlen(temp
));
2328 case 'B' : /* Integer formats */
2336 if ((width
+ 2) > sizeof(temp
))
2339 # ifdef HAVE_LONG_LONG
2341 sprintf(temp
, tformat
, va_arg(ap
, long long));
2343 # endif /* HAVE_LONG_LONG */
2345 sprintf(temp
, tformat
, va_arg(ap
, long));
2347 sprintf(temp
, tformat
, va_arg(ap
, int));
2349 httpWrite2(client
->http
, temp
, strlen(temp
));
2352 case 'p' : /* Pointer value */
2353 if ((width
+ 2) > sizeof(temp
))
2356 sprintf(temp
, tformat
, va_arg(ap
, void *));
2358 httpWrite2(client
->http
, temp
, strlen(temp
));
2361 case 'c' : /* Character or character array */
2364 temp
[0] = va_arg(ap
, int);
2366 html_escape(client
, temp
, 1);
2369 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2372 case 's' : /* String */
2373 if ((s
= va_arg(ap
, char *)) == NULL
)
2376 html_escape(client
, s
, strlen(s
));
2385 httpWrite2(client
->http
, start
, format
- start
);
2392 * 'ipp_cancel_job()' - Cancel a job.
2396 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2398 _ipp_job_t
*job
; /* Job information */
2405 if ((job
= find_job(client
)) == NULL
)
2407 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2412 * See if the job is already completed, canceled, or aborted; if so,
2413 * we can't cancel...
2418 case IPP_JSTATE_CANCELED
:
2419 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2420 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2423 case IPP_JSTATE_ABORTED
:
2424 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2425 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2428 case IPP_JSTATE_COMPLETED
:
2429 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2430 "Job #%d is already completed - can\'t cancel.", job
->id
);
2438 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2440 if (job
->state
== IPP_JSTATE_PROCESSING
||
2441 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2445 job
->state
= IPP_JSTATE_CANCELED
;
2446 job
->completed
= time(NULL
);
2449 _cupsRWUnlock(&(client
->printer
->rwlock
));
2451 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2458 * 'ipp_create_job()' - Create a job object.
2462 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2464 _ipp_job_t
*job
; /* New job */
2465 cups_array_t
*ra
; /* Attributes to send in response */
2469 * Validate print job attributes...
2472 if (!valid_job_attributes(client
))
2474 httpFlush(client
->http
);
2479 * Do we have a file to print?
2482 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2484 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2485 "Unexpected document data following request.");
2493 if ((job
= create_job(client
)) == NULL
)
2495 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2496 "Currently printing another job.");
2501 * Return the job info...
2504 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2506 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2507 cupsArrayAdd(ra
, "job-id");
2508 cupsArrayAdd(ra
, "job-state");
2509 cupsArrayAdd(ra
, "job-state-reasons");
2510 cupsArrayAdd(ra
, "job-uri");
2512 copy_job_attributes(client
, job
, ra
);
2513 cupsArrayDelete(ra
);
2518 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2522 ipp_get_job_attributes(
2523 _ipp_client_t
*client
) /* I - Client */
2525 _ipp_job_t
*job
; /* Job */
2526 cups_array_t
*ra
; /* requested-attributes */
2529 if ((job
= find_job(client
)) == NULL
)
2531 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2535 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2537 ra
= create_requested_array(client
);
2538 copy_job_attributes(client
, job
, ra
);
2539 cupsArrayDelete(ra
);
2544 * 'ipp_get_jobs()' - Get a list of job objects.
2548 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2550 ipp_attribute_t
*attr
; /* Current attribute */
2551 const char *which_jobs
= NULL
;
2552 /* which-jobs values */
2553 int job_comparison
; /* Job comparison */
2554 ipp_jstate_t job_state
; /* job-state value */
2555 int first_job_id
, /* First job ID */
2556 limit
, /* Maximum number of jobs to return */
2557 count
; /* Number of jobs that match */
2558 const char *username
; /* Username */
2559 _ipp_job_t
*job
; /* Current job pointer */
2560 cups_array_t
*ra
; /* Requested attributes array */
2564 * See if the "which-jobs" attribute have been specified...
2567 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2568 IPP_TAG_KEYWORD
)) != NULL
)
2570 which_jobs
= ippGetString(attr
, 0, NULL
);
2571 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
2574 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
2576 job_comparison
= -1;
2577 job_state
= IPP_JSTATE_STOPPED
;
2579 else if (!strcmp(which_jobs
, "completed"))
2582 job_state
= IPP_JSTATE_CANCELED
;
2584 else if (!strcmp(which_jobs
, "aborted"))
2587 job_state
= IPP_JSTATE_ABORTED
;
2589 else if (!strcmp(which_jobs
, "all"))
2592 job_state
= IPP_JSTATE_PENDING
;
2594 else if (!strcmp(which_jobs
, "canceled"))
2597 job_state
= IPP_JSTATE_CANCELED
;
2599 else if (!strcmp(which_jobs
, "pending"))
2602 job_state
= IPP_JSTATE_PENDING
;
2604 else if (!strcmp(which_jobs
, "pending-held"))
2607 job_state
= IPP_JSTATE_HELD
;
2609 else if (!strcmp(which_jobs
, "processing"))
2612 job_state
= IPP_JSTATE_PROCESSING
;
2614 else if (!strcmp(which_jobs
, "processing-stopped"))
2617 job_state
= IPP_JSTATE_STOPPED
;
2621 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
2622 "The which-jobs value \"%s\" is not supported.", which_jobs
);
2623 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2624 "which-jobs", NULL
, which_jobs
);
2629 * See if they want to limit the number of jobs reported...
2632 if ((attr
= ippFindAttribute(client
->request
, "limit",
2633 IPP_TAG_INTEGER
)) != NULL
)
2635 limit
= ippGetInteger(attr
, 0);
2637 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
2642 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2643 IPP_TAG_INTEGER
)) != NULL
)
2645 first_job_id
= ippGetInteger(attr
, 0);
2647 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
2654 * See if we only want to see jobs for a specific user...
2659 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2660 IPP_TAG_BOOLEAN
)) != NULL
)
2662 int my_jobs
= ippGetBoolean(attr
, 0);
2664 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
2665 my_jobs
? "true" : "false");
2669 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
2670 IPP_TAG_NAME
)) == NULL
)
2672 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2673 "Need requesting-user-name with my-jobs.");
2677 username
= ippGetString(attr
, 0, NULL
);
2679 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2680 client
->hostname
, username
);
2685 * OK, build a list of jobs for this printer...
2688 if ((ra
= create_requested_array(client
)) == NULL
&&
2689 !ippFindAttribute(client
->request
, "requested-attributes",
2693 * IPP conformance - Get-Jobs has a default requested-attributes value of
2694 * "job-id" and "job-uri".
2697 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2698 cupsArrayAdd(ra
, "job-id");
2699 cupsArrayAdd(ra
, "job-uri");
2702 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2704 _cupsRWLockRead(&(client
->printer
->rwlock
));
2706 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
2707 (limit
<= 0 || count
< limit
) && job
;
2708 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
2711 * Filter out jobs that don't match...
2714 if ((job_comparison
< 0 && job
->state
> job_state
) ||
2715 (job_comparison
== 0 && job
->state
!= job_state
) ||
2716 (job_comparison
> 0 && job
->state
< job_state
) ||
2717 job
->id
< first_job_id
||
2718 (username
&& job
->username
&&
2719 _cups_strcasecmp(username
, job
->username
)))
2723 ippAddSeparator(client
->response
);
2726 copy_job_attributes(client
, job
, ra
);
2729 cupsArrayDelete(ra
);
2731 _cupsRWUnlock(&(client
->printer
->rwlock
));
2736 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2740 ipp_get_printer_attributes(
2741 _ipp_client_t
*client
) /* I - Client */
2743 cups_array_t
*ra
; /* Requested attributes array */
2744 _ipp_printer_t
*printer
; /* Printer */
2748 * Send the attributes...
2751 ra
= create_requested_array(client
);
2752 printer
= client
->printer
;
2754 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2756 _cupsRWLockRead(&(printer
->rwlock
));
2758 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
2759 IPP_TAG_CUPS_CONST
);
2761 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
2762 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
2763 "printer-state", printer
->state
);
2765 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
2767 if (printer
->state_reasons
== _IPP_PSTATE_NONE
)
2768 ippAddString(client
->response
, IPP_TAG_PRINTER
,
2769 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
2770 "printer-state-reasons", NULL
, "none");
2773 int num_reasons
= 0;/* Number of reasons */
2774 const char *reasons
[32]; /* Reason strings */
2776 if (printer
->state_reasons
& _IPP_PSTATE_OTHER
)
2777 reasons
[num_reasons
++] = "other";
2778 if (printer
->state_reasons
& _IPP_PSTATE_COVER_OPEN
)
2779 reasons
[num_reasons
++] = "cover-open";
2780 if (printer
->state_reasons
& _IPP_PSTATE_INPUT_TRAY_MISSING
)
2781 reasons
[num_reasons
++] = "input-tray-missing";
2782 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_SUPPLY_EMPTY
)
2783 reasons
[num_reasons
++] = "marker-supply-empty-warning";
2784 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_SUPPLY_LOW
)
2785 reasons
[num_reasons
++] = "marker-supply-low-report";
2786 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL
)
2787 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
2788 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_WASTE_FULL
)
2789 reasons
[num_reasons
++] = "marker-waste-full-warning";
2790 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_EMPTY
)
2791 reasons
[num_reasons
++] = "media-empty-warning";
2792 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_JAM
)
2793 reasons
[num_reasons
++] = "media-jam-warning";
2794 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_LOW
)
2795 reasons
[num_reasons
++] = "media-low-report";
2796 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_NEEDED
)
2797 reasons
[num_reasons
++] = "media-needed-report";
2798 if (printer
->state_reasons
& _IPP_PSTATE_MOVING_TO_PAUSED
)
2799 reasons
[num_reasons
++] = "moving-to-paused";
2800 if (printer
->state_reasons
& _IPP_PSTATE_PAUSED
)
2801 reasons
[num_reasons
++] = "paused";
2802 if (printer
->state_reasons
& _IPP_PSTATE_SPOOL_AREA_FULL
)
2803 reasons
[num_reasons
++] = "spool-area-full";
2804 if (printer
->state_reasons
& _IPP_PSTATE_TONER_EMPTY
)
2805 reasons
[num_reasons
++] = "toner-empty-warning";
2806 if (printer
->state_reasons
& _IPP_PSTATE_TONER_LOW
)
2807 reasons
[num_reasons
++] = "toner-low-report";
2809 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
2810 IPP_TAG_KEYWORD
| IPP_TAG_CUPS_CONST
,
2811 "printer-state-reasons", num_reasons
, NULL
, reasons
);
2815 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
2816 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2817 "printer-up-time", (int)time(NULL
));
2819 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
2820 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2822 printer
->active_job
&&
2823 printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
2825 _cupsRWUnlock(&(printer
->rwlock
));
2827 cupsArrayDelete(ra
);
2832 * 'ipp_print_job()' - Create a job object with an attached document.
2836 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
2838 _ipp_job_t
*job
; /* New job */
2839 char filename
[1024], /* Filename buffer */
2840 buffer
[4096]; /* Copy buffer */
2841 ssize_t bytes
; /* Bytes read */
2842 cups_array_t
*ra
; /* Attributes to send in response */
2846 * Validate print job attributes...
2849 if (!valid_job_attributes(client
))
2851 httpFlush(client
->http
);
2856 * Do we have a file to print?
2859 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
2861 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
2869 if ((job
= create_job(client
)) == NULL
)
2871 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2872 "Currently printing another job.");
2877 * Create a file for the request data...
2880 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
2881 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
2882 client
->printer
->directory
, job
->id
);
2883 else if (!_cups_strcasecmp(job
->format
, "image/png"))
2884 snprintf(filename
, sizeof(filename
), "%s/%d.png",
2885 client
->printer
->directory
, job
->id
);
2886 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
2887 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
2888 client
->printer
->directory
, job
->id
);
2889 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
2890 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
2891 client
->printer
->directory
, job
->id
);
2893 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
2894 client
->printer
->directory
, job
->id
);
2896 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
2898 job
->state
= IPP_JSTATE_ABORTED
;
2900 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2901 "Unable to create print file: %s", strerror(errno
));
2905 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
2907 if (write(job
->fd
, buffer
, bytes
) < bytes
)
2909 int error
= errno
; /* Write error */
2911 job
->state
= IPP_JSTATE_ABORTED
;
2918 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2919 "Unable to write print file: %s", strerror(error
));
2927 * Got an error while reading the print data, so abort this job.
2930 job
->state
= IPP_JSTATE_ABORTED
;
2937 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2938 "Unable to read print file.");
2944 int error
= errno
; /* Write error */
2946 job
->state
= IPP_JSTATE_ABORTED
;
2951 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2952 "Unable to write print file: %s", strerror(error
));
2957 job
->filename
= strdup(filename
);
2958 job
->state
= IPP_JSTATE_PENDING
;
2961 * Process the job...
2965 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
2967 job
->state
= IPP_JSTATE_ABORTED
;
2968 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
2977 * Return the job info...
2980 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2982 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2983 cupsArrayAdd(ra
, "job-id");
2984 cupsArrayAdd(ra
, "job-state");
2985 cupsArrayAdd(ra
, "job-state-reasons");
2986 cupsArrayAdd(ra
, "job-uri");
2988 copy_job_attributes(client
, job
, ra
);
2989 cupsArrayDelete(ra
);
2994 * 'ipp_print_uri()' - Create a job object with a referenced document.
2998 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3000 _ipp_job_t
*job
; /* New job */
3001 ipp_attribute_t
*uri
; /* document-uri */
3002 char scheme
[256], /* URI scheme */
3003 userpass
[256], /* Username and password info */
3004 hostname
[256], /* Hostname */
3005 resource
[1024]; /* Resource path */
3006 int port
; /* Port number */
3007 http_uri_status_t uri_status
; /* URI decode status */
3008 http_encryption_t encryption
; /* Encryption to use, if any */
3009 http_t
*http
; /* Connection for http/https URIs */
3010 http_status_t status
; /* Access status for http/https URIs */
3011 int infile
; /* Input file for local file URIs */
3012 char filename
[1024], /* Filename buffer */
3013 buffer
[4096]; /* Copy buffer */
3014 ssize_t bytes
; /* Bytes read */
3015 cups_array_t
*ra
; /* Attributes to send in response */
3016 static const char * const uri_status_strings
[] =
3017 { /* URI decode errors */
3019 "Bad arguments to function.",
3020 "Bad resource in URI.",
3021 "Bad port number in URI.",
3022 "Bad hostname in URI.",
3023 "Bad username in URI.",
3024 "Bad scheme in URI.",
3030 * Validate print job attributes...
3033 if (!valid_job_attributes(client
))
3035 httpFlush(client
->http
);
3040 * Do we have a file to print?
3043 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3045 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3046 "Unexpected document data following request.");
3051 * Do we have a document URI?
3054 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3055 IPP_TAG_URI
)) == NULL
)
3057 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3061 if (ippGetCount(uri
) != 1)
3063 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3064 "Too many document-uri values.");
3068 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3069 scheme
, sizeof(scheme
), userpass
,
3070 sizeof(userpass
), hostname
, sizeof(hostname
),
3071 &port
, resource
, sizeof(resource
));
3072 if (uri_status
< HTTP_URI_STATUS_OK
)
3074 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3075 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3079 if (strcmp(scheme
, "file") &&
3081 strcmp(scheme
, "https") &&
3082 #endif /* HAVE_SSL */
3083 strcmp(scheme
, "http"))
3085 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3086 "URI scheme \"%s\" not supported.", scheme
);
3090 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3092 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3093 "Unable to access URI: %s", strerror(errno
));
3101 if ((job
= create_job(client
)) == NULL
)
3103 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3104 "Currently printing another job.");
3109 * Create a file for the request data...
3112 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3113 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3114 client
->printer
->directory
, job
->id
);
3115 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3116 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3117 client
->printer
->directory
, job
->id
);
3118 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3119 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3120 client
->printer
->directory
, job
->id
);
3121 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3122 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3123 client
->printer
->directory
, job
->id
);
3125 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3126 client
->printer
->directory
, job
->id
);
3128 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3130 job
->state
= IPP_JSTATE_ABORTED
;
3132 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3133 "Unable to create print file: %s", strerror(errno
));
3137 if (!strcmp(scheme
, "file"))
3139 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3141 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3142 "Unable to access URI: %s", strerror(errno
));
3148 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3149 (errno
== EAGAIN
|| errno
== EINTR
))
3151 else if (bytes
> 0 && write(job
->fd
, buffer
, bytes
) < bytes
)
3153 int error
= errno
; /* Write error */
3155 job
->state
= IPP_JSTATE_ABORTED
;
3163 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3164 "Unable to write print file: %s", strerror(error
));
3175 if (port
== 443 || !strcmp(scheme
, "https"))
3176 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3178 #endif /* HAVE_SSL */
3179 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3181 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3182 1, 30000, NULL
)) == NULL
)
3184 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3185 "Unable to connect to %s: %s", hostname
,
3186 cupsLastErrorString());
3187 job
->state
= IPP_JSTATE_ABORTED
;
3196 httpClearFields(http
);
3197 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3198 if (httpGet(http
, resource
))
3200 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3201 "Unable to GET URI: %s", strerror(errno
));
3203 job
->state
= IPP_JSTATE_ABORTED
;
3213 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3215 if (status
!= HTTP_STATUS_OK
)
3217 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3218 "Unable to GET URI: %s", httpStatus(status
));
3220 job
->state
= IPP_JSTATE_ABORTED
;
3230 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3232 if (write(job
->fd
, buffer
, bytes
) < bytes
)
3234 int error
= errno
; /* Write error */
3236 job
->state
= IPP_JSTATE_ABORTED
;
3244 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3245 "Unable to write print file: %s", strerror(error
));
3255 int error
= errno
; /* Write error */
3257 job
->state
= IPP_JSTATE_ABORTED
;
3262 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3263 "Unable to write print file: %s", strerror(error
));
3268 job
->filename
= strdup(filename
);
3269 job
->state
= IPP_JSTATE_PENDING
;
3272 * Process the job...
3276 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3278 job
->state
= IPP_JSTATE_ABORTED
;
3279 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3288 * Return the job info...
3291 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3293 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3294 cupsArrayAdd(ra
, "job-id");
3295 cupsArrayAdd(ra
, "job-state");
3296 cupsArrayAdd(ra
, "job-state-reasons");
3297 cupsArrayAdd(ra
, "job-uri");
3299 copy_job_attributes(client
, job
, ra
);
3300 cupsArrayDelete(ra
);
3305 * 'ipp_send_document()' - Add an attached document to a job object created with
3310 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3312 _ipp_job_t
*job
; /* Job information */
3313 char filename
[1024], /* Filename buffer */
3314 buffer
[4096]; /* Copy buffer */
3315 ssize_t bytes
; /* Bytes read */
3316 ipp_attribute_t
*attr
; /* Current attribute */
3317 cups_array_t
*ra
; /* Attributes to send in response */
3324 if ((job
= find_job(client
)) == NULL
)
3326 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3327 httpFlush(client
->http
);
3332 * See if we already have a document for this job or the job has already
3333 * in a non-pending state...
3336 if (job
->state
> IPP_JSTATE_HELD
)
3338 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3339 "Job is not in a pending state.");
3340 httpFlush(client
->http
);
3343 else if (job
->filename
|| job
->fd
>= 0)
3345 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3346 "Multiple document jobs are not supported.");
3347 httpFlush(client
->http
);
3351 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3352 IPP_TAG_ZERO
)) == NULL
)
3354 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3355 "Missing required last-document attribute.");
3356 httpFlush(client
->http
);
3359 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3360 !ippGetBoolean(attr
, 0))
3362 respond_unsupported(client
, attr
);
3363 httpFlush(client
->http
);
3368 * Validate document attributes...
3371 if (!valid_doc_attributes(client
))
3373 httpFlush(client
->http
);
3378 * Get the document format for the job...
3381 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3383 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3384 IPP_TAG_MIMETYPE
)) != NULL
)
3385 job
->format
= ippGetString(attr
, 0, NULL
);
3387 job
->format
= "application/octet-stream";
3390 * Create a file for the request data...
3393 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3394 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3395 client
->printer
->directory
, job
->id
);
3396 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3397 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3398 client
->printer
->directory
, job
->id
);
3399 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3400 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3401 client
->printer
->directory
, job
->id
);
3402 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3403 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3404 client
->printer
->directory
, job
->id
);
3406 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3407 client
->printer
->directory
, job
->id
);
3409 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3411 _cupsRWUnlock(&(client
->printer
->rwlock
));
3415 job
->state
= IPP_JSTATE_ABORTED
;
3417 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3418 "Unable to create print file: %s", strerror(errno
));
3422 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3424 if (write(job
->fd
, buffer
, bytes
) < bytes
)
3426 int error
= errno
; /* Write error */
3428 job
->state
= IPP_JSTATE_ABORTED
;
3435 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3436 "Unable to write print file: %s", strerror(error
));
3444 * Got an error while reading the print data, so abort this job.
3447 job
->state
= IPP_JSTATE_ABORTED
;
3454 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3455 "Unable to read print file.");
3461 int error
= errno
; /* Write error */
3463 job
->state
= IPP_JSTATE_ABORTED
;
3468 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3469 "Unable to write print file: %s", strerror(error
));
3473 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3476 job
->filename
= strdup(filename
);
3477 job
->state
= IPP_JSTATE_PENDING
;
3479 _cupsRWUnlock(&(client
->printer
->rwlock
));
3482 * Process the job...
3486 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3488 job
->state
= IPP_JSTATE_ABORTED
;
3489 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3498 * Return the job info...
3501 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3503 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3504 cupsArrayAdd(ra
, "job-id");
3505 cupsArrayAdd(ra
, "job-state");
3506 cupsArrayAdd(ra
, "job-state-reasons");
3507 cupsArrayAdd(ra
, "job-uri");
3509 copy_job_attributes(client
, job
, ra
);
3510 cupsArrayDelete(ra
);
3515 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3520 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
3522 _ipp_job_t
*job
; /* Job information */
3523 ipp_attribute_t
*uri
; /* document-uri */
3524 char scheme
[256], /* URI scheme */
3525 userpass
[256], /* Username and password info */
3526 hostname
[256], /* Hostname */
3527 resource
[1024]; /* Resource path */
3528 int port
; /* Port number */
3529 http_uri_status_t uri_status
; /* URI decode status */
3530 http_encryption_t encryption
; /* Encryption to use, if any */
3531 http_t
*http
; /* Connection for http/https URIs */
3532 http_status_t status
; /* Access status for http/https URIs */
3533 int infile
; /* Input file for local file URIs */
3534 char filename
[1024], /* Filename buffer */
3535 buffer
[4096]; /* Copy buffer */
3536 ssize_t bytes
; /* Bytes read */
3537 ipp_attribute_t
*attr
; /* Current attribute */
3538 cups_array_t
*ra
; /* Attributes to send in response */
3539 static const char * const uri_status_strings
[] =
3540 { /* URI decode errors */
3542 "Bad arguments to function.",
3543 "Bad resource in URI.",
3544 "Bad port number in URI.",
3545 "Bad hostname in URI.",
3546 "Bad username in URI.",
3547 "Bad scheme in URI.",
3556 if ((job
= find_job(client
)) == NULL
)
3558 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3559 httpFlush(client
->http
);
3564 * See if we already have a document for this job or the job has already
3565 * in a non-pending state...
3568 if (job
->state
> IPP_JSTATE_HELD
)
3570 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3571 "Job is not in a pending state.");
3572 httpFlush(client
->http
);
3575 else if (job
->filename
|| job
->fd
>= 0)
3577 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3578 "Multiple document jobs are not supported.");
3579 httpFlush(client
->http
);
3583 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3584 IPP_TAG_ZERO
)) == NULL
)
3586 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3587 "Missing required last-document attribute.");
3588 httpFlush(client
->http
);
3591 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3592 !ippGetBoolean(attr
, 0))
3594 respond_unsupported(client
, attr
);
3595 httpFlush(client
->http
);
3600 * Validate document attributes...
3603 if (!valid_doc_attributes(client
))
3605 httpFlush(client
->http
);
3610 * Do we have a file to print?
3613 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3615 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3616 "Unexpected document data following request.");
3621 * Do we have a document URI?
3624 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3625 IPP_TAG_URI
)) == NULL
)
3627 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3631 if (ippGetCount(uri
) != 1)
3633 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3634 "Too many document-uri values.");
3638 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3639 scheme
, sizeof(scheme
), userpass
,
3640 sizeof(userpass
), hostname
, sizeof(hostname
),
3641 &port
, resource
, sizeof(resource
));
3642 if (uri_status
< HTTP_URI_STATUS_OK
)
3644 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3645 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3649 if (strcmp(scheme
, "file") &&
3651 strcmp(scheme
, "https") &&
3652 #endif /* HAVE_SSL */
3653 strcmp(scheme
, "http"))
3655 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3656 "URI scheme \"%s\" not supported.", scheme
);
3660 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3662 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3663 "Unable to access URI: %s", strerror(errno
));
3668 * Get the document format for the job...
3671 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3673 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3674 IPP_TAG_MIMETYPE
)) != NULL
)
3675 job
->format
= ippGetString(attr
, 0, NULL
);
3677 job
->format
= "application/octet-stream";
3680 * Create a file for the request data...
3683 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3684 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3685 client
->printer
->directory
, job
->id
);
3686 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3687 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3688 client
->printer
->directory
, job
->id
);
3689 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3690 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3691 client
->printer
->directory
, job
->id
);
3692 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3693 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3694 client
->printer
->directory
, job
->id
);
3696 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3697 client
->printer
->directory
, job
->id
);
3699 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3701 _cupsRWUnlock(&(client
->printer
->rwlock
));
3705 job
->state
= IPP_JSTATE_ABORTED
;
3707 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3708 "Unable to create print file: %s", strerror(errno
));
3712 if (!strcmp(scheme
, "file"))
3714 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3716 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3717 "Unable to access URI: %s", strerror(errno
));
3723 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3724 (errno
== EAGAIN
|| errno
== EINTR
))
3726 else if (bytes
> 0 && write(job
->fd
, buffer
, bytes
) < bytes
)
3728 int error
= errno
; /* Write error */
3730 job
->state
= IPP_JSTATE_ABORTED
;
3738 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3739 "Unable to write print file: %s", strerror(error
));
3750 if (port
== 443 || !strcmp(scheme
, "https"))
3751 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3753 #endif /* HAVE_SSL */
3754 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3756 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3757 1, 30000, NULL
)) == NULL
)
3759 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3760 "Unable to connect to %s: %s", hostname
,
3761 cupsLastErrorString());
3762 job
->state
= IPP_JSTATE_ABORTED
;
3771 httpClearFields(http
);
3772 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3773 if (httpGet(http
, resource
))
3775 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3776 "Unable to GET URI: %s", strerror(errno
));
3778 job
->state
= IPP_JSTATE_ABORTED
;
3788 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3790 if (status
!= HTTP_STATUS_OK
)
3792 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3793 "Unable to GET URI: %s", httpStatus(status
));
3795 job
->state
= IPP_JSTATE_ABORTED
;
3805 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3807 if (write(job
->fd
, buffer
, bytes
) < bytes
)
3809 int error
= errno
; /* Write error */
3811 job
->state
= IPP_JSTATE_ABORTED
;
3819 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3820 "Unable to write print file: %s", strerror(error
));
3830 int error
= errno
; /* Write error */
3832 job
->state
= IPP_JSTATE_ABORTED
;
3837 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3838 "Unable to write print file: %s", strerror(error
));
3842 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3845 job
->filename
= strdup(filename
);
3846 job
->state
= IPP_JSTATE_PENDING
;
3848 _cupsRWUnlock(&(client
->printer
->rwlock
));
3851 * Process the job...
3855 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3857 job
->state
= IPP_JSTATE_ABORTED
;
3858 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3867 * Return the job info...
3870 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3872 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3873 cupsArrayAdd(ra
, "job-id");
3874 cupsArrayAdd(ra
, "job-state");
3875 cupsArrayAdd(ra
, "job-state-reasons");
3876 cupsArrayAdd(ra
, "job-uri");
3878 copy_job_attributes(client
, job
, ra
);
3879 cupsArrayDelete(ra
);
3884 * 'ipp_validate_job()' - Validate job creation attributes.
3888 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
3890 if (valid_job_attributes(client
))
3891 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3896 * 'process_client()' - Process client requests on a thread.
3899 static void * /* O - Exit status */
3900 process_client(_ipp_client_t
*client
) /* I - Client */
3903 * Loop until we are out of requests or timeout (30 seconds)...
3906 while (httpWait(client
->http
, 30000))
3907 if (!process_http(client
))
3911 * Close the conection to the client and return...
3914 delete_client(client
);
3921 * 'process_http()' - Process a HTTP request.
3924 int /* O - 1 on success, 0 on failure */
3925 process_http(_ipp_client_t
*client
) /* I - Client connection */
3927 char uri
[1024]; /* URI */
3928 http_state_t http_state
; /* HTTP state */
3929 http_status_t http_status
; /* HTTP status */
3930 ipp_state_t ipp_state
; /* State of IPP transfer */
3931 char scheme
[32], /* Method/scheme */
3932 userpass
[128], /* Username:password */
3933 hostname
[HTTP_MAX_HOST
];
3935 int port
; /* Port number */
3936 const char *encoding
; /* Content-Encoding value */
3937 static const char * const http_states
[] =
3938 { /* Strings for logging HTTP method */
3959 * Clear state variables...
3962 ippDelete(client
->request
);
3963 ippDelete(client
->response
);
3965 client
->request
= NULL
;
3966 client
->response
= NULL
;
3967 client
->operation
= HTTP_STATE_WAITING
;
3970 * Read a request from the connection...
3973 while ((http_state
= httpReadRequest(client
->http
, uri
,
3974 sizeof(uri
))) == HTTP_STATE_WAITING
)
3978 * Parse the request line...
3981 if (http_state
== HTTP_STATE_ERROR
)
3983 if (httpError(client
->http
) == EPIPE
)
3984 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
3986 fprintf(stderr
, "%s Bad request line.\n", client
->hostname
);
3990 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
3992 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
3993 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3996 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
3998 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
3999 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4003 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
4007 * Separate the URI into its components...
4010 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4011 userpass
, sizeof(userpass
),
4012 hostname
, sizeof(hostname
), &port
,
4013 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
)
4015 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
4016 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4021 * Process the request...
4024 client
->start
= time(NULL
);
4025 client
->operation
= httpGetState(client
->http
);
4028 * Parse incoming parameters until the status changes...
4031 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4033 if (http_status
!= HTTP_STATUS_OK
)
4035 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4039 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4040 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4043 * HTTP/1.1 and higher require the "Host:" field...
4046 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4051 * Handle HTTP Upgrade...
4054 if (!_cups_strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4057 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4062 * Handle HTTP Expect...
4065 if (httpGetExpect(client
->http
) &&
4066 (client
->operation
== HTTP_STATE_POST
||
4067 client
->operation
== HTTP_STATE_PUT
))
4069 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4072 * Send 100-continue header...
4075 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4081 * Send 417-expectation-failed header...
4084 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4090 * Handle new transfers...
4093 encoding
= httpGetContentEncoding(client
->http
);
4095 switch (client
->operation
)
4097 case HTTP_STATE_OPTIONS
:
4099 * Do HEAD/OPTIONS command...
4102 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4104 case HTTP_STATE_HEAD
:
4105 if (!strcmp(client
->uri
, "/icon.png"))
4106 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4107 else if (!strcmp(client
->uri
, "/"))
4108 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4110 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4113 case HTTP_STATE_GET
:
4114 if (!strcmp(client
->uri
, "/icon.png"))
4117 * Send PNG icon file.
4120 int fd
; /* Icon file */
4121 struct stat fileinfo
; /* Icon file information */
4122 char buffer
[4096]; /* Copy buffer */
4123 ssize_t bytes
; /* Bytes */
4125 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
4127 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4128 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4130 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
4137 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4138 httpWrite2(client
->http
, buffer
, bytes
);
4140 httpFlushWrite(client
->http
);
4145 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4147 else if (!strcmp(client
->uri
, "/"))
4150 * Show web status page...
4153 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4157 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
4158 "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
4161 "<title>%s</title>\n"
4162 "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
4163 "type=\"image/png\">\n"
4167 "<h1><img align=\"right\" src=\"/icon.png\">%s</h1>\n"
4168 "<p>%s, %d job(s).</p>\n"
4171 client
->printer
->name
, client
->printer
->name
,
4172 client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" :
4173 client
->printer
->state
== IPP_PSTATE_PROCESSING
?
4174 "Printing" : "Stopped",
4175 cupsArrayCount(client
->printer
->jobs
));
4176 httpWrite2(client
->http
, "", 0);
4181 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4184 case HTTP_STATE_POST
:
4185 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
4189 * Not an IPP request...
4192 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
4196 * Read the IPP request...
4199 client
->request
= ippNew();
4201 while ((ipp_state
= ippRead(client
->http
,
4202 client
->request
)) != IPP_STATE_DATA
)
4204 if (ipp_state
== IPP_STATE_ERROR
)
4206 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
4207 cupsLastErrorString());
4208 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4214 * Now that we have the IPP request, process the request...
4217 return (process_ipp(client
));
4220 break; /* Anti-compiler-warning-code */
4228 * 'process_ipp()' - Process an IPP request.
4231 static int /* O - 1 on success, 0 on error */
4232 process_ipp(_ipp_client_t
*client
) /* I - Client */
4234 ipp_tag_t group
; /* Current group tag */
4235 ipp_attribute_t
*attr
; /* Current attribute */
4236 ipp_attribute_t
*charset
; /* Character set attribute */
4237 ipp_attribute_t
*language
; /* Language attribute */
4238 ipp_attribute_t
*uri
; /* Printer URI attribute */
4239 int major
, minor
; /* Version number */
4240 const char *name
; /* Name of attribute */
4243 debug_attributes("Request", client
->request
, 1);
4246 * First build an empty response message for this request...
4249 client
->operation_id
= ippGetOperation(client
->request
);
4250 client
->response
= ippNewResponse(client
->request
);
4253 * Then validate the request header and required attributes...
4256 major
= ippGetVersion(client
->request
, &minor
);
4258 if (major
< 1 || major
> 2)
4261 * Return an error, since we only support IPP 1.x and 2.x.
4264 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
4265 "Bad request version number %d.%d.", major
, minor
);
4267 else if (ippGetRequestId(client
->request
) <= 0)
4268 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
4269 ippGetRequestId(client
->request
));
4270 else if (!ippFirstAttribute(client
->request
))
4271 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4272 "No attributes in request.");
4276 * Make sure that the attributes are provided in the correct order and
4277 * don't repeat groups...
4280 for (attr
= ippFirstAttribute(client
->request
),
4281 group
= ippGetGroupTag(attr
);
4283 attr
= ippNextAttribute(client
->request
))
4285 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
4288 * Out of order; return an error...
4291 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4292 "Attribute groups are out of order (%x < %x).",
4293 ippGetGroupTag(attr
), group
);
4297 group
= ippGetGroupTag(attr
);
4303 * Then make sure that the first three attributes are:
4305 * attributes-charset
4306 * attributes-natural-language
4307 * printer-uri/job-uri
4310 attr
= ippFirstAttribute(client
->request
);
4311 name
= ippGetName(attr
);
4312 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
4313 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
4318 attr
= ippNextAttribute(client
->request
);
4319 name
= ippGetName(attr
);
4321 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
4322 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
4327 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
4328 IPP_TAG_URI
)) != NULL
)
4330 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
4331 IPP_TAG_URI
)) != NULL
)
4337 _cups_strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
4338 _cups_strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
4341 * Bad character set...
4344 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4345 "Unsupported character set \"%s\".",
4346 ippGetString(charset
, 0, NULL
));
4348 else if (!charset
|| !language
|| !uri
)
4351 * Return an error, since attributes-charset,
4352 * attributes-natural-language, and printer-uri/job-uri are required
4353 * for all operations.
4356 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4357 "Missing required attributes.");
4359 else if (strcmp(ippGetString(uri
, 0, NULL
), client
->printer
->uri
) &&
4360 strncmp(ippGetString(uri
, 0, NULL
), client
->printer
->uri
,
4361 client
->printer
->urilen
))
4363 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
4364 ippGetName(uri
), ippGetString(uri
, 0, NULL
));
4369 * Try processing the operation...
4372 #if 0 /* Already doing this in process_http()... */
4373 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4376 * Send 100-continue header...
4379 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4384 switch (ippGetOperation(client
->request
))
4386 case IPP_OP_PRINT_JOB
:
4387 ipp_print_job(client
);
4390 case IPP_OP_PRINT_URI
:
4391 ipp_print_uri(client
);
4394 case IPP_OP_VALIDATE_JOB
:
4395 ipp_validate_job(client
);
4398 case IPP_OP_CREATE_JOB
:
4399 ipp_create_job(client
);
4402 case IPP_OP_SEND_DOCUMENT
:
4403 ipp_send_document(client
);
4406 case IPP_OP_SEND_URI
:
4407 ipp_send_uri(client
);
4410 case IPP_OP_CANCEL_JOB
:
4411 ipp_cancel_job(client
);
4414 case IPP_OP_GET_JOB_ATTRIBUTES
:
4415 ipp_get_job_attributes(client
);
4418 case IPP_OP_GET_JOBS
:
4419 ipp_get_jobs(client
);
4422 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
4423 ipp_get_printer_attributes(client
);
4427 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
4428 "Operation not supported.");
4436 * Send the HTTP header and return...
4439 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
4440 httpFlush(client
->http
); /* Flush trailing (junk) data */
4442 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
4443 ippLength(client
->response
)));
4448 * 'process_job()' - Process a print job.
4451 static void * /* O - Thread exit status */
4452 process_job(_ipp_job_t
*job
) /* I - Job */
4454 job
->state
= IPP_JSTATE_PROCESSING
;
4455 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
4460 job
->state
= IPP_JSTATE_CANCELED
;
4462 job
->state
= IPP_JSTATE_COMPLETED
;
4464 job
->completed
= time(NULL
);
4465 job
->printer
->state
= IPP_PSTATE_IDLE
;
4466 job
->printer
->active_job
= NULL
;
4474 * 'register_printer()' - Register a printer object via Bonjour.
4477 static int /* O - 1 on success, 0 on error */
4479 _ipp_printer_t
*printer
, /* I - Printer */
4480 const char *location
, /* I - Location */
4481 const char *make
, /* I - Manufacturer */
4482 const char *model
, /* I - Model name */
4483 const char *formats
, /* I - Supported formats */
4484 const char *adminurl
, /* I - Web interface URL */
4485 int color
, /* I - 1 = color, 0 = monochrome */
4486 int duplex
, /* I - 1 = duplex, 0 = simplex */
4487 const char *subtype
) /* I - Service subtype */
4489 DNSServiceErrorType error
; /* Error from Bonjour */
4490 char make_model
[256],/* Make and model together */
4491 product
[256], /* Product string */
4492 regtype
[256]; /* Bonjour service type */
4496 * Build the TXT record for IPP...
4499 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
4500 snprintf(product
, sizeof(product
), "(%s)", model
);
4502 TXTRecordCreate(&(printer
->ipp_txt
), 1024, NULL
);
4503 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 3, "ipp");
4504 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
4506 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
4509 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
4511 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
4513 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
4515 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
4516 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
4517 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
4519 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
4523 * Create a shared service reference for Bonjour...
4526 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
4527 != kDNSServiceErr_NoError
)
4529 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
4534 * Register the _printer._tcp (LPD) service type with a port number of 0 to
4535 * defend our service name but not actually support LPD...
4538 printer
->printer_ref
= printer
->common_ref
;
4540 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
4541 kDNSServiceFlagsShareConnection
,
4542 0 /* interfaceIndex */, printer
->dnssd_name
,
4543 "_printer._tcp", NULL
/* domain */,
4544 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
4545 NULL
/* txtRecord */,
4546 (DNSServiceRegisterReply
)dnssd_callback
,
4547 printer
)) != kDNSServiceErr_NoError
)
4549 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
4550 printer
->dnssd_name
, error
);
4555 * Then register the _ipp._tcp (IPP) service type with the real port number to
4556 * advertise our IPP printer...
4559 printer
->ipp_ref
= printer
->common_ref
;
4561 if (subtype
&& *subtype
)
4562 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
4564 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
4566 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
4567 kDNSServiceFlagsShareConnection
,
4568 0 /* interfaceIndex */, printer
->dnssd_name
,
4569 regtype
, NULL
/* domain */,
4570 NULL
/* host */, htons(printer
->port
),
4571 TXTRecordGetLength(&(printer
->ipp_txt
)),
4572 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4573 (DNSServiceRegisterReply
)dnssd_callback
,
4574 printer
)) != kDNSServiceErr_NoError
)
4576 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4577 printer
->dnssd_name
, regtype
, error
);
4583 * Then register the _ipps._tcp (IPP) service type with the real port number to
4584 * advertise our IPP printer...
4587 printer
->ipps_ref
= printer
->common_ref
;
4589 if (subtype
&& *subtype
)
4590 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
4592 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
4594 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
4595 kDNSServiceFlagsShareConnection
,
4596 0 /* interfaceIndex */, printer
->dnssd_name
,
4597 regtype
, NULL
/* domain */,
4598 NULL
/* host */, htons(printer
->port
),
4599 TXTRecordGetLength(&(printer
->ipp_txt
)),
4600 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4601 (DNSServiceRegisterReply
)dnssd_callback
,
4602 printer
)) != kDNSServiceErr_NoError
)
4604 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4605 printer
->dnssd_name
, regtype
, error
);
4608 # endif /* HAVE_SSL */
4611 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
4612 * real port number to advertise our IPP printer...
4615 printer
->http_ref
= printer
->common_ref
;
4617 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
4618 kDNSServiceFlagsShareConnection
,
4619 0 /* interfaceIndex */, printer
->dnssd_name
,
4620 "_http._tcp,_printer", NULL
/* domain */,
4621 NULL
/* host */, htons(printer
->port
),
4622 0 /* txtLen */, NULL
, /* txtRecord */
4623 (DNSServiceRegisterReply
)dnssd_callback
,
4624 printer
)) != kDNSServiceErr_NoError
)
4626 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4627 printer
->dnssd_name
, regtype
, error
);
4633 #endif /* HAVE_DNSSD */
4637 * 'respond_http()' - Send a HTTP response.
4640 int /* O - 1 on success, 0 on failure */
4642 _ipp_client_t
*client
, /* I - Client */
4643 http_status_t code
, /* I - HTTP status of response */
4644 const char *content_encoding
, /* I - Content-Encoding of response */
4645 const char *type
, /* I - MIME media type of response */
4646 size_t length
) /* I - Length of response */
4648 char message
[1024]; /* Text message */
4651 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
4653 if (code
== HTTP_STATUS_CONTINUE
)
4656 * 100-continue doesn't send any headers...
4659 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
4663 * Format an error message...
4666 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
)
4668 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
4670 type
= "text/plain";
4671 length
= strlen(message
);
4677 * Send the HTTP response header...
4680 httpClearFields(client
->http
);
4682 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
4683 client
->operation
== HTTP_STATE_OPTIONS
)
4684 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
4688 if (!strcmp(type
, "text/html"))
4689 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
4690 "text/html; charset=utf-8");
4692 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
4694 if (content_encoding
)
4695 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
4698 httpSetLength(client
->http
, length
);
4700 if (httpWriteResponse(client
->http
, code
) < 0)
4704 * Send the response data...
4710 * Send a plain text message.
4713 if (httpPrintf(client
->http
, "%s", message
) < 0)
4716 if (httpWrite2(client
->http
, "", 0) < 0)
4719 else if (client
->response
)
4722 * Send an IPP response...
4725 debug_attributes("Response", client
->response
, 2);
4727 ippSetState(client
->response
, IPP_STATE_IDLE
);
4729 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
4738 * 'respond_ipp()' - Send an IPP response.
4742 respond_ipp(_ipp_client_t
*client
, /* I - Client */
4743 ipp_status_t status
, /* I - status-code */
4744 const char *message
, /* I - printf-style status-message */
4745 ...) /* I - Additional args as needed */
4747 const char *formatted
= NULL
; /* Formatted message */
4750 ippSetStatusCode(client
->response
, status
);
4754 va_list ap
; /* Pointer to additional args */
4755 ipp_attribute_t
*attr
; /* New status-message attribute */
4757 va_start(ap
, message
);
4758 if ((attr
= ippFindAttribute(client
->response
, "status-message",
4759 IPP_TAG_TEXT
)) != NULL
)
4760 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
4762 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
4763 "status-message", NULL
, message
, ap
);
4766 formatted
= ippGetString(attr
, 0, NULL
);
4770 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
4771 ippOpString(client
->operation_id
), ippErrorString(status
),
4774 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
4775 ippOpString(client
->operation_id
), ippErrorString(status
));
4780 * 'respond_unsupported()' - Respond with an unsupported attribute.
4784 respond_unsupported(
4785 _ipp_client_t
*client
, /* I - Client */
4786 ipp_attribute_t
*attr
) /* I - Atribute */
4788 ipp_attribute_t
*temp
; /* Copy of attribute */
4791 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
4792 "Unsupported %s %s%s value.", ippGetName(attr
),
4793 ippGetCount(attr
) > 1 ? "1setOf " : "",
4794 ippTagString(ippGetValueTag(attr
)));
4796 temp
= ippCopyAttribute(client
->response
, attr
, 0);
4797 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
4802 * 'run_printer()' - Run the printer service.
4806 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
4808 int num_fds
; /* Number of file descriptors */
4809 struct pollfd polldata
[3]; /* poll() data */
4810 int timeout
; /* Timeout for poll() */
4811 _ipp_client_t
*client
; /* New client */
4815 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
4818 polldata
[0].fd
= printer
->ipv4
;
4819 polldata
[0].events
= POLLIN
;
4821 polldata
[1].fd
= printer
->ipv6
;
4822 polldata
[1].events
= POLLIN
;
4827 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
4828 polldata
[num_fds
++].events
= POLLIN
;
4829 #endif /* HAVE_DNSSD */
4832 * Loop until we are killed or have a hard error...
4837 if (cupsArrayCount(printer
->jobs
))
4842 if (poll(polldata
, num_fds
, timeout
) < 0 && errno
!= EINTR
)
4844 perror("poll() failed");
4848 if (polldata
[0].revents
& POLLIN
)
4850 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
4852 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4854 perror("Unable to create client thread");
4855 delete_client(client
);
4860 if (polldata
[1].revents
& POLLIN
)
4862 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
4864 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4866 perror("Unable to create client thread");
4867 delete_client(client
);
4873 if (polldata
[2].revents
& POLLIN
)
4874 DNSServiceProcessResult(printer
->common_ref
);
4875 #endif /* HAVE_DNSSD */
4878 * Clean out old jobs...
4881 clean_jobs(printer
);
4887 * 'usage()' - Show program usage.
4891 usage(int status
) /* O - Exit status */
4895 puts(CUPS_SVERSION
" - Copyright 2010-2012 by Apple Inc. All rights "
4900 puts("Usage: ippserver [options] \"name\"");
4903 puts("-2 Supports 2-sided printing (default=1-sided)");
4904 puts("-M manufacturer Manufacturer name (default=Test)");
4905 printf("-d spool-directory Spool directory "
4906 "(default=/tmp/ippserver.%d)\n", (int)getpid());
4907 puts("-f type/subtype[,...] List of supported types "
4908 "(default=application/pdf,image/jpeg)");
4909 puts("-h Show program help");
4910 puts("-i iconfile.png PNG icon file (default=printer.png)");
4911 puts("-l location Location of printer (default=empty string)");
4912 puts("-m model Model name (default=Printer)");
4913 puts("-n hostname Hostname for printer");
4914 puts("-p port Port number (default=auto)");
4915 puts("-r subtype Bonjour service subtype (default=_print)");
4916 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
4917 puts("-v[vvv] Be (very) verbose");
4924 * 'valid_doc_attributes()' - Determine whether the document attributes are
4927 * When one or more document attributes are invalid, this function adds a
4928 * suitable response and attributes to the unsupported group.
4931 static int /* O - 1 if valid, 0 if not */
4932 valid_doc_attributes(
4933 _ipp_client_t
*client
) /* I - Client */
4935 int valid
= 1; /* Valid attributes? */
4936 ipp_op_t op
= ippGetOperation(client
->request
);
4938 const char *op_name
= ippOpString(op
);
4939 /* IPP operation name */
4940 ipp_attribute_t
*attr
, /* Current attribute */
4941 *supported
; /* xxx-supported attribute */
4942 const char *compression
= NULL
,
4943 /* compression value */
4944 *format
= NULL
; /* document-format value */
4948 * Check operation attributes...
4951 if ((attr
= ippFindAttribute(client
->request
, "compression",
4952 IPP_TAG_ZERO
)) != NULL
)
4955 * If compression is specified, only accept a supported value in a Print-Job
4956 * or Send-Document request...
4959 compression
= ippGetString(attr
, 0, NULL
);
4960 supported
= ippFindAttribute(client
->printer
->attrs
,
4961 "compression-supported", IPP_TAG_KEYWORD
);
4963 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
4964 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
4965 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
) ||
4966 !ippContainsString(supported
, compression
))
4968 respond_unsupported(client
, attr
);
4973 fprintf(stderr
, "%s %s compression=\"%s\"\n",
4974 client
->hostname
, op_name
, compression
);
4976 if (strcmp(compression
, "none"))
4977 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
4982 * Is it a format we support?
4985 if ((attr
= ippFindAttribute(client
->request
, "document-format",
4986 IPP_TAG_ZERO
)) != NULL
)
4988 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
4989 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
4991 respond_unsupported(client
, attr
);
4996 format
= ippGetString(attr
, 0, NULL
);
4998 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
4999 client
->hostname
, op_name
, format
);
5004 format
= "application/octet-stream";
5005 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
5006 "document-format", NULL
, format
);
5009 if (!strcmp(format
, "application/octet-stream") &&
5010 (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
||
5011 ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
5014 * Auto-type the file using the first 4 bytes of the file...
5017 unsigned char header
[4]; /* First 4 bytes of file */
5019 memset(header
, 0, sizeof(header
));
5020 httpPeek(client
->http
, (char *)header
, sizeof(header
));
5022 if (!memcmp(header
, "%PDF", 4))
5023 format
= "application/pdf";
5024 else if (!memcmp(header
, "%!", 2))
5025 format
= "application/postscript";
5026 else if (!memcmp(header
, "\377\330\377", 3) &&
5027 header
[3] >= 0xe0 && header
[3] <= 0xef)
5028 format
= "image/jpeg";
5029 else if (!memcmp(header
, "\211PNG", 4))
5030 format
= "image/png";
5033 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
5034 client
->hostname
, op_name
, format
);
5037 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
5038 "document-format", NULL
, format
);
5040 ippSetString(client
->request
, &attr
, 0, format
);
5043 if (op
!= IPP_OP_CREATE_JOB
&&
5044 (supported
= ippFindAttribute(client
->printer
->attrs
,
5045 "document-format-supported",
5046 IPP_TAG_MIMETYPE
)) != NULL
&&
5047 !ippContainsString(supported
, format
))
5049 respond_unsupported(client
, attr
);
5058 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
5060 * When one or more job attributes are invalid, this function adds a suitable
5061 * response and attributes to the unsupported group.
5064 static int /* O - 1 if valid, 0 if not */
5065 valid_job_attributes(
5066 _ipp_client_t
*client
) /* I - Client */
5068 int i
, /* Looping var */
5069 valid
= 1; /* Valid attributes? */
5070 ipp_attribute_t
*attr
, /* Current attribute */
5071 *supported
; /* xxx-supported attribute */
5075 * Check operation attributes...
5078 valid
= valid_doc_attributes(client
);
5081 * Check the various job template attributes...
5084 if ((attr
= ippFindAttribute(client
->request
, "copies",
5085 IPP_TAG_ZERO
)) != NULL
)
5087 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5088 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
5090 respond_unsupported(client
, attr
);
5095 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity",
5096 IPP_TAG_ZERO
)) != NULL
)
5098 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
5100 respond_unsupported(client
, attr
);
5105 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until",
5106 IPP_TAG_ZERO
)) != NULL
)
5108 if (ippGetCount(attr
) != 1 ||
5109 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5110 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5111 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5112 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
5114 respond_unsupported(client
, attr
);
5119 if ((attr
= ippFindAttribute(client
->request
, "job-name",
5120 IPP_TAG_ZERO
)) != NULL
)
5122 if (ippGetCount(attr
) != 1 ||
5123 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5124 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
5126 respond_unsupported(client
, attr
);
5131 if ((attr
= ippFindAttribute(client
->request
, "job-priority",
5132 IPP_TAG_ZERO
)) != NULL
)
5134 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5135 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
5137 respond_unsupported(client
, attr
);
5142 if ((attr
= ippFindAttribute(client
->request
, "job-sheets",
5143 IPP_TAG_ZERO
)) != NULL
)
5145 if (ippGetCount(attr
) != 1 ||
5146 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5147 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5148 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5149 strcmp(ippGetString(attr
, 0, NULL
), "none"))
5151 respond_unsupported(client
, attr
);
5156 if ((attr
= ippFindAttribute(client
->request
, "media",
5157 IPP_TAG_ZERO
)) != NULL
)
5159 if (ippGetCount(attr
) != 1 ||
5160 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5161 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5162 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
5164 respond_unsupported(client
, attr
);
5170 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
5172 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
5175 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
5177 respond_unsupported(client
, attr
);
5183 if ((attr
= ippFindAttribute(client
->request
, "media-col",
5184 IPP_TAG_ZERO
)) != NULL
)
5186 if (ippGetCount(attr
) != 1 ||
5187 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
5189 respond_unsupported(client
, attr
);
5192 /* TODO: check for valid media-col */
5195 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling",
5196 IPP_TAG_ZERO
)) != NULL
)
5198 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5199 (strcmp(ippGetString(attr
, 0, NULL
),
5200 "separate-documents-uncollated-copies") &&
5201 strcmp(ippGetString(attr
, 0, NULL
),
5202 "separate-documents-collated-copies")))
5204 respond_unsupported(client
, attr
);
5209 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested",
5210 IPP_TAG_ZERO
)) != NULL
)
5212 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
5213 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
5214 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
5216 respond_unsupported(client
, attr
);
5221 if ((attr
= ippFindAttribute(client
->request
, "page-ranges",
5222 IPP_TAG_ZERO
)) != NULL
)
5224 respond_unsupported(client
, attr
);
5228 if ((attr
= ippFindAttribute(client
->request
, "print-quality",
5229 IPP_TAG_ZERO
)) != NULL
)
5231 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
5232 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
5233 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
5235 respond_unsupported(client
, attr
);
5240 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution",
5241 IPP_TAG_ZERO
)) != NULL
)
5243 respond_unsupported(client
, attr
);
5247 if ((attr
= ippFindAttribute(client
->request
, "sides",
5248 IPP_TAG_ZERO
)) != NULL
)
5250 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
5252 respond_unsupported(client
, attr
);
5256 if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides",
5257 IPP_TAG_KEYWORD
)) != NULL
)
5259 int count
= ippGetCount(supported
);
5260 const char *sides
= ippGetString(attr
, 0, NULL
);
5262 for (i
= 0; i
< count
; i
++)
5263 if (!strcmp(sides
, ippGetString(supported
, i
, NULL
)))
5268 respond_unsupported(client
, attr
);
5274 respond_unsupported(client
, attr
);