2 * "$Id: ipp.c 6755 2007-08-01 19:02:47Z mike $"
4 * IPP routines for the Common UNIX Printing System (CUPS) scheduler.
6 * Copyright 2007 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
9 * This file contains Kerberos support code, copyright 2006 by
12 * These coded instructions, statements, and computer programs are the
13 * property of Apple Inc. and are protected by Federal copyright
14 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
15 * which should have been included with this file. If this file is
16 * file is missing or damaged, see the license at "http://www.cups.org/".
20 * cupsdProcessIPPRequest() - Process an incoming IPP request.
21 * cupsdTimeoutJob() - Timeout a job waiting on job files.
22 * accept_jobs() - Accept print jobs to a printer.
23 * add_class() - Add a class to the system.
24 * add_file() - Add a file to a job.
25 * add_job() - Add a job to a print queue.
26 * add_job_state_reasons() - Add the "job-state-reasons" attribute based
27 * upon the job and printer state...
28 * add_job_subscriptions() - Add any subcriptions for a job.
29 * add_job_uuid() - Add job-uuid attribute to a job.
30 * add_printer() - Add a printer to the system.
31 * add_printer_state_reasons() - Add the "printer-state-reasons" attribute
32 * based upon the printer state...
33 * add_queued_job_count() - Add the "queued-job-count" attribute for
34 * apply_printer_defaults() - Apply printer default options to a job.
35 * authenticate_job() - Set job authentication info.
36 * cancel_all_jobs() - Cancel all print jobs.
37 * cancel_job() - Cancel a print job.
38 * cancel_subscription() - Cancel a subscription.
39 * check_quotas() - Check quotas for a printer and user.
40 * copy_attribute() - Copy a single attribute.
41 * copy_attrs() - Copy attributes from one request to another.
42 * copy_banner() - Copy a banner file to the requests directory
43 * for the specified job.
44 * copy_file() - Copy a PPD file or interface script...
45 * copy_model() - Copy a PPD model file, substituting default
47 * copy_job_attrs() - Copy job attributes.
48 * copy_printer_attrs() - Copy printer attributes.
49 * copy_subscription_attrs() - Copy subscription attributes.
50 * create_job() - Print a file to a printer or class.
51 * create_requested_array() - Create an array for the requested-attributes.
52 * create_subscription() - Create a notification subscription.
53 * delete_printer() - Remove a printer or class from the system.
54 * get_default() - Get the default destination.
55 * get_devices() - Get the list of available devices on the
57 * get_job_attrs() - Get job attributes.
58 * get_jobs() - Get a list of jobs for the specified printer.
59 * get_notifications() - Get events for a subscription.
60 * get_ppd() - Get a named PPD from the local system.
61 * get_ppds() - Get the list of PPD files on the local
63 * get_printer_attrs() - Get printer attributes.
64 * get_printers() - Get a list of printers.
65 * get_subscription_attrs() - Get subscription attributes.
66 * get_subscriptions() - Get subscriptions.
67 * get_username() - Get the username associated with a request.
68 * hold_job() - Hold a print job.
69 * move_job() - Move a job to a new destination.
70 * ppd_parse_line() - Parse a PPD default line.
71 * print_job() - Print a file to a printer or class.
72 * read_ps_line() - Read a line from a PS file...
73 * read_ps_job_ticket() - Reads a job ticket embedded in a PS file.
74 * reject_jobs() - Reject print jobs to a printer.
75 * release_job() - Release a held print job.
76 * restart_job() - Restart an old print job.
77 * save_auth_info() - Save authentication information for a job.
78 * save_krb5_creds() - Save Kerberos credentials for a job.
79 * send_document() - Send a file to a printer or class.
80 * send_http_error() - Send a HTTP error back to the IPP client.
81 * send_ipp_status() - Send a status back to the IPP client.
82 * set_default() - Set the default destination...
83 * set_job_attrs() - Set job attributes.
84 * set_printer_defaults() - Set printer default options from a request.
85 * start_printer() - Start a printer.
86 * stop_printer() - Stop a printer.
87 * url_encode_attr() - URL-encode a string attribute.
88 * url_encode_string() - URL-encode a string.
89 * user_allowed() - See if a user is allowed to print to a queue.
90 * validate_job() - Validate printer options and destination.
91 * validate_name() - Make sure the printer name only contains
93 * validate_user() - Validate the user for the request.
97 * Include necessary headers...
104 #endif /* HAVE_KRB5_H */
108 #endif /* HAVE_LIBPAPER */
111 # ifdef HAVE_MEMBERSHIP_H
112 # include <membership.h>
113 # endif /* HAVE_MEMBERSHIP_H */
114 # ifdef HAVE_MEMBERSHIPPRIV_H
115 # include <membershipPriv.h>
117 extern int mbr_user_name_to_uuid(const char* name
, uuid_t uu
);
118 extern int mbr_group_name_to_uuid(const char* name
, uuid_t uu
);
119 extern int mbr_check_membership_by_id(uuid_t user
, gid_t group
, int* ismember
);
120 # endif /* HAVE_MEMBERSHIPPRIV_H */
121 #endif /* __APPLE__ */
128 static void accept_jobs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
129 static void add_class(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
130 static int add_file(cupsd_client_t
*con
, cupsd_job_t
*job
,
131 mime_type_t
*filetype
, int compression
);
132 static cupsd_job_t
*add_job(cupsd_client_t
*con
, cupsd_printer_t
*printer
,
133 mime_type_t
*filetype
);
134 static void add_job_state_reasons(cupsd_client_t
*con
, cupsd_job_t
*job
);
135 static void add_job_subscriptions(cupsd_client_t
*con
, cupsd_job_t
*job
);
136 static void add_job_uuid(cupsd_client_t
*con
, cupsd_job_t
*job
);
137 static void add_printer(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
138 static void add_printer_state_reasons(cupsd_client_t
*con
,
140 static void add_queued_job_count(cupsd_client_t
*con
, cupsd_printer_t
*p
);
141 static void apply_printer_defaults(cupsd_printer_t
*printer
,
143 static void authenticate_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
144 static void cancel_all_jobs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
145 static void cancel_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
146 static void cancel_subscription(cupsd_client_t
*con
, int id
);
147 static int check_quotas(cupsd_client_t
*con
, cupsd_printer_t
*p
);
148 static ipp_attribute_t
*copy_attribute(ipp_t
*to
, ipp_attribute_t
*attr
,
150 static void copy_attrs(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
151 ipp_tag_t group
, int quickcopy
);
152 static int copy_banner(cupsd_client_t
*con
, cupsd_job_t
*job
,
154 static int copy_file(const char *from
, const char *to
);
155 static int copy_model(cupsd_client_t
*con
, const char *from
,
157 static void copy_job_attrs(cupsd_client_t
*con
,
160 static void copy_printer_attrs(cupsd_client_t
*con
,
161 cupsd_printer_t
*printer
,
163 static void copy_subscription_attrs(cupsd_client_t
*con
,
164 cupsd_subscription_t
*sub
,
166 static void create_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
167 static cups_array_t
*create_requested_array(ipp_t
*request
);
168 static void create_subscription(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
169 static void delete_printer(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
170 static void get_default(cupsd_client_t
*con
);
171 static void get_devices(cupsd_client_t
*con
);
172 static void get_jobs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
173 static void get_job_attrs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
174 static void get_notifications(cupsd_client_t
*con
);
175 static void get_ppd(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
176 static void get_ppds(cupsd_client_t
*con
);
177 static void get_printers(cupsd_client_t
*con
, int type
);
178 static void get_printer_attrs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
179 static void get_subscription_attrs(cupsd_client_t
*con
, int sub_id
);
180 static void get_subscriptions(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
181 static const char *get_username(cupsd_client_t
*con
);
182 static void hold_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
183 static void move_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
184 static int ppd_parse_line(const char *line
, char *option
, int olen
,
185 char *choice
, int clen
);
186 static void print_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
187 static void read_ps_job_ticket(cupsd_client_t
*con
);
188 static void reject_jobs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
189 static void release_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
190 static void renew_subscription(cupsd_client_t
*con
, int sub_id
);
191 static void restart_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
192 static void save_auth_info(cupsd_client_t
*con
, cupsd_job_t
*job
,
193 ipp_attribute_t
*auth_info
);
194 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
195 static void save_krb5_creds(cupsd_client_t
*con
, cupsd_job_t
*job
);
196 #endif /* HAVE_GSSAPI && HAVE_KRB5_H */
197 static void send_document(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
198 static void send_http_error(cupsd_client_t
*con
, http_status_t status
,
199 cupsd_printer_t
*printer
);
200 static void send_ipp_status(cupsd_client_t
*con
, ipp_status_t status
,
201 const char *message
, ...)
203 __attribute__ ((__format__ (__printf__
, 3, 4)))
204 # endif /* __GNUC__ */
206 static void set_default(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
207 static void set_job_attrs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
208 static void set_printer_defaults(cupsd_client_t
*con
,
209 cupsd_printer_t
*printer
);
210 static void start_printer(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
211 static void stop_printer(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
212 static void url_encode_attr(ipp_attribute_t
*attr
, char *buffer
,
214 static char *url_encode_string(const char *s
, char *buffer
, int bufsize
);
215 static int user_allowed(cupsd_printer_t
*p
, const char *username
);
216 static void validate_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
217 static int validate_name(const char *name
);
218 static int validate_user(cupsd_job_t
*job
, cupsd_client_t
*con
,
219 const char *owner
, char *username
,
224 * 'cupsdProcessIPPRequest()' - Process an incoming IPP request.
227 int /* O - 1 on success, 0 on failure */
228 cupsdProcessIPPRequest(
229 cupsd_client_t
*con
) /* I - Client connection */
231 ipp_tag_t group
; /* Current group tag */
232 ipp_attribute_t
*attr
; /* Current attribute */
233 ipp_attribute_t
*charset
; /* Character set attribute */
234 ipp_attribute_t
*language
; /* Language attribute */
235 ipp_attribute_t
*uri
; /* Printer URI attribute */
236 ipp_attribute_t
*username
; /* requesting-user-name attr */
237 int sub_id
; /* Subscription ID */
240 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
241 "cupsdProcessIPPRequest(%p[%d]): operation_id = %04x",
242 con
, con
->http
.fd
, con
->request
->request
.op
.operation_id
);
245 * First build an empty response message for this request...
248 con
->response
= ippNew();
250 con
->response
->request
.status
.version
[0] =
251 con
->request
->request
.op
.version
[0];
252 con
->response
->request
.status
.version
[1] =
253 con
->request
->request
.op
.version
[1];
254 con
->response
->request
.status
.request_id
=
255 con
->request
->request
.op
.request_id
;
258 * Then validate the request header and required attributes...
261 if (con
->request
->request
.any
.version
[0] != 1)
264 * Return an error, since we only support IPP 1.x.
267 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
,
268 "%04X %s Bad request version number %d.%d",
269 IPP_VERSION_NOT_SUPPORTED
, con
->http
.hostname
,
270 con
->request
->request
.any
.version
[0],
271 con
->request
->request
.any
.version
[1]);
273 send_ipp_status(con
, IPP_VERSION_NOT_SUPPORTED
,
274 _("Bad request version number %d.%d!"),
275 con
->request
->request
.any
.version
[0],
276 con
->request
->request
.any
.version
[1]);
278 else if (!con
->request
->attrs
)
280 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
,
281 "%04X %s No attributes in request",
282 IPP_BAD_REQUEST
, con
->http
.hostname
);
284 send_ipp_status(con
, IPP_BAD_REQUEST
, _("No attributes in request!"));
289 * Make sure that the attributes are provided in the correct order and
290 * don't repeat groups...
293 for (attr
= con
->request
->attrs
, group
= attr
->group_tag
;
296 if (attr
->group_tag
< group
&& attr
->group_tag
!= IPP_TAG_ZERO
)
299 * Out of order; return an error...
302 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
,
303 "%04X %s Attribute groups are out of order",
304 IPP_BAD_REQUEST
, con
->http
.hostname
);
306 send_ipp_status(con
, IPP_BAD_REQUEST
,
307 _("Attribute groups are out of order (%x < %x)!"),
308 attr
->group_tag
, group
);
312 group
= attr
->group_tag
;
317 * Then make sure that the first three attributes are:
320 * attributes-natural-language
321 * printer-uri/job-uri
324 attr
= con
->request
->attrs
;
325 if (attr
&& !strcmp(attr
->name
, "attributes-charset") &&
326 (attr
->value_tag
& IPP_TAG_MASK
) == IPP_TAG_CHARSET
)
334 if (attr
&& !strcmp(attr
->name
, "attributes-natural-language") &&
335 (attr
->value_tag
& IPP_TAG_MASK
) == IPP_TAG_LANGUAGE
)
340 if ((attr
= ippFindAttribute(con
->request
, "printer-uri",
341 IPP_TAG_URI
)) != NULL
)
343 else if ((attr
= ippFindAttribute(con
->request
, "job-uri",
344 IPP_TAG_URI
)) != NULL
)
346 else if (con
->request
->request
.op
.operation_id
== CUPS_GET_PPD
)
347 uri
= ippFindAttribute(con
->request
, "ppd-name", IPP_TAG_NAME
);
352 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
353 "attributes-charset", NULL
,
354 charset
->values
[0].string
.text
);
356 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
357 "attributes-charset", NULL
, DefaultCharset
);
360 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
361 "attributes-natural-language", NULL
,
362 language
->values
[0].string
.text
);
364 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
365 "attributes-natural-language", NULL
, DefaultLanguage
);
367 if (!charset
|| !language
||
369 con
->request
->request
.op
.operation_id
!= CUPS_GET_DEFAULT
&&
370 con
->request
->request
.op
.operation_id
!= CUPS_GET_PRINTERS
&&
371 con
->request
->request
.op
.operation_id
!= CUPS_GET_CLASSES
&&
372 con
->request
->request
.op
.operation_id
!= CUPS_GET_DEVICES
&&
373 con
->request
->request
.op
.operation_id
!= CUPS_GET_PPDS
))
376 * Return an error, since attributes-charset,
377 * attributes-natural-language, and printer-uri/job-uri are required
378 * for all operations.
383 cupsdLogMessage(CUPSD_LOG_ERROR
,
384 "Missing attributes-charset attribute!");
386 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
,
387 "%04X %s Missing attributes-charset attribute",
388 IPP_BAD_REQUEST
, con
->http
.hostname
);
393 cupsdLogMessage(CUPSD_LOG_ERROR
,
394 "Missing attributes-natural-language attribute!");
396 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
,
397 "%04X %s Missing attributes-natural-language attribute",
398 IPP_BAD_REQUEST
, con
->http
.hostname
);
403 cupsdLogMessage(CUPSD_LOG_ERROR
,
404 "Missing printer-uri, job-uri, or ppd-name "
407 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
,
408 "%04X %s Missing printer-uri, job-uri, or ppd-name "
409 "attribute", IPP_BAD_REQUEST
, con
->http
.hostname
);
412 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Request attributes follow...");
414 for (attr
= con
->request
->attrs
; attr
; attr
= attr
->next
)
415 cupsdLogMessage(CUPSD_LOG_DEBUG
,
416 "attr \"%s\": group_tag = %x, value_tag = %x",
417 attr
->name
? attr
->name
: "(null)", attr
->group_tag
,
420 cupsdLogMessage(CUPSD_LOG_DEBUG
, "End of attributes...");
422 send_ipp_status(con
, IPP_BAD_REQUEST
,
423 _("Missing required attributes!"));
428 * OK, all the checks pass so far; make sure requesting-user-name is
429 * not "root" from a remote host...
432 if ((username
= ippFindAttribute(con
->request
, "requesting-user-name",
433 IPP_TAG_NAME
)) != NULL
)
436 * Check for root user...
439 if (!strcmp(username
->values
[0].string
.text
, "root") &&
440 strcasecmp(con
->http
.hostname
, "localhost") &&
441 strcmp(con
->username
, "root"))
444 * Remote unauthenticated user masquerading as local root...
447 _cupsStrFree(username
->values
[0].string
.text
);
448 username
->values
[0].string
.text
= _cupsStrAlloc(RemoteRoot
);
452 if ((attr
= ippFindAttribute(con
->request
, "notify-subscription-id",
453 IPP_TAG_INTEGER
)) != NULL
)
454 sub_id
= attr
->values
[0].integer
;
459 * Then try processing the operation...
463 cupsdLogMessage(CUPSD_LOG_DEBUG
, "%s %s",
464 ippOpString(con
->request
->request
.op
.operation_id
),
465 uri
->values
[0].string
.text
);
467 cupsdLogMessage(CUPSD_LOG_DEBUG
, "%s",
468 ippOpString(con
->request
->request
.op
.operation_id
));
470 switch (con
->request
->request
.op
.operation_id
)
476 case IPP_VALIDATE_JOB
:
477 validate_job(con
, uri
);
480 case IPP_CREATE_JOB
:
481 create_job(con
, uri
);
484 case IPP_SEND_DOCUMENT
:
485 send_document(con
, uri
);
488 case IPP_CANCEL_JOB
:
489 cancel_job(con
, uri
);
492 case IPP_GET_JOB_ATTRIBUTES
:
493 get_job_attrs(con
, uri
);
500 case IPP_GET_PRINTER_ATTRIBUTES
:
501 get_printer_attrs(con
, uri
);
508 case IPP_RELEASE_JOB
:
509 release_job(con
, uri
);
512 case IPP_RESTART_JOB
:
513 restart_job(con
, uri
);
516 case IPP_PAUSE_PRINTER
:
517 stop_printer(con
, uri
);
520 case IPP_RESUME_PRINTER
:
521 start_printer(con
, uri
);
524 case IPP_PURGE_JOBS
:
525 cancel_all_jobs(con
, uri
);
528 case IPP_SET_JOB_ATTRIBUTES
:
529 set_job_attrs(con
, uri
);
532 case CUPS_GET_DEFAULT
:
536 case CUPS_GET_PRINTERS
:
537 get_printers(con
, 0);
540 case CUPS_GET_CLASSES
:
541 get_printers(con
, CUPS_PRINTER_CLASS
);
544 case CUPS_ADD_PRINTER
:
545 add_printer(con
, uri
);
548 case CUPS_DELETE_PRINTER
:
549 delete_printer(con
, uri
);
552 case CUPS_ADD_CLASS
:
556 case CUPS_DELETE_CLASS
:
557 delete_printer(con
, uri
);
560 case CUPS_ACCEPT_JOBS
:
561 case IPP_ENABLE_PRINTER
:
562 accept_jobs(con
, uri
);
565 case CUPS_REJECT_JOBS
:
566 case IPP_DISABLE_PRINTER
:
567 reject_jobs(con
, uri
);
570 case CUPS_SET_DEFAULT
:
571 set_default(con
, uri
);
574 case CUPS_GET_DEVICES
:
590 case CUPS_AUTHENTICATE_JOB
:
591 authenticate_job(con
, uri
);
594 case IPP_CREATE_PRINTER_SUBSCRIPTION
:
595 case IPP_CREATE_JOB_SUBSCRIPTION
:
596 create_subscription(con
, uri
);
599 case IPP_GET_SUBSCRIPTION_ATTRIBUTES
:
600 get_subscription_attrs(con
, sub_id
);
603 case IPP_GET_SUBSCRIPTIONS
:
604 get_subscriptions(con
, uri
);
607 case IPP_RENEW_SUBSCRIPTION
:
608 renew_subscription(con
, sub_id
);
611 case IPP_CANCEL_SUBSCRIPTION
:
612 cancel_subscription(con
, sub_id
);
615 case IPP_GET_NOTIFICATIONS
:
616 get_notifications(con
);
620 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
,
621 "%04X %s Operation %04X (%s) not supported",
622 IPP_OPERATION_NOT_SUPPORTED
, con
->http
.hostname
,
623 con
->request
->request
.op
.operation_id
,
624 ippOpString(con
->request
->request
.op
.operation_id
));
626 send_ipp_status(con
, IPP_OPERATION_NOT_SUPPORTED
,
627 _("%s not supported!"),
629 con
->request
->request
.op
.operation_id
));
639 * Sending data from the scheduler...
642 cupsdLogMessage(CUPSD_LOG_DEBUG
,
643 "cupsdProcessIPPRequest: %d status_code=%x (%s)",
644 con
->http
.fd
, con
->response
->request
.status
.status_code
,
645 ippErrorString(con
->response
->request
.status
.status_code
));
647 if (cupsdSendHeader(con
, HTTP_OK
, "application/ipp", AUTH_NONE
))
649 #ifdef CUPSD_USE_CHUNKING
651 * Because older versions of CUPS (1.1.17 and older) and some IPP
652 * clients do not implement chunking properly, we cannot use
653 * chunking by default. This may become the default in future
654 * CUPS releases, or we might add a configuration directive for
658 if (con
->http
.version
== HTTP_1_1
)
660 if (httpPrintf(HTTP(con
), "Transfer-Encoding: chunked\r\n\r\n") < 0)
663 if (cupsdFlushHeader(con
) < 0)
666 con
->http
.data_encoding
= HTTP_ENCODE_CHUNKED
;
669 #endif /* CUPSD_USE_CHUNKING */
671 size_t length
; /* Length of response */
674 length
= ippLength(con
->response
);
676 if (con
->file
>= 0 && !con
->pipe_pid
)
678 struct stat fileinfo
; /* File information */
681 if (!fstat(con
->file
, &fileinfo
))
682 length
+= fileinfo
.st_size
;
685 if (httpPrintf(HTTP(con
), "Content-Length: " CUPS_LLFMT
"\r\n\r\n",
686 CUPS_LLCAST length
) < 0)
689 if (cupsdFlushHeader(con
) < 0)
692 con
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
693 con
->http
.data_remaining
= length
;
695 if (con
->http
.data_remaining
<= INT_MAX
)
696 con
->http
._data_remaining
= con
->http
.data_remaining
;
698 con
->http
._data_remaining
= INT_MAX
;
701 cupsdAddSelect(con
->http
.fd
, (cupsd_selfunc_t
)cupsdReadClient
,
702 (cupsd_selfunc_t
)cupsdWriteClient
, con
);
705 * Tell the caller the response header was sent successfully...
713 * Tell the caller the response header could not be sent...
722 * Sending data from a subprocess like cups-deviced; tell the caller
723 * everything is A-OK so far...
732 * 'cupsdTimeoutJob()' - Timeout a job waiting on job files.
736 cupsdTimeoutJob(cupsd_job_t
*job
) /* I - Job to timeout */
738 cupsd_printer_t
*printer
; /* Destination printer or class */
739 ipp_attribute_t
*attr
; /* job-sheets attribute */
740 int kbytes
; /* Kilobytes in banner */
743 job
->pending_timeout
= 0;
746 * See if we need to add the ending sheet...
749 printer
= cupsdFindDest(job
->dest
);
750 attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_NAME
);
753 !(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)) &&
754 attr
&& attr
->num_values
> 1)
760 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job %d] Adding end banner page \"%s\".",
761 job
->id
, attr
->values
[1].string
.text
);
763 kbytes
= copy_banner(NULL
, job
, attr
->values
[1].string
.text
);
765 cupsdUpdateQuota(printer
, job
->username
, 0, kbytes
);
771 * 'accept_jobs()' - Accept print jobs to a printer.
775 accept_jobs(cupsd_client_t
*con
, /* I - Client connection */
776 ipp_attribute_t
*uri
) /* I - Printer or class URI */
778 http_status_t status
; /* Policy status */
779 cups_ptype_t dtype
; /* Destination type (printer/class) */
780 cupsd_printer_t
*printer
; /* Printer data */
783 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "accept_jobs(%p[%d], %s)", con
,
784 con
->http
.fd
, uri
->values
[0].string
.text
);
787 * Is the destination valid?
790 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
796 send_ipp_status(con
, IPP_NOT_FOUND
,
797 _("The printer or class was not found."));
805 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
807 send_http_error(con
, status
, printer
);
812 * Accept jobs sent to the printer...
815 printer
->accepting
= 1;
816 printer
->state_message
[0] = '\0';
818 cupsdAddPrinterHistory(printer
);
820 if (dtype
& CUPS_PRINTER_CLASS
)
822 cupsdSaveAllClasses();
824 cupsdLogMessage(CUPSD_LOG_INFO
, "Class \"%s\" now accepting jobs (\"%s\").",
825 printer
->name
, get_username(con
));
829 cupsdSaveAllPrinters();
831 cupsdLogMessage(CUPSD_LOG_INFO
,
832 "Printer \"%s\" now accepting jobs (\"%s\").",
833 printer
->name
, get_username(con
));
837 * Everything was ok, so return OK status...
840 con
->response
->request
.status
.status_code
= IPP_OK
;
845 * 'add_class()' - Add a class to the system.
849 add_class(cupsd_client_t
*con
, /* I - Client connection */
850 ipp_attribute_t
*uri
) /* I - URI of class */
852 http_status_t status
; /* Policy status */
853 int i
; /* Looping var */
854 char method
[HTTP_MAX_URI
], /* Method portion of URI */
855 username
[HTTP_MAX_URI
], /* Username portion of URI */
856 host
[HTTP_MAX_URI
], /* Host portion of URI */
857 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
858 int port
; /* Port portion of URI */
859 cupsd_printer_t
*pclass
, /* Class */
860 *member
; /* Member printer/class */
861 cups_ptype_t dtype
; /* Destination type */
862 ipp_attribute_t
*attr
; /* Printer attribute */
863 int modify
; /* Non-zero if we just modified */
864 char newname
[IPP_MAX_NAME
]; /* New class name */
865 int need_restart_job
; /* Need to restart job? */
868 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "add_class(%p[%d], %s)", con
,
869 con
->http
.fd
, uri
->values
[0].string
.text
);
872 * Do we have a valid URI?
875 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, method
,
876 sizeof(method
), username
, sizeof(username
), host
,
877 sizeof(host
), &port
, resource
, sizeof(resource
));
880 if (strncmp(resource
, "/classes/", 9) || strlen(resource
) == 9)
883 * No, return an error...
886 send_ipp_status(con
, IPP_BAD_REQUEST
,
887 _("The printer-uri must be of the form "
888 "\"ipp://HOSTNAME/classes/CLASSNAME\"."));
893 * Do we have a valid printer name?
896 if (!validate_name(resource
+ 9))
899 * No, return an error...
902 send_ipp_status(con
, IPP_BAD_REQUEST
,
903 _("The printer-uri \"%s\" contains invalid characters."),
904 uri
->values
[0].string
.text
);
912 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
914 send_http_error(con
, status
, NULL
);
919 * See if the class already exists; if not, create a new class...
922 if ((pclass
= cupsdFindClass(resource
+ 9)) == NULL
)
925 * Class doesn't exist; see if we have a printer of the same name...
928 if ((pclass
= cupsdFindPrinter(resource
+ 9)) != NULL
&&
929 !(pclass
->type
& CUPS_PRINTER_DISCOVERED
))
932 * Yes, return an error...
935 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
936 _("A printer named \"%s\" already exists!"),
942 * No, add the pclass...
945 pclass
= cupsdAddClass(resource
+ 9);
948 else if (pclass
->type
& CUPS_PRINTER_IMPLICIT
)
951 * Rename the implicit class to "AnyClass" or remove it...
954 if (ImplicitAnyClasses
)
956 snprintf(newname
, sizeof(newname
), "Any%s", resource
+ 9);
957 cupsdRenamePrinter(pclass
, newname
);
960 cupsdDeletePrinter(pclass
, 1);
963 * Add the class as a new local class...
966 pclass
= cupsdAddClass(resource
+ 9);
969 else if (pclass
->type
& CUPS_PRINTER_DISCOVERED
)
972 * Rename the remote class to "Class"...
975 snprintf(newname
, sizeof(newname
), "%s@%s", resource
+ 9, pclass
->hostname
);
976 cupsdRenamePrinter(pclass
, newname
);
979 * Add the class as a new local class...
982 pclass
= cupsdAddClass(resource
+ 9);
989 * Look for attributes and copy them over as needed...
992 need_restart_job
= 0;
994 if ((attr
= ippFindAttribute(con
->request
, "printer-location",
995 IPP_TAG_TEXT
)) != NULL
)
996 cupsdSetString(&pclass
->location
, attr
->values
[0].string
.text
);
998 if ((attr
= ippFindAttribute(con
->request
, "printer-info",
999 IPP_TAG_TEXT
)) != NULL
)
1000 cupsdSetString(&pclass
->info
, attr
->values
[0].string
.text
);
1002 if ((attr
= ippFindAttribute(con
->request
, "printer-is-accepting-jobs",
1003 IPP_TAG_BOOLEAN
)) != NULL
)
1005 cupsdLogMessage(CUPSD_LOG_INFO
,
1006 "Setting %s printer-is-accepting-jobs to %d (was %d.)",
1007 pclass
->name
, attr
->values
[0].boolean
, pclass
->accepting
);
1009 pclass
->accepting
= attr
->values
[0].boolean
;
1010 cupsdAddPrinterHistory(pclass
);
1013 if ((attr
= ippFindAttribute(con
->request
, "printer-is-shared",
1014 IPP_TAG_BOOLEAN
)) != NULL
)
1016 if (pclass
->shared
&& !attr
->values
[0].boolean
)
1017 cupsdDeregisterPrinter(pclass
, 1);
1019 cupsdLogMessage(CUPSD_LOG_INFO
,
1020 "Setting %s printer-is-shared to %d (was %d.)",
1021 pclass
->name
, attr
->values
[0].boolean
, pclass
->shared
);
1023 pclass
->shared
= attr
->values
[0].boolean
;
1026 if ((attr
= ippFindAttribute(con
->request
, "printer-state",
1027 IPP_TAG_ENUM
)) != NULL
)
1029 if (attr
->values
[0].integer
!= IPP_PRINTER_IDLE
&&
1030 attr
->values
[0].integer
!= IPP_PRINTER_STOPPED
)
1032 send_ipp_status(con
, IPP_BAD_REQUEST
,
1033 _("Attempt to set %s printer-state to bad value %d!"),
1034 pclass
->name
, attr
->values
[0].integer
);
1038 cupsdLogMessage(CUPSD_LOG_INFO
, "Setting %s printer-state to %d (was %d.)",
1039 pclass
->name
, attr
->values
[0].integer
, pclass
->state
);
1041 if (attr
->values
[0].integer
== IPP_PRINTER_STOPPED
)
1042 cupsdStopPrinter(pclass
, 0);
1045 cupsdSetPrinterState(pclass
, (ipp_pstate_t
)(attr
->values
[0].integer
), 0);
1046 need_restart_job
= 1;
1049 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
1050 IPP_TAG_TEXT
)) != NULL
)
1052 strlcpy(pclass
->state_message
, attr
->values
[0].string
.text
,
1053 sizeof(pclass
->state_message
));
1054 cupsdAddPrinterHistory(pclass
);
1056 if ((attr
= ippFindAttribute(con
->request
, "member-uris",
1057 IPP_TAG_URI
)) != NULL
)
1060 * Clear the printer array as needed...
1063 need_restart_job
= 1;
1065 if (pclass
->num_printers
> 0)
1067 free(pclass
->printers
);
1068 pclass
->num_printers
= 0;
1072 * Add each printer or class that is listed...
1075 for (i
= 0; i
< attr
->num_values
; i
++)
1078 * Search for the printer or class URI...
1081 if (!cupsdValidateDest(attr
->values
[i
].string
.text
, &dtype
, &member
))
1087 send_ipp_status(con
, IPP_NOT_FOUND
,
1088 _("The printer or class was not found."));
1093 * Add it to the class...
1096 cupsdAddPrinterToClass(pclass
, member
);
1100 set_printer_defaults(con
, pclass
);
1102 if ((attr
= ippFindAttribute(con
->request
, "auth-info-required",
1103 IPP_TAG_KEYWORD
)) != NULL
)
1104 cupsdSetAuthInfoRequired(pclass
, NULL
, attr
);
1107 * Update the printer class attributes and return...
1110 cupsdSetPrinterAttrs(pclass
);
1111 cupsdSaveAllClasses();
1113 if (need_restart_job
&& pclass
->job
)
1118 * Stop the current job and then restart it below...
1121 job
= (cupsd_job_t
*)pclass
->job
;
1123 cupsdStopJob(job
, 1);
1125 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1126 job
->state_value
= IPP_JOB_PENDING
;
1129 if (need_restart_job
)
1132 cupsdWritePrintcap();
1136 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED
, pclass
, NULL
,
1137 "Class \"%s\" modified by \"%s\".", pclass
->name
,
1140 cupsdLogMessage(CUPSD_LOG_INFO
, "Class \"%s\" modified by \"%s\".",
1141 pclass
->name
, get_username(con
));
1145 cupsdAddPrinterHistory(pclass
);
1147 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED
, pclass
, NULL
,
1148 "New class \"%s\" added by \"%s\".", pclass
->name
,
1151 cupsdLogMessage(CUPSD_LOG_INFO
, "New class \"%s\" added by \"%s\".",
1152 pclass
->name
, get_username(con
));
1155 con
->response
->request
.status
.status_code
= IPP_OK
;
1160 * 'add_file()' - Add a file to a job.
1163 static int /* O - 0 on success, -1 on error */
1164 add_file(cupsd_client_t
*con
, /* I - Connection to client */
1165 cupsd_job_t
*job
, /* I - Job to add to */
1166 mime_type_t
*filetype
, /* I - Type of file */
1167 int compression
) /* I - Compression */
1169 mime_type_t
**filetypes
; /* New filetypes array... */
1170 int *compressions
; /* New compressions array... */
1173 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1174 "add_file(con=%p[%d], job=%d, filetype=%s/%s, "
1175 "compression=%d)", con
, con
? con
->http
.fd
: -1, job
->id
,
1176 filetype
->super
, filetype
->type
, compression
);
1179 * Add the file to the job...
1182 if (job
->num_files
== 0)
1184 compressions
= (int *)malloc(sizeof(int));
1185 filetypes
= (mime_type_t
**)malloc(sizeof(mime_type_t
*));
1189 compressions
= (int *)realloc(job
->compressions
,
1190 (job
->num_files
+ 1) * sizeof(int));
1191 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
1192 (job
->num_files
+ 1) *
1193 sizeof(mime_type_t
*));
1196 if (!compressions
|| !filetypes
)
1198 cupsdCancelJob(job
, 1, IPP_JOB_ABORTED
);
1201 send_ipp_status(con
, IPP_INTERNAL_ERROR
,
1202 _("Unable to allocate memory for file types!"));
1207 job
->compressions
= compressions
;
1208 job
->compressions
[job
->num_files
] = compression
;
1209 job
->filetypes
= filetypes
;
1210 job
->filetypes
[job
->num_files
] = filetype
;
1219 * 'add_job()' - Add a job to a print queue.
1222 static cupsd_job_t
* /* O - Job object */
1223 add_job(cupsd_client_t
*con
, /* I - Client connection */
1224 cupsd_printer_t
*printer
, /* I - Destination printer */
1225 mime_type_t
*filetype
) /* I - First print file type, if any */
1227 http_status_t status
; /* Policy status */
1228 ipp_attribute_t
*attr
, /* Current attribute */
1229 *auth_info
; /* auth-info attribute */
1230 const char *val
; /* Default option value */
1231 int priority
; /* Job priority */
1232 char *title
; /* Job name/title */
1233 cupsd_job_t
*job
; /* Current job */
1234 char job_uri
[HTTP_MAX_URI
]; /* Job URI */
1235 int kbytes
; /* Size of print file */
1236 int i
; /* Looping var */
1237 int lowerpagerange
; /* Page range bound */
1240 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "add_job(%p[%d], %p(%s), %p(%s/%s))",
1241 con
, con
->http
.fd
, printer
, printer
->name
,
1242 filetype
, filetype
->super
, filetype
->type
);
1245 * Check remote printing to non-shared printer...
1248 if (!printer
->shared
&&
1249 strcasecmp(con
->http
.hostname
, "localhost") &&
1250 strcasecmp(con
->http
.hostname
, ServerName
))
1252 send_ipp_status(con
, IPP_NOT_AUTHORIZED
,
1253 _("The printer or class is not shared!"));
1261 auth_info
= ippFindAttribute(con
->request
, "auth-info", IPP_TAG_TEXT
);
1263 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
1265 send_http_error(con
, status
, printer
);
1268 else if ((printer
->type
& CUPS_PRINTER_AUTHENTICATED
) &&
1269 !con
->username
[0] && !auth_info
)
1271 send_http_error(con
, HTTP_UNAUTHORIZED
, printer
);
1275 else if (auth_info
&& !con
->http
.tls
&&
1276 !httpAddrLocalhost(con
->http
.hostaddr
))
1279 * Require encryption of auth-info over non-local connections...
1282 send_http_error(con
, HTTP_UPGRADE_REQUIRED
, printer
);
1285 #endif /* HAVE_SSL */
1288 * See if the printer is accepting jobs...
1291 if (!printer
->accepting
)
1293 send_ipp_status(con
, IPP_NOT_ACCEPTING
,
1294 _("Destination \"%s\" is not accepting jobs."),
1300 * Validate job template attributes; for now just document-format,
1301 * copies, number-up, and page-ranges...
1304 if (filetype
&& printer
->filetypes
&&
1305 !cupsArrayFind(printer
->filetypes
, filetype
))
1307 char mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
1308 /* MIME media type string */
1311 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
1314 send_ipp_status(con
, IPP_DOCUMENT_FORMAT
,
1315 _("Unsupported format \'%s\'!"), mimetype
);
1317 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
1318 "document-format", NULL
, mimetype
);
1323 if ((attr
= ippFindAttribute(con
->request
, "copies",
1324 IPP_TAG_INTEGER
)) != NULL
)
1326 if (attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> MaxCopies
)
1328 send_ipp_status(con
, IPP_ATTRIBUTES
, _("Bad copies value %d."),
1329 attr
->values
[0].integer
);
1330 ippAddInteger(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_INTEGER
,
1331 "copies", attr
->values
[0].integer
);
1336 if ((attr
= ippFindAttribute(con
->request
, "number-up",
1337 IPP_TAG_INTEGER
)) != NULL
)
1339 if (attr
->values
[0].integer
!= 1 &&
1340 attr
->values
[0].integer
!= 2 &&
1341 attr
->values
[0].integer
!= 4 &&
1342 attr
->values
[0].integer
!= 6 &&
1343 attr
->values
[0].integer
!= 9 &&
1344 attr
->values
[0].integer
!= 16)
1346 send_ipp_status(con
, IPP_ATTRIBUTES
, _("Bad number-up value %d."),
1347 attr
->values
[0].integer
);
1348 ippAddInteger(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_INTEGER
,
1349 "number-up", attr
->values
[0].integer
);
1354 if ((attr
= ippFindAttribute(con
->request
, "page-ranges",
1355 IPP_TAG_RANGE
)) != NULL
)
1357 for (i
= 0, lowerpagerange
= 1; i
< attr
->num_values
; i
++)
1359 if (attr
->values
[i
].range
.lower
< lowerpagerange
||
1360 attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
1362 send_ipp_status(con
, IPP_BAD_REQUEST
,
1363 _("Bad page-ranges values %d-%d."),
1364 attr
->values
[i
].range
.lower
,
1365 attr
->values
[i
].range
.upper
);
1369 lowerpagerange
= attr
->values
[i
].range
.upper
+ 1;
1374 * Make sure we aren't over our limit...
1377 if (MaxJobs
&& cupsArrayCount(Jobs
) >= MaxJobs
)
1380 if (MaxJobs
&& cupsArrayCount(Jobs
) >= MaxJobs
)
1382 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
1383 _("Too many active jobs."));
1387 if ((i
= check_quotas(con
, printer
)) < 0)
1389 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("Quota limit reached."));
1394 send_ipp_status(con
, IPP_NOT_AUTHORIZED
, _("Not allowed to print."));
1399 * Create the job and set things up...
1402 if ((attr
= ippFindAttribute(con
->request
, "job-priority",
1403 IPP_TAG_INTEGER
)) != NULL
)
1404 priority
= attr
->values
[0].integer
;
1407 if ((val
= cupsGetOption("job-priority", printer
->num_options
,
1408 printer
->options
)) != NULL
)
1409 priority
= atoi(val
);
1413 ippAddInteger(con
->request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
1417 if ((attr
= ippFindAttribute(con
->request
, "job-name",
1418 IPP_TAG_NAME
)) != NULL
)
1419 title
= attr
->values
[0].string
.text
;
1421 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
1422 title
= "Untitled");
1424 if ((job
= cupsdAddJob(priority
, printer
->name
)) == NULL
)
1426 send_ipp_status(con
, IPP_INTERNAL_ERROR
,
1427 _("Unable to add job for destination \"%s\"!"),
1432 job
->dtype
= printer
->type
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
|
1433 CUPS_PRINTER_REMOTE
);
1434 job
->attrs
= con
->request
;
1435 con
->request
= ippNewRequest(job
->attrs
->request
.op
.operation_id
);
1437 add_job_uuid(con
, job
);
1438 apply_printer_defaults(printer
, job
);
1440 attr
= ippFindAttribute(job
->attrs
, "requesting-user-name", IPP_TAG_NAME
);
1442 if (con
->username
[0])
1444 cupsdSetString(&job
->username
, con
->username
);
1447 cupsdSetString(&attr
->values
[0].string
.text
, con
->username
);
1451 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1452 "add_job: requesting-user-name=\"%s\"",
1453 attr
->values
[0].string
.text
);
1455 cupsdSetString(&job
->username
, attr
->values
[0].string
.text
);
1458 cupsdSetString(&job
->username
, "anonymous");
1461 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
,
1462 "job-originating-user-name", NULL
, job
->username
);
1465 attr
->group_tag
= IPP_TAG_JOB
;
1466 _cupsStrFree(attr
->name
);
1467 attr
->name
= _cupsStrAlloc("job-originating-user-name");
1470 if (con
->username
[0] || auth_info
)
1472 save_auth_info(con
, job
, auth_info
);
1475 * Remove the auth-info attribute from the attribute data...
1480 if (job
->attrs
->prev
)
1481 job
->attrs
->prev
->next
= auth_info
->next
;
1483 job
->attrs
->attrs
= auth_info
->next
;
1485 if (job
->attrs
->last
== auth_info
)
1486 job
->attrs
->last
= job
->attrs
->prev
;
1488 _ippFreeAttr(auth_info
);
1492 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-host-name",
1493 IPP_TAG_ZERO
)) != NULL
)
1496 * Request contains a job-originating-host-name attribute; validate it...
1499 if (attr
->value_tag
!= IPP_TAG_NAME
||
1500 attr
->num_values
!= 1 ||
1501 strcmp(con
->http
.hostname
, "localhost"))
1504 * Can't override the value if we aren't connected via localhost.
1505 * Also, we can only have 1 value and it must be a name value.
1508 switch (attr
->value_tag
)
1510 case IPP_TAG_STRING
:
1511 case IPP_TAG_TEXTLANG
:
1512 case IPP_TAG_NAMELANG
:
1515 case IPP_TAG_KEYWORD
:
1517 case IPP_TAG_URISCHEME
:
1518 case IPP_TAG_CHARSET
:
1519 case IPP_TAG_LANGUAGE
:
1520 case IPP_TAG_MIMETYPE
:
1522 * Free old strings...
1525 for (i
= 0; i
< attr
->num_values
; i
++)
1527 _cupsStrFree(attr
->values
[i
].string
.text
);
1528 attr
->values
[i
].string
.text
= NULL
;
1529 if (attr
->values
[i
].string
.charset
)
1531 _cupsStrFree(attr
->values
[i
].string
.charset
);
1532 attr
->values
[i
].string
.charset
= NULL
;
1541 * Use the default connection hostname instead...
1544 attr
->value_tag
= IPP_TAG_NAME
;
1545 attr
->num_values
= 1;
1546 attr
->values
[0].string
.text
= _cupsStrAlloc(con
->http
.hostname
);
1549 attr
->group_tag
= IPP_TAG_JOB
;
1554 * No job-originating-host-name attribute, so use the hostname from
1558 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
,
1559 "job-originating-host-name", NULL
, con
->http
.hostname
);
1562 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
1564 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
1565 "time-at-processing", 0);
1566 attr
->value_tag
= IPP_TAG_NOVALUE
;
1567 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
1568 "time-at-completed", 0);
1569 attr
->value_tag
= IPP_TAG_NOVALUE
;
1572 * Add remaining job attributes...
1575 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1576 job
->state
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
1577 "job-state", IPP_JOB_STOPPED
);
1578 job
->state_value
= (ipp_jstate_t
)job
->state
->values
[0].integer
;
1579 job
->sheets
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
1580 "job-media-sheets-completed", 0);
1581 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
1583 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
1586 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets",
1587 IPP_TAG_INTEGER
)) != NULL
)
1588 attr
->values
[0].integer
= 0;
1590 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
1593 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
1594 IPP_TAG_KEYWORD
)) == NULL
)
1595 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
1598 if ((val
= cupsGetOption("job-hold-until", printer
->num_options
,
1599 printer
->options
)) == NULL
)
1602 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
1603 "job-hold-until", NULL
, val
);
1605 if (attr
&& strcmp(attr
->values
[0].string
.text
, "no-hold") &&
1606 !(printer
->type
& CUPS_PRINTER_REMOTE
))
1609 * Hold job until specified time...
1612 cupsdSetJobHoldUntil(job
, attr
->values
[0].string
.text
);
1614 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
1615 job
->state_value
= IPP_JOB_HELD
;
1617 else if (job
->attrs
->request
.op
.operation_id
== IPP_CREATE_JOB
)
1619 job
->hold_until
= time(NULL
) + 60;
1620 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
1621 job
->state_value
= IPP_JOB_HELD
;
1625 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1626 job
->state_value
= IPP_JOB_PENDING
;
1629 if (!(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)) ||
1633 * Add job sheets options...
1636 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets",
1637 IPP_TAG_ZERO
)) == NULL
)
1639 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1640 "Adding default job-sheets values \"%s,%s\"...",
1641 printer
->job_sheets
[0], printer
->job_sheets
[1]);
1643 attr
= ippAddStrings(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-sheets",
1645 attr
->values
[0].string
.text
= _cupsStrAlloc(printer
->job_sheets
[0]);
1646 attr
->values
[1].string
.text
= _cupsStrAlloc(printer
->job_sheets
[1]);
1649 job
->job_sheets
= attr
;
1652 * Enforce classification level if set...
1657 cupsdLogMessage(CUPSD_LOG_INFO
,
1658 "Classification=\"%s\", ClassifyOverride=%d",
1659 Classification
? Classification
: "(null)",
1662 if (ClassifyOverride
)
1664 if (!strcmp(attr
->values
[0].string
.text
, "none") &&
1665 (attr
->num_values
== 1 ||
1666 !strcmp(attr
->values
[1].string
.text
, "none")))
1669 * Force the leading banner to have the classification on it...
1672 cupsdSetString(&attr
->values
[0].string
.text
, Classification
);
1674 cupsdLogMessage(CUPSD_LOG_NOTICE
, "[Job %d] CLASSIFICATION FORCED "
1675 "job-sheets=\"%s,none\", "
1676 "job-originating-user-name=\"%s\"",
1677 job
->id
, Classification
, job
->username
);
1679 else if (attr
->num_values
== 2 &&
1680 strcmp(attr
->values
[0].string
.text
,
1681 attr
->values
[1].string
.text
) &&
1682 strcmp(attr
->values
[0].string
.text
, "none") &&
1683 strcmp(attr
->values
[1].string
.text
, "none"))
1686 * Can't put two different security markings on the same document!
1689 cupsdSetString(&attr
->values
[1].string
.text
, attr
->values
[0].string
.text
);
1691 cupsdLogMessage(CUPSD_LOG_NOTICE
, "[Job %d] CLASSIFICATION FORCED "
1692 "job-sheets=\"%s,%s\", "
1693 "job-originating-user-name=\"%s\"",
1694 job
->id
, attr
->values
[0].string
.text
,
1695 attr
->values
[1].string
.text
, job
->username
);
1697 else if (strcmp(attr
->values
[0].string
.text
, Classification
) &&
1698 strcmp(attr
->values
[0].string
.text
, "none") &&
1699 (attr
->num_values
== 1 ||
1700 (strcmp(attr
->values
[1].string
.text
, Classification
) &&
1701 strcmp(attr
->values
[1].string
.text
, "none"))))
1703 if (attr
->num_values
== 1)
1704 cupsdLogMessage(CUPSD_LOG_NOTICE
,
1705 "[Job %d] CLASSIFICATION OVERRIDDEN "
1706 "job-sheets=\"%s\", "
1707 "job-originating-user-name=\"%s\"",
1708 job
->id
, attr
->values
[0].string
.text
, job
->username
);
1710 cupsdLogMessage(CUPSD_LOG_NOTICE
,
1711 "[Job %d] CLASSIFICATION OVERRIDDEN "
1712 "job-sheets=\"%s,%s\",fffff "
1713 "job-originating-user-name=\"%s\"",
1714 job
->id
, attr
->values
[0].string
.text
,
1715 attr
->values
[1].string
.text
, job
->username
);
1718 else if (strcmp(attr
->values
[0].string
.text
, Classification
) &&
1719 (attr
->num_values
== 1 ||
1720 strcmp(attr
->values
[1].string
.text
, Classification
)))
1723 * Force the banner to have the classification on it...
1726 if (attr
->num_values
> 1 &&
1727 !strcmp(attr
->values
[0].string
.text
, attr
->values
[1].string
.text
))
1729 cupsdSetString(&(attr
->values
[0].string
.text
), Classification
);
1730 cupsdSetString(&(attr
->values
[1].string
.text
), Classification
);
1734 if (attr
->num_values
== 1 ||
1735 strcmp(attr
->values
[0].string
.text
, "none"))
1736 cupsdSetString(&(attr
->values
[0].string
.text
), Classification
);
1738 if (attr
->num_values
> 1 &&
1739 strcmp(attr
->values
[1].string
.text
, "none"))
1740 cupsdSetString(&(attr
->values
[1].string
.text
), Classification
);
1743 if (attr
->num_values
> 1)
1744 cupsdLogMessage(CUPSD_LOG_NOTICE
,
1745 "[Job %d] CLASSIFICATION FORCED "
1746 "job-sheets=\"%s,%s\", "
1747 "job-originating-user-name=\"%s\"",
1748 job
->id
, attr
->values
[0].string
.text
,
1749 attr
->values
[1].string
.text
, job
->username
);
1751 cupsdLogMessage(CUPSD_LOG_NOTICE
,
1752 "[Job %d] CLASSIFICATION FORCED "
1753 "job-sheets=\"%s\", "
1754 "job-originating-user-name=\"%s\"",
1755 job
->id
, Classification
, job
->username
);
1760 * See if we need to add the starting sheet...
1763 if (!(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)))
1765 cupsdLogMessage(CUPSD_LOG_INFO
,
1766 "[Job %d] Adding start banner page \"%s\".",
1767 job
->id
, attr
->values
[0].string
.text
);
1769 kbytes
= copy_banner(con
, job
, attr
->values
[0].string
.text
);
1771 cupsdUpdateQuota(printer
, job
->username
, 0, kbytes
);
1774 else if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets",
1775 IPP_TAG_ZERO
)) != NULL
)
1779 * Fill in the response info...
1782 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
1783 LocalPort
, job
->id
);
1785 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
,
1788 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1790 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
1792 add_job_state_reasons(con
, job
);
1794 con
->response
->request
.status
.status_code
= IPP_OK
;
1797 * Add any job subscriptions...
1800 add_job_subscriptions(con
, job
);
1803 * Set all but the first two attributes to the job attributes group...
1806 for (attr
= job
->attrs
->attrs
->next
->next
; attr
; attr
= attr
->next
)
1807 attr
->group_tag
= IPP_TAG_JOB
;
1810 * Fire the "job created" event...
1813 cupsdAddEvent(CUPSD_EVENT_JOB_CREATED
, printer
, job
, "Job created.");
1816 * Return the new job...
1824 * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
1825 * upon the job and printer state...
1829 add_job_state_reasons(
1830 cupsd_client_t
*con
, /* I - Client connection */
1831 cupsd_job_t
*job
) /* I - Job info */
1833 cupsd_printer_t
*dest
; /* Destination printer */
1836 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "add_job_state_reasons(%p[%d], %d)",
1837 con
, con
->http
.fd
, job
? job
->id
: 0);
1839 switch (job
? job
->state_value
: IPP_JOB_CANCELED
)
1841 case IPP_JOB_PENDING
:
1842 dest
= cupsdFindDest(job
->dest
);
1844 if (dest
&& dest
->state
== IPP_PRINTER_STOPPED
)
1845 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
1846 "job-state-reasons", NULL
, "printer-stopped");
1848 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
1849 "job-state-reasons", NULL
, "none");
1853 if (ippFindAttribute(job
->attrs
, "job-hold-until",
1854 IPP_TAG_KEYWORD
) != NULL
||
1855 ippFindAttribute(job
->attrs
, "job-hold-until",
1856 IPP_TAG_NAME
) != NULL
)
1857 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
1858 "job-state-reasons", NULL
, "job-hold-until-specified");
1860 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
1861 "job-state-reasons", NULL
, "job-incoming");
1864 case IPP_JOB_PROCESSING
:
1865 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
1866 "job-state-reasons", NULL
, "job-printing");
1869 case IPP_JOB_STOPPED
:
1870 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
1871 "job-state-reasons", NULL
, "job-stopped");
1874 case IPP_JOB_CANCELED
:
1875 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
1876 "job-state-reasons", NULL
, "job-canceled-by-user");
1879 case IPP_JOB_ABORTED
:
1880 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
1881 "job-state-reasons", NULL
, "aborted-by-system");
1884 case IPP_JOB_COMPLETED
:
1885 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
1886 "job-state-reasons", NULL
, "job-completed-successfully");
1893 * 'add_job_subscriptions()' - Add any subcriptions for a job.
1897 add_job_subscriptions(
1898 cupsd_client_t
*con
, /* I - Client connection */
1899 cupsd_job_t
*job
) /* I - Newly created job */
1901 int i
; /* Looping var */
1902 ipp_attribute_t
*prev
, /* Previous attribute */
1903 *next
, /* Next attribute */
1904 *attr
; /* Current attribute */
1905 cupsd_subscription_t
*sub
; /* Subscription object */
1906 const char *recipient
, /* notify-recipient-uri */
1907 *pullmethod
; /* notify-pull-method */
1908 ipp_attribute_t
*user_data
; /* notify-user-data */
1909 int interval
; /* notify-time-interval */
1910 unsigned mask
; /* notify-events */
1914 * Find the first subscription group attribute; return if we have
1918 for (attr
= job
->attrs
->attrs
, prev
= NULL
;
1920 prev
= attr
, attr
= attr
->next
)
1921 if (attr
->group_tag
== IPP_TAG_SUBSCRIPTION
)
1928 * Process the subscription attributes in the request...
1937 mask
= CUPSD_EVENT_NONE
;
1939 while (attr
&& attr
->group_tag
!= IPP_TAG_ZERO
)
1941 if (!strcmp(attr
->name
, "notify-recipient-uri") &&
1942 attr
->value_tag
== IPP_TAG_URI
)
1943 recipient
= attr
->values
[0].string
.text
;
1944 else if (!strcmp(attr
->name
, "notify-pull-method") &&
1945 attr
->value_tag
== IPP_TAG_KEYWORD
)
1946 pullmethod
= attr
->values
[0].string
.text
;
1947 else if (!strcmp(attr
->name
, "notify-charset") &&
1948 attr
->value_tag
== IPP_TAG_CHARSET
&&
1949 strcmp(attr
->values
[0].string
.text
, "us-ascii") &&
1950 strcmp(attr
->values
[0].string
.text
, "utf-8"))
1952 send_ipp_status(con
, IPP_CHARSET
,
1953 _("Character set \"%s\" not supported!"),
1954 attr
->values
[0].string
.text
);
1957 else if (!strcmp(attr
->name
, "notify-natural-language") &&
1958 (attr
->value_tag
!= IPP_TAG_LANGUAGE
||
1959 strcmp(attr
->values
[0].string
.text
, DefaultLanguage
)))
1961 send_ipp_status(con
, IPP_CHARSET
,
1962 _("Language \"%s\" not supported!"),
1963 attr
->values
[0].string
.text
);
1966 else if (!strcmp(attr
->name
, "notify-user-data") &&
1967 attr
->value_tag
== IPP_TAG_STRING
)
1969 if (attr
->num_values
> 1 || attr
->values
[0].unknown
.length
> 63)
1971 send_ipp_status(con
, IPP_REQUEST_VALUE
,
1972 _("The notify-user-data value is too large "
1973 "(%d > 63 octets)!"),
1974 attr
->values
[0].unknown
.length
);
1980 else if (!strcmp(attr
->name
, "notify-events") &&
1981 attr
->value_tag
== IPP_TAG_KEYWORD
)
1983 for (i
= 0; i
< attr
->num_values
; i
++)
1984 mask
|= cupsdEventValue(attr
->values
[i
].string
.text
);
1986 else if (!strcmp(attr
->name
, "notify-lease-duration"))
1988 send_ipp_status(con
, IPP_BAD_REQUEST
,
1989 _("The notify-lease-duration attribute cannot be "
1990 "used with job subscriptions."));
1993 else if (!strcmp(attr
->name
, "notify-time-interval") &&
1994 attr
->value_tag
== IPP_TAG_INTEGER
)
1995 interval
= attr
->values
[0].integer
;
2000 if (!recipient
&& !pullmethod
)
2003 if (mask
== CUPSD_EVENT_NONE
)
2004 mask
= CUPSD_EVENT_JOB_COMPLETED
;
2006 sub
= cupsdAddSubscription(mask
, cupsdFindDest(job
->dest
), job
, recipient
,
2009 sub
->interval
= interval
;
2011 cupsdSetString(&sub
->owner
, job
->username
);
2015 sub
->user_data_len
= user_data
->values
[0].unknown
.length
;
2016 memcpy(sub
->user_data
, user_data
->values
[0].unknown
.data
,
2017 sub
->user_data_len
);
2020 ippAddSeparator(con
->response
);
2021 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
2022 "notify-subscription-id", sub
->id
);
2028 cupsdSaveAllSubscriptions();
2031 * Remove all of the subscription attributes from the job request...
2034 for (attr
= job
->attrs
->attrs
, prev
= NULL
; attr
; attr
= next
)
2038 if (attr
->group_tag
== IPP_TAG_SUBSCRIPTION
||
2039 attr
->group_tag
== IPP_TAG_ZERO
)
2042 * Free and remove this attribute...
2050 job
->attrs
->attrs
= next
;
2056 job
->attrs
->last
= prev
;
2057 job
->attrs
->current
= prev
;
2062 * 'add_job_uuid()' - Add job-uuid attribute to a job.
2064 * See RFC 4122 for the definition of UUIDs and the format.
2068 add_job_uuid(cupsd_client_t
*con
, /* I - Client connection */
2069 cupsd_job_t
*job
) /* I - Job */
2071 char uuid
[1024]; /* job-uuid string */
2072 _cups_md5_state_t md5state
; /* MD5 state */
2073 unsigned char md5sum
[16]; /* MD5 digest/sum */
2077 * First see if the job already has a job-uuid attribute; if so, return...
2080 if (ippFindAttribute(job
->attrs
, "job-uuid", IPP_TAG_URI
))
2084 * No job-uuid attribute, so build a version 3 UUID with the local job
2085 * ID at the end; see RFC 4122 for details. Start with the MD5 sum of
2086 * the ServerName, server name and port that the client connected to,
2087 * and local job ID...
2090 snprintf(uuid
, sizeof(uuid
), "%s:%s:%d:%d", ServerName
, con
->servername
,
2091 con
->serverport
, job
->id
);
2093 _cupsMD5Init(&md5state
);
2094 _cupsMD5Append(&md5state
, (unsigned char *)uuid
, strlen(uuid
));
2095 _cupsMD5Finish(&md5state
, md5sum
);
2098 * Format the UUID URI using the MD5 sum and job ID.
2101 snprintf(uuid
, sizeof(uuid
),
2102 "urn:uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
2103 "%02x%02x%02x%02x%02x%02x",
2104 md5sum
[0], md5sum
[1], md5sum
[2], md5sum
[3], md5sum
[4], md5sum
[5],
2105 (md5sum
[6] & 15) | 0x30, md5sum
[7], (md5sum
[8] & 0x3f) | 0x40,
2106 md5sum
[9], md5sum
[10], md5sum
[11], md5sum
[12], md5sum
[13],
2107 md5sum
[14], md5sum
[15]);
2109 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
2114 * 'add_printer()' - Add a printer to the system.
2118 add_printer(cupsd_client_t
*con
, /* I - Client connection */
2119 ipp_attribute_t
*uri
) /* I - URI of printer */
2121 http_status_t status
; /* Policy status */
2122 int i
; /* Looping var */
2123 char scheme
[HTTP_MAX_URI
], /* Method portion of URI */
2124 username
[HTTP_MAX_URI
], /* Username portion of URI */
2125 host
[HTTP_MAX_URI
], /* Host portion of URI */
2126 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
2127 int port
; /* Port portion of URI */
2128 cupsd_printer_t
*printer
; /* Printer/class */
2129 ipp_attribute_t
*attr
; /* Printer attribute */
2130 cups_file_t
*fp
; /* Script/PPD file */
2131 char line
[1024]; /* Line from file... */
2132 char srcfile
[1024], /* Source Script/PPD file */
2133 dstfile
[1024]; /* Destination Script/PPD file */
2134 int modify
; /* Non-zero if we are modifying */
2135 char newname
[IPP_MAX_NAME
]; /* New printer name */
2136 int need_restart_job
; /* Need to restart job? */
2137 int set_device_uri
, /* Did we set the device URI? */
2138 set_port_monitor
; /* Did we set the port monitor? */
2141 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "add_printer(%p[%d], %s)", con
,
2142 con
->http
.fd
, uri
->values
[0].string
.text
);
2145 * Do we have a valid URI?
2148 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
2149 sizeof(scheme
), username
, sizeof(username
), host
,
2150 sizeof(host
), &port
, resource
, sizeof(resource
));
2152 if (strncmp(resource
, "/printers/", 10) || strlen(resource
) == 10)
2155 * No, return an error...
2158 send_ipp_status(con
, IPP_BAD_REQUEST
,
2159 _("The printer-uri must be of the form "
2160 "\"ipp://HOSTNAME/printers/PRINTERNAME\"."));
2165 * Do we have a valid printer name?
2168 if (!validate_name(resource
+ 10))
2171 * No, return an error...
2174 send_ipp_status(con
, IPP_BAD_REQUEST
,
2175 _("The printer-uri \"%s\" contains invalid characters."),
2176 uri
->values
[0].string
.text
);
2184 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
2186 send_http_error(con
, status
, NULL
);
2191 * See if the printer already exists; if not, create a new printer...
2194 if ((printer
= cupsdFindPrinter(resource
+ 10)) == NULL
)
2197 * Printer doesn't exist; see if we have a class of the same name...
2200 if ((printer
= cupsdFindClass(resource
+ 10)) != NULL
&&
2201 !(printer
->type
& CUPS_PRINTER_DISCOVERED
))
2204 * Yes, return an error...
2207 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
2208 _("A class named \"%s\" already exists!"),
2214 * No, add the printer...
2217 printer
= cupsdAddPrinter(resource
+ 10);
2220 else if (printer
->type
& CUPS_PRINTER_IMPLICIT
)
2223 * Rename the implicit printer to "AnyPrinter" or delete it...
2226 if (ImplicitAnyClasses
)
2228 snprintf(newname
, sizeof(newname
), "Any%s", resource
+ 10);
2229 cupsdRenamePrinter(printer
, newname
);
2232 cupsdDeletePrinter(printer
, 1);
2235 * Add the printer as a new local printer...
2238 printer
= cupsdAddPrinter(resource
+ 10);
2241 else if (printer
->type
& CUPS_PRINTER_DISCOVERED
)
2244 * Rename the remote printer to "Printer@server"...
2247 snprintf(newname
, sizeof(newname
), "%s@%s", resource
+ 10,
2249 cupsdRenamePrinter(printer
, newname
);
2252 * Add the printer as a new local printer...
2255 printer
= cupsdAddPrinter(resource
+ 10);
2262 * Look for attributes and copy them over as needed...
2265 need_restart_job
= 0;
2267 if ((attr
= ippFindAttribute(con
->request
, "printer-location",
2268 IPP_TAG_TEXT
)) != NULL
)
2269 cupsdSetString(&printer
->location
, attr
->values
[0].string
.text
);
2271 if ((attr
= ippFindAttribute(con
->request
, "printer-info",
2272 IPP_TAG_TEXT
)) != NULL
)
2273 cupsdSetString(&printer
->info
, attr
->values
[0].string
.text
);
2277 if ((attr
= ippFindAttribute(con
->request
, "device-uri",
2278 IPP_TAG_URI
)) != NULL
)
2281 * Do we have a valid device URI?
2284 need_restart_job
= 1;
2286 httpSeparateURI(HTTP_URI_CODING_ALL
, attr
->values
[0].string
.text
, scheme
,
2287 sizeof(scheme
), username
, sizeof(username
), host
,
2288 sizeof(host
), &port
, resource
, sizeof(resource
));
2290 if (!strcmp(scheme
, "file"))
2293 * See if the administrator has enabled file devices...
2296 if (!FileDevice
&& strcmp(resource
, "/dev/null"))
2299 * File devices are disabled and the URL is not file:/dev/null...
2302 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
2303 _("File device URIs have been disabled! "
2304 "To enable, see the FileDevice directive in "
2305 "\"%s/cupsd.conf\"."),
2313 * See if the backend exists and is executable...
2316 snprintf(srcfile
, sizeof(srcfile
), "%s/backend/%s", ServerBin
, scheme
);
2317 if (access(srcfile
, X_OK
))
2320 * Could not find device in list!
2323 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("Bad device-uri \"%s\"!"),
2324 attr
->values
[0].string
.text
);
2329 cupsdLogMessage(CUPSD_LOG_INFO
,
2330 "Setting %s device-uri to \"%s\" (was \"%s\".)",
2332 cupsdSanitizeURI(attr
->values
[0].string
.text
, line
,
2334 cupsdSanitizeURI(printer
->device_uri
, resource
,
2337 cupsdSetString(&printer
->device_uri
, attr
->values
[0].string
.text
);
2341 set_port_monitor
= 0;
2343 if ((attr
= ippFindAttribute(con
->request
, "port-monitor",
2344 IPP_TAG_NAME
)) != NULL
)
2346 ipp_attribute_t
*supported
; /* port-monitor-supported attribute */
2349 need_restart_job
= 1;
2351 supported
= ippFindAttribute(printer
->attrs
, "port-monitor-supported",
2353 for (i
= 0; i
< supported
->num_values
; i
++)
2354 if (!strcmp(supported
->values
[i
].string
.text
,
2355 attr
->values
[0].string
.text
))
2358 if (i
>= supported
->num_values
)
2360 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("Bad port-monitor \"%s\"!"),
2361 attr
->values
[0].string
.text
);
2365 cupsdLogMessage(CUPSD_LOG_INFO
,
2366 "Setting %s port-monitor to \"%s\" (was \"%s\".)",
2367 printer
->name
, attr
->values
[0].string
.text
,
2368 printer
->port_monitor
? printer
->port_monitor
: "none");
2370 if (strcmp(attr
->values
[0].string
.text
, "none"))
2371 cupsdSetString(&printer
->port_monitor
, attr
->values
[0].string
.text
);
2373 cupsdClearString(&printer
->port_monitor
);
2375 set_port_monitor
= 1;
2378 if ((attr
= ippFindAttribute(con
->request
, "printer-is-accepting-jobs",
2379 IPP_TAG_BOOLEAN
)) != NULL
)
2381 cupsdLogMessage(CUPSD_LOG_INFO
,
2382 "Setting %s printer-is-accepting-jobs to %d (was %d.)",
2383 printer
->name
, attr
->values
[0].boolean
, printer
->accepting
);
2385 printer
->accepting
= attr
->values
[0].boolean
;
2386 cupsdAddPrinterHistory(printer
);
2389 if ((attr
= ippFindAttribute(con
->request
, "printer-is-shared",
2390 IPP_TAG_BOOLEAN
)) != NULL
)
2392 if (printer
->shared
&& !attr
->values
[0].boolean
)
2393 cupsdDeregisterPrinter(printer
, 1);
2395 cupsdLogMessage(CUPSD_LOG_INFO
,
2396 "Setting %s printer-is-shared to %d (was %d.)",
2397 printer
->name
, attr
->values
[0].boolean
, printer
->shared
);
2399 printer
->shared
= attr
->values
[0].boolean
;
2402 if ((attr
= ippFindAttribute(con
->request
, "printer-state",
2403 IPP_TAG_ENUM
)) != NULL
)
2405 if (attr
->values
[0].integer
!= IPP_PRINTER_IDLE
&&
2406 attr
->values
[0].integer
!= IPP_PRINTER_STOPPED
)
2408 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad printer-state value %d!"),
2409 attr
->values
[0].integer
);
2413 cupsdLogMessage(CUPSD_LOG_INFO
, "Setting %s printer-state to %d (was %d.)",
2414 printer
->name
, attr
->values
[0].integer
, printer
->state
);
2416 if (attr
->values
[0].integer
== IPP_PRINTER_STOPPED
)
2417 cupsdStopPrinter(printer
, 0);
2420 need_restart_job
= 1;
2421 cupsdSetPrinterState(printer
, (ipp_pstate_t
)(attr
->values
[0].integer
), 0);
2424 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
2425 IPP_TAG_TEXT
)) != NULL
)
2427 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
2428 sizeof(printer
->state_message
));
2429 cupsdAddPrinterHistory(printer
);
2432 set_printer_defaults(con
, printer
);
2434 if ((attr
= ippFindAttribute(con
->request
, "auth-info-required",
2435 IPP_TAG_KEYWORD
)) != NULL
)
2436 cupsdSetAuthInfoRequired(printer
, NULL
, attr
);
2439 * See if we have all required attributes...
2442 if (!printer
->device_uri
)
2443 cupsdSetString(&printer
->device_uri
, "file:///dev/null");
2446 * See if we have an interface script or PPD file attached to the request...
2451 need_restart_job
= 1;
2453 strlcpy(srcfile
, con
->filename
, sizeof(srcfile
));
2455 if ((fp
= cupsFileOpen(srcfile
, "rb")))
2458 * Yes; get the first line from it...
2462 cupsFileGets(fp
, line
, sizeof(line
));
2466 * Then see what kind of file it is...
2469 snprintf(dstfile
, sizeof(dstfile
), "%s/interfaces/%s", ServerRoot
,
2472 if (!strncmp(line
, "*PPD-Adobe", 10))
2475 * The new file is a PPD file, so remove any old interface script
2476 * that might be lying around...
2484 * This must be an interface script, so move the file over to the
2485 * interfaces directory and make it executable...
2488 if (copy_file(srcfile
, dstfile
))
2490 send_ipp_status(con
, IPP_INTERNAL_ERROR
,
2491 _("Unable to copy interface script - %s!"),
2497 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2498 "Copied interface script successfully!");
2499 chmod(dstfile
, 0755);
2503 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
2506 if (!strncmp(line
, "*PPD-Adobe", 10))
2509 * The new file is a PPD file, so move the file over to the
2510 * ppd directory and make it readable by all...
2513 if (copy_file(srcfile
, dstfile
))
2515 send_ipp_status(con
, IPP_INTERNAL_ERROR
,
2516 _("Unable to copy PPD file - %s!"),
2522 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2523 "Copied PPD file successfully!");
2524 chmod(dstfile
, 0644);
2530 * This must be an interface script, so remove any old PPD file that
2531 * may be lying around...
2538 else if ((attr
= ippFindAttribute(con
->request
, "ppd-name",
2539 IPP_TAG_NAME
)) != NULL
)
2541 need_restart_job
= 1;
2543 if (!strcmp(attr
->values
[0].string
.text
, "raw"))
2546 * Raw driver, remove any existing PPD or interface script files.
2549 snprintf(dstfile
, sizeof(dstfile
), "%s/interfaces/%s", ServerRoot
,
2553 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
2563 snprintf(dstfile
, sizeof(dstfile
), "%s/interfaces/%s", ServerRoot
,
2567 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
2570 if (copy_model(con
, attr
->values
[0].string
.text
, dstfile
))
2572 send_ipp_status(con
, IPP_INTERNAL_ERROR
, _("Unable to copy PPD file!"));
2577 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2578 "Copied PPD file successfully!");
2579 chmod(dstfile
, 0644);
2585 * If we set the device URI but not the port monitor, check which port
2586 * monitor to use by default...
2589 if (set_device_uri
&& !set_port_monitor
)
2591 ppd_file_t
*ppd
; /* PPD file */
2592 ppd_attr_t
*ppdattr
; /* cupsPortMonitor attribute */
2595 httpSeparateURI(HTTP_URI_CODING_ALL
, printer
->device_uri
, scheme
,
2596 sizeof(scheme
), username
, sizeof(username
), host
,
2597 sizeof(host
), &port
, resource
, sizeof(resource
));
2599 snprintf(srcfile
, sizeof(srcfile
), "%s/ppd/%s.ppd", ServerRoot
,
2601 if ((ppd
= ppdOpenFile(srcfile
)) != NULL
)
2603 for (ppdattr
= ppdFindAttr(ppd
, "cupsPortMonitor", NULL
);
2605 ppdattr
= ppdFindNextAttr(ppd
, "cupsPortMonitor", NULL
))
2606 if (!strcmp(scheme
, ppdattr
->spec
))
2608 cupsdLogMessage(CUPSD_LOG_INFO
,
2609 "Setting %s port-monitor to \"%s\" (was \"%s\".)",
2610 printer
->name
, ppdattr
->value
,
2611 printer
->port_monitor
? printer
->port_monitor
2614 if (strcmp(ppdattr
->value
, "none"))
2615 cupsdSetString(&printer
->port_monitor
, ppdattr
->value
);
2617 cupsdClearString(&printer
->port_monitor
);
2627 * Update the printer attributes and return...
2630 cupsdSetPrinterAttrs(printer
);
2631 cupsdSaveAllPrinters();
2633 if (need_restart_job
&& printer
->job
)
2638 * Stop the current job and then restart it below...
2641 job
= (cupsd_job_t
*)printer
->job
;
2643 cupsdStopJob(job
, 1);
2645 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
2646 job
->state_value
= IPP_JOB_PENDING
;
2649 if (need_restart_job
)
2652 cupsdWritePrintcap();
2656 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED
, printer
, NULL
,
2657 "Printer \"%s\" modified by \"%s\".", printer
->name
,
2660 cupsdLogMessage(CUPSD_LOG_INFO
, "Printer \"%s\" modified by \"%s\".",
2661 printer
->name
, get_username(con
));
2665 cupsdAddPrinterHistory(printer
);
2667 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED
, printer
, NULL
,
2668 "New printer \"%s\" added by \"%s\".", printer
->name
,
2671 cupsdLogMessage(CUPSD_LOG_INFO
, "New printer \"%s\" added by \"%s\".",
2672 printer
->name
, get_username(con
));
2675 con
->response
->request
.status
.status_code
= IPP_OK
;
2680 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
2681 * based upon the printer state...
2685 add_printer_state_reasons(
2686 cupsd_client_t
*con
, /* I - Client connection */
2687 cupsd_printer_t
*p
) /* I - Printer info */
2689 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2690 "add_printer_state_reasons(%p[%d], %p[%s])",
2691 con
, con
->http
.fd
, p
, p
->name
);
2693 if (p
->num_reasons
== 0)
2694 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2695 "printer-state-reasons", NULL
,
2696 p
->state
== IPP_PRINTER_STOPPED
? "paused" : "none");
2698 ippAddStrings(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2699 "printer-state-reasons", p
->num_reasons
, NULL
,
2700 (const char * const *)p
->reasons
);
2705 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
2706 * the specified printer or class.
2710 add_queued_job_count(
2711 cupsd_client_t
*con
, /* I - Client connection */
2712 cupsd_printer_t
*p
) /* I - Printer or class */
2714 int count
; /* Number of jobs on destination */
2717 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "add_queued_job_count(%p[%d], %p[%s])",
2718 con
, con
->http
.fd
, p
, p
->name
);
2720 count
= cupsdGetPrinterJobCount(p
->name
);
2722 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2723 "queued-job-count", count
);
2728 * 'apply_printer_defaults()' - Apply printer default options to a job.
2732 apply_printer_defaults(
2733 cupsd_printer_t
*printer
, /* I - Printer */
2734 cupsd_job_t
*job
) /* I - Job */
2736 int i
, /* Looping var */
2737 num_options
; /* Number of default options */
2738 cups_option_t
*options
, /* Default options */
2739 *option
; /* Current option */
2743 * Collect all of the default options and add the missing ones to the
2747 for (i
= printer
->num_options
, num_options
= 0, option
= printer
->options
;
2750 if (!ippFindAttribute(job
->attrs
, option
->name
, IPP_TAG_ZERO
))
2752 num_options
= cupsAddOption(option
->name
, option
->value
, num_options
,
2757 * Encode these options as attributes in the job object...
2760 cupsEncodeOptions2(job
->attrs
, num_options
, options
, IPP_TAG_JOB
);
2761 cupsFreeOptions(num_options
, options
);
2766 * 'authenticate_job()' - Set job authentication info.
2770 authenticate_job(cupsd_client_t
*con
, /* I - Client connection */
2771 ipp_attribute_t
*uri
) /* I - Job URI */
2773 ipp_attribute_t
*attr
, /* job-id attribute */
2774 *auth_info
; /* auth-info attribute */
2775 int jobid
; /* Job ID */
2776 cupsd_job_t
*job
; /* Current job */
2777 char method
[HTTP_MAX_URI
],
2778 /* Method portion of URI */
2779 username
[HTTP_MAX_URI
],
2780 /* Username portion of URI */
2782 /* Host portion of URI */
2783 resource
[HTTP_MAX_URI
];
2784 /* Resource portion of URI */
2785 int port
; /* Port portion of URI */
2788 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "authenticate_job(%p[%d], %s)",
2789 con
, con
->http
.fd
, uri
->values
[0].string
.text
);
2792 * Start with "everything is OK" status...
2795 con
->response
->request
.status
.status_code
= IPP_OK
;
2798 * See if we have a job URI or a printer URI...
2801 if (!strcmp(uri
->name
, "printer-uri"))
2804 * Got a printer URI; see if we also have a job-id attribute...
2807 if ((attr
= ippFindAttribute(con
->request
, "job-id",
2808 IPP_TAG_INTEGER
)) == NULL
)
2810 send_ipp_status(con
, IPP_BAD_REQUEST
,
2811 _("Got a printer-uri attribute but no job-id!"));
2815 jobid
= attr
->values
[0].integer
;
2820 * Got a job URI; parse it to get the job ID...
2823 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, method
,
2824 sizeof(method
), username
, sizeof(username
), host
,
2825 sizeof(host
), &port
, resource
, sizeof(resource
));
2827 if (strncmp(resource
, "/jobs/", 6))
2833 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad job-uri attribute \"%s\"!"),
2834 uri
->values
[0].string
.text
);
2838 jobid
= atoi(resource
+ 6);
2842 * See if the job exists...
2845 if ((job
= cupsdFindJob(jobid
)) == NULL
)
2848 * Nope - return a "not found" error...
2851 send_ipp_status(con
, IPP_NOT_FOUND
,
2852 _("Job #%d does not exist!"), jobid
);
2857 * See if the job has been completed...
2860 if (job
->state_value
!= IPP_JOB_HELD
)
2863 * Return a "not-possible" error...
2866 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
2867 _("Job #%d is not held for authentication!"),
2873 * See if we have already authenticated...
2876 auth_info
= ippFindAttribute(con
->request
, "auth-info", IPP_TAG_TEXT
);
2878 if (!con
->username
[0] && !auth_info
)
2880 send_ipp_status(con
, IPP_NOT_AUTHORIZED
,
2881 _("No authentication information provided!"));
2886 * See if the job is owned by the requesting user...
2889 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
2891 send_http_error(con
, HTTP_UNAUTHORIZED
, NULL
);
2896 * Save the authentication information for this job...
2899 save_auth_info(con
, job
, auth_info
);
2902 * Reset the job-hold-until value to "no-hold"...
2905 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
2906 IPP_TAG_KEYWORD
)) == NULL
)
2907 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
2911 attr
->value_tag
= IPP_TAG_KEYWORD
;
2912 cupsdSetString(&(attr
->values
[0].string
.text
), "no-hold");
2916 * Release the job and return...
2919 cupsdReleaseJob(job
);
2921 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job %d] Authenticated by \"%s\".", jobid
,
2927 * 'cancel_all_jobs()' - Cancel all print jobs.
2931 cancel_all_jobs(cupsd_client_t
*con
, /* I - Client connection */
2932 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
2934 http_status_t status
; /* Policy status */
2935 cups_ptype_t dtype
; /* Destination type */
2936 char scheme
[HTTP_MAX_URI
], /* Scheme portion of URI */
2937 userpass
[HTTP_MAX_URI
], /* Username portion of URI */
2938 hostname
[HTTP_MAX_URI
], /* Host portion of URI */
2939 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
2940 int port
; /* Port portion of URI */
2941 ipp_attribute_t
*attr
; /* Attribute in request */
2942 const char *username
; /* Username */
2943 int purge
; /* Purge? */
2944 cupsd_printer_t
*printer
; /* Printer */
2947 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cancel_all_jobs(%p[%d], %s)", con
,
2948 con
->http
.fd
, uri
->values
[0].string
.text
);
2951 * See if we have a printer URI...
2954 if (strcmp(uri
->name
, "printer-uri"))
2956 send_ipp_status(con
, IPP_BAD_REQUEST
,
2957 _("The printer-uri attribute is required!"));
2962 * Get the username (if any) for the jobs we want to cancel (only if
2963 * "my-jobs" is specified...
2966 if ((attr
= ippFindAttribute(con
->request
, "my-jobs",
2967 IPP_TAG_BOOLEAN
)) != NULL
&&
2968 attr
->values
[0].boolean
)
2970 if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name",
2971 IPP_TAG_NAME
)) != NULL
)
2972 username
= attr
->values
[0].string
.text
;
2975 send_ipp_status(con
, IPP_BAD_REQUEST
,
2976 _("Missing requesting-user-name attribute!"));
2984 * Look for the "purge-jobs" attribute...
2987 if ((attr
= ippFindAttribute(con
->request
, "purge-jobs",
2988 IPP_TAG_BOOLEAN
)) != NULL
)
2989 purge
= attr
->values
[0].boolean
;
2994 * And if the destination is valid...
2997 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
3003 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
,
3004 scheme
, sizeof(scheme
), userpass
, sizeof(userpass
),
3005 hostname
, sizeof(hostname
), &port
,
3006 resource
, sizeof(resource
));
3008 if ((!strncmp(resource
, "/printers/", 10) && resource
[10]) ||
3009 (!strncmp(resource
, "/classes/", 9) && resource
[9]))
3011 send_ipp_status(con
, IPP_NOT_FOUND
,
3012 _("The printer or class was not found."));
3020 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
3022 send_http_error(con
, status
, NULL
);
3027 * Cancel all jobs on all printers...
3030 cupsdCancelJobs(NULL
, username
, purge
);
3032 cupsdLogMessage(CUPSD_LOG_INFO
, "All jobs were %s by \"%s\".",
3033 purge
? "purged" : "canceled", get_username(con
));
3041 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
,
3044 send_http_error(con
, status
, printer
);
3049 * Cancel all of the jobs on the named printer...
3052 cupsdCancelJobs(printer
->name
, username
, purge
);
3054 cupsdLogMessage(CUPSD_LOG_INFO
, "All jobs on \"%s\" were %s by \"%s\".",
3055 printer
->name
, purge
? "purged" : "canceled",
3059 con
->response
->request
.status
.status_code
= IPP_OK
;
3064 * 'cancel_job()' - Cancel a print job.
3068 cancel_job(cupsd_client_t
*con
, /* I - Client connection */
3069 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
3071 ipp_attribute_t
*attr
; /* Current attribute */
3072 int jobid
; /* Job ID */
3073 char scheme
[HTTP_MAX_URI
], /* Scheme portion of URI */
3074 username
[HTTP_MAX_URI
], /* Username portion of URI */
3075 host
[HTTP_MAX_URI
], /* Host portion of URI */
3076 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
3077 int port
; /* Port portion of URI */
3078 cupsd_job_t
*job
; /* Job information */
3079 cups_ptype_t dtype
; /* Destination type (printer/class) */
3080 cupsd_printer_t
*printer
; /* Printer data */
3083 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cancel_job(%p[%d], %s)", con
,
3084 con
->http
.fd
, uri
->values
[0].string
.text
);
3087 * See if we have a job URI or a printer URI...
3090 if (!strcmp(uri
->name
, "printer-uri"))
3093 * Got a printer URI; see if we also have a job-id attribute...
3096 if ((attr
= ippFindAttribute(con
->request
, "job-id",
3097 IPP_TAG_INTEGER
)) == NULL
)
3099 send_ipp_status(con
, IPP_BAD_REQUEST
,
3100 _("Got a printer-uri attribute but no job-id!"));
3104 if ((jobid
= attr
->values
[0].integer
) == 0)
3107 * Find the current job on the specified printer...
3110 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
3116 send_ipp_status(con
, IPP_NOT_FOUND
,
3117 _("The printer or class was not found."));
3122 * See if the printer is currently printing a job...
3126 jobid
= ((cupsd_job_t
*)printer
->job
)->id
;
3130 * No, see if there are any pending jobs...
3133 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
3135 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
3136 if (job
->state_value
<= IPP_JOB_PROCESSING
&&
3137 !strcasecmp(job
->dest
, printer
->name
))
3144 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
3146 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
3147 if (job
->state_value
== IPP_JOB_STOPPED
&&
3148 !strcasecmp(job
->dest
, printer
->name
))
3155 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("No active jobs on %s!"),
3166 * Got a job URI; parse it to get the job ID...
3169 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
3170 sizeof(scheme
), username
, sizeof(username
), host
,
3171 sizeof(host
), &port
, resource
, sizeof(resource
));
3173 if (strncmp(resource
, "/jobs/", 6))
3179 send_ipp_status(con
, IPP_BAD_REQUEST
,
3180 _("Bad job-uri attribute \"%s\"!"),
3181 uri
->values
[0].string
.text
);
3185 jobid
= atoi(resource
+ 6);
3189 * See if the job exists...
3192 if ((job
= cupsdFindJob(jobid
)) == NULL
)
3195 * Nope - return a "not found" error...
3198 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist!"), jobid
);
3203 * See if the job is owned by the requesting user...
3206 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
3208 send_http_error(con
, HTTP_UNAUTHORIZED
, NULL
);
3213 * See if the job is already completed, canceled, or aborted; if so,
3214 * we can't cancel...
3217 if (job
->state_value
>= IPP_JOB_CANCELED
)
3219 switch (job
->state_value
)
3221 case IPP_JOB_CANCELED
:
3222 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
3223 _("Job #%d is already canceled - can\'t cancel."),
3227 case IPP_JOB_ABORTED
:
3228 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
3229 _("Job #%d is already aborted - can\'t cancel."),
3234 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
3235 _("Job #%d is already completed - can\'t cancel."),
3244 * Cancel the job and return...
3247 cupsdCancelJob(job
, 0, IPP_JOB_CANCELED
);
3250 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job %d] Canceled by \"%s\".", jobid
,
3253 con
->response
->request
.status
.status_code
= IPP_OK
;
3258 * 'cancel_subscription()' - Cancel a subscription.
3262 cancel_subscription(
3263 cupsd_client_t
*con
, /* I - Client connection */
3264 int sub_id
) /* I - Subscription ID */
3266 http_status_t status
; /* Policy status */
3267 cupsd_subscription_t
*sub
; /* Subscription */
3270 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3271 "cancel_subscription(con=%p[%d], sub_id=%d)",
3272 con
, con
->http
.fd
, sub_id
);
3275 * Is the subscription ID valid?
3278 if ((sub
= cupsdFindSubscription(sub_id
)) == NULL
)
3281 * Bad subscription ID...
3284 send_ipp_status(con
, IPP_NOT_FOUND
,
3285 _("notify-subscription-id %d no good!"), sub_id
);
3293 if ((status
= cupsdCheckPolicy(sub
->dest
? sub
->dest
->op_policy_ptr
:
3295 con
, sub
->owner
)) != HTTP_OK
)
3297 send_http_error(con
, status
, sub
->dest
);
3302 * Cancel the subscription...
3305 cupsdDeleteSubscription(sub
, 1);
3307 con
->response
->request
.status
.status_code
= IPP_OK
;
3312 * 'check_quotas()' - Check quotas for a printer and user.
3315 static int /* O - 1 if OK, 0 if not */
3316 check_quotas(cupsd_client_t
*con
, /* I - Client connection */
3317 cupsd_printer_t
*p
) /* I - Printer or class */
3319 int i
; /* Looping var */
3320 char username
[33]; /* Username */
3321 cupsd_quota_t
*q
; /* Quota data */
3322 #ifdef HAVE_MBR_UID_TO_UUID
3324 * Use Apple membership APIs which require that all names represent
3325 * valid user account or group records accessible by the server.
3328 uuid_t usr_uuid
; /* UUID for job requesting user */
3329 uuid_t usr2_uuid
; /* UUID for ACL user name entry */
3330 uuid_t grp_uuid
; /* UUID for ACL group name entry */
3331 int mbr_err
; /* Error from membership function */
3332 int is_member
; /* Is this user a member? */
3335 * Use standard POSIX APIs for checking users and groups...
3338 struct passwd
*pw
; /* User password data */
3339 #endif /* HAVE_MBR_UID_TO_UUID */
3342 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "check_quotas(%p[%d], %p[%s])",
3343 con
, con
->http
.fd
, p
, p
->name
);
3353 * Figure out who is printing...
3356 strlcpy(username
, get_username(con
), sizeof(username
));
3359 * Check global active job limits for printers and users...
3362 if (MaxJobsPerPrinter
)
3365 * Check if there are too many pending jobs on this printer...
3368 if (cupsdGetPrinterJobCount(p
->name
) >= MaxJobsPerPrinter
)
3370 cupsdLogMessage(CUPSD_LOG_INFO
, "Too many jobs for printer \"%s\"...",
3379 * Check if there are too many pending jobs for this user...
3382 if (cupsdGetUserJobCount(username
) >= MaxJobsPerUser
)
3384 cupsdLogMessage(CUPSD_LOG_INFO
, "Too many jobs for user \"%s\"...",
3391 * Check against users...
3394 if (p
->num_users
== 0 && p
->k_limit
== 0 && p
->page_limit
== 0)
3399 #ifdef HAVE_MBR_UID_TO_UUID
3401 * Get UUID for job requesting user...
3404 if (mbr_user_name_to_uuid((char *)username
, usr_uuid
))
3410 cupsdLogMessage(CUPSD_LOG_DEBUG
,
3411 "check_quotas: UUID lookup failed for user \"%s\"",
3413 cupsdLogMessage(CUPSD_LOG_INFO
,
3414 "Denying user \"%s\" access to printer \"%s\" "
3415 "(unknown user)...",
3421 * Get UID and GID of requesting user...
3424 pw
= getpwnam(username
);
3426 #endif /* HAVE_MBR_UID_TO_UUID */
3428 for (i
= 0; i
< p
->num_users
; i
++)
3429 if (p
->users
[i
][0] == '@')
3432 * Check group membership...
3435 #ifdef HAVE_MBR_UID_TO_UUID
3436 if ((mbr_err
= mbr_group_name_to_uuid((char *)p
->users
[i
] + 1,
3440 * Invalid ACL entries are ignored for matching; just record a
3441 * warning in the log...
3444 cupsdLogMessage(CUPSD_LOG_DEBUG
,
3445 "check_quotas: UUID lookup failed for ACL entry "
3446 "\"%s\" (err=%d)", p
->users
[i
], mbr_err
);
3447 cupsdLogMessage(CUPSD_LOG_WARN
,
3448 "Access control entry \"%s\" not a valid group name; "
3449 "entry ignored", p
->users
[i
]);
3453 if ((mbr_err
= mbr_check_membership(usr_uuid
, grp_uuid
,
3457 * At this point, there should be no errors, but check anyways...
3460 cupsdLogMessage(CUPSD_LOG_DEBUG
,
3461 "check_quotas: group \"%s\" membership check "
3462 "failed (err=%d)", p
->users
[i
] + 1, mbr_err
);
3467 * Stop if we found a match...
3474 if (cupsdCheckGroup(username
, pw
, p
->users
[i
] + 1))
3476 #endif /* HAVE_MBR_UID_TO_UUID */
3478 #ifdef HAVE_MBR_UID_TO_UUID
3481 if ((mbr_err
= mbr_user_name_to_uuid((char *)p
->users
[i
],
3485 * Invalid ACL entries are ignored for matching; just record a
3486 * warning in the log...
3489 cupsdLogMessage(CUPSD_LOG_DEBUG
,
3490 "check_quotas: UUID lookup failed for ACL entry "
3491 "\"%s\" (err=%d)", p
->users
[i
], mbr_err
);
3492 cupsdLogMessage(CUPSD_LOG_WARN
,
3493 "Access control entry \"%s\" not a valid user name; "
3494 "entry ignored", p
->users
[i
]);
3498 if ((mbr_err
= mbr_check_membership(usr_uuid
, usr2_uuid
,
3501 cupsdLogMessage(CUPSD_LOG_DEBUG
,
3502 "check_quotas: User \"%s\" identity check failed "
3503 "(err=%d)", p
->users
[i
], mbr_err
);
3512 else if (!strcasecmp(username
, p
->users
[i
]))
3514 #endif /* HAVE_MBR_UID_TO_UUID */
3516 if ((i
< p
->num_users
) == p
->deny_users
)
3518 cupsdLogMessage(CUPSD_LOG_INFO
,
3519 "Denying user \"%s\" access to printer \"%s\"...",
3530 if (AppleQuotas
&& (q
= cupsdFindQuota(p
, username
)) != NULL
)
3533 * TODO: Define these special page count values as constants!
3536 if (q
->page_count
== -4) /* special case: unlimited user */
3538 cupsdLogMessage(CUPSD_LOG_INFO
,
3539 "User \"%s\" request approved for printer %s (%s): "
3541 username
, p
->name
, p
->info
);
3542 q
->page_count
= 0; /* allow user to print */
3545 else if (q
->page_count
== -3) /* quota exceeded */
3547 cupsdLogMessage(CUPSD_LOG_INFO
,
3548 "User \"%s\" request denied for printer %s (%s): "
3549 "quota limit exceeded.",
3550 username
, p
->name
, p
->info
);
3551 q
->page_count
= 2; /* force quota exceeded failure */
3554 else if (q
->page_count
== -2) /* quota disabled for user */
3556 cupsdLogMessage(CUPSD_LOG_INFO
,
3557 "User \"%s\" request denied for printer %s (%s): "
3558 "printing disabled for user.",
3559 username
, p
->name
, p
->info
);
3560 q
->page_count
= 2; /* force quota exceeded failure */
3563 else if (q
->page_count
== -1) /* quota access error */
3565 cupsdLogMessage(CUPSD_LOG_INFO
,
3566 "User \"%s\" request denied for printer %s (%s): "
3567 "unable to determine quota limit.",
3568 username
, p
->name
, p
->info
);
3569 q
->page_count
= 2; /* force quota exceeded failure */
3572 else if (q
->page_count
< 0) /* user not found or other error */
3574 cupsdLogMessage(CUPSD_LOG_INFO
,
3575 "User \"%s\" request denied for printer %s (%s): "
3576 "user disabled / missing quota.",
3577 username
, p
->name
, p
->info
);
3578 q
->page_count
= 2; /* force quota exceeded failure */
3581 else /* page within user limits */
3583 q
->page_count
= 0; /* allow user to print */
3588 #endif /* __APPLE__ */
3589 if (p
->k_limit
|| p
->page_limit
)
3591 if ((q
= cupsdUpdateQuota(p
, username
, 0, 0)) == NULL
)
3593 cupsdLogMessage(CUPSD_LOG_ERROR
,
3594 "Unable to allocate quota data for user \"%s\"!",
3599 if ((q
->k_count
>= p
->k_limit
&& p
->k_limit
) ||
3600 (q
->page_count
>= p
->page_limit
&& p
->page_limit
))
3602 cupsdLogMessage(CUPSD_LOG_INFO
, "User \"%s\" is over the quota limit...",
3609 * If we have gotten this far, we're done!
3617 * 'copy_attribute()' - Copy a single attribute.
3620 static ipp_attribute_t
* /* O - New attribute */
3622 ipp_t
*to
, /* O - Destination request/response */
3623 ipp_attribute_t
*attr
, /* I - Attribute to copy */
3624 int quickcopy
) /* I - Do a quick copy? */
3626 int i
; /* Looping var */
3627 ipp_attribute_t
*toattr
; /* Destination attribute */
3630 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3631 "copy_attribute(%p, %p[%s,%x,%x])", to
, attr
,
3632 attr
->name
? attr
->name
: "(null)", attr
->group_tag
,
3635 switch (attr
->value_tag
& ~IPP_TAG_COPY
)
3638 toattr
= ippAddSeparator(to
);
3641 case IPP_TAG_INTEGER
:
3643 toattr
= ippAddIntegers(to
, attr
->group_tag
, attr
->value_tag
,
3644 attr
->name
, attr
->num_values
, NULL
);
3646 for (i
= 0; i
< attr
->num_values
; i
++)
3647 toattr
->values
[i
].integer
= attr
->values
[i
].integer
;
3650 case IPP_TAG_BOOLEAN
:
3651 toattr
= ippAddBooleans(to
, attr
->group_tag
, attr
->name
,
3652 attr
->num_values
, NULL
);
3654 for (i
= 0; i
< attr
->num_values
; i
++)
3655 toattr
->values
[i
].boolean
= attr
->values
[i
].boolean
;
3658 case IPP_TAG_STRING
:
3661 case IPP_TAG_KEYWORD
:
3663 case IPP_TAG_URISCHEME
:
3664 case IPP_TAG_CHARSET
:
3665 case IPP_TAG_LANGUAGE
:
3666 case IPP_TAG_MIMETYPE
:
3667 toattr
= ippAddStrings(to
, attr
->group_tag
,
3668 (ipp_tag_t
)(attr
->value_tag
| quickcopy
),
3669 attr
->name
, attr
->num_values
, NULL
, NULL
);
3673 for (i
= 0; i
< attr
->num_values
; i
++)
3674 toattr
->values
[i
].string
.text
= attr
->values
[i
].string
.text
;
3678 for (i
= 0; i
< attr
->num_values
; i
++)
3679 toattr
->values
[i
].string
.text
=
3680 _cupsStrAlloc(attr
->values
[i
].string
.text
);
3685 toattr
= ippAddDate(to
, attr
->group_tag
, attr
->name
,
3686 attr
->values
[0].date
);
3689 case IPP_TAG_RESOLUTION
:
3690 toattr
= ippAddResolutions(to
, attr
->group_tag
, attr
->name
,
3691 attr
->num_values
, IPP_RES_PER_INCH
,
3694 for (i
= 0; i
< attr
->num_values
; i
++)
3696 toattr
->values
[i
].resolution
.xres
= attr
->values
[i
].resolution
.xres
;
3697 toattr
->values
[i
].resolution
.yres
= attr
->values
[i
].resolution
.yres
;
3698 toattr
->values
[i
].resolution
.units
= attr
->values
[i
].resolution
.units
;
3702 case IPP_TAG_RANGE
:
3703 toattr
= ippAddRanges(to
, attr
->group_tag
, attr
->name
,
3704 attr
->num_values
, NULL
, NULL
);
3706 for (i
= 0; i
< attr
->num_values
; i
++)
3708 toattr
->values
[i
].range
.lower
= attr
->values
[i
].range
.lower
;
3709 toattr
->values
[i
].range
.upper
= attr
->values
[i
].range
.upper
;
3713 case IPP_TAG_TEXTLANG
:
3714 case IPP_TAG_NAMELANG
:
3715 toattr
= ippAddStrings(to
, attr
->group_tag
,
3716 (ipp_tag_t
)(attr
->value_tag
| quickcopy
),
3717 attr
->name
, attr
->num_values
, NULL
, NULL
);
3721 for (i
= 0; i
< attr
->num_values
; i
++)
3723 toattr
->values
[i
].string
.charset
= attr
->values
[i
].string
.charset
;
3724 toattr
->values
[i
].string
.text
= attr
->values
[i
].string
.text
;
3729 for (i
= 0; i
< attr
->num_values
; i
++)
3732 toattr
->values
[i
].string
.charset
=
3733 _cupsStrAlloc(attr
->values
[i
].string
.charset
);
3735 toattr
->values
[i
].string
.charset
=
3736 toattr
->values
[0].string
.charset
;
3738 toattr
->values
[i
].string
.text
=
3739 _cupsStrAlloc(attr
->values
[i
].string
.text
);
3744 case IPP_TAG_BEGIN_COLLECTION
:
3745 toattr
= ippAddCollections(to
, attr
->group_tag
, attr
->name
,
3746 attr
->num_values
, NULL
);
3748 for (i
= 0; i
< attr
->num_values
; i
++)
3750 toattr
->values
[i
].collection
= ippNew();
3751 copy_attrs(toattr
->values
[i
].collection
, attr
->values
[i
].collection
,
3752 NULL
, IPP_TAG_ZERO
, 0);
3757 toattr
= ippAddIntegers(to
, attr
->group_tag
, attr
->value_tag
,
3758 attr
->name
, attr
->num_values
, NULL
);
3760 for (i
= 0; i
< attr
->num_values
; i
++)
3762 toattr
->values
[i
].unknown
.length
= attr
->values
[i
].unknown
.length
;
3764 if (toattr
->values
[i
].unknown
.length
> 0)
3766 if ((toattr
->values
[i
].unknown
.data
=
3767 malloc(toattr
->values
[i
].unknown
.length
)) == NULL
)
3768 toattr
->values
[i
].unknown
.length
= 0;
3770 memcpy(toattr
->values
[i
].unknown
.data
,
3771 attr
->values
[i
].unknown
.data
,
3772 toattr
->values
[i
].unknown
.length
);
3775 break; /* anti-compiler-warning-code */
3783 * 'copy_attrs()' - Copy attributes from one request to another.
3787 copy_attrs(ipp_t
*to
, /* I - Destination request */
3788 ipp_t
*from
, /* I - Source request */
3789 cups_array_t
*ra
, /* I - Requested attributes */
3790 ipp_tag_t group
, /* I - Group to copy */
3791 int quickcopy
) /* I - Do a quick copy? */
3793 ipp_attribute_t
*fromattr
; /* Source attribute */
3796 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3797 "copy_attrs(to=%p, from=%p, ra=%p, group=%x, quickcopy=%d)",
3798 to
, from
, ra
, group
, quickcopy
);
3803 for (fromattr
= from
->attrs
; fromattr
; fromattr
= fromattr
->next
)
3806 * Filter attributes as needed...
3809 if ((group
!= IPP_TAG_ZERO
&& fromattr
->group_tag
!= group
&&
3810 fromattr
->group_tag
!= IPP_TAG_ZERO
) || !fromattr
->name
)
3813 if (!ra
|| cupsArrayFind(ra
, fromattr
->name
))
3814 copy_attribute(to
, fromattr
, quickcopy
);
3820 * 'copy_banner()' - Copy a banner file to the requests directory for the
3824 static int /* O - Size of banner file in kbytes */
3825 copy_banner(cupsd_client_t
*con
, /* I - Client connection */
3826 cupsd_job_t
*job
, /* I - Job information */
3827 const char *name
) /* I - Name of banner */
3829 int i
; /* Looping var */
3830 int kbytes
; /* Size of banner file in kbytes */
3831 char filename
[1024]; /* Job filename */
3832 cupsd_banner_t
*banner
; /* Pointer to banner */
3833 cups_file_t
*in
; /* Input file */
3834 cups_file_t
*out
; /* Output file */
3835 int ch
; /* Character from file */
3836 char attrname
[255], /* Name of attribute */
3837 *s
; /* Pointer into name */
3838 ipp_attribute_t
*attr
; /* Attribute */
3841 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "copy_banner(%p[%d], %p[%d], %s)",
3842 con
, con
? con
->http
.fd
: -1, job
, job
->id
,
3843 name
? name
: "(null)");
3846 * Find the banner; return if not found or "none"...
3849 if (!name
|| !strcmp(name
, "none") ||
3850 (banner
= cupsdFindBanner(name
)) == NULL
)
3854 * Open the banner and job files...
3857 if (add_file(con
, job
, banner
->filetype
, 0))
3860 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
,
3862 if ((out
= cupsFileOpen(filename
, "w")) == NULL
)
3864 cupsdLogMessage(CUPSD_LOG_ERROR
,
3865 "copy_banner: Unable to create banner job file %s - %s",
3866 filename
, strerror(errno
));
3871 fchmod(cupsFileNumber(out
), 0640);
3872 fchown(cupsFileNumber(out
), RunUser
, Group
);
3875 * Try the localized banner file under the subdirectory...
3878 strlcpy(attrname
, job
->attrs
->attrs
->next
->values
[0].string
.text
,
3880 if (strlen(attrname
) > 2 && attrname
[2] == '-')
3883 * Convert ll-cc to ll_CC...
3887 attrname
[3] = toupper(attrname
[3] & 255);
3888 attrname
[4] = toupper(attrname
[4] & 255);
3891 snprintf(filename
, sizeof(filename
), "%s/banners/%s/%s", DataDir
,
3894 if (access(filename
, 0) && strlen(attrname
) > 2)
3897 * Wasn't able to find "ll_CC" locale file; try the non-national
3898 * localization banner directory.
3903 snprintf(filename
, sizeof(filename
), "%s/banners/%s/%s", DataDir
,
3907 if (access(filename
, 0))
3910 * Use the non-localized banner file.
3913 snprintf(filename
, sizeof(filename
), "%s/banners/%s", DataDir
, name
);
3916 if ((in
= cupsFileOpen(filename
, "r")) == NULL
)
3920 cupsdLogMessage(CUPSD_LOG_ERROR
,
3921 "copy_banner: Unable to open banner template file %s - %s",
3922 filename
, strerror(errno
));
3928 * Parse the file to the end...
3931 while ((ch
= cupsFileGetChar(in
)) != EOF
)
3935 * Get an attribute name...
3938 for (s
= attrname
; (ch
= cupsFileGetChar(in
)) != EOF
;)
3939 if (!isalpha(ch
& 255) && ch
!= '-' && ch
!= '?')
3941 else if (s
< (attrname
+ sizeof(attrname
) - 1))
3951 * Ignore { followed by stuff that is not an attribute name...
3954 cupsFilePrintf(out
, "{%s%c", attrname
, ch
);
3959 * See if it is defined...
3962 if (attrname
[0] == '?')
3967 if (!strcmp(s
, "printer-name"))
3969 cupsFilePuts(out
, job
->dest
);
3972 else if ((attr
= ippFindAttribute(job
->attrs
, s
, IPP_TAG_ZERO
)) == NULL
)
3975 * See if we have a leading question mark...
3978 if (attrname
[0] != '?')
3981 * Nope, write to file as-is; probably a PostScript procedure...
3984 cupsFilePrintf(out
, "{%s}", attrname
);
3991 * Output value(s)...
3994 for (i
= 0; i
< attr
->num_values
; i
++)
3997 cupsFilePutChar(out
, ',');
3999 switch (attr
->value_tag
)
4001 case IPP_TAG_INTEGER
:
4003 if (!strncmp(s
, "time-at-", 8))
4004 cupsFilePuts(out
, cupsdGetDateTime(attr
->values
[i
].integer
));
4006 cupsFilePrintf(out
, "%d", attr
->values
[i
].integer
);
4009 case IPP_TAG_BOOLEAN
:
4010 cupsFilePrintf(out
, "%d", attr
->values
[i
].boolean
);
4013 case IPP_TAG_NOVALUE
:
4014 cupsFilePuts(out
, "novalue");
4017 case IPP_TAG_RANGE
:
4018 cupsFilePrintf(out
, "%d-%d", attr
->values
[i
].range
.lower
,
4019 attr
->values
[i
].range
.upper
);
4022 case IPP_TAG_RESOLUTION
:
4023 cupsFilePrintf(out
, "%dx%d%s", attr
->values
[i
].resolution
.xres
,
4024 attr
->values
[i
].resolution
.yres
,
4025 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4030 case IPP_TAG_STRING
:
4033 case IPP_TAG_KEYWORD
:
4034 case IPP_TAG_CHARSET
:
4035 case IPP_TAG_LANGUAGE
:
4036 if (!strcasecmp(banner
->filetype
->type
, "postscript"))
4039 * Need to quote strings for PS banners...
4044 for (p
= attr
->values
[i
].string
.text
; *p
; p
++)
4046 if (*p
== '(' || *p
== ')' || *p
== '\\')
4048 cupsFilePutChar(out
, '\\');
4049 cupsFilePutChar(out
, *p
);
4051 else if (*p
< 32 || *p
> 126)
4052 cupsFilePrintf(out
, "\\%03o", *p
& 255);
4054 cupsFilePutChar(out
, *p
);
4058 cupsFilePuts(out
, attr
->values
[i
].string
.text
);
4062 break; /* anti-compiler-warning-code */
4066 else if (ch
== '\\') /* Quoted char */
4068 ch
= cupsFileGetChar(in
);
4070 if (ch
!= '{') /* Only do special handling for \{ */
4071 cupsFilePutChar(out
, '\\');
4073 cupsFilePutChar(out
, ch
);
4076 cupsFilePutChar(out
, ch
);
4080 kbytes
= (cupsFileTell(out
) + 1023) / 1024;
4082 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets",
4083 IPP_TAG_INTEGER
)) != NULL
)
4084 attr
->values
[0].integer
+= kbytes
;
4093 * 'copy_file()' - Copy a PPD file or interface script...
4096 static int /* O - 0 = success, -1 = error */
4097 copy_file(const char *from
, /* I - Source file */
4098 const char *to
) /* I - Destination file */
4100 cups_file_t
*src
, /* Source file */
4101 *dst
; /* Destination file */
4102 int bytes
; /* Bytes to read/write */
4103 char buffer
[2048]; /* Copy buffer */
4106 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "copy_file(\"%s\", \"%s\")", from
, to
);
4109 * Open the source and destination file for a copy...
4112 if ((src
= cupsFileOpen(from
, "rb")) == NULL
)
4115 if ((dst
= cupsFileOpen(to
, "wb")) == NULL
)
4122 * Copy the source file to the destination...
4125 while ((bytes
= cupsFileRead(src
, buffer
, sizeof(buffer
))) > 0)
4126 if (cupsFileWrite(dst
, buffer
, bytes
) < bytes
)
4134 * Close both files and return...
4139 return (cupsFileClose(dst
));
4144 * 'copy_model()' - Copy a PPD model file, substituting default values
4148 static int /* O - 0 = success, -1 = error */
4149 copy_model(cupsd_client_t
*con
, /* I - Client connection */
4150 const char *from
, /* I - Source file */
4151 const char *to
) /* I - Destination file */
4153 fd_set input
; /* select() input set */
4154 struct timeval timeout
; /* select() timeout */
4155 int maxfd
; /* Max file descriptor for select() */
4156 char tempfile
[1024]; /* Temporary PPD file */
4157 int tempfd
; /* Temporary PPD file descriptor */
4158 int temppid
; /* Process ID of cups-driverd */
4159 int temppipe
[2]; /* Temporary pipes */
4160 char *argv
[4], /* Command-line arguments */
4161 *envp
[MAX_ENV
]; /* Environment */
4162 cups_file_t
*src
, /* Source file */
4163 *dst
; /* Destination file */
4164 ppd_file_t
*ppd
; /* PPD file */
4165 int bytes
, /* Bytes from pipe */
4166 total
; /* Total bytes from pipe */
4167 char buffer
[2048]; /* Copy buffer */
4168 int i
; /* Looping var */
4169 char option
[PPD_MAX_NAME
], /* Option name */
4170 choice
[PPD_MAX_NAME
]; /* Choice name */
4171 int num_defaults
; /* Number of default options */
4172 cups_option_t
*defaults
; /* Default options */
4173 char cups_protocol
[PPD_MAX_LINE
];
4174 /* cupsProtocol attribute */
4175 int have_letter
, /* Have Letter size */
4176 have_a4
; /* Have A4 size */
4177 #ifdef HAVE_LIBPAPER
4178 char *paper_result
; /* Paper size name from libpaper */
4179 char system_paper
[64]; /* Paper size name buffer */
4180 #endif /* HAVE_LIBPAPER */
4183 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
4184 "copy_model(con=%p, from=\"%s\", to=\"%s\")",
4188 * Run cups-driverd to get the PPD file...
4191 argv
[0] = "cups-driverd";
4193 argv
[2] = (char *)from
;
4196 cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
4198 snprintf(buffer
, sizeof(buffer
), "%s/daemon/cups-driverd", ServerBin
);
4199 snprintf(tempfile
, sizeof(tempfile
), "%s/%d.ppd", TempDir
, con
->http
.fd
);
4200 tempfd
= open(tempfile
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4204 cupsdOpenPipe(temppipe
);
4206 cupsdLogMessage(CUPSD_LOG_DEBUG
,
4207 "copy_model: Running \"cups-driverd cat %s\"...", from
);
4209 if (!cupsdStartProcess(buffer
, argv
, envp
, -1, temppipe
[1], CGIPipes
[1],
4210 -1, -1, 0, &temppid
))
4220 * Wait up to 30 seconds for the PPD file to be copied...
4225 if (temppipe
[0] > CGIPipes
[0])
4226 maxfd
= temppipe
[0] + 1;
4228 maxfd
= CGIPipes
[0] + 1;
4233 * See if we have data ready...
4239 FD_SET(temppipe
[0], &input
);
4240 FD_SET(CGIPipes
[0], &input
);
4242 timeout
.tv_sec
= 30;
4243 timeout
.tv_usec
= 0;
4245 if ((i
= select(maxfd
, &input
, NULL
, NULL
, &timeout
)) < 0)
4255 * We have timed out...
4261 if (FD_ISSET(temppipe
[0], &input
))
4264 * Read the PPD file from the pipe, and write it to the PPD file.
4267 if ((bytes
= read(temppipe
[0], buffer
, sizeof(buffer
))) > 0)
4269 if (write(tempfd
, buffer
, bytes
) < bytes
)
4278 if (FD_ISSET(CGIPipes
[0], &input
))
4288 * No data from cups-deviced...
4291 cupsdLogMessage(CUPSD_LOG_ERROR
, "copy_model: empty PPD file!");
4297 * Read the source file and see what page sizes are supported...
4300 if ((ppd
= ppdOpenFile(tempfile
)) == NULL
)
4306 have_letter
= ppdPageSize(ppd
, "Letter") != NULL
;
4307 have_a4
= ppdPageSize(ppd
, "A4") != NULL
;
4310 * Open the destination (if possible) and set the default options...
4315 cups_protocol
[0] = '\0';
4317 if ((dst
= cupsFileOpen(to
, "rb")) != NULL
)
4320 * Read all of the default lines from the old PPD...
4323 while (cupsFileGets(dst
, buffer
, sizeof(buffer
)))
4324 if (!strncmp(buffer
, "*Default", 8))
4327 * Add the default option...
4330 if (!ppd_parse_line(buffer
, option
, sizeof(option
),
4331 choice
, sizeof(choice
)))
4333 ppd_option_t
*ppdo
; /* PPD option */
4337 * Only add the default if the default hasn't already been
4338 * set and the choice exists in the new PPD...
4341 if (!cupsGetOption(option
, num_defaults
, defaults
) &&
4342 (ppdo
= ppdFindOption(ppd
, option
)) != NULL
&&
4343 ppdFindChoice(ppdo
, choice
))
4344 num_defaults
= cupsAddOption(option
, choice
, num_defaults
,
4348 else if (!strncmp(buffer
, "*cupsProtocol:", 14))
4349 strlcpy(cups_protocol
, buffer
, sizeof(cups_protocol
));
4353 #ifdef HAVE_LIBPAPER
4354 else if ((paper_result
= systempapername()) != NULL
)
4357 * Set the default media sizes from the systemwide default...
4360 strlcpy(system_paper
, paper_result
, sizeof(system_paper
));
4361 system_paper
[0] = toupper(system_paper
[0] & 255);
4363 if ((!strcmp(system_paper
, "Letter") && have_letter
) ||
4364 (!strcmp(system_paper
, "A4") && have_a4
))
4366 num_defaults
= cupsAddOption("PageSize", system_paper
,
4367 num_defaults
, &defaults
);
4368 num_defaults
= cupsAddOption("PageRegion", system_paper
,
4369 num_defaults
, &defaults
);
4370 num_defaults
= cupsAddOption("PaperDimension", system_paper
,
4371 num_defaults
, &defaults
);
4372 num_defaults
= cupsAddOption("ImageableArea", system_paper
,
4373 num_defaults
, &defaults
);
4376 #endif /* HAVE_LIBPAPER */
4380 * Add the default media sizes...
4382 * Note: These values are generally not valid for large-format devices
4383 * like plotters, however it is probably safe to say that those
4384 * users will configure the media size after initially adding
4385 * the device anyways...
4388 if (!DefaultLanguage
||
4389 !strcasecmp(DefaultLanguage
, "C") ||
4390 !strcasecmp(DefaultLanguage
, "POSIX") ||
4391 !strcasecmp(DefaultLanguage
, "en") ||
4392 !strncasecmp(DefaultLanguage
, "en.", 3) ||
4393 !strncasecmp(DefaultLanguage
, "en_US", 5) ||
4394 !strncasecmp(DefaultLanguage
, "en_CA", 5) ||
4395 !strncasecmp(DefaultLanguage
, "fr_CA", 5))
4398 * These are the only locales that will default to "letter" size...
4403 num_defaults
= cupsAddOption("PageSize", "Letter", num_defaults
,
4405 num_defaults
= cupsAddOption("PageRegion", "Letter", num_defaults
,
4407 num_defaults
= cupsAddOption("PaperDimension", "Letter", num_defaults
,
4409 num_defaults
= cupsAddOption("ImageableArea", "Letter", num_defaults
,
4416 * The rest default to "a4" size...
4419 num_defaults
= cupsAddOption("PageSize", "A4", num_defaults
,
4421 num_defaults
= cupsAddOption("PageRegion", "A4", num_defaults
,
4423 num_defaults
= cupsAddOption("PaperDimension", "A4", num_defaults
,
4425 num_defaults
= cupsAddOption("ImageableArea", "A4", num_defaults
,
4433 * Open the source file for a copy...
4436 if ((src
= cupsFileOpen(tempfile
, "rb")) == NULL
)
4438 cupsFreeOptions(num_defaults
, defaults
);
4444 * Open the destination file for a copy...
4447 if ((dst
= cupsFileOpen(to
, "wb")) == NULL
)
4449 cupsFreeOptions(num_defaults
, defaults
);
4456 * Copy the source file to the destination...
4459 while (cupsFileGets(src
, buffer
, sizeof(buffer
)))
4461 if (!strncmp(buffer
, "*Default", 8))
4464 * Check for an previous default option choice...
4467 if (!ppd_parse_line(buffer
, option
, sizeof(option
),
4468 choice
, sizeof(choice
)))
4470 const char *val
; /* Default option value */
4473 if ((val
= cupsGetOption(option
, num_defaults
, defaults
)) != NULL
)
4476 * Substitute the previous choice...
4479 snprintf(buffer
, sizeof(buffer
), "*Default%s: %s", option
, val
);
4484 cupsFilePrintf(dst
, "%s\n", buffer
);
4487 if (cups_protocol
[0])
4488 cupsFilePrintf(dst
, "%s\n", cups_protocol
);
4490 cupsFreeOptions(num_defaults
, defaults
);
4493 * Close both files and return...
4500 return (cupsFileClose(dst
));
4505 * 'copy_job_attrs()' - Copy job attributes.
4509 copy_job_attrs(cupsd_client_t
*con
, /* I - Client connection */
4510 cupsd_job_t
*job
, /* I - Job */
4511 cups_array_t
*ra
) /* I - Requested attributes array */
4513 char job_uri
[HTTP_MAX_URI
]; /* Job URI */
4517 * Send the requested attributes for each job...
4520 httpAssembleURIf(HTTP_URI_CODING_ALL
, job_uri
, sizeof(job_uri
), "ipp", NULL
,
4521 con
->servername
, con
->serverport
, "/jobs/%d",
4524 if (!ra
|| cupsArrayFind(ra
, "job-more-info"))
4525 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
4526 "job-more-info", NULL
, job_uri
);
4528 if (job
->state_value
> IPP_JOB_PROCESSING
&&
4529 (!ra
|| cupsArrayFind(ra
, "job-preserved")))
4530 ippAddBoolean(con
->response
, IPP_TAG_JOB
, "job-preserved",
4531 job
->num_files
> 0);
4533 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
4534 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4535 "job-printer-up-time", time(NULL
));
4537 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
4538 add_job_state_reasons(con
, job
);
4540 if (!ra
|| cupsArrayFind(ra
, "job-uri"))
4541 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
4542 "job-uri", NULL
, job_uri
);
4544 copy_attrs(con
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
4549 * 'copy_printer_attrs()' - Copy printer attributes.
4554 cupsd_client_t
*con
, /* I - Client connection */
4555 cupsd_printer_t
*printer
, /* I - Printer */
4556 cups_array_t
*ra
) /* I - Requested attributes array */
4558 char printer_uri
[HTTP_MAX_URI
];
4560 time_t curtime
; /* Current time */
4561 int i
; /* Looping var */
4562 ipp_attribute_t
*history
; /* History collection */
4566 * Copy the printer attributes to the response using requested-attributes
4567 * and document-format attributes that may be provided by the client.
4570 curtime
= time(NULL
);
4573 if ((!ra
|| cupsArrayFind(ra
, "com.apple.print.recoverable-message")) &&
4574 printer
->recoverable
)
4575 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
4576 "com.apple.print.recoverable-message", NULL
,
4577 printer
->recoverable
);
4578 #endif /* __APPLE__ */
4580 if (printer
->alert
&& (!ra
|| cupsArrayFind(ra
, "printer-alert")))
4581 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_STRING
,
4582 "printer-alert", NULL
, printer
->alert
);
4584 if (printer
->alert_description
&&
4585 (!ra
|| cupsArrayFind(ra
, "printer-alert-description")))
4586 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
4587 "printer-alert-description", NULL
,
4588 printer
->alert_description
);
4590 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
4591 ippAddDate(con
->response
, IPP_TAG_PRINTER
, "printer-current-time",
4592 ippTimeToDate(curtime
));
4594 if (!ra
|| cupsArrayFind(ra
, "printer-error-policy"))
4595 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
4596 "printer-error-policy", NULL
, printer
->error_policy
);
4598 if (!ra
|| cupsArrayFind(ra
, "printer-is-accepting-jobs"))
4599 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
4600 printer
->accepting
);
4602 if (!ra
|| cupsArrayFind(ra
, "printer-is-shared"))
4603 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-shared",
4606 if (!ra
|| cupsArrayFind(ra
, "printer-op-policy"))
4607 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
4608 "printer-op-policy", NULL
, printer
->op_policy
);
4610 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
4611 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state",
4614 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
4615 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
4616 "printer-state-change-time", printer
->state_time
);
4618 if (MaxPrinterHistory
> 0 && printer
->num_history
> 0 &&
4619 cupsArrayFind(ra
, "printer-state-history"))
4622 * Printer history is only sent if specifically requested, so that
4623 * older CUPS/IPP clients won't barf on the collection attributes.
4626 history
= ippAddCollections(con
->response
, IPP_TAG_PRINTER
,
4627 "printer-state-history",
4628 printer
->num_history
, NULL
);
4630 for (i
= 0; i
< printer
->num_history
; i
++)
4631 copy_attrs(history
->values
[i
].collection
= ippNew(), printer
->history
[i
],
4632 NULL
, IPP_TAG_ZERO
, 0);
4635 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
4636 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
4637 "printer-state-message", NULL
, printer
->state_message
);
4639 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
4640 add_printer_state_reasons(con
, printer
);
4642 if (!ra
|| cupsArrayFind(ra
, "printer-type"))
4644 int type
; /* printer-type value */
4648 * Add the CUPS-specific printer-type attribute...
4651 type
= printer
->type
;
4653 if (printer
== DefaultPrinter
)
4654 type
|= CUPS_PRINTER_DEFAULT
;
4656 if (!printer
->accepting
)
4657 type
|= CUPS_PRINTER_REJECTING
;
4659 if (!printer
->shared
)
4660 type
|= CUPS_PRINTER_NOT_SHARED
;
4662 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-type",
4666 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
4667 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
4668 "printer-up-time", curtime
);
4670 if ((!ra
|| cupsArrayFind(ra
, "printer-uri-supported")) &&
4671 !ippFindAttribute(printer
->attrs
, "printer-uri-supported",
4674 httpAssembleURIf(HTTP_URI_CODING_ALL
, printer_uri
, sizeof(printer_uri
),
4675 "ipp", NULL
, con
->servername
, con
->serverport
,
4676 (printer
->type
& CUPS_PRINTER_CLASS
) ?
4677 "/classes/%s" : "/printers/%s", printer
->name
);
4678 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
4679 "printer-uri-supported", NULL
, printer_uri
);
4680 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "printer-uri-supported=\"%s\"",
4684 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
4685 add_queued_job_count(con
, printer
);
4687 copy_attrs(con
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
, 0);
4688 copy_attrs(con
->response
, CommonData
, ra
, IPP_TAG_ZERO
, IPP_TAG_COPY
);
4693 * 'copy_subscription_attrs()' - Copy subscription attributes.
4697 copy_subscription_attrs(
4698 cupsd_client_t
*con
, /* I - Client connection */
4699 cupsd_subscription_t
*sub
, /* I - Subscription */
4700 cups_array_t
*ra
) /* I - Requested attributes array */
4702 ipp_attribute_t
*attr
; /* Current attribute */
4703 char printer_uri
[HTTP_MAX_URI
];
4705 int count
; /* Number of events */
4706 unsigned mask
; /* Current event mask */
4707 const char *name
; /* Current event name */
4711 * Copy the subscription attributes to the response using the
4712 * requested-attributes attribute that may be provided by the client.
4715 if (!ra
|| cupsArrayFind(ra
, "notify-events"))
4717 if ((name
= cupsdEventName((cupsd_eventmask_t
)sub
->mask
)) != NULL
)
4720 * Simple event list...
4723 ippAddString(con
->response
, IPP_TAG_SUBSCRIPTION
,
4724 (ipp_tag_t
)(IPP_TAG_KEYWORD
| IPP_TAG_COPY
),
4725 "notify-events", NULL
, name
);
4730 * Complex event list...
4733 for (mask
= 1, count
= 0; mask
< CUPSD_EVENT_ALL
; mask
<<= 1)
4734 if (sub
->mask
& mask
)
4737 attr
= ippAddStrings(con
->response
, IPP_TAG_SUBSCRIPTION
,
4738 (ipp_tag_t
)(IPP_TAG_KEYWORD
| IPP_TAG_COPY
),
4739 "notify-events", count
, NULL
, NULL
);
4741 for (mask
= 1, count
= 0; mask
< CUPSD_EVENT_ALL
; mask
<<= 1)
4742 if (sub
->mask
& mask
)
4744 attr
->values
[count
].string
.text
=
4745 (char *)cupsdEventName((cupsd_eventmask_t
)mask
);
4752 if (sub
->job
&& (!ra
|| cupsArrayFind(ra
, "notify-job-id")))
4753 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
4754 "notify-job-id", sub
->job
->id
);
4756 if (!sub
->job
&& (!ra
|| cupsArrayFind(ra
, "notify-lease-duration")))
4757 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
4758 "notify-lease-duration", sub
->lease
);
4760 if (sub
->dest
&& (!ra
|| cupsArrayFind(ra
, "notify-printer-uri")))
4762 httpAssembleURIf(HTTP_URI_CODING_ALL
, printer_uri
, sizeof(printer_uri
),
4763 "ipp", NULL
, con
->servername
, con
->serverport
,
4764 "/printers/%s", sub
->dest
->name
);
4765 ippAddString(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_URI
,
4766 "notify-printer-uri", NULL
, printer_uri
);
4769 if (sub
->recipient
&& (!ra
|| cupsArrayFind(ra
, "notify-recipient-uri")))
4770 ippAddString(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_URI
,
4771 "notify-recipient-uri", NULL
, sub
->recipient
);
4772 else if (!ra
|| cupsArrayFind(ra
, "notify-pull-method"))
4773 ippAddString(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_KEYWORD
,
4774 "notify-pull-method", NULL
, "ippget");
4776 if (!ra
|| cupsArrayFind(ra
, "notify-subscriber-user-name"))
4777 ippAddString(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_NAME
,
4778 "notify-subscriber-user-name", NULL
, sub
->owner
);
4780 if (!ra
|| cupsArrayFind(ra
, "notify-subscription-id"))
4781 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
4782 "notify-subscription-id", sub
->id
);
4784 if (!ra
|| cupsArrayFind(ra
, "notify-time-interval"))
4785 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
4786 "notify-time-interval", sub
->interval
);
4788 if (sub
->user_data_len
> 0 && (!ra
|| cupsArrayFind(ra
, "notify-user-data")))
4789 ippAddOctetString(con
->response
, IPP_TAG_SUBSCRIPTION
, "notify-user-data",
4790 sub
->user_data
, sub
->user_data_len
);
4795 * 'create_job()' - Print a file to a printer or class.
4799 create_job(cupsd_client_t
*con
, /* I - Client connection */
4800 ipp_attribute_t
*uri
) /* I - Printer URI */
4802 cupsd_printer_t
*printer
; /* Printer */
4803 cupsd_job_t
*job
; /* New job */
4806 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "create_job(%p[%d], %s)", con
,
4807 con
->http
.fd
, uri
->values
[0].string
.text
);
4810 * Is the destination valid?
4813 if (!cupsdValidateDest(uri
->values
[0].string
.text
, NULL
, &printer
))
4819 send_ipp_status(con
, IPP_NOT_FOUND
,
4820 _("The printer or class was not found."));
4825 * Create the job object...
4828 if ((job
= add_job(con
, printer
, NULL
)) == NULL
)
4831 job
->pending_timeout
= 1;
4834 * Save and log the job...
4839 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job %d] Queued on \"%s\" by \"%s\".",
4840 job
->id
, job
->dest
, job
->username
);
4845 * 'create_requested_array()' - Create an array for the requested-attributes.
4848 static cups_array_t
* /* O - Array of attributes or NULL */
4849 create_requested_array(ipp_t
*request
) /* I - IPP request */
4851 int i
; /* Looping var */
4852 ipp_attribute_t
*requested
; /* requested-attributes attribute */
4853 cups_array_t
*ra
; /* Requested attributes array */
4854 char *value
; /* Current value */
4858 * Get the requested-attributes attribute, and return NULL if we don't
4862 if ((requested
= ippFindAttribute(request
, "requested-attributes",
4863 IPP_TAG_KEYWORD
)) == NULL
)
4867 * If the attribute contains a single "all" keyword, return NULL...
4870 if (requested
->num_values
== 1 &&
4871 !strcmp(requested
->values
[0].string
.text
, "all"))
4875 * Create an array using "strcmp" as the comparison function...
4878 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4880 for (i
= 0; i
< requested
->num_values
; i
++)
4882 value
= requested
->values
[i
].string
.text
;
4884 if (!strcmp(value
, "job-template"))
4886 cupsArrayAdd(ra
, "copies");
4887 cupsArrayAdd(ra
, "copies-default");
4888 cupsArrayAdd(ra
, "copies-supported");
4889 cupsArrayAdd(ra
, "finishings");
4890 cupsArrayAdd(ra
, "finishings-default");
4891 cupsArrayAdd(ra
, "finishings-supported");
4892 cupsArrayAdd(ra
, "job-hold-until");
4893 cupsArrayAdd(ra
, "job-hold-until-default");
4894 cupsArrayAdd(ra
, "job-hold-until-supported");
4895 cupsArrayAdd(ra
, "job-priority");
4896 cupsArrayAdd(ra
, "job-priority-default");
4897 cupsArrayAdd(ra
, "job-priority-supported");
4898 cupsArrayAdd(ra
, "job-sheets");
4899 cupsArrayAdd(ra
, "job-sheets-default");
4900 cupsArrayAdd(ra
, "job-sheets-supported");
4901 cupsArrayAdd(ra
, "media");
4902 cupsArrayAdd(ra
, "media-default");
4903 cupsArrayAdd(ra
, "media-supported");
4904 cupsArrayAdd(ra
, "multiple-document-handling");
4905 cupsArrayAdd(ra
, "multiple-document-handling-default");
4906 cupsArrayAdd(ra
, "multiple-document-handling-supported");
4907 cupsArrayAdd(ra
, "number-up");
4908 cupsArrayAdd(ra
, "number-up-default");
4909 cupsArrayAdd(ra
, "number-up-supported");
4910 cupsArrayAdd(ra
, "orientation-requested");
4911 cupsArrayAdd(ra
, "orientation-requested-default");
4912 cupsArrayAdd(ra
, "orientation-requested-supported");
4913 cupsArrayAdd(ra
, "page-ranges");
4914 cupsArrayAdd(ra
, "page-ranges-supported");
4915 cupsArrayAdd(ra
, "printer-resolution");
4916 cupsArrayAdd(ra
, "printer-resolution-default");
4917 cupsArrayAdd(ra
, "printer-resolution-supported");
4918 cupsArrayAdd(ra
, "print-quality");
4919 cupsArrayAdd(ra
, "print-quality-default");
4920 cupsArrayAdd(ra
, "print-quality-supported");
4921 cupsArrayAdd(ra
, "sides");
4922 cupsArrayAdd(ra
, "sides-default");
4923 cupsArrayAdd(ra
, "sides-supported");
4925 else if (!strcmp(value
, "job-description"))
4927 cupsArrayAdd(ra
, "date-time-at-completed");
4928 cupsArrayAdd(ra
, "date-time-at-creation");
4929 cupsArrayAdd(ra
, "date-time-at-processing");
4930 cupsArrayAdd(ra
, "job-detailed-status-message");
4931 cupsArrayAdd(ra
, "job-document-access-errors");
4932 cupsArrayAdd(ra
, "job-id");
4933 cupsArrayAdd(ra
, "job-impressions");
4934 cupsArrayAdd(ra
, "job-impressions-completed");
4935 cupsArrayAdd(ra
, "job-k-octets");
4936 cupsArrayAdd(ra
, "job-k-octets-processed");
4937 cupsArrayAdd(ra
, "job-media-sheets");
4938 cupsArrayAdd(ra
, "job-media-sheets-completed");
4939 cupsArrayAdd(ra
, "job-message-from-operator");
4940 cupsArrayAdd(ra
, "job-more-info");
4941 cupsArrayAdd(ra
, "job-name");
4942 cupsArrayAdd(ra
, "job-originating-user-name");
4943 cupsArrayAdd(ra
, "job-printer-up-time");
4944 cupsArrayAdd(ra
, "job-printer-uri");
4945 cupsArrayAdd(ra
, "job-state");
4946 cupsArrayAdd(ra
, "job-state-message");
4947 cupsArrayAdd(ra
, "job-state-reasons");
4948 cupsArrayAdd(ra
, "job-uri");
4949 cupsArrayAdd(ra
, "number-of-documents");
4950 cupsArrayAdd(ra
, "number-of-intervening-jobs");
4951 cupsArrayAdd(ra
, "output-device-assigned");
4952 cupsArrayAdd(ra
, "time-at-completed");
4953 cupsArrayAdd(ra
, "time-at-creation");
4954 cupsArrayAdd(ra
, "time-at-processing");
4956 else if (!strcmp(value
, "printer-description"))
4958 cupsArrayAdd(ra
, "charset-configured");
4959 cupsArrayAdd(ra
, "charset-supported");
4960 cupsArrayAdd(ra
, "color-supported");
4961 cupsArrayAdd(ra
, "compression-supported");
4962 cupsArrayAdd(ra
, "document-format-default");
4963 cupsArrayAdd(ra
, "document-format-supported");
4964 cupsArrayAdd(ra
, "generated-natural-language-supported");
4965 cupsArrayAdd(ra
, "ipp-versions-supported");
4966 cupsArrayAdd(ra
, "job-impressions-supported");
4967 cupsArrayAdd(ra
, "job-k-octets-supported");
4968 cupsArrayAdd(ra
, "job-media-sheets-supported");
4969 cupsArrayAdd(ra
, "multiple-document-jobs-supported");
4970 cupsArrayAdd(ra
, "multiple-operation-time-out");
4971 cupsArrayAdd(ra
, "natural-language-configured");
4972 cupsArrayAdd(ra
, "notify-attributes-supported");
4973 cupsArrayAdd(ra
, "notify-lease-duration-default");
4974 cupsArrayAdd(ra
, "notify-lease-duration-supported");
4975 cupsArrayAdd(ra
, "notify-max-events-supported");
4976 cupsArrayAdd(ra
, "notify-events-default");
4977 cupsArrayAdd(ra
, "notify-events-supported");
4978 cupsArrayAdd(ra
, "notify-pull-method-supported");
4979 cupsArrayAdd(ra
, "notify-schemes-supported");
4980 cupsArrayAdd(ra
, "operations-supported");
4981 cupsArrayAdd(ra
, "pages-per-minute");
4982 cupsArrayAdd(ra
, "pages-per-minute-color");
4983 cupsArrayAdd(ra
, "pdl-override-supported");
4984 cupsArrayAdd(ra
, "printer-alert");
4985 cupsArrayAdd(ra
, "printer-alert-description");
4986 cupsArrayAdd(ra
, "printer-current-time");
4987 cupsArrayAdd(ra
, "printer-driver-installer");
4988 cupsArrayAdd(ra
, "printer-info");
4989 cupsArrayAdd(ra
, "printer-is-accepting-jobs");
4990 cupsArrayAdd(ra
, "printer-location");
4991 cupsArrayAdd(ra
, "printer-make-and-model");
4992 cupsArrayAdd(ra
, "printer-message-from-operator");
4993 cupsArrayAdd(ra
, "printer-more-info");
4994 cupsArrayAdd(ra
, "printer-more-info-manufacturer");
4995 cupsArrayAdd(ra
, "printer-name");
4996 cupsArrayAdd(ra
, "printer-state");
4997 cupsArrayAdd(ra
, "printer-state-message");
4998 cupsArrayAdd(ra
, "printer-state-reasons");
4999 cupsArrayAdd(ra
, "printer-up-time");
5000 cupsArrayAdd(ra
, "printer-uri-supported");
5001 cupsArrayAdd(ra
, "queued-job-count");
5002 cupsArrayAdd(ra
, "reference-uri-schemes-supported");
5003 cupsArrayAdd(ra
, "uri-authentication-supported");
5004 cupsArrayAdd(ra
, "uri-security-supported");
5006 else if (!strcmp(value
, "printer-defaults"))
5008 char *name
; /* Option name */
5011 for (name
= (char *)cupsArrayFirst(CommonDefaults
);
5013 name
= (char *)cupsArrayNext(CommonDefaults
))
5014 cupsArrayAdd(ra
, name
);
5016 else if (!strcmp(value
, "subscription-template"))
5018 cupsArrayAdd(ra
, "notify-attributes");
5019 cupsArrayAdd(ra
, "notify-charset");
5020 cupsArrayAdd(ra
, "notify-events");
5021 cupsArrayAdd(ra
, "notify-lease-duration");
5022 cupsArrayAdd(ra
, "notify-natural-language");
5023 cupsArrayAdd(ra
, "notify-pull-method");
5024 cupsArrayAdd(ra
, "notify-recipient-uri");
5025 cupsArrayAdd(ra
, "notify-time-interval");
5026 cupsArrayAdd(ra
, "notify-user-data");
5029 cupsArrayAdd(ra
, value
);
5037 * 'create_subscription()' - Create a notification subscription.
5041 create_subscription(
5042 cupsd_client_t
*con
, /* I - Client connection */
5043 ipp_attribute_t
*uri
) /* I - Printer URI */
5045 http_status_t status
; /* Policy status */
5046 int i
; /* Looping var */
5047 ipp_attribute_t
*attr
; /* Current attribute */
5048 cups_ptype_t dtype
; /* Destination type (printer/class) */
5049 char scheme
[HTTP_MAX_URI
],
5050 /* Scheme portion of URI */
5051 userpass
[HTTP_MAX_URI
],
5052 /* Username portion of URI */
5054 /* Host portion of URI */
5055 resource
[HTTP_MAX_URI
];
5056 /* Resource portion of URI */
5057 int port
; /* Port portion of URI */
5058 cupsd_printer_t
*printer
; /* Printer/class */
5059 cupsd_job_t
*job
; /* Job */
5060 int jobid
; /* Job ID */
5061 cupsd_subscription_t
*sub
; /* Subscription object */
5062 const char *username
, /* requesting-user-name or
5063 authenticated username */
5064 *recipient
, /* notify-recipient-uri */
5065 *pullmethod
; /* notify-pull-method */
5066 ipp_attribute_t
*user_data
; /* notify-user-data */
5067 int interval
, /* notify-time-interval */
5068 lease
; /* notify-lease-duration */
5069 unsigned mask
; /* notify-events */
5070 ipp_attribute_t
*notify_events
,/* notify-events(-default) */
5071 *notify_lease
; /* notify-lease-duration(-default) */
5075 for (attr
= con
->request
->attrs
; attr
; attr
= attr
->next
)
5077 if (attr
->group_tag
!= IPP_TAG_ZERO
)
5078 cupsdLogMessage(CUPSD_LOG_DEBUG
, "g%04x v%04x %s", attr
->group_tag
,
5079 attr
->value_tag
, attr
->name
);
5081 cupsdLogMessage(CUPSD_LOG_DEBUG
, "----SEP----");
5086 * Is the destination valid?
5089 cupsdLogMessage(CUPSD_LOG_DEBUG
,
5090 "cupsdCreateSubscription(con=%p(%d), uri=\"%s\")",
5091 con
, con
->http
.fd
, uri
->values
[0].string
.text
);
5093 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
5094 sizeof(scheme
), userpass
, sizeof(userpass
), host
,
5095 sizeof(host
), &port
, resource
, sizeof(resource
));
5097 if (!strcmp(resource
, "/"))
5099 dtype
= (cups_ptype_t
)0;
5102 else if (!strncmp(resource
, "/printers", 9) && strlen(resource
) <= 10)
5104 dtype
= (cups_ptype_t
)0;
5107 else if (!strncmp(resource
, "/classes", 8) && strlen(resource
) <= 9)
5109 dtype
= CUPS_PRINTER_CLASS
;
5112 else if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
5118 send_ipp_status(con
, IPP_NOT_FOUND
,
5119 _("The printer or class was not found."));
5129 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
,
5132 send_http_error(con
, status
, printer
);
5136 else if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
5138 send_http_error(con
, status
, NULL
);
5143 * Get the user that is requesting the subscription...
5146 username
= get_username(con
);
5149 * Find the first subscription group attribute; return if we have
5153 for (attr
= con
->request
->attrs
; attr
; attr
= attr
->next
)
5154 if (attr
->group_tag
== IPP_TAG_SUBSCRIPTION
)
5159 send_ipp_status(con
, IPP_BAD_REQUEST
,
5160 _("No subscription attributes in request!"));
5165 * Process the subscription attributes in the request...
5168 con
->response
->request
.status
.status_code
= IPP_BAD_REQUEST
;
5176 lease
= DefaultLeaseDuration
;
5178 mask
= CUPSD_EVENT_NONE
;
5182 notify_events
= ippFindAttribute(printer
->attrs
, "notify-events-default",
5184 notify_lease
= ippFindAttribute(printer
->attrs
,
5185 "notify-lease-duration-default",
5189 lease
= notify_lease
->values
[0].integer
;
5193 notify_events
= NULL
;
5194 notify_lease
= NULL
;
5197 while (attr
&& attr
->group_tag
!= IPP_TAG_ZERO
)
5199 if (!strcmp(attr
->name
, "notify-recipient-uri") &&
5200 attr
->value_tag
== IPP_TAG_URI
)
5203 * Validate the recipient scheme against the ServerBin/notifier
5207 char notifier
[1024]; /* Notifier filename */
5210 recipient
= attr
->values
[0].string
.text
;
5212 if (httpSeparateURI(HTTP_URI_CODING_ALL
, recipient
,
5213 scheme
, sizeof(scheme
), userpass
, sizeof(userpass
),
5214 host
, sizeof(host
), &port
,
5215 resource
, sizeof(resource
)) < HTTP_URI_OK
)
5217 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
5218 _("Bad notify-recipient-uri URI \"%s\"!"), recipient
);
5219 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
,
5220 "notify-status-code", IPP_URI_SCHEME
);
5224 snprintf(notifier
, sizeof(notifier
), "%s/notifier/%s", ServerBin
,
5226 if (access(notifier
, X_OK
))
5228 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
5229 _("notify-recipient-uri URI \"%s\" uses unknown "
5230 "scheme!"), recipient
);
5231 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
,
5232 "notify-status-code", IPP_URI_SCHEME
);
5236 else if (!strcmp(attr
->name
, "notify-pull-method") &&
5237 attr
->value_tag
== IPP_TAG_KEYWORD
)
5239 pullmethod
= attr
->values
[0].string
.text
;
5241 if (strcmp(pullmethod
, "ippget"))
5243 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
5244 _("Bad notify-pull-method \"%s\"!"), pullmethod
);
5245 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
,
5246 "notify-status-code", IPP_ATTRIBUTES
);
5250 else if (!strcmp(attr
->name
, "notify-charset") &&
5251 attr
->value_tag
== IPP_TAG_CHARSET
&&
5252 strcmp(attr
->values
[0].string
.text
, "us-ascii") &&
5253 strcmp(attr
->values
[0].string
.text
, "utf-8"))
5255 send_ipp_status(con
, IPP_CHARSET
,
5256 _("Character set \"%s\" not supported!"),
5257 attr
->values
[0].string
.text
);
5260 else if (!strcmp(attr
->name
, "notify-natural-language") &&
5261 (attr
->value_tag
!= IPP_TAG_LANGUAGE
||
5262 strcmp(attr
->values
[0].string
.text
, DefaultLanguage
)))
5264 send_ipp_status(con
, IPP_CHARSET
,
5265 _("Language \"%s\" not supported!"),
5266 attr
->values
[0].string
.text
);
5269 else if (!strcmp(attr
->name
, "notify-user-data") &&
5270 attr
->value_tag
== IPP_TAG_STRING
)
5272 if (attr
->num_values
> 1 || attr
->values
[0].unknown
.length
> 63)
5274 send_ipp_status(con
, IPP_REQUEST_VALUE
,
5275 _("The notify-user-data value is too large "
5276 "(%d > 63 octets)!"),
5277 attr
->values
[0].unknown
.length
);
5283 else if (!strcmp(attr
->name
, "notify-events") &&
5284 attr
->value_tag
== IPP_TAG_KEYWORD
)
5285 notify_events
= attr
;
5286 else if (!strcmp(attr
->name
, "notify-lease-duration") &&
5287 attr
->value_tag
== IPP_TAG_INTEGER
)
5288 lease
= attr
->values
[0].integer
;
5289 else if (!strcmp(attr
->name
, "notify-time-interval") &&
5290 attr
->value_tag
== IPP_TAG_INTEGER
)
5291 interval
= attr
->values
[0].integer
;
5292 else if (!strcmp(attr
->name
, "notify-job-id") &&
5293 attr
->value_tag
== IPP_TAG_INTEGER
)
5294 jobid
= attr
->values
[0].integer
;
5301 for (i
= 0; i
< notify_events
->num_values
; i
++)
5302 mask
|= cupsdEventValue(notify_events
->values
[i
].string
.text
);
5306 cupsdLogMessage(CUPSD_LOG_DEBUG
, "recipient=\"%s\"", recipient
);
5308 cupsdLogMessage(CUPSD_LOG_DEBUG
, "pullmethod=\"%s\"", pullmethod
);
5309 cupsdLogMessage(CUPSD_LOG_DEBUG
, "notify-lease-duration=%d", lease
);
5310 cupsdLogMessage(CUPSD_LOG_DEBUG
, "notify-time-interval=%d", interval
);
5312 if (!recipient
&& !pullmethod
)
5315 if (mask
== CUPSD_EVENT_NONE
)
5318 mask
= CUPSD_EVENT_JOB_COMPLETED
;
5320 mask
= CUPSD_EVENT_PRINTER_STATE_CHANGED
;
5323 send_ipp_status(con
, IPP_BAD_REQUEST
,
5324 _("notify-events not specified!"));
5329 if (MaxLeaseDuration
&& (lease
== 0 || lease
> MaxLeaseDuration
))
5331 cupsdLogMessage(CUPSD_LOG_INFO
,
5332 "create_subscription: Limiting notify-lease-duration to "
5335 lease
= MaxLeaseDuration
;
5340 if ((job
= cupsdFindJob(jobid
)) == NULL
)
5342 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job %d not found!"), jobid
);
5349 sub
= cupsdAddSubscription(mask
, printer
, job
, recipient
, 0);
5352 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Added subscription %d for job %d",
5355 cupsdLogMessage(CUPSD_LOG_DEBUG
,
5356 "Added subscription %d for printer \"%s\"",
5357 sub
->id
, printer
->name
);
5359 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Added subscription %d for server",
5362 sub
->interval
= interval
;
5364 sub
->expire
= lease
? time(NULL
) + lease
: 0;
5366 cupsdSetString(&sub
->owner
, username
);
5370 sub
->user_data_len
= user_data
->values
[0].unknown
.length
;
5371 memcpy(sub
->user_data
, user_data
->values
[0].unknown
.data
,
5372 sub
->user_data_len
);
5375 ippAddSeparator(con
->response
);
5376 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
5377 "notify-subscription-id", sub
->id
);
5379 con
->response
->request
.status
.status_code
= IPP_OK
;
5385 cupsdSaveAllSubscriptions();
5391 * 'delete_printer()' - Remove a printer or class from the system.
5395 delete_printer(cupsd_client_t
*con
, /* I - Client connection */
5396 ipp_attribute_t
*uri
) /* I - URI of printer or class */
5398 http_status_t status
; /* Policy status */
5399 cups_ptype_t dtype
; /* Destination type (printer/class) */
5400 cupsd_printer_t
*printer
; /* Printer/class */
5401 char filename
[1024]; /* Script/PPD filename */
5404 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "delete_printer(%p[%d], %s)", con
,
5405 con
->http
.fd
, uri
->values
[0].string
.text
);
5408 * Do we have a valid URI?
5411 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
5417 send_ipp_status(con
, IPP_NOT_FOUND
,
5418 _("The printer or class was not found."));
5426 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
5428 send_http_error(con
, status
, NULL
);
5433 * Remove old jobs...
5436 cupsdCancelJobs(printer
->name
, NULL
, 1);
5439 * Remove old subscriptions and send a "deleted printer" event...
5442 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED
, printer
, NULL
,
5443 "%s \"%s\" deleted by \"%s\".",
5444 (dtype
& CUPS_PRINTER_CLASS
) ? "Class" : "Printer",
5445 printer
->name
, get_username(con
));
5447 cupsdExpireSubscriptions(printer
, NULL
);
5450 * Remove any old PPD or script files...
5453 snprintf(filename
, sizeof(filename
), "%s/interfaces/%s", ServerRoot
,
5457 snprintf(filename
, sizeof(filename
), "%s/ppd/%s.ppd", ServerRoot
,
5461 if (dtype
& CUPS_PRINTER_CLASS
)
5463 cupsdLogMessage(CUPSD_LOG_INFO
, "Class \"%s\" deleted by \"%s\".",
5464 printer
->name
, get_username(con
));
5466 cupsdDeletePrinter(printer
, 0);
5467 cupsdSaveAllClasses();
5471 cupsdLogMessage(CUPSD_LOG_INFO
, "Printer \"%s\" deleted by \"%s\".",
5472 printer
->name
, get_username(con
));
5474 cupsdDeletePrinter(printer
, 0);
5475 cupsdSaveAllPrinters();
5478 cupsdWritePrintcap();
5481 * Return with no errors...
5484 con
->response
->request
.status
.status_code
= IPP_OK
;
5489 * 'get_default()' - Get the default destination.
5493 get_default(cupsd_client_t
*con
) /* I - Client connection */
5495 http_status_t status
; /* Policy status */
5496 cups_array_t
*ra
; /* Requested attributes array */
5499 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_default(%p[%d])", con
, con
->http
.fd
);
5505 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
5507 send_http_error(con
, status
, NULL
);
5513 ra
= create_requested_array(con
->request
);
5515 copy_printer_attrs(con
, DefaultPrinter
, ra
);
5517 cupsArrayDelete(ra
);
5519 con
->response
->request
.status
.status_code
= IPP_OK
;
5522 send_ipp_status(con
, IPP_NOT_FOUND
, _("No default printer"));
5527 * 'get_devices()' - Get the list of available devices on the local system.
5531 get_devices(cupsd_client_t
*con
) /* I - Client connection */
5533 http_status_t status
; /* Policy status */
5534 ipp_attribute_t
*limit
, /* Limit attribute */
5535 *requested
; /* requested-attributes attribute */
5536 char command
[1024], /* cups-deviced command */
5537 options
[1024], /* Options to pass to command */
5539 /* String for requested attributes */
5542 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_devices(%p[%d])", con
, con
->http
.fd
);
5548 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
5550 send_http_error(con
, status
, NULL
);
5555 * Run cups-deviced command with the given options...
5558 limit
= ippFindAttribute(con
->request
, "limit", IPP_TAG_INTEGER
);
5559 requested
= ippFindAttribute(con
->request
, "requested-attributes",
5563 url_encode_attr(requested
, requested_str
, sizeof(requested_str
));
5565 strlcpy(requested_str
, "requested-attributes=all", sizeof(requested_str
));
5567 snprintf(command
, sizeof(command
), "%s/daemon/cups-deviced", ServerBin
);
5568 snprintf(options
, sizeof(options
),
5570 con
->request
->request
.op
.request_id
,
5571 limit
? limit
->values
[0].integer
: 0, (int)User
,
5574 if (cupsdSendCommand(con
, command
, options
, 1))
5577 * Command started successfully, don't send an IPP response here...
5580 ippDelete(con
->response
);
5581 con
->response
= NULL
;
5586 * Command failed, return "internal error" so the user knows something
5590 send_ipp_status(con
, IPP_INTERNAL_ERROR
,
5591 _("cups-deviced failed to execute."));
5597 * 'get_job_attrs()' - Get job attributes.
5601 get_job_attrs(cupsd_client_t
*con
, /* I - Client connection */
5602 ipp_attribute_t
*uri
) /* I - Job URI */
5604 http_status_t status
; /* Policy status */
5605 ipp_attribute_t
*attr
; /* Current attribute */
5606 int jobid
; /* Job ID */
5607 cupsd_job_t
*job
; /* Current job */
5608 char method
[HTTP_MAX_URI
], /* Method portion of URI */
5609 username
[HTTP_MAX_URI
], /* Username portion of URI */
5610 host
[HTTP_MAX_URI
], /* Host portion of URI */
5611 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
5612 int port
; /* Port portion of URI */
5613 cups_array_t
*ra
; /* Requested attributes array */
5616 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_job_attrs(%p[%d], %s)", con
,
5617 con
->http
.fd
, uri
->values
[0].string
.text
);
5620 * See if we have a job URI or a printer URI...
5623 if (!strcmp(uri
->name
, "printer-uri"))
5626 * Got a printer URI; see if we also have a job-id attribute...
5629 if ((attr
= ippFindAttribute(con
->request
, "job-id",
5630 IPP_TAG_INTEGER
)) == NULL
)
5632 send_ipp_status(con
, IPP_BAD_REQUEST
,
5633 _("Got a printer-uri attribute but no job-id!"));
5637 jobid
= attr
->values
[0].integer
;
5642 * Got a job URI; parse it to get the job ID...
5645 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, method
,
5646 sizeof(method
), username
, sizeof(username
), host
,
5647 sizeof(host
), &port
, resource
, sizeof(resource
));
5649 if (strncmp(resource
, "/jobs/", 6))
5655 send_ipp_status(con
, IPP_BAD_REQUEST
,
5656 _("Bad job-uri attribute \"%s\"!"),
5657 uri
->values
[0].string
.text
);
5661 jobid
= atoi(resource
+ 6);
5665 * See if the job exists...
5668 if ((job
= cupsdFindJob(jobid
)) == NULL
)
5671 * Nope - return a "not found" error...
5674 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist!"), jobid
);
5682 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
5684 send_http_error(con
, status
, NULL
);
5689 * Copy attributes...
5694 ra
= create_requested_array(con
->request
);
5695 copy_job_attrs(con
, job
, ra
);
5696 cupsArrayDelete(ra
);
5698 con
->response
->request
.status
.status_code
= IPP_OK
;
5703 * 'get_jobs()' - Get a list of jobs for the specified printer.
5707 get_jobs(cupsd_client_t
*con
, /* I - Client connection */
5708 ipp_attribute_t
*uri
) /* I - Printer URI */
5710 http_status_t status
; /* Policy status */
5711 ipp_attribute_t
*attr
; /* Current attribute */
5712 const char *dest
; /* Destination */
5713 cups_ptype_t dtype
; /* Destination type (printer/class) */
5714 cups_ptype_t dmask
; /* Destination type mask */
5715 char scheme
[HTTP_MAX_URI
], /* Scheme portion of URI */
5716 username
[HTTP_MAX_URI
], /* Username portion of URI */
5717 host
[HTTP_MAX_URI
], /* Host portion of URI */
5718 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
5719 int port
; /* Port portion of URI */
5720 int completed
; /* Completed jobs? */
5721 int first_job_id
; /* First job ID */
5722 int limit
; /* Maximum number of jobs to return */
5723 int count
; /* Number of jobs that match */
5724 cupsd_job_t
*job
; /* Current job pointer */
5725 cupsd_printer_t
*printer
; /* Printer */
5726 cups_array_t
*list
; /* Which job list... */
5727 cups_array_t
*ra
; /* Requested attributes array */
5730 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_jobs(%p[%d], %s)", con
, con
->http
.fd
,
5731 uri
->values
[0].string
.text
);
5734 * Is the destination valid?
5737 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
5738 sizeof(scheme
), username
, sizeof(username
), host
,
5739 sizeof(host
), &port
, resource
, sizeof(resource
));
5741 if (!strcmp(resource
, "/") ||
5742 (!strncmp(resource
, "/jobs", 5) && strlen(resource
) <= 6))
5745 dtype
= (cups_ptype_t
)0;
5746 dmask
= (cups_ptype_t
)0;
5749 else if (!strncmp(resource
, "/printers", 9) && strlen(resource
) <= 10)
5752 dtype
= (cups_ptype_t
)0;
5753 dmask
= CUPS_PRINTER_CLASS
;
5756 else if (!strncmp(resource
, "/classes", 8) && strlen(resource
) <= 9)
5759 dtype
= CUPS_PRINTER_CLASS
;
5760 dmask
= CUPS_PRINTER_CLASS
;
5763 else if ((dest
= cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
,
5770 send_ipp_status(con
, IPP_NOT_FOUND
,
5771 _("The printer or class was not found."));
5776 dtype
&= CUPS_PRINTER_CLASS
;
5777 dmask
= CUPS_PRINTER_CLASS
;
5786 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
,
5789 send_http_error(con
, status
, printer
);
5793 else if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
5795 send_http_error(con
, status
, NULL
);
5800 * See if the "which-jobs" attribute have been specified...
5803 if ((attr
= ippFindAttribute(con
->request
, "which-jobs",
5804 IPP_TAG_KEYWORD
)) != NULL
&&
5805 !strcmp(attr
->values
[0].string
.text
, "completed"))
5810 else if (attr
&& !strcmp(attr
->values
[0].string
.text
, "all"))
5822 * See if they want to limit the number of jobs reported...
5825 if ((attr
= ippFindAttribute(con
->request
, "limit",
5826 IPP_TAG_INTEGER
)) != NULL
)
5827 limit
= attr
->values
[0].integer
;
5831 if ((attr
= ippFindAttribute(con
->request
, "first-job-id",
5832 IPP_TAG_INTEGER
)) != NULL
)
5833 first_job_id
= attr
->values
[0].integer
;
5838 * See if we only want to see jobs for a specific user...
5841 if ((attr
= ippFindAttribute(con
->request
, "my-jobs",
5842 IPP_TAG_BOOLEAN
)) != NULL
&&
5843 attr
->values
[0].boolean
)
5844 strlcpy(username
, get_username(con
), sizeof(username
));
5848 ra
= create_requested_array(con
->request
);
5851 * OK, build a list of jobs for this printer...
5854 for (count
= 0, job
= (cupsd_job_t
*)cupsArrayFirst(list
);
5855 count
< limit
&& job
;
5856 job
= (cupsd_job_t
*)cupsArrayNext(list
))
5859 * Filter out jobs that don't match...
5862 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_jobs: job->id = %d", job
->id
);
5864 if ((dest
&& strcmp(job
->dest
, dest
)) &&
5865 (!job
->printer
|| !dest
|| strcmp(job
->printer
->name
, dest
)))
5867 if ((job
->dtype
& dmask
) != dtype
&&
5868 (!job
->printer
|| (job
->printer
->type
& dmask
) != dtype
))
5870 if (completed
&& job
->state_value
<= IPP_JOB_STOPPED
)
5873 if (job
->id
< first_job_id
)
5881 if (username
[0] && strcasecmp(username
, job
->username
))
5885 ippAddSeparator(con
->response
);
5889 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_jobs: count = %d", count
);
5891 copy_job_attrs(con
, job
, ra
);
5894 cupsArrayDelete(ra
);
5896 con
->response
->request
.status
.status_code
= IPP_OK
;
5901 * 'get_notifications()' - Get events for a subscription.
5905 get_notifications(cupsd_client_t
*con
) /* I - Client connection */
5907 int i
, j
; /* Looping vars */
5908 http_status_t status
; /* Policy status */
5909 cupsd_subscription_t
*sub
; /* Subscription */
5910 ipp_attribute_t
*ids
, /* notify-subscription-ids */
5911 *sequences
; /* notify-sequence-numbers */
5912 int min_seq
; /* Minimum sequence number */
5913 int interval
; /* Poll interval */
5916 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_notifications(con=%p[%d])",
5920 * Get subscription attributes...
5923 ids
= ippFindAttribute(con
->request
, "notify-subscription-ids",
5925 sequences
= ippFindAttribute(con
->request
, "notify-sequence-numbers",
5930 send_ipp_status(con
, IPP_BAD_REQUEST
,
5931 _("Missing notify-subscription-ids attribute!"));
5936 * Are the subscription IDs valid?
5939 for (i
= 0, interval
= 60; i
< ids
->num_values
; i
++)
5941 if ((sub
= cupsdFindSubscription(ids
->values
[i
].integer
)) == NULL
)
5944 * Bad subscription ID...
5947 send_ipp_status(con
, IPP_NOT_FOUND
,
5948 _("notify-subscription-id %d no good!"),
5949 ids
->values
[i
].integer
);
5957 if ((status
= cupsdCheckPolicy(sub
->dest
? sub
->dest
->op_policy_ptr
:
5959 con
, sub
->owner
)) != HTTP_OK
)
5961 send_http_error(con
, status
, sub
->dest
);
5966 * Check the subscription type and update the interval accordingly.
5969 if (sub
->job
&& sub
->job
->state_value
== IPP_JOB_PROCESSING
&&
5972 else if (sub
->job
&& sub
->job
->state_value
>= IPP_JOB_STOPPED
)
5974 else if (sub
->dest
&& sub
->dest
->state
== IPP_PRINTER_PROCESSING
&&
5980 * Tell the client to poll again in N seconds...
5984 ippAddInteger(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
,
5985 "notify-get-interval", interval
);
5987 ippAddInteger(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
,
5988 "printer-up-time", time(NULL
));
5991 * Copy the subscription event attributes to the response.
5994 con
->response
->request
.status
.status_code
=
5995 interval
? IPP_OK
: IPP_OK_EVENTS_COMPLETE
;
5997 for (i
= 0; i
< ids
->num_values
; i
++)
6000 * Get the subscription and sequence number...
6003 sub
= cupsdFindSubscription(ids
->values
[i
].integer
);
6005 if (sequences
&& i
< sequences
->num_values
)
6006 min_seq
= sequences
->values
[i
].integer
;
6011 * If we don't have any new events, nothing to do here...
6014 if (min_seq
> (sub
->first_event_id
+ sub
->num_events
))
6018 * Otherwise copy all of the new events...
6021 if (sub
->first_event_id
> min_seq
)
6024 j
= min_seq
- sub
->first_event_id
;
6026 for (; j
< sub
->num_events
; j
++)
6028 ippAddSeparator(con
->response
);
6030 copy_attrs(con
->response
, sub
->events
[j
]->attrs
, NULL
,
6031 IPP_TAG_EVENT_NOTIFICATION
, 0);
6038 * 'get_ppd()' - Get a named PPD from the local system.
6042 get_ppd(cupsd_client_t
*con
, /* I - Client connection */
6043 ipp_attribute_t
*uri
) /* I - Printer URI or PPD name */
6045 http_status_t status
; /* Policy status */
6046 cupsd_printer_t
*dest
; /* Destination */
6047 cups_ptype_t dtype
; /* Destination type */
6050 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_ppd(%p[%d], %p[%s=%s])", con
,
6051 con
->http
.fd
, uri
, uri
->name
, uri
->values
[0].string
.text
);
6053 if (!strcmp(uri
->name
, "ppd-name"))
6056 * Return a PPD file from cups-driverd...
6059 char command
[1024], /* cups-driverd command */
6060 options
[1024], /* Options to pass to command */
6061 ppd_name
[1024]; /* ppd-name */
6068 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
6070 send_http_error(con
, status
, NULL
);
6075 * Run cups-driverd command with the given options...
6078 snprintf(command
, sizeof(command
), "%s/daemon/cups-driverd", ServerBin
);
6079 url_encode_string(uri
->values
[0].string
.text
, ppd_name
, sizeof(ppd_name
));
6080 snprintf(options
, sizeof(options
), "get+%d+%s",
6081 con
->request
->request
.op
.request_id
, ppd_name
);
6083 if (cupsdSendCommand(con
, command
, options
, 0))
6086 * Command started successfully, don't send an IPP response here...
6089 ippDelete(con
->response
);
6090 con
->response
= NULL
;
6095 * Command failed, return "internal error" so the user knows something
6099 send_ipp_status(con
, IPP_INTERNAL_ERROR
,
6100 _("cups-driverd failed to execute."));
6103 else if (!strcmp(uri
->name
, "printer-uri") &&
6104 cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &dest
))
6106 int i
; /* Looping var */
6107 char filename
[1024]; /* PPD filename */
6114 if ((status
= cupsdCheckPolicy(dest
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
6116 send_http_error(con
, status
, NULL
);
6121 * See if we need the PPD for a class or remote printer...
6124 snprintf(filename
, sizeof(filename
), "%s/ppd/%s.ppd", ServerRoot
,
6127 if ((dtype
& CUPS_PRINTER_REMOTE
) && access(filename
, 0))
6129 con
->response
->request
.status
.status_code
= CUPS_SEE_OTHER
;
6130 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
6131 "printer-uri", NULL
, dest
->uri
);
6134 else if (dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
6136 for (i
= 0; i
< dest
->num_printers
; i
++)
6137 if (!(dest
->printers
[i
]->type
&
6138 (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
)))
6140 snprintf(filename
, sizeof(filename
), "%s/ppd/%s.ppd", ServerRoot
,
6141 dest
->printers
[i
]->name
);
6143 if (!access(filename
, 0))
6147 if (i
< dest
->num_printers
)
6148 dest
= dest
->printers
[i
];
6151 con
->response
->request
.status
.status_code
= CUPS_SEE_OTHER
;
6152 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
6153 "printer-uri", NULL
, dest
->printers
[0]->uri
);
6159 * Found the printer with the PPD file, now see if there is one...
6162 if ((con
->file
= open(filename
, O_RDONLY
)) < 0)
6164 send_ipp_status(con
, IPP_NOT_FOUND
,
6165 _("The PPD file \"%s\" could not be opened: %s"),
6166 uri
->values
[0].string
.text
, strerror(errno
));
6170 fcntl(con
->file
, F_SETFD
, fcntl(con
->file
, F_GETFD
) | FD_CLOEXEC
);
6174 con
->response
->request
.status
.status_code
= IPP_OK
;
6177 send_ipp_status(con
, IPP_NOT_FOUND
,
6178 _("The PPD file \"%s\" could not be found."),
6179 uri
->values
[0].string
.text
);
6184 * 'get_ppds()' - Get the list of PPD files on the local system.
6188 get_ppds(cupsd_client_t
*con
) /* I - Client connection */
6190 http_status_t status
; /* Policy status */
6191 ipp_attribute_t
*limit
, /* Limit attribute */
6192 *device
, /* ppd-device-id attribute */
6193 *language
, /* ppd-natural-language attribute */
6194 *make
, /* ppd-make attribute */
6195 *model
, /* ppd-make-and-model attribute */
6196 *model_number
, /* ppd-model-number attribute */
6197 *product
, /* ppd-product attribute */
6198 *psversion
, /* ppd-psverion attribute */
6199 *type
, /* ppd-type attribute */
6200 *requested
; /* requested-attributes attribute */
6201 char command
[1024], /* cups-driverd command */
6202 options
[1024], /* Options to pass to command */
6203 device_str
[256],/* Escaped ppd-device-id string */
6205 /* Escaped ppd-natural-language */
6206 make_str
[256], /* Escaped ppd-make string */
6207 model_str
[256], /* Escaped ppd-make-and-model string */
6208 model_number_str
[256],
6209 /* ppd-model-number string */
6211 /* Escaped ppd-product string */
6213 /* Escaped ppd-psversion string */
6214 type_str
[256], /* Escaped ppd-type string */
6216 /* String for requested attributes */
6219 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_ppds(%p[%d])", con
, con
->http
.fd
);
6225 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
6227 send_http_error(con
, status
, NULL
);
6232 * Run cups-driverd command with the given options...
6235 limit
= ippFindAttribute(con
->request
, "limit", IPP_TAG_INTEGER
);
6236 device
= ippFindAttribute(con
->request
, "ppd-device-id", IPP_TAG_TEXT
);
6237 language
= ippFindAttribute(con
->request
, "ppd-natural-language",
6239 make
= ippFindAttribute(con
->request
, "ppd-make", IPP_TAG_TEXT
);
6240 model
= ippFindAttribute(con
->request
, "ppd-make-and-model",
6242 model_number
= ippFindAttribute(con
->request
, "ppd-model-number",
6244 product
= ippFindAttribute(con
->request
, "ppd-product", IPP_TAG_TEXT
);
6245 psversion
= ippFindAttribute(con
->request
, "ppd-psversion", IPP_TAG_TEXT
);
6246 type
= ippFindAttribute(con
->request
, "ppd-type", IPP_TAG_KEYWORD
);
6247 requested
= ippFindAttribute(con
->request
, "requested-attributes",
6251 url_encode_attr(requested
, requested_str
, sizeof(requested_str
));
6253 strlcpy(requested_str
, "requested-attributes=all", sizeof(requested_str
));
6256 url_encode_attr(device
, device_str
, sizeof(device_str
));
6258 device_str
[0] = '\0';
6261 url_encode_attr(language
, language_str
, sizeof(language_str
));
6263 language_str
[0] = '\0';
6266 url_encode_attr(make
, make_str
, sizeof(make_str
));
6271 url_encode_attr(model
, model_str
, sizeof(model_str
));
6273 model_str
[0] = '\0';
6276 snprintf(model_number_str
, sizeof(model_number_str
), "ppd-model-number=%d",
6277 model_number
->values
[0].integer
);
6279 model_number_str
[0] = '\0';
6282 url_encode_attr(product
, product_str
, sizeof(product_str
));
6284 product_str
[0] = '\0';
6287 url_encode_attr(psversion
, psversion_str
, sizeof(psversion_str
));
6289 psversion_str
[0] = '\0';
6292 url_encode_attr(type
, type_str
, sizeof(type_str
));
6296 snprintf(command
, sizeof(command
), "%s/daemon/cups-driverd", ServerBin
);
6297 snprintf(options
, sizeof(options
),
6298 "list+%d+%d+%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
6299 con
->request
->request
.op
.request_id
,
6300 limit
? limit
->values
[0].integer
: 0,
6302 device
? "%20" : "", device_str
,
6303 language
? "%20" : "", language_str
,
6304 make
? "%20" : "", make_str
,
6305 model
? "%20" : "", model_str
,
6306 model_number
? "%20" : "", model_number_str
,
6307 product
? "%20" : "", product_str
,
6308 psversion
? "%20" : "", psversion_str
,
6309 type
? "%20" : "", type_str
);
6311 if (cupsdSendCommand(con
, command
, options
, 0))
6314 * Command started successfully, don't send an IPP response here...
6317 ippDelete(con
->response
);
6318 con
->response
= NULL
;
6323 * Command failed, return "internal error" so the user knows something
6327 send_ipp_status(con
, IPP_INTERNAL_ERROR
,
6328 _("cups-driverd failed to execute."));
6334 * 'get_printer_attrs()' - Get printer attributes.
6338 get_printer_attrs(cupsd_client_t
*con
, /* I - Client connection */
6339 ipp_attribute_t
*uri
) /* I - Printer URI */
6341 http_status_t status
; /* Policy status */
6342 cups_ptype_t dtype
; /* Destination type (printer/class) */
6343 cupsd_printer_t
*printer
; /* Printer/class */
6344 cups_array_t
*ra
; /* Requested attributes array */
6347 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_printer_attrs(%p[%d], %s)", con
,
6348 con
->http
.fd
, uri
->values
[0].string
.text
);
6351 * Is the destination valid?
6354 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
6360 send_ipp_status(con
, IPP_NOT_FOUND
,
6361 _("The printer or class was not found."));
6369 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
6371 send_http_error(con
, status
, printer
);
6376 * Send the attributes...
6379 ra
= create_requested_array(con
->request
);
6381 copy_printer_attrs(con
, printer
, ra
);
6383 cupsArrayDelete(ra
);
6385 con
->response
->request
.status
.status_code
= IPP_OK
;
6390 * 'get_printers()' - Get a list of printers or classes.
6394 get_printers(cupsd_client_t
*con
, /* I - Client connection */
6395 int type
) /* I - 0 or CUPS_PRINTER_CLASS */
6397 http_status_t status
; /* Policy status */
6398 ipp_attribute_t
*attr
; /* Current attribute */
6399 int limit
; /* Max number of printers to return */
6400 int count
; /* Number of printers that match */
6401 cupsd_printer_t
*printer
; /* Current printer pointer */
6402 int printer_type
, /* printer-type attribute */
6403 printer_mask
; /* printer-type-mask attribute */
6404 char *location
; /* Location string */
6405 const char *username
; /* Current user */
6406 char *first_printer_name
; /* first-printer-name attribute */
6407 cups_array_t
*ra
; /* Requested attributes array */
6410 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_printers(%p[%d], %x)", con
,
6411 con
->http
.fd
, type
);
6417 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
6419 send_http_error(con
, status
, NULL
);
6424 * Check for printers...
6427 if (!Printers
|| !cupsArrayCount(Printers
))
6429 send_ipp_status(con
, IPP_NOT_FOUND
, _("No destinations added."));
6434 * See if they want to limit the number of printers reported...
6437 if ((attr
= ippFindAttribute(con
->request
, "limit",
6438 IPP_TAG_INTEGER
)) != NULL
)
6439 limit
= attr
->values
[0].integer
;
6443 if ((attr
= ippFindAttribute(con
->request
, "first-printer-name",
6444 IPP_TAG_NAME
)) != NULL
)
6445 first_printer_name
= attr
->values
[0].string
.text
;
6447 first_printer_name
= NULL
;
6450 * Support filtering...
6453 if ((attr
= ippFindAttribute(con
->request
, "printer-type",
6454 IPP_TAG_ENUM
)) != NULL
)
6455 printer_type
= attr
->values
[0].integer
;
6459 if ((attr
= ippFindAttribute(con
->request
, "printer-type-mask",
6460 IPP_TAG_ENUM
)) != NULL
)
6461 printer_mask
= attr
->values
[0].integer
;
6465 if ((attr
= ippFindAttribute(con
->request
, "printer-location",
6466 IPP_TAG_TEXT
)) != NULL
)
6467 location
= attr
->values
[0].string
.text
;
6471 if (con
->username
[0])
6472 username
= con
->username
;
6473 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name",
6474 IPP_TAG_NAME
)) != NULL
)
6475 username
= attr
->values
[0].string
.text
;
6479 ra
= create_requested_array(con
->request
);
6482 * OK, build a list of printers for this printer...
6485 if (first_printer_name
)
6487 if ((printer
= cupsdFindDest(first_printer_name
)) == NULL
)
6488 printer
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
6491 printer
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
6494 count
< limit
&& printer
;
6495 printer
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
6497 if ((!type
|| (printer
->type
& CUPS_PRINTER_CLASS
) == type
) &&
6498 (printer
->type
& printer_mask
) == printer_type
&&
6499 (!location
|| !printer
->location
||
6500 !strcasecmp(printer
->location
, location
)))
6503 * If HideImplicitMembers is enabled, see if this printer or class
6504 * is a member of an implicit class...
6507 if (ImplicitClasses
&& HideImplicitMembers
&&
6508 printer
->in_implicit_class
)
6512 * If a username is specified, see if it is allowed or denied
6516 if (printer
->num_users
&& username
&& !user_allowed(printer
, username
))
6520 * Add the group separator as needed...
6524 ippAddSeparator(con
->response
);
6529 * Send the attributes...
6532 copy_printer_attrs(con
, printer
, ra
);
6536 cupsArrayDelete(ra
);
6538 con
->response
->request
.status
.status_code
= IPP_OK
;
6543 * 'get_subscription_attrs()' - Get subscription attributes.
6547 get_subscription_attrs(
6548 cupsd_client_t
*con
, /* I - Client connection */
6549 int sub_id
) /* I - Subscription ID */
6551 http_status_t status
; /* Policy status */
6552 cupsd_subscription_t
*sub
; /* Subscription */
6553 cups_array_t
*ra
; /* Requested attributes array */
6556 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
6557 "get_subscription_attrs(con=%p[%d], sub_id=%d)",
6558 con
, con
->http
.fd
, sub_id
);
6561 * Is the subscription ID valid?
6564 if ((sub
= cupsdFindSubscription(sub_id
)) == NULL
)
6567 * Bad subscription ID...
6570 send_ipp_status(con
, IPP_NOT_FOUND
,
6571 _("notify-subscription-id %d no good!"), sub_id
);
6579 if ((status
= cupsdCheckPolicy(sub
->dest
? sub
->dest
->op_policy_ptr
:
6581 con
, sub
->owner
)) != HTTP_OK
)
6583 send_http_error(con
, status
, sub
->dest
);
6588 * Copy the subscription attributes to the response using the
6589 * requested-attributes attribute that may be provided by the client.
6592 ra
= create_requested_array(con
->request
);
6594 copy_subscription_attrs(con
, sub
, ra
);
6596 cupsArrayDelete(ra
);
6598 con
->response
->request
.status
.status_code
= IPP_OK
;
6603 * 'get_subscriptions()' - Get subscriptions.
6607 get_subscriptions(cupsd_client_t
*con
, /* I - Client connection */
6608 ipp_attribute_t
*uri
) /* I - Printer/job URI */
6610 http_status_t status
; /* Policy status */
6611 int count
; /* Number of subscriptions */
6612 int limit
; /* Limit */
6613 cupsd_subscription_t
*sub
; /* Subscription */
6614 cups_array_t
*ra
; /* Requested attributes array */
6615 ipp_attribute_t
*attr
; /* Attribute */
6616 cups_ptype_t dtype
; /* Destination type (printer/class) */
6617 char scheme
[HTTP_MAX_URI
],
6618 /* Scheme portion of URI */
6619 username
[HTTP_MAX_URI
],
6620 /* Username portion of URI */
6622 /* Host portion of URI */
6623 resource
[HTTP_MAX_URI
];
6624 /* Resource portion of URI */
6625 int port
; /* Port portion of URI */
6626 cupsd_job_t
*job
; /* Job pointer */
6627 cupsd_printer_t
*printer
; /* Printer */
6630 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
6631 "get_subscriptions(con=%p[%d], uri=%s)",
6632 con
, con
->http
.fd
, uri
->values
[0].string
.text
);
6635 * Is the destination valid?
6638 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
6639 sizeof(scheme
), username
, sizeof(username
), host
,
6640 sizeof(host
), &port
, resource
, sizeof(resource
));
6642 if (!strcmp(resource
, "/") ||
6643 (!strncmp(resource
, "/jobs", 5) && strlen(resource
) <= 6) ||
6644 (!strncmp(resource
, "/printers", 9) && strlen(resource
) <= 10) ||
6645 (!strncmp(resource
, "/classes", 8) && strlen(resource
) <= 9))
6650 else if (!strncmp(resource
, "/jobs/", 6) && resource
[6])
6653 job
= cupsdFindJob(atoi(resource
+ 6));
6657 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%s does not exist!"),
6662 else if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
6668 send_ipp_status(con
, IPP_NOT_FOUND
,
6669 _("The printer or class was not found."));
6672 else if ((attr
= ippFindAttribute(con
->request
, "notify-job-id",
6673 IPP_TAG_INTEGER
)) != NULL
)
6675 job
= cupsdFindJob(attr
->values
[0].integer
);
6679 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist!"),
6680 attr
->values
[0].integer
);
6691 if ((status
= cupsdCheckPolicy(printer
? printer
->op_policy_ptr
:
6693 con
, NULL
)) != HTTP_OK
)
6695 send_http_error(con
, status
, printer
);
6700 * Copy the subscription attributes to the response using the
6701 * requested-attributes attribute that may be provided by the client.
6704 ra
= create_requested_array(con
->request
);
6706 if ((attr
= ippFindAttribute(con
->request
, "limit",
6707 IPP_TAG_INTEGER
)) != NULL
)
6708 limit
= attr
->values
[0].integer
;
6713 * See if we only want to see subscriptions for a specific user...
6716 if ((attr
= ippFindAttribute(con
->request
, "my-subscriptions",
6717 IPP_TAG_BOOLEAN
)) != NULL
&&
6718 attr
->values
[0].boolean
)
6719 strlcpy(username
, get_username(con
), sizeof(username
));
6723 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
), count
= 0;
6725 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
6726 if ((!printer
|| sub
->dest
== printer
) && (!job
|| sub
->job
== job
) &&
6727 (!username
[0] || !strcasecmp(username
, sub
->owner
)))
6729 ippAddSeparator(con
->response
);
6730 copy_subscription_attrs(con
, sub
, ra
);
6733 if (limit
&& count
>= limit
)
6737 cupsArrayDelete(ra
);
6740 con
->response
->request
.status
.status_code
= IPP_OK
;
6742 send_ipp_status(con
, IPP_NOT_FOUND
, _("No subscriptions found."));
6747 * 'get_username()' - Get the username associated with a request.
6750 static const char * /* O - Username */
6751 get_username(cupsd_client_t
*con
) /* I - Connection */
6753 ipp_attribute_t
*attr
; /* Attribute */
6756 if (con
->username
[0])
6757 return (con
->username
);
6758 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name",
6759 IPP_TAG_NAME
)) != NULL
)
6760 return (attr
->values
[0].string
.text
);
6762 return ("anonymous");
6767 * 'hold_job()' - Hold a print job.
6771 hold_job(cupsd_client_t
*con
, /* I - Client connection */
6772 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
6774 ipp_attribute_t
*attr
, /* Current job-hold-until */
6775 *newattr
; /* New job-hold-until */
6776 int jobid
; /* Job ID */
6777 char method
[HTTP_MAX_URI
], /* Method portion of URI */
6778 username
[HTTP_MAX_URI
], /* Username portion of URI */
6779 host
[HTTP_MAX_URI
], /* Host portion of URI */
6780 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
6781 int port
; /* Port portion of URI */
6782 cupsd_job_t
*job
; /* Job information */
6785 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "hold_job(%p[%d], %s)", con
, con
->http
.fd
,
6786 uri
->values
[0].string
.text
);
6789 * See if we have a job URI or a printer URI...
6792 if (!strcmp(uri
->name
, "printer-uri"))
6795 * Got a printer URI; see if we also have a job-id attribute...
6798 if ((attr
= ippFindAttribute(con
->request
, "job-id",
6799 IPP_TAG_INTEGER
)) == NULL
)
6801 send_ipp_status(con
, IPP_BAD_REQUEST
,
6802 _("Got a printer-uri attribute but no job-id!"));
6806 jobid
= attr
->values
[0].integer
;
6811 * Got a job URI; parse it to get the job ID...
6814 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, method
,
6815 sizeof(method
), username
, sizeof(username
), host
,
6816 sizeof(host
), &port
, resource
, sizeof(resource
));
6818 if (strncmp(resource
, "/jobs/", 6))
6824 send_ipp_status(con
, IPP_BAD_REQUEST
,
6825 _("Bad job-uri attribute \"%s\"!"),
6826 uri
->values
[0].string
.text
);
6830 jobid
= atoi(resource
+ 6);
6834 * See if the job exists...
6837 if ((job
= cupsdFindJob(jobid
)) == NULL
)
6840 * Nope - return a "not found" error...
6843 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist!"), jobid
);
6848 * See if the job is owned by the requesting user...
6851 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
6853 send_http_error(con
, HTTP_UNAUTHORIZED
, NULL
);
6858 * Hold the job and return...
6863 cupsdAddEvent(CUPSD_EVENT_JOB_STATE
, job
->printer
, job
,
6864 "Job held by user.");
6866 if ((newattr
= ippFindAttribute(con
->request
, "job-hold-until",
6867 IPP_TAG_KEYWORD
)) == NULL
)
6868 newattr
= ippFindAttribute(con
->request
, "job-hold-until", IPP_TAG_NAME
);
6870 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
6871 IPP_TAG_KEYWORD
)) == NULL
)
6872 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
6877 * Free the old hold value and copy the new one over...
6880 _cupsStrFree(attr
->values
[0].string
.text
);
6884 attr
->value_tag
= newattr
->value_tag
;
6885 attr
->values
[0].string
.text
=
6886 _cupsStrAlloc(newattr
->values
[0].string
.text
);
6890 attr
->value_tag
= IPP_TAG_KEYWORD
;
6891 attr
->values
[0].string
.text
= _cupsStrAlloc("indefinite");
6895 * Hold job until specified time...
6898 cupsdSetJobHoldUntil(job
, attr
->values
[0].string
.text
);
6900 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED
, job
->printer
, job
,
6901 "Job job-hold-until value changed by user.");
6904 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job %d] Held by \"%s\".", jobid
,
6907 con
->response
->request
.status
.status_code
= IPP_OK
;
6912 * 'move_job()' - Move a job to a new destination.
6916 move_job(cupsd_client_t
*con
, /* I - Client connection */
6917 ipp_attribute_t
*uri
) /* I - Job URI */
6919 http_status_t status
; /* Policy status */
6920 ipp_attribute_t
*attr
; /* Current attribute */
6921 int jobid
; /* Job ID */
6922 cupsd_job_t
*job
; /* Current job */
6923 const char *src
; /* Source printer/class */
6924 cups_ptype_t stype
, /* Source type (printer or class) */
6925 dtype
; /* Destination type (printer/class) */
6926 char scheme
[HTTP_MAX_URI
], /* Scheme portion of URI */
6927 username
[HTTP_MAX_URI
], /* Username portion of URI */
6928 host
[HTTP_MAX_URI
], /* Host portion of URI */
6929 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
6930 int port
; /* Port portion of URI */
6931 cupsd_printer_t
*sprinter
, /* Source printer */
6932 *dprinter
; /* Destination printer */
6935 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "move_job(%p[%d], %s)", con
, con
->http
.fd
,
6936 uri
->values
[0].string
.text
);
6939 * Get the new printer or class...
6942 if ((attr
= ippFindAttribute(con
->request
, "job-printer-uri",
6943 IPP_TAG_URI
)) == NULL
)
6946 * Need job-printer-uri...
6949 send_ipp_status(con
, IPP_BAD_REQUEST
,
6950 _("job-printer-uri attribute missing!"));
6954 if (!cupsdValidateDest(attr
->values
[0].string
.text
, &dtype
, &dprinter
))
6960 send_ipp_status(con
, IPP_NOT_FOUND
,
6961 _("The printer or class was not found."));
6969 if ((status
= cupsdCheckPolicy(dprinter
->op_policy_ptr
, con
,
6972 send_http_error(con
, status
, dprinter
);
6977 * See if we have a job URI or a printer URI...
6980 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
6981 sizeof(scheme
), username
, sizeof(username
), host
,
6982 sizeof(host
), &port
, resource
, sizeof(resource
));
6984 if (!strcmp(uri
->name
, "printer-uri"))
6987 * Got a printer URI; see if we also have a job-id attribute...
6990 if ((attr
= ippFindAttribute(con
->request
, "job-id",
6991 IPP_TAG_INTEGER
)) == NULL
)
6997 if ((src
= cupsdValidateDest(uri
->values
[0].string
.text
, &stype
,
6998 &sprinter
)) == NULL
)
7004 send_ipp_status(con
, IPP_NOT_FOUND
,
7005 _("The printer or class was not found."));
7014 * Otherwise, just move a single job...
7017 if ((job
= cupsdFindJob(attr
->values
[0].integer
)) == NULL
)
7020 * Nope - return a "not found" error...
7023 send_ipp_status(con
, IPP_NOT_FOUND
,
7024 _("Job #%d does not exist!"), attr
->values
[0].integer
);
7030 * Job found, initialize source pointers...
7041 * Got a job URI; parse it to get the job ID...
7044 if (strncmp(resource
, "/jobs/", 6))
7050 send_ipp_status(con
, IPP_BAD_REQUEST
,
7051 _("Bad job-uri attribute \"%s\"!"),
7052 uri
->values
[0].string
.text
);
7057 * See if the job exists...
7060 jobid
= atoi(resource
+ 6);
7062 if ((job
= cupsdFindJob(jobid
)) == NULL
)
7065 * Nope - return a "not found" error...
7068 send_ipp_status(con
, IPP_NOT_FOUND
,
7069 _("Job #%d does not exist!"), jobid
);
7075 * Job found, initialize source pointers...
7084 * Now move the job or jobs...
7090 * See if the job has been completed...
7093 if (job
->state_value
> IPP_JOB_STOPPED
)
7096 * Return a "not-possible" error...
7099 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
7100 _("Job #%d is finished and cannot be altered!"),
7106 * See if the job is owned by the requesting user...
7109 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
7111 send_http_error(con
, HTTP_UNAUTHORIZED
, NULL
);
7116 * Move the job to a different printer or class...
7119 cupsdMoveJob(job
, dprinter
);
7124 * Got the source printer, now look through the jobs...
7127 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
7129 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
7132 * See if the job is pointing at the source printer or has not been
7136 if (strcasecmp(job
->dest
, src
) ||
7137 job
->state_value
> IPP_JOB_STOPPED
)
7141 * See if the job can be moved by the requesting user...
7144 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
7148 * Move the job to a different printer or class...
7151 cupsdMoveJob(job
, dprinter
);
7156 * Start jobs if possible...
7162 * Return with "everything is OK" status...
7165 con
->response
->request
.status
.status_code
= IPP_OK
;
7170 * 'ppd_parse_line()' - Parse a PPD default line.
7173 static int /* O - 0 on success, -1 on failure */
7174 ppd_parse_line(const char *line
, /* I - Line */
7175 char *option
, /* O - Option name */
7176 int olen
, /* I - Size of option name */
7177 char *choice
, /* O - Choice name */
7178 int clen
) /* I - Size of choice name */
7181 * Verify this is a default option line...
7184 if (strncmp(line
, "*Default", 8))
7188 * Read the option name...
7191 for (line
+= 8, olen
--; isalnum(*line
& 255); line
++)
7201 * Skip everything else up to the colon (:)...
7204 while (*line
&& *line
!= ':')
7213 * Now grab the option choice, skipping leading whitespace...
7216 while (isspace(*line
& 255))
7219 for (clen
--; isalnum(*line
& 255); line
++)
7229 * Return with no errors...
7237 * 'print_job()' - Print a file to a printer or class.
7241 print_job(cupsd_client_t
*con
, /* I - Client connection */
7242 ipp_attribute_t
*uri
) /* I - Printer URI */
7244 ipp_attribute_t
*attr
; /* Current attribute */
7245 ipp_attribute_t
*format
; /* Document-format attribute */
7246 const char *default_format
; /* document-format-default value */
7247 cupsd_job_t
*job
; /* New job */
7248 char filename
[1024]; /* Job filename */
7249 mime_type_t
*filetype
; /* Type of file */
7250 char super
[MIME_MAX_SUPER
], /* Supertype of file */
7251 type
[MIME_MAX_TYPE
], /* Subtype of file */
7252 mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
7253 /* Textual name of mime type */
7254 cupsd_printer_t
*printer
; /* Printer data */
7255 struct stat fileinfo
; /* File information */
7256 int kbytes
; /* Size of file */
7257 int compression
; /* Document compression */
7260 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "print_job(%p[%d], %s)", con
, con
->http
.fd
,
7261 uri
->values
[0].string
.text
);
7264 * Validate print file attributes, for now just document-format and
7265 * compression (CUPS only supports "none" and "gzip")...
7268 compression
= CUPS_FILE_NONE
;
7270 if ((attr
= ippFindAttribute(con
->request
, "compression",
7271 IPP_TAG_KEYWORD
)) != NULL
)
7273 if (strcmp(attr
->values
[0].string
.text
, "none")
7275 && strcmp(attr
->values
[0].string
.text
, "gzip")
7276 #endif /* HAVE_LIBZ */
7279 send_ipp_status(con
, IPP_ATTRIBUTES
,
7280 _("Unsupported compression \"%s\"!"),
7281 attr
->values
[0].string
.text
);
7282 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
7283 "compression", NULL
, attr
->values
[0].string
.text
);
7288 if (!strcmp(attr
->values
[0].string
.text
, "gzip"))
7289 compression
= CUPS_FILE_GZIP
;
7290 #endif /* HAVE_LIBZ */
7294 * Do we have a file to print?
7299 send_ipp_status(con
, IPP_BAD_REQUEST
, _("No file!?!"));
7304 * Is the destination valid?
7307 if (!cupsdValidateDest(uri
->values
[0].string
.text
, NULL
, &printer
))
7313 send_ipp_status(con
, IPP_NOT_FOUND
,
7314 _("The printer or class was not found."));
7319 * Is it a format we support?
7322 if ((format
= ippFindAttribute(con
->request
, "document-format",
7323 IPP_TAG_MIMETYPE
)) != NULL
)
7326 * Grab format from client...
7329 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]", super
,
7332 send_ipp_status(con
, IPP_BAD_REQUEST
,
7333 _("Could not scan type \"%s\"!"),
7334 format
->values
[0].string
.text
);
7338 else if ((default_format
= cupsGetOption("document-format",
7339 printer
->num_options
,
7340 printer
->options
)) != NULL
)
7343 * Use default document format...
7346 if (sscanf(default_format
, "%15[^/]/%31[^;]", super
, type
) != 2)
7348 send_ipp_status(con
, IPP_BAD_REQUEST
,
7349 _("Could not scan type \"%s\"!"),
7360 strcpy(super
, "application");
7361 strcpy(type
, "octet-stream");
7364 if (!strcmp(super
, "application") && !strcmp(type
, "octet-stream"))
7367 * Auto-type the file...
7370 ipp_attribute_t
*doc_name
; /* document-name attribute */
7373 cupsdLogMessage(CUPSD_LOG_DEBUG
, "print_job: auto-typing file...");
7375 doc_name
= ippFindAttribute(con
->request
, "document-name", IPP_TAG_NAME
);
7376 filetype
= mimeFileType(MimeDatabase
, con
->filename
,
7377 doc_name
? doc_name
->values
[0].string
.text
: NULL
,
7381 filetype
= mimeType(MimeDatabase
, super
, type
);
7384 filetype
= mimeType(MimeDatabase
, super
, type
);
7388 (!strcmp(super
, "application") && !strcmp(type
, "octet-stream"))))
7391 * Replace the document-format attribute value with the auto-typed or
7395 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
7400 _cupsStrFree(format
->values
[0].string
.text
);
7402 format
->values
[0].string
.text
= _cupsStrAlloc(mimetype
);
7405 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
7406 "document-format", NULL
, mimetype
);
7410 send_ipp_status(con
, IPP_DOCUMENT_FORMAT
,
7411 _("Unsupported format \'%s/%s\'!"), super
, type
);
7412 cupsdLogMessage(CUPSD_LOG_INFO
,
7413 "Hint: Do you have the raw file printing rules enabled?");
7416 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
7417 "document-format", NULL
, format
->values
[0].string
.text
);
7423 * Read any embedded job ticket info from PS files...
7426 if (!strcasecmp(filetype
->super
, "application") &&
7427 !strcasecmp(filetype
->type
, "postscript"))
7428 read_ps_job_ticket(con
);
7431 * Create the job object...
7434 if ((job
= add_job(con
, printer
, filetype
)) == NULL
)
7437 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job %d] Adding job file of type %s/%s.",
7438 job
->id
, filetype
->super
, filetype
->type
);
7441 * Update quota data...
7444 if (stat(con
->filename
, &fileinfo
))
7447 kbytes
= (fileinfo
.st_size
+ 1023) / 1024;
7449 cupsdUpdateQuota(printer
, job
->username
, 0, kbytes
);
7451 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets",
7452 IPP_TAG_INTEGER
)) != NULL
)
7453 attr
->values
[0].integer
+= kbytes
;
7456 * Add the job file...
7459 if (add_file(con
, job
, filetype
, compression
))
7462 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
,
7464 rename(con
->filename
, filename
);
7465 cupsdClearString(&con
->filename
);
7468 * See if we need to add the ending sheet...
7471 cupsdTimeoutJob(job
);
7474 * Log and save the job...
7477 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job %d] Queued on \"%s\" by \"%s\".",
7478 job
->id
, job
->dest
, job
->username
);
7479 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Job %d] hold_until = %d", job
->id
,
7480 (int)job
->hold_until
);
7485 * Start the job if possible...
7493 * 'read_ps_job_ticket()' - Reads a job ticket embedded in a PS file.
7495 * This function only gets called when printing a single PostScript
7496 * file using the Print-Job operation. It doesn't work for Create-Job +
7497 * Send-File, since the job attributes need to be set at job creation
7498 * time for banners to work. The embedded PS job ticket stuff is here
7499 * only to allow the Windows printer driver for CUPS to pass in JCL
7500 * options and IPP attributes which otherwise would be lost.
7502 * The format of a PS job ticket is simple:
7504 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
7506 * %cupsJobTicket: attr1=value1
7507 * %cupsJobTicket: attr2=value2
7509 * %cupsJobTicket: attrN=valueN
7511 * Job ticket lines must appear immediately after the first line that
7512 * specifies PostScript format (%!PS-Adobe-3.0), and CUPS will stop
7513 * looking for job ticket info when it finds a line that does not begin
7514 * with "%cupsJobTicket:".
7516 * The maximum length of a job ticket line, including the prefix, is
7517 * 255 characters to conform with the Adobe DSC.
7519 * Read-only attributes are rejected with a notice to the error log in
7520 * case a malicious user tries anything. Since the job ticket is read
7521 * prior to attribute validation in print_job(), job ticket attributes
7522 * will go through the same validation as IPP attributes...
7526 read_ps_job_ticket(cupsd_client_t
*con
) /* I - Client connection */
7528 cups_file_t
*fp
; /* File to read from */
7529 char line
[256]; /* Line data */
7530 int num_options
; /* Number of options */
7531 cups_option_t
*options
; /* Options */
7532 ipp_t
*ticket
; /* New attributes */
7533 ipp_attribute_t
*attr
, /* Current attribute */
7534 *attr2
, /* Job attribute */
7535 *prev2
; /* Previous job attribute */
7539 * First open the print file...
7542 if ((fp
= cupsFileOpen(con
->filename
, "rb")) == NULL
)
7544 cupsdLogMessage(CUPSD_LOG_ERROR
,
7545 "read_ps_job_ticket: Unable to open PostScript print file "
7552 * Skip the first line...
7555 if (cupsFileGets(fp
, line
, sizeof(line
)) == NULL
)
7557 cupsdLogMessage(CUPSD_LOG_ERROR
,
7558 "read_ps_job_ticket: Unable to read from PostScript print "
7565 if (strncmp(line
, "%!PS-Adobe-", 11))
7568 * Not a DSC-compliant file, so no job ticket info will be available...
7576 * Read job ticket info from the file...
7582 while (cupsFileGets(fp
, line
, sizeof(line
)))
7585 * Stop at the first non-ticket line...
7588 if (strncmp(line
, "%cupsJobTicket:", 15))
7592 * Add the options to the option array...
7595 num_options
= cupsParseOptions(line
+ 15, num_options
, &options
);
7599 * Done with the file; see if we have any options...
7604 if (num_options
== 0)
7608 * OK, convert the options to an attribute list, and apply them to
7613 cupsEncodeOptions(ticket
, num_options
, options
);
7616 * See what the user wants to change.
7619 for (attr
= ticket
->attrs
; attr
; attr
= attr
->next
)
7621 if (attr
->group_tag
!= IPP_TAG_JOB
|| !attr
->name
)
7624 if (!strcmp(attr
->name
, "job-originating-host-name") ||
7625 !strcmp(attr
->name
, "job-originating-user-name") ||
7626 !strcmp(attr
->name
, "job-media-sheets-completed") ||
7627 !strcmp(attr
->name
, "job-k-octets") ||
7628 !strcmp(attr
->name
, "job-id") ||
7629 !strncmp(attr
->name
, "job-state", 9) ||
7630 !strncmp(attr
->name
, "time-at-", 8))
7631 continue; /* Read-only attrs */
7633 if ((attr2
= ippFindAttribute(con
->request
, attr
->name
,
7634 IPP_TAG_ZERO
)) != NULL
)
7637 * Some other value; first free the old value...
7640 if (con
->request
->attrs
== attr2
)
7642 con
->request
->attrs
= attr2
->next
;
7647 for (prev2
= con
->request
->attrs
; prev2
; prev2
= prev2
->next
)
7648 if (prev2
->next
== attr2
)
7650 prev2
->next
= attr2
->next
;
7655 if (con
->request
->last
== attr2
)
7656 con
->request
->last
= prev2
;
7658 _ippFreeAttr(attr2
);
7662 * Add new option by copying it...
7665 copy_attribute(con
->request
, attr
, 0);
7669 * Then free the attribute list and option array...
7673 cupsFreeOptions(num_options
, options
);
7678 * 'reject_jobs()' - Reject print jobs to a printer.
7682 reject_jobs(cupsd_client_t
*con
, /* I - Client connection */
7683 ipp_attribute_t
*uri
) /* I - Printer or class URI */
7685 http_status_t status
; /* Policy status */
7686 cups_ptype_t dtype
; /* Destination type (printer/class) */
7687 cupsd_printer_t
*printer
; /* Printer data */
7688 ipp_attribute_t
*attr
; /* printer-state-message text */
7691 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "reject_jobs(%p[%d], %s)", con
,
7692 con
->http
.fd
, uri
->values
[0].string
.text
);
7695 * Is the destination valid?
7698 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
7704 send_ipp_status(con
, IPP_NOT_FOUND
,
7705 _("The printer or class was not found."));
7713 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
7715 send_http_error(con
, status
, printer
);
7720 * Reject jobs sent to the printer...
7723 printer
->accepting
= 0;
7725 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
7726 IPP_TAG_TEXT
)) == NULL
)
7727 strcpy(printer
->state_message
, "Rejecting Jobs");
7729 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
7730 sizeof(printer
->state_message
));
7732 cupsdAddPrinterHistory(printer
);
7734 if (dtype
& CUPS_PRINTER_CLASS
)
7736 cupsdSaveAllClasses();
7738 cupsdLogMessage(CUPSD_LOG_INFO
, "Class \"%s\" rejecting jobs (\"%s\").",
7739 printer
->name
, get_username(con
));
7743 cupsdSaveAllPrinters();
7745 cupsdLogMessage(CUPSD_LOG_INFO
, "Printer \"%s\" rejecting jobs (\"%s\").",
7746 printer
->name
, get_username(con
));
7750 * Everything was ok, so return OK status...
7753 con
->response
->request
.status
.status_code
= IPP_OK
;
7758 * 'release_job()' - Release a held print job.
7762 release_job(cupsd_client_t
*con
, /* I - Client connection */
7763 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
7765 ipp_attribute_t
*attr
; /* Current attribute */
7766 int jobid
; /* Job ID */
7767 char method
[HTTP_MAX_URI
], /* Method portion of URI */
7768 username
[HTTP_MAX_URI
], /* Username portion of URI */
7769 host
[HTTP_MAX_URI
], /* Host portion of URI */
7770 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
7771 int port
; /* Port portion of URI */
7772 cupsd_job_t
*job
; /* Job information */
7775 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "release_job(%p[%d], %s)", con
,
7776 con
->http
.fd
, uri
->values
[0].string
.text
);
7779 * See if we have a job URI or a printer URI...
7782 if (!strcmp(uri
->name
, "printer-uri"))
7785 * Got a printer URI; see if we also have a job-id attribute...
7788 if ((attr
= ippFindAttribute(con
->request
, "job-id",
7789 IPP_TAG_INTEGER
)) == NULL
)
7791 send_ipp_status(con
, IPP_BAD_REQUEST
,
7792 _("Got a printer-uri attribute but no job-id!"));
7796 jobid
= attr
->values
[0].integer
;
7801 * Got a job URI; parse it to get the job ID...
7804 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, method
,
7805 sizeof(method
), username
, sizeof(username
), host
,
7806 sizeof(host
), &port
, resource
, sizeof(resource
));
7808 if (strncmp(resource
, "/jobs/", 6))
7814 send_ipp_status(con
, IPP_BAD_REQUEST
,
7815 _("Bad job-uri attribute \"%s\"!"),
7816 uri
->values
[0].string
.text
);
7820 jobid
= atoi(resource
+ 6);
7824 * See if the job exists...
7827 if ((job
= cupsdFindJob(jobid
)) == NULL
)
7830 * Nope - return a "not found" error...
7833 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist!"), jobid
);
7838 * See if job is "held"...
7841 if (job
->state_value
!= IPP_JOB_HELD
)
7844 * Nope - return a "not possible" error...
7847 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("Job #%d is not held!"), jobid
);
7852 * See if the job is owned by the requesting user...
7855 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
7857 send_http_error(con
, HTTP_UNAUTHORIZED
, NULL
);
7862 * Reset the job-hold-until value to "no-hold"...
7865 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
7866 IPP_TAG_KEYWORD
)) == NULL
)
7867 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
7871 _cupsStrFree(attr
->values
[0].string
.text
);
7873 attr
->value_tag
= IPP_TAG_KEYWORD
;
7874 attr
->values
[0].string
.text
= _cupsStrAlloc("no-hold");
7876 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED
, job
->printer
, job
,
7877 "Job job-hold-until value changed by user.");
7881 * Release the job and return...
7884 cupsdReleaseJob(job
);
7886 cupsdAddEvent(CUPSD_EVENT_JOB_STATE
, job
->printer
, job
,
7887 "Job released by user.");
7889 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job %d] Released by \"%s\".", jobid
,
7892 con
->response
->request
.status
.status_code
= IPP_OK
;
7897 * 'renew_subscription()' - Renew an existing subscription...
7902 cupsd_client_t
*con
, /* I - Client connection */
7903 int sub_id
) /* I - Subscription ID */
7905 http_status_t status
; /* Policy status */
7906 cupsd_subscription_t
*sub
; /* Subscription */
7907 ipp_attribute_t
*lease
; /* notify-lease-duration */
7910 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
7911 "renew_subscription(con=%p[%d], sub_id=%d)",
7912 con
, con
->http
.fd
, sub_id
);
7915 * Is the subscription ID valid?
7918 if ((sub
= cupsdFindSubscription(sub_id
)) == NULL
)
7921 * Bad subscription ID...
7924 send_ipp_status(con
, IPP_NOT_FOUND
,
7925 _("notify-subscription-id %d no good!"), sub_id
);
7932 * Job subscriptions cannot be renewed...
7935 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
7936 _("Job subscriptions cannot be renewed!"));
7944 if ((status
= cupsdCheckPolicy(sub
->dest
? sub
->dest
->op_policy_ptr
:
7946 con
, sub
->owner
)) != HTTP_OK
)
7948 send_http_error(con
, status
, sub
->dest
);
7953 * Renew the subscription...
7956 lease
= ippFindAttribute(con
->request
, "notify-lease-duration",
7959 sub
->lease
= lease
? lease
->values
[0].integer
: DefaultLeaseDuration
;
7961 if (MaxLeaseDuration
&& (sub
->lease
== 0 || sub
->lease
> MaxLeaseDuration
))
7963 cupsdLogMessage(CUPSD_LOG_INFO
,
7964 "renew_subscription: Limiting notify-lease-duration to "
7967 sub
->lease
= MaxLeaseDuration
;
7970 sub
->expire
= sub
->lease
? time(NULL
) + sub
->lease
: 0;
7972 cupsdSaveAllSubscriptions();
7974 con
->response
->request
.status
.status_code
= IPP_OK
;
7976 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
7977 "notify-lease-duration", sub
->lease
);
7982 * 'restart_job()' - Restart an old print job.
7986 restart_job(cupsd_client_t
*con
, /* I - Client connection */
7987 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
7989 ipp_attribute_t
*attr
; /* Current attribute */
7990 int jobid
; /* Job ID */
7991 char method
[HTTP_MAX_URI
], /* Method portion of URI */
7992 username
[HTTP_MAX_URI
], /* Username portion of URI */
7993 host
[HTTP_MAX_URI
], /* Host portion of URI */
7994 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
7995 int port
; /* Port portion of URI */
7996 cupsd_job_t
*job
; /* Job information */
7999 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "restart_job(%p[%d], %s)", con
,
8000 con
->http
.fd
, uri
->values
[0].string
.text
);
8003 * See if we have a job URI or a printer URI...
8006 if (!strcmp(uri
->name
, "printer-uri"))
8009 * Got a printer URI; see if we also have a job-id attribute...
8012 if ((attr
= ippFindAttribute(con
->request
, "job-id",
8013 IPP_TAG_INTEGER
)) == NULL
)
8015 send_ipp_status(con
, IPP_BAD_REQUEST
,
8016 _("Got a printer-uri attribute but no job-id!"));
8020 jobid
= attr
->values
[0].integer
;
8025 * Got a job URI; parse it to get the job ID...
8028 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, method
,
8029 sizeof(method
), username
, sizeof(username
), host
,
8030 sizeof(host
), &port
, resource
, sizeof(resource
));
8032 if (strncmp(resource
, "/jobs/", 6))
8038 send_ipp_status(con
, IPP_BAD_REQUEST
,
8039 _("Bad job-uri attribute \"%s\"!"),
8040 uri
->values
[0].string
.text
);
8044 jobid
= atoi(resource
+ 6);
8048 * See if the job exists...
8051 if ((job
= cupsdFindJob(jobid
)) == NULL
)
8054 * Nope - return a "not found" error...
8057 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist!"), jobid
);
8062 * See if job is in any of the "completed" states...
8065 if (job
->state_value
<= IPP_JOB_PROCESSING
)
8068 * Nope - return a "not possible" error...
8071 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("Job #%d is not complete!"),
8077 * See if we have retained the job files...
8082 if (!job
->attrs
|| job
->num_files
== 0)
8085 * Nope - return a "not possible" error...
8088 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
8089 _("Job #%d cannot be restarted - no files!"), jobid
);
8094 * See if the job is owned by the requesting user...
8097 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
8099 send_http_error(con
, HTTP_UNAUTHORIZED
, NULL
);
8104 * Restart the job and return...
8107 cupsdRestartJob(job
);
8109 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job %d] Restarted by \"%s\".", jobid
,
8112 con
->response
->request
.status
.status_code
= IPP_OK
;
8117 * 'save_auth_info()' - Save authentication information for a job.
8122 cupsd_client_t
*con
, /* I - Client connection */
8123 cupsd_job_t
*job
, /* I - Job */
8124 ipp_attribute_t
*auth_info
) /* I - auth-info attribute, if any */
8126 int i
; /* Looping var */
8127 char filename
[1024]; /* Job authentication filename */
8128 cups_file_t
*fp
; /* Job authentication file */
8129 char line
[2048]; /* Line for file */
8130 cupsd_printer_t
*dest
; /* Destination printer/class */
8134 * This function saves the in-memory authentication information for
8135 * a job so that it can be used to authenticate with a remote host.
8136 * The information is stored in a file that is readable only by the
8137 * root user. The fields are Base-64 encoded, each on a separate line,
8138 * followed by random number (up to 1024) of newlines to limit the
8139 * amount of information that is exposed.
8141 * Because of the potential for exposing of authentication information,
8142 * this functionality is only enabled when running cupsd as root.
8144 * This caching only works for the Basic and BasicDigest authentication
8145 * types. Digest authentication cannot be cached this way, and in
8146 * the future Kerberos authentication may make all of this obsolete.
8148 * Authentication information is saved whenever an authenticated
8149 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
8152 * This information is deleted after a job is completed or canceled,
8153 * so reprints may require subsequent re-authentication.
8159 if ((dest
= cupsdFindDest(job
->dest
)) == NULL
)
8163 * Create the authentication file and change permissions...
8166 snprintf(filename
, sizeof(filename
), "%s/a%05d", RequestRoot
, job
->id
);
8167 if ((fp
= cupsFileOpen(filename
, "w")) == NULL
)
8169 cupsdLogMessage(CUPSD_LOG_ERROR
,
8170 "Unable to save authentication info to \"%s\" - %s",
8171 filename
, strerror(errno
));
8175 fchown(cupsFileNumber(fp
), 0, 0);
8176 fchmod(cupsFileNumber(fp
), 0400);
8178 if (auth_info
&& auth_info
->num_values
== dest
->num_auth_info_required
)
8181 * Write 1 to 3 auth values...
8184 cupsdClearString(&job
->auth_username
);
8185 cupsdClearString(&job
->auth_domain
);
8186 cupsdClearString(&job
->auth_password
);
8188 for (i
= 0; i
< auth_info
->num_values
; i
++)
8190 httpEncode64_2(line
, sizeof(line
), auth_info
->values
[i
].string
.text
,
8191 strlen(auth_info
->values
[i
].string
.text
));
8192 cupsFilePrintf(fp
, "%s\n", line
);
8194 if (!strcmp(dest
->auth_info_required
[i
], "username"))
8195 cupsdSetStringf(&job
->auth_username
, "AUTH_USERNAME=%s",
8196 auth_info
->values
[i
].string
.text
);
8197 else if (!strcmp(dest
->auth_info_required
[i
], "domain"))
8198 cupsdSetStringf(&job
->auth_domain
, "AUTH_DOMAIN=%s",
8199 auth_info
->values
[i
].string
.text
);
8200 else if (!strcmp(dest
->auth_info_required
[i
], "password"))
8201 cupsdSetStringf(&job
->auth_password
, "AUTH_PASSWORD=%s",
8202 auth_info
->values
[i
].string
.text
);
8205 else if (con
->username
[0])
8208 * Write the authenticated username...
8211 httpEncode64_2(line
, sizeof(line
), con
->username
, strlen(con
->username
));
8212 cupsFilePrintf(fp
, "%s\n", line
);
8214 cupsdSetStringf(&job
->auth_username
, "AUTH_USERNAME=%s", con
->username
);
8215 cupsdClearString(&job
->auth_domain
);
8218 * Write the authenticated password...
8221 httpEncode64_2(line
, sizeof(line
), con
->password
, strlen(con
->password
));
8222 cupsFilePrintf(fp
, "%s\n", line
);
8224 cupsdSetStringf(&job
->auth_password
, "AUTH_PASSWORD=%s", con
->password
);
8228 * Write a random number of newlines to the end of the file...
8231 for (i
= (rand() % 1024); i
>= 0; i
--)
8232 cupsFilePutChar(fp
, '\n');
8235 * Close the file and return...
8240 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
8241 if (con
->gss_have_creds
)
8242 save_krb5_creds(con
, job
);
8243 else if (job
->ccname
)
8244 cupsdClearString(&(job
->ccname
));
8245 #endif /* HAVE_GSSAPI && HAVE_KRB5_H */
8249 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
8251 * 'save_krb5_creds()' - Save Kerberos credentials for the job.
8255 save_krb5_creds(cupsd_client_t
*con
, /* I - Client connection */
8256 cupsd_job_t
*job
) /* I - Job */
8258 # if !defined(HAVE_KRB5_CC_NEW_UNIQUE) && !defined(HAVE_HEIMDAL)
8259 cupsdLogMessage(CUPSD_LOG_INFO
,
8260 "Sorry, your version of Kerberos does not support delegated "
8265 krb5_error_code error
; /* Kerberos error code */
8266 OM_uint32 major_status
, /* Major status code */
8267 minor_status
; /* Minor status code */
8272 * If the weak-linked GSSAPI/Kerberos library is not present, don't try
8276 if (krb5_init_context
== NULL
)
8278 # endif /* __APPLE__ */
8281 * We MUST create a file-based cache because memory-based caches are
8282 * only valid for the current process/address space.
8284 * Due to various bugs/features in different versions of Kerberos, we
8285 * need either the krb5_cc_new_unique() function or Heimdal's version
8286 * of krb5_cc_gen_new() to create a new FILE: credential cache that
8287 * can be passed to the backend. These functions create a temporary
8288 * file (typically in /tmp) containing the cached credentials, which
8289 * are removed when we have successfully printed a job.
8292 # ifdef HAVE_KRB5_CC_NEW_UNIQUE
8293 if ((error
= krb5_cc_new_unique(KerberosContext
, "FILE", NULL
,
8294 &(job
->ccache
))) != 0)
8295 # else /* HAVE_HEIMDAL */
8296 if ((error
= krb5_cc_gen_new(krb_context
, &krb5_fcc_ops
,
8297 &(job
->ccache
))) != 0)
8298 # endif /* HAVE_KRB5_CC_NEW_UNIQUE */
8300 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to create new credentials (%d/%s)",
8301 error
, strerror(errno
));
8307 * Copy the user's credentials to the new cache file...
8310 major_status
= gss_krb5_copy_ccache(&minor_status
, con
->gss_delegated_cred
,
8313 if (GSS_ERROR(major_status
))
8315 cupsdLogGSSMessage(CUPSD_LOG_ERROR
, major_status
, minor_status
,
8316 "Unable to import client credentials cache");
8317 krb5_cc_destroy(KerberosContext
, job
->ccache
);
8323 * Add the KRB5CCNAME environment variable to the job so that the
8324 * backend can use the credentials when printing.
8327 cupsdSetStringf(&(job
->ccname
), "KRB5CCNAME=FILE:%s",
8328 krb5_cc_get_name(KerberosContext
, job
->ccache
));
8330 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "[Job %d] save_krb5_creds: %s", job
->id
,
8332 # endif /* HAVE_KRB5_CC_NEW_UNIQUE || HAVE_HEIMDAL */
8334 #endif /* HAVE_GSSAPI && HAVE_KRB5_H */
8338 * 'send_document()' - Send a file to a printer or class.
8342 send_document(cupsd_client_t
*con
, /* I - Client connection */
8343 ipp_attribute_t
*uri
) /* I - Printer URI */
8345 ipp_attribute_t
*attr
; /* Current attribute */
8346 ipp_attribute_t
*format
; /* Document-format attribute */
8347 const char *default_format
;/* document-format-default value */
8348 int jobid
; /* Job ID number */
8349 cupsd_job_t
*job
; /* Current job */
8350 char job_uri
[HTTP_MAX_URI
],
8352 method
[HTTP_MAX_URI
],
8353 /* Method portion of URI */
8354 username
[HTTP_MAX_URI
],
8355 /* Username portion of URI */
8357 /* Host portion of URI */
8358 resource
[HTTP_MAX_URI
];
8359 /* Resource portion of URI */
8360 int port
; /* Port portion of URI */
8361 mime_type_t
*filetype
; /* Type of file */
8362 char super
[MIME_MAX_SUPER
],
8363 /* Supertype of file */
8364 type
[MIME_MAX_TYPE
],
8365 /* Subtype of file */
8366 mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
8367 /* Textual name of mime type */
8368 char filename
[1024]; /* Job filename */
8369 cupsd_printer_t
*printer
; /* Current printer */
8370 struct stat fileinfo
; /* File information */
8371 int kbytes
; /* Size of file */
8372 int compression
; /* Type of compression */
8375 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "send_document(%p[%d], %s)", con
,
8376 con
->http
.fd
, uri
->values
[0].string
.text
);
8379 * See if we have a job URI or a printer URI...
8382 if (!strcmp(uri
->name
, "printer-uri"))
8385 * Got a printer URI; see if we also have a job-id attribute...
8388 if ((attr
= ippFindAttribute(con
->request
, "job-id",
8389 IPP_TAG_INTEGER
)) == NULL
)
8391 send_ipp_status(con
, IPP_BAD_REQUEST
,
8392 _("Got a printer-uri attribute but no job-id!"));
8396 jobid
= attr
->values
[0].integer
;
8401 * Got a job URI; parse it to get the job ID...
8404 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, method
,
8405 sizeof(method
), username
, sizeof(username
), host
,
8406 sizeof(host
), &port
, resource
, sizeof(resource
));
8408 if (strncmp(resource
, "/jobs/", 6))
8414 send_ipp_status(con
, IPP_BAD_REQUEST
,
8415 _("Bad job-uri attribute \"%s\"!"),
8416 uri
->values
[0].string
.text
);
8420 jobid
= atoi(resource
+ 6);
8424 * See if the job exists...
8427 if ((job
= cupsdFindJob(jobid
)) == NULL
)
8430 * Nope - return a "not found" error...
8433 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist!"), jobid
);
8437 printer
= cupsdFindDest(job
->dest
);
8440 * See if the job is owned by the requesting user...
8443 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
8445 send_http_error(con
, HTTP_UNAUTHORIZED
, NULL
);
8450 * OK, see if the client is sending the document compressed - CUPS
8451 * only supports "none" and "gzip".
8454 compression
= CUPS_FILE_NONE
;
8456 if ((attr
= ippFindAttribute(con
->request
, "compression",
8457 IPP_TAG_KEYWORD
)) != NULL
)
8459 if (strcmp(attr
->values
[0].string
.text
, "none")
8461 && strcmp(attr
->values
[0].string
.text
, "gzip")
8462 #endif /* HAVE_LIBZ */
8465 send_ipp_status(con
, IPP_ATTRIBUTES
, _("Unsupported compression \"%s\"!"),
8466 attr
->values
[0].string
.text
);
8467 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
8468 "compression", NULL
, attr
->values
[0].string
.text
);
8473 if (!strcmp(attr
->values
[0].string
.text
, "gzip"))
8474 compression
= CUPS_FILE_GZIP
;
8475 #endif /* HAVE_LIBZ */
8479 * Do we have a file to print?
8484 send_ipp_status(con
, IPP_BAD_REQUEST
, _("No file!?!"));
8489 * Is it a format we support?
8492 if ((format
= ippFindAttribute(con
->request
, "document-format",
8493 IPP_TAG_MIMETYPE
)) != NULL
)
8496 * Grab format from client...
8499 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]",
8502 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad document-format \"%s\"!"),
8503 format
->values
[0].string
.text
);
8507 else if ((default_format
= cupsGetOption("document-format",
8508 printer
->num_options
,
8509 printer
->options
)) != NULL
)
8512 * Use default document format...
8515 if (sscanf(default_format
, "%15[^/]/%31[^;]", super
, type
) != 2)
8517 send_ipp_status(con
, IPP_BAD_REQUEST
,
8518 _("Could not scan type \"%s\"!"),
8526 * No document format attribute? Auto-type it!
8529 strcpy(super
, "application");
8530 strcpy(type
, "octet-stream");
8533 if (!strcmp(super
, "application") && !strcmp(type
, "octet-stream"))
8536 * Auto-type the file...
8539 ipp_attribute_t
*doc_name
; /* document-name attribute */
8542 cupsdLogMessage(CUPSD_LOG_DEBUG
, "send_document: auto-typing file...");
8544 doc_name
= ippFindAttribute(con
->request
, "document-name", IPP_TAG_NAME
);
8545 filetype
= mimeFileType(MimeDatabase
, con
->filename
,
8546 doc_name
? doc_name
->values
[0].string
.text
: NULL
,
8550 filetype
= mimeType(MimeDatabase
, super
, type
);
8553 filetype
= mimeType(MimeDatabase
, super
, type
);
8557 (!strcmp(super
, "application") && !strcmp(type
, "octet-stream"))))
8560 * Replace the document-format attribute value with the auto-typed or
8564 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
8569 _cupsStrFree(format
->values
[0].string
.text
);
8571 format
->values
[0].string
.text
= _cupsStrAlloc(mimetype
);
8574 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
8575 "document-format", NULL
, mimetype
);
8579 send_ipp_status(con
, IPP_DOCUMENT_FORMAT
,
8580 _("Unsupported format \'%s/%s\'!"), super
, type
);
8581 cupsdLogMessage(CUPSD_LOG_INFO
,
8582 "Hint: Do you have the raw file printing rules enabled?");
8585 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
8586 "document-format", NULL
, format
->values
[0].string
.text
);
8591 if (printer
->filetypes
&& !cupsArrayFind(printer
->filetypes
, filetype
))
8593 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
8596 send_ipp_status(con
, IPP_DOCUMENT_FORMAT
,
8597 _("Unsupported format \'%s\'!"), mimetype
);
8599 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
8600 "document-format", NULL
, mimetype
);
8605 cupsdLogMessage(CUPSD_LOG_DEBUG
,
8606 "send_document: request file type is %s/%s.",
8607 filetype
->super
, filetype
->type
);
8610 * Add the file to the job...
8615 if (add_file(con
, job
, filetype
, compression
))
8618 if (stat(con
->filename
, &fileinfo
))
8621 kbytes
= (fileinfo
.st_size
+ 1023) / 1024;
8623 cupsdUpdateQuota(printer
, job
->username
, 0, kbytes
);
8625 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets",
8626 IPP_TAG_INTEGER
)) != NULL
)
8627 attr
->values
[0].integer
+= kbytes
;
8629 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
,
8631 rename(con
->filename
, filename
);
8633 cupsdClearString(&con
->filename
);
8635 cupsdLogMessage(CUPSD_LOG_INFO
,
8636 "File of type %s/%s queued in job #%d by \"%s\".",
8637 filetype
->super
, filetype
->type
, job
->id
, job
->username
);
8640 * Start the job if this is the last document...
8643 if ((attr
= ippFindAttribute(con
->request
, "last-document",
8644 IPP_TAG_BOOLEAN
)) != NULL
&&
8645 attr
->values
[0].boolean
)
8648 * See if we need to add the ending sheet...
8651 cupsdTimeoutJob(job
);
8653 if (job
->state_value
== IPP_JOB_STOPPED
)
8655 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
8656 job
->state_value
= IPP_JOB_PENDING
;
8658 else if (job
->state_value
== IPP_JOB_HELD
)
8660 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
8661 IPP_TAG_KEYWORD
)) == NULL
)
8662 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
8664 if (!attr
|| !strcmp(attr
->values
[0].string
.text
, "no-hold"))
8666 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
8667 job
->state_value
= IPP_JOB_PENDING
;
8674 * Start the job if possible... Since cupsdCheckJobs() can cancel a
8675 * job if it doesn't print, we need to re-find the job afterwards...
8682 job
= cupsdFindJob(jobid
);
8686 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
8687 IPP_TAG_KEYWORD
)) == NULL
)
8688 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
8690 if (!attr
|| !strcmp(attr
->values
[0].string
.text
, "no-hold"))
8692 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
8693 job
->state_value
= IPP_JOB_HELD
;
8694 job
->hold_until
= time(NULL
) + 60;
8700 * Fill in the response info...
8703 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
8706 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
,
8709 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", jobid
);
8711 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
8712 job
? job
->state_value
: IPP_JOB_CANCELED
);
8713 add_job_state_reasons(con
, job
);
8715 con
->response
->request
.status
.status_code
= IPP_OK
;
8720 * 'send_http_error()' - Send a HTTP error back to the IPP client.
8725 cupsd_client_t
*con
, /* I - Client connection */
8726 http_status_t status
, /* I - HTTP status code */
8727 cupsd_printer_t
*printer
) /* I - Printer, if any */
8729 cupsdLogMessage(CUPSD_LOG_ERROR
, "%s: %s",
8730 ippOpString(con
->request
->request
.op
.operation_id
),
8731 httpStatus(status
));
8733 if (status
== HTTP_UNAUTHORIZED
&&
8734 printer
&& printer
->num_auth_info_required
> 0 &&
8735 !strcmp(printer
->auth_info_required
[0], "negotiate"))
8736 cupsdSendError(con
, status
, AUTH_NEGOTIATE
);
8738 cupsdSendError(con
, status
, AUTH_NONE
);
8740 ippDelete(con
->response
);
8741 con
->response
= NULL
;
8748 * 'send_ipp_status()' - Send a status back to the IPP client.
8752 send_ipp_status(cupsd_client_t
*con
, /* I - Client connection */
8753 ipp_status_t status
, /* I - IPP status code */
8754 const char *message
, /* I - Status message */
8755 ...) /* I - Additional args as needed */
8757 va_list ap
; /* Pointer to additional args */
8758 char formatted
[1024]; /* Formatted errror message */
8761 va_start(ap
, message
);
8762 vsnprintf(formatted
, sizeof(formatted
),
8763 _cupsLangString(con
->language
, message
), ap
);
8766 cupsdLogMessage(CUPSD_LOG_DEBUG
, "%s %s: %s",
8767 ippOpString(con
->request
->request
.op
.operation_id
),
8768 ippErrorString(status
), formatted
);
8770 con
->response
->request
.status
.status_code
= status
;
8772 if (ippFindAttribute(con
->response
, "attributes-charset",
8773 IPP_TAG_ZERO
) == NULL
)
8774 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
8775 "attributes-charset", NULL
, DefaultCharset
);
8777 if (ippFindAttribute(con
->response
, "attributes-natural-language",
8778 IPP_TAG_ZERO
) == NULL
)
8779 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
8780 "attributes-natural-language", NULL
, DefaultLanguage
);
8782 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
8783 "status-message", NULL
, formatted
);
8788 * 'set_default()' - Set the default destination...
8792 set_default(cupsd_client_t
*con
, /* I - Client connection */
8793 ipp_attribute_t
*uri
) /* I - Printer URI */
8795 http_status_t status
; /* Policy status */
8796 cups_ptype_t dtype
; /* Destination type (printer/class) */
8797 cupsd_printer_t
*printer
; /* Printer */
8800 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "set_default(%p[%d], %s)", con
,
8801 con
->http
.fd
, uri
->values
[0].string
.text
);
8804 * Is the destination valid?
8807 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
8813 send_ipp_status(con
, IPP_NOT_FOUND
,
8814 _("The printer or class was not found."));
8822 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
8824 send_http_error(con
, status
, NULL
);
8829 * Set it as the default...
8832 DefaultPrinter
= printer
;
8834 cupsdSaveAllPrinters();
8835 cupsdSaveAllClasses();
8837 cupsdWritePrintcap();
8839 cupsdLogMessage(CUPSD_LOG_INFO
,
8840 "Default destination set to \"%s\" by \"%s\".",
8841 printer
->name
, get_username(con
));
8844 * Everything was ok, so return OK status...
8847 con
->response
->request
.status
.status_code
= IPP_OK
;
8852 * 'set_job_attrs()' - Set job attributes.
8856 set_job_attrs(cupsd_client_t
*con
, /* I - Client connection */
8857 ipp_attribute_t
*uri
) /* I - Job URI */
8859 ipp_attribute_t
*attr
, /* Current attribute */
8860 *attr2
; /* Job attribute */
8861 int jobid
; /* Job ID */
8862 cupsd_job_t
*job
; /* Current job */
8863 char method
[HTTP_MAX_URI
],
8864 /* Method portion of URI */
8865 username
[HTTP_MAX_URI
],
8866 /* Username portion of URI */
8868 /* Host portion of URI */
8869 resource
[HTTP_MAX_URI
];
8870 /* Resource portion of URI */
8871 int port
; /* Port portion of URI */
8872 int event
; /* Events? */
8875 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "set_job_attrs(%p[%d], %s)", con
,
8876 con
->http
.fd
, uri
->values
[0].string
.text
);
8879 * Start with "everything is OK" status...
8882 con
->response
->request
.status
.status_code
= IPP_OK
;
8885 * See if we have a job URI or a printer URI...
8888 if (!strcmp(uri
->name
, "printer-uri"))
8891 * Got a printer URI; see if we also have a job-id attribute...
8894 if ((attr
= ippFindAttribute(con
->request
, "job-id",
8895 IPP_TAG_INTEGER
)) == NULL
)
8897 send_ipp_status(con
, IPP_BAD_REQUEST
,
8898 _("Got a printer-uri attribute but no job-id!"));
8902 jobid
= attr
->values
[0].integer
;
8907 * Got a job URI; parse it to get the job ID...
8910 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, method
,
8911 sizeof(method
), username
, sizeof(username
), host
,
8912 sizeof(host
), &port
, resource
, sizeof(resource
));
8914 if (strncmp(resource
, "/jobs/", 6))
8920 send_ipp_status(con
, IPP_BAD_REQUEST
,
8921 _("Bad job-uri attribute \"%s\"!"),
8922 uri
->values
[0].string
.text
);
8926 jobid
= atoi(resource
+ 6);
8930 * See if the job exists...
8933 if ((job
= cupsdFindJob(jobid
)) == NULL
)
8936 * Nope - return a "not found" error...
8939 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist!"), jobid
);
8944 * See if the job has been completed...
8947 if (job
->state_value
> IPP_JOB_STOPPED
)
8950 * Return a "not-possible" error...
8953 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
8954 _("Job #%d is finished and cannot be altered!"), jobid
);
8959 * See if the job is owned by the requesting user...
8962 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
8964 send_http_error(con
, HTTP_UNAUTHORIZED
, NULL
);
8969 * See what the user wants to change.
8976 for (attr
= con
->request
->attrs
; attr
; attr
= attr
->next
)
8978 if (attr
->group_tag
!= IPP_TAG_JOB
|| !attr
->name
)
8981 if (!strcmp(attr
->name
, "attributes-charset") ||
8982 !strcmp(attr
->name
, "attributes-natural-language") ||
8983 !strcmp(attr
->name
, "document-compression") ||
8984 !strcmp(attr
->name
, "document-format") ||
8985 !strcmp(attr
->name
, "job-detailed-status-messages") ||
8986 !strcmp(attr
->name
, "job-document-access-errors") ||
8987 !strcmp(attr
->name
, "job-id") ||
8988 !strcmp(attr
->name
, "job-k-octets") ||
8989 !strcmp(attr
->name
, "job-originating-host-name") ||
8990 !strcmp(attr
->name
, "job-originating-user-name") ||
8991 !strcmp(attr
->name
, "job-printer-up-time") ||
8992 !strcmp(attr
->name
, "job-printer-uri") ||
8993 !strcmp(attr
->name
, "job-sheets") ||
8994 !strcmp(attr
->name
, "job-state-message") ||
8995 !strcmp(attr
->name
, "job-state-reasons") ||
8996 !strcmp(attr
->name
, "job-uri") ||
8997 !strcmp(attr
->name
, "number-of-documents") ||
8998 !strcmp(attr
->name
, "number-of-intervening-jobs") ||
8999 !strcmp(attr
->name
, "output-device-assigned") ||
9000 !strncmp(attr
->name
, "date-time-at-", 13) ||
9001 !strncmp(attr
->name
, "job-impressions", 15) ||
9002 !strncmp(attr
->name
, "job-k-octets", 12) ||
9003 !strncmp(attr
->name
, "job-media-sheets", 16) ||
9004 !strncmp(attr
->name
, "time-at-", 8))
9010 send_ipp_status(con
, IPP_ATTRIBUTES_NOT_SETTABLE
,
9011 _("%s cannot be changed."), attr
->name
);
9013 if ((attr2
= copy_attribute(con
->response
, attr
, 0)) != NULL
)
9014 attr2
->group_tag
= IPP_TAG_UNSUPPORTED_GROUP
;
9019 if (!strcmp(attr
->name
, "job-priority"))
9022 * Change the job priority...
9025 if (attr
->value_tag
!= IPP_TAG_INTEGER
)
9027 send_ipp_status(con
, IPP_REQUEST_VALUE
, _("Bad job-priority value!"));
9029 if ((attr2
= copy_attribute(con
->response
, attr
, 0)) != NULL
)
9030 attr2
->group_tag
= IPP_TAG_UNSUPPORTED_GROUP
;
9032 else if (job
->state_value
>= IPP_JOB_PROCESSING
)
9034 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
9035 _("Job is completed and cannot be changed."));
9038 else if (con
->response
->request
.status
.status_code
== IPP_OK
)
9040 cupsdSetJobPriority(job
, attr
->values
[0].integer
);
9041 event
|= CUPSD_EVENT_JOB_CONFIG_CHANGED
;
9044 else if (!strcmp(attr
->name
, "job-state"))
9047 * Change the job state...
9050 if (attr
->value_tag
!= IPP_TAG_ENUM
)
9052 send_ipp_status(con
, IPP_REQUEST_VALUE
, _("Bad job-state value!"));
9054 if ((attr2
= copy_attribute(con
->response
, attr
, 0)) != NULL
)
9055 attr2
->group_tag
= IPP_TAG_UNSUPPORTED_GROUP
;
9059 switch (attr
->values
[0].integer
)
9061 case IPP_JOB_PENDING
:
9063 if (job
->state_value
> IPP_JOB_HELD
)
9065 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
9066 _("Job state cannot be changed."));
9069 else if (con
->response
->request
.status
.status_code
== IPP_OK
)
9071 job
->state
->values
[0].integer
= attr
->values
[0].integer
;
9072 job
->state_value
= (ipp_jstate_t
)attr
->values
[0].integer
;
9074 event
|= CUPSD_EVENT_JOB_STATE
;
9078 case IPP_JOB_PROCESSING
:
9079 case IPP_JOB_STOPPED
:
9080 if (job
->state_value
!= attr
->values
[0].integer
)
9082 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
9083 _("Job state cannot be changed."));
9088 case IPP_JOB_CANCELED
:
9089 case IPP_JOB_ABORTED
:
9090 case IPP_JOB_COMPLETED
:
9091 if (job
->state_value
> IPP_JOB_PROCESSING
)
9093 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
9094 _("Job state cannot be changed."));
9097 else if (con
->response
->request
.status
.status_code
== IPP_OK
)
9098 cupsdCancelJob(job
, 0, (ipp_jstate_t
)attr
->values
[0].integer
);
9103 else if (con
->response
->request
.status
.status_code
!= IPP_OK
)
9105 else if ((attr2
= ippFindAttribute(job
->attrs
, attr
->name
,
9106 IPP_TAG_ZERO
)) != NULL
)
9109 * Some other value; first free the old value...
9112 if (job
->attrs
->prev
)
9113 job
->attrs
->prev
->next
= attr2
->next
;
9115 job
->attrs
->attrs
= attr2
->next
;
9117 if (job
->attrs
->last
== attr2
)
9118 job
->attrs
->last
= job
->attrs
->prev
;
9120 _ippFreeAttr(attr2
);
9123 * Then copy the attribute...
9126 copy_attribute(job
->attrs
, attr
, 0);
9129 * See if the job-name or job-hold-until is being changed.
9132 if (!strcmp(attr
->name
, "job-hold-until"))
9134 cupsdSetJobHoldUntil(job
, attr
->values
[0].string
.text
);
9136 if (!strcmp(attr
->values
[0].string
.text
, "no-hold"))
9137 cupsdReleaseJob(job
);
9141 event
|= CUPSD_EVENT_JOB_CONFIG_CHANGED
| CUPSD_EVENT_JOB_STATE
;
9144 else if (attr
->value_tag
== IPP_TAG_DELETEATTR
)
9147 * Delete the attribute...
9150 if ((attr2
= ippFindAttribute(job
->attrs
, attr
->name
,
9151 IPP_TAG_ZERO
)) != NULL
)
9153 if (job
->attrs
->prev
)
9154 job
->attrs
->prev
->next
= attr2
->next
;
9156 job
->attrs
->attrs
= attr2
->next
;
9158 if (attr2
== job
->attrs
->last
)
9159 job
->attrs
->last
= job
->attrs
->prev
;
9161 _ippFreeAttr(attr2
);
9163 event
|= CUPSD_EVENT_JOB_CONFIG_CHANGED
;
9169 * Add new option by copying it...
9172 copy_attribute(job
->attrs
, attr
, 0);
9174 event
|= CUPSD_EVENT_JOB_CONFIG_CHANGED
;
9185 * Send events as needed...
9188 if (event
& CUPSD_EVENT_JOB_STATE
)
9189 cupsdAddEvent(CUPSD_EVENT_JOB_STATE
, job
->printer
, job
,
9190 job
->state_value
== IPP_JOB_HELD
?
9191 "Job held by user." : "Job restarted by user.");
9193 if (event
& CUPSD_EVENT_JOB_CONFIG_CHANGED
)
9194 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED
, job
->printer
, job
,
9195 "Job options changed by user.");
9198 * Start jobs if possible...
9206 * 'set_printer_defaults()' - Set printer default options from a request.
9210 set_printer_defaults(
9211 cupsd_client_t
*con
, /* I - Client connection */
9212 cupsd_printer_t
*printer
) /* I - Printer */
9214 int i
; /* Looping var */
9215 ipp_attribute_t
*attr
; /* Current attribute */
9216 int namelen
; /* Length of attribute name */
9217 char name
[256], /* New attribute name */
9218 value
[256]; /* String version of integer attrs */
9221 for (attr
= con
->request
->attrs
; attr
; attr
= attr
->next
)
9224 * Skip non-printer attributes...
9227 if (attr
->group_tag
!= IPP_TAG_PRINTER
|| !attr
->name
)
9230 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "set_printer_defaults: %s", attr
->name
);
9232 if (!strcmp(attr
->name
, "job-sheets-default"))
9235 * Only allow keywords and names...
9238 if (attr
->value_tag
!= IPP_TAG_NAME
&& attr
->value_tag
!= IPP_TAG_KEYWORD
)
9242 * Only allow job-sheets-default to be set when running without a
9243 * system high classification level...
9249 cupsdSetString(&printer
->job_sheets
[0], attr
->values
[0].string
.text
);
9251 if (attr
->num_values
> 1)
9252 cupsdSetString(&printer
->job_sheets
[1], attr
->values
[1].string
.text
);
9254 cupsdSetString(&printer
->job_sheets
[1], "none");
9256 else if (!strcmp(attr
->name
, "requesting-user-name-allowed"))
9258 cupsdFreePrinterUsers(printer
);
9260 printer
->deny_users
= 0;
9262 if (attr
->value_tag
== IPP_TAG_NAME
&&
9263 (attr
->num_values
> 1 ||
9264 strcmp(attr
->values
[0].string
.text
, "all")))
9266 for (i
= 0; i
< attr
->num_values
; i
++)
9267 cupsdAddPrinterUser(printer
, attr
->values
[i
].string
.text
);
9270 else if (!strcmp(attr
->name
, "requesting-user-name-denied"))
9272 cupsdFreePrinterUsers(printer
);
9274 printer
->deny_users
= 1;
9276 if (attr
->value_tag
== IPP_TAG_NAME
&&
9277 (attr
->num_values
> 1 ||
9278 strcmp(attr
->values
[0].string
.text
, "none")))
9280 for (i
= 0; i
< attr
->num_values
; i
++)
9281 cupsdAddPrinterUser(printer
, attr
->values
[i
].string
.text
);
9284 else if (!strcmp(attr
->name
, "job-quota-period"))
9286 if (attr
->value_tag
!= IPP_TAG_INTEGER
)
9289 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Setting job-quota-period to %d...",
9290 attr
->values
[0].integer
);
9291 cupsdFreeQuotas(printer
);
9293 printer
->quota_period
= attr
->values
[0].integer
;
9295 else if (!strcmp(attr
->name
, "job-k-limit"))
9297 if (attr
->value_tag
!= IPP_TAG_INTEGER
)
9300 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Setting job-k-limit to %d...",
9301 attr
->values
[0].integer
);
9302 cupsdFreeQuotas(printer
);
9304 printer
->k_limit
= attr
->values
[0].integer
;
9306 else if (!strcmp(attr
->name
, "job-page-limit"))
9308 if (attr
->value_tag
!= IPP_TAG_INTEGER
)
9311 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Setting job-page-limit to %d...",
9312 attr
->values
[0].integer
);
9313 cupsdFreeQuotas(printer
);
9315 printer
->page_limit
= attr
->values
[0].integer
;
9317 else if (!strcmp(attr
->name
, "printer-op-policy"))
9319 cupsd_policy_t
*p
; /* Policy */
9322 if (attr
->value_tag
!= IPP_TAG_NAME
)
9325 if ((p
= cupsdFindPolicy(attr
->values
[0].string
.text
)) != NULL
)
9327 cupsdLogMessage(CUPSD_LOG_DEBUG
,
9328 "Setting printer-op-policy to \"%s\"...",
9329 attr
->values
[0].string
.text
);
9330 cupsdSetString(&printer
->op_policy
, attr
->values
[0].string
.text
);
9331 printer
->op_policy_ptr
= p
;
9335 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
9336 _("Unknown printer-op-policy \"%s\"."),
9337 attr
->values
[0].string
.text
);
9341 else if (!strcmp(attr
->name
, "printer-error-policy"))
9343 if (attr
->value_tag
!= IPP_TAG_NAME
&& attr
->value_tag
!= IPP_TAG_KEYWORD
)
9346 if (strcmp(attr
->values
[0].string
.text
, "abort-job") &&
9347 strcmp(attr
->values
[0].string
.text
, "retry-job") &&
9348 strcmp(attr
->values
[0].string
.text
, "stop-printer"))
9350 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
9351 _("Unknown printer-error-policy \"%s\"."),
9352 attr
->values
[0].string
.text
);
9356 cupsdLogMessage(CUPSD_LOG_DEBUG
,
9357 "Setting printer-error-policy to \"%s\"...",
9358 attr
->values
[0].string
.text
);
9359 cupsdSetString(&printer
->error_policy
, attr
->values
[0].string
.text
);
9363 * Skip any other non-default attributes...
9366 namelen
= strlen(attr
->name
);
9367 if (namelen
< 9 || strcmp(attr
->name
+ namelen
- 8, "-default") ||
9368 namelen
> (sizeof(name
) - 1) || attr
->num_values
!= 1)
9372 * OK, anything else must be a user-defined default...
9375 strlcpy(name
, attr
->name
, sizeof(name
));
9376 name
[namelen
- 8] = '\0'; /* Strip "-default" */
9378 switch (attr
->value_tag
)
9380 case IPP_TAG_DELETEATTR
:
9381 printer
->num_options
= cupsRemoveOption(name
,
9382 printer
->num_options
,
9383 &(printer
->options
));
9384 cupsdLogMessage(CUPSD_LOG_DEBUG
,
9385 "Deleting %s", attr
->name
);
9389 case IPP_TAG_KEYWORD
:
9391 printer
->num_options
= cupsAddOption(name
,
9392 attr
->values
[0].string
.text
,
9393 printer
->num_options
,
9394 &(printer
->options
));
9395 cupsdLogMessage(CUPSD_LOG_DEBUG
,
9396 "Setting %s to \"%s\"...", attr
->name
,
9397 attr
->values
[0].string
.text
);
9400 case IPP_TAG_BOOLEAN
:
9401 printer
->num_options
= cupsAddOption(name
,
9402 attr
->values
[0].boolean
?
9404 printer
->num_options
,
9405 &(printer
->options
));
9406 cupsdLogMessage(CUPSD_LOG_DEBUG
,
9407 "Setting %s to %s...", attr
->name
,
9408 attr
->values
[0].boolean
? "true" : "false");
9411 case IPP_TAG_INTEGER
:
9413 sprintf(value
, "%d", attr
->values
[0].integer
);
9414 printer
->num_options
= cupsAddOption(name
, value
,
9415 printer
->num_options
,
9416 &(printer
->options
));
9417 cupsdLogMessage(CUPSD_LOG_DEBUG
,
9418 "Setting %s to %s...", attr
->name
, value
);
9421 case IPP_TAG_RANGE
:
9422 sprintf(value
, "%d-%d", attr
->values
[0].range
.lower
,
9423 attr
->values
[0].range
.upper
);
9424 printer
->num_options
= cupsAddOption(name
, value
,
9425 printer
->num_options
,
9426 &(printer
->options
));
9427 cupsdLogMessage(CUPSD_LOG_DEBUG
,
9428 "Setting %s to %s...", attr
->name
, value
);
9431 case IPP_TAG_RESOLUTION
:
9432 sprintf(value
, "%dx%d%s", attr
->values
[0].resolution
.xres
,
9433 attr
->values
[0].resolution
.yres
,
9434 attr
->values
[0].resolution
.units
== IPP_RES_PER_INCH
?
9436 printer
->num_options
= cupsAddOption(name
, value
,
9437 printer
->num_options
,
9438 &(printer
->options
));
9439 cupsdLogMessage(CUPSD_LOG_DEBUG
,
9440 "Setting %s to %s...", attr
->name
, value
);
9444 /* Do nothing for other values */
9452 * 'start_printer()' - Start a printer.
9456 start_printer(cupsd_client_t
*con
, /* I - Client connection */
9457 ipp_attribute_t
*uri
) /* I - Printer URI */
9459 http_status_t status
; /* Policy status */
9460 cups_ptype_t dtype
; /* Destination type (printer/class) */
9461 cupsd_printer_t
*printer
; /* Printer data */
9464 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_printer(%p[%d], %s)", con
,
9465 con
->http
.fd
, uri
->values
[0].string
.text
);
9468 * Is the destination valid?
9471 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
9477 send_ipp_status(con
, IPP_NOT_FOUND
,
9478 _("The printer or class was not found."));
9486 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
9488 send_http_error(con
, status
, printer
);
9493 * Start the printer...
9496 printer
->state_message
[0] = '\0';
9498 cupsdStartPrinter(printer
, 1);
9500 if (dtype
& CUPS_PRINTER_CLASS
)
9501 cupsdLogMessage(CUPSD_LOG_INFO
, "Class \"%s\" started by \"%s\".",
9502 printer
->name
, get_username(con
));
9504 cupsdLogMessage(CUPSD_LOG_INFO
, "Printer \"%s\" started by \"%s\".",
9505 printer
->name
, get_username(con
));
9510 * Everything was ok, so return OK status...
9513 con
->response
->request
.status
.status_code
= IPP_OK
;
9518 * 'stop_printer()' - Stop a printer.
9522 stop_printer(cupsd_client_t
*con
, /* I - Client connection */
9523 ipp_attribute_t
*uri
) /* I - Printer URI */
9525 http_status_t status
; /* Policy status */
9526 cups_ptype_t dtype
; /* Destination type (printer/class) */
9527 cupsd_printer_t
*printer
; /* Printer data */
9528 ipp_attribute_t
*attr
; /* printer-state-message attribute */
9531 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "stop_printer(%p[%d], %s)", con
,
9532 con
->http
.fd
, uri
->values
[0].string
.text
);
9535 * Is the destination valid?
9538 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
9544 send_ipp_status(con
, IPP_NOT_FOUND
,
9545 _("The printer or class was not found."));
9553 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
9555 send_http_error(con
, status
, printer
);
9560 * Stop the printer...
9563 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
9564 IPP_TAG_TEXT
)) == NULL
)
9565 strcpy(printer
->state_message
, "Paused");
9568 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
9569 sizeof(printer
->state_message
));
9572 cupsdStopPrinter(printer
, 1);
9574 if (dtype
& CUPS_PRINTER_CLASS
)
9575 cupsdLogMessage(CUPSD_LOG_INFO
, "Class \"%s\" stopped by \"%s\".",
9576 printer
->name
, get_username(con
));
9578 cupsdLogMessage(CUPSD_LOG_INFO
, "Printer \"%s\" stopped by \"%s\".",
9579 printer
->name
, get_username(con
));
9582 * Everything was ok, so return OK status...
9585 con
->response
->request
.status
.status_code
= IPP_OK
;
9590 * 'url_encode_attr()' - URL-encode a string attribute.
9594 url_encode_attr(ipp_attribute_t
*attr
, /* I - Attribute */
9595 char *buffer
,/* I - String buffer */
9596 int bufsize
)/* I - Size of buffer */
9598 int i
; /* Looping var */
9599 char *bufptr
, /* Pointer into buffer */
9600 *bufend
; /* End of buffer */
9603 strlcpy(buffer
, attr
->name
, bufsize
);
9604 bufptr
= buffer
+ strlen(buffer
);
9605 bufend
= buffer
+ bufsize
- 1;
9607 for (i
= 0; i
< attr
->num_values
; i
++)
9609 if (bufptr
>= bufend
)
9617 if (bufptr
>= bufend
)
9622 bufptr
= url_encode_string(attr
->values
[i
].string
.text
,
9623 bufptr
, bufend
- bufptr
+ 1);
9625 if (bufptr
>= bufend
)
9636 * 'url_encode_string()' - URL-encode a string.
9639 static char * /* O - End of string */
9640 url_encode_string(const char *s
, /* I - String */
9641 char *buffer
, /* I - String buffer */
9642 int bufsize
) /* I - Size of buffer */
9644 char *bufptr
, /* Pointer into buffer */
9645 *bufend
; /* End of buffer */
9646 static const char *hex
= "0123456789ABCDEF";
9651 bufend
= buffer
+ bufsize
- 1;
9653 while (*s
&& bufptr
< bufend
)
9655 if (*s
== ' ' || *s
== '%')
9657 if (bufptr
>= (bufend
- 2))
9661 *bufptr
++ = hex
[(*s
>> 4) & 15];
9662 *bufptr
++ = hex
[*s
& 15];
9666 else if (*s
== '\'' || *s
== '\\')
9668 if (bufptr
>= (bufend
- 1))
9685 * 'user_allowed()' - See if a user is allowed to print to a queue.
9688 static int /* O - 0 if not allowed, 1 if allowed */
9689 user_allowed(cupsd_printer_t
*p
, /* I - Printer or class */
9690 const char *username
) /* I - Username */
9692 int i
; /* Looping var */
9693 struct passwd
*pw
; /* User password data */
9696 if (p
->num_users
== 0)
9699 if (!strcmp(username
, "root"))
9702 pw
= getpwnam(username
);
9705 for (i
= 0; i
< p
->num_users
; i
++)
9707 if (p
->users
[i
][0] == '@')
9710 * Check group membership...
9713 if (cupsdCheckGroup(username
, pw
, p
->users
[i
] + 1))
9716 else if (!strcasecmp(username
, p
->users
[i
]))
9720 return ((i
< p
->num_users
) != p
->deny_users
);
9725 * 'validate_job()' - Validate printer options and destination.
9729 validate_job(cupsd_client_t
*con
, /* I - Client connection */
9730 ipp_attribute_t
*uri
) /* I - Printer URI */
9732 http_status_t status
; /* Policy status */
9733 ipp_attribute_t
*attr
; /* Current attribute */
9734 ipp_attribute_t
*format
; /* Document-format attribute */
9735 cups_ptype_t dtype
; /* Destination type (printer/class) */
9736 char super
[MIME_MAX_SUPER
],
9737 /* Supertype of file */
9738 type
[MIME_MAX_TYPE
];
9739 /* Subtype of file */
9740 cupsd_printer_t
*printer
; /* Printer */
9743 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "validate_job(%p[%d], %s)", con
,
9744 con
->http
.fd
, uri
->values
[0].string
.text
);
9747 * OK, see if the client is sending the document compressed - CUPS
9748 * doesn't support compression yet...
9751 if ((attr
= ippFindAttribute(con
->request
, "compression",
9752 IPP_TAG_KEYWORD
)) != NULL
&&
9753 !strcmp(attr
->values
[0].string
.text
, "none"))
9755 send_ipp_status(con
, IPP_ATTRIBUTES
,
9756 _("Unsupported compression attribute %s!"),
9757 attr
->values
[0].string
.text
);
9758 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
9759 "compression", NULL
, attr
->values
[0].string
.text
);
9764 * Is it a format we support?
9767 if ((format
= ippFindAttribute(con
->request
, "document-format",
9768 IPP_TAG_MIMETYPE
)) != NULL
)
9770 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]",
9773 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad document-format \"%s\"!"),
9774 format
->values
[0].string
.text
);
9778 if ((strcmp(super
, "application") || strcmp(type
, "octet-stream")) &&
9779 !mimeType(MimeDatabase
, super
, type
))
9781 cupsdLogMessage(CUPSD_LOG_INFO
,
9782 "Hint: Do you have the raw file printing rules enabled?");
9783 send_ipp_status(con
, IPP_DOCUMENT_FORMAT
,
9784 _("Unsupported format \"%s\"!"),
9785 format
->values
[0].string
.text
);
9786 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
9787 "document-format", NULL
, format
->values
[0].string
.text
);
9793 * Is the destination valid?
9796 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
9802 send_ipp_status(con
, IPP_NOT_FOUND
,
9803 _("The printer or class was not found."));
9811 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
9813 send_http_error(con
, status
, printer
);
9818 * Everything was ok, so return OK status...
9821 con
->response
->request
.status
.status_code
= IPP_OK
;
9826 * 'validate_name()' - Make sure the printer name only contains valid chars.
9829 static int /* O - 0 if name is no good, 1 if good */
9830 validate_name(const char *name
) /* I - Name to check */
9832 const char *ptr
; /* Pointer into name */
9836 * Scan the whole name...
9839 for (ptr
= name
; *ptr
; ptr
++)
9840 if ((*ptr
> 0 && *ptr
<= ' ') || *ptr
== 127 || *ptr
== '/' || *ptr
== '#')
9844 * All the characters are good; validate the length, too...
9847 return ((ptr
- name
) < 128);
9852 * 'validate_user()' - Validate the user for the request.
9855 static int /* O - 1 if permitted, 0 otherwise */
9856 validate_user(cupsd_job_t
*job
, /* I - Job */
9857 cupsd_client_t
*con
, /* I - Client connection */
9858 const char *owner
, /* I - Owner of job/resource */
9859 char *username
, /* O - Authenticated username */
9860 int userlen
) /* I - Length of username */
9862 cupsd_printer_t
*printer
; /* Printer for job */
9865 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
9866 "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, "
9868 job
? job
->id
: 0, con
->http
.fd
, owner
? owner
: "(null)",
9875 if (!con
|| !owner
|| !username
|| userlen
<= 0)
9879 * Get the best authenticated username that is available.
9882 strlcpy(username
, get_username(con
), userlen
);
9885 * Check the username against the owner...
9888 printer
= cupsdFindDest(job
->dest
);
9890 return (cupsdCheckPolicy(printer
? printer
->op_policy_ptr
: DefaultPolicyPtr
,
9891 con
, owner
) == HTTP_OK
);
9896 * End of "$Id: ipp.c 6755 2007-08-01 19:02:47Z mike $".