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 * Include necessary headers...
79 #include <cups/cups-private.h>
82 #endif /* HAVE_DNSSD */
85 #ifdef HAVE_SYS_MOUNT_H
86 # include <sys/mount.h>
87 #endif /* HAVE_SYS_MOUNT_H */
88 #ifdef HAVE_SYS_STATFS_H
89 # include <sys/statfs.h>
90 #endif /* HAVE_SYS_STATFS_H */
91 #ifdef HAVE_SYS_STATVFS_H
92 # include <sys/statvfs.h>
93 #endif /* HAVE_SYS_STATVFS_H */
96 #endif /* HAVE_SYS_VFS_H */
103 enum _ipp_preasons_e
/* printer-state-reasons bit values */
105 _IPP_PRINTER_NONE
= 0x0000, /* none */
106 _IPP_PRINTER_OTHER
= 0x0001, /* other */
107 _IPP_PRINTER_COVER_OPEN
= 0x0002, /* cover-open */
108 _IPP_PRINTER_INPUT_TRAY_MISSING
= 0x0004,
109 /* input-tray-missing */
110 _IPP_PRINTER_MARKER_SUPPLY_EMPTY
= 0x0008,
111 /* marker-supply-empty */
112 _IPP_PRINTER_MARKER_SUPPLY_LOW
= 0x0010,
113 /* marker-suply-low */
114 _IPP_PRINTER_MARKER_WASTE_ALMOST_FULL
= 0x0020,
115 /* marker-waste-almost-full */
116 _IPP_PRINTER_MARKER_WASTE_FULL
= 0x0040,
117 /* marker-waste-full */
118 _IPP_PRINTER_MEDIA_EMPTY
= 0x0080, /* media-empty */
119 _IPP_PRINTER_MEDIA_JAM
= 0x0100, /* media-jam */
120 _IPP_PRINTER_MEDIA_LOW
= 0x0200, /* media-low */
121 _IPP_PRINTER_MEDIA_NEEDED
= 0x0400, /* media-needed */
122 _IPP_PRINTER_MOVING_TO_PAUSED
= 0x0800,
123 /* moving-to-paused */
124 _IPP_PRINTER_PAUSED
= 0x1000, /* paused */
125 _IPP_PRINTER_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
126 _IPP_PRINTER_TONER_EMPTY
= 0x4000, /* toner-empty */
127 _IPP_PRINTER_TONER_LOW
= 0x8000 /* toner-low */
129 typedef unsigned int _ipp_preasons_t
; /* Bitfield for printer-state-reasons */
131 typedef enum _ipp_media_class_e
133 _IPP_GENERAL
, /* General-purpose size */
134 _IPP_PHOTO_ONLY
, /* Photo-only size */
135 _IPP_ENV_ONLY
/* Envelope-only size */
136 } _ipp_media_class_t
;
138 static const char * const media_supported
[] =
139 { /* media-supported values */
140 "iso_a4_210x297mm", /* A4 */
141 "iso_a5_148x210mm", /* A5 */
142 "iso_a6_105x148mm", /* A6 */
143 "iso_dl_110x220mm", /* DL */
144 "na_legal_8.5x14in", /* Legal */
145 "na_letter_8.5x11in", /* Letter */
146 "na_number-10_4.125x9.5in", /* #10 */
147 "na_index-3x5_3x5in", /* 3x5 */
148 "oe_photo-l_3.5x5in", /* L */
149 "na_index-4x6_4x6in", /* 4x6 */
150 "na_5x7_5x7in" /* 5x7 aka 2L */
152 static const int media_col_sizes
[][3] =
153 { /* media-col-database sizes */
154 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
155 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
156 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
157 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
158 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
159 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
160 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
161 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
162 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
163 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
164 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
166 static const char * const media_type_supported
[] =
167 /* media-type-supported values */
174 "photographic-glossy",
175 "photographic-high-gloss",
176 "photographic-matte",
177 "photographic-satin",
178 "photographic-semi-gloss",
180 "stationery-letterhead",
189 typedef struct _ipp_job_s _ipp_job_t
;
191 typedef struct _ipp_printer_s
/**** Printer data ****/
193 int ipv4
, /* IPv4 listener */
194 ipv6
; /* IPv6 listener */
196 DNSServiceRef common_ref
, /* Shared service connection */
197 ipp_ref
, /* Bonjour IPP service */
198 http_ref
, /* Bonjour HTTP service */
199 printer_ref
; /* Bonjour LPD service */
200 TXTRecordRef ipp_txt
; /* Bonjour IPP TXT record */
201 char *dnssd_name
; /* printer-dnssd-name */
202 #endif /* HAVE_DNSSD */
203 char *name
, /* printer-name */
204 *icon
, /* Icon filename */
205 *directory
, /* Spool directory */
206 *hostname
, /* Hostname */
207 *uri
; /* printer-uri-supported */
209 size_t urilen
; /* Length of printer URI */
210 ipp_t
*attrs
; /* Static attributes */
211 ipp_pstate_t state
; /* printer-state value */
212 _ipp_preasons_t state_reasons
; /* printer-state-reasons values */
213 cups_array_t
*jobs
; /* Jobs */
214 _ipp_job_t
*active_job
; /* Current active/pending job */
215 int next_job_id
; /* Next job-id value */
216 _cups_rwlock_t rwlock
; /* Printer lock */
219 struct _ipp_job_s
/**** Job data ****/
222 char *name
, /* job-name */
223 *username
, /* job-originating-user-name */
224 *format
; /* document-format */
225 ipp_jstate_t state
; /* job-state value */
226 time_t processing
, /* time-at-processing value */
227 completed
; /* time-at-completed value */
228 ipp_t
*attrs
; /* Static attributes */
229 int cancel
; /* Non-zero when job canceled */
230 char *filename
; /* Print file name */
231 int fd
; /* Print file descriptor */
232 _ipp_printer_t
*printer
; /* Printer */
235 typedef struct _ipp_client_s
/**** Client data ****/
237 http_t http
; /* HTTP connection */
238 ipp_t
*request
, /* IPP request */
239 *response
; /* IPP response */
240 time_t start
; /* Request start time */
241 http_state_t operation
; /* Request operation */
242 ipp_op_t operation_id
; /* IPP operation-id */
243 char uri
[1024]; /* Request URI */
244 http_addr_t addr
; /* Client address */
245 _ipp_printer_t
*printer
; /* Printer */
246 _ipp_job_t
*job
; /* Current job, if any */
254 static void clean_jobs(_ipp_printer_t
*printer
);
255 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
256 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
257 ipp_tag_t group_tag
, int quickcopy
);
258 static void copy_job_attributes(_ipp_client_t
*client
,
259 _ipp_job_t
*job
, cups_array_t
*ra
);
260 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
261 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
262 static int create_listener(int family
, int *port
);
263 static ipp_t
*create_media_col(const char *media
, const char *type
,
264 int width
, int length
, int margins
);
265 static ipp_t
*create_media_size(int width
, int length
);
266 static _ipp_printer_t
*create_printer(const char *servername
,
267 const char *name
, const char *location
,
268 const char *make
, const char *model
,
270 const char *docformats
, int ppm
,
271 int ppm_color
, int duplex
, int port
,
274 #endif /* HAVE_DNSSD */
275 const char *directory
);
276 static cups_array_t
*create_requested_array(_ipp_client_t
*client
);
277 static void debug_attributes(const char *title
, ipp_t
*ipp
,
279 static void delete_client(_ipp_client_t
*client
);
280 static void delete_job(_ipp_job_t
*job
);
281 static void delete_printer(_ipp_printer_t
*printer
);
283 static void dnssd_callback(DNSServiceRef sdRef
,
284 DNSServiceFlags flags
,
285 DNSServiceErrorType errorCode
,
289 _ipp_printer_t
*printer
);
290 #endif /* HAVE_DNSSD */
291 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
292 static void html_escape(_ipp_client_t
*client
, const char *s
,
294 static void html_printf(_ipp_client_t
*client
, const char *format
,
295 ...) __attribute__((__format__(__printf__
,
297 static void ipp_cancel_job(_ipp_client_t
*client
);
298 static void ipp_create_job(_ipp_client_t
*client
);
299 static void ipp_get_job_attributes(_ipp_client_t
*client
);
300 static void ipp_get_jobs(_ipp_client_t
*client
);
301 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
302 static void ipp_print_job(_ipp_client_t
*client
);
303 static void ipp_print_uri(_ipp_client_t
*client
);
304 static void ipp_send_document(_ipp_client_t
*client
);
305 static void ipp_send_uri(_ipp_client_t
*client
);
306 static void ipp_validate_job(_ipp_client_t
*client
);
307 static void *process_client(_ipp_client_t
*client
);
308 static int process_http(_ipp_client_t
*client
);
309 static int process_ipp(_ipp_client_t
*client
);
310 static void *process_job(_ipp_job_t
*job
);
312 static int register_printer(_ipp_printer_t
*printer
,
313 const char *location
, const char *make
,
314 const char *model
, const char *formats
,
315 const char *adminurl
, int color
,
316 int duplex
, const char *regtype
);
317 #endif /* HAVE_DNSSD */
318 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
319 const char *type
, size_t length
);
320 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
321 const char *message
, ...)
322 __attribute__ ((__format__ (__printf__
, 3, 4)));
323 static void respond_unsupported(_ipp_client_t
*client
,
324 ipp_attribute_t
*attr
);
325 static void run_printer(_ipp_printer_t
*printer
);
326 static void usage(int status
) __attribute__((noreturn
));
327 static int valid_doc_attributes(_ipp_client_t
*client
);
328 static int valid_job_attributes(_ipp_client_t
*client
);
335 static int KeepFiles
= 0,
340 * 'main()' - Main entry to the sample server.
343 int /* O - Exit status */
344 main(int argc
, /* I - Number of command-line args */
345 char *argv
[]) /* I - Command-line arguments */
347 int i
; /* Looping var */
348 const char *opt
, /* Current option character */
349 *servername
= NULL
, /* Server host name */
350 *name
= NULL
, /* Printer name */
351 *location
= "", /* Location of printer */
352 *make
= "Test", /* Manufacturer */
353 *model
= "Printer", /* Model */
354 *icon
= "printer.png", /* Icon file */
355 *formats
= "application/pdf,image/jpeg";
356 /* Supported formats */
358 const char *regtype
= "_ipp._tcp"; /* Bonjour service type */
359 #endif /* HAVE_DNSSD */
360 int port
= 8631, /* Port number (0 = auto) TODO: FIX */
361 duplex
= 0, /* Duplex mode */
362 ppm
= 10, /* Pages per minute for mono */
363 ppm_color
= 0; /* Pages per minute for color */
364 char directory
[1024] = ""; /* Spool directory */
365 _ipp_printer_t
*printer
; /* Printer object */
369 * Parse command-line arguments...
372 for (i
= 1; i
< argc
; i
++)
373 if (argv
[i
][0] == '-')
375 for (opt
= argv
[i
] + 1; *opt
; opt
++)
378 case '2' : /* -2 (enable 2-sided printing) */
382 case 'M' : /* -M manufacturer */
389 case 'd' : /* -d spool-directory */
393 strlcpy(directory
, argv
[i
], sizeof(directory
));
396 case 'f' : /* -f type/subtype[,...] */
403 case 'h' : /* -h (show help) */
407 case 'i' : /* -i icon.png */
414 case 'k' : /* -k (keep files) */
418 case 'l' : /* -l location */
425 case 'm' : /* -m model */
432 case 'n' : /* -n hostname */
436 servername
= argv
[i
];
439 case 'p' : /* -p port */
441 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
443 port
= atoi(argv
[i
]);
447 case 'r' : /* -r regtype */
453 #endif /* HAVE_DNSSD */
455 case 's' : /* -s speed[,color-speed] */
459 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
463 case 'v' : /* -v (be verbose) */
467 default : /* Unknown */
468 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
479 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
487 * Apply defaults as needed...
492 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
494 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
496 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
497 directory
, strerror(errno
));
502 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
506 * Create the printer...
509 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
510 formats
, ppm
, ppm_color
, duplex
, port
,
513 #endif /* HAVE_DNSSD */
518 * Run the print service...
521 run_printer(printer
);
524 * Destroy the printer and exit...
527 delete_printer(printer
);
534 * 'clean_jobs()' - Clean out old (completed) jobs.
538 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
540 _ipp_job_t
*job
; /* Current job */
541 time_t cleantime
; /* Clean time */
544 if (cupsArrayCount(printer
->jobs
) == 0)
547 cleantime
= time(NULL
) - 60;
549 _cupsRWLockWrite(&(printer
->rwlock
));
550 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
552 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
553 if (job
->completed
&& job
->completed
< cleantime
)
555 cupsArrayRemove(printer
->jobs
, job
);
560 _cupsRWUnlock(&(printer
->rwlock
));
565 * 'compare_jobs()' - Compare two jobs.
568 static int /* O - Result of comparison */
569 compare_jobs(_ipp_job_t
*a
, /* I - First job */
570 _ipp_job_t
*b
) /* I - Second job */
572 return (b
->id
- a
->id
);
577 * 'copy_attributes()' - Copy attributes from one request to another.
581 copy_attributes(ipp_t
*to
, /* I - Destination request */
582 ipp_t
*from
, /* I - Source request */
583 cups_array_t
*ra
, /* I - Requested attributes */
584 ipp_tag_t group_tag
, /* I - Group to copy */
585 int quickcopy
) /* I - Do a quick copy? */
587 ipp_attribute_t
*fromattr
; /* Source attribute */
593 for (fromattr
= from
->attrs
; fromattr
; fromattr
= fromattr
->next
)
596 * Filter attributes as needed...
599 if ((group_tag
!= IPP_TAG_ZERO
&& fromattr
->group_tag
!= group_tag
&&
600 fromattr
->group_tag
!= IPP_TAG_ZERO
) || !fromattr
->name
)
603 if (!ra
|| cupsArrayFind(ra
, fromattr
->name
))
604 ippCopyAttribute(to
, fromattr
, quickcopy
);
610 * 'copy_job_attrs()' - Copy job attributes to the response.
615 _ipp_client_t
*client
, /* I - Client */
616 _ipp_job_t
*job
, /* I - Job */
617 cups_array_t
*ra
) /* I - requested-attributes */
619 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
621 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
622 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
623 "job-printer-up-time", (int)time(NULL
));
625 if (!ra
|| cupsArrayFind(ra
, "job-state"))
626 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
627 "job-state", job
->state
);
629 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
633 case IPP_JOB_PENDING
:
634 ippAddString(client
->response
, IPP_TAG_JOB
,
635 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
641 ippAddString(client
->response
, IPP_TAG_JOB
,
642 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
643 NULL
, "job-incoming");
644 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
645 ippAddString(client
->response
, IPP_TAG_JOB
,
646 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
647 NULL
, "job-hold-until-specified");
649 ippAddString(client
->response
, IPP_TAG_JOB
,
650 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
651 NULL
, "job-data-insufficient");
654 case IPP_JOB_PROCESSING
:
656 ippAddString(client
->response
, IPP_TAG_JOB
,
657 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
658 NULL
, "processing-to-stop-point");
660 ippAddString(client
->response
, IPP_TAG_JOB
,
661 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
662 NULL
, "job-printing");
665 case IPP_JOB_STOPPED
:
666 ippAddString(client
->response
, IPP_TAG_JOB
,
667 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
668 NULL
, "job-stopped");
671 case IPP_JOB_CANCELED
:
672 ippAddString(client
->response
, IPP_TAG_JOB
,
673 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
674 NULL
, "job-canceled-by-user");
677 case IPP_JOB_ABORTED
:
678 ippAddString(client
->response
, IPP_TAG_JOB
,
679 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
680 NULL
, "aborted-by-system");
683 case IPP_JOB_COMPLETED
:
684 ippAddString(client
->response
, IPP_TAG_JOB
,
685 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
686 NULL
, "job-completed-successfully");
691 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
692 ippAddInteger(client
->response
, IPP_TAG_JOB
,
693 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
694 "time-at-completed", job
->completed
);
696 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
697 ippAddInteger(client
->response
, IPP_TAG_JOB
,
698 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
699 "time-at-processing", job
->processing
);
704 * 'create_client()' - Accept a new network connection and create a client
708 static _ipp_client_t
* /* O - Client */
709 create_client(_ipp_printer_t
*printer
, /* I - Printer */
710 int sock
) /* I - Listen socket */
712 _ipp_client_t
*client
; /* Client */
713 int val
; /* Parameter value */
714 socklen_t addrlen
; /* Length of address */
717 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
719 perror("Unable to allocate memory for client");
723 client
->printer
= printer
;
724 client
->http
.activity
= time(NULL
);
725 client
->http
.hostaddr
= &(client
->addr
);
726 client
->http
.blocking
= 1;
727 client
->http
.wait_value
= 60000;
730 * Accept the client and get the remote address...
733 addrlen
= sizeof(http_addr_t
);
735 if ((client
->http
.fd
= accept(sock
, (struct sockaddr
*)&(client
->addr
),
738 perror("Unable to accept client connection");
745 httpAddrString(&(client
->addr
), client
->http
.hostname
,
746 sizeof(client
->http
.hostname
));
749 fprintf(stderr
, "Accepted connection from %s (%s)\n", client
->http
.hostname
,
750 client
->http
.hostaddr
->addr
.sa_family
== AF_INET
? "IPv4" : "IPv6");
753 * Using TCP_NODELAY improves responsiveness, especially on systems
754 * with a slow loopback interface. Since we write large buffers
755 * when sending print files and requests, there shouldn't be any
756 * performance penalty for this...
760 setsockopt(client
->http
.fd
, IPPROTO_TCP
, TCP_NODELAY
, (char *)&val
,
768 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
772 static _ipp_job_t
* /* O - Job */
773 create_job(_ipp_client_t
*client
) /* I - Client */
775 _ipp_job_t
*job
; /* Job */
776 ipp_attribute_t
*attr
; /* Job attribute */
777 char uri
[1024]; /* job-uri value */
780 _cupsRWLockWrite(&(client
->printer
->rwlock
));
781 if (client
->printer
->active_job
&&
782 client
->printer
->active_job
->state
< IPP_JOB_CANCELED
)
785 * Only accept a single job at a time...
788 _cupsRWLockWrite(&(client
->printer
->rwlock
));
793 * Allocate and initialize the job object...
796 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
798 perror("Unable to allocate memory for job");
802 job
->printer
= client
->printer
;
803 job
->attrs
= client
->request
;
804 job
->state
= IPP_JOB_HELD
;
806 client
->request
= NULL
;
809 * Set all but the first two attributes to the job attributes group...
812 for (attr
= job
->attrs
->attrs
->next
->next
; attr
; attr
= attr
->next
)
813 attr
->group_tag
= IPP_TAG_JOB
;
816 * Get the requesting-user-name, document format, and priority...
819 if ((attr
= ippFindAttribute(job
->attrs
, "requesting-user-name",
820 IPP_TAG_NAME
)) != NULL
)
822 _cupsStrFree(attr
->name
);
823 attr
->name
= _cupsStrAlloc("job-originating-user-name");
826 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
| IPP_TAG_COPY
,
827 "job-originating-user-name", NULL
, "anonymous");
830 job
->username
= attr
->values
[0].string
.text
;
832 job
->username
= "anonymous";
834 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
835 IPP_TAG_MIMETYPE
)) != NULL
)
836 job
->format
= attr
->values
[0].string
.text
;
838 job
->format
= "application/octet-stream";
841 * Add job description attributes and add to the jobs array...
844 job
->id
= client
->printer
->next_job_id
++;
846 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
848 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
849 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
850 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
851 client
->printer
->uri
);
852 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
855 cupsArrayAdd(client
->printer
->jobs
, job
);
856 client
->printer
->active_job
= job
;
858 _cupsRWUnlock(&(client
->printer
->rwlock
));
865 * 'create_listener()' - Create a listener socket.
868 static int /* O - Listener socket or -1 on error */
869 create_listener(int family
, /* I - Address family */
870 int *port
) /* IO - Port number */
872 int sock
, /* Listener socket */
873 val
; /* Socket value */
874 http_addr_t address
; /* Listen address */
875 socklen_t addrlen
; /* Length of listen address */
878 if ((sock
= socket(family
, SOCK_STREAM
, 0)) < 0)
882 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, &val
, sizeof(val
));
885 if (family
== AF_INET6
)
886 setsockopt(sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, &val
, sizeof(val
));
887 #endif /* IPV6_V6ONLY */
892 * Get the auto-assigned port number for the IPv4 socket...
895 /* TODO: This code does not appear to work - port is always 0... */
896 addrlen
= sizeof(address
);
897 if (getsockname(sock
, (struct sockaddr
*)&address
, &addrlen
))
899 perror("getsockname() failed");
903 *port
= _httpAddrPort(&address
);
905 fprintf(stderr
, "Listening on port %d.\n", *port
);
908 memset(&address
, 0, sizeof(address
));
909 address
.addr
.sa_family
= family
;
910 _httpAddrSetPort(&address
, *port
);
912 if (bind(sock
, (struct sockaddr
*)&address
, httpAddrLength(&address
)))
929 * 'create_media_col()' - Create a media-col value.
932 static ipp_t
* /* O - media-col collection */
933 create_media_col(const char *media
, /* I - Media name */
934 const char *type
, /* I - Nedua type */
935 int width
, /* I - x-dimension in 2540ths */
936 int length
, /* I - y-dimension in 2540ths */
937 int margins
) /* I - Value for margins */
939 ipp_t
*media_col
= ippNew(), /* media-col value */
940 *media_size
= create_media_size(width
, length
);
941 /* media-size value */
942 char media_key
[256]; /* media-key value */
945 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, type
,
946 margins
== 0 ? "_borderless" : "");
948 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
950 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
951 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
952 "media-bottom-margin", margins
);
953 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
954 "media-left-margin", margins
);
955 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
956 "media-right-margin", margins
);
957 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
958 "media-top-margin", margins
);
959 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type",
962 ippDelete(media_size
);
969 * 'create_media_size()' - Create a media-size value.
972 static ipp_t
* /* O - media-col collection */
973 create_media_size(int width
, /* I - x-dimension in 2540ths */
974 int length
) /* I - y-dimension in 2540ths */
976 ipp_t
*media_size
= ippNew(); /* media-size value */
979 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
981 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
989 * 'create_printer()' - Create, register, and listen for connections to a
993 static _ipp_printer_t
* /* O - Printer */
994 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
995 const char *name
, /* I - printer-name */
996 const char *location
, /* I - printer-location */
997 const char *make
, /* I - printer-make-and-model */
998 const char *model
, /* I - printer-make-and-model */
999 const char *icon
, /* I - printer-icons */
1000 const char *docformats
, /* I - document-format-supported */
1001 int ppm
, /* I - Pages per minute in grayscale */
1002 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1003 int duplex
, /* I - 1 = duplex, 0 = simplex */
1004 int port
, /* I - Port for listeners or 0 for auto */
1006 const char *regtype
, /* I - Bonjour service type */
1007 #endif /* HAVE_DNSSD */
1008 const char *directory
) /* I - Spool directory */
1010 int i
, j
; /* Looping vars */
1011 _ipp_printer_t
*printer
; /* Printer */
1012 char hostname
[256], /* Hostname */
1013 uri
[1024], /* Printer URI */
1014 icons
[1024], /* printer-icons URI */
1015 adminurl
[1024], /* printer-more-info URI */
1016 device_id
[1024],/* printer-device-id */
1017 make_model
[128];/* printer-make-and-model */
1018 int num_formats
; /* Number of document-format-supported values */
1019 char *defformat
, /* document-format-default value */
1020 *formats
[100], /* document-format-supported values */
1021 *ptr
; /* Pointer into string */
1022 const char *prefix
; /* Prefix string */
1023 int num_database
; /* Number of database values */
1024 ipp_attribute_t
*media_col_database
,
1025 /* media-col-database value */
1026 *media_size_supported
;
1027 /* media-size-supported value */
1028 ipp_t
*media_col_default
;
1029 /* media-col-default value */
1030 _ipp_value_t
*media_col_value
;
1031 /* Current media-col-database value */
1032 int k_supported
; /* Maximum file size supported */
1034 struct statvfs spoolinfo
; /* FS info for spool directory */
1035 double spoolsize
; /* FS size */
1036 #elif defined(HAVE_STATFS)
1037 struct statfs spoolinfo
; /* FS info for spool directory */
1038 double spoolsize
; /* FS size */
1039 #endif /* HAVE_STATVFS */
1040 static const int orients
[4] = /* orientation-requested-supported values */
1044 IPP_REVERSE_LANDSCAPE
,
1045 IPP_REVERSE_PORTRAIT
1047 static const char * const versions
[] =/* ipp-versions-supported values */
1053 static const int ops
[] = /* operations-supported values */
1062 IPP_GET_JOB_ATTRIBUTES
,
1064 IPP_GET_PRINTER_ATTRIBUTES
1066 static const char * const charsets
[] =/* charset-supported values */
1071 static const char * const job_creation
[] =
1072 { /* job-creation-attributes-supported values */
1074 "ipp-attribute-fidelity",
1079 "multiple-document-handling",
1080 "orientation-requested",
1084 static const char * const media_col_supported
[] =
1085 { /* media-col-supported values */
1086 "media-bottom-margin",
1087 "media-left-margin",
1088 "media-right-margin",
1093 static const int media_xxx_margin_supported
[] =
1094 { /* media-xxx-margin-supported values */
1098 static const char * const multiple_document_handling
[] =
1099 { /* multiple-document-handling-supported values */
1100 "separate-documents-uncollated-copies",
1101 "separate-documents-collated-copies"
1103 static const int print_quality_supported
[] =
1104 { /* print-quality-supported values */
1109 static const char * const reference_uri_schemes_supported
[] =
1110 { /* reference-uri-schemes-supported */
1116 #endif /* HAVE_SSL */
1118 static const char * const sides_supported
[] =
1119 { /* sides-supported values */
1121 "two-sided-long-edge",
1122 "two-sided-short-edge"
1124 static const char * const which_jobs
[] =
1125 { /* which-jobs-supported values */
1134 "processing-stopped"
1139 * Allocate memory for the printer...
1142 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1144 perror("Unable to allocate memory for printer");
1150 printer
->name
= _cupsStrAlloc(name
);
1152 printer
->dnssd_name
= _cupsStrRetain(printer
->name
);
1153 #endif /* HAVE_DNSSD */
1154 printer
->directory
= _cupsStrAlloc(directory
);
1155 printer
->hostname
= _cupsStrAlloc(servername
? servername
:
1156 httpGetHostname(NULL
, hostname
,
1158 printer
->port
= port
;
1159 printer
->state
= IPP_PRINTER_IDLE
;
1160 printer
->state_reasons
= _IPP_PRINTER_NONE
;
1161 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1162 printer
->next_job_id
= 1;
1164 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1165 printer
->hostname
, printer
->port
, "/ipp");
1166 printer
->uri
= _cupsStrAlloc(uri
);
1167 printer
->urilen
= strlen(uri
);
1169 _cupsRWInit(&(printer
->rwlock
));
1172 * Create the listener sockets...
1175 if ((printer
->ipv4
= create_listener(AF_INET
, &(printer
->port
))) < 0)
1177 perror("Unable to create IPv4 listener");
1181 if ((printer
->ipv6
= create_listener(AF_INET6
, &(printer
->port
))) < 0)
1183 perror("Unable to create IPv6 listener");
1188 * Prepare values for the printer attributes...
1191 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1192 printer
->hostname
, printer
->port
, "/icon.png");
1193 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
,
1194 printer
->hostname
, printer
->port
, "/");
1198 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1199 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1202 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1205 formats
[0] = strdup(docformats
);
1206 defformat
= formats
[0];
1207 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1210 formats
[num_formats
++] = ptr
;
1212 if (!_cups_strcasecmp(ptr
, "application/octet-stream"))
1216 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1217 ptr
= device_id
+ strlen(device_id
);
1219 for (i
= 0; i
< num_formats
; i
++)
1221 if (!_cups_strcasecmp(formats
[i
], "application/pdf"))
1222 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPDF", prefix
);
1223 else if (!_cups_strcasecmp(formats
[i
], "application/postscript"))
1224 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPS", prefix
);
1225 else if (!_cups_strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1226 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPCL", prefix
);
1227 else if (!_cups_strcasecmp(formats
[i
], "image/jpeg"))
1228 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sJPEG", prefix
);
1229 else if (!_cups_strcasecmp(formats
[i
], "image/png"))
1230 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPNG", prefix
);
1231 else if (_cups_strcasecmp(formats
[i
], "application/octet-stream"))
1232 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%s%s", prefix
,
1238 strlcat(device_id
, ";", sizeof(device_id
));
1241 * Get the maximum spool size based on the size of the filesystem used for
1242 * the spool directory. If the host OS doesn't support the statfs call
1243 * or the filesystem is larger than 2TiB, always report INT_MAX.
1247 if (statvfs(printer
->directory
, &spoolinfo
))
1248 k_supported
= INT_MAX
;
1249 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1250 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1251 k_supported
= INT_MAX
;
1253 k_supported
= (int)spoolsize
;
1255 #elif defined(HAVE_STATFS)
1256 if (statfs(printer
->directory
, &spoolinfo
))
1257 k_supported
= INT_MAX
;
1258 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1259 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1260 k_supported
= INT_MAX
;
1262 k_supported
= (int)spoolsize
;
1265 k_supported
= INT_MAX
;
1266 #endif /* HAVE_STATVFS */
1269 * Create the printer attributes. This list of attributes is sorted to improve
1270 * performance when the client provides a requested-attributes attribute...
1273 printer
->attrs
= ippNew();
1275 /* charset-configured */
1276 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_CHARSET
| IPP_TAG_COPY
,
1277 "charset-configured", NULL
, "utf-8");
1279 /* charset-supported */
1280 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_CHARSET
| IPP_TAG_COPY
,
1281 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1284 /* color-supported */
1285 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1288 /* compression-supported */
1289 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1290 "compression-supported", NULL
, "none");
1292 /* copies-default */
1293 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1294 "copies-default", 1);
1296 /* copies-supported */
1297 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1299 /* document-format-default */
1300 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1301 "document-format-default", NULL
, defformat
);
1303 /* document-format-supported */
1304 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1305 "document-format-supported", num_formats
, NULL
,
1306 (const char * const *)formats
);
1308 /* finishings-default */
1309 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1310 "finishings-default", IPP_FINISHINGS_NONE
);
1312 /* finishings-supported */
1313 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1314 "finishings-supported", IPP_FINISHINGS_NONE
);
1316 /* generated-natural-language-supported */
1317 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_LANGUAGE
| IPP_TAG_COPY
,
1318 "generated-natural-language-supported", NULL
, "en");
1320 /* ipp-versions-supported */
1321 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1322 "ipp-versions-supported",
1323 sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1325 /* job-creation-attributes-supported */
1326 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1327 "job-creation-attributes-supported",
1328 sizeof(job_creation
) / sizeof(job_creation
[0]),
1329 NULL
, job_creation
);
1331 /* job-k-octets-supported */
1332 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1335 /* job-priority-default */
1336 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1337 "job-priority-default", 50);
1339 /* job-priority-supported */
1340 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1341 "job-priority-supported", 100);
1343 /* job-sheets-default */
1344 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
| IPP_TAG_COPY
,
1345 "job-sheets-default", NULL
, "none");
1347 /* job-sheets-supported */
1348 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
| IPP_TAG_COPY
,
1349 "job-sheets-supported", NULL
, "none");
1351 /* media-bottom-margin-supported */
1352 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1353 "media-bottom-margin-supported",
1354 (int)(sizeof(media_xxx_margin_supported
) /
1355 sizeof(media_xxx_margin_supported
[0])),
1356 media_xxx_margin_supported
);
1358 /* media-col-database */
1359 for (num_database
= 0, i
= 0;
1360 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1363 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1364 num_database
+= 2; /* auto + envelope */
1365 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1366 num_database
+= 12; /* auto + photographic-* + borderless */
1368 num_database
+= (int)(sizeof(media_type_supported
) /
1369 sizeof(media_type_supported
[0])) + 6;
1370 /* All types + borderless */
1373 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1374 "media-col-database", num_database
,
1376 for (media_col_value
= media_col_database
->values
, i
= 0;
1377 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1381 j
< (int)(sizeof(media_type_supported
) /
1382 sizeof(media_type_supported
[0]));
1385 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
&&
1386 strcmp(media_type_supported
[j
], "auto") &&
1387 strcmp(media_type_supported
[j
], "envelope"))
1389 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
&&
1390 strcmp(media_type_supported
[j
], "auto") &&
1391 strncmp(media_type_supported
[j
], "photographic-", 13))
1394 media_col_value
->collection
=
1395 create_media_col(media_supported
[i
], media_type_supported
[j
],
1396 media_col_sizes
[i
][0], media_col_sizes
[i
][1],
1397 media_xxx_margin_supported
[1]);
1400 if (media_col_sizes
[i
][2] != _IPP_ENV_ONLY
&&
1401 (!strcmp(media_type_supported
[j
], "auto") ||
1402 !strncmp(media_type_supported
[j
], "photographic-", 13)))
1405 * Add borderless version for this combination...
1408 media_col_value
->collection
=
1409 create_media_col(media_supported
[i
], media_type_supported
[j
],
1410 media_col_sizes
[i
][0], media_col_sizes
[i
][1],
1411 media_xxx_margin_supported
[0]);
1417 /* media-col-default */
1418 media_col_default
= create_media_col(media_supported
[0],
1419 media_type_supported
[0],
1420 media_col_sizes
[0][0],
1421 media_col_sizes
[0][1],
1422 media_xxx_margin_supported
[1]);
1424 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1426 ippDelete(media_col_default
);
1428 /* media-col-supported */
1429 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1430 "media-col-supported",
1431 (int)(sizeof(media_col_supported
) /
1432 sizeof(media_col_supported
[0])), NULL
,
1433 media_col_supported
);
1436 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1437 "media-default", NULL
, media_supported
[0]);
1439 /* media-left-margin-supported */
1440 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1441 "media-left-margin-supported",
1442 (int)(sizeof(media_xxx_margin_supported
) /
1443 sizeof(media_xxx_margin_supported
[0])),
1444 media_xxx_margin_supported
);
1446 /* media-right-margin-supported */
1447 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1448 "media-right-margin-supported",
1449 (int)(sizeof(media_xxx_margin_supported
) /
1450 sizeof(media_xxx_margin_supported
[0])),
1451 media_xxx_margin_supported
);
1453 /* media-supported */
1454 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1456 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1457 NULL
, media_supported
);
1459 /* media-size-supported */
1460 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1461 "media-size-supported",
1462 (int)(sizeof(media_col_sizes
) /
1463 sizeof(media_col_sizes
[0])),
1466 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1468 media_size_supported
->values
[i
].collection
=
1469 create_media_size(media_col_sizes
[i
][0], media_col_sizes
[i
][1]);
1471 /* media-top-margin-supported */
1472 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1473 "media-top-margin-supported",
1474 (int)(sizeof(media_xxx_margin_supported
) /
1475 sizeof(media_xxx_margin_supported
[0])),
1476 media_xxx_margin_supported
);
1478 /* media-type-supported */
1479 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1480 "media-type-supported",
1481 (int)(sizeof(media_type_supported
) /
1482 sizeof(media_type_supported
[0])),
1483 NULL
, media_type_supported
);
1485 /* multiple-document-handling-supported */
1486 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1487 "multiple-document-handling-supported",
1488 sizeof(multiple_document_handling
) /
1489 sizeof(multiple_document_handling
[0]), NULL
,
1490 multiple_document_handling
);
1492 /* multiple-document-jobs-supported */
1493 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
,
1494 "multiple-document-jobs-supported", 0);
1496 /* natural-language-configured */
1497 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_LANGUAGE
| IPP_TAG_COPY
,
1498 "natural-language-configured", NULL
, "en");
1500 /* number-up-default */
1501 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1502 "number-up-default", 1);
1504 /* number-up-supported */
1505 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1506 "number-up-supported", 1);
1508 /* operations-supported */
1509 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1510 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1512 /* orientation-requested-default */
1513 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1514 "orientation-requested-default", 0);
1516 /* orientation-requested-supported */
1517 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1518 "orientation-requested-supported", 4, orients
);
1520 /* output-bin-default */
1521 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1522 "output-bin-default", NULL
, "face-down");
1524 /* output-bin-supported */
1525 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1526 "output-bin-supported", NULL
, "face-down");
1528 /* pages-per-minute */
1529 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1530 "pages-per-minute", ppm
);
1532 /* pages-per-minute-color */
1534 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1535 "pages-per-minute-color", ppm_color
);
1537 /* pdl-override-supported */
1538 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1539 "pdl-override-supported", NULL
, "attempted");
1541 /* print-quality-default */
1542 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1543 "print-quality-default", IPP_QUALITY_NORMAL
);
1545 /* print-quality-supported */
1546 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1547 "print-quality-supported",
1548 (int)(sizeof(print_quality_supported
) /
1549 sizeof(print_quality_supported
[0])),
1550 print_quality_supported
);
1552 /* printer-device-id */
1553 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1554 "printer-device-id", NULL
, device_id
);
1557 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1558 "printer-icons", NULL
, icons
);
1560 /* printer-is-accepting-jobs */
1561 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1565 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1568 /* printer-location */
1569 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1570 "printer-location", NULL
, location
);
1572 /* printer-make-and-model */
1573 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1574 "printer-make-and-model", NULL
, make_model
);
1576 /* printer-more-info */
1577 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1578 "printer-more-info", NULL
, adminurl
);
1581 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1584 /* printer-resolution-default */
1585 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1586 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1588 /* printer-resolution-supported */
1589 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1590 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1592 /* printer-uri-supported */
1593 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1594 "printer-uri-supported", NULL
, uri
);
1596 /* reference-uri-scheme-supported */
1597 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1598 IPP_TAG_URISCHEME
| IPP_TAG_COPY
,
1599 "reference-uri-schemes-supported",
1600 (int)(sizeof(reference_uri_schemes_supported
) /
1601 sizeof(reference_uri_schemes_supported
[0])),
1602 NULL
, reference_uri_schemes_supported
);
1605 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1606 "sides-default", NULL
, "one-sided");
1608 /* sides-supported */
1609 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1610 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
1612 /* uri-authentication-supported */
1613 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1614 "uri-authentication-supported", NULL
, "none");
1616 /* uri-security-supported */
1617 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1618 "uri-security-supported", NULL
, "none");
1620 /* which-jobs-supported */
1621 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1622 "which-jobs-supported",
1623 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
1627 debug_attributes("Printer", printer
->attrs
, 0);
1631 * Register the printer with Bonjour...
1634 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
,
1635 ppm_color
> 0, duplex
, regtype
))
1637 #endif /* HAVE_DNSSD */
1647 * If we get here we were unable to create the printer...
1652 delete_printer(printer
);
1658 * 'create_requested_array()' - Create an array for requested-attributes.
1661 static cups_array_t
* /* O - requested-attributes array */
1662 create_requested_array(
1663 _ipp_client_t
*client
) /* I - Client */
1665 int i
; /* Looping var */
1666 ipp_attribute_t
*requested
; /* requested-attributes attribute */
1667 cups_array_t
*ra
; /* Requested attributes array */
1668 char *value
; /* Current value */
1672 * Get the requested-attributes attribute, and return NULL if we don't
1676 if ((requested
= ippFindAttribute(client
->request
, "requested-attributes",
1677 IPP_TAG_KEYWORD
)) == NULL
)
1681 * If the attribute contains a single "all" keyword, return NULL...
1684 if (requested
->num_values
== 1 &&
1685 !strcmp(requested
->values
[0].string
.text
, "all"))
1689 * Create an array using "strcmp" as the comparison function...
1692 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
1694 for (i
= 0; i
< requested
->num_values
; i
++)
1696 value
= requested
->values
[i
].string
.text
;
1698 if (!strcmp(value
, "job-template"))
1700 cupsArrayAdd(ra
, "copies");
1701 cupsArrayAdd(ra
, "copies-default");
1702 cupsArrayAdd(ra
, "copies-supported");
1703 cupsArrayAdd(ra
, "finishings");
1704 cupsArrayAdd(ra
, "finishings-default");
1705 cupsArrayAdd(ra
, "finishings-supported");
1706 cupsArrayAdd(ra
, "job-hold-until");
1707 cupsArrayAdd(ra
, "job-hold-until-default");
1708 cupsArrayAdd(ra
, "job-hold-until-supported");
1709 cupsArrayAdd(ra
, "job-priority");
1710 cupsArrayAdd(ra
, "job-priority-default");
1711 cupsArrayAdd(ra
, "job-priority-supported");
1712 cupsArrayAdd(ra
, "job-sheets");
1713 cupsArrayAdd(ra
, "job-sheets-default");
1714 cupsArrayAdd(ra
, "job-sheets-supported");
1715 cupsArrayAdd(ra
, "media");
1716 cupsArrayAdd(ra
, "media-col");
1717 cupsArrayAdd(ra
, "media-col-default");
1718 cupsArrayAdd(ra
, "media-col-supported");
1719 cupsArrayAdd(ra
, "media-default");
1720 cupsArrayAdd(ra
, "media-source-supported");
1721 cupsArrayAdd(ra
, "media-supported");
1722 cupsArrayAdd(ra
, "media-type-supported");
1723 cupsArrayAdd(ra
, "multiple-document-handling");
1724 cupsArrayAdd(ra
, "multiple-document-handling-default");
1725 cupsArrayAdd(ra
, "multiple-document-handling-supported");
1726 cupsArrayAdd(ra
, "number-up");
1727 cupsArrayAdd(ra
, "number-up-default");
1728 cupsArrayAdd(ra
, "number-up-supported");
1729 cupsArrayAdd(ra
, "orientation-requested");
1730 cupsArrayAdd(ra
, "orientation-requested-default");
1731 cupsArrayAdd(ra
, "orientation-requested-supported");
1732 cupsArrayAdd(ra
, "page-ranges");
1733 cupsArrayAdd(ra
, "page-ranges-supported");
1734 cupsArrayAdd(ra
, "printer-resolution");
1735 cupsArrayAdd(ra
, "printer-resolution-default");
1736 cupsArrayAdd(ra
, "printer-resolution-supported");
1737 cupsArrayAdd(ra
, "print-quality");
1738 cupsArrayAdd(ra
, "print-quality-default");
1739 cupsArrayAdd(ra
, "print-quality-supported");
1740 cupsArrayAdd(ra
, "sides");
1741 cupsArrayAdd(ra
, "sides-default");
1742 cupsArrayAdd(ra
, "sides-supported");
1744 else if (!strcmp(value
, "job-description"))
1746 cupsArrayAdd(ra
, "date-time-at-completed");
1747 cupsArrayAdd(ra
, "date-time-at-creation");
1748 cupsArrayAdd(ra
, "date-time-at-processing");
1749 cupsArrayAdd(ra
, "job-detailed-status-message");
1750 cupsArrayAdd(ra
, "job-document-access-errors");
1751 cupsArrayAdd(ra
, "job-id");
1752 cupsArrayAdd(ra
, "job-impressions");
1753 cupsArrayAdd(ra
, "job-impressions-completed");
1754 cupsArrayAdd(ra
, "job-k-octets");
1755 cupsArrayAdd(ra
, "job-k-octets-processed");
1756 cupsArrayAdd(ra
, "job-media-sheets");
1757 cupsArrayAdd(ra
, "job-media-sheets-completed");
1758 cupsArrayAdd(ra
, "job-message-from-operator");
1759 cupsArrayAdd(ra
, "job-more-info");
1760 cupsArrayAdd(ra
, "job-name");
1761 cupsArrayAdd(ra
, "job-originating-user-name");
1762 cupsArrayAdd(ra
, "job-printer-up-time");
1763 cupsArrayAdd(ra
, "job-printer-uri");
1764 cupsArrayAdd(ra
, "job-state");
1765 cupsArrayAdd(ra
, "job-state-message");
1766 cupsArrayAdd(ra
, "job-state-reasons");
1767 cupsArrayAdd(ra
, "job-uri");
1768 cupsArrayAdd(ra
, "number-of-documents");
1769 cupsArrayAdd(ra
, "number-of-intervening-jobs");
1770 cupsArrayAdd(ra
, "output-device-assigned");
1771 cupsArrayAdd(ra
, "time-at-completed");
1772 cupsArrayAdd(ra
, "time-at-creation");
1773 cupsArrayAdd(ra
, "time-at-processing");
1775 else if (!strcmp(value
, "printer-description"))
1777 cupsArrayAdd(ra
, "charset-configured");
1778 cupsArrayAdd(ra
, "charset-supported");
1779 cupsArrayAdd(ra
, "color-supported");
1780 cupsArrayAdd(ra
, "compression-supported");
1781 cupsArrayAdd(ra
, "document-format-default");
1782 cupsArrayAdd(ra
, "document-format-supported");
1783 cupsArrayAdd(ra
, "generated-natural-language-supported");
1784 cupsArrayAdd(ra
, "ipp-versions-supported");
1785 cupsArrayAdd(ra
, "job-impressions-supported");
1786 cupsArrayAdd(ra
, "job-k-octets-supported");
1787 cupsArrayAdd(ra
, "job-media-sheets-supported");
1788 cupsArrayAdd(ra
, "multiple-document-jobs-supported");
1789 cupsArrayAdd(ra
, "multiple-operation-time-out");
1790 cupsArrayAdd(ra
, "natural-language-configured");
1791 cupsArrayAdd(ra
, "notify-attributes-supported");
1792 cupsArrayAdd(ra
, "notify-lease-duration-default");
1793 cupsArrayAdd(ra
, "notify-lease-duration-supported");
1794 cupsArrayAdd(ra
, "notify-max-events-supported");
1795 cupsArrayAdd(ra
, "notify-events-default");
1796 cupsArrayAdd(ra
, "notify-events-supported");
1797 cupsArrayAdd(ra
, "notify-pull-method-supported");
1798 cupsArrayAdd(ra
, "notify-schemes-supported");
1799 cupsArrayAdd(ra
, "operations-supported");
1800 cupsArrayAdd(ra
, "pages-per-minute");
1801 cupsArrayAdd(ra
, "pages-per-minute-color");
1802 cupsArrayAdd(ra
, "pdl-override-supported");
1803 cupsArrayAdd(ra
, "printer-alert");
1804 cupsArrayAdd(ra
, "printer-alert-description");
1805 cupsArrayAdd(ra
, "printer-current-time");
1806 cupsArrayAdd(ra
, "printer-driver-installer");
1807 cupsArrayAdd(ra
, "printer-info");
1808 cupsArrayAdd(ra
, "printer-is-accepting-jobs");
1809 cupsArrayAdd(ra
, "printer-location");
1810 cupsArrayAdd(ra
, "printer-make-and-model");
1811 cupsArrayAdd(ra
, "printer-message-from-operator");
1812 cupsArrayAdd(ra
, "printer-more-info");
1813 cupsArrayAdd(ra
, "printer-more-info-manufacturer");
1814 cupsArrayAdd(ra
, "printer-name");
1815 cupsArrayAdd(ra
, "printer-state");
1816 cupsArrayAdd(ra
, "printer-state-message");
1817 cupsArrayAdd(ra
, "printer-state-reasons");
1818 cupsArrayAdd(ra
, "printer-up-time");
1819 cupsArrayAdd(ra
, "printer-uri-supported");
1820 cupsArrayAdd(ra
, "queued-job-count");
1821 cupsArrayAdd(ra
, "reference-uri-schemes-supported");
1822 cupsArrayAdd(ra
, "uri-authentication-supported");
1823 cupsArrayAdd(ra
, "uri-security-supported");
1825 else if (!strcmp(value
, "printer-defaults"))
1827 cupsArrayAdd(ra
, "copies-default");
1828 cupsArrayAdd(ra
, "document-format-default");
1829 cupsArrayAdd(ra
, "finishings-default");
1830 cupsArrayAdd(ra
, "job-hold-until-default");
1831 cupsArrayAdd(ra
, "job-priority-default");
1832 cupsArrayAdd(ra
, "job-sheets-default");
1833 cupsArrayAdd(ra
, "media-default");
1834 cupsArrayAdd(ra
, "media-col-default");
1835 cupsArrayAdd(ra
, "number-up-default");
1836 cupsArrayAdd(ra
, "orientation-requested-default");
1837 cupsArrayAdd(ra
, "sides-default");
1839 else if (!strcmp(value
, "subscription-template"))
1841 cupsArrayAdd(ra
, "notify-attributes");
1842 cupsArrayAdd(ra
, "notify-charset");
1843 cupsArrayAdd(ra
, "notify-events");
1844 cupsArrayAdd(ra
, "notify-lease-duration");
1845 cupsArrayAdd(ra
, "notify-natural-language");
1846 cupsArrayAdd(ra
, "notify-pull-method");
1847 cupsArrayAdd(ra
, "notify-recipient-uri");
1848 cupsArrayAdd(ra
, "notify-time-interval");
1849 cupsArrayAdd(ra
, "notify-user-data");
1852 cupsArrayAdd(ra
, value
);
1860 * 'debug_attributes()' - Print attributes in a request or response.
1864 debug_attributes(const char *title
, /* I - Title */
1865 ipp_t
*ipp
, /* I - Request/response */
1866 int type
) /* I - 0 = object, 1 = request, 2 = response */
1868 ipp_tag_t group_tag
; /* Current group */
1869 ipp_attribute_t
*attr
; /* Current attribute */
1870 char buffer
[2048]; /* String buffer for value */
1876 fprintf(stderr
, "%s:\n", title
);
1877 fprintf(stderr
, " version=%d.%d\n", ipp
->request
.any
.version
[0],
1878 ipp
->request
.any
.version
[1]);
1880 fprintf(stderr
, " operation-id=%s(%04x)\n",
1881 ippOpString(ipp
->request
.op
.operation_id
),
1882 ipp
->request
.op
.operation_id
);
1884 fprintf(stderr
, " status-code=%s(%04x)\n",
1885 ippErrorString(ipp
->request
.status
.status_code
),
1886 ipp
->request
.status
.status_code
);
1887 fprintf(stderr
, " request-id=%d\n\n", ipp
->request
.any
.request_id
);
1889 for (attr
= ipp
->attrs
, group_tag
= IPP_TAG_ZERO
; attr
; attr
= attr
->next
)
1891 if (attr
->group_tag
!= group_tag
)
1893 group_tag
= attr
->group_tag
;
1894 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1899 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1900 fprintf(stderr
, " %s (%s%s) %s\n", attr
->name
,
1901 attr
->num_values
> 1 ? "1setOf " : "",
1902 ippTagString(attr
->value_tag
), buffer
);
1909 * 'delete_client()' - Close the socket and free all memory used by a client
1914 delete_client(_ipp_client_t
*client
) /* I - Client */
1917 fprintf(stderr
, "Closing connection from %s (%s)\n", client
->http
.hostname
,
1918 client
->http
.hostaddr
->addr
.sa_family
== AF_INET
? "IPv4" : "IPv6");
1921 * Flush pending writes before closing...
1924 httpFlushWrite(&(client
->http
));
1926 if (client
->http
.fd
>= 0)
1927 close(client
->http
.fd
);
1933 httpClearCookie(&(client
->http
));
1934 httpClearFields(&(client
->http
));
1936 ippDelete(client
->request
);
1938 ippDelete(client
->response
);
1945 * 'delete_job()' - Remove from the printer and free all memory used by a job
1950 delete_job(_ipp_job_t
*job
) /* I - Job */
1953 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
1955 ippDelete(job
->attrs
);
1960 unlink(job
->filename
);
1962 free(job
->filename
);
1970 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
1971 * used by a printer object.
1975 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
1977 if (printer
->ipv4
>= 0)
1978 close(printer
->ipv4
);
1980 if (printer
->ipv6
>= 0)
1981 close(printer
->ipv6
);
1984 if (printer
->printer_ref
)
1985 DNSServiceRefDeallocate(printer
->printer_ref
);
1987 if (printer
->ipp_ref
)
1988 DNSServiceRefDeallocate(printer
->ipp_ref
);
1990 if (printer
->http_ref
)
1991 DNSServiceRefDeallocate(printer
->http_ref
);
1993 if (printer
->common_ref
)
1994 DNSServiceRefDeallocate(printer
->common_ref
);
1996 TXTRecordDeallocate(&(printer
->ipp_txt
));
1998 if (printer
->dnssd_name
)
1999 _cupsStrFree(printer
->dnssd_name
);
2000 #endif /* HAVE_DNSSD */
2003 _cupsStrFree(printer
->name
);
2005 _cupsStrFree(printer
->icon
);
2006 if (printer
->directory
)
2007 _cupsStrFree(printer
->directory
);
2008 if (printer
->hostname
)
2009 _cupsStrFree(printer
->hostname
);
2011 _cupsStrFree(printer
->uri
);
2013 ippDelete(printer
->attrs
);
2014 cupsArrayDelete(printer
->jobs
);
2022 * 'dnssd_callback()' - Handle Bonjour registration events.
2027 DNSServiceRef sdRef
, /* I - Service reference */
2028 DNSServiceFlags flags
, /* I - Status flags */
2029 DNSServiceErrorType errorCode
, /* I - Error, if any */
2030 const char *name
, /* I - Service name */
2031 const char *regtype
, /* I - Service type */
2032 const char *domain
, /* I - Domain for service */
2033 _ipp_printer_t
*printer
) /* I - Printer */
2037 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2038 regtype
, (int)errorCode
);
2041 else if (_cups_strcasecmp(name
, printer
->dnssd_name
))
2044 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2046 /* No lock needed since only the main thread accesses/changes this */
2047 _cupsStrFree(printer
->dnssd_name
);
2048 printer
->dnssd_name
= _cupsStrAlloc(name
);
2051 #endif /* HAVE_DNSSD */
2055 * 'find_job()' - Find a job specified in a request.
2058 static _ipp_job_t
* /* O - Job or NULL */
2059 find_job(_ipp_client_t
*client
) /* I - Client */
2061 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2062 _ipp_job_t key
, /* Job search key */
2063 *job
; /* Matching job, if any */
2068 if ((attr
= ippFindAttribute(client
->request
, "job-uri",
2069 IPP_TAG_URI
)) != NULL
)
2071 if (!strncmp(attr
->values
[0].string
.text
, client
->printer
->uri
,
2072 client
->printer
->urilen
) &&
2073 attr
->values
[0].string
.text
[client
->printer
->urilen
] == '/')
2074 key
.id
= atoi(attr
->values
[0].string
.text
+ client
->printer
->urilen
+ 1);
2076 else if ((attr
= ippFindAttribute(client
->request
, "job-id",
2077 IPP_TAG_INTEGER
)) != NULL
)
2078 key
.id
= attr
->values
[0].integer
;
2080 _cupsRWLockRead(&(client
->printer
->rwlock
));
2081 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2082 _cupsRWUnlock(&(client
->printer
->rwlock
));
2089 * 'html_escape()' - Write a HTML-safe string.
2093 html_escape(_ipp_client_t
*client
, /* I - Client */
2094 const char *s
, /* I - String to write */
2095 size_t slen
) /* I - Number of characters to write */
2097 const char *start
, /* Start of segment */
2098 *end
; /* End of string */
2102 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2104 while (*s
&& s
< end
)
2106 if (*s
== '&' || *s
== '<')
2109 httpWrite2(&(client
->http
), start
, s
- start
);
2112 httpWrite2(&(client
->http
), "&", 5);
2114 httpWrite2(&(client
->http
), "<", 4);
2123 httpWrite2(&(client
->http
), start
, s
- start
);
2128 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2132 html_printf(_ipp_client_t
*client
, /* I - Client */
2133 const char *format
, /* I - Printf-style format string */
2134 ...) /* I - Additional arguments as needed */
2136 va_list ap
; /* Pointer to arguments */
2137 const char *start
; /* Start of string */
2138 char size
, /* Size character (h, l, L) */
2139 type
; /* Format type character */
2140 int width
, /* Width of field */
2141 prec
; /* Number of characters of precision */
2142 char tformat
[100], /* Temporary format string for sprintf() */
2143 *tptr
, /* Pointer into temporary format */
2144 temp
[1024]; /* Buffer for formatted numbers */
2145 char *s
; /* Pointer to string */
2149 * Loop through the format string, formatting as needed...
2152 va_start(ap
, format
);
2160 httpWrite2(&(client
->http
), start
, format
- start
);
2163 *tptr
++ = *format
++;
2167 httpWrite2(&(client
->http
), "%", 1);
2171 else if (strchr(" -+#\'", *format
))
2172 *tptr
++ = *format
++;
2177 * Get width from argument...
2181 width
= va_arg(ap
, int);
2183 snprintf(tptr
, sizeof(tformat
) - (tptr
- tformat
), "%d", width
);
2184 tptr
+= strlen(tptr
);
2190 while (isdigit(*format
& 255))
2192 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2195 width
= width
* 10 + *format
++ - '0';
2201 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2209 * Get precision from argument...
2213 prec
= va_arg(ap
, int);
2215 snprintf(tptr
, sizeof(tformat
) - (tptr
- tformat
), "%d", prec
);
2216 tptr
+= strlen(tptr
);
2222 while (isdigit(*format
& 255))
2224 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2227 prec
= prec
* 10 + *format
++ - '0';
2232 if (*format
== 'l' && format
[1] == 'l')
2236 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2244 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2246 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2261 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2270 case 'E' : /* Floating point formats */
2275 if ((width
+ 2) > sizeof(temp
))
2278 sprintf(temp
, tformat
, va_arg(ap
, double));
2280 httpWrite2(&(client
->http
), temp
, strlen(temp
));
2283 case 'B' : /* Integer formats */
2291 if ((width
+ 2) > sizeof(temp
))
2294 # ifdef HAVE_LONG_LONG
2296 sprintf(temp
, tformat
, va_arg(ap
, long long));
2298 # endif /* HAVE_LONG_LONG */
2300 sprintf(temp
, tformat
, va_arg(ap
, long));
2302 sprintf(temp
, tformat
, va_arg(ap
, int));
2304 httpWrite2(&(client
->http
), temp
, strlen(temp
));
2307 case 'p' : /* Pointer value */
2308 if ((width
+ 2) > sizeof(temp
))
2311 sprintf(temp
, tformat
, va_arg(ap
, void *));
2313 httpWrite2(&(client
->http
), temp
, strlen(temp
));
2316 case 'c' : /* Character or character array */
2319 temp
[0] = va_arg(ap
, int);
2321 html_escape(client
, temp
, 1);
2324 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2327 case 's' : /* String */
2328 if ((s
= va_arg(ap
, char *)) == NULL
)
2331 html_escape(client
, s
, strlen(s
));
2340 httpWrite2(&(client
->http
), start
, format
- start
);
2347 * 'ipp_cancel_job()' - Cancel a job.
2351 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2353 _ipp_job_t
*job
; /* Job information */
2360 if ((job
= find_job(client
)) == NULL
)
2362 respond_ipp(client
, IPP_NOT_FOUND
, "Job does not exist.");
2367 * See if the job is already completed, canceled, or aborted; if so,
2368 * we can't cancel...
2373 case IPP_JOB_CANCELED
:
2374 respond_ipp(client
, IPP_NOT_POSSIBLE
,
2375 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2378 case IPP_JOB_ABORTED
:
2379 respond_ipp(client
, IPP_NOT_POSSIBLE
,
2380 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2383 case IPP_JOB_COMPLETED
:
2384 respond_ipp(client
, IPP_NOT_POSSIBLE
,
2385 "Job #%d is already completed - can\'t cancel.", job
->id
);
2393 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2395 if (job
->state
== IPP_JOB_PROCESSING
||
2396 (job
->state
== IPP_JOB_HELD
&& job
->fd
>= 0))
2400 job
->state
= IPP_JOB_CANCELED
;
2401 job
->completed
= time(NULL
);
2404 _cupsRWUnlock(&(client
->printer
->rwlock
));
2406 respond_ipp(client
, IPP_OK
, NULL
);
2413 * 'ipp_create_job()' - Create a job object.
2417 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2419 _ipp_job_t
*job
; /* New job */
2420 cups_array_t
*ra
; /* Attributes to send in response */
2424 * Validate print job attributes...
2427 if (!valid_job_attributes(client
))
2429 httpFlush(&(client
->http
));
2434 * Do we have a file to print?
2437 if (client
->http
.state
== HTTP_POST_RECV
)
2439 respond_ipp(client
, IPP_BAD_REQUEST
,
2440 "Unexpected document data following request.");
2448 if ((job
= create_job(client
)) == NULL
)
2450 respond_ipp(client
, IPP_PRINTER_BUSY
, "Currently printing another job.");
2455 * Return the job info...
2458 respond_ipp(client
, IPP_OK
, NULL
);
2460 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2461 cupsArrayAdd(ra
, "job-id");
2462 cupsArrayAdd(ra
, "job-state");
2463 cupsArrayAdd(ra
, "job-state-reasons");
2464 cupsArrayAdd(ra
, "job-uri");
2466 copy_job_attributes(client
, job
, ra
);
2467 cupsArrayDelete(ra
);
2472 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2476 ipp_get_job_attributes(
2477 _ipp_client_t
*client
) /* I - Client */
2479 _ipp_job_t
*job
; /* Job */
2480 cups_array_t
*ra
; /* requested-attributes */
2483 if ((job
= find_job(client
)) == NULL
)
2485 respond_ipp(client
, IPP_NOT_FOUND
, "Job not found.");
2489 respond_ipp(client
, IPP_OK
, NULL
);
2491 ra
= create_requested_array(client
);
2492 copy_job_attributes(client
, job
, ra
);
2493 cupsArrayDelete(ra
);
2498 * 'ipp_get_jobs()' - Get a list of job objects.
2502 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2504 ipp_attribute_t
*attr
; /* Current attribute */
2505 int job_comparison
; /* Job comparison */
2506 ipp_jstate_t job_state
; /* job-state value */
2507 int first_job_id
, /* First job ID */
2508 limit
, /* Maximum number of jobs to return */
2509 count
; /* Number of jobs that match */
2510 const char *username
; /* Username */
2511 _ipp_job_t
*job
; /* Current job pointer */
2512 cups_array_t
*ra
; /* Requested attributes array */
2516 * See if the "which-jobs" attribute have been specified...
2519 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2520 IPP_TAG_KEYWORD
)) != NULL
)
2521 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->http
.hostname
,
2522 attr
->values
[0].string
.text
);
2524 if (!attr
|| !strcmp(attr
->values
[0].string
.text
, "not-completed"))
2526 job_comparison
= -1;
2527 job_state
= IPP_JOB_STOPPED
;
2529 else if (!strcmp(attr
->values
[0].string
.text
, "completed"))
2532 job_state
= IPP_JOB_CANCELED
;
2534 else if (!strcmp(attr
->values
[0].string
.text
, "aborted"))
2537 job_state
= IPP_JOB_ABORTED
;
2539 else if (!strcmp(attr
->values
[0].string
.text
, "all"))
2542 job_state
= IPP_JOB_PENDING
;
2544 else if (!strcmp(attr
->values
[0].string
.text
, "canceled"))
2547 job_state
= IPP_JOB_CANCELED
;
2549 else if (!strcmp(attr
->values
[0].string
.text
, "pending"))
2552 job_state
= IPP_JOB_PENDING
;
2554 else if (!strcmp(attr
->values
[0].string
.text
, "pending-held"))
2557 job_state
= IPP_JOB_HELD
;
2559 else if (!strcmp(attr
->values
[0].string
.text
, "processing"))
2562 job_state
= IPP_JOB_PROCESSING
;
2564 else if (!strcmp(attr
->values
[0].string
.text
, "processing-stopped"))
2567 job_state
= IPP_JOB_STOPPED
;
2571 respond_ipp(client
, IPP_ATTRIBUTES
,
2572 "The which-jobs value \"%s\" is not supported.",
2573 attr
->values
[0].string
.text
);
2574 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2575 "which-jobs", NULL
, attr
->values
[0].string
.text
);
2580 * See if they want to limit the number of jobs reported...
2583 if ((attr
= ippFindAttribute(client
->request
, "limit",
2584 IPP_TAG_INTEGER
)) != NULL
)
2586 limit
= attr
->values
[0].integer
;
2588 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->http
.hostname
, limit
);
2593 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2594 IPP_TAG_INTEGER
)) != NULL
)
2596 first_job_id
= attr
->values
[0].integer
;
2598 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->http
.hostname
,
2605 * See if we only want to see jobs for a specific user...
2610 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2611 IPP_TAG_BOOLEAN
)) != NULL
)
2613 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->http
.hostname
,
2614 attr
->values
[0].boolean
? "true" : "false");
2616 if (attr
->values
[0].boolean
)
2618 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
2619 IPP_TAG_NAME
)) == NULL
)
2621 respond_ipp(client
, IPP_BAD_REQUEST
,
2622 "Need requesting-user-name with my-jobs.");
2626 username
= attr
->values
[0].string
.text
;
2628 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2629 client
->http
.hostname
, username
);
2634 * OK, build a list of jobs for this printer...
2637 if ((ra
= create_requested_array(client
)) == NULL
&&
2638 !ippFindAttribute(client
->request
, "requested-attributes",
2642 * IPP conformance - Get-Jobs has a default requested-attributes value of
2643 * "job-id" and "job-uri".
2646 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2647 cupsArrayAdd(ra
, "job-id");
2648 cupsArrayAdd(ra
, "job-uri");
2651 respond_ipp(client
, IPP_OK
, NULL
);
2653 _cupsRWLockRead(&(client
->printer
->rwlock
));
2655 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
2656 (limit
<= 0 || count
< limit
) && job
;
2657 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
2660 * Filter out jobs that don't match...
2663 if ((job_comparison
< 0 && job
->state
> job_state
) ||
2664 (job_comparison
== 0 && job
->state
!= job_state
) ||
2665 (job_comparison
> 0 && job
->state
< job_state
) ||
2666 job
->id
< first_job_id
||
2667 (username
&& job
->username
&& _cups_strcasecmp(username
, job
->username
)))
2671 ippAddSeparator(client
->response
);
2674 copy_job_attributes(client
, job
, ra
);
2677 cupsArrayDelete(ra
);
2679 _cupsRWUnlock(&(client
->printer
->rwlock
));
2684 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2688 ipp_get_printer_attributes(
2689 _ipp_client_t
*client
) /* I - Client */
2691 cups_array_t
*ra
; /* Requested attributes array */
2692 _ipp_printer_t
*printer
; /* Printer */
2696 * Send the attributes...
2699 ra
= create_requested_array(client
);
2700 printer
= client
->printer
;
2702 respond_ipp(client
, IPP_OK
, NULL
);
2704 _cupsRWLockRead(&(printer
->rwlock
));
2706 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
2709 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
2710 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
2711 "printer-state", printer
->state
);
2713 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
2715 if (printer
->state_reasons
== _IPP_PRINTER_NONE
)
2716 ippAddString(client
->response
, IPP_TAG_PRINTER
,
2717 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "printer-state-reasons",
2721 int num_reasons
= 0;/* Number of reasons */
2722 const char *reasons
[32]; /* Reason strings */
2724 if (printer
->state_reasons
& _IPP_PRINTER_OTHER
)
2725 reasons
[num_reasons
++] = "other";
2726 if (printer
->state_reasons
& _IPP_PRINTER_COVER_OPEN
)
2727 reasons
[num_reasons
++] = "cover-open";
2728 if (printer
->state_reasons
& _IPP_PRINTER_INPUT_TRAY_MISSING
)
2729 reasons
[num_reasons
++] = "input-tray-missing";
2730 if (printer
->state_reasons
& _IPP_PRINTER_MARKER_SUPPLY_EMPTY
)
2731 reasons
[num_reasons
++] = "marker-supply-empty-warning";
2732 if (printer
->state_reasons
& _IPP_PRINTER_MARKER_SUPPLY_LOW
)
2733 reasons
[num_reasons
++] = "marker-supply-low-report";
2734 if (printer
->state_reasons
& _IPP_PRINTER_MARKER_WASTE_ALMOST_FULL
)
2735 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
2736 if (printer
->state_reasons
& _IPP_PRINTER_MARKER_WASTE_FULL
)
2737 reasons
[num_reasons
++] = "marker-waste-full-warning";
2738 if (printer
->state_reasons
& _IPP_PRINTER_MEDIA_EMPTY
)
2739 reasons
[num_reasons
++] = "media-empty-warning";
2740 if (printer
->state_reasons
& _IPP_PRINTER_MEDIA_JAM
)
2741 reasons
[num_reasons
++] = "media-jam-warning";
2742 if (printer
->state_reasons
& _IPP_PRINTER_MEDIA_LOW
)
2743 reasons
[num_reasons
++] = "media-low-report";
2744 if (printer
->state_reasons
& _IPP_PRINTER_MEDIA_NEEDED
)
2745 reasons
[num_reasons
++] = "media-needed-report";
2746 if (printer
->state_reasons
& _IPP_PRINTER_MOVING_TO_PAUSED
)
2747 reasons
[num_reasons
++] = "moving-to-paused";
2748 if (printer
->state_reasons
& _IPP_PRINTER_PAUSED
)
2749 reasons
[num_reasons
++] = "paused";
2750 if (printer
->state_reasons
& _IPP_PRINTER_SPOOL_AREA_FULL
)
2751 reasons
[num_reasons
++] = "spool-area-full";
2752 if (printer
->state_reasons
& _IPP_PRINTER_TONER_EMPTY
)
2753 reasons
[num_reasons
++] = "toner-empty-warning";
2754 if (printer
->state_reasons
& _IPP_PRINTER_TONER_LOW
)
2755 reasons
[num_reasons
++] = "toner-low-report";
2757 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
2758 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "printer-state-reasons",
2759 num_reasons
, NULL
, reasons
);
2763 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
2764 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2765 "printer-up-time", (int)time(NULL
));
2767 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
2768 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2770 printer
->active_job
&&
2771 printer
->active_job
->state
< IPP_JOB_CANCELED
);
2773 _cupsRWUnlock(&(printer
->rwlock
));
2775 cupsArrayDelete(ra
);
2780 * 'ipp_print_job()' - Create a job object with an attached document.
2784 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
2786 _ipp_job_t
*job
; /* New job */
2787 char filename
[1024], /* Filename buffer */
2788 buffer
[4096]; /* Copy buffer */
2789 ssize_t bytes
; /* Bytes read */
2790 cups_array_t
*ra
; /* Attributes to send in response */
2794 * Validate print job attributes...
2797 if (!valid_job_attributes(client
))
2799 httpFlush(&(client
->http
));
2804 * Do we have a file to print?
2807 if (client
->http
.state
== HTTP_POST_SEND
)
2809 respond_ipp(client
, IPP_BAD_REQUEST
, "No file in request.");
2817 if ((job
= create_job(client
)) == NULL
)
2819 respond_ipp(client
, IPP_PRINTER_BUSY
, "Currently printing another job.");
2824 * Create a file for the request data...
2827 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
2828 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
2829 client
->printer
->directory
, job
->id
);
2830 else if (!_cups_strcasecmp(job
->format
, "image/png"))
2831 snprintf(filename
, sizeof(filename
), "%s/%d.png",
2832 client
->printer
->directory
, job
->id
);
2833 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
2834 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
2835 client
->printer
->directory
, job
->id
);
2836 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
2837 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
2838 client
->printer
->directory
, job
->id
);
2840 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
2841 client
->printer
->directory
, job
->id
);
2843 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
2845 job
->state
= IPP_JOB_ABORTED
;
2847 respond_ipp(client
, IPP_INTERNAL_ERROR
,
2848 "Unable to create print file: %s", strerror(errno
));
2852 while ((bytes
= httpRead2(&(client
->http
), buffer
, sizeof(buffer
))) > 0)
2854 if (write(job
->fd
, buffer
, bytes
) < bytes
)
2856 int error
= errno
; /* Write error */
2858 job
->state
= IPP_JOB_ABORTED
;
2865 respond_ipp(client
, IPP_INTERNAL_ERROR
,
2866 "Unable to write print file: %s", strerror(error
));
2874 * Got an error while reading the print data, so abort this job.
2877 job
->state
= IPP_JOB_ABORTED
;
2884 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to read print file.");
2890 int error
= errno
; /* Write error */
2892 job
->state
= IPP_JOB_ABORTED
;
2897 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to write print file: %s",
2903 job
->filename
= strdup(filename
);
2904 job
->state
= IPP_JOB_PENDING
;
2907 * Process the job...
2911 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
2913 job
->state
= IPP_JOB_ABORTED
;
2914 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to process job.");
2923 * Return the job info...
2926 respond_ipp(client
, IPP_OK
, NULL
);
2928 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2929 cupsArrayAdd(ra
, "job-id");
2930 cupsArrayAdd(ra
, "job-state");
2931 cupsArrayAdd(ra
, "job-state-reasons");
2932 cupsArrayAdd(ra
, "job-uri");
2934 copy_job_attributes(client
, job
, ra
);
2935 cupsArrayDelete(ra
);
2940 * 'ipp_print_uri()' - Create a job object with a referenced document.
2944 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
2946 _ipp_job_t
*job
; /* New job */
2947 ipp_attribute_t
*uri
; /* document-uri */
2948 char scheme
[256], /* URI scheme */
2949 userpass
[256], /* Username and password info */
2950 hostname
[256], /* Hostname */
2951 resource
[1024]; /* Resource path */
2952 int port
; /* Port number */
2953 http_uri_status_t uri_status
; /* URI decode status */
2954 http_encryption_t encryption
; /* Encryption to use, if any */
2955 http_t
*http
; /* Connection for http/https URIs */
2956 http_status_t status
; /* Access status for http/https URIs */
2957 int infile
; /* Input file for local file URIs */
2958 char filename
[1024], /* Filename buffer */
2959 buffer
[4096]; /* Copy buffer */
2960 ssize_t bytes
; /* Bytes read */
2961 cups_array_t
*ra
; /* Attributes to send in response */
2962 static const char * const uri_status_strings
[] =
2963 { /* URI decode errors */
2965 "Bad arguments to function.",
2966 "Bad resource in URI.",
2967 "Bad port number in URI.",
2968 "Bad hostname in URI.",
2969 "Bad username in URI.",
2970 "Bad scheme in URI.",
2976 * Validate print job attributes...
2979 if (!valid_job_attributes(client
))
2981 httpFlush(&(client
->http
));
2986 * Do we have a file to print?
2989 if (client
->http
.state
== HTTP_POST_RECV
)
2991 respond_ipp(client
, IPP_BAD_REQUEST
,
2992 "Unexpected document data following request.");
2997 * Do we have a document URI?
3000 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3001 IPP_TAG_URI
)) == NULL
)
3003 respond_ipp(client
, IPP_BAD_REQUEST
, "Missing document-uri.");
3007 if (uri
->num_values
!= 1)
3009 respond_ipp(client
, IPP_BAD_REQUEST
, "Too many document-uri values.");
3013 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
,
3014 scheme
, sizeof(scheme
), userpass
,
3015 sizeof(userpass
), hostname
, sizeof(hostname
),
3016 &port
, resource
, sizeof(resource
));
3017 if (uri_status
< HTTP_URI_OK
)
3019 respond_ipp(client
, IPP_BAD_REQUEST
, "Bad document-uri: %s",
3020 uri_status_strings
[uri_status
- HTTP_URI_OVERFLOW
]);
3024 if (strcmp(scheme
, "file") &&
3026 strcmp(scheme
, "https") &&
3027 #endif /* HAVE_SSL */
3028 strcmp(scheme
, "http"))
3030 respond_ipp(client
, IPP_URI_SCHEME
, "URI scheme \"%s\" not supported.",
3035 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3037 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to access URI: %s",
3046 if ((job
= create_job(client
)) == NULL
)
3048 respond_ipp(client
, IPP_PRINTER_BUSY
, "Currently printing another job.");
3053 * Create a file for the request data...
3056 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3057 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3058 client
->printer
->directory
, job
->id
);
3059 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3060 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3061 client
->printer
->directory
, job
->id
);
3062 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3063 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3064 client
->printer
->directory
, job
->id
);
3065 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3066 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3067 client
->printer
->directory
, job
->id
);
3069 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3070 client
->printer
->directory
, job
->id
);
3072 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3074 job
->state
= IPP_JOB_ABORTED
;
3076 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3077 "Unable to create print file: %s", strerror(errno
));
3081 if (!strcmp(scheme
, "file"))
3083 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3085 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to access URI: %s",
3092 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3093 (errno
== EAGAIN
|| errno
== EINTR
))
3095 else if (bytes
> 0 && write(job
->fd
, buffer
, bytes
) < bytes
)
3097 int error
= errno
; /* Write error */
3099 job
->state
= IPP_JOB_ABORTED
;
3107 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3108 "Unable to write print file: %s", strerror(error
));
3119 if (port
== 443 || !strcmp(scheme
, "https"))
3120 encryption
= HTTP_ENCRYPT_ALWAYS
;
3122 #endif /* HAVE_SSL */
3123 encryption
= HTTP_ENCRYPT_IF_REQUESTED
;
3125 if ((http
= httpConnectEncrypt(hostname
, port
, encryption
)) == NULL
)
3127 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
,
3128 "Unable to connect to %s: %s", hostname
,
3129 cupsLastErrorString());
3130 job
->state
= IPP_JOB_ABORTED
;
3139 httpClearFields(http
);
3140 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3141 if (httpGet(http
, resource
))
3143 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to GET URI: %s",
3146 job
->state
= IPP_JOB_ABORTED
;
3156 while ((status
= httpUpdate(http
)) == HTTP_CONTINUE
);
3158 if (status
!= HTTP_OK
)
3160 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to GET URI: %s",
3161 httpStatus(status
));
3163 job
->state
= IPP_JOB_ABORTED
;
3173 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3175 if (write(job
->fd
, buffer
, bytes
) < bytes
)
3177 int error
= errno
; /* Write error */
3179 job
->state
= IPP_JOB_ABORTED
;
3187 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3188 "Unable to write print file: %s", strerror(error
));
3198 int error
= errno
; /* Write error */
3200 job
->state
= IPP_JOB_ABORTED
;
3205 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to write print file: %s",
3211 job
->filename
= strdup(filename
);
3212 job
->state
= IPP_JOB_PENDING
;
3215 * Process the job...
3219 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3221 job
->state
= IPP_JOB_ABORTED
;
3222 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to process job.");
3231 * Return the job info...
3234 respond_ipp(client
, IPP_OK
, NULL
);
3236 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3237 cupsArrayAdd(ra
, "job-id");
3238 cupsArrayAdd(ra
, "job-state");
3239 cupsArrayAdd(ra
, "job-state-reasons");
3240 cupsArrayAdd(ra
, "job-uri");
3242 copy_job_attributes(client
, job
, ra
);
3243 cupsArrayDelete(ra
);
3248 * 'ipp_send_document()' - Add an attached document to a job object created with
3253 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3255 _ipp_job_t
*job
; /* Job information */
3256 char filename
[1024], /* Filename buffer */
3257 buffer
[4096]; /* Copy buffer */
3258 ssize_t bytes
; /* Bytes read */
3259 ipp_attribute_t
*attr
; /* Current attribute */
3260 cups_array_t
*ra
; /* Attributes to send in response */
3267 if ((job
= find_job(client
)) == NULL
)
3269 respond_ipp(client
, IPP_NOT_FOUND
, "Job does not exist.");
3270 httpFlush(&(client
->http
));
3275 * See if we already have a document for this job or the job has already
3276 * in a non-pending state...
3279 if (job
->state
> IPP_JOB_HELD
)
3281 respond_ipp(client
, IPP_NOT_POSSIBLE
, "Job is not in a pending state.");
3282 httpFlush(&(client
->http
));
3285 else if (job
->filename
|| job
->fd
>= 0)
3287 respond_ipp(client
, IPP_MULTIPLE_JOBS_NOT_SUPPORTED
,
3288 "Multiple document jobs are not supported.");
3289 httpFlush(&(client
->http
));
3293 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3294 IPP_TAG_ZERO
)) == NULL
)
3296 respond_ipp(client
, IPP_BAD_REQUEST
,
3297 "Missing required last-document attribute.");
3298 httpFlush(&(client
->http
));
3301 else if (attr
->value_tag
!= IPP_TAG_BOOLEAN
|| attr
->num_values
!= 1 ||
3302 !attr
->values
[0].boolean
)
3304 respond_unsupported(client
, attr
);
3305 httpFlush(&(client
->http
));
3310 * Validate document attributes...
3313 if (!valid_doc_attributes(client
))
3315 httpFlush(&(client
->http
));
3320 * Get the document format for the job...
3323 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3325 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3326 IPP_TAG_MIMETYPE
)) != NULL
)
3327 job
->format
= attr
->values
[0].string
.text
;
3329 job
->format
= "application/octet-stream";
3332 * Create a file for the request data...
3335 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3336 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3337 client
->printer
->directory
, job
->id
);
3338 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3339 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3340 client
->printer
->directory
, job
->id
);
3341 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3342 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3343 client
->printer
->directory
, job
->id
);
3344 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3345 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3346 client
->printer
->directory
, job
->id
);
3348 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3349 client
->printer
->directory
, job
->id
);
3351 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3353 _cupsRWUnlock(&(client
->printer
->rwlock
));
3357 job
->state
= IPP_JOB_ABORTED
;
3359 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3360 "Unable to create print file: %s", strerror(errno
));
3364 while ((bytes
= httpRead2(&(client
->http
), buffer
, sizeof(buffer
))) > 0)
3366 if (write(job
->fd
, buffer
, bytes
) < bytes
)
3368 int error
= errno
; /* Write error */
3370 job
->state
= IPP_JOB_ABORTED
;
3377 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3378 "Unable to write print file: %s", strerror(error
));
3386 * Got an error while reading the print data, so abort this job.
3389 job
->state
= IPP_JOB_ABORTED
;
3396 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to read print file.");
3402 int error
= errno
; /* Write error */
3404 job
->state
= IPP_JOB_ABORTED
;
3409 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to write print file: %s",
3414 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3417 job
->filename
= strdup(filename
);
3418 job
->state
= IPP_JOB_PENDING
;
3420 _cupsRWUnlock(&(client
->printer
->rwlock
));
3423 * Process the job...
3427 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3429 job
->state
= IPP_JOB_ABORTED
;
3430 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to process job.");
3439 * Return the job info...
3442 respond_ipp(client
, IPP_OK
, NULL
);
3444 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3445 cupsArrayAdd(ra
, "job-id");
3446 cupsArrayAdd(ra
, "job-state");
3447 cupsArrayAdd(ra
, "job-state-reasons");
3448 cupsArrayAdd(ra
, "job-uri");
3450 copy_job_attributes(client
, job
, ra
);
3451 cupsArrayDelete(ra
);
3456 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3461 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
3463 _ipp_job_t
*job
; /* Job information */
3464 ipp_attribute_t
*uri
; /* document-uri */
3465 char scheme
[256], /* URI scheme */
3466 userpass
[256], /* Username and password info */
3467 hostname
[256], /* Hostname */
3468 resource
[1024]; /* Resource path */
3469 int port
; /* Port number */
3470 http_uri_status_t uri_status
; /* URI decode status */
3471 http_encryption_t encryption
; /* Encryption to use, if any */
3472 http_t
*http
; /* Connection for http/https URIs */
3473 http_status_t status
; /* Access status for http/https URIs */
3474 int infile
; /* Input file for local file URIs */
3475 char filename
[1024], /* Filename buffer */
3476 buffer
[4096]; /* Copy buffer */
3477 ssize_t bytes
; /* Bytes read */
3478 ipp_attribute_t
*attr
; /* Current attribute */
3479 cups_array_t
*ra
; /* Attributes to send in response */
3480 static const char * const uri_status_strings
[] =
3481 { /* URI decode errors */
3483 "Bad arguments to function.",
3484 "Bad resource in URI.",
3485 "Bad port number in URI.",
3486 "Bad hostname in URI.",
3487 "Bad username in URI.",
3488 "Bad scheme in URI.",
3497 if ((job
= find_job(client
)) == NULL
)
3499 respond_ipp(client
, IPP_NOT_FOUND
, "Job does not exist.");
3500 httpFlush(&(client
->http
));
3505 * See if we already have a document for this job or the job has already
3506 * in a non-pending state...
3509 if (job
->state
> IPP_JOB_HELD
)
3511 respond_ipp(client
, IPP_NOT_POSSIBLE
, "Job is not in a pending state.");
3512 httpFlush(&(client
->http
));
3515 else if (job
->filename
|| job
->fd
>= 0)
3517 respond_ipp(client
, IPP_MULTIPLE_JOBS_NOT_SUPPORTED
,
3518 "Multiple document jobs are not supported.");
3519 httpFlush(&(client
->http
));
3523 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3524 IPP_TAG_ZERO
)) == NULL
)
3526 respond_ipp(client
, IPP_BAD_REQUEST
,
3527 "Missing required last-document attribute.");
3528 httpFlush(&(client
->http
));
3531 else if (attr
->value_tag
!= IPP_TAG_BOOLEAN
|| attr
->num_values
!= 1 ||
3532 !attr
->values
[0].boolean
)
3534 respond_unsupported(client
, attr
);
3535 httpFlush(&(client
->http
));
3540 * Validate document attributes...
3543 if (!valid_doc_attributes(client
))
3545 httpFlush(&(client
->http
));
3550 * Do we have a file to print?
3553 if (client
->http
.state
== HTTP_POST_RECV
)
3555 respond_ipp(client
, IPP_BAD_REQUEST
,
3556 "Unexpected document data following request.");
3561 * Do we have a document URI?
3564 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3565 IPP_TAG_URI
)) == NULL
)
3567 respond_ipp(client
, IPP_BAD_REQUEST
, "Missing document-uri.");
3571 if (uri
->num_values
!= 1)
3573 respond_ipp(client
, IPP_BAD_REQUEST
, "Too many document-uri values.");
3577 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
,
3578 scheme
, sizeof(scheme
), userpass
,
3579 sizeof(userpass
), hostname
, sizeof(hostname
),
3580 &port
, resource
, sizeof(resource
));
3581 if (uri_status
< HTTP_URI_OK
)
3583 respond_ipp(client
, IPP_BAD_REQUEST
, "Bad document-uri: %s",
3584 uri_status_strings
[uri_status
- HTTP_URI_OVERFLOW
]);
3588 if (strcmp(scheme
, "file") &&
3590 strcmp(scheme
, "https") &&
3591 #endif /* HAVE_SSL */
3592 strcmp(scheme
, "http"))
3594 respond_ipp(client
, IPP_URI_SCHEME
, "URI scheme \"%s\" not supported.",
3599 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3601 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to access URI: %s",
3607 * Get the document format for the job...
3610 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3612 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3613 IPP_TAG_MIMETYPE
)) != NULL
)
3614 job
->format
= attr
->values
[0].string
.text
;
3616 job
->format
= "application/octet-stream";
3619 * Create a file for the request data...
3622 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3623 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3624 client
->printer
->directory
, job
->id
);
3625 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3626 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3627 client
->printer
->directory
, job
->id
);
3628 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3629 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3630 client
->printer
->directory
, job
->id
);
3631 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3632 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3633 client
->printer
->directory
, job
->id
);
3635 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3636 client
->printer
->directory
, job
->id
);
3638 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3640 _cupsRWUnlock(&(client
->printer
->rwlock
));
3644 job
->state
= IPP_JOB_ABORTED
;
3646 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3647 "Unable to create print file: %s", strerror(errno
));
3651 if (!strcmp(scheme
, "file"))
3653 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3655 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to access URI: %s",
3662 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3663 (errno
== EAGAIN
|| errno
== EINTR
))
3665 else if (bytes
> 0 && write(job
->fd
, buffer
, bytes
) < bytes
)
3667 int error
= errno
; /* Write error */
3669 job
->state
= IPP_JOB_ABORTED
;
3677 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3678 "Unable to write print file: %s", strerror(error
));
3689 if (port
== 443 || !strcmp(scheme
, "https"))
3690 encryption
= HTTP_ENCRYPT_ALWAYS
;
3692 #endif /* HAVE_SSL */
3693 encryption
= HTTP_ENCRYPT_IF_REQUESTED
;
3695 if ((http
= httpConnectEncrypt(hostname
, port
, encryption
)) == NULL
)
3697 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
,
3698 "Unable to connect to %s: %s", hostname
,
3699 cupsLastErrorString());
3700 job
->state
= IPP_JOB_ABORTED
;
3709 httpClearFields(http
);
3710 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3711 if (httpGet(http
, resource
))
3713 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to GET URI: %s",
3716 job
->state
= IPP_JOB_ABORTED
;
3726 while ((status
= httpUpdate(http
)) == HTTP_CONTINUE
);
3728 if (status
!= HTTP_OK
)
3730 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to GET URI: %s",
3731 httpStatus(status
));
3733 job
->state
= IPP_JOB_ABORTED
;
3743 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3745 if (write(job
->fd
, buffer
, bytes
) < bytes
)
3747 int error
= errno
; /* Write error */
3749 job
->state
= IPP_JOB_ABORTED
;
3757 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3758 "Unable to write print file: %s", strerror(error
));
3768 int error
= errno
; /* Write error */
3770 job
->state
= IPP_JOB_ABORTED
;
3775 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to write print file: %s",
3780 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3783 job
->filename
= strdup(filename
);
3784 job
->state
= IPP_JOB_PENDING
;
3786 _cupsRWUnlock(&(client
->printer
->rwlock
));
3789 * Process the job...
3793 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3795 job
->state
= IPP_JOB_ABORTED
;
3796 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to process job.");
3805 * Return the job info...
3808 respond_ipp(client
, IPP_OK
, NULL
);
3810 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3811 cupsArrayAdd(ra
, "job-id");
3812 cupsArrayAdd(ra
, "job-state");
3813 cupsArrayAdd(ra
, "job-state-reasons");
3814 cupsArrayAdd(ra
, "job-uri");
3816 copy_job_attributes(client
, job
, ra
);
3817 cupsArrayDelete(ra
);
3822 * 'ipp_validate_job()' - Validate job creation attributes.
3826 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
3828 if (valid_job_attributes(client
))
3829 respond_ipp(client
, IPP_OK
, NULL
);
3834 * 'process_client()' - Process client requests on a thread.
3837 static void * /* O - Exit status */
3838 process_client(_ipp_client_t
*client
) /* I - Client */
3841 * Loop until we are out of requests or timeout (30 seconds)...
3844 while (httpWait(&(client
->http
), 30000))
3845 if (!process_http(client
))
3849 * Close the conection to the client and return...
3852 delete_client(client
);
3859 * 'process_http()' - Process a HTTP request.
3862 int /* O - 1 on success, 0 on failure */
3863 process_http(_ipp_client_t
*client
) /* I - Client connection */
3865 char line
[4096], /* Line from client... */
3866 operation
[64], /* Operation code from socket */
3867 uri
[1024], /* URI */
3868 version
[64], /* HTTP version number string */
3869 *ptr
; /* Pointer into strings */
3870 int major
, minor
; /* HTTP version numbers */
3871 http_status_t status
; /* Transfer status */
3872 ipp_state_t state
; /* State of IPP transfer */
3876 * Abort if we have an error on the connection...
3879 if (client
->http
.error
)
3883 * Clear state variables...
3886 httpClearFields(&(client
->http
));
3887 ippDelete(client
->request
);
3888 ippDelete(client
->response
);
3890 client
->http
.activity
= time(NULL
);
3891 client
->http
.version
= HTTP_1_1
;
3892 client
->http
.keep_alive
= HTTP_KEEPALIVE_OFF
;
3893 client
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
3894 client
->http
.data_remaining
= 0;
3895 client
->request
= NULL
;
3896 client
->response
= NULL
;
3897 client
->operation
= HTTP_WAITING
;
3900 * Read a request from the connection...
3903 while ((ptr
= httpGets(line
, sizeof(line
) - 1, &(client
->http
))) != NULL
)
3911 * Parse the request line...
3914 fprintf(stderr
, "%s %s\n", client
->http
.hostname
, line
);
3916 switch (sscanf(line
, "%63s%1023s%63s", operation
, uri
, version
))
3919 fprintf(stderr
, "%s Bad request line.\n", client
->http
.hostname
);
3920 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
3924 client
->http
.version
= HTTP_0_9
;
3928 if (sscanf(version
, "HTTP/%d.%d", &major
, &minor
) != 2)
3930 fprintf(stderr
, "%s Bad HTTP version.\n", client
->http
.hostname
);
3931 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
3937 client
->http
.version
= (http_version_t
)(major
* 100 + minor
);
3938 if (client
->http
.version
== HTTP_1_1
)
3939 client
->http
.keep_alive
= HTTP_KEEPALIVE_ON
;
3941 client
->http
.keep_alive
= HTTP_KEEPALIVE_OFF
;
3945 respond_http(client
, HTTP_NOT_SUPPORTED
, NULL
, 0);
3952 * Handle full URLs in the request line...
3955 if (!strncmp(client
->uri
, "http:", 5) || !strncmp(client
->uri
, "ipp:", 4))
3957 char scheme
[32], /* Method/scheme */
3958 userpass
[128], /* Username:password */
3959 hostname
[HTTP_MAX_HOST
];/* Hostname */
3960 int port
; /* Port number */
3963 * Separate the URI into its components...
3966 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
3967 userpass
, sizeof(userpass
),
3968 hostname
, sizeof(hostname
), &port
,
3969 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_OK
)
3971 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->http
.hostname
, uri
);
3972 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
3982 if (!_httpDecodeURI(client
->uri
, uri
, sizeof(client
->uri
)))
3984 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->http
.hostname
, uri
);
3985 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
3991 * Process the request...
3994 if (!strcmp(operation
, "GET"))
3995 client
->http
.state
= HTTP_GET
;
3996 else if (!strcmp(operation
, "POST"))
3997 client
->http
.state
= HTTP_POST
;
3998 else if (!strcmp(operation
, "OPTIONS"))
3999 client
->http
.state
= HTTP_OPTIONS
;
4000 else if (!strcmp(operation
, "HEAD"))
4001 client
->http
.state
= HTTP_HEAD
;
4004 fprintf(stderr
, "%s Bad operation \"%s\".\n", client
->http
.hostname
,
4006 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
4010 client
->start
= time(NULL
);
4011 client
->operation
= client
->http
.state
;
4012 client
->http
.status
= HTTP_OK
;
4015 * Parse incoming parameters until the status changes...
4018 while ((status
= httpUpdate(&(client
->http
))) == HTTP_CONTINUE
);
4020 if (status
!= HTTP_OK
)
4022 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
4026 if (!client
->http
.fields
[HTTP_FIELD_HOST
][0] &&
4027 client
->http
.version
>= HTTP_1_1
)
4030 * HTTP/1.1 and higher require the "Host:" field...
4033 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
4038 * Handle HTTP Upgrade...
4041 if (!_cups_strcasecmp(client
->http
.fields
[HTTP_FIELD_CONNECTION
], "Upgrade"))
4043 if (!respond_http(client
, HTTP_NOT_IMPLEMENTED
, NULL
, 0))
4048 * Handle HTTP Expect...
4051 if (client
->http
.expect
&&
4052 (client
->operation
== HTTP_POST
|| client
->operation
== HTTP_PUT
))
4054 if (client
->http
.expect
== HTTP_CONTINUE
)
4057 * Send 100-continue header...
4060 if (!respond_http(client
, HTTP_CONTINUE
, NULL
, 0))
4066 * Send 417-expectation-failed header...
4069 if (!respond_http(client
, HTTP_EXPECTATION_FAILED
, NULL
, 0))
4072 httpPrintf(&(client
->http
), "Content-Length: 0\r\n");
4073 httpPrintf(&(client
->http
), "\r\n");
4074 httpFlushWrite(&(client
->http
));
4075 client
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
4080 * Handle new transfers...
4083 switch (client
->operation
)
4087 * Do HEAD/OPTIONS command...
4090 return (respond_http(client
, HTTP_OK
, NULL
, 0));
4093 if (!strcmp(client
->uri
, "/icon.png"))
4094 return (respond_http(client
, HTTP_OK
, "image/png", 0));
4095 else if (!strcmp(client
->uri
, "/"))
4096 return (respond_http(client
, HTTP_OK
, "text/html", 0));
4098 return (respond_http(client
, HTTP_NOT_FOUND
, NULL
, 0));
4102 if (!strcmp(client
->uri
, "/icon.png"))
4105 * Send PNG icon file.
4108 int fd
; /* Icon file */
4109 struct stat fileinfo
; /* Icon file information */
4110 char buffer
[4096]; /* Copy buffer */
4111 ssize_t bytes
; /* Bytes */
4113 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4114 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4116 if (!respond_http(client
, HTTP_OK
, "image/png", fileinfo
.st_size
))
4122 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4123 httpWrite2(&(client
->http
), buffer
, bytes
);
4125 httpFlushWrite(&(client
->http
));
4130 return (respond_http(client
, HTTP_NOT_FOUND
, NULL
, 0));
4132 else if (!strcmp(client
->uri
, "/"))
4135 * Show web status page...
4138 if (!respond_http(client
, HTTP_OK
, "text/html", 0))
4142 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
4143 "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
4146 "<title>%s</title>\n"
4147 "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
4148 "type=\"image/png\">\n"
4153 "<p>%s, %d job(s).</p>\n"
4156 client
->printer
->name
, client
->printer
->name
,
4157 client
->printer
->state
== IPP_PRINTER_IDLE
? "Idle" :
4158 client
->printer
->state
== IPP_PRINTER_PROCESSING
?
4159 "Printing" : "Stopped",
4160 cupsArrayCount(client
->printer
->jobs
));
4161 httpWrite2(&(client
->http
), "", 0);
4166 return (respond_http(client
, HTTP_NOT_FOUND
, NULL
, 0));
4170 if (client
->http
.data_remaining
< 0 ||
4171 (!client
->http
.fields
[HTTP_FIELD_CONTENT_LENGTH
][0] &&
4172 client
->http
.data_encoding
== HTTP_ENCODE_LENGTH
))
4175 * Negative content lengths are invalid...
4178 return (respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0));
4181 if (strcmp(client
->http
.fields
[HTTP_FIELD_CONTENT_TYPE
],
4185 * Not an IPP request...
4188 return (respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0));
4192 * Read the IPP request...
4195 client
->request
= ippNew();
4197 while ((state
= ippRead(&(client
->http
), client
->request
)) != IPP_DATA
)
4198 if (state
== IPP_ERROR
)
4200 fprintf(stderr
, "%s IPP read error (%s).\n", client
->http
.hostname
,
4201 cupsLastErrorString());
4202 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
4207 * Now that we have the IPP request, process the request...
4210 return (process_ipp(client
));
4213 break; /* Anti-compiler-warning-code */
4221 * 'process_ipp()' - Process an IPP request.
4224 static int /* O - 1 on success, 0 on error */
4225 process_ipp(_ipp_client_t
*client
) /* I - Client */
4227 ipp_tag_t group
; /* Current group tag */
4228 ipp_attribute_t
*attr
; /* Current attribute */
4229 ipp_attribute_t
*charset
; /* Character set attribute */
4230 ipp_attribute_t
*language
; /* Language attribute */
4231 ipp_attribute_t
*uri
; /* Printer URI attribute */
4234 debug_attributes("Request", client
->request
, 1);
4237 * First build an empty response message for this request...
4240 client
->operation_id
= client
->request
->request
.op
.operation_id
;
4241 client
->response
= ippNew();
4243 client
->response
->request
.status
.version
[0] =
4244 client
->request
->request
.op
.version
[0];
4245 client
->response
->request
.status
.version
[1] =
4246 client
->request
->request
.op
.version
[1];
4247 client
->response
->request
.status
.request_id
=
4248 client
->request
->request
.op
.request_id
;
4251 * Then validate the request header and required attributes...
4254 if (client
->request
->request
.any
.version
[0] < 1 ||
4255 client
->request
->request
.any
.version
[0] > 2)
4258 * Return an error, since we only support IPP 1.x and 2.x.
4261 respond_ipp(client
, IPP_VERSION_NOT_SUPPORTED
,
4262 "Bad request version number %d.%d.",
4263 client
->request
->request
.any
.version
[0],
4264 client
->request
->request
.any
.version
[1]);
4266 else if (client
->request
->request
.any
.request_id
<= 0)
4267 respond_ipp(client
, IPP_BAD_REQUEST
, "Bad request-id %d.",
4268 client
->request
->request
.any
.request_id
);
4269 else if (!client
->request
->attrs
)
4270 respond_ipp(client
, IPP_BAD_REQUEST
, "No attributes in request.");
4274 * Make sure that the attributes are provided in the correct order and
4275 * don't repeat groups...
4278 for (attr
= client
->request
->attrs
, group
= attr
->group_tag
;
4281 if (attr
->group_tag
< group
&& attr
->group_tag
!= IPP_TAG_ZERO
)
4284 * Out of order; return an error...
4287 respond_ipp(client
, IPP_BAD_REQUEST
,
4288 "Attribute groups are out of order (%x < %x).",
4289 attr
->group_tag
, group
);
4293 group
= attr
->group_tag
;
4298 * Then make sure that the first three attributes are:
4300 * attributes-charset
4301 * attributes-natural-language
4302 * printer-uri/job-uri
4305 attr
= client
->request
->attrs
;
4306 if (attr
&& attr
->name
&&
4307 !strcmp(attr
->name
, "attributes-charset") &&
4308 (attr
->value_tag
& IPP_TAG_MASK
) == IPP_TAG_CHARSET
)
4316 if (attr
&& attr
->name
&&
4317 !strcmp(attr
->name
, "attributes-natural-language") &&
4318 (attr
->value_tag
& IPP_TAG_MASK
) == IPP_TAG_LANGUAGE
)
4323 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
4324 IPP_TAG_URI
)) != NULL
)
4326 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
4327 IPP_TAG_URI
)) != NULL
)
4332 ippAddString(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
4333 "attributes-charset", NULL
,
4334 charset
? charset
->values
[0].string
.text
: "utf-8");
4336 ippAddString(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
4337 "attributes-natural-language", NULL
,
4338 language
? language
->values
[0].string
.text
: "en");
4341 _cups_strcasecmp(charset
->values
[0].string
.text
, "us-ascii") &&
4342 _cups_strcasecmp(charset
->values
[0].string
.text
, "utf-8"))
4345 * Bad character set...
4348 respond_ipp(client
, IPP_BAD_REQUEST
,
4349 "Unsupported character set \"%s\".",
4350 charset
->values
[0].string
.text
);
4352 else if (!charset
|| !language
|| !uri
)
4355 * Return an error, since attributes-charset,
4356 * attributes-natural-language, and printer-uri/job-uri are required
4357 * for all operations.
4360 respond_ipp(client
, IPP_BAD_REQUEST
, "Missing required attributes.");
4362 else if (strcmp(uri
->values
[0].string
.text
, client
->printer
->uri
) &&
4363 strncmp(uri
->values
[0].string
.text
, client
->printer
->uri
,
4364 client
->printer
->urilen
))
4366 respond_ipp(client
, IPP_NOT_FOUND
, "%s %s not found.", uri
->name
,
4367 uri
->values
[0].string
.text
);
4372 * Try processing the operation...
4375 if (client
->http
.expect
== HTTP_CONTINUE
)
4378 * Send 100-continue header...
4381 if (!respond_http(client
, HTTP_CONTINUE
, NULL
, 0))
4385 switch (client
->request
->request
.op
.operation_id
)
4387 case IPP_PRINT_JOB
:
4388 ipp_print_job(client
);
4391 case IPP_PRINT_URI
:
4392 ipp_print_uri(client
);
4395 case IPP_VALIDATE_JOB
:
4396 ipp_validate_job(client
);
4399 case IPP_CREATE_JOB
:
4400 ipp_create_job(client
);
4403 case IPP_SEND_DOCUMENT
:
4404 ipp_send_document(client
);
4408 ipp_send_uri(client
);
4411 case IPP_CANCEL_JOB
:
4412 ipp_cancel_job(client
);
4415 case IPP_GET_JOB_ATTRIBUTES
:
4416 ipp_get_job_attributes(client
);
4420 ipp_get_jobs(client
);
4423 case IPP_GET_PRINTER_ATTRIBUTES
:
4424 ipp_get_printer_attributes(client
);
4428 respond_ipp(client
, IPP_OPERATION_NOT_SUPPORTED
,
4429 "Operation not supported.");
4437 * Send the HTTP header and return...
4440 if (client
->http
.state
!= HTTP_POST_SEND
)
4441 httpFlush(&(client
->http
)); /* Flush trailing (junk) data */
4443 return (respond_http(client
, HTTP_OK
, "application/ipp",
4444 ippLength(client
->response
)));
4449 * 'process_job()' - Process a print job.
4452 static void * /* O - Thread exit status */
4453 process_job(_ipp_job_t
*job
) /* I - Job */
4455 job
->state
= IPP_JOB_PROCESSING
;
4456 job
->printer
->state
= IPP_PRINTER_PROCESSING
;
4461 job
->state
= IPP_JOB_CANCELED
;
4463 job
->state
= IPP_JOB_COMPLETED
;
4465 job
->completed
= time(NULL
);
4466 job
->printer
->state
= IPP_PRINTER_IDLE
;
4467 job
->printer
->active_job
= NULL
;
4475 * 'register_printer()' - Register a printer object via Bonjour.
4478 static int /* O - 1 on success, 0 on error */
4480 _ipp_printer_t
*printer
, /* I - Printer */
4481 const char *location
, /* I - Location */
4482 const char *make
, /* I - Manufacturer */
4483 const char *model
, /* I - Model name */
4484 const char *formats
, /* I - Supported formats */
4485 const char *adminurl
, /* I - Web interface URL */
4486 int color
, /* I - 1 = color, 0 = monochrome */
4487 int duplex
, /* I - 1 = duplex, 0 = simplex */
4488 const char *regtype
) /* I - Service type */
4490 DNSServiceErrorType error
; /* Error from Bonjour */
4491 char make_model
[256],/* Make and model together */
4492 product
[256]; /* Product string */
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
), "txtvers", 1, "1");
4504 TXTRecordSetValue(&(printer
->ipp_txt
), "qtotal", 1, "1");
4505 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 3, "ipp");
4506 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
4508 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
4510 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
4512 TXTRecordSetValue(&(printer
->ipp_txt
), "priority", 1, "0");
4513 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
4515 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
4517 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
4518 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
4519 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
4521 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
4523 TXTRecordSetValue(&(printer
->ipp_txt
), "air", 4, "none");
4526 * Create a shared service reference for Bonjour...
4529 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
4530 != kDNSServiceErr_NoError
)
4532 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
4537 * Register the _printer._tcp (LPD) service type with a port number of 0 to
4538 * defend our service name but not actually support LPD...
4541 printer
->printer_ref
= printer
->common_ref
;
4543 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
4544 kDNSServiceFlagsShareConnection
,
4545 0 /* interfaceIndex */, printer
->dnssd_name
,
4546 "_printer._tcp", NULL
/* domain */,
4547 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
4548 NULL
/* txtRecord */,
4549 (DNSServiceRegisterReply
)dnssd_callback
,
4550 printer
)) != kDNSServiceErr_NoError
)
4552 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
4553 printer
->dnssd_name
, error
);
4558 * Then register the _ipp._tcp (IPP) service type with the real port number to
4559 * advertise our IPP printer...
4562 printer
->ipp_ref
= printer
->common_ref
;
4564 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
4565 kDNSServiceFlagsShareConnection
,
4566 0 /* interfaceIndex */, printer
->dnssd_name
,
4567 regtype
, NULL
/* domain */,
4568 NULL
/* host */, htons(printer
->port
),
4569 TXTRecordGetLength(&(printer
->ipp_txt
)),
4570 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4571 (DNSServiceRegisterReply
)dnssd_callback
,
4572 printer
)) != kDNSServiceErr_NoError
)
4574 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4575 printer
->dnssd_name
, regtype
, error
);
4580 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
4581 * real port number to advertise our IPP printer...
4584 printer
->http_ref
= printer
->common_ref
;
4586 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
4587 kDNSServiceFlagsShareConnection
,
4588 0 /* interfaceIndex */, printer
->dnssd_name
,
4589 "_http._tcp,_printer", NULL
/* domain */,
4590 NULL
/* host */, htons(printer
->port
),
4591 0 /* txtLen */, NULL
, /* txtRecord */
4592 (DNSServiceRegisterReply
)dnssd_callback
,
4593 printer
)) != kDNSServiceErr_NoError
)
4595 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4596 printer
->dnssd_name
, regtype
, error
);
4602 #endif /* HAVE_DNSSD */
4606 * 'respond_http()' - Send a HTTP response.
4609 int /* O - 1 on success, 0 on failure */
4610 respond_http(_ipp_client_t
*client
, /* I - Client */
4611 http_status_t code
, /* I - HTTP status of response */
4612 const char *type
, /* I - MIME type of response */
4613 size_t length
) /* I - Length of response */
4615 char message
[1024]; /* Text message */
4618 fprintf(stderr
, "%s %s\n", client
->http
.hostname
, httpStatus(code
));
4620 if (code
== HTTP_CONTINUE
)
4623 * 100-continue doesn't send any headers...
4626 return (httpPrintf(&(client
->http
), "HTTP/%d.%d 100 Continue\r\n\r\n",
4627 client
->http
.version
/ 100,
4628 client
->http
.version
% 100) > 0);
4632 * Format an error message...
4635 if (!type
&& !length
&& code
!= HTTP_OK
)
4637 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
4639 type
= "text/plain";
4640 length
= strlen(message
);
4646 * Send the HTTP status header...
4649 httpFlushWrite(&(client
->http
));
4651 client
->http
.data_encoding
= HTTP_ENCODE_FIELDS
;
4653 if (httpPrintf(&(client
->http
), "HTTP/%d.%d %d %s\r\n", client
->http
.version
/ 100,
4654 client
->http
.version
% 100, code
, httpStatus(code
)) < 0)
4658 * Follow the header with the response fields...
4661 if (httpPrintf(&(client
->http
), "Date: %s\r\n", httpGetDateString(time(NULL
))) < 0)
4664 if (client
->http
.keep_alive
&& client
->http
.version
>= HTTP_1_0
)
4666 if (httpPrintf(&(client
->http
),
4667 "Connection: Keep-Alive\r\n"
4668 "Keep-Alive: timeout=10\r\n") < 0)
4672 if (code
== HTTP_METHOD_NOT_ALLOWED
|| client
->operation
== HTTP_OPTIONS
)
4674 if (httpPrintf(&(client
->http
), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
4680 if (!strcmp(type
, "text/html"))
4682 if (httpPrintf(&(client
->http
),
4683 "Content-Type: text/html; charset=utf-8\r\n") < 0)
4686 else if (httpPrintf(&(client
->http
), "Content-Type: %s\r\n", type
) < 0)
4690 if (length
== 0 && !message
[0])
4692 if (httpPrintf(&(client
->http
), "Transfer-Encoding: chunked\r\n\r\n") < 0)
4695 else if (httpPrintf(&(client
->http
), "Content-Length: " CUPS_LLFMT
"\r\n\r\n",
4696 CUPS_LLCAST length
) < 0)
4699 if (httpFlushWrite(&(client
->http
)) < 0)
4703 * Send the response data...
4709 * Send a plain text message.
4712 if (httpPrintf(&(client
->http
), "%s", message
) < 0)
4715 else if (client
->response
)
4718 * Send an IPP response...
4721 debug_attributes("Response", client
->response
, 2);
4723 client
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
4724 client
->http
.data_remaining
= (off_t
)ippLength(client
->response
);
4725 client
->response
->state
= IPP_IDLE
;
4727 if (ippWrite(&(client
->http
), client
->response
) != IPP_DATA
)
4731 client
->http
.data_encoding
= HTTP_ENCODE_CHUNKED
;
4734 * Flush the data and return...
4737 return (httpFlushWrite(&(client
->http
)) >= 0);
4742 * 'respond_ipp()' - Send an IPP response.
4746 respond_ipp(_ipp_client_t
*client
, /* I - Client */
4747 ipp_status_t status
, /* I - status-code */
4748 const char *message
, /* I - printf-style status-message */
4749 ...) /* I - Additional args as needed */
4751 va_list ap
; /* Pointer to additional args */
4752 char formatted
[1024]; /* Formatted errror message */
4755 client
->response
->request
.status
.status_code
= status
;
4757 if (!client
->response
->attrs
)
4759 ippAddString(client
->response
, IPP_TAG_OPERATION
,
4760 IPP_TAG_CHARSET
| IPP_TAG_COPY
, "attributes-charset", NULL
,
4762 ippAddString(client
->response
, IPP_TAG_OPERATION
,
4763 IPP_TAG_LANGUAGE
| IPP_TAG_COPY
, "attributes-natural-language",
4769 va_start(ap
, message
);
4770 vsnprintf(formatted
, sizeof(formatted
), message
, ap
);
4773 ippAddString(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
4774 "status-message", NULL
, formatted
);
4777 formatted
[0] = '\0';
4779 fprintf(stderr
, "%s %s %s (%s)\n", client
->http
.hostname
,
4780 ippOpString(client
->operation_id
), ippErrorString(status
), formatted
);
4785 * 'respond_unsupported()' - Respond with an unsupported attribute.
4789 respond_unsupported(
4790 _ipp_client_t
*client
, /* I - Client */
4791 ipp_attribute_t
*attr
) /* I - Atribute */
4793 ipp_attribute_t
*temp
; /* Copy of attribute */
4796 if (!client
->response
->attrs
)
4797 respond_ipp(client
, IPP_ATTRIBUTES
, "Unsupported %s %s%s value.",
4798 attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
4799 ippTagString(attr
->value_tag
));
4801 ippSetStatusCode(client
->response
, IPP_ATTRIBUTES
);
4803 temp
= ippCopyAttribute(client
->response
, attr
, 0);
4804 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
4809 * 'run_printer()' - Run the printer service.
4813 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
4815 int num_fds
; /* Number of file descriptors */
4816 struct pollfd polldata
[3]; /* poll() data */
4817 int timeout
; /* Timeout for poll() */
4818 _ipp_client_t
*client
; /* New client */
4822 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
4825 polldata
[0].fd
= printer
->ipv4
;
4826 polldata
[0].events
= POLLIN
;
4828 polldata
[1].fd
= printer
->ipv6
;
4829 polldata
[1].events
= POLLIN
;
4834 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
4835 polldata
[num_fds
++].events
= POLLIN
;
4836 #endif /* HAVE_DNSSD */
4839 * Loop until we are killed or have a hard error...
4844 if (cupsArrayCount(printer
->jobs
))
4849 if (poll(polldata
, num_fds
, timeout
) < 0 && errno
!= EINTR
)
4851 perror("poll() failed");
4855 if (polldata
[0].revents
& POLLIN
)
4857 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
4859 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4861 perror("Unable to create client thread");
4862 delete_client(client
);
4867 if (polldata
[1].revents
& POLLIN
)
4869 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
4871 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4873 perror("Unable to create client thread");
4874 delete_client(client
);
4880 if (polldata
[2].revents
& POLLIN
)
4881 DNSServiceProcessResult(printer
->common_ref
);
4882 #endif /* HAVE_DNSSD */
4885 * Clean out old jobs...
4888 clean_jobs(printer
);
4894 * 'usage()' - Show program usage.
4898 usage(int status
) /* O - Exit status */
4902 puts(CUPS_SVERSION
" - Copyright 2010 by Apple Inc. All rights reserved.");
4906 puts("Usage: ippserver [options] \"name\"");
4909 puts("-2 Supports 2-sided printing (default=1-sided)");
4910 puts("-M manufacturer Manufacturer name (default=Test)");
4911 printf("-d spool-directory Spool directory "
4912 "(default=/tmp/ippserver.%d)\n", (int)getpid());
4913 puts("-f type/subtype[,...] List of supported types "
4914 "(default=application/pdf,image/jpeg)");
4915 puts("-h Show program help");
4916 puts("-i iconfile.png PNG icon file (default=printer.png)");
4917 puts("-l location Location of printer (default=empty string)");
4918 puts("-m model Model name (default=Printer)");
4919 puts("-n hostname Hostname for printer");
4920 puts("-p port Port number (default=auto)");
4921 puts("-r regtype Bonjour service type (default=_ipp._tcp)");
4922 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
4923 puts("-v[vvv] Be (very) verbose");
4930 * 'valid_doc_attributes()' - Determine whether the document attributes are
4933 * When one or more document attributes are invalid, this function adds a
4934 * suitable response and attributes to the unsupported group.
4937 static int /* O - 1 if valid, 0 if not */
4938 valid_doc_attributes(
4939 _ipp_client_t
*client
) /* I - Client */
4941 int i
; /* Looping var */
4942 ipp_attribute_t
*attr
, /* Current attribute */
4943 *supported
; /* document-format-supported */
4944 const char *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 "none"...
4958 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_KEYWORD
||
4959 strcmp(attr
->values
[0].string
.text
, "none"))
4960 respond_unsupported(client
, attr
);
4962 fprintf(stderr
, "%s %s compression=\"%s\"\n",
4963 client
->http
.hostname
,
4964 ippOpString(client
->request
->request
.op
.operation_id
),
4965 attr
->values
[0].string
.text
);
4969 * Is it a format we support?
4972 if ((attr
= ippFindAttribute(client
->request
, "document-format",
4973 IPP_TAG_ZERO
)) != NULL
)
4975 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_MIMETYPE
)
4976 respond_unsupported(client
, attr
);
4979 format
= attr
->values
[0].string
.text
;
4981 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
4982 client
->http
.hostname
,
4983 ippOpString(client
->request
->request
.op
.operation_id
), format
);
4987 format
= "application/octet-stream";
4989 if (!strcmp(format
, "application/octet-stream") &&
4990 (client
->request
->request
.op
.operation_id
== IPP_PRINT_JOB
||
4991 client
->request
->request
.op
.operation_id
== IPP_SEND_DOCUMENT
))
4994 * Auto-type the file using the first 4 bytes of the file...
4997 unsigned char header
[4]; /* First 4 bytes of file */
4999 memset(header
, 0, sizeof(header
));
5000 _httpPeek(&(client
->http
), (char *)header
, sizeof(header
));
5002 if (!memcmp(header
, "%PDF", 4))
5003 format
= "application/pdf";
5004 else if (!memcmp(header
, "%!", 2))
5005 format
= "application/postscript";
5006 else if (!memcmp(header
, "\377\330\377", 3) &&
5007 header
[3] >= 0xe0 && header
[3] <= 0xef)
5008 format
= "image/jpeg";
5009 else if (!memcmp(header
, "\211PNG", 4))
5010 format
= "image/png";
5013 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
5014 client
->http
.hostname
,
5015 ippOpString(client
->request
->request
.op
.operation_id
), format
);
5018 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
5019 "document-format", NULL
, format
);
5022 _cupsStrFree(attr
->values
[0].string
.text
);
5023 attr
->values
[0].string
.text
= _cupsStrAlloc(format
);
5027 if (client
->request
->request
.op
.operation_id
!= IPP_CREATE_JOB
&&
5028 (supported
= ippFindAttribute(client
->printer
->attrs
,
5029 "document-format-supported",
5030 IPP_TAG_MIMETYPE
)) != NULL
)
5032 for (i
= 0; i
< supported
->num_values
; i
++)
5033 if (!_cups_strcasecmp(format
, supported
->values
[i
].string
.text
))
5036 if (i
>= supported
->num_values
&& attr
)
5037 respond_unsupported(client
, attr
);
5040 return (!client
->response
->attrs
||
5041 !client
->response
->attrs
->next
||
5042 !client
->response
->attrs
->next
->next
);
5047 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
5049 * When one or more job attributes are invalid, this function adds a suitable
5050 * response and attributes to the unsupported group.
5053 static int /* O - 1 if valid, 0 if not */
5054 valid_job_attributes(
5055 _ipp_client_t
*client
) /* I - Client */
5057 int i
; /* Looping var */
5058 ipp_attribute_t
*attr
, /* Current attribute */
5059 *supported
; /* xxx-supported attribute */
5063 * Check operation attributes...
5066 valid_doc_attributes(client
);
5069 * Check the various job template attributes...
5072 if ((attr
= ippFindAttribute(client
->request
, "copies",
5073 IPP_TAG_ZERO
)) != NULL
)
5075 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_INTEGER
||
5076 attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> 999)
5078 respond_unsupported(client
, attr
);
5082 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity",
5083 IPP_TAG_ZERO
)) != NULL
)
5085 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_BOOLEAN
)
5087 respond_unsupported(client
, attr
);
5091 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until",
5092 IPP_TAG_ZERO
)) != NULL
)
5094 if (attr
->num_values
!= 1 ||
5095 (attr
->value_tag
!= IPP_TAG_NAME
&&
5096 attr
->value_tag
!= IPP_TAG_NAMELANG
&&
5097 attr
->value_tag
!= IPP_TAG_KEYWORD
) ||
5098 strcmp(attr
->values
[0].string
.text
, "no-hold"))
5100 respond_unsupported(client
, attr
);
5104 if ((attr
= ippFindAttribute(client
->request
, "job-name",
5105 IPP_TAG_ZERO
)) != NULL
)
5107 if (attr
->num_values
!= 1 ||
5108 (attr
->value_tag
!= IPP_TAG_NAME
&&
5109 attr
->value_tag
!= IPP_TAG_NAMELANG
))
5111 respond_unsupported(client
, attr
);
5115 if ((attr
= ippFindAttribute(client
->request
, "job-priority",
5116 IPP_TAG_ZERO
)) != NULL
)
5118 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_INTEGER
||
5119 attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> 100)
5121 respond_unsupported(client
, attr
);
5125 if ((attr
= ippFindAttribute(client
->request
, "job-sheets",
5126 IPP_TAG_ZERO
)) != NULL
)
5128 if (attr
->num_values
!= 1 ||
5129 (attr
->value_tag
!= IPP_TAG_NAME
&&
5130 attr
->value_tag
!= IPP_TAG_NAMELANG
&&
5131 attr
->value_tag
!= IPP_TAG_KEYWORD
) ||
5132 strcmp(attr
->values
[0].string
.text
, "none"))
5134 respond_unsupported(client
, attr
);
5138 if ((attr
= ippFindAttribute(client
->request
, "media",
5139 IPP_TAG_ZERO
)) != NULL
)
5141 if (attr
->num_values
!= 1 ||
5142 (attr
->value_tag
!= IPP_TAG_NAME
&&
5143 attr
->value_tag
!= IPP_TAG_NAMELANG
&&
5144 attr
->value_tag
!= IPP_TAG_KEYWORD
))
5146 respond_unsupported(client
, attr
);
5151 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
5153 if (!strcmp(attr
->values
[0].string
.text
, media_supported
[i
]))
5156 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
5158 respond_unsupported(client
, attr
);
5163 if ((attr
= ippFindAttribute(client
->request
, "media-col",
5164 IPP_TAG_ZERO
)) != NULL
)
5166 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_BEGIN_COLLECTION
)
5168 respond_unsupported(client
, attr
);
5170 /* TODO: check for valid media-col */
5173 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling",
5174 IPP_TAG_ZERO
)) != NULL
)
5176 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_KEYWORD
||
5177 (strcmp(attr
->values
[0].string
.text
,
5178 "separate-documents-uncollated-copies") &&
5179 strcmp(attr
->values
[0].string
.text
,
5180 "separate-documents-collated-copies")))
5182 respond_unsupported(client
, attr
);
5186 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested",
5187 IPP_TAG_ZERO
)) != NULL
)
5189 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_ENUM
||
5190 attr
->values
[0].integer
< IPP_PORTRAIT
||
5191 attr
->values
[0].integer
> IPP_REVERSE_PORTRAIT
)
5193 respond_unsupported(client
, attr
);
5197 if ((attr
= ippFindAttribute(client
->request
, "page-ranges",
5198 IPP_TAG_ZERO
)) != NULL
)
5200 respond_unsupported(client
, attr
);
5203 if ((attr
= ippFindAttribute(client
->request
, "print-quality",
5204 IPP_TAG_ZERO
)) != NULL
)
5206 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_ENUM
||
5207 attr
->values
[0].integer
< IPP_QUALITY_DRAFT
||
5208 attr
->values
[0].integer
> IPP_QUALITY_HIGH
)
5210 respond_unsupported(client
, attr
);
5214 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution",
5215 IPP_TAG_ZERO
)) != NULL
)
5217 respond_unsupported(client
, attr
);
5220 if ((attr
= ippFindAttribute(client
->request
, "sides",
5221 IPP_TAG_ZERO
)) != NULL
)
5223 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_KEYWORD
)
5225 respond_unsupported(client
, attr
);
5228 if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides",
5229 IPP_TAG_KEYWORD
)) != NULL
)
5231 for (i
= 0; i
< supported
->num_values
; i
++)
5232 if (!strcmp(attr
->values
[0].string
.text
,
5233 supported
->values
[i
].string
.text
))
5236 if (i
>= supported
->num_values
)
5238 respond_unsupported(client
, attr
);
5243 respond_unsupported(client
, attr
);
5247 return (!client
->response
->attrs
||
5248 !client
->response
->attrs
->next
||
5249 !client
->response
->attrs
->next
->next
);