2 * IPP routines for the CUPS scheduler.
4 * Copyright © 2007-2018 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
7 * This file contains Kerberos support code, copyright 2006 by
10 * Licensed under Apache License v2.0. See the file "LICENSE" for more
15 * Include necessary headers...
19 #include <cups/ppd-private.h>
22 /*# include <ApplicationServices/ApplicationServices.h>
23 extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id);
24 # include <CoreFoundation/CoreFoundation.h>*/
25 # ifdef HAVE_MEMBERSHIP_H
26 # include <membership.h>
27 # endif /* HAVE_MEMBERSHIP_H */
28 # ifdef HAVE_MEMBERSHIPPRIV_H
29 # include <membershipPriv.h>
31 extern int mbr_user_name_to_uuid(const char* name
, uuid_t uu
);
32 extern int mbr_group_name_to_uuid(const char* name
, uuid_t uu
);
33 extern int mbr_check_membership_by_id(uuid_t user
, gid_t group
, int* ismember
);
34 # endif /* HAVE_MEMBERSHIPPRIV_H */
35 #endif /* __APPLE__ */
42 static void accept_jobs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
43 static void add_class(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
44 static int add_file(cupsd_client_t
*con
, cupsd_job_t
*job
,
45 mime_type_t
*filetype
, int compression
);
46 static cupsd_job_t
*add_job(cupsd_client_t
*con
, cupsd_printer_t
*printer
,
47 mime_type_t
*filetype
);
48 static void add_job_subscriptions(cupsd_client_t
*con
, cupsd_job_t
*job
);
49 static void add_job_uuid(cupsd_job_t
*job
);
50 static void add_printer(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
51 static void add_printer_state_reasons(cupsd_client_t
*con
,
53 static void add_queued_job_count(cupsd_client_t
*con
, cupsd_printer_t
*p
);
54 static void apply_printer_defaults(cupsd_printer_t
*printer
,
56 static void authenticate_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
57 static void cancel_all_jobs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
58 static void cancel_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
59 static void cancel_subscription(cupsd_client_t
*con
, int id
);
60 static int check_rss_recipient(const char *recipient
);
61 static int check_quotas(cupsd_client_t
*con
, cupsd_printer_t
*p
);
62 static void close_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
63 static void copy_attrs(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
64 ipp_tag_t group
, int quickcopy
,
65 cups_array_t
*exclude
);
66 static int copy_banner(cupsd_client_t
*con
, cupsd_job_t
*job
,
68 static int copy_file(const char *from
, const char *to
, mode_t mode
);
69 static int copy_model(cupsd_client_t
*con
, const char *from
,
71 static void copy_job_attrs(cupsd_client_t
*con
,
73 cups_array_t
*ra
, cups_array_t
*exclude
);
74 static void copy_printer_attrs(cupsd_client_t
*con
,
75 cupsd_printer_t
*printer
,
77 static void copy_subscription_attrs(cupsd_client_t
*con
,
78 cupsd_subscription_t
*sub
,
80 cups_array_t
*exclude
);
81 static void create_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
82 static void create_local_printer(cupsd_client_t
*con
);
83 static cups_array_t
*create_requested_array(ipp_t
*request
);
84 static void create_subscriptions(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
85 static void delete_printer(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
86 static void get_default(cupsd_client_t
*con
);
87 static void get_devices(cupsd_client_t
*con
);
88 static void get_document(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
89 static void get_jobs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
90 static void get_job_attrs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
91 static void get_notifications(cupsd_client_t
*con
);
92 static void get_ppd(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
93 static void get_ppds(cupsd_client_t
*con
);
94 static void get_printers(cupsd_client_t
*con
, int type
);
95 static void get_printer_attrs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
96 static void get_printer_supported(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
97 static void get_subscription_attrs(cupsd_client_t
*con
, int sub_id
);
98 static void get_subscriptions(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
99 static const char *get_username(cupsd_client_t
*con
);
100 static void hold_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
101 static void hold_new_jobs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
102 static void move_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
103 static int ppd_parse_line(const char *line
, char *option
, int olen
,
104 char *choice
, int clen
);
105 static void print_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
106 static void read_job_ticket(cupsd_client_t
*con
);
107 static void reject_jobs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
108 static void release_held_new_jobs(cupsd_client_t
*con
,
109 ipp_attribute_t
*uri
);
110 static void release_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
111 static void renew_subscription(cupsd_client_t
*con
, int sub_id
);
112 static void restart_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
113 static void save_auth_info(cupsd_client_t
*con
, cupsd_job_t
*job
,
114 ipp_attribute_t
*auth_info
);
115 static void send_document(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
116 static void send_http_error(cupsd_client_t
*con
, http_status_t status
,
117 cupsd_printer_t
*printer
);
118 static void send_ipp_status(cupsd_client_t
*con
, ipp_status_t status
,
119 const char *message
, ...)
120 __attribute__((__format__(__printf__
, 3, 4)));
121 static void set_default(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
122 static void set_job_attrs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
123 static void set_printer_attrs(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
124 static int set_printer_defaults(cupsd_client_t
*con
, cupsd_printer_t
*printer
);
125 static void start_printer(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
126 static void stop_printer(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
127 static void url_encode_attr(ipp_attribute_t
*attr
, char *buffer
, size_t bufsize
);
128 static char *url_encode_string(const char *s
, char *buffer
, size_t bufsize
);
129 static int user_allowed(cupsd_printer_t
*p
, const char *username
);
130 static void validate_job(cupsd_client_t
*con
, ipp_attribute_t
*uri
);
131 static int validate_name(const char *name
);
132 static int validate_user(cupsd_job_t
*job
, cupsd_client_t
*con
, const char *owner
, char *username
, size_t userlen
);
136 * 'cupsdProcessIPPRequest()' - Process an incoming IPP request.
139 int /* O - 1 on success, 0 on failure */
140 cupsdProcessIPPRequest(
141 cupsd_client_t
*con
) /* I - Client connection */
143 ipp_tag_t group
; /* Current group tag */
144 ipp_attribute_t
*attr
; /* Current attribute */
145 ipp_attribute_t
*charset
; /* Character set attribute */
146 ipp_attribute_t
*language
; /* Language attribute */
147 ipp_attribute_t
*uri
= NULL
; /* Printer or job URI attribute */
148 ipp_attribute_t
*username
; /* requesting-user-name attr */
149 int sub_id
; /* Subscription ID */
150 int valid
= 1; /* Valid request? */
153 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdProcessIPPRequest(%p[%d]): operation_id=%04x(%s)", con
, con
->number
, con
->request
->request
.op
.operation_id
, ippOpString(con
->request
->request
.op
.operation_id
));
155 if (LogLevel
>= CUPSD_LOG_DEBUG2
)
157 for (group
= IPP_TAG_ZERO
, attr
= ippFirstAttribute(con
->request
); attr
; attr
= ippNextAttribute(con
->request
))
159 const char *name
; /* Attribute name */
160 char value
[1024]; /* Attribute value */
162 if (group
!= ippGetGroupTag(attr
))
164 group
= ippGetGroupTag(attr
);
165 if (group
!= IPP_TAG_ZERO
)
166 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdProcessIPPRequest: %s", ippTagString(group
));
169 if ((name
= ippGetName(attr
)) == NULL
)
172 ippAttributeString(attr
, value
, sizeof(value
));
174 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdProcessIPPRequest: %s %s%s '%s'", name
, ippGetCount(attr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr
)), value
);
179 * First build an empty response message for this request...
182 con
->response
= ippNew();
184 con
->response
->request
.status
.version
[0] = con
->request
->request
.op
.version
[0];
185 con
->response
->request
.status
.version
[1] = con
->request
->request
.op
.version
[1];
186 con
->response
->request
.status
.request_id
= con
->request
->request
.op
.request_id
;
189 * Then validate the request header and required attributes...
192 if (con
->request
->request
.any
.version
[0] != 1 && con
->request
->request
.any
.version
[0] != 2)
195 * Return an error, since we only support IPP 1.x and 2.x.
198 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
, "%04X %s Bad request version number %d.%d.", IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
, con
->http
->hostname
, con
->request
->request
.any
.version
[0], con
->request
->request
.any
.version
[1]);
200 send_ipp_status(con
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
, _("Bad request version number %d.%d."), con
->request
->request
.any
.version
[0], con
->request
->request
.any
.version
[1]);
202 else if (con
->request
->request
.any
.request_id
< 1)
205 * Return an error, since request IDs must be between 1 and 2^31-1
208 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
, "%04X %s Bad request ID %d.", IPP_STATUS_ERROR_BAD_REQUEST
, con
->http
->hostname
, con
->request
->request
.any
.request_id
);
210 send_ipp_status(con
, IPP_STATUS_ERROR_BAD_REQUEST
, _("Bad request ID %d."), con
->request
->request
.any
.request_id
);
212 else if (!con
->request
->attrs
)
214 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
, "%04X %s No attributes in request.", IPP_STATUS_ERROR_BAD_REQUEST
, con
->http
->hostname
);
216 send_ipp_status(con
, IPP_STATUS_ERROR_BAD_REQUEST
, _("No attributes in request."));
221 * Make sure that the attributes are provided in the correct order and
222 * don't repeat groups...
225 for (attr
= con
->request
->attrs
, group
= attr
->group_tag
;
228 if (attr
->group_tag
< group
&& attr
->group_tag
!= IPP_TAG_ZERO
)
231 * Out of order; return an error...
234 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
, "%04X %s Attribute groups are out of order", IPP_STATUS_ERROR_BAD_REQUEST
, con
->http
->hostname
);
236 send_ipp_status(con
, IPP_STATUS_ERROR_BAD_REQUEST
, _("Attribute groups are out of order (%x < %x)."), attr
->group_tag
, group
);
240 group
= attr
->group_tag
;
245 * Then make sure that the first three attributes are:
248 * attributes-natural-language
249 * printer-uri/job-uri
252 attr
= con
->request
->attrs
;
253 if (attr
&& attr
->name
&& !strcmp(attr
->name
, "attributes-charset") && (attr
->value_tag
& IPP_TAG_MASK
) == IPP_TAG_CHARSET
)
261 if (attr
&& attr
->name
&& !strcmp(attr
->name
, "attributes-natural-language") && (attr
->value_tag
& IPP_TAG_MASK
) == IPP_TAG_LANGUAGE
)
266 * Reset language for this request if different from Accept-Language.
269 if (!con
->language
||
270 strcmp(attr
->values
[0].string
.text
, con
->language
->language
))
272 cupsLangFree(con
->language
);
273 con
->language
= cupsLangGet(attr
->values
[0].string
.text
);
279 if ((attr
= ippFindAttribute(con
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
281 else if ((attr
= ippFindAttribute(con
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
283 else if (con
->request
->request
.op
.operation_id
== CUPS_GET_PPD
)
284 uri
= ippFindAttribute(con
->request
, "ppd-name", IPP_TAG_NAME
);
289 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
, "attributes-charset", NULL
, charset
->values
[0].string
.text
);
291 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
, "attributes-charset", NULL
, "utf-8");
294 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
, "attributes-natural-language", NULL
, language
->values
[0].string
.text
);
296 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
, "attributes-natural-language", NULL
, DefaultLanguage
);
298 if (charset
&& _cups_strcasecmp(charset
->values
[0].string
.text
, "us-ascii") && _cups_strcasecmp(charset
->values
[0].string
.text
, "utf-8"))
301 * Bad character set...
304 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unsupported character set \"%s\"",
305 charset
->values
[0].string
.text
);
306 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
, "%04X %s Unsupported attributes-charset value \"%s\".", IPP_STATUS_ERROR_CHARSET
, con
->http
->hostname
, charset
->values
[0].string
.text
);
307 send_ipp_status(con
, IPP_STATUS_ERROR_CHARSET
, _("Unsupported character set \"%s\"."), charset
->values
[0].string
.text
);
309 else if (!charset
|| !language
||
311 con
->request
->request
.op
.operation_id
!= CUPS_GET_DEFAULT
&&
312 con
->request
->request
.op
.operation_id
!= CUPS_GET_PRINTERS
&&
313 con
->request
->request
.op
.operation_id
!= CUPS_GET_CLASSES
&&
314 con
->request
->request
.op
.operation_id
!= CUPS_GET_DEVICES
&&
315 con
->request
->request
.op
.operation_id
!= CUPS_GET_PPDS
))
318 * Return an error, since attributes-charset,
319 * attributes-natural-language, and printer-uri/job-uri are required
320 * for all operations.
325 cupsdLogMessage(CUPSD_LOG_ERROR
, "Missing attributes-charset attribute.");
327 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
, "%04X %s Missing attributes-charset attribute.", IPP_STATUS_ERROR_BAD_REQUEST
, con
->http
->hostname
);
332 cupsdLogMessage(CUPSD_LOG_ERROR
,
333 "Missing attributes-natural-language attribute.");
335 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
, "%04X %s Missing attributes-natural-language attribute.", IPP_STATUS_ERROR_BAD_REQUEST
, con
->http
->hostname
);
340 cupsdLogMessage(CUPSD_LOG_ERROR
, "Missing printer-uri, job-uri, or ppd-name attribute.");
342 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
, "%04X %s Missing printer-uri, job-uri, or ppd-name attribute.", IPP_STATUS_ERROR_BAD_REQUEST
, con
->http
->hostname
);
345 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Request attributes follow...");
347 for (attr
= con
->request
->attrs
; attr
; attr
= attr
->next
)
348 cupsdLogMessage(CUPSD_LOG_DEBUG
,
349 "attr \"%s\": group_tag = %x, value_tag = %x",
350 attr
->name
? attr
->name
: "(null)", attr
->group_tag
,
353 cupsdLogMessage(CUPSD_LOG_DEBUG
, "End of attributes...");
355 send_ipp_status(con
, IPP_BAD_REQUEST
,
356 _("Missing required attributes."));
361 * OK, all the checks pass so far; validate "requesting-user-name"
365 if ((username
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_ZERO
)) != NULL
)
368 * Validate "requesting-user-name"...
371 if (username
->group_tag
!= IPP_TAG_OPERATION
&& StrictConformance
)
373 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
, "%04X %s \"requesting-user-name\" attribute in wrong group.", IPP_STATUS_ERROR_BAD_REQUEST
, con
->http
->hostname
);
374 send_ipp_status(con
, IPP_STATUS_ERROR_BAD_REQUEST
, _("\"requesting-user-name\" attribute in wrong group."));
377 else if (username
->value_tag
!= IPP_TAG_NAME
&& username
->value_tag
!= IPP_TAG_NAMELANG
)
379 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
, "%04X %s \"requesting-user-name\" attribute with wrong syntax.", IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, con
->http
->hostname
);
380 send_ipp_status(con
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, _("\"requesting-user-name\" attribute with wrong syntax."));
381 if ((attr
= ippCopyAttribute(con
->response
, username
, 0)) != NULL
)
382 attr
->group_tag
= IPP_TAG_UNSUPPORTED_GROUP
;
385 else if (!ippValidateAttribute(username
))
387 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
, "%04X %s \"requesting-user-name\" attribute with bad value.", IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, con
->http
->hostname
);
389 if (StrictConformance
)
395 send_ipp_status(con
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, _("\"requesting-user-name\" attribute with wrong syntax."));
396 if ((attr
= ippCopyAttribute(con
->response
, username
, 0)) != NULL
)
397 attr
->group_tag
= IPP_TAG_UNSUPPORTED_GROUP
;
403 * Map bad "requesting-user-name" to 'anonymous'...
406 ippSetString(con
->request
, &username
, 0, "anonymous");
409 else if (!strcmp(username
->values
[0].string
.text
, "root") && _cups_strcasecmp(con
->http
->hostname
, "localhost") && strcmp(con
->username
, "root"))
412 * Remote unauthenticated user masquerading as local root...
415 ippSetString(con
->request
, &username
, 0, RemoteRoot
);
419 if ((attr
= ippFindAttribute(con
->request
, "notify-subscription-id", IPP_TAG_INTEGER
)) != NULL
)
420 sub_id
= attr
->values
[0].integer
;
427 * Try processing the operation...
431 cupsdLogMessage(CUPSD_LOG_DEBUG
, "%s %s", ippOpString(con
->request
->request
.op
.operation_id
), uri
->values
[0].string
.text
);
433 cupsdLogMessage(CUPSD_LOG_DEBUG
, "%s", ippOpString(con
->request
->request
.op
.operation_id
));
435 switch (con
->request
->request
.op
.operation_id
)
437 case IPP_OP_PRINT_JOB
:
441 case IPP_OP_VALIDATE_JOB
:
442 validate_job(con
, uri
);
445 case IPP_OP_CREATE_JOB
:
446 create_job(con
, uri
);
449 case IPP_OP_SEND_DOCUMENT
:
450 send_document(con
, uri
);
453 case IPP_OP_CANCEL_JOB
:
454 cancel_job(con
, uri
);
457 case IPP_OP_GET_JOB_ATTRIBUTES
:
458 get_job_attrs(con
, uri
);
461 case IPP_OP_GET_JOBS
:
465 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
466 get_printer_attrs(con
, uri
);
469 case IPP_OP_GET_PRINTER_SUPPORTED_VALUES
:
470 get_printer_supported(con
, uri
);
473 case IPP_OP_HOLD_JOB
:
477 case IPP_OP_RELEASE_JOB
:
478 release_job(con
, uri
);
481 case IPP_OP_RESTART_JOB
:
482 restart_job(con
, uri
);
485 case IPP_OP_PAUSE_PRINTER
:
486 stop_printer(con
, uri
);
489 case IPP_OP_RESUME_PRINTER
:
490 start_printer(con
, uri
);
493 case IPP_OP_PURGE_JOBS
:
494 case IPP_OP_CANCEL_JOBS
:
495 case IPP_OP_CANCEL_MY_JOBS
:
496 cancel_all_jobs(con
, uri
);
499 case IPP_OP_SET_JOB_ATTRIBUTES
:
500 set_job_attrs(con
, uri
);
503 case IPP_OP_SET_PRINTER_ATTRIBUTES
:
504 set_printer_attrs(con
, uri
);
507 case IPP_OP_HOLD_NEW_JOBS
:
508 hold_new_jobs(con
, uri
);
511 case IPP_OP_RELEASE_HELD_NEW_JOBS
:
512 release_held_new_jobs(con
, uri
);
515 case IPP_OP_CLOSE_JOB
:
519 case IPP_OP_CUPS_GET_DEFAULT
:
523 case IPP_OP_CUPS_GET_PRINTERS
:
524 get_printers(con
, 0);
527 case IPP_OP_CUPS_GET_CLASSES
:
528 get_printers(con
, CUPS_PRINTER_CLASS
);
531 case IPP_OP_CUPS_ADD_MODIFY_PRINTER
:
532 add_printer(con
, uri
);
535 case IPP_OP_CUPS_DELETE_PRINTER
:
536 delete_printer(con
, uri
);
539 case IPP_OP_CUPS_ADD_MODIFY_CLASS
:
543 case IPP_OP_CUPS_DELETE_CLASS
:
544 delete_printer(con
, uri
);
547 case IPP_OP_CUPS_ACCEPT_JOBS
:
548 case IPP_OP_ENABLE_PRINTER
:
549 accept_jobs(con
, uri
);
552 case IPP_OP_CUPS_REJECT_JOBS
:
553 case IPP_OP_DISABLE_PRINTER
:
554 reject_jobs(con
, uri
);
557 case IPP_OP_CUPS_SET_DEFAULT
:
558 set_default(con
, uri
);
561 case IPP_OP_CUPS_GET_DEVICES
:
565 case IPP_OP_CUPS_GET_DOCUMENT
:
566 get_document(con
, uri
);
569 case IPP_OP_CUPS_GET_PPD
:
573 case IPP_OP_CUPS_GET_PPDS
:
577 case IPP_OP_CUPS_MOVE_JOB
:
581 case IPP_OP_CUPS_AUTHENTICATE_JOB
:
582 authenticate_job(con
, uri
);
585 case IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS
:
586 case IPP_OP_CREATE_JOB_SUBSCRIPTIONS
:
587 create_subscriptions(con
, uri
);
590 case IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES
:
591 get_subscription_attrs(con
, sub_id
);
594 case IPP_OP_GET_SUBSCRIPTIONS
:
595 get_subscriptions(con
, uri
);
598 case IPP_OP_RENEW_SUBSCRIPTION
:
599 renew_subscription(con
, sub_id
);
602 case IPP_OP_CANCEL_SUBSCRIPTION
:
603 cancel_subscription(con
, sub_id
);
606 case IPP_OP_GET_NOTIFICATIONS
:
607 get_notifications(con
);
610 case IPP_OP_CUPS_CREATE_LOCAL_PRINTER
:
611 create_local_printer(con
);
615 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT
, NULL
, NULL
, "%04X %s Operation %04X (%s) not supported.", IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
, con
->http
->hostname
, con
->request
->request
.op
.operation_id
, ippOpString(con
->request
->request
.op
.operation_id
));
617 send_ipp_status(con
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
, _("%s not supported."), ippOpString(con
->request
->request
.op
.operation_id
));
628 * Sending data from the scheduler...
631 cupsdLogClient(con
, con
->response
->request
.status
.status_code
>= IPP_STATUS_ERROR_BAD_REQUEST
&& con
->response
->request
.status
.status_code
!= IPP_STATUS_ERROR_NOT_FOUND
? CUPSD_LOG_ERROR
: CUPSD_LOG_DEBUG
, "Returning IPP %s for %s (%s) from %s.", ippErrorString(con
->response
->request
.status
.status_code
), ippOpString(con
->request
->request
.op
.operation_id
), uri
? uri
->values
[0].string
.text
: "no URI", con
->http
->hostname
);
633 httpClearFields(con
->http
);
635 #ifdef CUPSD_USE_CHUNKING
637 * Because older versions of CUPS (1.1.17 and older) and some IPP
638 * clients do not implement chunking properly, we cannot use
639 * chunking by default. This may become the default in future
640 * CUPS releases, or we might add a configuration directive for
644 if (con
->http
->version
== HTTP_1_1
)
646 cupsdLogClient(con
, CUPSD_LOG_DEBUG
, "Transfer-Encoding: chunked");
647 cupsdSetLength(con
->http
, 0);
650 #endif /* CUPSD_USE_CHUNKING */
652 size_t length
; /* Length of response */
655 length
= ippLength(con
->response
);
657 if (con
->file
>= 0 && !con
->pipe_pid
)
659 struct stat fileinfo
; /* File information */
661 if (!fstat(con
->file
, &fileinfo
))
662 length
+= (size_t)fileinfo
.st_size
;
665 cupsdLogClient(con
, CUPSD_LOG_DEBUG
, "Content-Length: " CUPS_LLFMT
, CUPS_LLCAST length
);
666 httpSetLength(con
->http
, length
);
669 if (cupsdSendHeader(con
, HTTP_OK
, "application/ipp", CUPSD_AUTH_NONE
))
672 * Tell the caller the response header was sent successfully...
675 cupsdAddSelect(httpGetFd(con
->http
), (cupsd_selfunc_t
)cupsdReadClient
, (cupsd_selfunc_t
)cupsdWriteClient
, con
);
682 * Tell the caller the response header could not be sent...
691 * Sending data from a subprocess like cups-deviced; tell the caller
692 * everything is A-OK so far...
701 * 'cupsdTimeoutJob()' - Timeout a job waiting on job files.
704 int /* O - 0 on success, -1 on error */
705 cupsdTimeoutJob(cupsd_job_t
*job
) /* I - Job to timeout */
707 cupsd_printer_t
*printer
; /* Destination printer or class */
708 ipp_attribute_t
*attr
; /* job-sheets attribute */
709 int kbytes
; /* Kilobytes in banner */
712 job
->pending_timeout
= 0;
715 * See if we need to add the ending sheet...
718 if (!cupsdLoadJob(job
))
721 printer
= cupsdFindDest(job
->dest
);
722 attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_NAME
);
724 if (printer
&& !(printer
->type
& CUPS_PRINTER_REMOTE
) &&
725 attr
&& attr
->num_values
> 1)
731 cupsdLogJob(job
, CUPSD_LOG_INFO
, "Adding end banner page \"%s\".",
732 attr
->values
[1].string
.text
);
734 if ((kbytes
= copy_banner(NULL
, job
, attr
->values
[1].string
.text
)) < 0)
737 cupsdUpdateQuota(printer
, job
->username
, 0, kbytes
);
745 * 'accept_jobs()' - Accept print jobs to a printer.
749 accept_jobs(cupsd_client_t
*con
, /* I - Client connection */
750 ipp_attribute_t
*uri
) /* I - Printer or class URI */
752 http_status_t status
; /* Policy status */
753 cups_ptype_t dtype
; /* Destination type (printer/class) */
754 cupsd_printer_t
*printer
; /* Printer data */
757 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "accept_jobs(%p[%d], %s)", con
,
758 con
->number
, uri
->values
[0].string
.text
);
761 * Is the destination valid?
764 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
770 send_ipp_status(con
, IPP_NOT_FOUND
,
771 _("The printer or class does not exist."));
779 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
781 send_http_error(con
, status
, printer
);
786 * Accept jobs sent to the printer...
789 printer
->accepting
= 1;
790 printer
->state_message
[0] = '\0';
792 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE
, printer
, NULL
,
793 "Now accepting jobs.");
795 if (dtype
& CUPS_PRINTER_CLASS
)
797 cupsdMarkDirty(CUPSD_DIRTY_CLASSES
);
799 cupsdLogMessage(CUPSD_LOG_INFO
, "Class \"%s\" now accepting jobs (\"%s\").",
800 printer
->name
, get_username(con
));
804 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS
);
806 cupsdLogMessage(CUPSD_LOG_INFO
,
807 "Printer \"%s\" now accepting jobs (\"%s\").",
808 printer
->name
, get_username(con
));
812 * Everything was ok, so return OK status...
815 con
->response
->request
.status
.status_code
= IPP_OK
;
820 * 'add_class()' - Add a class to the system.
824 add_class(cupsd_client_t
*con
, /* I - Client connection */
825 ipp_attribute_t
*uri
) /* I - URI of class */
827 http_status_t status
; /* Policy status */
828 int i
; /* Looping var */
829 char scheme
[HTTP_MAX_URI
], /* Method portion of URI */
830 username
[HTTP_MAX_URI
], /* Username portion of URI */
831 host
[HTTP_MAX_URI
], /* Host portion of URI */
832 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
833 int port
; /* Port portion of URI */
834 cupsd_printer_t
*pclass
, /* Class */
835 *member
; /* Member printer/class */
836 cups_ptype_t dtype
; /* Destination type */
837 ipp_attribute_t
*attr
; /* Printer attribute */
838 int modify
; /* Non-zero if we just modified */
839 int need_restart_job
; /* Need to restart job? */
842 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "add_class(%p[%d], %s)", con
,
843 con
->number
, uri
->values
[0].string
.text
);
846 * Do we have a valid URI?
849 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
850 sizeof(scheme
), username
, sizeof(username
), host
,
851 sizeof(host
), &port
, resource
, sizeof(resource
));
854 if (strncmp(resource
, "/classes/", 9) || strlen(resource
) == 9)
857 * No, return an error...
860 send_ipp_status(con
, IPP_BAD_REQUEST
,
861 _("The printer-uri must be of the form "
862 "\"ipp://HOSTNAME/classes/CLASSNAME\"."));
867 * Do we have a valid printer name?
870 if (!validate_name(resource
+ 9))
873 * No, return an error...
876 send_ipp_status(con
, IPP_BAD_REQUEST
,
877 _("The printer-uri \"%s\" contains invalid characters."),
878 uri
->values
[0].string
.text
);
883 * See if the class already exists; if not, create a new class...
886 if ((pclass
= cupsdFindClass(resource
+ 9)) == NULL
)
889 * Class doesn't exist; see if we have a printer of the same name...
892 if ((pclass
= cupsdFindPrinter(resource
+ 9)) != NULL
)
895 * Yes, return an error...
898 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
899 _("A printer named \"%s\" already exists."),
905 * No, check the default policy and then add the class...
908 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
910 send_http_error(con
, status
, NULL
);
914 pclass
= cupsdAddClass(resource
+ 9);
917 pclass
->printer_id
= NextPrinterId
++;
919 else if ((status
= cupsdCheckPolicy(pclass
->op_policy_ptr
, con
,
922 send_http_error(con
, status
, pclass
);
929 * Look for attributes and copy them over as needed...
932 need_restart_job
= 0;
934 if ((attr
= ippFindAttribute(con
->request
, "printer-location", IPP_TAG_TEXT
)) != NULL
)
935 cupsdSetString(&pclass
->location
, attr
->values
[0].string
.text
);
937 if ((attr
= ippFindAttribute(con
->request
, "printer-geo-location", IPP_TAG_URI
)) != NULL
&& !strncmp(attr
->values
[0].string
.text
, "geo:", 4))
938 cupsdSetString(&pclass
->geo_location
, attr
->values
[0].string
.text
);
940 if ((attr
= ippFindAttribute(con
->request
, "printer-organization", IPP_TAG_TEXT
)) != NULL
)
941 cupsdSetString(&pclass
->organization
, attr
->values
[0].string
.text
);
943 if ((attr
= ippFindAttribute(con
->request
, "printer-organizational-unit", IPP_TAG_TEXT
)) != NULL
)
944 cupsdSetString(&pclass
->organizational_unit
, attr
->values
[0].string
.text
);
946 if ((attr
= ippFindAttribute(con
->request
, "printer-info",
947 IPP_TAG_TEXT
)) != NULL
)
948 cupsdSetString(&pclass
->info
, attr
->values
[0].string
.text
);
950 if ((attr
= ippFindAttribute(con
->request
, "printer-is-accepting-jobs",
951 IPP_TAG_BOOLEAN
)) != NULL
&&
952 attr
->values
[0].boolean
!= pclass
->accepting
)
954 cupsdLogMessage(CUPSD_LOG_INFO
,
955 "Setting %s printer-is-accepting-jobs to %d (was %d.)",
956 pclass
->name
, attr
->values
[0].boolean
, pclass
->accepting
);
958 pclass
->accepting
= attr
->values
[0].boolean
;
960 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE
, pclass
, NULL
, "%s accepting jobs.",
961 pclass
->accepting
? "Now" : "No longer");
964 if ((attr
= ippFindAttribute(con
->request
, "printer-is-shared", IPP_TAG_BOOLEAN
)) != NULL
)
966 if (pclass
->type
& CUPS_PRINTER_REMOTE
)
969 * Cannot re-share remote printers.
972 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Cannot change printer-is-shared for remote queues."));
974 cupsdDeletePrinter(pclass
, 0);
979 if (pclass
->shared
&& !ippGetBoolean(attr
, 0))
980 cupsdDeregisterPrinter(pclass
, 1);
982 cupsdLogMessage(CUPSD_LOG_INFO
,
983 "Setting %s printer-is-shared to %d (was %d.)",
984 pclass
->name
, attr
->values
[0].boolean
, pclass
->shared
);
986 pclass
->shared
= ippGetBoolean(attr
, 0);
989 if ((attr
= ippFindAttribute(con
->request
, "printer-state",
990 IPP_TAG_ENUM
)) != NULL
)
992 if (attr
->values
[0].integer
!= IPP_PRINTER_IDLE
&&
993 attr
->values
[0].integer
!= IPP_PRINTER_STOPPED
)
995 send_ipp_status(con
, IPP_BAD_REQUEST
,
996 _("Attempt to set %s printer-state to bad value %d."),
997 pclass
->name
, attr
->values
[0].integer
);
999 cupsdDeletePrinter(pclass
, 0);
1004 cupsdLogMessage(CUPSD_LOG_INFO
, "Setting %s printer-state to %d (was %d.)",
1005 pclass
->name
, attr
->values
[0].integer
, pclass
->state
);
1007 if (attr
->values
[0].integer
== IPP_PRINTER_STOPPED
)
1008 cupsdStopPrinter(pclass
, 0);
1011 cupsdSetPrinterState(pclass
, (ipp_pstate_t
)(attr
->values
[0].integer
), 0);
1012 need_restart_job
= 1;
1015 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
1016 IPP_TAG_TEXT
)) != NULL
)
1018 strlcpy(pclass
->state_message
, attr
->values
[0].string
.text
,
1019 sizeof(pclass
->state_message
));
1021 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE
, pclass
, NULL
, "%s",
1022 pclass
->state_message
);
1024 if ((attr
= ippFindAttribute(con
->request
, "member-uris",
1025 IPP_TAG_URI
)) != NULL
)
1028 * Clear the printer array as needed...
1031 need_restart_job
= 1;
1033 if (pclass
->num_printers
> 0)
1035 free(pclass
->printers
);
1036 pclass
->num_printers
= 0;
1040 * Add each printer or class that is listed...
1043 for (i
= 0; i
< attr
->num_values
; i
++)
1046 * Search for the printer or class URI...
1049 if (!cupsdValidateDest(attr
->values
[i
].string
.text
, &dtype
, &member
))
1055 send_ipp_status(con
, IPP_NOT_FOUND
,
1056 _("The printer or class does not exist."));
1058 cupsdDeletePrinter(pclass
, 0);
1062 else if (dtype
& CUPS_PRINTER_CLASS
)
1064 send_ipp_status(con
, IPP_BAD_REQUEST
,
1065 _("Nested classes are not allowed."));
1067 cupsdDeletePrinter(pclass
, 0);
1073 * Add it to the class...
1076 cupsdAddPrinterToClass(pclass
, member
);
1080 if (!set_printer_defaults(con
, pclass
))
1083 cupsdDeletePrinter(pclass
, 0);
1088 if ((attr
= ippFindAttribute(con
->request
, "auth-info-required",
1089 IPP_TAG_KEYWORD
)) != NULL
)
1090 cupsdSetAuthInfoRequired(pclass
, NULL
, attr
);
1092 pclass
->config_time
= time(NULL
);
1095 * Update the printer class attributes and return...
1098 cupsdSetPrinterAttrs(pclass
);
1099 cupsdMarkDirty(CUPSD_DIRTY_CLASSES
);
1101 if (need_restart_job
&& pclass
->job
)
1104 * Reset the current job to a "pending" status...
1107 cupsdSetJobState(pclass
->job
, IPP_JOB_PENDING
, CUPSD_JOB_FORCE
,
1108 "Job restarted because the class was modified.");
1111 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP
);
1115 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED
,
1116 pclass
, NULL
, "Class \"%s\" modified by \"%s\".",
1117 pclass
->name
, get_username(con
));
1119 cupsdLogMessage(CUPSD_LOG_INFO
, "Class \"%s\" modified by \"%s\".",
1120 pclass
->name
, get_username(con
));
1124 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED
,
1125 pclass
, NULL
, "New class \"%s\" added by \"%s\".",
1126 pclass
->name
, get_username(con
));
1128 cupsdLogMessage(CUPSD_LOG_INFO
, "New class \"%s\" added by \"%s\".",
1129 pclass
->name
, get_username(con
));
1132 con
->response
->request
.status
.status_code
= IPP_OK
;
1137 * 'add_file()' - Add a file to a job.
1140 static int /* O - 0 on success, -1 on error */
1141 add_file(cupsd_client_t
*con
, /* I - Connection to client */
1142 cupsd_job_t
*job
, /* I - Job to add to */
1143 mime_type_t
*filetype
, /* I - Type of file */
1144 int compression
) /* I - Compression */
1146 mime_type_t
**filetypes
; /* New filetypes array... */
1147 int *compressions
; /* New compressions array... */
1150 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1151 "add_file(con=%p[%d], job=%d, filetype=%s/%s, "
1152 "compression=%d)", con
, con
? con
->number
: -1, job
->id
,
1153 filetype
->super
, filetype
->type
, compression
);
1156 * Add the file to the job...
1159 if (job
->num_files
== 0)
1161 compressions
= (int *)malloc(sizeof(int));
1162 filetypes
= (mime_type_t
**)malloc(sizeof(mime_type_t
*));
1166 compressions
= (int *)realloc(job
->compressions
,
1167 (size_t)(job
->num_files
+ 1) * sizeof(int));
1168 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
1169 (size_t)(job
->num_files
+ 1) *
1170 sizeof(mime_type_t
*));
1174 job
->compressions
= compressions
;
1177 job
->filetypes
= filetypes
;
1179 if (!compressions
|| !filetypes
)
1181 cupsdSetJobState(job
, IPP_JOB_ABORTED
, CUPSD_JOB_PURGE
,
1182 "Job aborted because the scheduler ran out of memory.");
1185 send_ipp_status(con
, IPP_INTERNAL_ERROR
,
1186 _("Unable to allocate memory for file types."));
1191 job
->compressions
[job
->num_files
] = compression
;
1192 job
->filetypes
[job
->num_files
] = filetype
;
1197 cupsdMarkDirty(CUPSD_DIRTY_JOBS
);
1204 * 'add_job()' - Add a job to a print queue.
1207 static cupsd_job_t
* /* O - Job object */
1208 add_job(cupsd_client_t
*con
, /* I - Client connection */
1209 cupsd_printer_t
*printer
, /* I - Destination printer */
1210 mime_type_t
*filetype
) /* I - First print file type, if any */
1212 http_status_t status
; /* Policy status */
1213 ipp_attribute_t
*attr
, /* Current attribute */
1214 *auth_info
; /* auth-info attribute */
1215 const char *mandatory
; /* Current mandatory job attribute */
1216 const char *val
; /* Default option value */
1217 int priority
; /* Job priority */
1218 cupsd_job_t
*job
; /* Current job */
1219 char job_uri
[HTTP_MAX_URI
]; /* Job URI */
1220 int kbytes
; /* Size of print file */
1221 int i
; /* Looping var */
1222 int lowerpagerange
; /* Page range bound */
1223 int exact
; /* Did we have an exact match? */
1224 ipp_attribute_t
*media_col
, /* media-col attribute */
1225 *media_margin
; /* media-*-margin attribute */
1226 ipp_t
*unsup_col
; /* media-col in unsupported response */
1227 static const char * const readonly
[] =/* List of read-only attributes */
1229 "date-time-at-completed",
1230 "date-time-at-creation",
1231 "date-time-at-processing",
1232 "job-detailed-status-messages",
1233 "job-document-access-errors",
1235 "job-impressions-completed",
1236 "job-k-octets-completed",
1237 "job-media-sheets-completed",
1238 "job-pages-completed",
1239 "job-printer-up-time",
1242 "job-state-message",
1243 "job-state-reasons",
1245 "number-of-documents",
1246 "number-of-intervening-jobs",
1247 "output-device-assigned",
1248 "time-at-completed",
1250 "time-at-processing"
1254 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "add_job(%p[%d], %p(%s), %p(%s/%s))",
1255 con
, con
->number
, printer
, printer
->name
,
1256 filetype
, filetype
? filetype
->super
: "none",
1257 filetype
? filetype
->type
: "none");
1260 * Check remote printing to non-shared printer...
1263 if (!printer
->shared
&&
1264 _cups_strcasecmp(con
->http
->hostname
, "localhost") &&
1265 _cups_strcasecmp(con
->http
->hostname
, ServerName
))
1267 send_ipp_status(con
, IPP_NOT_AUTHORIZED
,
1268 _("The printer or class is not shared."));
1276 auth_info
= ippFindAttribute(con
->request
, "auth-info", IPP_TAG_TEXT
);
1278 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
1280 send_http_error(con
, status
, printer
);
1283 else if (printer
->num_auth_info_required
== 1 &&
1284 !strcmp(printer
->auth_info_required
[0], "negotiate") &&
1287 send_http_error(con
, HTTP_UNAUTHORIZED
, printer
);
1291 else if (auth_info
&& !con
->http
->tls
&&
1292 !httpAddrLocalhost(con
->http
->hostaddr
))
1295 * Require encryption of auth-info over non-local connections...
1298 send_http_error(con
, HTTP_UPGRADE_REQUIRED
, printer
);
1301 #endif /* HAVE_SSL */
1304 * See if the printer is accepting jobs...
1307 if (!printer
->accepting
)
1309 send_ipp_status(con
, IPP_NOT_ACCEPTING
,
1310 _("Destination \"%s\" is not accepting jobs."),
1316 * Validate job template attributes; for now just document-format,
1317 * copies, job-sheets, number-up, page-ranges, mandatory attributes, and
1321 for (i
= 0; i
< (int)(sizeof(readonly
) / sizeof(readonly
[0])); i
++)
1323 if ((attr
= ippFindAttribute(con
->request
, readonly
[i
], IPP_TAG_ZERO
)) != NULL
)
1325 ippDeleteAttribute(con
->request
, attr
);
1327 if (StrictConformance
)
1329 send_ipp_status(con
, IPP_BAD_REQUEST
, _("The '%s' Job Status attribute cannot be supplied in a job creation request."), readonly
[i
]);
1333 cupsdLogMessage(CUPSD_LOG_INFO
, "Unexpected '%s' Job Status attribute in a job creation request.", readonly
[i
]);
1339 for (mandatory
= (char *)cupsArrayFirst(printer
->pc
->mandatory
);
1341 mandatory
= (char *)cupsArrayNext(printer
->pc
->mandatory
))
1343 if (!ippFindAttribute(con
->request
, mandatory
, IPP_TAG_ZERO
))
1346 * Missing a required attribute...
1349 send_ipp_status(con
, IPP_CONFLICT
,
1350 _("The \"%s\" attribute is required for print jobs."),
1357 if (filetype
&& printer
->filetypes
&&
1358 !cupsArrayFind(printer
->filetypes
, filetype
))
1360 char mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
1361 /* MIME media type string */
1364 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
1367 send_ipp_status(con
, IPP_DOCUMENT_FORMAT
,
1368 _("Unsupported format \"%s\"."), mimetype
);
1370 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
1371 "document-format", NULL
, mimetype
);
1376 if ((attr
= ippFindAttribute(con
->request
, "copies",
1377 IPP_TAG_INTEGER
)) != NULL
)
1379 if (attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> MaxCopies
)
1381 send_ipp_status(con
, IPP_ATTRIBUTES
, _("Bad copies value %d."),
1382 attr
->values
[0].integer
);
1383 ippAddInteger(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_INTEGER
,
1384 "copies", attr
->values
[0].integer
);
1389 if ((attr
= ippFindAttribute(con
->request
, "job-sheets",
1390 IPP_TAG_ZERO
)) != NULL
)
1392 if (attr
->value_tag
!= IPP_TAG_KEYWORD
&&
1393 attr
->value_tag
!= IPP_TAG_NAME
)
1395 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad job-sheets value type."));
1399 if (attr
->num_values
> 2)
1401 send_ipp_status(con
, IPP_BAD_REQUEST
,
1402 _("Too many job-sheets values (%d > 2)."),
1407 for (i
= 0; i
< attr
->num_values
; i
++)
1408 if (strcmp(attr
->values
[i
].string
.text
, "none") &&
1409 !cupsdFindBanner(attr
->values
[i
].string
.text
))
1411 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad job-sheets value \"%s\"."),
1412 attr
->values
[i
].string
.text
);
1417 if ((attr
= ippFindAttribute(con
->request
, "number-up",
1418 IPP_TAG_INTEGER
)) != NULL
)
1420 if (attr
->values
[0].integer
!= 1 &&
1421 attr
->values
[0].integer
!= 2 &&
1422 attr
->values
[0].integer
!= 4 &&
1423 attr
->values
[0].integer
!= 6 &&
1424 attr
->values
[0].integer
!= 9 &&
1425 attr
->values
[0].integer
!= 16)
1427 send_ipp_status(con
, IPP_ATTRIBUTES
, _("Bad number-up value %d."),
1428 attr
->values
[0].integer
);
1429 ippAddInteger(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_INTEGER
,
1430 "number-up", attr
->values
[0].integer
);
1435 if ((attr
= ippFindAttribute(con
->request
, "page-ranges",
1436 IPP_TAG_RANGE
)) != NULL
)
1438 for (i
= 0, lowerpagerange
= 1; i
< attr
->num_values
; i
++)
1440 if (attr
->values
[i
].range
.lower
< lowerpagerange
||
1441 attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
1443 send_ipp_status(con
, IPP_BAD_REQUEST
,
1444 _("Bad page-ranges values %d-%d."),
1445 attr
->values
[i
].range
.lower
,
1446 attr
->values
[i
].range
.upper
);
1450 lowerpagerange
= attr
->values
[i
].range
.upper
+ 1;
1455 * Do media selection as needed...
1458 if (!ippFindAttribute(con
->request
, "PageRegion", IPP_TAG_ZERO
) &&
1459 !ippFindAttribute(con
->request
, "PageSize", IPP_TAG_ZERO
) &&
1460 _ppdCacheGetPageSize(printer
->pc
, con
->request
, NULL
, &exact
))
1463 (media_col
= ippFindAttribute(con
->request
, "media-col",
1464 IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
1466 send_ipp_status(con
, IPP_OK_SUBST
, _("Unsupported margins."));
1468 unsup_col
= ippNew();
1469 if ((media_margin
= ippFindAttribute(media_col
->values
[0].collection
,
1470 "media-bottom-margin",
1471 IPP_TAG_INTEGER
)) != NULL
)
1472 ippAddInteger(unsup_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1473 "media-bottom-margin", media_margin
->values
[0].integer
);
1475 if ((media_margin
= ippFindAttribute(media_col
->values
[0].collection
,
1476 "media-left-margin",
1477 IPP_TAG_INTEGER
)) != NULL
)
1478 ippAddInteger(unsup_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1479 "media-left-margin", media_margin
->values
[0].integer
);
1481 if ((media_margin
= ippFindAttribute(media_col
->values
[0].collection
,
1482 "media-right-margin",
1483 IPP_TAG_INTEGER
)) != NULL
)
1484 ippAddInteger(unsup_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1485 "media-right-margin", media_margin
->values
[0].integer
);
1487 if ((media_margin
= ippFindAttribute(media_col
->values
[0].collection
,
1489 IPP_TAG_INTEGER
)) != NULL
)
1490 ippAddInteger(unsup_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1491 "media-top-margin", media_margin
->values
[0].integer
);
1493 ippAddCollection(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, "media-col",
1495 ippDelete(unsup_col
);
1500 * Make sure we aren't over our limit...
1503 if (MaxJobs
&& cupsArrayCount(Jobs
) >= MaxJobs
)
1506 if (MaxJobs
&& cupsArrayCount(Jobs
) >= MaxJobs
)
1508 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("Too many active jobs."));
1512 if ((i
= check_quotas(con
, printer
)) < 0)
1514 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("Quota limit reached."));
1519 send_ipp_status(con
, IPP_NOT_AUTHORIZED
, _("Not allowed to print."));
1524 * Create the job and set things up...
1527 if ((attr
= ippFindAttribute(con
->request
, "job-priority",
1528 IPP_TAG_INTEGER
)) != NULL
)
1529 priority
= attr
->values
[0].integer
;
1532 if ((val
= cupsGetOption("job-priority", printer
->num_options
,
1533 printer
->options
)) != NULL
)
1534 priority
= atoi(val
);
1538 ippAddInteger(con
->request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
1542 if ((attr
= ippFindAttribute(con
->request
, "job-name", IPP_TAG_ZERO
)) == NULL
)
1543 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
1544 else if ((attr
->value_tag
!= IPP_TAG_NAME
&&
1545 attr
->value_tag
!= IPP_TAG_NAMELANG
) ||
1546 attr
->num_values
!= 1)
1548 send_ipp_status(con
, IPP_ATTRIBUTES
,
1549 _("Bad job-name value: Wrong type or count."));
1550 if ((attr
= ippCopyAttribute(con
->response
, attr
, 0)) != NULL
)
1551 attr
->group_tag
= IPP_TAG_UNSUPPORTED_GROUP
;
1553 if (StrictConformance
)
1556 /* Don't use invalid attribute */
1557 ippDeleteAttribute(con
->request
, attr
);
1559 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
1561 else if (!ippValidateAttribute(attr
))
1563 send_ipp_status(con
, IPP_ATTRIBUTES
, _("Bad job-name value: %s"),
1564 cupsLastErrorString());
1566 if ((attr
= ippCopyAttribute(con
->response
, attr
, 0)) != NULL
)
1567 attr
->group_tag
= IPP_TAG_UNSUPPORTED_GROUP
;
1569 if (StrictConformance
)
1572 /* Don't use invalid attribute */
1573 ippDeleteAttribute(con
->request
, attr
);
1575 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
1578 attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
);
1580 if ((job
= cupsdAddJob(priority
, printer
->name
)) == NULL
)
1582 send_ipp_status(con
, IPP_INTERNAL_ERROR
,
1583 _("Unable to add job for destination \"%s\"."),
1588 job
->dtype
= printer
->type
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_REMOTE
);
1589 job
->attrs
= con
->request
;
1591 con
->request
= ippNewRequest(job
->attrs
->request
.op
.operation_id
);
1593 cupsdMarkDirty(CUPSD_DIRTY_JOBS
);
1596 apply_printer_defaults(printer
, job
);
1598 if (con
->username
[0])
1600 cupsdSetString(&job
->username
, con
->username
);
1603 ippSetString(job
->attrs
, &attr
, 0, con
->username
);
1607 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1608 "add_job: requesting-user-name=\"%s\"",
1609 attr
->values
[0].string
.text
);
1611 cupsdSetString(&job
->username
, attr
->values
[0].string
.text
);
1614 cupsdSetString(&job
->username
, "anonymous");
1617 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
,
1618 "job-originating-user-name", NULL
, job
->username
);
1621 ippSetGroupTag(job
->attrs
, &attr
, IPP_TAG_JOB
);
1622 ippSetName(job
->attrs
, &attr
, "job-originating-user-name");
1625 if (con
->username
[0] || auth_info
)
1627 save_auth_info(con
, job
, auth_info
);
1630 * Remove the auth-info attribute from the attribute data...
1634 ippDeleteAttribute(job
->attrs
, auth_info
);
1637 if ((attr
= ippFindAttribute(con
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1638 cupsdSetString(&(job
->name
), attr
->values
[0].string
.text
);
1640 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-host-name",
1641 IPP_TAG_ZERO
)) != NULL
)
1644 * Request contains a job-originating-host-name attribute; validate it...
1647 if (attr
->value_tag
!= IPP_TAG_NAME
||
1648 attr
->num_values
!= 1 ||
1649 strcmp(con
->http
->hostname
, "localhost"))
1652 * Can't override the value if we aren't connected via localhost.
1653 * Also, we can only have 1 value and it must be a name value.
1656 ippDeleteAttribute(job
->attrs
, attr
);
1657 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-host-name", NULL
, con
->http
->hostname
);
1660 ippSetGroupTag(job
->attrs
, &attr
, IPP_TAG_JOB
);
1665 * No job-originating-host-name attribute, so use the hostname from
1669 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
,
1670 "job-originating-host-name", NULL
, con
->http
->hostname
);
1673 ippAddOutOfBand(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
1674 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(NULL
)));
1675 ippAddOutOfBand(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
1676 ippAddOutOfBand(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "time-at-completed");
1677 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", time(NULL
));
1678 ippAddOutOfBand(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "time-at-processing");
1681 * Add remaining job attributes...
1684 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1685 job
->state
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
1686 "job-state", IPP_JOB_STOPPED
);
1687 job
->state_value
= (ipp_jstate_t
)job
->state
->values
[0].integer
;
1688 job
->reasons
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
1689 "job-state-reasons", NULL
, "job-incoming");
1690 job
->impressions
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", 0);
1691 job
->sheets
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
1692 "job-media-sheets-completed", 0);
1693 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
1696 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) != NULL
)
1697 attr
->values
[0].integer
= 0;
1699 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-k-octets", 0);
1701 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
1702 IPP_TAG_KEYWORD
)) == NULL
)
1703 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
1706 if ((val
= cupsGetOption("job-hold-until", printer
->num_options
,
1707 printer
->options
)) == NULL
)
1710 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
1711 "job-hold-until", NULL
, val
);
1714 if (printer
->holding_new_jobs
)
1717 * Hold all new jobs on this printer...
1720 if (attr
&& strcmp(attr
->values
[0].string
.text
, "no-hold"))
1721 cupsdSetJobHoldUntil(job
, ippGetString(attr
, 0, NULL
), 0);
1723 cupsdSetJobHoldUntil(job
, "indefinite", 0);
1725 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
1726 job
->state_value
= IPP_JOB_HELD
;
1728 ippSetString(job
->attrs
, &job
->reasons
, 0, "job-held-on-create");
1730 else if (attr
&& strcmp(attr
->values
[0].string
.text
, "no-hold"))
1733 * Hold job until specified time...
1736 cupsdSetJobHoldUntil(job
, attr
->values
[0].string
.text
, 0);
1738 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
1739 job
->state_value
= IPP_JOB_HELD
;
1741 ippSetString(job
->attrs
, &job
->reasons
, 0, "job-hold-until-specified");
1743 else if (job
->attrs
->request
.op
.operation_id
== IPP_CREATE_JOB
)
1745 job
->hold_until
= time(NULL
) + MultipleOperationTimeout
;
1746 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
1747 job
->state_value
= IPP_JOB_HELD
;
1751 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1752 job
->state_value
= IPP_JOB_PENDING
;
1754 ippSetString(job
->attrs
, &job
->reasons
, 0, "none");
1757 if (!(printer
->type
& CUPS_PRINTER_REMOTE
) || Classification
)
1760 * Add job sheets options...
1763 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets",
1764 IPP_TAG_ZERO
)) == NULL
)
1766 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1767 "Adding default job-sheets values \"%s,%s\"...",
1768 printer
->job_sheets
[0], printer
->job_sheets
[1]);
1770 attr
= ippAddStrings(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-sheets",
1772 ippSetString(job
->attrs
, &attr
, 0, printer
->job_sheets
[0]);
1773 ippSetString(job
->attrs
, &attr
, 1, printer
->job_sheets
[1]);
1776 job
->job_sheets
= attr
;
1779 * Enforce classification level if set...
1784 cupsdLogMessage(CUPSD_LOG_INFO
,
1785 "Classification=\"%s\", ClassifyOverride=%d",
1786 Classification
? Classification
: "(null)",
1789 if (ClassifyOverride
)
1791 if (!strcmp(attr
->values
[0].string
.text
, "none") &&
1792 (attr
->num_values
== 1 ||
1793 !strcmp(attr
->values
[1].string
.text
, "none")))
1796 * Force the leading banner to have the classification on it...
1799 ippSetString(job
->attrs
, &attr
, 0, Classification
);
1801 cupsdLogJob(job
, CUPSD_LOG_NOTICE
, "CLASSIFICATION FORCED "
1802 "job-sheets=\"%s,none\", "
1803 "job-originating-user-name=\"%s\"",
1804 Classification
, job
->username
);
1806 else if (attr
->num_values
== 2 &&
1807 strcmp(attr
->values
[0].string
.text
,
1808 attr
->values
[1].string
.text
) &&
1809 strcmp(attr
->values
[0].string
.text
, "none") &&
1810 strcmp(attr
->values
[1].string
.text
, "none"))
1813 * Can't put two different security markings on the same document!
1816 ippSetString(job
->attrs
, &attr
, 1, attr
->values
[0].string
.text
);
1818 cupsdLogJob(job
, CUPSD_LOG_NOTICE
, "CLASSIFICATION FORCED "
1819 "job-sheets=\"%s,%s\", "
1820 "job-originating-user-name=\"%s\"",
1821 attr
->values
[0].string
.text
,
1822 attr
->values
[1].string
.text
, job
->username
);
1824 else if (strcmp(attr
->values
[0].string
.text
, Classification
) &&
1825 strcmp(attr
->values
[0].string
.text
, "none") &&
1826 (attr
->num_values
== 1 ||
1827 (strcmp(attr
->values
[1].string
.text
, Classification
) &&
1828 strcmp(attr
->values
[1].string
.text
, "none"))))
1830 if (attr
->num_values
== 1)
1831 cupsdLogJob(job
, CUPSD_LOG_NOTICE
,
1832 "CLASSIFICATION OVERRIDDEN "
1833 "job-sheets=\"%s\", "
1834 "job-originating-user-name=\"%s\"",
1835 attr
->values
[0].string
.text
, job
->username
);
1837 cupsdLogJob(job
, CUPSD_LOG_NOTICE
,
1838 "CLASSIFICATION OVERRIDDEN "
1839 "job-sheets=\"%s,%s\",fffff "
1840 "job-originating-user-name=\"%s\"",
1841 attr
->values
[0].string
.text
,
1842 attr
->values
[1].string
.text
, job
->username
);
1845 else if (strcmp(attr
->values
[0].string
.text
, Classification
) &&
1846 (attr
->num_values
== 1 ||
1847 strcmp(attr
->values
[1].string
.text
, Classification
)))
1850 * Force the banner to have the classification on it...
1853 if (attr
->num_values
> 1 &&
1854 !strcmp(attr
->values
[0].string
.text
, attr
->values
[1].string
.text
))
1856 ippSetString(job
->attrs
, &attr
, 0, Classification
);
1857 ippSetString(job
->attrs
, &attr
, 1, Classification
);
1861 if (attr
->num_values
== 1 ||
1862 strcmp(attr
->values
[0].string
.text
, "none"))
1863 ippSetString(job
->attrs
, &attr
, 0, Classification
);
1865 if (attr
->num_values
> 1 &&
1866 strcmp(attr
->values
[1].string
.text
, "none"))
1867 ippSetString(job
->attrs
, &attr
, 1, Classification
);
1870 if (attr
->num_values
> 1)
1871 cupsdLogJob(job
, CUPSD_LOG_NOTICE
,
1872 "CLASSIFICATION FORCED "
1873 "job-sheets=\"%s,%s\", "
1874 "job-originating-user-name=\"%s\"",
1875 attr
->values
[0].string
.text
,
1876 attr
->values
[1].string
.text
, job
->username
);
1878 cupsdLogJob(job
, CUPSD_LOG_NOTICE
,
1879 "CLASSIFICATION FORCED "
1880 "job-sheets=\"%s\", "
1881 "job-originating-user-name=\"%s\"",
1882 Classification
, job
->username
);
1887 * See if we need to add the starting sheet...
1890 if (!(printer
->type
& CUPS_PRINTER_REMOTE
))
1892 cupsdLogJob(job
, CUPSD_LOG_INFO
, "Adding start banner page \"%s\".",
1893 attr
->values
[0].string
.text
);
1895 if ((kbytes
= copy_banner(con
, job
, attr
->values
[0].string
.text
)) < 0)
1897 cupsdSetJobState(job
, IPP_JOB_ABORTED
, CUPSD_JOB_PURGE
,
1898 "Aborting job because the start banner could not be "
1903 cupsdUpdateQuota(printer
, job
->username
, 0, kbytes
);
1906 else if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets",
1907 IPP_TAG_ZERO
)) != NULL
)
1908 job
->job_sheets
= attr
;
1911 * Fill in the response info...
1914 httpAssembleURIf(HTTP_URI_CODING_ALL
, job_uri
, sizeof(job_uri
), "ipp", NULL
,
1915 con
->clientname
, con
->clientport
, "/jobs/%d", job
->id
);
1916 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
,
1919 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1921 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
1923 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_TEXT
, "job-state-message", NULL
, "");
1924 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "job-state-reasons",
1925 NULL
, job
->reasons
->values
[0].string
.text
);
1927 con
->response
->request
.status
.status_code
= IPP_OK
;
1930 * Add any job subscriptions...
1933 add_job_subscriptions(con
, job
);
1936 * Set all but the first two attributes to the job attributes group...
1939 for (attr
= job
->attrs
->attrs
->next
->next
; attr
; attr
= attr
->next
)
1940 attr
->group_tag
= IPP_TAG_JOB
;
1943 * Fire the "job created" event...
1946 cupsdAddEvent(CUPSD_EVENT_JOB_CREATED
, printer
, job
, "Job created.");
1949 * Return the new job...
1957 * 'add_job_subscriptions()' - Add any subscriptions for a job.
1961 add_job_subscriptions(
1962 cupsd_client_t
*con
, /* I - Client connection */
1963 cupsd_job_t
*job
) /* I - Newly created job */
1965 int i
; /* Looping var */
1966 ipp_attribute_t
*prev
, /* Previous attribute */
1967 *next
, /* Next attribute */
1968 *attr
; /* Current attribute */
1969 cupsd_subscription_t
*sub
; /* Subscription object */
1970 const char *recipient
, /* notify-recipient-uri */
1971 *pullmethod
; /* notify-pull-method */
1972 ipp_attribute_t
*user_data
; /* notify-user-data */
1973 int interval
; /* notify-time-interval */
1974 unsigned mask
; /* notify-events */
1978 * Find the first subscription group attribute; return if we have
1982 for (attr
= job
->attrs
->attrs
; attr
; attr
= attr
->next
)
1983 if (attr
->group_tag
== IPP_TAG_SUBSCRIPTION
)
1990 * Process the subscription attributes in the request...
1999 mask
= CUPSD_EVENT_NONE
;
2001 while (attr
&& attr
->group_tag
!= IPP_TAG_ZERO
)
2003 if (!strcmp(attr
->name
, "notify-recipient-uri") &&
2004 attr
->value_tag
== IPP_TAG_URI
)
2007 * Validate the recipient scheme against the ServerBin/notifier
2011 char notifier
[1024], /* Notifier filename */
2012 scheme
[HTTP_MAX_URI
], /* Scheme portion of URI */
2013 userpass
[HTTP_MAX_URI
], /* Username portion of URI */
2014 host
[HTTP_MAX_URI
], /* Host portion of URI */
2015 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
2016 int port
; /* Port portion of URI */
2019 recipient
= attr
->values
[0].string
.text
;
2021 if (httpSeparateURI(HTTP_URI_CODING_ALL
, recipient
,
2022 scheme
, sizeof(scheme
), userpass
, sizeof(userpass
),
2023 host
, sizeof(host
), &port
,
2024 resource
, sizeof(resource
)) < HTTP_URI_OK
)
2026 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
2027 _("Bad notify-recipient-uri \"%s\"."), recipient
);
2028 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
,
2029 "notify-status-code", IPP_URI_SCHEME
);
2033 snprintf(notifier
, sizeof(notifier
), "%s/notifier/%s", ServerBin
,
2035 if (access(notifier
, X_OK
) || !strcmp(scheme
, ".") || !strcmp(scheme
, ".."))
2037 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
2038 _("notify-recipient-uri URI \"%s\" uses unknown "
2039 "scheme."), recipient
);
2040 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
,
2041 "notify-status-code", IPP_URI_SCHEME
);
2045 if (!strcmp(scheme
, "rss") && !check_rss_recipient(recipient
))
2047 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
2048 _("notify-recipient-uri URI \"%s\" is already used."),
2050 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
,
2051 "notify-status-code", IPP_ATTRIBUTES
);
2055 else if (!strcmp(attr
->name
, "notify-pull-method") &&
2056 attr
->value_tag
== IPP_TAG_KEYWORD
)
2058 pullmethod
= attr
->values
[0].string
.text
;
2060 if (strcmp(pullmethod
, "ippget"))
2062 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
2063 _("Bad notify-pull-method \"%s\"."), pullmethod
);
2064 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
,
2065 "notify-status-code", IPP_ATTRIBUTES
);
2069 else if (!strcmp(attr
->name
, "notify-charset") &&
2070 attr
->value_tag
== IPP_TAG_CHARSET
&&
2071 strcmp(attr
->values
[0].string
.text
, "us-ascii") &&
2072 strcmp(attr
->values
[0].string
.text
, "utf-8"))
2074 send_ipp_status(con
, IPP_CHARSET
,
2075 _("Character set \"%s\" not supported."),
2076 attr
->values
[0].string
.text
);
2079 else if (!strcmp(attr
->name
, "notify-natural-language") &&
2080 (attr
->value_tag
!= IPP_TAG_LANGUAGE
||
2081 strcmp(attr
->values
[0].string
.text
, DefaultLanguage
)))
2083 send_ipp_status(con
, IPP_CHARSET
,
2084 _("Language \"%s\" not supported."),
2085 attr
->values
[0].string
.text
);
2088 else if (!strcmp(attr
->name
, "notify-user-data") &&
2089 attr
->value_tag
== IPP_TAG_STRING
)
2091 if (attr
->num_values
> 1 || attr
->values
[0].unknown
.length
> 63)
2093 send_ipp_status(con
, IPP_REQUEST_VALUE
,
2094 _("The notify-user-data value is too large "
2095 "(%d > 63 octets)."),
2096 attr
->values
[0].unknown
.length
);
2102 else if (!strcmp(attr
->name
, "notify-events") &&
2103 attr
->value_tag
== IPP_TAG_KEYWORD
)
2105 for (i
= 0; i
< attr
->num_values
; i
++)
2106 mask
|= cupsdEventValue(attr
->values
[i
].string
.text
);
2108 else if (!strcmp(attr
->name
, "notify-lease-duration"))
2110 send_ipp_status(con
, IPP_BAD_REQUEST
,
2111 _("The notify-lease-duration attribute cannot be "
2112 "used with job subscriptions."));
2115 else if (!strcmp(attr
->name
, "notify-time-interval") &&
2116 attr
->value_tag
== IPP_TAG_INTEGER
)
2117 interval
= attr
->values
[0].integer
;
2122 if (!recipient
&& !pullmethod
)
2125 if (mask
== CUPSD_EVENT_NONE
)
2126 mask
= CUPSD_EVENT_JOB_COMPLETED
;
2128 if ((sub
= cupsdAddSubscription(mask
, cupsdFindDest(job
->dest
), job
,
2129 recipient
, 0)) != NULL
)
2131 sub
->interval
= interval
;
2133 cupsdSetString(&sub
->owner
, job
->username
);
2137 sub
->user_data_len
= user_data
->values
[0].unknown
.length
;
2138 memcpy(sub
->user_data
, user_data
->values
[0].unknown
.data
,
2139 (size_t)sub
->user_data_len
);
2142 ippAddSeparator(con
->response
);
2143 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
2144 "notify-subscription-id", sub
->id
);
2146 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Added subscription %d for job %d",
2154 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS
);
2157 * Remove all of the subscription attributes from the job request...
2159 * TODO: Optimize this since subscription groups have to come at the
2160 * end of the request...
2163 for (attr
= job
->attrs
->attrs
, prev
= NULL
; attr
; attr
= next
)
2167 if (attr
->group_tag
== IPP_TAG_SUBSCRIPTION
||
2168 attr
->group_tag
== IPP_TAG_ZERO
)
2171 * Free and remove this attribute...
2174 ippDeleteAttribute(NULL
, attr
);
2179 job
->attrs
->attrs
= next
;
2185 job
->attrs
->last
= prev
;
2186 job
->attrs
->current
= prev
;
2191 * 'add_job_uuid()' - Add job-uuid attribute to a job.
2193 * See RFC 4122 for the definition of UUIDs and the format.
2197 add_job_uuid(cupsd_job_t
*job
) /* I - Job */
2199 char uuid
[64]; /* job-uuid string */
2203 * Add a job-uuid attribute if none exists...
2206 if (!ippFindAttribute(job
->attrs
, "job-uuid", IPP_TAG_URI
))
2207 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
,
2208 httpAssembleUUID(ServerName
, RemotePort
, job
->dest
, job
->id
,
2209 uuid
, sizeof(uuid
)));
2214 * 'add_printer()' - Add a printer to the system.
2218 add_printer(cupsd_client_t
*con
, /* I - Client connection */
2219 ipp_attribute_t
*uri
) /* I - URI of printer */
2221 http_status_t status
; /* Policy status */
2222 int i
; /* Looping var */
2223 char scheme
[HTTP_MAX_URI
], /* Method portion of URI */
2224 username
[HTTP_MAX_URI
], /* Username portion of URI */
2225 host
[HTTP_MAX_URI
], /* Host portion of URI */
2226 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
2227 int port
; /* Port portion of URI */
2228 cupsd_printer_t
*printer
; /* Printer/class */
2229 ipp_attribute_t
*attr
; /* Printer attribute */
2230 cups_file_t
*fp
; /* Script/PPD file */
2231 char line
[1024]; /* Line from file... */
2232 char srcfile
[1024], /* Source Script/PPD file */
2233 dstfile
[1024]; /* Destination Script/PPD file */
2234 int modify
; /* Non-zero if we are modifying */
2235 int changed_driver
, /* Changed the PPD? */
2236 need_restart_job
, /* Need to restart job? */
2237 set_device_uri
, /* Did we set the device URI? */
2238 set_port_monitor
; /* Did we set the port monitor? */
2241 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "add_printer(%p[%d], %s)", con
,
2242 con
->number
, uri
->values
[0].string
.text
);
2245 * Do we have a valid URI?
2248 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
2249 sizeof(scheme
), username
, sizeof(username
), host
,
2250 sizeof(host
), &port
, resource
, sizeof(resource
));
2252 if (strncmp(resource
, "/printers/", 10) || strlen(resource
) == 10)
2255 * No, return an error...
2258 send_ipp_status(con
, IPP_BAD_REQUEST
,
2259 _("The printer-uri must be of the form "
2260 "\"ipp://HOSTNAME/printers/PRINTERNAME\"."));
2265 * Do we have a valid printer name?
2268 if (!validate_name(resource
+ 10))
2271 * No, return an error...
2274 send_ipp_status(con
, IPP_BAD_REQUEST
,
2275 _("The printer-uri \"%s\" contains invalid characters."),
2276 uri
->values
[0].string
.text
);
2281 * See if the printer already exists; if not, create a new printer...
2284 if ((printer
= cupsdFindPrinter(resource
+ 10)) == NULL
)
2287 * Printer doesn't exist; see if we have a class of the same name...
2290 if ((printer
= cupsdFindClass(resource
+ 10)) != NULL
)
2293 * Yes, return an error...
2296 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
2297 _("A class named \"%s\" already exists."),
2303 * No, check the default policy then add the printer...
2306 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
2308 send_http_error(con
, status
, NULL
);
2312 printer
= cupsdAddPrinter(resource
+ 10);
2315 printer
->printer_id
= NextPrinterId
++;
2317 else if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
,
2320 send_http_error(con
, status
, printer
);
2327 * Look for attributes and copy them over as needed...
2331 need_restart_job
= 0;
2333 if ((attr
= ippFindAttribute(con
->request
, "printer-is-temporary", IPP_TAG_BOOLEAN
)) != NULL
)
2334 printer
->temporary
= ippGetBoolean(attr
, 0);
2336 if ((attr
= ippFindAttribute(con
->request
, "printer-location",
2337 IPP_TAG_TEXT
)) != NULL
)
2338 cupsdSetString(&printer
->location
, attr
->values
[0].string
.text
);
2340 if ((attr
= ippFindAttribute(con
->request
, "printer-geo-location", IPP_TAG_URI
)) != NULL
&& !strncmp(attr
->values
[0].string
.text
, "geo:", 4))
2341 cupsdSetString(&printer
->geo_location
, attr
->values
[0].string
.text
);
2343 if ((attr
= ippFindAttribute(con
->request
, "printer-organization", IPP_TAG_TEXT
)) != NULL
)
2344 cupsdSetString(&printer
->organization
, attr
->values
[0].string
.text
);
2346 if ((attr
= ippFindAttribute(con
->request
, "printer-organizational-unit", IPP_TAG_TEXT
)) != NULL
)
2347 cupsdSetString(&printer
->organizational_unit
, attr
->values
[0].string
.text
);
2349 if ((attr
= ippFindAttribute(con
->request
, "printer-info",
2350 IPP_TAG_TEXT
)) != NULL
)
2351 cupsdSetString(&printer
->info
, attr
->values
[0].string
.text
);
2355 if ((attr
= ippFindAttribute(con
->request
, "device-uri",
2356 IPP_TAG_URI
)) != NULL
)
2359 * Do we have a valid device URI?
2362 http_uri_status_t uri_status
; /* URI separation status */
2363 char old_device_uri
[1024];
2364 /* Old device URI */
2366 need_restart_job
= 1;
2368 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
2369 attr
->values
[0].string
.text
,
2370 scheme
, sizeof(scheme
),
2371 username
, sizeof(username
),
2372 host
, sizeof(host
), &port
,
2373 resource
, sizeof(resource
));
2375 cupsdLogMessage(CUPSD_LOG_DEBUG
, "%s device-uri: %s", printer
->name
, httpURIStatusString(uri_status
));
2377 if (uri_status
< HTTP_URI_OK
)
2379 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("Bad device-uri \"%s\"."),
2380 attr
->values
[0].string
.text
);
2382 cupsdDeletePrinter(printer
, 0);
2387 if (!strcmp(scheme
, "file"))
2390 * See if the administrator has enabled file devices...
2393 if (!FileDevice
&& strcmp(resource
, "/dev/null"))
2396 * File devices are disabled and the URL is not file:/dev/null...
2399 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
2400 _("File device URIs have been disabled. "
2401 "To enable, see the FileDevice directive in "
2402 "\"%s/cups-files.conf\"."),
2405 cupsdDeletePrinter(printer
, 0);
2413 * See if the backend exists and is executable...
2416 snprintf(srcfile
, sizeof(srcfile
), "%s/backend/%s", ServerBin
, scheme
);
2417 if (access(srcfile
, X_OK
))
2420 * Could not find device in list!
2423 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
2424 _("Bad device-uri scheme \"%s\"."), scheme
);
2426 cupsdDeletePrinter(printer
, 0);
2432 if (printer
->sanitized_device_uri
)
2433 strlcpy(old_device_uri
, printer
->sanitized_device_uri
,
2434 sizeof(old_device_uri
));
2436 old_device_uri
[0] = '\0';
2438 cupsdSetDeviceURI(printer
, attr
->values
[0].string
.text
);
2440 cupsdLogMessage(CUPSD_LOG_INFO
,
2441 "Setting %s device-uri to \"%s\" (was \"%s\".)",
2442 printer
->name
, printer
->sanitized_device_uri
,
2448 set_port_monitor
= 0;
2450 if ((attr
= ippFindAttribute(con
->request
, "port-monitor",
2451 IPP_TAG_NAME
)) != NULL
)
2453 ipp_attribute_t
*supported
; /* port-monitor-supported attribute */
2456 need_restart_job
= 1;
2458 supported
= ippFindAttribute(printer
->ppd_attrs
, "port-monitor-supported",
2462 for (i
= 0; i
< supported
->num_values
; i
++)
2463 if (!strcmp(supported
->values
[i
].string
.text
,
2464 attr
->values
[0].string
.text
))
2468 if (!supported
|| i
>= supported
->num_values
)
2470 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("Bad port-monitor \"%s\"."),
2471 attr
->values
[0].string
.text
);
2473 cupsdDeletePrinter(printer
, 0);
2478 cupsdLogMessage(CUPSD_LOG_INFO
,
2479 "Setting %s port-monitor to \"%s\" (was \"%s\".)",
2480 printer
->name
, attr
->values
[0].string
.text
,
2481 printer
->port_monitor
? printer
->port_monitor
: "none");
2483 if (strcmp(attr
->values
[0].string
.text
, "none"))
2484 cupsdSetString(&printer
->port_monitor
, attr
->values
[0].string
.text
);
2486 cupsdClearString(&printer
->port_monitor
);
2488 set_port_monitor
= 1;
2491 if ((attr
= ippFindAttribute(con
->request
, "printer-is-accepting-jobs",
2492 IPP_TAG_BOOLEAN
)) != NULL
&&
2493 attr
->values
[0].boolean
!= printer
->accepting
)
2495 cupsdLogMessage(CUPSD_LOG_INFO
,
2496 "Setting %s printer-is-accepting-jobs to %d (was %d.)",
2497 printer
->name
, attr
->values
[0].boolean
, printer
->accepting
);
2499 printer
->accepting
= attr
->values
[0].boolean
;
2501 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE
, printer
, NULL
,
2502 "%s accepting jobs.",
2503 printer
->accepting
? "Now" : "No longer");
2506 if ((attr
= ippFindAttribute(con
->request
, "printer-is-shared", IPP_TAG_BOOLEAN
)) != NULL
)
2508 if (ippGetBoolean(attr
, 0) &&
2509 printer
->num_auth_info_required
== 1 &&
2510 !strcmp(printer
->auth_info_required
[0], "negotiate"))
2512 send_ipp_status(con
, IPP_BAD_REQUEST
,
2513 _("Cannot share a remote Kerberized printer."));
2515 cupsdDeletePrinter(printer
, 0);
2520 if (printer
->type
& CUPS_PRINTER_REMOTE
)
2523 * Cannot re-share remote printers.
2526 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Cannot change printer-is-shared for remote queues."));
2528 cupsdDeletePrinter(printer
, 0);
2533 if (printer
->shared
&& !ippGetBoolean(attr
, 0))
2534 cupsdDeregisterPrinter(printer
, 1);
2536 cupsdLogMessage(CUPSD_LOG_INFO
,
2537 "Setting %s printer-is-shared to %d (was %d.)",
2538 printer
->name
, attr
->values
[0].boolean
, printer
->shared
);
2540 printer
->shared
= ippGetBoolean(attr
, 0);
2541 if (printer
->shared
&& printer
->temporary
)
2542 printer
->temporary
= 0;
2545 if ((attr
= ippFindAttribute(con
->request
, "printer-state",
2546 IPP_TAG_ENUM
)) != NULL
)
2548 if (attr
->values
[0].integer
!= IPP_PRINTER_IDLE
&&
2549 attr
->values
[0].integer
!= IPP_PRINTER_STOPPED
)
2551 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad printer-state value %d."),
2552 attr
->values
[0].integer
);
2554 cupsdDeletePrinter(printer
, 0);
2559 cupsdLogMessage(CUPSD_LOG_INFO
, "Setting %s printer-state to %d (was %d.)",
2560 printer
->name
, attr
->values
[0].integer
, printer
->state
);
2562 if (attr
->values
[0].integer
== IPP_PRINTER_STOPPED
)
2563 cupsdStopPrinter(printer
, 0);
2566 need_restart_job
= 1;
2567 cupsdSetPrinterState(printer
, (ipp_pstate_t
)(attr
->values
[0].integer
), 0);
2571 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
2572 IPP_TAG_TEXT
)) != NULL
)
2574 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
2575 sizeof(printer
->state_message
));
2577 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE
, printer
, NULL
, "%s",
2578 printer
->state_message
);
2581 if ((attr
= ippFindAttribute(con
->request
, "printer-state-reasons",
2582 IPP_TAG_KEYWORD
)) != NULL
)
2584 if (attr
->num_values
>
2585 (int)(sizeof(printer
->reasons
) / sizeof(printer
->reasons
[0])))
2587 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
2588 _("Too many printer-state-reasons values (%d > %d)."),
2590 (int)(sizeof(printer
->reasons
) /
2591 sizeof(printer
->reasons
[0])));
2593 cupsdDeletePrinter(printer
, 0);
2598 for (i
= 0; i
< printer
->num_reasons
; i
++)
2599 _cupsStrFree(printer
->reasons
[i
]);
2601 printer
->num_reasons
= 0;
2602 for (i
= 0; i
< attr
->num_values
; i
++)
2604 if (!strcmp(attr
->values
[i
].string
.text
, "none"))
2607 printer
->reasons
[printer
->num_reasons
] =
2608 _cupsStrRetain(attr
->values
[i
].string
.text
);
2609 printer
->num_reasons
++;
2611 if (!strcmp(attr
->values
[i
].string
.text
, "paused") &&
2612 printer
->state
!= IPP_PRINTER_STOPPED
)
2614 cupsdLogMessage(CUPSD_LOG_INFO
,
2615 "Setting %s printer-state to %d (was %d.)",
2616 printer
->name
, IPP_PRINTER_STOPPED
, printer
->state
);
2617 cupsdStopPrinter(printer
, 0);
2621 if (PrintcapFormat
== PRINTCAP_PLIST
)
2622 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP
);
2624 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE
, printer
, NULL
,
2625 "Printer \"%s\" state changed.", printer
->name
);
2628 if (!set_printer_defaults(con
, printer
))
2631 cupsdDeletePrinter(printer
, 0);
2636 if ((attr
= ippFindAttribute(con
->request
, "auth-info-required",
2637 IPP_TAG_KEYWORD
)) != NULL
)
2638 cupsdSetAuthInfoRequired(printer
, NULL
, attr
);
2641 * See if we have all required attributes...
2644 if (!printer
->device_uri
)
2645 cupsdSetString(&printer
->device_uri
, "file:///dev/null");
2648 * See if we have a PPD file attached to the request...
2653 need_restart_job
= 1;
2656 strlcpy(srcfile
, con
->filename
, sizeof(srcfile
));
2658 if ((fp
= cupsFileOpen(srcfile
, "rb")))
2661 * Yes; get the first line from it...
2665 cupsFileGets(fp
, line
, sizeof(line
));
2669 * Then see what kind of file it is...
2672 if (strncmp(line
, "*PPD-Adobe", 10))
2674 send_ipp_status(con
, IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED
, _("Bad PPD file."));
2676 cupsdDeletePrinter(printer
, 0);
2681 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
2685 * The new file is a PPD file, so move the file over to the ppd
2689 if (copy_file(srcfile
, dstfile
, ConfigFilePerm
))
2691 send_ipp_status(con
, IPP_INTERNAL_ERROR
, _("Unable to copy PPD file - %s"), strerror(errno
));
2693 cupsdDeletePrinter(printer
, 0);
2698 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Copied PPD file successfully");
2701 else if ((attr
= ippFindAttribute(con
->request
, "ppd-name", IPP_TAG_NAME
)) != NULL
)
2703 const char *ppd_name
= ippGetString(attr
, 0, NULL
);
2704 /* ppd-name value */
2706 need_restart_job
= 1;
2709 if (!strcmp(ppd_name
, "raw"))
2712 * Raw driver, remove any existing PPD file.
2715 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
, printer
->name
);
2718 else if (strstr(ppd_name
, "../"))
2720 send_ipp_status(con
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, _("Invalid ppd-name value."));
2722 cupsdDeletePrinter(printer
, 0);
2732 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
, printer
->name
);
2734 if (copy_model(con
, ppd_name
, dstfile
))
2736 send_ipp_status(con
, IPP_INTERNAL_ERROR
, _("Unable to copy PPD file."));
2738 cupsdDeletePrinter(printer
, 0);
2743 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Copied PPD file successfully");
2750 * If we changed the PPD, then remove the printer's cache file and clear the
2751 * printer-state-reasons...
2754 char cache_name
[1024]; /* Cache filename for printer attrs */
2756 snprintf(cache_name
, sizeof(cache_name
), "%s/%s.data", CacheDir
, printer
->name
);
2759 cupsdSetPrinterReasons(printer
, "none");
2762 * (Re)register color profiles...
2765 cupsdRegisterColor(printer
);
2769 * If we set the device URI but not the port monitor, check which port
2770 * monitor to use by default...
2773 if (set_device_uri
&& !set_port_monitor
)
2775 ppd_file_t
*ppd
; /* PPD file */
2776 ppd_attr_t
*ppdattr
; /* cupsPortMonitor attribute */
2779 httpSeparateURI(HTTP_URI_CODING_ALL
, printer
->device_uri
, scheme
,
2780 sizeof(scheme
), username
, sizeof(username
), host
,
2781 sizeof(host
), &port
, resource
, sizeof(resource
));
2783 snprintf(srcfile
, sizeof(srcfile
), "%s/ppd/%s.ppd", ServerRoot
,
2785 if ((ppd
= _ppdOpenFile(srcfile
, _PPD_LOCALIZATION_NONE
)) != NULL
)
2787 for (ppdattr
= ppdFindAttr(ppd
, "cupsPortMonitor", NULL
);
2789 ppdattr
= ppdFindNextAttr(ppd
, "cupsPortMonitor", NULL
))
2790 if (!strcmp(scheme
, ppdattr
->spec
))
2792 cupsdLogMessage(CUPSD_LOG_INFO
,
2793 "Setting %s port-monitor to \"%s\" (was \"%s\".)",
2794 printer
->name
, ppdattr
->value
,
2795 printer
->port_monitor
? printer
->port_monitor
2798 if (strcmp(ppdattr
->value
, "none"))
2799 cupsdSetString(&printer
->port_monitor
, ppdattr
->value
);
2801 cupsdClearString(&printer
->port_monitor
);
2810 printer
->config_time
= time(NULL
);
2813 * Update the printer attributes and return...
2816 if (!printer
->temporary
)
2818 if (!printer
->printer_id
)
2819 printer
->printer_id
= NextPrinterId
++;
2821 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS
);
2824 cupsdSetPrinterAttrs(printer
);
2826 if (need_restart_job
&& printer
->job
)
2829 * Restart the current job...
2832 cupsdSetJobState(printer
->job
, IPP_JOB_PENDING
, CUPSD_JOB_FORCE
,
2833 "Job restarted because the printer was modified.");
2836 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP
);
2840 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED
,
2841 printer
, NULL
, "Printer \"%s\" modified by \"%s\".",
2842 printer
->name
, get_username(con
));
2844 cupsdLogMessage(CUPSD_LOG_INFO
, "Printer \"%s\" modified by \"%s\".",
2845 printer
->name
, get_username(con
));
2849 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED
,
2850 printer
, NULL
, "New printer \"%s\" added by \"%s\".",
2851 printer
->name
, get_username(con
));
2853 cupsdLogMessage(CUPSD_LOG_INFO
, "New printer \"%s\" added by \"%s\".",
2854 printer
->name
, get_username(con
));
2857 con
->response
->request
.status
.status_code
= IPP_OK
;
2862 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
2863 * based upon the printer state...
2867 add_printer_state_reasons(
2868 cupsd_client_t
*con
, /* I - Client connection */
2869 cupsd_printer_t
*p
) /* I - Printer info */
2871 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2872 "add_printer_state_reasons(%p[%d], %p[%s])",
2873 con
, con
->number
, p
, p
->name
);
2875 if (p
->num_reasons
== 0)
2876 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2877 "printer-state-reasons", NULL
, "none");
2879 ippAddStrings(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2880 "printer-state-reasons", p
->num_reasons
, NULL
,
2881 (const char * const *)p
->reasons
);
2886 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
2887 * the specified printer or class.
2891 add_queued_job_count(
2892 cupsd_client_t
*con
, /* I - Client connection */
2893 cupsd_printer_t
*p
) /* I - Printer or class */
2895 int count
; /* Number of jobs on destination */
2898 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "add_queued_job_count(%p[%d], %p[%s])",
2899 con
, con
->number
, p
, p
->name
);
2901 count
= cupsdGetPrinterJobCount(p
->name
);
2903 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2904 "queued-job-count", count
);
2909 * 'apply_printer_defaults()' - Apply printer default options to a job.
2913 apply_printer_defaults(
2914 cupsd_printer_t
*printer
, /* I - Printer */
2915 cupsd_job_t
*job
) /* I - Job */
2917 int i
, /* Looping var */
2918 num_options
; /* Number of default options */
2919 cups_option_t
*options
, /* Default options */
2920 *option
; /* Current option */
2923 cupsdLogJob(job
, CUPSD_LOG_DEBUG
, "Applying default options...");
2926 * Collect all of the default options and add the missing ones to the
2930 for (i
= printer
->num_options
, num_options
= 0, options
= NULL
,
2931 option
= printer
->options
;
2934 if (!ippFindAttribute(job
->attrs
, option
->name
, IPP_TAG_ZERO
))
2936 if (!strcmp(option
->name
, "print-quality") && ippFindAttribute(job
->attrs
, "cupsPrintQuality", IPP_TAG_NAME
))
2937 continue; /* Don't override cupsPrintQuality */
2939 cupsdLogJob(job
, CUPSD_LOG_DEBUG
, "Adding default %s=%s", option
->name
, option
->value
);
2941 num_options
= cupsAddOption(option
->name
, option
->value
, num_options
, &options
);
2945 * Encode these options as attributes in the job object...
2948 cupsEncodeOptions2(job
->attrs
, num_options
, options
, IPP_TAG_JOB
);
2949 cupsFreeOptions(num_options
, options
);
2954 * 'authenticate_job()' - Set job authentication info.
2958 authenticate_job(cupsd_client_t
*con
, /* I - Client connection */
2959 ipp_attribute_t
*uri
) /* I - Job URI */
2961 ipp_attribute_t
*attr
, /* job-id attribute */
2962 *auth_info
; /* auth-info attribute */
2963 int jobid
; /* Job ID */
2964 cupsd_job_t
*job
; /* Current job */
2965 char scheme
[HTTP_MAX_URI
],
2966 /* Method portion of URI */
2967 username
[HTTP_MAX_URI
],
2968 /* Username portion of URI */
2970 /* Host portion of URI */
2971 resource
[HTTP_MAX_URI
];
2972 /* Resource portion of URI */
2973 int port
; /* Port portion of URI */
2976 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "authenticate_job(%p[%d], %s)",
2977 con
, con
->number
, uri
->values
[0].string
.text
);
2980 * Start with "everything is OK" status...
2983 con
->response
->request
.status
.status_code
= IPP_OK
;
2986 * See if we have a job URI or a printer URI...
2989 if (!strcmp(uri
->name
, "printer-uri"))
2992 * Got a printer URI; see if we also have a job-id attribute...
2995 if ((attr
= ippFindAttribute(con
->request
, "job-id",
2996 IPP_TAG_INTEGER
)) == NULL
)
2998 send_ipp_status(con
, IPP_BAD_REQUEST
,
2999 _("Got a printer-uri attribute but no job-id."));
3003 jobid
= attr
->values
[0].integer
;
3008 * Got a job URI; parse it to get the job ID...
3011 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
3012 sizeof(scheme
), username
, sizeof(username
), host
,
3013 sizeof(host
), &port
, resource
, sizeof(resource
));
3015 if (strncmp(resource
, "/jobs/", 6))
3021 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad job-uri \"%s\"."),
3022 uri
->values
[0].string
.text
);
3026 jobid
= atoi(resource
+ 6);
3030 * See if the job exists...
3033 if ((job
= cupsdFindJob(jobid
)) == NULL
)
3036 * Nope - return a "not found" error...
3039 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."), jobid
);
3044 * See if the job has been completed...
3047 if (job
->state_value
!= IPP_JOB_HELD
)
3050 * Return a "not-possible" error...
3053 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
3054 _("Job #%d is not held for authentication."),
3060 * See if we have already authenticated...
3063 auth_info
= ippFindAttribute(con
->request
, "auth-info", IPP_TAG_TEXT
);
3065 if (!con
->username
[0] && !auth_info
)
3067 cupsd_printer_t
*printer
; /* Job destination */
3070 * No auth data. If we need to authenticate via Kerberos, send a
3071 * HTTP auth challenge, otherwise just return an IPP error...
3074 printer
= cupsdFindDest(job
->dest
);
3076 if (printer
&& printer
->num_auth_info_required
> 0 &&
3077 !strcmp(printer
->auth_info_required
[0], "negotiate"))
3078 send_http_error(con
, HTTP_UNAUTHORIZED
, printer
);
3080 send_ipp_status(con
, IPP_NOT_AUTHORIZED
,
3081 _("No authentication information provided."));
3086 * See if the job is owned by the requesting user...
3089 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
3091 send_http_error(con
, con
->username
[0] ? HTTP_FORBIDDEN
: HTTP_UNAUTHORIZED
,
3092 cupsdFindDest(job
->dest
));
3097 * Save the authentication information for this job...
3100 save_auth_info(con
, job
, auth_info
);
3103 * Reset the job-hold-until value to "no-hold"...
3106 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
3107 IPP_TAG_KEYWORD
)) == NULL
)
3108 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
3112 ippSetValueTag(job
->attrs
, &attr
, IPP_TAG_KEYWORD
);
3113 ippSetString(job
->attrs
, &attr
, 0, "no-hold");
3117 * Release the job and return...
3120 cupsdReleaseJob(job
);
3122 cupsdAddEvent(CUPSD_EVENT_JOB_STATE
, NULL
, job
, "Job authenticated by user");
3124 cupsdLogJob(job
, CUPSD_LOG_INFO
, "Authenticated by \"%s\".", con
->username
);
3131 * 'cancel_all_jobs()' - Cancel all or selected print jobs.
3135 cancel_all_jobs(cupsd_client_t
*con
, /* I - Client connection */
3136 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
3138 int i
; /* Looping var */
3139 http_status_t status
; /* Policy status */
3140 cups_ptype_t dtype
; /* Destination type */
3141 char scheme
[HTTP_MAX_URI
], /* Scheme portion of URI */
3142 userpass
[HTTP_MAX_URI
], /* Username portion of URI */
3143 hostname
[HTTP_MAX_URI
], /* Host portion of URI */
3144 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
3145 int port
; /* Port portion of URI */
3146 ipp_attribute_t
*attr
; /* Attribute in request */
3147 const char *username
= NULL
; /* Username */
3148 cupsd_jobaction_t purge
= CUPSD_JOB_DEFAULT
;
3150 cupsd_printer_t
*printer
; /* Printer */
3151 ipp_attribute_t
*job_ids
; /* job-ids attribute */
3152 cupsd_job_t
*job
; /* Job */
3155 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cancel_all_jobs(%p[%d], %s)", con
,
3156 con
->number
, uri
->values
[0].string
.text
);
3159 * Get the jobs to cancel/purge...
3162 switch (con
->request
->request
.op
.operation_id
)
3164 case IPP_PURGE_JOBS
:
3166 * Get the username (if any) for the jobs we want to cancel (only if
3167 * "my-jobs" is specified...
3170 if ((attr
= ippFindAttribute(con
->request
, "my-jobs",
3171 IPP_TAG_BOOLEAN
)) != NULL
&&
3172 attr
->values
[0].boolean
)
3174 if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name",
3175 IPP_TAG_NAME
)) != NULL
)
3176 username
= attr
->values
[0].string
.text
;
3179 send_ipp_status(con
, IPP_BAD_REQUEST
,
3180 _("Missing requesting-user-name attribute."));
3186 * Look for the "purge-jobs" attribute...
3189 if ((attr
= ippFindAttribute(con
->request
, "purge-jobs",
3190 IPP_TAG_BOOLEAN
)) != NULL
)
3191 purge
= attr
->values
[0].boolean
? CUPSD_JOB_PURGE
: CUPSD_JOB_DEFAULT
;
3193 purge
= CUPSD_JOB_PURGE
;
3196 case IPP_CANCEL_MY_JOBS
:
3197 if (con
->username
[0])
3198 username
= con
->username
;
3199 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name",
3200 IPP_TAG_NAME
)) != NULL
)
3201 username
= attr
->values
[0].string
.text
;
3204 send_ipp_status(con
, IPP_BAD_REQUEST
,
3205 _("Missing requesting-user-name attribute."));
3213 job_ids
= ippFindAttribute(con
->request
, "job-ids", IPP_TAG_INTEGER
);
3216 * See if we have a printer URI...
3219 if (strcmp(uri
->name
, "printer-uri"))
3221 send_ipp_status(con
, IPP_BAD_REQUEST
,
3222 _("The printer-uri attribute is required."));
3227 * And if the destination is valid...
3230 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
3236 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
,
3237 scheme
, sizeof(scheme
), userpass
, sizeof(userpass
),
3238 hostname
, sizeof(hostname
), &port
,
3239 resource
, sizeof(resource
));
3241 if ((!strncmp(resource
, "/printers/", 10) && resource
[10]) ||
3242 (!strncmp(resource
, "/classes/", 9) && resource
[9]))
3244 send_ipp_status(con
, IPP_NOT_FOUND
,
3245 _("The printer or class does not exist."));
3253 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
3255 send_http_error(con
, status
, NULL
);
3261 for (i
= 0; i
< job_ids
->num_values
; i
++)
3263 if ((job
= cupsdFindJob(job_ids
->values
[i
].integer
)) == NULL
)
3266 if (con
->request
->request
.op
.operation_id
== IPP_CANCEL_MY_JOBS
&&
3267 _cups_strcasecmp(job
->username
, username
))
3271 if (i
< job_ids
->num_values
)
3273 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."),
3274 job_ids
->values
[i
].integer
);
3278 for (i
= 0; i
< job_ids
->num_values
; i
++)
3280 job
= cupsdFindJob(job_ids
->values
[i
].integer
);
3282 cupsdSetJobState(job
, IPP_JOB_CANCELED
, purge
,
3283 purge
== CUPSD_JOB_PURGE
? "Job purged by user." :
3284 "Job canceled by user.");
3287 cupsdLogMessage(CUPSD_LOG_INFO
, "Selected jobs were %s by \"%s\".",
3288 purge
== CUPSD_JOB_PURGE
? "purged" : "canceled",
3294 * Cancel all jobs on all printers...
3297 cupsdCancelJobs(NULL
, username
, purge
);
3299 cupsdLogMessage(CUPSD_LOG_INFO
, "All jobs were %s by \"%s\".",
3300 purge
== CUPSD_JOB_PURGE
? "purged" : "canceled",
3310 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
,
3313 send_http_error(con
, status
, printer
);
3319 for (i
= 0; i
< job_ids
->num_values
; i
++)
3321 if ((job
= cupsdFindJob(job_ids
->values
[i
].integer
)) == NULL
||
3322 _cups_strcasecmp(job
->dest
, printer
->name
))
3325 if (con
->request
->request
.op
.operation_id
== IPP_CANCEL_MY_JOBS
&&
3326 _cups_strcasecmp(job
->username
, username
))
3330 if (i
< job_ids
->num_values
)
3332 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."),
3333 job_ids
->values
[i
].integer
);
3337 for (i
= 0; i
< job_ids
->num_values
; i
++)
3339 job
= cupsdFindJob(job_ids
->values
[i
].integer
);
3341 cupsdSetJobState(job
, IPP_JOB_CANCELED
, purge
,
3342 purge
== CUPSD_JOB_PURGE
? "Job purged by user." :
3343 "Job canceled by user.");
3346 cupsdLogMessage(CUPSD_LOG_INFO
, "Selected jobs were %s by \"%s\".",
3347 purge
== CUPSD_JOB_PURGE
? "purged" : "canceled",
3353 * Cancel all of the jobs on the named printer...
3356 cupsdCancelJobs(printer
->name
, username
, purge
);
3358 cupsdLogMessage(CUPSD_LOG_INFO
, "All jobs on \"%s\" were %s by \"%s\".",
3360 purge
== CUPSD_JOB_PURGE
? "purged" : "canceled",
3365 con
->response
->request
.status
.status_code
= IPP_OK
;
3372 * 'cancel_job()' - Cancel a print job.
3376 cancel_job(cupsd_client_t
*con
, /* I - Client connection */
3377 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
3379 ipp_attribute_t
*attr
; /* Current attribute */
3380 int jobid
; /* Job ID */
3381 char scheme
[HTTP_MAX_URI
], /* Scheme portion of URI */
3382 username
[HTTP_MAX_URI
], /* Username portion of URI */
3383 host
[HTTP_MAX_URI
], /* Host portion of URI */
3384 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
3385 int port
; /* Port portion of URI */
3386 cupsd_job_t
*job
; /* Job information */
3387 cups_ptype_t dtype
; /* Destination type (printer/class) */
3388 cupsd_printer_t
*printer
; /* Printer data */
3389 cupsd_jobaction_t purge
; /* Purge the job? */
3392 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cancel_job(%p[%d], %s)", con
,
3393 con
->number
, uri
->values
[0].string
.text
);
3396 * See if we have a job URI or a printer URI...
3399 if (!strcmp(uri
->name
, "printer-uri"))
3402 * Got a printer URI; see if we also have a job-id attribute...
3405 if ((attr
= ippFindAttribute(con
->request
, "job-id",
3406 IPP_TAG_INTEGER
)) == NULL
)
3408 send_ipp_status(con
, IPP_BAD_REQUEST
,
3409 _("Got a printer-uri attribute but no job-id."));
3413 if ((jobid
= attr
->values
[0].integer
) == 0)
3416 * Find the current job on the specified printer...
3419 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
3425 send_ipp_status(con
, IPP_NOT_FOUND
,
3426 _("The printer or class does not exist."));
3431 * See if there are any pending jobs...
3434 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
3436 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
3437 if (job
->state_value
<= IPP_JOB_PROCESSING
&&
3438 !_cups_strcasecmp(job
->dest
, printer
->name
))
3446 * No, try stopped jobs...
3449 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
3451 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
3452 if (job
->state_value
== IPP_JOB_STOPPED
&&
3453 !_cups_strcasecmp(job
->dest
, printer
->name
))
3460 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("No active jobs on %s."),
3470 * Got a job URI; parse it to get the job ID...
3473 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
3474 sizeof(scheme
), username
, sizeof(username
), host
,
3475 sizeof(host
), &port
, resource
, sizeof(resource
));
3477 if (strncmp(resource
, "/jobs/", 6))
3483 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad job-uri \"%s\"."),
3484 uri
->values
[0].string
.text
);
3488 jobid
= atoi(resource
+ 6);
3492 * Look for the "purge-job" attribute...
3495 if ((attr
= ippFindAttribute(con
->request
, "purge-job",
3496 IPP_TAG_BOOLEAN
)) != NULL
)
3497 purge
= attr
->values
[0].boolean
? CUPSD_JOB_PURGE
: CUPSD_JOB_DEFAULT
;
3499 purge
= CUPSD_JOB_DEFAULT
;
3502 * See if the job exists...
3505 if ((job
= cupsdFindJob(jobid
)) == NULL
)
3508 * Nope - return a "not found" error...
3511 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."), jobid
);
3516 * See if the job is owned by the requesting user...
3519 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
3521 send_http_error(con
, con
->username
[0] ? HTTP_FORBIDDEN
: HTTP_UNAUTHORIZED
,
3522 cupsdFindDest(job
->dest
));
3527 * See if the job is already completed, canceled, or aborted; if so,
3528 * we can't cancel...
3531 if (job
->state_value
>= IPP_JOB_CANCELED
&& purge
!= CUPSD_JOB_PURGE
)
3533 switch (job
->state_value
)
3535 case IPP_JOB_CANCELED
:
3536 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
3537 _("Job #%d is already canceled - can\'t cancel."),
3541 case IPP_JOB_ABORTED
:
3542 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
3543 _("Job #%d is already aborted - can\'t cancel."),
3548 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
3549 _("Job #%d is already completed - can\'t cancel."),
3558 * Cancel the job and return...
3561 cupsdSetJobState(job
, IPP_JOB_CANCELED
, purge
,
3562 purge
== CUPSD_JOB_PURGE
? "Job purged by \"%s\"" :
3563 "Job canceled by \"%s\"",
3567 if (purge
== CUPSD_JOB_PURGE
)
3568 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job %d] Purged by \"%s\".", jobid
,
3571 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job %d] Canceled by \"%s\".", jobid
,
3574 con
->response
->request
.status
.status_code
= IPP_OK
;
3579 * 'cancel_subscription()' - Cancel a subscription.
3583 cancel_subscription(
3584 cupsd_client_t
*con
, /* I - Client connection */
3585 int sub_id
) /* I - Subscription ID */
3587 http_status_t status
; /* Policy status */
3588 cupsd_subscription_t
*sub
; /* Subscription */
3591 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3592 "cancel_subscription(con=%p[%d], sub_id=%d)",
3593 con
, con
->number
, sub_id
);
3596 * Is the subscription ID valid?
3599 if ((sub
= cupsdFindSubscription(sub_id
)) == NULL
)
3602 * Bad subscription ID...
3605 send_ipp_status(con
, IPP_NOT_FOUND
,
3606 _("Subscription #%d does not exist."), sub_id
);
3614 if ((status
= cupsdCheckPolicy(sub
->dest
? sub
->dest
->op_policy_ptr
:
3616 con
, sub
->owner
)) != HTTP_OK
)
3618 send_http_error(con
, status
, sub
->dest
);
3623 * Cancel the subscription...
3626 cupsdDeleteSubscription(sub
, 1);
3628 con
->response
->request
.status
.status_code
= IPP_OK
;
3633 * 'check_rss_recipient()' - Check that we do not have a duplicate RSS feed URI.
3636 static int /* O - 1 if OK, 0 if not */
3637 check_rss_recipient(
3638 const char *recipient
) /* I - Recipient URI */
3640 cupsd_subscription_t
*sub
; /* Current subscription */
3643 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
3645 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
3649 * Compare the URIs up to the first ?...
3652 const char *r1
, *r2
;
3654 for (r1
= recipient
, r2
= sub
->recipient
;
3655 *r1
== *r2
&& *r1
&& *r1
!= '?' && *r2
&& *r2
!= '?';
3667 * 'check_quotas()' - Check quotas for a printer and user.
3670 static int /* O - 1 if OK, 0 if forbidden,
3671 -1 if limit reached */
3672 check_quotas(cupsd_client_t
*con
, /* I - Client connection */
3673 cupsd_printer_t
*p
) /* I - Printer or class */
3675 char username
[33], /* Username */
3676 *name
; /* Current user name */
3677 cupsd_quota_t
*q
; /* Quota data */
3678 #ifdef HAVE_MBR_UID_TO_UUID
3680 * Use Apple membership APIs which require that all names represent
3681 * valid user account or group records accessible by the server.
3684 uuid_t usr_uuid
; /* UUID for job requesting user */
3685 uuid_t usr2_uuid
; /* UUID for ACL user name entry */
3686 uuid_t grp_uuid
; /* UUID for ACL group name entry */
3687 int mbr_err
; /* Error from membership function */
3688 int is_member
; /* Is this user a member? */
3691 * Use standard POSIX APIs for checking users and groups...
3694 struct passwd
*pw
; /* User password data */
3695 #endif /* HAVE_MBR_UID_TO_UUID */
3698 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "check_quotas(%p[%d], %p[%s])",
3699 con
, con
->number
, p
, p
->name
);
3702 * Figure out who is printing...
3705 strlcpy(username
, get_username(con
), sizeof(username
));
3707 if ((name
= strchr(username
, '@')) != NULL
)
3708 *name
= '\0'; /* Strip @REALM */
3711 * Check global active job limits for printers and users...
3714 if (MaxJobsPerPrinter
)
3717 * Check if there are too many pending jobs on this printer...
3720 if (cupsdGetPrinterJobCount(p
->name
) >= MaxJobsPerPrinter
)
3722 cupsdLogMessage(CUPSD_LOG_INFO
, "Too many jobs for printer \"%s\"...",
3731 * Check if there are too many pending jobs for this user...
3734 if (cupsdGetUserJobCount(username
) >= MaxJobsPerUser
)
3736 cupsdLogMessage(CUPSD_LOG_INFO
, "Too many jobs for user \"%s\"...",
3743 * Check against users...
3746 if (cupsArrayCount(p
->users
) == 0 && p
->k_limit
== 0 && p
->page_limit
== 0)
3749 if (cupsArrayCount(p
->users
))
3751 #ifdef HAVE_MBR_UID_TO_UUID
3753 * Get UUID for job requesting user...
3756 if (mbr_user_name_to_uuid((char *)username
, usr_uuid
))
3762 cupsdLogMessage(CUPSD_LOG_DEBUG
,
3763 "check_quotas: UUID lookup failed for user \"%s\"",
3765 cupsdLogMessage(CUPSD_LOG_INFO
,
3766 "Denying user \"%s\" access to printer \"%s\" "
3767 "(unknown user)...",
3773 * Get UID and GID of requesting user...
3776 pw
= getpwnam(username
);
3778 #endif /* HAVE_MBR_UID_TO_UUID */
3780 for (name
= (char *)cupsArrayFirst(p
->users
);
3782 name
= (char *)cupsArrayNext(p
->users
))
3786 * Check group membership...
3789 #ifdef HAVE_MBR_UID_TO_UUID
3792 if (uuid_parse(name
+ 2, grp_uuid
))
3793 uuid_clear(grp_uuid
);
3795 else if ((mbr_err
= mbr_group_name_to_uuid(name
+ 1, grp_uuid
)) != 0)
3798 * Invalid ACL entries are ignored for matching; just record a
3799 * warning in the log...
3802 cupsdLogMessage(CUPSD_LOG_DEBUG
,
3803 "check_quotas: UUID lookup failed for ACL entry "
3804 "\"%s\" (err=%d)", name
, mbr_err
);
3805 cupsdLogMessage(CUPSD_LOG_WARN
,
3806 "Access control entry \"%s\" not a valid group name; "
3807 "entry ignored", name
);
3810 if ((mbr_err
= mbr_check_membership(usr_uuid
, grp_uuid
,
3814 * At this point, there should be no errors, but check anyways...
3817 cupsdLogMessage(CUPSD_LOG_DEBUG
,
3818 "check_quotas: group \"%s\" membership check "
3819 "failed (err=%d)", name
+ 1, mbr_err
);
3824 * Stop if we found a match...
3831 if (cupsdCheckGroup(username
, pw
, name
+ 1))
3833 #endif /* HAVE_MBR_UID_TO_UUID */
3835 #ifdef HAVE_MBR_UID_TO_UUID
3840 if (uuid_parse(name
+ 1, usr2_uuid
))
3841 uuid_clear(usr2_uuid
);
3843 else if ((mbr_err
= mbr_user_name_to_uuid(name
, usr2_uuid
)) != 0)
3846 * Invalid ACL entries are ignored for matching; just record a
3847 * warning in the log...
3850 cupsdLogMessage(CUPSD_LOG_DEBUG
,
3851 "check_quotas: UUID lookup failed for ACL entry "
3852 "\"%s\" (err=%d)", name
, mbr_err
);
3853 cupsdLogMessage(CUPSD_LOG_WARN
,
3854 "Access control entry \"%s\" not a valid user name; "
3855 "entry ignored", name
);
3858 if (!uuid_compare(usr_uuid
, usr2_uuid
))
3862 else if (!_cups_strcasecmp(username
, name
))
3864 #endif /* HAVE_MBR_UID_TO_UUID */
3866 if ((name
!= NULL
) == p
->deny_users
)
3868 cupsdLogMessage(CUPSD_LOG_INFO
,
3869 "Denying user \"%s\" access to printer \"%s\"...",
3879 if (p
->k_limit
|| p
->page_limit
)
3881 if ((q
= cupsdUpdateQuota(p
, username
, 0, 0)) == NULL
)
3883 cupsdLogMessage(CUPSD_LOG_ERROR
,
3884 "Unable to allocate quota data for user \"%s\"",
3889 if ((q
->k_count
>= p
->k_limit
&& p
->k_limit
) ||
3890 (q
->page_count
>= p
->page_limit
&& p
->page_limit
))
3892 cupsdLogMessage(CUPSD_LOG_INFO
, "User \"%s\" is over the quota limit...",
3899 * If we have gotten this far, we're done!
3907 * 'close_job()' - Close a multi-file job.
3911 close_job(cupsd_client_t
*con
, /* I - Client connection */
3912 ipp_attribute_t
*uri
) /* I - Printer URI */
3914 cupsd_job_t
*job
; /* Job */
3915 ipp_attribute_t
*attr
; /* Attribute */
3916 char job_uri
[HTTP_MAX_URI
],
3918 username
[256]; /* User name */
3921 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "close_job(%p[%d], %s)", con
,
3922 con
->number
, uri
->values
[0].string
.text
);
3925 * See if we have a job URI or a printer URI...
3928 if (strcmp(uri
->name
, "printer-uri"))
3931 * job-uri is not supported by Close-Job!
3934 send_ipp_status(con
, IPP_BAD_REQUEST
,
3935 _("Close-Job doesn't support the job-uri attribute."));
3940 * Got a printer URI; see if we also have a job-id attribute...
3943 if ((attr
= ippFindAttribute(con
->request
, "job-id",
3944 IPP_TAG_INTEGER
)) == NULL
)
3946 send_ipp_status(con
, IPP_BAD_REQUEST
,
3947 _("Got a printer-uri attribute but no job-id."));
3951 if ((job
= cupsdFindJob(attr
->values
[0].integer
)) == NULL
)
3954 * Nope - return a "not found" error...
3957 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."),
3958 attr
->values
[0].integer
);
3963 * See if the job is owned by the requesting user...
3966 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
3968 send_http_error(con
, con
->username
[0] ? HTTP_FORBIDDEN
: HTTP_UNAUTHORIZED
,
3969 cupsdFindDest(job
->dest
));
3974 * Add any ending sheet...
3977 if (cupsdTimeoutJob(job
))
3980 if (job
->state_value
== IPP_JOB_STOPPED
)
3982 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
3983 job
->state_value
= IPP_JOB_PENDING
;
3985 else if (job
->state_value
== IPP_JOB_HELD
)
3987 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
3988 IPP_TAG_KEYWORD
)) == NULL
)
3989 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
3991 if (!attr
|| !strcmp(attr
->values
[0].string
.text
, "no-hold"))
3993 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
3994 job
->state_value
= IPP_JOB_PENDING
;
3999 cupsdMarkDirty(CUPSD_DIRTY_JOBS
);
4002 * Fill in the response info...
4005 httpAssembleURIf(HTTP_URI_CODING_ALL
, job_uri
, sizeof(job_uri
), "ipp", NULL
,
4006 con
->clientname
, con
->clientport
, "/jobs/%d", job
->id
);
4007 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
,
4010 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
4012 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
4015 con
->response
->request
.status
.status_code
= IPP_OK
;
4018 * Start the job if necessary...
4026 * 'copy_attrs()' - Copy attributes from one request to another.
4030 copy_attrs(ipp_t
*to
, /* I - Destination request */
4031 ipp_t
*from
, /* I - Source request */
4032 cups_array_t
*ra
, /* I - Requested attributes */
4033 ipp_tag_t group
, /* I - Group to copy */
4034 int quickcopy
, /* I - Do a quick copy? */
4035 cups_array_t
*exclude
) /* I - Attributes to exclude? */
4037 ipp_attribute_t
*fromattr
; /* Source attribute */
4040 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
4041 "copy_attrs(to=%p, from=%p, ra=%p, group=%x, quickcopy=%d)",
4042 to
, from
, ra
, group
, quickcopy
);
4047 for (fromattr
= from
->attrs
; fromattr
; fromattr
= fromattr
->next
)
4050 * Filter attributes as needed...
4053 if ((group
!= IPP_TAG_ZERO
&& fromattr
->group_tag
!= group
&&
4054 fromattr
->group_tag
!= IPP_TAG_ZERO
) || !fromattr
->name
)
4057 if (!strcmp(fromattr
->name
, "document-password") ||
4058 !strcmp(fromattr
->name
, "job-authorization-uri") ||
4059 !strcmp(fromattr
->name
, "job-password") ||
4060 !strcmp(fromattr
->name
, "job-password-encryption") ||
4061 !strcmp(fromattr
->name
, "job-printer-uri"))
4065 (cupsArrayFind(exclude
, fromattr
->name
) ||
4066 cupsArrayFind(exclude
, "all")))
4069 * We need to exclude this attribute for security reasons; we require the
4070 * job-id attribute regardless of the security settings for IPP
4073 * The job-printer-uri attribute is handled by copy_job_attrs().
4075 * Subscription attribute security is handled by copy_subscription_attrs().
4078 if (strcmp(fromattr
->name
, "job-id"))
4082 if (!ra
|| cupsArrayFind(ra
, fromattr
->name
))
4085 * Don't send collection attributes by default to IPP/1.x clients
4086 * since many do not support collections. Also don't send
4087 * media-col-database unless specifically requested by the client.
4090 if (fromattr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
&&
4092 (to
->request
.status
.version
[0] == 1 ||
4093 !strcmp(fromattr
->name
, "media-col-database")))
4096 ippCopyAttribute(to
, fromattr
, quickcopy
);
4103 * 'copy_banner()' - Copy a banner file to the requests directory for the
4107 static int /* O - Size of banner file in kbytes */
4108 copy_banner(cupsd_client_t
*con
, /* I - Client connection */
4109 cupsd_job_t
*job
, /* I - Job information */
4110 const char *name
) /* I - Name of banner */
4112 int i
; /* Looping var */
4113 int kbytes
; /* Size of banner file in kbytes */
4114 char filename
[1024]; /* Job filename */
4115 cupsd_banner_t
*banner
; /* Pointer to banner */
4116 cups_file_t
*in
; /* Input file */
4117 cups_file_t
*out
; /* Output file */
4118 int ch
; /* Character from file */
4119 char attrname
[255], /* Name of attribute */
4120 *s
; /* Pointer into name */
4121 ipp_attribute_t
*attr
; /* Attribute */
4124 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
4125 "copy_banner(con=%p[%d], job=%p[%d], name=\"%s\")",
4126 con
, con
? con
->number
: -1, job
, job
->id
,
4127 name
? name
: "(null)");
4130 * Find the banner; return if not found or "none"...
4133 if (!name
|| !strcmp(name
, "none") ||
4134 (banner
= cupsdFindBanner(name
)) == NULL
)
4138 * Open the banner and job files...
4141 if (add_file(con
, job
, banner
->filetype
, 0))
4144 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
,
4146 if ((out
= cupsFileOpen(filename
, "w")) == NULL
)
4148 cupsdLogMessage(CUPSD_LOG_ERROR
,
4149 "Unable to create banner job file %s - %s",
4150 filename
, strerror(errno
));
4155 fchmod(cupsFileNumber(out
), 0640);
4156 fchown(cupsFileNumber(out
), RunUser
, Group
);
4159 * Try the localized banner file under the subdirectory...
4162 strlcpy(attrname
, job
->attrs
->attrs
->next
->values
[0].string
.text
,
4164 if (strlen(attrname
) > 2 && attrname
[2] == '-')
4167 * Convert ll-cc to ll_CC...
4171 attrname
[3] = (char)toupper(attrname
[3] & 255);
4172 attrname
[4] = (char)toupper(attrname
[4] & 255);
4175 snprintf(filename
, sizeof(filename
), "%s/banners/%s/%s", DataDir
,
4178 if (access(filename
, 0) && strlen(attrname
) > 2)
4181 * Wasn't able to find "ll_CC" locale file; try the non-national
4182 * localization banner directory.
4187 snprintf(filename
, sizeof(filename
), "%s/banners/%s/%s", DataDir
,
4191 if (access(filename
, 0))
4194 * Use the non-localized banner file.
4197 snprintf(filename
, sizeof(filename
), "%s/banners/%s", DataDir
, name
);
4200 if ((in
= cupsFileOpen(filename
, "r")) == NULL
)
4204 cupsdLogMessage(CUPSD_LOG_ERROR
,
4205 "Unable to open banner template file %s - %s",
4206 filename
, strerror(errno
));
4212 * Parse the file to the end...
4215 while ((ch
= cupsFileGetChar(in
)) != EOF
)
4219 * Get an attribute name...
4222 for (s
= attrname
; (ch
= cupsFileGetChar(in
)) != EOF
;)
4223 if (!isalpha(ch
& 255) && ch
!= '-' && ch
!= '?')
4225 else if (s
< (attrname
+ sizeof(attrname
) - 1))
4235 * Ignore { followed by stuff that is not an attribute name...
4238 cupsFilePrintf(out
, "{%s%c", attrname
, ch
);
4243 * See if it is defined...
4246 if (attrname
[0] == '?')
4251 if (!strcmp(s
, "printer-name"))
4253 cupsFilePuts(out
, job
->dest
);
4256 else if ((attr
= ippFindAttribute(job
->attrs
, s
, IPP_TAG_ZERO
)) == NULL
)
4259 * See if we have a leading question mark...
4262 if (attrname
[0] != '?')
4265 * Nope, write to file as-is; probably a PostScript procedure...
4268 cupsFilePrintf(out
, "{%s}", attrname
);
4275 * Output value(s)...
4278 for (i
= 0; i
< attr
->num_values
; i
++)
4281 cupsFilePutChar(out
, ',');
4283 switch (attr
->value_tag
)
4285 case IPP_TAG_INTEGER
:
4287 if (!strncmp(s
, "time-at-", 8))
4289 struct timeval tv
; /* Time value */
4291 tv
.tv_sec
= attr
->values
[i
].integer
;
4294 cupsFilePuts(out
, cupsdGetDateTime(&tv
, CUPSD_TIME_STANDARD
));
4297 cupsFilePrintf(out
, "%d", attr
->values
[i
].integer
);
4300 case IPP_TAG_BOOLEAN
:
4301 cupsFilePrintf(out
, "%d", attr
->values
[i
].boolean
);
4304 case IPP_TAG_NOVALUE
:
4305 cupsFilePuts(out
, "novalue");
4308 case IPP_TAG_RANGE
:
4309 cupsFilePrintf(out
, "%d-%d", attr
->values
[i
].range
.lower
,
4310 attr
->values
[i
].range
.upper
);
4313 case IPP_TAG_RESOLUTION
:
4314 cupsFilePrintf(out
, "%dx%d%s", attr
->values
[i
].resolution
.xres
,
4315 attr
->values
[i
].resolution
.yres
,
4316 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4321 case IPP_TAG_STRING
:
4324 case IPP_TAG_KEYWORD
:
4325 case IPP_TAG_CHARSET
:
4326 case IPP_TAG_LANGUAGE
:
4327 if (!_cups_strcasecmp(banner
->filetype
->type
, "postscript"))
4330 * Need to quote strings for PS banners...
4335 for (p
= attr
->values
[i
].string
.text
; *p
; p
++)
4337 if (*p
== '(' || *p
== ')' || *p
== '\\')
4339 cupsFilePutChar(out
, '\\');
4340 cupsFilePutChar(out
, *p
);
4342 else if (*p
< 32 || *p
> 126)
4343 cupsFilePrintf(out
, "\\%03o", *p
& 255);
4345 cupsFilePutChar(out
, *p
);
4349 cupsFilePuts(out
, attr
->values
[i
].string
.text
);
4353 break; /* anti-compiler-warning-code */
4357 else if (ch
== '\\') /* Quoted char */
4359 ch
= cupsFileGetChar(in
);
4361 if (ch
!= '{') /* Only do special handling for \{ */
4362 cupsFilePutChar(out
, '\\');
4364 cupsFilePutChar(out
, ch
);
4367 cupsFilePutChar(out
, ch
);
4371 kbytes
= (cupsFileTell(out
) + 1023) / 1024;
4373 job
->koctets
+= kbytes
;
4375 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) != NULL
)
4376 attr
->values
[0].integer
+= kbytes
;
4385 * 'copy_file()' - Copy a PPD file...
4388 static int /* O - 0 = success, -1 = error */
4389 copy_file(const char *from
, /* I - Source file */
4390 const char *to
, /* I - Destination file */
4391 mode_t mode
) /* I - Permissions */
4393 cups_file_t
*src
, /* Source file */
4394 *dst
; /* Destination file */
4395 int bytes
; /* Bytes to read/write */
4396 char buffer
[2048]; /* Copy buffer */
4399 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "copy_file(\"%s\", \"%s\")", from
, to
);
4402 * Open the source and destination file for a copy...
4405 if ((src
= cupsFileOpen(from
, "rb")) == NULL
)
4408 if ((dst
= cupsdCreateConfFile(to
, mode
)) == NULL
)
4415 * Copy the source file to the destination...
4418 while ((bytes
= cupsFileRead(src
, buffer
, sizeof(buffer
))) > 0)
4419 if (cupsFileWrite(dst
, buffer
, (size_t)bytes
) < bytes
)
4427 * Close both files and return...
4432 return (cupsdCloseCreatedConfFile(dst
, to
));
4437 * 'copy_model()' - Copy a PPD model file, substituting default values
4441 static int /* O - 0 = success, -1 = error */
4442 copy_model(cupsd_client_t
*con
, /* I - Client connection */
4443 const char *from
, /* I - Source file */
4444 const char *to
) /* I - Destination file */
4446 fd_set input
; /* select() input set */
4447 struct timeval timeout
; /* select() timeout */
4448 int maxfd
; /* Max file descriptor for select() */
4449 char tempfile
[1024]; /* Temporary PPD file */
4450 int tempfd
; /* Temporary PPD file descriptor */
4451 int temppid
; /* Process ID of cups-driverd */
4452 int temppipe
[2]; /* Temporary pipes */
4453 char *argv
[4], /* Command-line arguments */
4454 *envp
[MAX_ENV
]; /* Environment */
4455 cups_file_t
*src
, /* Source file */
4456 *dst
; /* Destination file */
4457 ppd_file_t
*ppd
; /* PPD file */
4458 int bytes
, /* Bytes from pipe */
4459 total
; /* Total bytes from pipe */
4460 char buffer
[2048]; /* Copy buffer */
4461 int i
; /* Looping var */
4462 char option
[PPD_MAX_NAME
], /* Option name */
4463 choice
[PPD_MAX_NAME
]; /* Choice name */
4464 ppd_size_t
*size
; /* Default size */
4465 int num_defaults
; /* Number of default options */
4466 cups_option_t
*defaults
; /* Default options */
4467 char cups_protocol
[PPD_MAX_LINE
];
4468 /* cupsProtocol attribute */
4471 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "copy_model(con=%p, from=\"%s\", to=\"%s\")", con
, from
, to
);
4474 * Run cups-driverd to get the PPD file...
4477 argv
[0] = "cups-driverd";
4479 argv
[2] = (char *)from
;
4482 cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
4484 snprintf(buffer
, sizeof(buffer
), "%s/daemon/cups-driverd", ServerBin
);
4485 snprintf(tempfile
, sizeof(tempfile
), "%s/%d.ppd", TempDir
, con
->number
);
4486 tempfd
= open(tempfile
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4487 if (tempfd
< 0 || cupsdOpenPipe(temppipe
))
4490 cupsdLogMessage(CUPSD_LOG_DEBUG
,
4491 "copy_model: Running \"cups-driverd cat %s\"...", from
);
4493 if (!cupsdStartProcess(buffer
, argv
, envp
, -1, temppipe
[1], CGIPipes
[1],
4494 -1, -1, 0, DefaultProfile
, NULL
, &temppid
))
4505 * Wait up to 30 seconds for the PPD file to be copied...
4510 if (temppipe
[0] > CGIPipes
[0])
4511 maxfd
= temppipe
[0] + 1;
4513 maxfd
= CGIPipes
[0] + 1;
4518 * See if we have data ready...
4522 FD_SET(temppipe
[0], &input
);
4523 FD_SET(CGIPipes
[0], &input
);
4525 timeout
.tv_sec
= 30;
4526 timeout
.tv_usec
= 0;
4528 if ((i
= select(maxfd
, &input
, NULL
, NULL
, &timeout
)) < 0)
4538 * We have timed out...
4544 if (FD_ISSET(temppipe
[0], &input
))
4547 * Read the PPD file from the pipe, and write it to the PPD file.
4550 if ((bytes
= read(temppipe
[0], buffer
, sizeof(buffer
))) > 0)
4552 if (write(tempfd
, buffer
, (size_t)bytes
) < bytes
)
4561 if (FD_ISSET(CGIPipes
[0], &input
))
4571 * No data from cups-deviced...
4574 cupsdLogMessage(CUPSD_LOG_ERROR
, "copy_model: empty PPD file");
4580 * Open the source file for a copy...
4583 if ((src
= cupsFileOpen(tempfile
, "rb")) == NULL
)
4590 * Read the source file and see what page sizes are supported...
4593 if ((ppd
= _ppdOpen(src
, _PPD_LOCALIZATION_NONE
)) == NULL
)
4601 * Open the destination (if possible) and set the default options...
4606 cups_protocol
[0] = '\0';
4608 if ((dst
= cupsFileOpen(to
, "rb")) != NULL
)
4611 * Read all of the default lines from the old PPD...
4614 while (cupsFileGets(dst
, buffer
, sizeof(buffer
)))
4615 if (!strncmp(buffer
, "*Default", 8))
4618 * Add the default option...
4621 if (!ppd_parse_line(buffer
, option
, sizeof(option
),
4622 choice
, sizeof(choice
)))
4624 ppd_option_t
*ppdo
; /* PPD option */
4628 * Only add the default if the default hasn't already been
4629 * set and the choice exists in the new PPD...
4632 if (!cupsGetOption(option
, num_defaults
, defaults
) &&
4633 (ppdo
= ppdFindOption(ppd
, option
)) != NULL
&&
4634 ppdFindChoice(ppdo
, choice
))
4635 num_defaults
= cupsAddOption(option
, choice
, num_defaults
,
4639 else if (!strncmp(buffer
, "*cupsProtocol:", 14))
4640 strlcpy(cups_protocol
, buffer
, sizeof(cups_protocol
));
4644 else if ((size
= ppdPageSize(ppd
, DefaultPaperSize
)) != NULL
)
4647 * Add the default media sizes...
4650 num_defaults
= cupsAddOption("PageSize", size
->name
,
4651 num_defaults
, &defaults
);
4652 num_defaults
= cupsAddOption("PageRegion", size
->name
,
4653 num_defaults
, &defaults
);
4654 num_defaults
= cupsAddOption("PaperDimension", size
->name
,
4655 num_defaults
, &defaults
);
4656 num_defaults
= cupsAddOption("ImageableArea", size
->name
,
4657 num_defaults
, &defaults
);
4663 * Open the destination file for a copy...
4666 if ((dst
= cupsdCreateConfFile(to
, ConfigFilePerm
)) == NULL
)
4668 cupsFreeOptions(num_defaults
, defaults
);
4675 * Copy the source file to the destination...
4678 cupsFileRewind(src
);
4680 while (cupsFileGets(src
, buffer
, sizeof(buffer
)))
4682 if (!strncmp(buffer
, "*Default", 8))
4685 * Check for an previous default option choice...
4688 if (!ppd_parse_line(buffer
, option
, sizeof(option
),
4689 choice
, sizeof(choice
)))
4691 const char *val
; /* Default option value */
4694 if ((val
= cupsGetOption(option
, num_defaults
, defaults
)) != NULL
)
4697 * Substitute the previous choice...
4700 snprintf(buffer
, sizeof(buffer
), "*Default%s: %s", option
, val
);
4705 cupsFilePrintf(dst
, "%s\n", buffer
);
4708 if (cups_protocol
[0])
4709 cupsFilePrintf(dst
, "%s\n", cups_protocol
);
4711 cupsFreeOptions(num_defaults
, defaults
);
4714 * Close both files and return...
4721 return (cupsdCloseCreatedConfFile(dst
, to
));
4726 * 'copy_job_attrs()' - Copy job attributes.
4730 copy_job_attrs(cupsd_client_t
*con
, /* I - Client connection */
4731 cupsd_job_t
*job
, /* I - Job */
4732 cups_array_t
*ra
, /* I - Requested attributes array */
4733 cups_array_t
*exclude
) /* I - Private attributes array */
4735 char job_uri
[HTTP_MAX_URI
]; /* Job URI */
4739 * Send the requested attributes for each job...
4742 if (!cupsArrayFind(exclude
, "all"))
4744 if ((!exclude
|| !cupsArrayFind(exclude
, "number-of-documents")) &&
4745 (!ra
|| cupsArrayFind(ra
, "number-of-documents")))
4746 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4747 "number-of-documents", job
->num_files
);
4749 if ((!exclude
|| !cupsArrayFind(exclude
, "job-media-progress")) &&
4750 (!ra
|| cupsArrayFind(ra
, "job-media-progress")))
4751 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4752 "job-media-progress", job
->progress
);
4754 if ((!exclude
|| !cupsArrayFind(exclude
, "job-more-info")) &&
4755 (!ra
|| cupsArrayFind(ra
, "job-more-info")))
4757 httpAssembleURIf(HTTP_URI_CODING_ALL
, job_uri
, sizeof(job_uri
), "http",
4758 NULL
, con
->clientname
, con
->clientport
, "/jobs/%d",
4760 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
4761 "job-more-info", NULL
, job_uri
);
4764 if (job
->state_value
> IPP_JOB_PROCESSING
&&
4765 (!exclude
|| !cupsArrayFind(exclude
, "job-preserved")) &&
4766 (!ra
|| cupsArrayFind(ra
, "job-preserved")))
4767 ippAddBoolean(con
->response
, IPP_TAG_JOB
, "job-preserved",
4768 job
->num_files
> 0);
4770 if ((!exclude
|| !cupsArrayFind(exclude
, "job-printer-up-time")) &&
4771 (!ra
|| cupsArrayFind(ra
, "job-printer-up-time")))
4772 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4773 "job-printer-up-time", time(NULL
));
4776 if (!ra
|| cupsArrayFind(ra
, "job-printer-uri"))
4778 httpAssembleURIf(HTTP_URI_CODING_ALL
, job_uri
, sizeof(job_uri
), "ipp", NULL
,
4779 con
->clientname
, con
->clientport
,
4780 (job
->dtype
& CUPS_PRINTER_CLASS
) ? "/classes/%s" :
4783 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
4784 "job-printer-uri", NULL
, job_uri
);
4787 if (!ra
|| cupsArrayFind(ra
, "job-uri"))
4789 httpAssembleURIf(HTTP_URI_CODING_ALL
, job_uri
, sizeof(job_uri
), "ipp", NULL
,
4790 con
->clientname
, con
->clientport
, "/jobs/%d",
4792 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
4793 "job-uri", NULL
, job_uri
);
4798 copy_attrs(con
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0, exclude
);
4803 * Generate attributes from the job structure...
4806 if (job
->completed_time
&& (!ra
|| cupsArrayFind(ra
, "date-time-at-completed")))
4807 ippAddDate(con
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed_time
));
4809 if (job
->creation_time
&& (!ra
|| cupsArrayFind(ra
, "date-time-at-creation")))
4810 ippAddDate(con
->response
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(job
->creation_time
));
4812 if (!ra
|| cupsArrayFind(ra
, "job-id"))
4813 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
4815 if (!ra
|| cupsArrayFind(ra
, "job-k-octets"))
4816 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-k-octets", job
->koctets
);
4818 if (job
->name
&& (!ra
|| cupsArrayFind(ra
, "job-name")))
4819 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-name", NULL
, job
->name
);
4821 if (job
->username
&& (!ra
|| cupsArrayFind(ra
, "job-originating-user-name")))
4822 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-originating-user-name", NULL
, job
->username
);
4824 if (!ra
|| cupsArrayFind(ra
, "job-state"))
4825 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state", (int)job
->state_value
);
4827 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
4829 switch (job
->state_value
)
4831 default : /* Should never get here for processing, pending, held, or stopped jobs since they don't get unloaded... */
4833 case IPP_JSTATE_ABORTED
:
4834 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "job-state-reasons", NULL
, "job-aborted-by-system");
4836 case IPP_JSTATE_CANCELED
:
4837 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "job-state-reasons", NULL
, "job-canceled-by-user");
4839 case IPP_JSTATE_COMPLETED
:
4840 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "job-state-reasons", NULL
, "job-completed-successfully");
4845 if (job
->completed_time
&& (!ra
|| cupsArrayFind(ra
, "time-at-completed")))
4846 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-completed", (int)job
->completed_time
);
4848 if (job
->creation_time
&& (!ra
|| cupsArrayFind(ra
, "time-at-creation")))
4849 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)job
->creation_time
);
4855 * 'copy_printer_attrs()' - Copy printer attributes.
4860 cupsd_client_t
*con
, /* I - Client connection */
4861 cupsd_printer_t
*printer
, /* I - Printer */
4862 cups_array_t
*ra
) /* I - Requested attributes array */
4864 char uri
[HTTP_MAX_URI
]; /* URI value */
4865 time_t curtime
; /* Current time */
4866 int i
; /* Looping var */
4867 int is_encrypted
= httpIsEncrypted(con
->http
);
4868 /* Is the connection encrypted? */
4872 * Copy the printer attributes to the response using requested-attributes
4873 * and document-format attributes that may be provided by the client.
4876 _cupsRWLockRead(&printer
->lock
);
4878 curtime
= time(NULL
);
4880 if (!ra
|| cupsArrayFind(ra
, "marker-change-time"))
4881 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "marker-change-time", printer
->marker_time
);
4883 if (printer
->num_printers
> 0 && (!ra
|| cupsArrayFind(ra
, "member-uris")))
4885 ipp_attribute_t
*member_uris
; /* member-uris attribute */
4886 cupsd_printer_t
*p2
; /* Printer in class */
4887 ipp_attribute_t
*p2_uri
; /* printer-uri-supported for class printer */
4890 if ((member_uris
= ippAddStrings(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "member-uris", printer
->num_printers
, NULL
, NULL
)) != NULL
)
4892 for (i
= 0; i
< printer
->num_printers
; i
++)
4894 p2
= printer
->printers
[i
];
4896 if ((p2_uri
= ippFindAttribute(p2
->attrs
, "printer-uri-supported", IPP_TAG_URI
)) != NULL
)
4898 member_uris
->values
[i
].string
.text
= _cupsStrRetain(p2_uri
->values
[0].string
.text
);
4902 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), is_encrypted
? "ipps" : "ipp", NULL
, con
->clientname
, con
->clientport
, (p2
->type
& CUPS_PRINTER_CLASS
) ? "/classes/%s" : "/printers/%s", p2
->name
);
4903 member_uris
->values
[i
].string
.text
= _cupsStrAlloc(uri
);
4909 if (printer
->alert
&& (!ra
|| cupsArrayFind(ra
, "printer-alert")))
4910 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_STRING
, "printer-alert", NULL
, printer
->alert
);
4912 if (printer
->alert_description
&& (!ra
|| cupsArrayFind(ra
, "printer-alert-description")))
4913 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-alert-description", NULL
, printer
->alert_description
);
4915 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
4916 ippAddDate(con
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
4918 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
4919 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", printer
->config_time
);
4921 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
4922 ippAddDate(con
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(curtime
));
4924 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
4925 if (!ra
|| cupsArrayFind(ra
, "printer-dns-sd-name"))
4927 if (printer
->reg_name
)
4928 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-dns-sd-name", NULL
, printer
->reg_name
);
4930 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "printer-dns-sd-name", 0);
4932 #endif /* HAVE_DNSSD || HAVE_AVAHI */
4934 if (!ra
|| cupsArrayFind(ra
, "printer-error-policy"))
4935 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-error-policy", NULL
, printer
->error_policy
);
4937 if (!ra
|| cupsArrayFind(ra
, "printer-error-policy-supported"))
4939 static const char * const errors
[] =/* printer-error-policy-supported values */
4942 "retry-current-job",
4947 if (printer
->type
& CUPS_PRINTER_CLASS
)
4948 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_NAME
| IPP_TAG_COPY
, "printer-error-policy-supported", NULL
, "retry-current-job");
4950 ippAddStrings(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_NAME
| IPP_TAG_COPY
, "printer-error-policy-supported", sizeof(errors
) / sizeof(errors
[0]), NULL
, errors
);
4953 if (!ra
|| cupsArrayFind(ra
, "printer-icons"))
4955 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), is_encrypted
? "https" : "http", NULL
, con
->clientname
, con
->clientport
, "/icons/%s.png", printer
->name
);
4956 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-icons", NULL
, uri
);
4957 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "printer-icons=\"%s\"", uri
);
4960 if (!ra
|| cupsArrayFind(ra
, "printer-is-accepting-jobs"))
4961 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", (char)printer
->accepting
);
4963 if (!ra
|| cupsArrayFind(ra
, "printer-is-shared"))
4964 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-shared", (char)printer
->shared
);
4966 if (!ra
|| cupsArrayFind(ra
, "printer-is-temporary"))
4967 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-temporary", (char)printer
->temporary
);
4969 if (!ra
|| cupsArrayFind(ra
, "printer-more-info"))
4971 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), is_encrypted
? "https" : "http", NULL
, con
->clientname
, con
->clientport
, (printer
->type
& CUPS_PRINTER_CLASS
) ? "/classes/%s" : "/printers/%s", printer
->name
);
4972 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, uri
);
4975 if (!ra
|| cupsArrayFind(ra
, "printer-op-policy"))
4976 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-op-policy", NULL
, printer
->op_policy
);
4978 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
4979 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state", printer
->state
);
4981 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
4982 ippAddDate(con
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
4984 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
4985 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", printer
->state_time
);
4987 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
4988 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-state-message", NULL
, printer
->state_message
);
4990 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
4991 add_printer_state_reasons(con
, printer
);
4993 if (!ra
|| cupsArrayFind(ra
, "printer-strings-uri"))
4995 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), is_encrypted
? "https" : "http", NULL
, con
->clientname
, con
->clientport
, "/strings/%s.strings", printer
->name
);
4996 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-strings-uri", NULL
, uri
);
4997 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "printer-strings-uri=\"%s\"", uri
);
5000 if (!ra
|| cupsArrayFind(ra
, "printer-type"))
5002 cups_ptype_t type
; /* printer-type value */
5005 * Add the CUPS-specific printer-type attribute...
5008 type
= printer
->type
;
5010 if (printer
== DefaultPrinter
)
5011 type
|= CUPS_PRINTER_DEFAULT
;
5013 if (!printer
->accepting
)
5014 type
|= CUPS_PRINTER_REJECTING
;
5016 if (!printer
->shared
)
5017 type
|= CUPS_PRINTER_NOT_SHARED
;
5019 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-type", (int)type
);
5022 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
5023 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", curtime
);
5025 if (!ra
|| cupsArrayFind(ra
, "printer-uri-supported"))
5027 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), is_encrypted
? "ipps" : "ipp", NULL
, con
->clientname
, con
->clientport
, (printer
->type
& CUPS_PRINTER_CLASS
) ? "/classes/%s" : "/printers/%s", printer
->name
);
5028 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
5029 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "printer-uri-supported=\"%s\"", uri
);
5032 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
5033 add_queued_job_count(con
, printer
);
5035 copy_attrs(con
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
, 0, NULL
);
5036 if (printer
->ppd_attrs
)
5037 copy_attrs(con
->response
, printer
->ppd_attrs
, ra
, IPP_TAG_ZERO
, 0, NULL
);
5038 copy_attrs(con
->response
, CommonData
, ra
, IPP_TAG_ZERO
, IPP_TAG_COPY
, NULL
);
5040 _cupsRWUnlock(&printer
->lock
);
5045 * 'copy_subscription_attrs()' - Copy subscription attributes.
5049 copy_subscription_attrs(
5050 cupsd_client_t
*con
, /* I - Client connection */
5051 cupsd_subscription_t
*sub
, /* I - Subscription */
5052 cups_array_t
*ra
, /* I - Requested attributes array */
5053 cups_array_t
*exclude
) /* I - Private attributes array */
5055 ipp_attribute_t
*attr
; /* Current attribute */
5056 char printer_uri
[HTTP_MAX_URI
];
5058 int count
; /* Number of events */
5059 unsigned mask
; /* Current event mask */
5060 const char *name
; /* Current event name */
5063 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
5064 "copy_subscription_attrs(con=%p, sub=%p, ra=%p, exclude=%p)",
5065 con
, sub
, ra
, exclude
);
5068 * Copy the subscription attributes to the response using the
5069 * requested-attributes attribute that may be provided by the client.
5072 if (!exclude
|| !cupsArrayFind(exclude
, "all"))
5074 if ((!exclude
|| !cupsArrayFind(exclude
, "notify-events")) &&
5075 (!ra
|| cupsArrayFind(ra
, "notify-events")))
5077 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "copy_subscription_attrs: notify-events");
5079 if ((name
= cupsdEventName((cupsd_eventmask_t
)sub
->mask
)) != NULL
)
5082 * Simple event list...
5085 ippAddString(con
->response
, IPP_TAG_SUBSCRIPTION
,
5086 (ipp_tag_t
)(IPP_TAG_KEYWORD
| IPP_TAG_COPY
),
5087 "notify-events", NULL
, name
);
5092 * Complex event list...
5095 for (mask
= 1, count
= 0; mask
< CUPSD_EVENT_ALL
; mask
<<= 1)
5096 if (sub
->mask
& mask
)
5099 attr
= ippAddStrings(con
->response
, IPP_TAG_SUBSCRIPTION
,
5100 (ipp_tag_t
)(IPP_TAG_KEYWORD
| IPP_TAG_COPY
),
5101 "notify-events", count
, NULL
, NULL
);
5103 for (mask
= 1, count
= 0; mask
< CUPSD_EVENT_ALL
; mask
<<= 1)
5104 if (sub
->mask
& mask
)
5106 attr
->values
[count
].string
.text
=
5107 (char *)cupsdEventName((cupsd_eventmask_t
)mask
);
5114 if ((!exclude
|| !cupsArrayFind(exclude
, "notify-lease-duration")) &&
5115 (!sub
->job
&& (!ra
|| cupsArrayFind(ra
, "notify-lease-duration"))))
5116 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
5117 "notify-lease-duration", sub
->lease
);
5119 if ((!exclude
|| !cupsArrayFind(exclude
, "notify-recipient-uri")) &&
5120 (sub
->recipient
&& (!ra
|| cupsArrayFind(ra
, "notify-recipient-uri"))))
5121 ippAddString(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_URI
,
5122 "notify-recipient-uri", NULL
, sub
->recipient
);
5123 else if ((!exclude
|| !cupsArrayFind(exclude
, "notify-pull-method")) &&
5124 (!ra
|| cupsArrayFind(ra
, "notify-pull-method")))
5125 ippAddString(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_KEYWORD
,
5126 "notify-pull-method", NULL
, "ippget");
5128 if ((!exclude
|| !cupsArrayFind(exclude
, "notify-subscriber-user-name")) &&
5129 (!ra
|| cupsArrayFind(ra
, "notify-subscriber-user-name")))
5130 ippAddString(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_NAME
,
5131 "notify-subscriber-user-name", NULL
, sub
->owner
);
5133 if ((!exclude
|| !cupsArrayFind(exclude
, "notify-time-interval")) &&
5134 (!ra
|| cupsArrayFind(ra
, "notify-time-interval")))
5135 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
5136 "notify-time-interval", sub
->interval
);
5138 if (sub
->user_data_len
> 0 &&
5139 (!exclude
|| !cupsArrayFind(exclude
, "notify-user-data")) &&
5140 (!ra
|| cupsArrayFind(ra
, "notify-user-data")))
5141 ippAddOctetString(con
->response
, IPP_TAG_SUBSCRIPTION
, "notify-user-data",
5142 sub
->user_data
, sub
->user_data_len
);
5145 if (sub
->job
&& (!ra
|| cupsArrayFind(ra
, "notify-job-id")))
5146 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
5147 "notify-job-id", sub
->job
->id
);
5149 if (sub
->dest
&& (!ra
|| cupsArrayFind(ra
, "notify-printer-uri")))
5151 httpAssembleURIf(HTTP_URI_CODING_ALL
, printer_uri
, sizeof(printer_uri
),
5152 "ipp", NULL
, con
->clientname
, con
->clientport
,
5153 "/printers/%s", sub
->dest
->name
);
5154 ippAddString(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_URI
,
5155 "notify-printer-uri", NULL
, printer_uri
);
5158 if (!ra
|| cupsArrayFind(ra
, "notify-subscription-id"))
5159 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
5160 "notify-subscription-id", sub
->id
);
5165 * 'create_job()' - Print a file to a printer or class.
5169 create_job(cupsd_client_t
*con
, /* I - Client connection */
5170 ipp_attribute_t
*uri
) /* I - Printer URI */
5172 int i
; /* Looping var */
5173 cupsd_printer_t
*printer
; /* Printer */
5174 cupsd_job_t
*job
; /* New job */
5175 static const char * const forbidden_attrs
[] =
5176 { /* List of forbidden attributes */
5180 "document-natural-language"
5184 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "create_job(%p[%d], %s)", con
,
5185 con
->number
, uri
->values
[0].string
.text
);
5188 * Is the destination valid?
5191 if (!cupsdValidateDest(uri
->values
[0].string
.text
, NULL
, &printer
))
5197 send_ipp_status(con
, IPP_NOT_FOUND
,
5198 _("The printer or class does not exist."));
5203 * Check for invalid Create-Job attributes and log a warning or error depending
5204 * on whether cupsd is running in "strict conformance" mode...
5208 i
< (int)(sizeof(forbidden_attrs
) / sizeof(forbidden_attrs
[0]));
5210 if (ippFindAttribute(con
->request
, forbidden_attrs
[i
], IPP_TAG_ZERO
))
5212 if (StrictConformance
)
5214 send_ipp_status(con
, IPP_BAD_REQUEST
,
5215 _("The '%s' operation attribute cannot be supplied in a "
5216 "Create-Job request."), forbidden_attrs
[i
]);
5220 cupsdLogMessage(CUPSD_LOG_WARN
,
5221 "Unexpected '%s' operation attribute in a Create-Job "
5222 "request.", forbidden_attrs
[i
]);
5226 * Create the job object...
5229 if ((job
= add_job(con
, printer
, NULL
)) == NULL
)
5232 job
->pending_timeout
= 1;
5235 * Save and log the job...
5238 cupsdLogJob(job
, CUPSD_LOG_INFO
, "Queued on \"%s\" by \"%s\".",
5239 job
->dest
, job
->username
);
5244 * 'create_local_bg_thread()' - Background thread for creating a local print queue.
5247 static void * /* O - Exit status */
5248 create_local_bg_thread(
5249 cupsd_printer_t
*printer
) /* I - Printer */
5251 cups_file_t
*from
, /* Source file */
5252 *to
; /* Destination file */
5253 char fromppd
[1024], /* Source PPD */
5254 toppd
[1024], /* Destination PPD */
5255 scheme
[32], /* URI scheme */
5256 userpass
[256], /* User:pass */
5257 host
[256], /* Hostname */
5258 resource
[1024], /* Resource path */
5259 line
[1024]; /* Line from PPD */
5260 int port
; /* Port number */
5261 http_encryption_t encryption
; /* Type of encryption to use */
5262 http_t
*http
; /* Connection to printer */
5263 ipp_t
*request
, /* Request to printer */
5264 *response
; /* Response from printer */
5265 ipp_attribute_t
*attr
; /* Attribute in response */
5266 ipp_status_t status
; /* Status code */
5270 * Try connecting to the printer...
5273 cupsdLogMessage(CUPSD_LOG_DEBUG
, "%s: Generating PPD file from \"%s\"...", printer
->name
, printer
->device_uri
);
5275 if (httpSeparateURI(HTTP_URI_CODING_ALL
, printer
->device_uri
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), host
, sizeof(host
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5277 cupsdLogMessage(CUPSD_LOG_ERROR
, "%s: Bad device URI \"%s\".", printer
->name
, printer
->device_uri
);
5281 if (!strcmp(scheme
, "ipps") || port
== 443)
5282 encryption
= HTTP_ENCRYPTION_ALWAYS
;
5284 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
5286 if ((http
= httpConnect2(host
, port
, NULL
, AF_UNSPEC
, encryption
, 1, 30000, NULL
)) == NULL
)
5288 cupsdLogMessage(CUPSD_LOG_ERROR
, "%s: Unable to connect to %s:%d: %s", printer
->name
, host
, port
, cupsLastErrorString());
5293 * Query the printer for its capabilities...
5296 cupsdLogMessage(CUPSD_LOG_DEBUG
, "%s: Connected to %s:%d, sending Get-Printer-Attributes request...", printer
->name
, host
, port
);
5298 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
5299 ippSetVersion(request
, 2, 0);
5300 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, printer
->device_uri
);
5301 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
, "requested-attributes", NULL
, "all");
5303 response
= cupsDoRequest(http
, request
, resource
);
5304 status
= cupsLastError();
5306 cupsdLogMessage(CUPSD_LOG_DEBUG
, "%s: Get-Printer-Attributes returned %s (%s)", printer
->name
, ippErrorString(cupsLastError()), cupsLastErrorString());
5308 if (status
== IPP_STATUS_ERROR_BAD_REQUEST
|| status
== IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
)
5311 * Try request using IPP/1.1, in case we are talking to an old CUPS server or
5315 ippDelete(response
);
5317 cupsdLogMessage(CUPSD_LOG_DEBUG
, "%s: Re-sending Get-Printer-Attributes request using IPP/1.1...", printer
->name
);
5319 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
5320 ippSetVersion(request
, 1, 1);
5321 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, printer
->device_uri
);
5322 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
, "requested-attributes", NULL
, "all");
5324 response
= cupsDoRequest(http
, request
, resource
);
5326 cupsdLogMessage(CUPSD_LOG_DEBUG
, "%s: IPP/1.1 Get-Printer-Attributes returned %s (%s)", printer
->name
, ippErrorString(cupsLastError()), cupsLastErrorString());
5329 // TODO: Grab printer icon file...
5333 * Write the PPD for the queue...
5336 if (_ppdCreateFromIPP(fromppd
, sizeof(fromppd
), response
))
5338 _cupsRWLockWrite(&printer
->lock
);
5340 if ((!printer
->info
|| !*(printer
->info
)) && (attr
= ippFindAttribute(response
, "printer-info", IPP_TAG_TEXT
)) != NULL
)
5341 cupsdSetString(&printer
->info
, ippGetString(attr
, 0, NULL
));
5343 if ((!printer
->location
|| !*(printer
->location
)) && (attr
= ippFindAttribute(response
, "printer-location", IPP_TAG_TEXT
)) != NULL
)
5344 cupsdSetString(&printer
->location
, ippGetString(attr
, 0, NULL
));
5346 if ((!printer
->geo_location
|| !*(printer
->geo_location
)) && (attr
= ippFindAttribute(response
, "printer-geo-location", IPP_TAG_URI
)) != NULL
)
5347 cupsdSetString(&printer
->geo_location
, ippGetString(attr
, 0, NULL
));
5349 _cupsRWUnlock(&printer
->lock
);
5351 if ((from
= cupsFileOpen(fromppd
, "r")) == NULL
)
5353 cupsdLogMessage(CUPSD_LOG_ERROR
, "%s: Unable to read generated PPD: %s", printer
->name
, strerror(errno
));
5357 snprintf(toppd
, sizeof(toppd
), "%s/ppd/%s.ppd", ServerRoot
, printer
->name
);
5358 if ((to
= cupsdCreateConfFile(toppd
, ConfigFilePerm
)) == NULL
)
5360 cupsdLogMessage(CUPSD_LOG_ERROR
, "%s: Unable to create PPD for printer: %s", printer
->name
, strerror(errno
));
5361 cupsFileClose(from
);
5365 while (cupsFileGets(from
, line
, sizeof(line
)))
5366 cupsFilePrintf(to
, "%s\n", line
);
5368 cupsFileClose(from
);
5369 if (!cupsdCloseCreatedConfFile(to
, toppd
))
5371 printer
->config_time
= time(NULL
);
5372 printer
->state
= IPP_PSTATE_IDLE
;
5373 printer
->accepting
= 1;
5375 cupsdSetPrinterAttrs(printer
);
5377 cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG
, printer
, NULL
, "Printer \"%s\" is now available.", printer
->name
);
5378 cupsdLogMessage(CUPSD_LOG_INFO
, "Printer \"%s\" is now available.", printer
->name
);
5382 cupsdLogMessage(CUPSD_LOG_ERROR
, "%s: PPD creation failed: %s", printer
->name
, cupsLastErrorString());
5389 * 'create_local_printer()' - Create a local (temporary) print queue.
5393 create_local_printer(
5394 cupsd_client_t
*con
) /* I - Client connection */
5396 ipp_attribute_t
*device_uri
, /* device-uri attribute */
5397 *printer_geo_location
, /* printer-geo-location attribute */
5398 *printer_info
, /* printer-info attribute */
5399 *printer_location
, /* printer-location attribute */
5400 *printer_name
; /* printer-name attribute */
5401 cupsd_printer_t
*printer
; /* New printer */
5402 http_status_t status
; /* Policy status */
5403 char name
[128], /* Sanitized printer name */
5404 *nameptr
, /* Pointer into name */
5405 uri
[1024]; /* printer-uri-supported value */
5406 const char *ptr
; /* Pointer into attribute value */
5410 * Require local access to create a local printer...
5413 if (!httpAddrLocalhost(httpGetAddress(con
->http
)))
5415 send_ipp_status(con
, IPP_STATUS_ERROR_FORBIDDEN
, _("Only local users can create a local printer."));
5420 * Check any other policy limits...
5423 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
5425 send_http_error(con
, status
, NULL
);
5430 * Grab needed attributes...
5433 if ((printer_name
= ippFindAttribute(con
->request
, "printer-name", IPP_TAG_ZERO
)) == NULL
|| ippGetGroupTag(printer_name
) != IPP_TAG_PRINTER
|| ippGetValueTag(printer_name
) != IPP_TAG_NAME
)
5436 send_ipp_status(con
, IPP_STATUS_ERROR_BAD_REQUEST
, _("Missing required attribute \"%s\"."), "printer-name");
5437 else if (ippGetGroupTag(printer_name
) != IPP_TAG_PRINTER
)
5438 send_ipp_status(con
, IPP_STATUS_ERROR_BAD_REQUEST
, _("Attribute \"%s\" is in the wrong group."), "printer-name");
5440 send_ipp_status(con
, IPP_STATUS_ERROR_BAD_REQUEST
, _("Attribute \"%s\" is the wrong value type."), "printer-name");
5445 for (nameptr
= name
, ptr
= ippGetString(printer_name
, 0, NULL
); *ptr
&& nameptr
< (name
+ sizeof(name
) - 1); ptr
++)
5448 * Sanitize the printer name...
5451 if (_cups_isalnum(*ptr
))
5453 else if (nameptr
== name
|| nameptr
[-1] != '_')
5459 if ((device_uri
= ippFindAttribute(con
->request
, "device-uri", IPP_TAG_ZERO
)) == NULL
|| ippGetGroupTag(device_uri
) != IPP_TAG_PRINTER
|| ippGetValueTag(device_uri
) != IPP_TAG_URI
)
5462 send_ipp_status(con
, IPP_STATUS_ERROR_BAD_REQUEST
, _("Missing required attribute \"%s\"."), "device-uri");
5463 else if (ippGetGroupTag(device_uri
) != IPP_TAG_PRINTER
)
5464 send_ipp_status(con
, IPP_STATUS_ERROR_BAD_REQUEST
, _("Attribute \"%s\" is in the wrong group."), "device-uri");
5466 send_ipp_status(con
, IPP_STATUS_ERROR_BAD_REQUEST
, _("Attribute \"%s\" is the wrong value type."), "device-uri");
5471 printer_geo_location
= ippFindAttribute(con
->request
, "printer-geo-location", IPP_TAG_URI
);
5472 printer_info
= ippFindAttribute(con
->request
, "printer-info", IPP_TAG_TEXT
);
5473 printer_location
= ippFindAttribute(con
->request
, "printer-location", IPP_TAG_TEXT
);
5476 * See if the printer already exists...
5479 if ((printer
= cupsdFindDest(name
)) != NULL
)
5481 send_ipp_status(con
, IPP_STATUS_ERROR_NOT_POSSIBLE
, _("Printer \"%s\" already exists."), name
);
5482 goto add_printer_attributes
;
5486 * Create the printer...
5489 if ((printer
= cupsdAddPrinter(name
)) == NULL
)
5491 send_ipp_status(con
, IPP_STATUS_ERROR_INTERNAL
, _("Unable to create printer."));
5495 printer
->shared
= 0;
5496 printer
->temporary
= 1;
5498 cupsdSetDeviceURI(printer
, ippGetString(device_uri
, 0, NULL
));
5500 if (printer_geo_location
)
5501 cupsdSetString(&printer
->geo_location
, ippGetString(printer_geo_location
, 0, NULL
));
5503 cupsdSetString(&printer
->info
, ippGetString(printer_info
, 0, NULL
));
5504 if (printer_location
)
5505 cupsdSetString(&printer
->location
, ippGetString(printer_location
, 0, NULL
));
5507 cupsdSetPrinterAttrs(printer
);
5510 * Run a background thread to create the PPD...
5513 _cupsThreadCreate((_cups_thread_func_t
)create_local_bg_thread
, printer
);
5516 * Return printer attributes...
5519 send_ipp_status(con
, IPP_STATUS_OK
, _("Local printer created."));
5521 add_printer_attributes
:
5523 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", (char)printer
->accepting
);
5524 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state", printer
->state
);
5525 add_printer_state_reasons(con
, printer
);
5527 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), httpIsEncrypted(con
->http
) ? "ipps" : "ipp", NULL
, con
->clientname
, con
->clientport
, "/printers/%s", printer
->name
);
5528 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
5533 * 'create_requested_array()' - Create an array for the requested-attributes.
5536 static cups_array_t
* /* O - Array of attributes or NULL */
5537 create_requested_array(ipp_t
*request
) /* I - IPP request */
5539 cups_array_t
*ra
; /* Requested attributes array */
5543 * Create the array for standard attributes...
5546 ra
= ippCreateRequestedArray(request
);
5549 * Add CUPS defaults as needed...
5552 if (cupsArrayFind(ra
, "printer-defaults"))
5555 * Include user-set defaults...
5558 char *name
; /* Option name */
5560 cupsArrayRemove(ra
, "printer-defaults");
5562 for (name
= (char *)cupsArrayFirst(CommonDefaults
);
5564 name
= (char *)cupsArrayNext(CommonDefaults
))
5565 if (!cupsArrayFind(ra
, name
))
5566 cupsArrayAdd(ra
, name
);
5574 * 'create_subscriptions()' - Create one or more notification subscriptions.
5578 create_subscriptions(
5579 cupsd_client_t
*con
, /* I - Client connection */
5580 ipp_attribute_t
*uri
) /* I - Printer URI */
5582 http_status_t status
; /* Policy status */
5583 int i
; /* Looping var */
5584 ipp_attribute_t
*attr
; /* Current attribute */
5585 cups_ptype_t dtype
; /* Destination type (printer/class) */
5586 char scheme
[HTTP_MAX_URI
],
5587 /* Scheme portion of URI */
5588 userpass
[HTTP_MAX_URI
],
5589 /* Username portion of URI */
5591 /* Host portion of URI */
5592 resource
[HTTP_MAX_URI
];
5593 /* Resource portion of URI */
5594 int port
; /* Port portion of URI */
5595 cupsd_printer_t
*printer
; /* Printer/class */
5596 cupsd_job_t
*job
; /* Job */
5597 int jobid
; /* Job ID */
5598 cupsd_subscription_t
*sub
; /* Subscription object */
5599 const char *username
, /* requesting-user-name or
5600 authenticated username */
5601 *recipient
, /* notify-recipient-uri */
5602 *pullmethod
; /* notify-pull-method */
5603 ipp_attribute_t
*user_data
; /* notify-user-data */
5604 int interval
, /* notify-time-interval */
5605 lease
; /* notify-lease-duration */
5606 unsigned mask
; /* notify-events */
5607 ipp_attribute_t
*notify_events
,/* notify-events(-default) */
5608 *notify_lease
; /* notify-lease-duration(-default) */
5612 for (attr
= con
->request
->attrs
; attr
; attr
= attr
->next
)
5614 if (attr
->group_tag
!= IPP_TAG_ZERO
)
5615 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "g%04x v%04x %s", attr
->group_tag
,
5616 attr
->value_tag
, attr
->name
);
5618 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "----SEP----");
5623 * Is the destination valid?
5626 cupsdLogMessage(CUPSD_LOG_DEBUG
, "create_subscriptions(con=%p(%d), uri=\"%s\")", con
, con
->number
, uri
->values
[0].string
.text
);
5628 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
5629 sizeof(scheme
), userpass
, sizeof(userpass
), host
,
5630 sizeof(host
), &port
, resource
, sizeof(resource
));
5632 if (!strcmp(resource
, "/"))
5634 dtype
= (cups_ptype_t
)0;
5637 else if (!strncmp(resource
, "/printers", 9) && strlen(resource
) <= 10)
5639 dtype
= (cups_ptype_t
)0;
5642 else if (!strncmp(resource
, "/classes", 8) && strlen(resource
) <= 9)
5644 dtype
= CUPS_PRINTER_CLASS
;
5647 else if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
5653 send_ipp_status(con
, IPP_NOT_FOUND
,
5654 _("The printer or class does not exist."));
5664 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
,
5667 send_http_error(con
, status
, printer
);
5671 else if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
5673 send_http_error(con
, status
, NULL
);
5678 * Get the user that is requesting the subscription...
5681 username
= get_username(con
);
5684 * Find the first subscription group attribute; return if we have
5688 for (attr
= con
->request
->attrs
; attr
; attr
= attr
->next
)
5689 if (attr
->group_tag
== IPP_TAG_SUBSCRIPTION
)
5694 send_ipp_status(con
, IPP_BAD_REQUEST
,
5695 _("No subscription attributes in request."));
5700 * Process the subscription attributes in the request...
5703 con
->response
->request
.status
.status_code
= IPP_BAD_REQUEST
;
5711 lease
= DefaultLeaseDuration
;
5713 mask
= CUPSD_EVENT_NONE
;
5717 notify_events
= ippFindAttribute(printer
->attrs
, "notify-events-default",
5719 notify_lease
= ippFindAttribute(printer
->attrs
,
5720 "notify-lease-duration-default",
5724 lease
= notify_lease
->values
[0].integer
;
5728 notify_events
= NULL
;
5729 notify_lease
= NULL
;
5732 while (attr
&& attr
->group_tag
!= IPP_TAG_ZERO
)
5734 if (!strcmp(attr
->name
, "notify-recipient-uri") &&
5735 attr
->value_tag
== IPP_TAG_URI
)
5738 * Validate the recipient scheme against the ServerBin/notifier
5742 char notifier
[1024]; /* Notifier filename */
5745 recipient
= attr
->values
[0].string
.text
;
5747 if (httpSeparateURI(HTTP_URI_CODING_ALL
, recipient
,
5748 scheme
, sizeof(scheme
), userpass
, sizeof(userpass
),
5749 host
, sizeof(host
), &port
,
5750 resource
, sizeof(resource
)) < HTTP_URI_OK
)
5752 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
5753 _("Bad notify-recipient-uri \"%s\"."), recipient
);
5754 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
,
5755 "notify-status-code", IPP_URI_SCHEME
);
5759 snprintf(notifier
, sizeof(notifier
), "%s/notifier/%s", ServerBin
,
5761 if (access(notifier
, X_OK
))
5763 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
5764 _("notify-recipient-uri URI \"%s\" uses unknown "
5765 "scheme."), recipient
);
5766 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
,
5767 "notify-status-code", IPP_URI_SCHEME
);
5771 if (!strcmp(scheme
, "rss") && !check_rss_recipient(recipient
))
5773 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
5774 _("notify-recipient-uri URI \"%s\" is already used."),
5776 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
,
5777 "notify-status-code", IPP_ATTRIBUTES
);
5781 else if (!strcmp(attr
->name
, "notify-pull-method") &&
5782 attr
->value_tag
== IPP_TAG_KEYWORD
)
5784 pullmethod
= attr
->values
[0].string
.text
;
5786 if (strcmp(pullmethod
, "ippget"))
5788 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
5789 _("Bad notify-pull-method \"%s\"."), pullmethod
);
5790 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
,
5791 "notify-status-code", IPP_ATTRIBUTES
);
5795 else if (!strcmp(attr
->name
, "notify-charset") &&
5796 attr
->value_tag
== IPP_TAG_CHARSET
&&
5797 strcmp(attr
->values
[0].string
.text
, "us-ascii") &&
5798 strcmp(attr
->values
[0].string
.text
, "utf-8"))
5800 send_ipp_status(con
, IPP_CHARSET
,
5801 _("Character set \"%s\" not supported."),
5802 attr
->values
[0].string
.text
);
5805 else if (!strcmp(attr
->name
, "notify-natural-language") &&
5806 (attr
->value_tag
!= IPP_TAG_LANGUAGE
||
5807 strcmp(attr
->values
[0].string
.text
, DefaultLanguage
)))
5809 send_ipp_status(con
, IPP_CHARSET
,
5810 _("Language \"%s\" not supported."),
5811 attr
->values
[0].string
.text
);
5814 else if (!strcmp(attr
->name
, "notify-user-data") &&
5815 attr
->value_tag
== IPP_TAG_STRING
)
5817 if (attr
->num_values
> 1 || attr
->values
[0].unknown
.length
> 63)
5819 send_ipp_status(con
, IPP_REQUEST_VALUE
,
5820 _("The notify-user-data value is too large "
5821 "(%d > 63 octets)."),
5822 attr
->values
[0].unknown
.length
);
5828 else if (!strcmp(attr
->name
, "notify-events") &&
5829 attr
->value_tag
== IPP_TAG_KEYWORD
)
5830 notify_events
= attr
;
5831 else if (!strcmp(attr
->name
, "notify-lease-duration") &&
5832 attr
->value_tag
== IPP_TAG_INTEGER
)
5833 lease
= attr
->values
[0].integer
;
5834 else if (!strcmp(attr
->name
, "notify-time-interval") &&
5835 attr
->value_tag
== IPP_TAG_INTEGER
)
5836 interval
= attr
->values
[0].integer
;
5837 else if (!strcmp(attr
->name
, "notify-job-id") &&
5838 attr
->value_tag
== IPP_TAG_INTEGER
)
5839 jobid
= attr
->values
[0].integer
;
5846 for (i
= 0; i
< notify_events
->num_values
; i
++)
5847 mask
|= cupsdEventValue(notify_events
->values
[i
].string
.text
);
5852 cupsdLogMessage(CUPSD_LOG_DEBUG
, "recipient=\"%s\"", recipient
);
5855 if (!strncmp(recipient
, "mailto:", 7) && user_data
)
5857 char temp
[64]; /* Temporary string */
5859 memcpy(temp
, user_data
->values
[0].unknown
.data
, user_data
->values
[0].unknown
.length
);
5860 temp
[user_data
->values
[0].unknown
.length
] = '\0';
5862 if (httpSeparateURI(HTTP_URI_CODING_ALL
, temp
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), host
, sizeof(host
), &port
, resource
, sizeof(resource
)) < HTTP_URI_OK
)
5864 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("Bad notify-user-data \"%s\"."), temp
);
5865 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
, "notify-status-code", IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
);
5872 cupsdLogMessage(CUPSD_LOG_DEBUG
, "pullmethod=\"%s\"", pullmethod
);
5873 cupsdLogMessage(CUPSD_LOG_DEBUG
, "notify-lease-duration=%d", lease
);
5874 cupsdLogMessage(CUPSD_LOG_DEBUG
, "notify-time-interval=%d", interval
);
5876 if (!recipient
&& !pullmethod
)
5879 if (mask
== CUPSD_EVENT_NONE
)
5882 mask
= CUPSD_EVENT_JOB_COMPLETED
;
5884 mask
= CUPSD_EVENT_PRINTER_STATE_CHANGED
;
5887 send_ipp_status(con
, IPP_BAD_REQUEST
,
5888 _("notify-events not specified."));
5893 if (MaxLeaseDuration
&& (lease
== 0 || lease
> MaxLeaseDuration
))
5895 cupsdLogMessage(CUPSD_LOG_INFO
,
5896 "create_subscriptions: Limiting notify-lease-duration to "
5899 lease
= MaxLeaseDuration
;
5904 if ((job
= cupsdFindJob(jobid
)) == NULL
)
5906 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."),
5914 if ((sub
= cupsdAddSubscription(mask
, printer
, job
, recipient
, 0)) == NULL
)
5916 send_ipp_status(con
, IPP_TOO_MANY_SUBSCRIPTIONS
,
5917 _("There are too many subscriptions."));
5922 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Added subscription #%d for job %d.",
5925 cupsdLogMessage(CUPSD_LOG_DEBUG
,
5926 "Added subscription #%d for printer \"%s\".",
5927 sub
->id
, printer
->name
);
5929 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Added subscription #%d for server.",
5932 sub
->interval
= interval
;
5934 sub
->expire
= lease
? time(NULL
) + lease
: 0;
5936 cupsdSetString(&sub
->owner
, username
);
5940 sub
->user_data_len
= user_data
->values
[0].unknown
.length
;
5941 memcpy(sub
->user_data
, user_data
->values
[0].unknown
.data
,
5942 (size_t)sub
->user_data_len
);
5945 ippAddSeparator(con
->response
);
5946 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
5947 "notify-subscription-id", sub
->id
);
5949 con
->response
->request
.status
.status_code
= IPP_OK
;
5955 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS
);
5960 * 'delete_printer()' - Remove a printer or class from the system.
5964 delete_printer(cupsd_client_t
*con
, /* I - Client connection */
5965 ipp_attribute_t
*uri
) /* I - URI of printer or class */
5967 http_status_t status
; /* Policy status */
5968 cups_ptype_t dtype
; /* Destination type (printer/class) */
5969 cupsd_printer_t
*printer
; /* Printer/class */
5970 char filename
[1024]; /* Script/PPD filename */
5971 int temporary
; /* Temporary queue? */
5974 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "delete_printer(%p[%d], %s)", con
,
5975 con
->number
, uri
->values
[0].string
.text
);
5978 * Do we have a valid URI?
5981 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
5987 send_ipp_status(con
, IPP_NOT_FOUND
,
5988 _("The printer or class does not exist."));
5996 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
5998 send_http_error(con
, status
, NULL
);
6003 * Remove old jobs...
6006 cupsdCancelJobs(printer
->name
, NULL
, 1);
6009 * Remove old subscriptions and send a "deleted printer" event...
6012 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED
, printer
, NULL
,
6013 "%s \"%s\" deleted by \"%s\".",
6014 (dtype
& CUPS_PRINTER_CLASS
) ? "Class" : "Printer",
6015 printer
->name
, get_username(con
));
6017 cupsdExpireSubscriptions(printer
, NULL
);
6020 * Remove any old PPD or script files...
6023 snprintf(filename
, sizeof(filename
), "%s/ppd/%s.ppd", ServerRoot
,
6026 snprintf(filename
, sizeof(filename
), "%s/ppd/%s.ppd.O", ServerRoot
,
6030 snprintf(filename
, sizeof(filename
), "%s/%s.png", CacheDir
, printer
->name
);
6033 snprintf(filename
, sizeof(filename
), "%s/%s.data", CacheDir
, printer
->name
);
6037 * Unregister color profiles...
6040 cupsdUnregisterColor(printer
);
6042 temporary
= printer
->temporary
;
6044 if (dtype
& CUPS_PRINTER_CLASS
)
6046 cupsdLogMessage(CUPSD_LOG_INFO
, "Class \"%s\" deleted by \"%s\".",
6047 printer
->name
, get_username(con
));
6049 cupsdDeletePrinter(printer
, 0);
6051 cupsdMarkDirty(CUPSD_DIRTY_CLASSES
);
6055 cupsdLogMessage(CUPSD_LOG_INFO
, "Printer \"%s\" deleted by \"%s\".",
6056 printer
->name
, get_username(con
));
6058 if (cupsdDeletePrinter(printer
, 0) && !temporary
)
6059 cupsdMarkDirty(CUPSD_DIRTY_CLASSES
);
6062 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS
);
6066 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP
);
6069 * Return with no errors...
6072 con
->response
->request
.status
.status_code
= IPP_OK
;
6077 * 'get_default()' - Get the default destination.
6081 get_default(cupsd_client_t
*con
) /* I - Client connection */
6083 http_status_t status
; /* Policy status */
6084 cups_array_t
*ra
; /* Requested attributes array */
6087 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_default(%p[%d])", con
, con
->number
);
6093 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
6095 send_http_error(con
, status
, NULL
);
6101 ra
= create_requested_array(con
->request
);
6103 copy_printer_attrs(con
, DefaultPrinter
, ra
);
6105 cupsArrayDelete(ra
);
6107 con
->response
->request
.status
.status_code
= IPP_OK
;
6110 send_ipp_status(con
, IPP_NOT_FOUND
, _("No default printer."));
6115 * 'get_devices()' - Get the list of available devices on the local system.
6119 get_devices(cupsd_client_t
*con
) /* I - Client connection */
6121 http_status_t status
; /* Policy status */
6122 ipp_attribute_t
*limit
, /* limit attribute */
6123 *timeout
, /* timeout attribute */
6124 *requested
, /* requested-attributes attribute */
6125 *exclude
, /* exclude-schemes attribute */
6126 *include
; /* include-schemes attribute */
6127 char command
[1024], /* cups-deviced command */
6128 options
[2048], /* Options to pass to command */
6130 /* String for requested attributes */
6132 /* String for excluded schemes */
6134 /* String for included schemes */
6137 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_devices(%p[%d])", con
, con
->number
);
6143 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
6145 send_http_error(con
, status
, NULL
);
6150 * Run cups-deviced command with the given options...
6153 limit
= ippFindAttribute(con
->request
, "limit", IPP_TAG_INTEGER
);
6154 timeout
= ippFindAttribute(con
->request
, "timeout", IPP_TAG_INTEGER
);
6155 requested
= ippFindAttribute(con
->request
, "requested-attributes",
6157 exclude
= ippFindAttribute(con
->request
, "exclude-schemes", IPP_TAG_NAME
);
6158 include
= ippFindAttribute(con
->request
, "include-schemes", IPP_TAG_NAME
);
6161 url_encode_attr(requested
, requested_str
, sizeof(requested_str
));
6163 strlcpy(requested_str
, "requested-attributes=all", sizeof(requested_str
));
6166 url_encode_attr(exclude
, exclude_str
, sizeof(exclude_str
));
6168 exclude_str
[0] = '\0';
6171 url_encode_attr(include
, include_str
, sizeof(include_str
));
6173 include_str
[0] = '\0';
6175 snprintf(command
, sizeof(command
), "%s/daemon/cups-deviced", ServerBin
);
6176 snprintf(options
, sizeof(options
),
6177 "%d+%d+%d+%d+%s%s%s%s%s",
6178 con
->request
->request
.op
.request_id
,
6179 limit
? limit
->values
[0].integer
: 0,
6180 timeout
? timeout
->values
[0].integer
: 15,
6183 exclude_str
[0] ? "%20" : "", exclude_str
,
6184 include_str
[0] ? "%20" : "", include_str
);
6186 if (cupsdSendCommand(con
, command
, options
, 1))
6189 * Command started successfully, don't send an IPP response here...
6192 ippDelete(con
->response
);
6193 con
->response
= NULL
;
6198 * Command failed, return "internal error" so the user knows something
6202 send_ipp_status(con
, IPP_INTERNAL_ERROR
,
6203 _("cups-deviced failed to execute."));
6209 * 'get_document()' - Get a copy of a job file.
6213 get_document(cupsd_client_t
*con
, /* I - Client connection */
6214 ipp_attribute_t
*uri
) /* I - Job URI */
6216 http_status_t status
; /* Policy status */
6217 ipp_attribute_t
*attr
; /* Current attribute */
6218 int jobid
; /* Job ID */
6219 int docnum
; /* Document number */
6220 cupsd_job_t
*job
; /* Current job */
6221 char scheme
[HTTP_MAX_URI
], /* Method portion of URI */
6222 username
[HTTP_MAX_URI
], /* Username portion of URI */
6223 host
[HTTP_MAX_URI
], /* Host portion of URI */
6224 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
6225 int port
; /* Port portion of URI */
6226 char filename
[1024], /* Filename for document */
6227 format
[1024]; /* Format for document */
6230 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_document(%p[%d], %s)", con
,
6231 con
->number
, uri
->values
[0].string
.text
);
6234 * See if we have a job URI or a printer URI...
6237 if (!strcmp(uri
->name
, "printer-uri"))
6240 * Got a printer URI; see if we also have a job-id attribute...
6243 if ((attr
= ippFindAttribute(con
->request
, "job-id",
6244 IPP_TAG_INTEGER
)) == NULL
)
6246 send_ipp_status(con
, IPP_BAD_REQUEST
,
6247 _("Got a printer-uri attribute but no job-id."));
6251 jobid
= attr
->values
[0].integer
;
6256 * Got a job URI; parse it to get the job ID...
6259 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
6260 sizeof(scheme
), username
, sizeof(username
), host
,
6261 sizeof(host
), &port
, resource
, sizeof(resource
));
6263 if (strncmp(resource
, "/jobs/", 6))
6269 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad job-uri \"%s\"."),
6270 uri
->values
[0].string
.text
);
6274 jobid
= atoi(resource
+ 6);
6278 * See if the job exists...
6281 if ((job
= cupsdFindJob(jobid
)) == NULL
)
6284 * Nope - return a "not found" error...
6287 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."), jobid
);
6295 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
,
6296 job
->username
)) != HTTP_OK
)
6298 send_http_error(con
, status
, NULL
);
6303 * Get the document number...
6306 if ((attr
= ippFindAttribute(con
->request
, "document-number",
6307 IPP_TAG_INTEGER
)) == NULL
)
6309 send_ipp_status(con
, IPP_BAD_REQUEST
,
6310 _("Missing document-number attribute."));
6314 if ((docnum
= attr
->values
[0].integer
) < 1 || docnum
> job
->num_files
||
6315 attr
->num_values
> 1)
6317 send_ipp_status(con
, IPP_NOT_FOUND
,
6318 _("Document #%d does not exist in job #%d."), docnum
,
6323 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, jobid
,
6325 if ((con
->file
= open(filename
, O_RDONLY
)) == -1)
6327 cupsdLogMessage(CUPSD_LOG_ERROR
,
6328 "Unable to open document %d in job %d - %s", docnum
, jobid
,
6330 send_ipp_status(con
, IPP_NOT_FOUND
,
6331 _("Unable to open document #%d in job #%d."), docnum
,
6336 fcntl(con
->file
, F_SETFD
, fcntl(con
->file
, F_GETFD
) | FD_CLOEXEC
);
6340 snprintf(format
, sizeof(format
), "%s/%s", job
->filetypes
[docnum
- 1]->super
,
6341 job
->filetypes
[docnum
- 1]->type
);
6343 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format",
6345 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "document-number",
6347 if ((attr
= ippFindAttribute(job
->attrs
, "document-name",
6348 IPP_TAG_NAME
)) != NULL
)
6349 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name",
6350 NULL
, attr
->values
[0].string
.text
);
6355 * 'get_job_attrs()' - Get job attributes.
6359 get_job_attrs(cupsd_client_t
*con
, /* I - Client connection */
6360 ipp_attribute_t
*uri
) /* I - Job URI */
6362 http_status_t status
; /* Policy status */
6363 ipp_attribute_t
*attr
; /* Current attribute */
6364 int jobid
; /* Job ID */
6365 cupsd_job_t
*job
; /* Current job */
6366 cupsd_printer_t
*printer
; /* Current printer */
6367 cupsd_policy_t
*policy
; /* Current security policy */
6368 char scheme
[HTTP_MAX_URI
], /* Scheme portion of URI */
6369 username
[HTTP_MAX_URI
], /* Username portion of URI */
6370 host
[HTTP_MAX_URI
], /* Host portion of URI */
6371 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
6372 int port
; /* Port portion of URI */
6373 cups_array_t
*ra
, /* Requested attributes array */
6374 *exclude
; /* Private attributes array */
6377 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_job_attrs(%p[%d], %s)", con
,
6378 con
->number
, uri
->values
[0].string
.text
);
6381 * See if we have a job URI or a printer URI...
6384 if (!strcmp(uri
->name
, "printer-uri"))
6387 * Got a printer URI; see if we also have a job-id attribute...
6390 if ((attr
= ippFindAttribute(con
->request
, "job-id",
6391 IPP_TAG_INTEGER
)) == NULL
)
6393 send_ipp_status(con
, IPP_BAD_REQUEST
,
6394 _("Got a printer-uri attribute but no job-id."));
6398 jobid
= attr
->values
[0].integer
;
6403 * Got a job URI; parse it to get the job ID...
6406 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
6407 sizeof(scheme
), username
, sizeof(username
), host
,
6408 sizeof(host
), &port
, resource
, sizeof(resource
));
6410 if (strncmp(resource
, "/jobs/", 6))
6416 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad job-uri \"%s\"."),
6417 uri
->values
[0].string
.text
);
6421 jobid
= atoi(resource
+ 6);
6425 * See if the job exists...
6428 if ((job
= cupsdFindJob(jobid
)) == NULL
)
6431 * Nope - return a "not found" error...
6434 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."), jobid
);
6442 if ((printer
= job
->printer
) == NULL
)
6443 printer
= cupsdFindDest(job
->dest
);
6446 policy
= printer
->op_policy_ptr
;
6448 policy
= DefaultPolicyPtr
;
6450 if ((status
= cupsdCheckPolicy(policy
, con
, job
->username
)) != HTTP_OK
)
6452 send_http_error(con
, status
, NULL
);
6456 exclude
= cupsdGetPrivateAttrs(policy
, con
, printer
, job
->username
);
6459 * Copy attributes...
6464 ra
= create_requested_array(con
->request
);
6465 copy_job_attrs(con
, job
, ra
, exclude
);
6466 cupsArrayDelete(ra
);
6468 con
->response
->request
.status
.status_code
= IPP_OK
;
6473 * 'get_jobs()' - Get a list of jobs for the specified printer.
6477 get_jobs(cupsd_client_t
*con
, /* I - Client connection */
6478 ipp_attribute_t
*uri
) /* I - Printer URI */
6480 http_status_t status
; /* Policy status */
6481 ipp_attribute_t
*attr
; /* Current attribute */
6482 const char *dest
; /* Destination */
6483 cups_ptype_t dtype
; /* Destination type (printer/class) */
6484 cups_ptype_t dmask
; /* Destination type mask */
6485 char scheme
[HTTP_MAX_URI
], /* Scheme portion of URI */
6486 username
[HTTP_MAX_URI
], /* Username portion of URI */
6487 host
[HTTP_MAX_URI
], /* Host portion of URI */
6488 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
6489 int port
; /* Port portion of URI */
6490 int job_comparison
; /* Job comparison */
6491 ipp_jstate_t job_state
; /* job-state value */
6492 int first_job_id
= 1, /* First job ID */
6493 first_index
= 1, /* First index */
6494 limit
= 0, /* Maximum number of jobs to return */
6495 count
, /* Number of jobs that match */
6496 need_load_job
= 0; /* Do we need to load the job? */
6497 const char *job_attr
; /* Job attribute requested */
6498 ipp_attribute_t
*job_ids
; /* job-ids attribute */
6499 cupsd_job_t
*job
; /* Current job pointer */
6500 cupsd_printer_t
*printer
; /* Printer */
6501 cups_array_t
*list
; /* Which job list... */
6502 int delete_list
= 0; /* Delete the list afterwards? */
6503 cups_array_t
*ra
, /* Requested attributes array */
6504 *exclude
; /* Private attributes array */
6505 cupsd_policy_t
*policy
; /* Current policy */
6508 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_jobs(%p[%d], %s)", con
, con
->number
,
6509 uri
->values
[0].string
.text
);
6512 * Is the destination valid?
6515 if (strcmp(uri
->name
, "printer-uri"))
6517 send_ipp_status(con
, IPP_BAD_REQUEST
, _("No printer-uri in request."));
6521 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
6522 sizeof(scheme
), username
, sizeof(username
), host
,
6523 sizeof(host
), &port
, resource
, sizeof(resource
));
6525 if (!strcmp(resource
, "/") || !strcmp(resource
, "/jobs"))
6528 dtype
= (cups_ptype_t
)0;
6529 dmask
= (cups_ptype_t
)0;
6532 else if (!strncmp(resource
, "/printers", 9) && strlen(resource
) <= 10)
6535 dtype
= (cups_ptype_t
)0;
6536 dmask
= CUPS_PRINTER_CLASS
;
6539 else if (!strncmp(resource
, "/classes", 8) && strlen(resource
) <= 9)
6542 dtype
= CUPS_PRINTER_CLASS
;
6543 dmask
= CUPS_PRINTER_CLASS
;
6546 else if ((dest
= cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
,
6553 send_ipp_status(con
, IPP_NOT_FOUND
,
6554 _("The printer or class does not exist."));
6559 dtype
&= CUPS_PRINTER_CLASS
;
6560 dmask
= CUPS_PRINTER_CLASS
;
6568 policy
= printer
->op_policy_ptr
;
6570 policy
= DefaultPolicyPtr
;
6572 if ((status
= cupsdCheckPolicy(policy
, con
, NULL
)) != HTTP_OK
)
6574 send_http_error(con
, status
, NULL
);
6578 job_ids
= ippFindAttribute(con
->request
, "job-ids", IPP_TAG_INTEGER
);
6581 * See if the "which-jobs" attribute have been specified...
6584 if ((attr
= ippFindAttribute(con
->request
, "which-jobs",
6585 IPP_TAG_KEYWORD
)) != NULL
&& job_ids
)
6587 send_ipp_status(con
, IPP_CONFLICT
,
6588 _("The %s attribute cannot be provided with job-ids."),
6592 else if (!attr
|| !strcmp(attr
->values
[0].string
.text
, "not-completed"))
6594 job_comparison
= -1;
6595 job_state
= IPP_JOB_STOPPED
;
6598 else if (!strcmp(attr
->values
[0].string
.text
, "completed"))
6601 job_state
= IPP_JOB_CANCELED
;
6602 list
= cupsdGetCompletedJobs(printer
);
6605 else if (!strcmp(attr
->values
[0].string
.text
, "aborted"))
6608 job_state
= IPP_JOB_ABORTED
;
6609 list
= cupsdGetCompletedJobs(printer
);
6612 else if (!strcmp(attr
->values
[0].string
.text
, "all"))
6615 job_state
= IPP_JOB_PENDING
;
6618 else if (!strcmp(attr
->values
[0].string
.text
, "canceled"))
6621 job_state
= IPP_JOB_CANCELED
;
6622 list
= cupsdGetCompletedJobs(printer
);
6625 else if (!strcmp(attr
->values
[0].string
.text
, "pending"))
6628 job_state
= IPP_JOB_PENDING
;
6631 else if (!strcmp(attr
->values
[0].string
.text
, "pending-held"))
6634 job_state
= IPP_JOB_HELD
;
6637 else if (!strcmp(attr
->values
[0].string
.text
, "processing"))
6640 job_state
= IPP_JOB_PROCESSING
;
6641 list
= PrintingJobs
;
6643 else if (!strcmp(attr
->values
[0].string
.text
, "processing-stopped"))
6646 job_state
= IPP_JOB_STOPPED
;
6651 send_ipp_status(con
, IPP_ATTRIBUTES
,
6652 _("The which-jobs value \"%s\" is not supported."),
6653 attr
->values
[0].string
.text
);
6654 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
6655 "which-jobs", NULL
, attr
->values
[0].string
.text
);
6660 * See if they want to limit the number of jobs reported...
6663 if ((attr
= ippFindAttribute(con
->request
, "limit", IPP_TAG_INTEGER
)) != NULL
)
6667 send_ipp_status(con
, IPP_CONFLICT
,
6668 _("The %s attribute cannot be provided with job-ids."),
6673 limit
= attr
->values
[0].integer
;
6676 if ((attr
= ippFindAttribute(con
->request
, "first-index", IPP_TAG_INTEGER
)) != NULL
)
6680 send_ipp_status(con
, IPP_CONFLICT
,
6681 _("The %s attribute cannot be provided with job-ids."),
6686 first_index
= attr
->values
[0].integer
;
6688 else if ((attr
= ippFindAttribute(con
->request
, "first-job-id", IPP_TAG_INTEGER
)) != NULL
)
6692 send_ipp_status(con
, IPP_CONFLICT
,
6693 _("The %s attribute cannot be provided with job-ids."),
6698 first_job_id
= attr
->values
[0].integer
;
6702 * See if we only want to see jobs for a specific user...
6705 if ((attr
= ippFindAttribute(con
->request
, "my-jobs", IPP_TAG_BOOLEAN
)) != NULL
&& job_ids
)
6707 send_ipp_status(con
, IPP_CONFLICT
,
6708 _("The %s attribute cannot be provided with job-ids."),
6712 else if (attr
&& attr
->values
[0].boolean
)
6713 strlcpy(username
, get_username(con
), sizeof(username
));
6717 ra
= create_requested_array(con
->request
);
6718 for (job_attr
= (char *)cupsArrayFirst(ra
); job_attr
; job_attr
= (char *)cupsArrayNext(ra
))
6719 if (strcmp(job_attr
, "job-id") &&
6720 strcmp(job_attr
, "job-k-octets") &&
6721 strcmp(job_attr
, "job-media-progress") &&
6722 strcmp(job_attr
, "job-more-info") &&
6723 strcmp(job_attr
, "job-name") &&
6724 strcmp(job_attr
, "job-originating-user-name") &&
6725 strcmp(job_attr
, "job-preserved") &&
6726 strcmp(job_attr
, "job-printer-up-time") &&
6727 strcmp(job_attr
, "job-printer-uri") &&
6728 strcmp(job_attr
, "job-state") &&
6729 strcmp(job_attr
, "job-state-reasons") &&
6730 strcmp(job_attr
, "job-uri") &&
6731 strcmp(job_attr
, "time-at-completed") &&
6732 strcmp(job_attr
, "time-at-creation") &&
6733 strcmp(job_attr
, "number-of-documents"))
6739 if (need_load_job
&& (limit
== 0 || limit
> 500) && (list
== Jobs
|| delete_list
))
6742 * Limit expensive Get-Jobs for job history to 500 jobs...
6745 ippAddInteger(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "limit", 500);
6748 ippAddInteger(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_INTEGER
, "limit", limit
);
6752 cupsdLogClient(con
, CUPSD_LOG_INFO
, "Limiting Get-Jobs response to %d jobs.", limit
);
6756 * OK, build a list of jobs for this printer...
6761 int i
; /* Looping var */
6763 for (i
= 0; i
< job_ids
->num_values
; i
++)
6765 if (!cupsdFindJob(job_ids
->values
[i
].integer
))
6769 if (i
< job_ids
->num_values
)
6771 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."),
6772 job_ids
->values
[i
].integer
);
6776 for (i
= 0; i
< job_ids
->num_values
; i
++)
6778 job
= cupsdFindJob(job_ids
->values
[i
].integer
);
6780 if (need_load_job
&& !job
->attrs
)
6786 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_jobs: No attributes for job %d", job
->id
);
6792 ippAddSeparator(con
->response
);
6794 exclude
= cupsdGetPrivateAttrs(job
->printer
?
6795 job
->printer
->op_policy_ptr
:
6796 policy
, con
, job
->printer
,
6799 copy_job_attrs(con
, job
, ra
, exclude
);
6804 if (first_index
> 1)
6805 job
= (cupsd_job_t
*)cupsArrayIndex(list
, first_index
- 1);
6807 job
= (cupsd_job_t
*)cupsArrayFirst(list
);
6809 for (count
= 0; (limit
<= 0 || count
< limit
) && job
; job
= (cupsd_job_t
*)cupsArrayNext(list
))
6812 * Filter out jobs that don't match...
6815 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
6816 "get_jobs: job->id=%d, dest=\"%s\", username=\"%s\", "
6817 "state_value=%d, attrs=%p", job
->id
, job
->dest
,
6818 job
->username
, job
->state_value
, job
->attrs
);
6820 if (!job
->dest
|| !job
->username
)
6823 if (!job
->dest
|| !job
->username
)
6826 if ((dest
&& strcmp(job
->dest
, dest
)) &&
6827 (!job
->printer
|| !dest
|| strcmp(job
->printer
->name
, dest
)))
6829 if ((job
->dtype
& dmask
) != dtype
&&
6830 (!job
->printer
|| (job
->printer
->type
& dmask
) != dtype
))
6833 if ((job_comparison
< 0 && job
->state_value
> job_state
) ||
6834 (job_comparison
== 0 && job
->state_value
!= job_state
) ||
6835 (job_comparison
> 0 && job
->state_value
< job_state
))
6838 if (job
->id
< first_job_id
)
6841 if (need_load_job
&& !job
->attrs
)
6847 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_jobs: No attributes for job %d", job
->id
);
6852 if (username
[0] && _cups_strcasecmp(username
, job
->username
))
6856 ippAddSeparator(con
->response
);
6860 exclude
= cupsdGetPrivateAttrs(job
->printer
?
6861 job
->printer
->op_policy_ptr
:
6862 policy
, con
, job
->printer
,
6865 copy_job_attrs(con
, job
, ra
, exclude
);
6868 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_jobs: count=%d", count
);
6871 cupsArrayDelete(ra
);
6874 cupsArrayDelete(list
);
6876 con
->response
->request
.status
.status_code
= IPP_OK
;
6881 * 'get_notifications()' - Get events for a subscription.
6885 get_notifications(cupsd_client_t
*con
) /* I - Client connection */
6887 int i
, j
; /* Looping vars */
6888 http_status_t status
; /* Policy status */
6889 cupsd_subscription_t
*sub
; /* Subscription */
6890 ipp_attribute_t
*ids
, /* notify-subscription-ids */
6891 *sequences
; /* notify-sequence-numbers */
6892 int min_seq
; /* Minimum sequence number */
6893 int interval
; /* Poll interval */
6896 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_notifications(con=%p[%d])",
6900 * Get subscription attributes...
6903 ids
= ippFindAttribute(con
->request
, "notify-subscription-ids",
6905 sequences
= ippFindAttribute(con
->request
, "notify-sequence-numbers",
6910 send_ipp_status(con
, IPP_BAD_REQUEST
,
6911 _("Missing notify-subscription-ids attribute."));
6916 * Are the subscription IDs valid?
6919 for (i
= 0, interval
= 60; i
< ids
->num_values
; i
++)
6921 if ((sub
= cupsdFindSubscription(ids
->values
[i
].integer
)) == NULL
)
6924 * Bad subscription ID...
6927 send_ipp_status(con
, IPP_NOT_FOUND
, _("Subscription #%d does not exist."),
6928 ids
->values
[i
].integer
);
6936 if ((status
= cupsdCheckPolicy(sub
->dest
? sub
->dest
->op_policy_ptr
:
6938 con
, sub
->owner
)) != HTTP_OK
)
6940 send_http_error(con
, status
, sub
->dest
);
6945 * Check the subscription type and update the interval accordingly.
6948 if (sub
->job
&& sub
->job
->state_value
== IPP_JOB_PROCESSING
&&
6951 else if (sub
->job
&& sub
->job
->state_value
>= IPP_JOB_STOPPED
)
6953 else if (sub
->dest
&& sub
->dest
->state
== IPP_PRINTER_PROCESSING
&&
6959 * Tell the client to poll again in N seconds...
6963 ippAddInteger(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
,
6964 "notify-get-interval", interval
);
6966 ippAddInteger(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
,
6967 "printer-up-time", time(NULL
));
6970 * Copy the subscription event attributes to the response.
6973 con
->response
->request
.status
.status_code
=
6974 interval
? IPP_OK
: IPP_OK_EVENTS_COMPLETE
;
6976 for (i
= 0; i
< ids
->num_values
; i
++)
6979 * Get the subscription and sequence number...
6982 sub
= cupsdFindSubscription(ids
->values
[i
].integer
);
6984 if (sequences
&& i
< sequences
->num_values
)
6985 min_seq
= sequences
->values
[i
].integer
;
6990 * If we don't have any new events, nothing to do here...
6993 if (min_seq
> (sub
->first_event_id
+ cupsArrayCount(sub
->events
)))
6997 * Otherwise copy all of the new events...
7000 if (sub
->first_event_id
> min_seq
)
7003 j
= min_seq
- sub
->first_event_id
;
7005 for (; j
< cupsArrayCount(sub
->events
); j
++)
7007 ippAddSeparator(con
->response
);
7009 copy_attrs(con
->response
,
7010 ((cupsd_event_t
*)cupsArrayIndex(sub
->events
, j
))->attrs
, NULL
,
7011 IPP_TAG_EVENT_NOTIFICATION
, 0, NULL
);
7018 * 'get_ppd()' - Get a named PPD from the local system.
7022 get_ppd(cupsd_client_t
*con
, /* I - Client connection */
7023 ipp_attribute_t
*uri
) /* I - Printer URI or PPD name */
7025 http_status_t status
; /* Policy status */
7026 cupsd_printer_t
*dest
; /* Destination */
7027 cups_ptype_t dtype
; /* Destination type */
7030 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_ppd(%p[%d], %p[%s=%s])", con
,
7031 con
->number
, uri
, uri
->name
, uri
->values
[0].string
.text
);
7033 if (!strcmp(ippGetName(uri
), "ppd-name"))
7036 * Return a PPD file from cups-driverd...
7039 const char *ppd_name
= ippGetString(uri
, 0, NULL
);
7040 /* ppd-name value */
7041 char command
[1024], /* cups-driverd command */
7042 options
[1024], /* Options to pass to command */
7043 oppd_name
[1024]; /* Escaped ppd-name */
7049 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
7051 send_http_error(con
, status
, NULL
);
7056 * Check ppd-name value...
7059 if (strstr(ppd_name
, "../"))
7061 send_ipp_status(con
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, _("Invalid ppd-name value."));
7066 * Run cups-driverd command with the given options...
7069 snprintf(command
, sizeof(command
), "%s/daemon/cups-driverd", ServerBin
);
7070 url_encode_string(ppd_name
, oppd_name
, sizeof(oppd_name
));
7071 snprintf(options
, sizeof(options
), "get+%d+%s", ippGetRequestId(con
->request
), oppd_name
);
7073 if (cupsdSendCommand(con
, command
, options
, 0))
7076 * Command started successfully, don't send an IPP response here...
7079 ippDelete(con
->response
);
7080 con
->response
= NULL
;
7085 * Command failed, return "internal error" so the user knows something
7089 send_ipp_status(con
, IPP_INTERNAL_ERROR
, _("cups-driverd failed to execute."));
7092 else if (!strcmp(ippGetName(uri
), "printer-uri") && cupsdValidateDest(ippGetString(uri
, 0, NULL
), &dtype
, &dest
))
7094 int i
; /* Looping var */
7095 char filename
[1024]; /* PPD filename */
7101 if ((status
= cupsdCheckPolicy(dest
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
7103 send_http_error(con
, status
, dest
);
7108 * See if we need the PPD for a class or remote printer...
7111 snprintf(filename
, sizeof(filename
), "%s/ppd/%s.ppd", ServerRoot
, dest
->name
);
7113 if ((dtype
& CUPS_PRINTER_REMOTE
) && access(filename
, 0))
7115 send_ipp_status(con
, IPP_STATUS_CUPS_SEE_OTHER
, _("See remote printer."));
7116 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, dest
->uri
);
7119 else if (dtype
& CUPS_PRINTER_CLASS
)
7121 for (i
= 0; i
< dest
->num_printers
; i
++)
7122 if (!(dest
->printers
[i
]->type
& CUPS_PRINTER_CLASS
))
7124 snprintf(filename
, sizeof(filename
), "%s/ppd/%s.ppd", ServerRoot
, dest
->printers
[i
]->name
);
7126 if (!access(filename
, 0))
7130 if (i
< dest
->num_printers
)
7131 dest
= dest
->printers
[i
];
7134 send_ipp_status(con
, IPP_STATUS_CUPS_SEE_OTHER
, _("See remote printer."));
7135 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, dest
->printers
[0]->uri
);
7141 * Found the printer with the PPD file, now see if there is one...
7144 if ((con
->file
= open(filename
, O_RDONLY
)) < 0)
7146 send_ipp_status(con
, IPP_STATUS_ERROR_NOT_FOUND
, _("The PPD file \"%s\" could not be opened: %s"), ippGetString(uri
, 0, NULL
), strerror(errno
));
7150 fcntl(con
->file
, F_SETFD
, fcntl(con
->file
, F_GETFD
) | FD_CLOEXEC
);
7154 ippSetStatusCode(con
->response
, IPP_STATUS_OK
);
7157 send_ipp_status(con
, IPP_STATUS_ERROR_NOT_FOUND
, _("The PPD file \"%s\" could not be found."), ippGetString(uri
, 0, NULL
));
7162 * 'get_ppds()' - Get the list of PPD files on the local system.
7166 get_ppds(cupsd_client_t
*con
) /* I - Client connection */
7168 http_status_t status
; /* Policy status */
7169 ipp_attribute_t
*limit
, /* Limit attribute */
7170 *device
, /* ppd-device-id attribute */
7171 *language
, /* ppd-natural-language attribute */
7172 *make
, /* ppd-make attribute */
7173 *model
, /* ppd-make-and-model attribute */
7174 *model_number
, /* ppd-model-number attribute */
7175 *product
, /* ppd-product attribute */
7176 *psversion
, /* ppd-psverion attribute */
7177 *type
, /* ppd-type attribute */
7178 *requested
, /* requested-attributes attribute */
7179 *exclude
, /* exclude-schemes attribute */
7180 *include
; /* include-schemes attribute */
7181 char command
[1024], /* cups-driverd command */
7182 options
[4096], /* Options to pass to command */
7183 device_str
[256],/* Escaped ppd-device-id string */
7185 /* Escaped ppd-natural-language */
7186 make_str
[256], /* Escaped ppd-make string */
7187 model_str
[256], /* Escaped ppd-make-and-model string */
7188 model_number_str
[256],
7189 /* ppd-model-number string */
7191 /* Escaped ppd-product string */
7193 /* Escaped ppd-psversion string */
7194 type_str
[256], /* Escaped ppd-type string */
7196 /* String for requested attributes */
7198 /* String for excluded schemes */
7200 /* String for included schemes */
7203 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_ppds(%p[%d])", con
, con
->number
);
7209 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
7211 send_http_error(con
, status
, NULL
);
7216 * Run cups-driverd command with the given options...
7219 limit
= ippFindAttribute(con
->request
, "limit", IPP_TAG_INTEGER
);
7220 device
= ippFindAttribute(con
->request
, "ppd-device-id", IPP_TAG_TEXT
);
7221 language
= ippFindAttribute(con
->request
, "ppd-natural-language",
7223 make
= ippFindAttribute(con
->request
, "ppd-make", IPP_TAG_TEXT
);
7224 model
= ippFindAttribute(con
->request
, "ppd-make-and-model",
7226 model_number
= ippFindAttribute(con
->request
, "ppd-model-number",
7228 product
= ippFindAttribute(con
->request
, "ppd-product", IPP_TAG_TEXT
);
7229 psversion
= ippFindAttribute(con
->request
, "ppd-psversion", IPP_TAG_TEXT
);
7230 type
= ippFindAttribute(con
->request
, "ppd-type", IPP_TAG_KEYWORD
);
7231 requested
= ippFindAttribute(con
->request
, "requested-attributes",
7233 exclude
= ippFindAttribute(con
->request
, "exclude-schemes",
7235 include
= ippFindAttribute(con
->request
, "include-schemes",
7239 url_encode_attr(requested
, requested_str
, sizeof(requested_str
));
7241 strlcpy(requested_str
, "requested-attributes=all", sizeof(requested_str
));
7244 url_encode_attr(device
, device_str
, sizeof(device_str
));
7246 device_str
[0] = '\0';
7249 url_encode_attr(language
, language_str
, sizeof(language_str
));
7251 language_str
[0] = '\0';
7254 url_encode_attr(make
, make_str
, sizeof(make_str
));
7259 url_encode_attr(model
, model_str
, sizeof(model_str
));
7261 model_str
[0] = '\0';
7264 snprintf(model_number_str
, sizeof(model_number_str
), "ppd-model-number=%d",
7265 model_number
->values
[0].integer
);
7267 model_number_str
[0] = '\0';
7270 url_encode_attr(product
, product_str
, sizeof(product_str
));
7272 product_str
[0] = '\0';
7275 url_encode_attr(psversion
, psversion_str
, sizeof(psversion_str
));
7277 psversion_str
[0] = '\0';
7280 url_encode_attr(type
, type_str
, sizeof(type_str
));
7285 url_encode_attr(exclude
, exclude_str
, sizeof(exclude_str
));
7287 exclude_str
[0] = '\0';
7290 url_encode_attr(include
, include_str
, sizeof(include_str
));
7292 include_str
[0] = '\0';
7294 snprintf(command
, sizeof(command
), "%s/daemon/cups-driverd", ServerBin
);
7295 snprintf(options
, sizeof(options
),
7296 "list+%d+%d+%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
7297 con
->request
->request
.op
.request_id
,
7298 limit
? limit
->values
[0].integer
: 0,
7300 device
? "%20" : "", device_str
,
7301 language
? "%20" : "", language_str
,
7302 make
? "%20" : "", make_str
,
7303 model
? "%20" : "", model_str
,
7304 model_number
? "%20" : "", model_number_str
,
7305 product
? "%20" : "", product_str
,
7306 psversion
? "%20" : "", psversion_str
,
7307 type
? "%20" : "", type_str
,
7308 exclude_str
[0] ? "%20" : "", exclude_str
,
7309 include_str
[0] ? "%20" : "", include_str
);
7311 if (cupsdSendCommand(con
, command
, options
, 0))
7314 * Command started successfully, don't send an IPP response here...
7317 ippDelete(con
->response
);
7318 con
->response
= NULL
;
7323 * Command failed, return "internal error" so the user knows something
7327 send_ipp_status(con
, IPP_INTERNAL_ERROR
,
7328 _("cups-driverd failed to execute."));
7334 * 'get_printer_attrs()' - Get printer attributes.
7338 get_printer_attrs(cupsd_client_t
*con
, /* I - Client connection */
7339 ipp_attribute_t
*uri
) /* I - Printer URI */
7341 http_status_t status
; /* Policy status */
7342 cups_ptype_t dtype
; /* Destination type (printer/class) */
7343 cupsd_printer_t
*printer
; /* Printer/class */
7344 cups_array_t
*ra
; /* Requested attributes array */
7347 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_printer_attrs(%p[%d], %s)", con
,
7348 con
->number
, uri
->values
[0].string
.text
);
7351 * Is the destination valid?
7354 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
7360 send_ipp_status(con
, IPP_NOT_FOUND
,
7361 _("The printer or class does not exist."));
7369 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
7371 send_http_error(con
, status
, printer
);
7376 * Send the attributes...
7379 ra
= create_requested_array(con
->request
);
7381 copy_printer_attrs(con
, printer
, ra
);
7383 cupsArrayDelete(ra
);
7385 con
->response
->request
.status
.status_code
= IPP_OK
;
7390 * 'get_printer_supported()' - Get printer supported values.
7394 get_printer_supported(
7395 cupsd_client_t
*con
, /* I - Client connection */
7396 ipp_attribute_t
*uri
) /* I - Printer URI */
7398 http_status_t status
; /* Policy status */
7399 cups_ptype_t dtype
; /* Destination type (printer/class) */
7400 cupsd_printer_t
*printer
; /* Printer/class */
7403 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_printer_supported(%p[%d], %s)", con
,
7404 con
->number
, uri
->values
[0].string
.text
);
7407 * Is the destination valid?
7410 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
7416 send_ipp_status(con
, IPP_NOT_FOUND
,
7417 _("The printer or class does not exist."));
7425 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
7427 send_http_error(con
, status
, printer
);
7432 * Return a list of attributes that can be set via Set-Printer-Attributes.
7435 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ADMINDEFINE
,
7436 "printer-geo-location", 0);
7437 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ADMINDEFINE
,
7439 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ADMINDEFINE
,
7440 "printer-location", 0);
7441 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ADMINDEFINE
,
7442 "printer-organization", 0);
7443 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ADMINDEFINE
,
7444 "printer-organizational-unit", 0);
7446 con
->response
->request
.status
.status_code
= IPP_OK
;
7451 * 'get_printers()' - Get a list of printers or classes.
7455 get_printers(cupsd_client_t
*con
, /* I - Client connection */
7456 int type
) /* I - 0 or CUPS_PRINTER_CLASS */
7458 http_status_t status
; /* Policy status */
7459 ipp_attribute_t
*attr
; /* Current attribute */
7460 int limit
; /* Max number of printers to return */
7461 int count
; /* Number of printers that match */
7462 int printer_id
; /* Printer we are interested in */
7463 cupsd_printer_t
*printer
; /* Current printer pointer */
7464 cups_ptype_t printer_type
, /* printer-type attribute */
7465 printer_mask
; /* printer-type-mask attribute */
7466 char *location
; /* Location string */
7467 const char *username
; /* Current user */
7468 char *first_printer_name
; /* first-printer-name attribute */
7469 cups_array_t
*ra
; /* Requested attributes array */
7470 int local
; /* Local connection? */
7473 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "get_printers(%p[%d], %x)", con
,
7480 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
7482 send_http_error(con
, status
, NULL
);
7487 * Check for printers...
7490 if (!Printers
|| !cupsArrayCount(Printers
))
7492 send_ipp_status(con
, IPP_NOT_FOUND
, _("No destinations added."));
7497 * See if they want to limit the number of printers reported...
7500 if ((attr
= ippFindAttribute(con
->request
, "limit",
7501 IPP_TAG_INTEGER
)) != NULL
)
7502 limit
= attr
->values
[0].integer
;
7506 if ((attr
= ippFindAttribute(con
->request
, "first-printer-name",
7507 IPP_TAG_NAME
)) != NULL
)
7508 first_printer_name
= attr
->values
[0].string
.text
;
7510 first_printer_name
= NULL
;
7513 * Support filtering...
7516 if ((attr
= ippFindAttribute(con
->request
, "printer-id", IPP_TAG_INTEGER
)) != NULL
)
7518 if ((printer_id
= ippGetInteger(attr
, 0)) <= 0)
7520 send_ipp_status(con
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, _("Bad \"printer-id\" value %d."), printer_id
);
7527 if ((attr
= ippFindAttribute(con
->request
, "printer-type",
7528 IPP_TAG_ENUM
)) != NULL
)
7529 printer_type
= (cups_ptype_t
)attr
->values
[0].integer
;
7531 printer_type
= (cups_ptype_t
)0;
7533 if ((attr
= ippFindAttribute(con
->request
, "printer-type-mask",
7534 IPP_TAG_ENUM
)) != NULL
)
7535 printer_mask
= (cups_ptype_t
)attr
->values
[0].integer
;
7537 printer_mask
= (cups_ptype_t
)0;
7539 local
= httpAddrLocalhost(&(con
->clientaddr
));
7541 if ((attr
= ippFindAttribute(con
->request
, "printer-location",
7542 IPP_TAG_TEXT
)) != NULL
)
7543 location
= attr
->values
[0].string
.text
;
7547 if (con
->username
[0])
7548 username
= con
->username
;
7549 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name",
7550 IPP_TAG_NAME
)) != NULL
)
7551 username
= attr
->values
[0].string
.text
;
7555 ra
= create_requested_array(con
->request
);
7558 * OK, build a list of printers for this printer...
7561 if (first_printer_name
)
7563 if ((printer
= cupsdFindDest(first_printer_name
)) == NULL
)
7564 printer
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
7567 printer
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
7570 count
< limit
&& printer
;
7571 printer
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
7573 if (!local
&& !printer
->shared
)
7576 if (printer_id
&& printer
->printer_id
!= printer_id
)
7579 if ((!type
|| (printer
->type
& CUPS_PRINTER_CLASS
) == type
) &&
7580 (printer
->type
& printer_mask
) == printer_type
&&
7582 (printer
->location
&& !_cups_strcasecmp(printer
->location
, location
))))
7585 * If a username is specified, see if it is allowed or denied
7589 if (cupsArrayCount(printer
->users
) && username
&&
7590 !user_allowed(printer
, username
))
7594 * Add the group separator as needed...
7598 ippAddSeparator(con
->response
);
7603 * Send the attributes...
7606 copy_printer_attrs(con
, printer
, ra
);
7610 cupsArrayDelete(ra
);
7612 con
->response
->request
.status
.status_code
= IPP_OK
;
7617 * 'get_subscription_attrs()' - Get subscription attributes.
7621 get_subscription_attrs(
7622 cupsd_client_t
*con
, /* I - Client connection */
7623 int sub_id
) /* I - Subscription ID */
7625 http_status_t status
; /* Policy status */
7626 cupsd_subscription_t
*sub
; /* Subscription */
7627 cupsd_policy_t
*policy
; /* Current security policy */
7628 cups_array_t
*ra
, /* Requested attributes array */
7629 *exclude
; /* Private attributes array */
7632 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
7633 "get_subscription_attrs(con=%p[%d], sub_id=%d)",
7634 con
, con
->number
, sub_id
);
7637 * Expire subscriptions as needed...
7640 cupsdExpireSubscriptions(NULL
, NULL
);
7643 * Is the subscription ID valid?
7646 if ((sub
= cupsdFindSubscription(sub_id
)) == NULL
)
7649 * Bad subscription ID...
7652 send_ipp_status(con
, IPP_NOT_FOUND
, _("Subscription #%d does not exist."),
7662 policy
= sub
->dest
->op_policy_ptr
;
7664 policy
= DefaultPolicyPtr
;
7666 if ((status
= cupsdCheckPolicy(policy
, con
, sub
->owner
)) != HTTP_OK
)
7668 send_http_error(con
, status
, sub
->dest
);
7672 exclude
= cupsdGetPrivateAttrs(policy
, con
, sub
->dest
, sub
->owner
);
7675 * Copy the subscription attributes to the response using the
7676 * requested-attributes attribute that may be provided by the client.
7679 ra
= create_requested_array(con
->request
);
7681 copy_subscription_attrs(con
, sub
, ra
, exclude
);
7683 cupsArrayDelete(ra
);
7685 con
->response
->request
.status
.status_code
= IPP_OK
;
7690 * 'get_subscriptions()' - Get subscriptions.
7694 get_subscriptions(cupsd_client_t
*con
, /* I - Client connection */
7695 ipp_attribute_t
*uri
) /* I - Printer/job URI */
7697 http_status_t status
; /* Policy status */
7698 int count
; /* Number of subscriptions */
7699 int limit
; /* Limit */
7700 cupsd_subscription_t
*sub
; /* Subscription */
7701 cups_array_t
*ra
; /* Requested attributes array */
7702 ipp_attribute_t
*attr
; /* Attribute */
7703 cups_ptype_t dtype
; /* Destination type (printer/class) */
7704 char scheme
[HTTP_MAX_URI
],
7705 /* Scheme portion of URI */
7706 username
[HTTP_MAX_URI
],
7707 /* Username portion of URI */
7709 /* Host portion of URI */
7710 resource
[HTTP_MAX_URI
];
7711 /* Resource portion of URI */
7712 int port
; /* Port portion of URI */
7713 cupsd_job_t
*job
; /* Job pointer */
7714 cupsd_printer_t
*printer
; /* Printer */
7715 cupsd_policy_t
*policy
; /* Policy */
7716 cups_array_t
*exclude
; /* Private attributes array */
7719 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
7720 "get_subscriptions(con=%p[%d], uri=%s)",
7721 con
, con
->number
, uri
->values
[0].string
.text
);
7724 * Is the destination valid?
7727 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
7728 sizeof(scheme
), username
, sizeof(username
), host
,
7729 sizeof(host
), &port
, resource
, sizeof(resource
));
7731 if (!strcmp(resource
, "/") ||
7732 (!strncmp(resource
, "/jobs", 5) && strlen(resource
) <= 6) ||
7733 (!strncmp(resource
, "/printers", 9) && strlen(resource
) <= 10) ||
7734 (!strncmp(resource
, "/classes", 8) && strlen(resource
) <= 9))
7739 else if (!strncmp(resource
, "/jobs/", 6) && resource
[6])
7742 job
= cupsdFindJob(atoi(resource
+ 6));
7746 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."),
7747 atoi(resource
+ 6));
7751 else if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
7757 send_ipp_status(con
, IPP_NOT_FOUND
,
7758 _("The printer or class does not exist."));
7761 else if ((attr
= ippFindAttribute(con
->request
, "notify-job-id",
7762 IPP_TAG_INTEGER
)) != NULL
)
7764 job
= cupsdFindJob(attr
->values
[0].integer
);
7768 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."),
7769 attr
->values
[0].integer
);
7781 policy
= printer
->op_policy_ptr
;
7783 policy
= DefaultPolicyPtr
;
7785 if ((status
= cupsdCheckPolicy(policy
, con
, NULL
)) != HTTP_OK
)
7787 send_http_error(con
, status
, printer
);
7792 * Expire subscriptions as needed...
7795 cupsdExpireSubscriptions(NULL
, NULL
);
7798 * Copy the subscription attributes to the response using the
7799 * requested-attributes attribute that may be provided by the client.
7802 ra
= create_requested_array(con
->request
);
7804 if ((attr
= ippFindAttribute(con
->request
, "limit",
7805 IPP_TAG_INTEGER
)) != NULL
)
7806 limit
= attr
->values
[0].integer
;
7811 * See if we only want to see subscriptions for a specific user...
7814 if ((attr
= ippFindAttribute(con
->request
, "my-subscriptions",
7815 IPP_TAG_BOOLEAN
)) != NULL
&&
7816 attr
->values
[0].boolean
)
7817 strlcpy(username
, get_username(con
), sizeof(username
));
7821 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
), count
= 0;
7823 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
7824 if ((!printer
|| sub
->dest
== printer
) && (!job
|| sub
->job
== job
) &&
7825 (!username
[0] || !_cups_strcasecmp(username
, sub
->owner
)))
7827 ippAddSeparator(con
->response
);
7829 exclude
= cupsdGetPrivateAttrs(sub
->dest
? sub
->dest
->op_policy_ptr
:
7830 policy
, con
, sub
->dest
,
7833 copy_subscription_attrs(con
, sub
, ra
, exclude
);
7836 if (limit
&& count
>= limit
)
7840 cupsArrayDelete(ra
);
7843 con
->response
->request
.status
.status_code
= IPP_OK
;
7845 send_ipp_status(con
, IPP_NOT_FOUND
, _("No subscriptions found."));
7850 * 'get_username()' - Get the username associated with a request.
7853 static const char * /* O - Username */
7854 get_username(cupsd_client_t
*con
) /* I - Connection */
7856 ipp_attribute_t
*attr
; /* Attribute */
7859 if (con
->username
[0])
7860 return (con
->username
);
7861 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name",
7862 IPP_TAG_NAME
)) != NULL
)
7863 return (attr
->values
[0].string
.text
);
7865 return ("anonymous");
7870 * 'hold_job()' - Hold a print job.
7874 hold_job(cupsd_client_t
*con
, /* I - Client connection */
7875 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
7877 ipp_attribute_t
*attr
; /* Current job-hold-until */
7878 const char *when
; /* New value */
7879 int jobid
; /* Job ID */
7880 char scheme
[HTTP_MAX_URI
], /* Method portion of URI */
7881 username
[HTTP_MAX_URI
], /* Username portion of URI */
7882 host
[HTTP_MAX_URI
], /* Host portion of URI */
7883 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
7884 int port
; /* Port portion of URI */
7885 cupsd_job_t
*job
; /* Job information */
7888 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "hold_job(%p[%d], %s)", con
, con
->number
,
7889 uri
->values
[0].string
.text
);
7892 * See if we have a job URI or a printer URI...
7895 if (!strcmp(uri
->name
, "printer-uri"))
7898 * Got a printer URI; see if we also have a job-id attribute...
7901 if ((attr
= ippFindAttribute(con
->request
, "job-id",
7902 IPP_TAG_INTEGER
)) == NULL
)
7904 send_ipp_status(con
, IPP_BAD_REQUEST
,
7905 _("Got a printer-uri attribute but no job-id."));
7909 jobid
= attr
->values
[0].integer
;
7914 * Got a job URI; parse it to get the job ID...
7917 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
7918 sizeof(scheme
), username
, sizeof(username
), host
,
7919 sizeof(host
), &port
, resource
, sizeof(resource
));
7921 if (strncmp(resource
, "/jobs/", 6))
7927 send_ipp_status(con
, IPP_BAD_REQUEST
,
7928 _("Bad job-uri \"%s\"."),
7929 uri
->values
[0].string
.text
);
7933 jobid
= atoi(resource
+ 6);
7937 * See if the job exists...
7940 if ((job
= cupsdFindJob(jobid
)) == NULL
)
7943 * Nope - return a "not found" error...
7946 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."), jobid
);
7951 * See if the job is owned by the requesting user...
7954 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
7956 send_http_error(con
, con
->username
[0] ? HTTP_FORBIDDEN
: HTTP_UNAUTHORIZED
,
7957 cupsdFindDest(job
->dest
));
7962 * See if the job is in a state that allows holding...
7965 if (job
->state_value
> IPP_JOB_STOPPED
)
7968 * Return a "not-possible" error...
7971 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
7972 _("Job #%d is finished and cannot be altered."),
7978 * Hold the job and return...
7981 if ((attr
= ippFindAttribute(con
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
7983 if ((ippGetValueTag(attr
) != IPP_TAG_KEYWORD
&& ippGetValueTag(attr
) != IPP_TAG_NAME
&& ippGetValueTag(attr
) != IPP_TAG_NAMELANG
) || ippGetCount(attr
) != 1 || !ippValidateAttribute(attr
))
7985 send_ipp_status(con
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, _("Unsupported 'job-hold-until' value."));
7986 ippCopyAttribute(con
->response
, attr
, 0);
7990 when
= ippGetString(attr
, 0, NULL
);
7992 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED
, cupsdFindDest(job
->dest
), job
,
7993 "Job job-hold-until value changed by user.");
7996 when
= "indefinite";
7998 cupsdSetJobHoldUntil(job
, when
, 1);
7999 cupsdSetJobState(job
, IPP_JOB_HELD
, CUPSD_JOB_DEFAULT
, "Job held by \"%s\".",
8002 con
->response
->request
.status
.status_code
= IPP_OK
;
8007 * 'hold_new_jobs()' - Hold pending/new jobs on a printer or class.
8011 hold_new_jobs(cupsd_client_t
*con
, /* I - Connection */
8012 ipp_attribute_t
*uri
) /* I - Printer URI */
8014 http_status_t status
; /* Policy status */
8015 cups_ptype_t dtype
; /* Destination type (printer/class) */
8016 cupsd_printer_t
*printer
; /* Printer data */
8019 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "hold_new_jobs(%p[%d], %s)", con
,
8020 con
->number
, uri
->values
[0].string
.text
);
8023 * Is the destination valid?
8026 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
8032 send_ipp_status(con
, IPP_NOT_FOUND
,
8033 _("The printer or class does not exist."));
8041 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
8043 send_http_error(con
, status
, printer
);
8048 * Hold pending/new jobs sent to the printer...
8051 printer
->holding_new_jobs
= 1;
8053 cupsdSetPrinterReasons(printer
, "+hold-new-jobs");
8055 if (dtype
& CUPS_PRINTER_CLASS
)
8056 cupsdLogMessage(CUPSD_LOG_INFO
,
8057 "Class \"%s\" now holding pending/new jobs (\"%s\").",
8058 printer
->name
, get_username(con
));
8060 cupsdLogMessage(CUPSD_LOG_INFO
,
8061 "Printer \"%s\" now holding pending/new jobs (\"%s\").",
8062 printer
->name
, get_username(con
));
8065 * Everything was ok, so return OK status...
8068 con
->response
->request
.status
.status_code
= IPP_OK
;
8073 * 'move_job()' - Move a job to a new destination.
8077 move_job(cupsd_client_t
*con
, /* I - Client connection */
8078 ipp_attribute_t
*uri
) /* I - Job URI */
8080 http_status_t status
; /* Policy status */
8081 ipp_attribute_t
*attr
; /* Current attribute */
8082 int jobid
; /* Job ID */
8083 cupsd_job_t
*job
; /* Current job */
8084 const char *src
; /* Source printer/class */
8085 cups_ptype_t stype
, /* Source type (printer or class) */
8086 dtype
; /* Destination type (printer/class) */
8087 char scheme
[HTTP_MAX_URI
], /* Scheme portion of URI */
8088 username
[HTTP_MAX_URI
], /* Username portion of URI */
8089 host
[HTTP_MAX_URI
], /* Host portion of URI */
8090 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
8091 int port
; /* Port portion of URI */
8092 cupsd_printer_t
*sprinter
, /* Source printer */
8093 *dprinter
; /* Destination printer */
8096 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "move_job(%p[%d], %s)", con
, con
->number
,
8097 uri
->values
[0].string
.text
);
8100 * Get the new printer or class...
8103 if ((attr
= ippFindAttribute(con
->request
, "job-printer-uri",
8104 IPP_TAG_URI
)) == NULL
)
8107 * Need job-printer-uri...
8110 send_ipp_status(con
, IPP_BAD_REQUEST
,
8111 _("job-printer-uri attribute missing."));
8115 if (!cupsdValidateDest(attr
->values
[0].string
.text
, &dtype
, &dprinter
))
8121 send_ipp_status(con
, IPP_NOT_FOUND
,
8122 _("The printer or class does not exist."));
8127 * See if we have a job URI or a printer URI...
8130 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
8131 sizeof(scheme
), username
, sizeof(username
), host
,
8132 sizeof(host
), &port
, resource
, sizeof(resource
));
8134 if (!strcmp(uri
->name
, "printer-uri"))
8137 * Got a printer URI; see if we also have a job-id attribute...
8140 if ((attr
= ippFindAttribute(con
->request
, "job-id",
8141 IPP_TAG_INTEGER
)) == NULL
)
8147 if ((src
= cupsdValidateDest(uri
->values
[0].string
.text
, &stype
,
8148 &sprinter
)) == NULL
)
8154 send_ipp_status(con
, IPP_NOT_FOUND
,
8155 _("The printer or class does not exist."));
8164 * Otherwise, just move a single job...
8167 if ((job
= cupsdFindJob(attr
->values
[0].integer
)) == NULL
)
8170 * Nope - return a "not found" error...
8173 send_ipp_status(con
, IPP_NOT_FOUND
,
8174 _("Job #%d does not exist."), attr
->values
[0].integer
);
8180 * Job found, initialize source pointers...
8191 * Got a job URI; parse it to get the job ID...
8194 if (strncmp(resource
, "/jobs/", 6))
8200 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad job-uri \"%s\"."),
8201 uri
->values
[0].string
.text
);
8206 * See if the job exists...
8209 jobid
= atoi(resource
+ 6);
8211 if ((job
= cupsdFindJob(jobid
)) == NULL
)
8214 * Nope - return a "not found" error...
8217 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."), jobid
);
8223 * Job found, initialize source pointers...
8232 * Check the policy of the destination printer...
8235 if ((status
= cupsdCheckPolicy(dprinter
->op_policy_ptr
, con
,
8236 job
? job
->username
: NULL
)) != HTTP_OK
)
8238 send_http_error(con
, status
, dprinter
);
8243 * Now move the job or jobs...
8249 * See if the job has been completed...
8252 if (job
->state_value
> IPP_JOB_STOPPED
)
8255 * Return a "not-possible" error...
8258 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
8259 _("Job #%d is finished and cannot be altered."),
8265 * See if the job is owned by the requesting user...
8268 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
8270 send_http_error(con
, con
->username
[0] ? HTTP_FORBIDDEN
: HTTP_UNAUTHORIZED
,
8271 cupsdFindDest(job
->dest
));
8276 * Move the job to a different printer or class...
8279 cupsdMoveJob(job
, dprinter
);
8284 * Got the source printer, now look through the jobs...
8287 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
8289 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
8292 * See if the job is pointing at the source printer or has not been
8296 if (_cups_strcasecmp(job
->dest
, src
) ||
8297 job
->state_value
> IPP_JOB_STOPPED
)
8301 * See if the job can be moved by the requesting user...
8304 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
8308 * Move the job to a different printer or class...
8311 cupsdMoveJob(job
, dprinter
);
8316 * Start jobs if possible...
8322 * Return with "everything is OK" status...
8325 con
->response
->request
.status
.status_code
= IPP_OK
;
8330 * 'ppd_parse_line()' - Parse a PPD default line.
8333 static int /* O - 0 on success, -1 on failure */
8334 ppd_parse_line(const char *line
, /* I - Line */
8335 char *option
, /* O - Option name */
8336 int olen
, /* I - Size of option name */
8337 char *choice
, /* O - Choice name */
8338 int clen
) /* I - Size of choice name */
8341 * Verify this is a default option line...
8344 if (strncmp(line
, "*Default", 8))
8348 * Read the option name...
8351 for (line
+= 8, olen
--;
8352 *line
> ' ' && *line
< 0x7f && *line
!= ':' && *line
!= '/';
8363 * Skip everything else up to the colon (:)...
8366 while (*line
&& *line
!= ':')
8375 * Now grab the option choice, skipping leading whitespace...
8378 while (isspace(*line
& 255))
8382 *line
> ' ' && *line
< 0x7f && *line
!= ':' && *line
!= '/';
8393 * Return with no errors...
8401 * 'print_job()' - Print a file to a printer or class.
8405 print_job(cupsd_client_t
*con
, /* I - Client connection */
8406 ipp_attribute_t
*uri
) /* I - Printer URI */
8408 ipp_attribute_t
*attr
; /* Current attribute */
8409 ipp_attribute_t
*doc_name
; /* document-name attribute */
8410 ipp_attribute_t
*format
; /* Document-format attribute */
8411 const char *default_format
; /* document-format-default value */
8412 cupsd_job_t
*job
; /* New job */
8413 char filename
[1024]; /* Job filename */
8414 mime_type_t
*filetype
; /* Type of file */
8415 char super
[MIME_MAX_SUPER
], /* Supertype of file */
8416 type
[MIME_MAX_TYPE
], /* Subtype of file */
8417 mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
8418 /* Textual name of mime type */
8419 cupsd_printer_t
*printer
; /* Printer data */
8420 struct stat fileinfo
; /* File information */
8421 int kbytes
; /* Size of file */
8422 int compression
; /* Document compression */
8425 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "print_job(%p[%d], %s)", con
, con
->number
,
8426 uri
->values
[0].string
.text
);
8429 * Validate print file attributes, for now just document-format and
8430 * compression (CUPS only supports "none" and "gzip")...
8433 compression
= CUPS_FILE_NONE
;
8435 if ((attr
= ippFindAttribute(con
->request
, "compression",
8436 IPP_TAG_KEYWORD
)) != NULL
)
8438 if (strcmp(attr
->values
[0].string
.text
, "none")
8440 && strcmp(attr
->values
[0].string
.text
, "gzip")
8441 #endif /* HAVE_LIBZ */
8444 send_ipp_status(con
, IPP_ATTRIBUTES
,
8445 _("Unsupported compression \"%s\"."),
8446 attr
->values
[0].string
.text
);
8447 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
8448 "compression", NULL
, attr
->values
[0].string
.text
);
8453 if (!strcmp(attr
->values
[0].string
.text
, "gzip"))
8454 compression
= CUPS_FILE_GZIP
;
8455 #endif /* HAVE_LIBZ */
8459 * Do we have a file to print?
8464 send_ipp_status(con
, IPP_BAD_REQUEST
, _("No file in print request."));
8469 * Is the destination valid?
8472 if (!cupsdValidateDest(uri
->values
[0].string
.text
, NULL
, &printer
))
8478 send_ipp_status(con
, IPP_NOT_FOUND
,
8479 _("The printer or class does not exist."));
8484 * Is it a format we support?
8487 doc_name
= ippFindAttribute(con
->request
, "document-name", IPP_TAG_NAME
);
8489 ippSetName(con
->request
, &doc_name
, "document-name-supplied");
8491 if ((format
= ippFindAttribute(con
->request
, "document-format",
8492 IPP_TAG_MIMETYPE
)) != NULL
)
8495 * Grab format from client...
8498 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%255[^;]", super
,
8501 send_ipp_status(con
, IPP_BAD_REQUEST
,
8502 _("Bad document-format \"%s\"."),
8503 format
->values
[0].string
.text
);
8507 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, ippGetString(format
, 0, NULL
));
8509 else if ((default_format
= cupsGetOption("document-format",
8510 printer
->num_options
,
8511 printer
->options
)) != NULL
)
8514 * Use default document format...
8517 if (sscanf(default_format
, "%15[^/]/%255[^;]", super
, type
) != 2)
8519 send_ipp_status(con
, IPP_BAD_REQUEST
,
8520 _("Bad document-format \"%s\"."),
8531 strlcpy(super
, "application", sizeof(super
));
8532 strlcpy(type
, "octet-stream", sizeof(type
));
8535 if (!strcmp(super
, "application") && !strcmp(type
, "octet-stream"))
8538 * Auto-type the file...
8541 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Job ???] Auto-typing file...");
8544 filetype
= mimeFileType(MimeDatabase
, con
->filename
,
8545 doc_name
? doc_name
->values
[0].string
.text
: NULL
,
8549 filetype
= mimeType(MimeDatabase
, super
, type
);
8551 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job ???] Request file type is %s/%s.",
8552 filetype
->super
, filetype
->type
);
8554 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
, filetype
->type
);
8555 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, mimetype
);
8558 filetype
= mimeType(MimeDatabase
, super
, type
);
8562 (!strcmp(super
, "application") && !strcmp(type
, "octet-stream"))))
8565 * Replace the document-format attribute value with the auto-typed or
8569 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
8573 ippSetString(con
->request
, &format
, 0, mimetype
);
8575 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
8576 "document-format", NULL
, mimetype
);
8580 send_ipp_status(con
, IPP_DOCUMENT_FORMAT
,
8581 _("Unsupported document-format \"%s\"."),
8582 format
? format
->values
[0].string
.text
:
8583 "application/octet-stream");
8584 cupsdLogMessage(CUPSD_LOG_INFO
,
8585 "Hint: Do you have the raw file printing rules enabled?");
8588 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
8589 "document-format", NULL
, format
->values
[0].string
.text
);
8595 * Read any embedded job ticket info from PS files...
8598 if (!_cups_strcasecmp(filetype
->super
, "application") &&
8599 (!_cups_strcasecmp(filetype
->type
, "postscript") ||
8600 !_cups_strcasecmp(filetype
->type
, "pdf")))
8601 read_job_ticket(con
);
8604 * Create the job object...
8607 if ((job
= add_job(con
, printer
, filetype
)) == NULL
)
8611 * Update quota data...
8614 if (stat(con
->filename
, &fileinfo
))
8617 kbytes
= (fileinfo
.st_size
+ 1023) / 1024;
8619 cupsdUpdateQuota(printer
, job
->username
, 0, kbytes
);
8621 job
->koctets
+= kbytes
;
8623 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) != NULL
)
8624 attr
->values
[0].integer
+= kbytes
;
8627 * Add the job file...
8630 if (add_file(con
, job
, filetype
, compression
))
8633 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
, job
->num_files
);
8634 if (rename(con
->filename
, filename
))
8636 cupsdLogJob(job
, CUPSD_LOG_ERROR
, "Unable to rename job document file \"%s\": %s", filename
, strerror(errno
));
8638 send_ipp_status(con
, IPP_INTERNAL_ERROR
, _("Unable to rename job document file."));
8642 cupsdClearString(&con
->filename
);
8645 * See if we need to add the ending sheet...
8648 if (cupsdTimeoutJob(job
))
8652 * Log and save the job...
8655 cupsdLogJob(job
, CUPSD_LOG_INFO
,
8656 "File of type %s/%s queued by \"%s\".",
8657 filetype
->super
, filetype
->type
, job
->username
);
8658 cupsdLogJob(job
, CUPSD_LOG_DEBUG
, "hold_until=%d", (int)job
->hold_until
);
8659 cupsdLogJob(job
, CUPSD_LOG_INFO
, "Queued on \"%s\" by \"%s\".",
8660 job
->dest
, job
->username
);
8663 * Start the job if possible...
8671 * 'read_job_ticket()' - Read a job ticket embedded in a print file.
8673 * This function only gets called when printing a single PDF or PostScript
8674 * file using the Print-Job operation. It doesn't work for Create-Job +
8675 * Send-File, since the job attributes need to be set at job creation
8676 * time for banners to work. The embedded job ticket stuff is here
8677 * primarily to allow the Windows printer driver for CUPS to pass in JCL
8678 * options and IPP attributes which otherwise would be lost.
8680 * The format of a job ticket is simple:
8682 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
8684 * %cupsJobTicket: attr1=value1
8685 * %cupsJobTicket: attr2=value2
8687 * %cupsJobTicket: attrN=valueN
8689 * Job ticket lines must appear immediately after the first line that
8690 * specifies PostScript (%!PS-Adobe-3.0) or PDF (%PDF) format, and CUPS
8691 * stops looking for job ticket info when it finds a line that does not begin
8692 * with "%cupsJobTicket:".
8694 * The maximum length of a job ticket line, including the prefix, is
8695 * 255 characters to conform with the Adobe DSC.
8697 * Read-only attributes are rejected with a notice to the error log in
8698 * case a malicious user tries anything. Since the job ticket is read
8699 * prior to attribute validation in print_job(), job ticket attributes
8700 * will go through the same validation as IPP attributes...
8704 read_job_ticket(cupsd_client_t
*con
) /* I - Client connection */
8706 cups_file_t
*fp
; /* File to read from */
8707 char line
[256]; /* Line data */
8708 int num_options
; /* Number of options */
8709 cups_option_t
*options
; /* Options */
8710 ipp_t
*ticket
; /* New attributes */
8711 ipp_attribute_t
*attr
, /* Current attribute */
8712 *attr2
, /* Job attribute */
8713 *prev2
; /* Previous job attribute */
8717 * First open the print file...
8720 if ((fp
= cupsFileOpen(con
->filename
, "rb")) == NULL
)
8722 cupsdLogMessage(CUPSD_LOG_ERROR
,
8723 "Unable to open print file for job ticket - %s",
8729 * Skip the first line...
8732 if (cupsFileGets(fp
, line
, sizeof(line
)) == NULL
)
8734 cupsdLogMessage(CUPSD_LOG_ERROR
,
8735 "Unable to read from print file for job ticket - %s",
8741 if (strncmp(line
, "%!PS-Adobe-", 11) && strncmp(line
, "%PDF-", 5))
8744 * Not a DSC-compliant file, so no job ticket info will be available...
8752 * Read job ticket info from the file...
8758 while (cupsFileGets(fp
, line
, sizeof(line
)))
8761 * Stop at the first non-ticket line...
8764 if (strncmp(line
, "%cupsJobTicket:", 15))
8768 * Add the options to the option array...
8771 num_options
= cupsParseOptions(line
+ 15, num_options
, &options
);
8775 * Done with the file; see if we have any options...
8780 if (num_options
== 0)
8784 * OK, convert the options to an attribute list, and apply them to
8789 cupsEncodeOptions(ticket
, num_options
, options
);
8792 * See what the user wants to change.
8795 for (attr
= ticket
->attrs
; attr
; attr
= attr
->next
)
8797 if (attr
->group_tag
!= IPP_TAG_JOB
|| !attr
->name
)
8800 if (!strncmp(attr
->name
, "date-time-at-", 13) ||
8801 !strcmp(attr
->name
, "job-impressions-completed") ||
8802 !strcmp(attr
->name
, "job-media-sheets-completed") ||
8803 !strncmp(attr
->name
, "job-k-octets", 12) ||
8804 !strcmp(attr
->name
, "job-id") ||
8805 !strcmp(attr
->name
, "job-originating-host-name") ||
8806 !strcmp(attr
->name
, "job-originating-user-name") ||
8807 !strcmp(attr
->name
, "job-pages-completed") ||
8808 !strcmp(attr
->name
, "job-printer-uri") ||
8809 !strncmp(attr
->name
, "job-state", 9) ||
8810 !strcmp(attr
->name
, "job-uri") ||
8811 !strncmp(attr
->name
, "time-at-", 8))
8812 continue; /* Read-only attrs */
8814 if ((attr2
= ippFindAttribute(con
->request
, attr
->name
,
8815 IPP_TAG_ZERO
)) != NULL
)
8818 * Some other value; first free the old value...
8821 if (con
->request
->attrs
== attr2
)
8823 con
->request
->attrs
= attr2
->next
;
8828 for (prev2
= con
->request
->attrs
; prev2
; prev2
= prev2
->next
)
8829 if (prev2
->next
== attr2
)
8831 prev2
->next
= attr2
->next
;
8836 if (con
->request
->last
== attr2
)
8837 con
->request
->last
= prev2
;
8839 ippDeleteAttribute(NULL
, attr2
);
8843 * Add new option by copying it...
8846 ippCopyAttribute(con
->request
, attr
, 0);
8850 * Then free the attribute list and option array...
8854 cupsFreeOptions(num_options
, options
);
8859 * 'reject_jobs()' - Reject print jobs to a printer.
8863 reject_jobs(cupsd_client_t
*con
, /* I - Client connection */
8864 ipp_attribute_t
*uri
) /* I - Printer or class URI */
8866 http_status_t status
; /* Policy status */
8867 cups_ptype_t dtype
; /* Destination type (printer/class) */
8868 cupsd_printer_t
*printer
; /* Printer data */
8869 ipp_attribute_t
*attr
; /* printer-state-message text */
8872 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "reject_jobs(%p[%d], %s)", con
,
8873 con
->number
, uri
->values
[0].string
.text
);
8876 * Is the destination valid?
8879 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
8885 send_ipp_status(con
, IPP_NOT_FOUND
,
8886 _("The printer or class does not exist."));
8894 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
8896 send_http_error(con
, status
, printer
);
8901 * Reject jobs sent to the printer...
8904 printer
->accepting
= 0;
8906 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
8907 IPP_TAG_TEXT
)) == NULL
)
8908 strlcpy(printer
->state_message
, "Rejecting Jobs",
8909 sizeof(printer
->state_message
));
8911 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
8912 sizeof(printer
->state_message
));
8914 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE
, printer
, NULL
,
8915 "No longer accepting jobs.");
8917 if (dtype
& CUPS_PRINTER_CLASS
)
8919 cupsdMarkDirty(CUPSD_DIRTY_CLASSES
);
8921 cupsdLogMessage(CUPSD_LOG_INFO
, "Class \"%s\" rejecting jobs (\"%s\").",
8922 printer
->name
, get_username(con
));
8926 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS
);
8928 cupsdLogMessage(CUPSD_LOG_INFO
, "Printer \"%s\" rejecting jobs (\"%s\").",
8929 printer
->name
, get_username(con
));
8933 * Everything was ok, so return OK status...
8936 con
->response
->request
.status
.status_code
= IPP_OK
;
8941 * 'release_held_new_jobs()' - Release pending/new jobs on a printer or class.
8945 release_held_new_jobs(
8946 cupsd_client_t
*con
, /* I - Connection */
8947 ipp_attribute_t
*uri
) /* I - Printer URI */
8949 http_status_t status
; /* Policy status */
8950 cups_ptype_t dtype
; /* Destination type (printer/class) */
8951 cupsd_printer_t
*printer
; /* Printer data */
8954 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "release_held_new_jobs(%p[%d], %s)", con
,
8955 con
->number
, uri
->values
[0].string
.text
);
8958 * Is the destination valid?
8961 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
8967 send_ipp_status(con
, IPP_NOT_FOUND
,
8968 _("The printer or class does not exist."));
8976 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
8978 send_http_error(con
, status
, printer
);
8983 * Hold pending/new jobs sent to the printer...
8986 printer
->holding_new_jobs
= 0;
8988 cupsdSetPrinterReasons(printer
, "-hold-new-jobs");
8990 if (dtype
& CUPS_PRINTER_CLASS
)
8991 cupsdLogMessage(CUPSD_LOG_INFO
,
8992 "Class \"%s\" now printing pending/new jobs (\"%s\").",
8993 printer
->name
, get_username(con
));
8995 cupsdLogMessage(CUPSD_LOG_INFO
,
8996 "Printer \"%s\" now printing pending/new jobs (\"%s\").",
8997 printer
->name
, get_username(con
));
9002 * Everything was ok, so return OK status...
9005 con
->response
->request
.status
.status_code
= IPP_OK
;
9010 * 'release_job()' - Release a held print job.
9014 release_job(cupsd_client_t
*con
, /* I - Client connection */
9015 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
9017 ipp_attribute_t
*attr
; /* Current attribute */
9018 int jobid
; /* Job ID */
9019 char scheme
[HTTP_MAX_URI
], /* Method portion of URI */
9020 username
[HTTP_MAX_URI
], /* Username portion of URI */
9021 host
[HTTP_MAX_URI
], /* Host portion of URI */
9022 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
9023 int port
; /* Port portion of URI */
9024 cupsd_job_t
*job
; /* Job information */
9027 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "release_job(%p[%d], %s)", con
,
9028 con
->number
, uri
->values
[0].string
.text
);
9031 * See if we have a job URI or a printer URI...
9034 if (!strcmp(uri
->name
, "printer-uri"))
9037 * Got a printer URI; see if we also have a job-id attribute...
9040 if ((attr
= ippFindAttribute(con
->request
, "job-id",
9041 IPP_TAG_INTEGER
)) == NULL
)
9043 send_ipp_status(con
, IPP_BAD_REQUEST
,
9044 _("Got a printer-uri attribute but no job-id."));
9048 jobid
= attr
->values
[0].integer
;
9053 * Got a job URI; parse it to get the job ID...
9056 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
9057 sizeof(scheme
), username
, sizeof(username
), host
,
9058 sizeof(host
), &port
, resource
, sizeof(resource
));
9060 if (strncmp(resource
, "/jobs/", 6))
9066 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad job-uri \"%s\"."),
9067 uri
->values
[0].string
.text
);
9071 jobid
= atoi(resource
+ 6);
9075 * See if the job exists...
9078 if ((job
= cupsdFindJob(jobid
)) == NULL
)
9081 * Nope - return a "not found" error...
9084 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."), jobid
);
9089 * See if job is "held"...
9092 if (job
->state_value
!= IPP_JOB_HELD
)
9095 * Nope - return a "not possible" error...
9098 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("Job #%d is not held."), jobid
);
9103 * See if the job is owned by the requesting user...
9106 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
9108 send_http_error(con
, con
->username
[0] ? HTTP_FORBIDDEN
: HTTP_UNAUTHORIZED
,
9109 cupsdFindDest(job
->dest
));
9114 * Reset the job-hold-until value to "no-hold"...
9117 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
9118 IPP_TAG_KEYWORD
)) == NULL
)
9119 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
9123 ippSetValueTag(job
->attrs
, &attr
, IPP_TAG_KEYWORD
);
9124 ippSetString(job
->attrs
, &attr
, 0, "no-hold");
9126 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED
, cupsdFindDest(job
->dest
), job
,
9127 "Job job-hold-until value changed by user.");
9128 ippSetString(job
->attrs
, &job
->reasons
, 0, "none");
9132 * Release the job and return...
9135 cupsdReleaseJob(job
);
9137 cupsdAddEvent(CUPSD_EVENT_JOB_STATE
, cupsdFindDest(job
->dest
), job
,
9138 "Job released by user.");
9140 cupsdLogJob(job
, CUPSD_LOG_INFO
, "Released by \"%s\".", username
);
9142 con
->response
->request
.status
.status_code
= IPP_OK
;
9149 * 'renew_subscription()' - Renew an existing subscription...
9154 cupsd_client_t
*con
, /* I - Client connection */
9155 int sub_id
) /* I - Subscription ID */
9157 http_status_t status
; /* Policy status */
9158 cupsd_subscription_t
*sub
; /* Subscription */
9159 ipp_attribute_t
*lease
; /* notify-lease-duration */
9162 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
9163 "renew_subscription(con=%p[%d], sub_id=%d)",
9164 con
, con
->number
, sub_id
);
9167 * Is the subscription ID valid?
9170 if ((sub
= cupsdFindSubscription(sub_id
)) == NULL
)
9173 * Bad subscription ID...
9176 send_ipp_status(con
, IPP_NOT_FOUND
, _("Subscription #%d does not exist."),
9184 * Job subscriptions cannot be renewed...
9187 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
9188 _("Job subscriptions cannot be renewed."));
9196 if ((status
= cupsdCheckPolicy(sub
->dest
? sub
->dest
->op_policy_ptr
:
9198 con
, sub
->owner
)) != HTTP_OK
)
9200 send_http_error(con
, status
, sub
->dest
);
9205 * Renew the subscription...
9208 lease
= ippFindAttribute(con
->request
, "notify-lease-duration",
9211 sub
->lease
= lease
? lease
->values
[0].integer
: DefaultLeaseDuration
;
9213 if (MaxLeaseDuration
&& (sub
->lease
== 0 || sub
->lease
> MaxLeaseDuration
))
9215 cupsdLogMessage(CUPSD_LOG_INFO
,
9216 "renew_subscription: Limiting notify-lease-duration to "
9219 sub
->lease
= MaxLeaseDuration
;
9222 sub
->expire
= sub
->lease
? time(NULL
) + sub
->lease
: 0;
9224 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS
);
9226 con
->response
->request
.status
.status_code
= IPP_OK
;
9228 ippAddInteger(con
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
9229 "notify-lease-duration", sub
->lease
);
9234 * 'restart_job()' - Restart an old print job.
9238 restart_job(cupsd_client_t
*con
, /* I - Client connection */
9239 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
9241 ipp_attribute_t
*attr
; /* Current attribute */
9242 int jobid
; /* Job ID */
9243 cupsd_job_t
*job
; /* Job information */
9244 char scheme
[HTTP_MAX_URI
], /* Method portion of URI */
9245 username
[HTTP_MAX_URI
], /* Username portion of URI */
9246 host
[HTTP_MAX_URI
], /* Host portion of URI */
9247 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
9248 int port
; /* Port portion of URI */
9251 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "restart_job(%p[%d], %s)", con
,
9252 con
->number
, uri
->values
[0].string
.text
);
9255 * See if we have a job URI or a printer URI...
9258 if (!strcmp(uri
->name
, "printer-uri"))
9261 * Got a printer URI; see if we also have a job-id attribute...
9264 if ((attr
= ippFindAttribute(con
->request
, "job-id",
9265 IPP_TAG_INTEGER
)) == NULL
)
9267 send_ipp_status(con
, IPP_BAD_REQUEST
,
9268 _("Got a printer-uri attribute but no job-id."));
9272 jobid
= attr
->values
[0].integer
;
9277 * Got a job URI; parse it to get the job ID...
9280 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
9281 sizeof(scheme
), username
, sizeof(username
), host
,
9282 sizeof(host
), &port
, resource
, sizeof(resource
));
9284 if (strncmp(resource
, "/jobs/", 6))
9290 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad job-uri \"%s\"."),
9291 uri
->values
[0].string
.text
);
9295 jobid
= atoi(resource
+ 6);
9299 * See if the job exists...
9302 if ((job
= cupsdFindJob(jobid
)) == NULL
)
9305 * Nope - return a "not found" error...
9308 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."), jobid
);
9313 * See if job is in any of the "completed" states...
9316 if (job
->state_value
<= IPP_JOB_PROCESSING
)
9319 * Nope - return a "not possible" error...
9322 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("Job #%d is not complete."),
9328 * See if we have retained the job files...
9333 if (!job
->attrs
|| job
->num_files
== 0)
9336 * Nope - return a "not possible" error...
9339 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
9340 _("Job #%d cannot be restarted - no files."), jobid
);
9345 * See if the job is owned by the requesting user...
9348 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
9350 send_http_error(con
, con
->username
[0] ? HTTP_FORBIDDEN
: HTTP_UNAUTHORIZED
,
9351 cupsdFindDest(job
->dest
));
9356 * See if the job-hold-until attribute is specified...
9359 if ((attr
= ippFindAttribute(con
->request
, "job-hold-until",
9360 IPP_TAG_KEYWORD
)) == NULL
)
9361 attr
= ippFindAttribute(con
->request
, "job-hold-until", IPP_TAG_NAME
);
9363 if (attr
&& strcmp(attr
->values
[0].string
.text
, "no-hold"))
9366 * Return the job to a held state...
9369 cupsdLogJob(job
, CUPSD_LOG_DEBUG
,
9370 "Restarted by \"%s\" with job-hold-until=%s.",
9371 username
, attr
->values
[0].string
.text
);
9372 cupsdSetJobHoldUntil(job
, attr
->values
[0].string
.text
, 0);
9374 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED
| CUPSD_EVENT_JOB_STATE
,
9375 NULL
, job
, "Job restarted by user with job-hold-until=%s",
9376 attr
->values
[0].string
.text
);
9381 * Restart the job...
9384 cupsdRestartJob(job
);
9388 cupsdLogJob(job
, CUPSD_LOG_INFO
, "Restarted by \"%s\".", username
);
9390 con
->response
->request
.status
.status_code
= IPP_OK
;
9395 * 'save_auth_info()' - Save authentication information for a job.
9400 cupsd_client_t
*con
, /* I - Client connection */
9401 cupsd_job_t
*job
, /* I - Job */
9402 ipp_attribute_t
*auth_info
) /* I - auth-info attribute, if any */
9404 int i
; /* Looping var */
9405 char filename
[1024]; /* Job authentication filename */
9406 cups_file_t
*fp
; /* Job authentication file */
9407 char line
[65536]; /* Line for file */
9408 cupsd_printer_t
*dest
; /* Destination printer/class */
9412 * This function saves the in-memory authentication information for
9413 * a job so that it can be used to authenticate with a remote host.
9414 * The information is stored in a file that is readable only by the
9415 * root user. The fields are Base-64 encoded, each on a separate line,
9416 * followed by random number (up to 1024) of newlines to limit the
9417 * amount of information that is exposed.
9419 * Because of the potential for exposing of authentication information,
9420 * this functionality is only enabled when running cupsd as root.
9422 * This caching only works for the Basic and BasicDigest authentication
9423 * types. Digest authentication cannot be cached this way, and in
9424 * the future Kerberos authentication may make all of this obsolete.
9426 * Authentication information is saved whenever an authenticated
9427 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
9430 * This information is deleted after a job is completed or canceled,
9431 * so reprints may require subsequent re-authentication.
9437 if ((dest
= cupsdFindDest(job
->dest
)) == NULL
)
9441 * Create the authentication file and change permissions...
9444 snprintf(filename
, sizeof(filename
), "%s/a%05d", RequestRoot
, job
->id
);
9445 if ((fp
= cupsFileOpen(filename
, "w")) == NULL
)
9447 cupsdLogMessage(CUPSD_LOG_ERROR
,
9448 "Unable to save authentication info to \"%s\" - %s",
9449 filename
, strerror(errno
));
9453 fchown(cupsFileNumber(fp
), 0, 0);
9454 fchmod(cupsFileNumber(fp
), 0400);
9456 cupsFilePuts(fp
, "CUPSD-AUTH-V3\n");
9459 i
< (int)(sizeof(job
->auth_env
) / sizeof(job
->auth_env
[0]));
9461 cupsdClearString(job
->auth_env
+ i
);
9463 if (auth_info
&& auth_info
->num_values
== dest
->num_auth_info_required
)
9466 * Write 1 to 3 auth values...
9470 i
< auth_info
->num_values
&&
9471 i
< (int)(sizeof(job
->auth_env
) / sizeof(job
->auth_env
[0]));
9474 if (strcmp(dest
->auth_info_required
[i
], "negotiate"))
9476 httpEncode64_2(line
, sizeof(line
), auth_info
->values
[i
].string
.text
, (int)strlen(auth_info
->values
[i
].string
.text
));
9477 cupsFilePutConf(fp
, dest
->auth_info_required
[i
], line
);
9480 cupsFilePutConf(fp
, dest
->auth_info_required
[i
],
9481 auth_info
->values
[i
].string
.text
);
9483 if (!strcmp(dest
->auth_info_required
[i
], "username"))
9484 cupsdSetStringf(job
->auth_env
+ i
, "AUTH_USERNAME=%s",
9485 auth_info
->values
[i
].string
.text
);
9486 else if (!strcmp(dest
->auth_info_required
[i
], "domain"))
9487 cupsdSetStringf(job
->auth_env
+ i
, "AUTH_DOMAIN=%s",
9488 auth_info
->values
[i
].string
.text
);
9489 else if (!strcmp(dest
->auth_info_required
[i
], "password"))
9490 cupsdSetStringf(job
->auth_env
+ i
, "AUTH_PASSWORD=%s",
9491 auth_info
->values
[i
].string
.text
);
9492 else if (!strcmp(dest
->auth_info_required
[i
], "negotiate"))
9493 cupsdSetStringf(job
->auth_env
+ i
, "AUTH_NEGOTIATE=%s",
9494 auth_info
->values
[i
].string
.text
);
9499 else if (auth_info
&& auth_info
->num_values
== 2 &&
9500 dest
->num_auth_info_required
== 1 &&
9501 !strcmp(dest
->auth_info_required
[0], "negotiate"))
9504 * Allow fallback to username+password for Kerberized queues...
9507 httpEncode64_2(line
, sizeof(line
), auth_info
->values
[0].string
.text
, (int)strlen(auth_info
->values
[0].string
.text
));
9508 cupsFilePutConf(fp
, "username", line
);
9510 cupsdSetStringf(job
->auth_env
+ 0, "AUTH_USERNAME=%s",
9511 auth_info
->values
[0].string
.text
);
9513 httpEncode64_2(line
, sizeof(line
), auth_info
->values
[1].string
.text
, (int)strlen(auth_info
->values
[1].string
.text
));
9514 cupsFilePutConf(fp
, "password", line
);
9516 cupsdSetStringf(job
->auth_env
+ 1, "AUTH_PASSWORD=%s",
9517 auth_info
->values
[1].string
.text
);
9519 else if (con
->username
[0])
9522 * Write the authenticated username...
9525 httpEncode64_2(line
, sizeof(line
), con
->username
, (int)strlen(con
->username
));
9526 cupsFilePutConf(fp
, "username", line
);
9528 cupsdSetStringf(job
->auth_env
+ 0, "AUTH_USERNAME=%s", con
->username
);
9531 * Write the authenticated password...
9534 httpEncode64_2(line
, sizeof(line
), con
->password
, (int)strlen(con
->password
));
9535 cupsFilePutConf(fp
, "password", line
);
9537 cupsdSetStringf(job
->auth_env
+ 1, "AUTH_PASSWORD=%s", con
->password
);
9541 if (con
->gss_uid
> 0)
9543 cupsFilePrintf(fp
, "uid %d\n", (int)con
->gss_uid
);
9544 cupsdSetStringf(&job
->auth_uid
, "AUTH_UID=%d", (int)con
->gss_uid
);
9546 #endif /* HAVE_GSSAPI */
9549 * Write a random number of newlines to the end of the file...
9552 for (i
= (CUPS_RAND() % 1024); i
>= 0; i
--)
9553 cupsFilePutChar(fp
, '\n');
9556 * Close the file and return...
9564 * 'send_document()' - Send a file to a printer or class.
9568 send_document(cupsd_client_t
*con
, /* I - Client connection */
9569 ipp_attribute_t
*uri
) /* I - Printer URI */
9571 ipp_attribute_t
*attr
; /* Current attribute */
9572 ipp_attribute_t
*format
; /* Request's document-format attribute */
9573 ipp_attribute_t
*jformat
; /* Job's document-format attribute */
9574 const char *default_format
;/* document-format-default value */
9575 int jobid
; /* Job ID number */
9576 cupsd_job_t
*job
; /* Current job */
9577 char job_uri
[HTTP_MAX_URI
],
9579 scheme
[HTTP_MAX_URI
],
9580 /* Method portion of URI */
9581 username
[HTTP_MAX_URI
],
9582 /* Username portion of URI */
9584 /* Host portion of URI */
9585 resource
[HTTP_MAX_URI
];
9586 /* Resource portion of URI */
9587 int port
; /* Port portion of URI */
9588 mime_type_t
*filetype
; /* Type of file */
9589 char super
[MIME_MAX_SUPER
],
9590 /* Supertype of file */
9591 type
[MIME_MAX_TYPE
],
9592 /* Subtype of file */
9593 mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
9594 /* Textual name of mime type */
9595 char filename
[1024]; /* Job filename */
9596 cupsd_printer_t
*printer
; /* Current printer */
9597 struct stat fileinfo
; /* File information */
9598 int kbytes
; /* Size of file */
9599 int compression
; /* Type of compression */
9600 int start_job
; /* Start the job? */
9603 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "send_document(%p[%d], %s)", con
,
9604 con
->number
, uri
->values
[0].string
.text
);
9607 * See if we have a job URI or a printer URI...
9610 if (!strcmp(uri
->name
, "printer-uri"))
9613 * Got a printer URI; see if we also have a job-id attribute...
9616 if ((attr
= ippFindAttribute(con
->request
, "job-id",
9617 IPP_TAG_INTEGER
)) == NULL
)
9619 send_ipp_status(con
, IPP_BAD_REQUEST
,
9620 _("Got a printer-uri attribute but no job-id."));
9624 jobid
= attr
->values
[0].integer
;
9629 * Got a job URI; parse it to get the job ID...
9632 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
9633 sizeof(scheme
), username
, sizeof(username
), host
,
9634 sizeof(host
), &port
, resource
, sizeof(resource
));
9636 if (strncmp(resource
, "/jobs/", 6))
9642 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad job-uri \"%s\"."),
9643 uri
->values
[0].string
.text
);
9647 jobid
= atoi(resource
+ 6);
9651 * See if the job exists...
9654 if ((job
= cupsdFindJob(jobid
)) == NULL
)
9657 * Nope - return a "not found" error...
9660 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."), jobid
);
9664 printer
= cupsdFindDest(job
->dest
);
9667 * See if the job is owned by the requesting user...
9670 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
9672 send_http_error(con
, con
->username
[0] ? HTTP_FORBIDDEN
: HTTP_UNAUTHORIZED
,
9673 cupsdFindDest(job
->dest
));
9678 * OK, see if the client is sending the document compressed - CUPS
9679 * only supports "none" and "gzip".
9682 compression
= CUPS_FILE_NONE
;
9684 if ((attr
= ippFindAttribute(con
->request
, "compression",
9685 IPP_TAG_KEYWORD
)) != NULL
)
9687 if (strcmp(attr
->values
[0].string
.text
, "none")
9689 && strcmp(attr
->values
[0].string
.text
, "gzip")
9690 #endif /* HAVE_LIBZ */
9693 send_ipp_status(con
, IPP_ATTRIBUTES
, _("Unsupported compression \"%s\"."),
9694 attr
->values
[0].string
.text
);
9695 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
9696 "compression", NULL
, attr
->values
[0].string
.text
);
9701 if (!strcmp(attr
->values
[0].string
.text
, "gzip"))
9702 compression
= CUPS_FILE_GZIP
;
9703 #endif /* HAVE_LIBZ */
9707 * Do we have a file to print?
9710 if ((attr
= ippFindAttribute(con
->request
, "last-document",
9711 IPP_TAG_BOOLEAN
)) == NULL
)
9713 send_ipp_status(con
, IPP_BAD_REQUEST
,
9714 _("Missing last-document attribute in request."));
9721 * Check for an empty request with "last-document" set to true, which is
9722 * used to close an "open" job by RFC 2911, section 3.3.2.
9725 if (job
->num_files
> 0 && attr
->values
[0].boolean
)
9728 send_ipp_status(con
, IPP_BAD_REQUEST
, _("No file in print request."));
9733 * Is it a format we support?
9738 if ((format
= ippFindAttribute(con
->request
, "document-format",
9739 IPP_TAG_MIMETYPE
)) != NULL
)
9742 * Grab format from client...
9745 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%255[^;]",
9748 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad document-format \"%s\"."),
9749 format
->values
[0].string
.text
);
9753 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, ippGetString(format
, 0, NULL
));
9755 else if ((default_format
= cupsGetOption("document-format",
9756 printer
->num_options
,
9757 printer
->options
)) != NULL
)
9760 * Use default document format...
9763 if (sscanf(default_format
, "%15[^/]/%255[^;]", super
, type
) != 2)
9765 send_ipp_status(con
, IPP_BAD_REQUEST
,
9766 _("Bad document-format-default \"%s\"."), default_format
);
9773 * No document format attribute? Auto-type it!
9776 strlcpy(super
, "application", sizeof(super
));
9777 strlcpy(type
, "octet-stream", sizeof(type
));
9780 if (!strcmp(super
, "application") && !strcmp(type
, "octet-stream"))
9783 * Auto-type the file...
9786 ipp_attribute_t
*doc_name
; /* document-name attribute */
9789 cupsdLogJob(job
, CUPSD_LOG_DEBUG
, "Auto-typing file...");
9791 doc_name
= ippFindAttribute(con
->request
, "document-name", IPP_TAG_NAME
);
9792 filetype
= mimeFileType(MimeDatabase
, con
->filename
,
9793 doc_name
? doc_name
->values
[0].string
.text
: NULL
,
9797 filetype
= mimeType(MimeDatabase
, super
, type
);
9800 cupsdLogJob(job
, CUPSD_LOG_DEBUG
, "Request file type is %s/%s.",
9801 filetype
->super
, filetype
->type
);
9803 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
, filetype
->type
);
9804 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, mimetype
);
9807 filetype
= mimeType(MimeDatabase
, super
, type
);
9812 * Replace the document-format attribute value with the auto-typed or
9816 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
9819 if ((jformat
= ippFindAttribute(job
->attrs
, "document-format",
9820 IPP_TAG_MIMETYPE
)) != NULL
)
9821 ippSetString(job
->attrs
, &jformat
, 0, mimetype
);
9823 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
9824 "document-format", NULL
, mimetype
);
9828 send_ipp_status(con
, IPP_DOCUMENT_FORMAT
,
9829 _("Unsupported document-format \"%s/%s\"."), super
, type
);
9830 cupsdLogMessage(CUPSD_LOG_INFO
,
9831 "Hint: Do you have the raw file printing rules enabled?");
9834 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
9835 "document-format", NULL
, format
->values
[0].string
.text
);
9840 if (printer
->filetypes
&& !cupsArrayFind(printer
->filetypes
, filetype
))
9842 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
9845 send_ipp_status(con
, IPP_DOCUMENT_FORMAT
,
9846 _("Unsupported document-format \"%s\"."), mimetype
);
9848 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
9849 "document-format", NULL
, mimetype
);
9855 * Add the file to the job...
9858 if (add_file(con
, job
, filetype
, compression
))
9861 if ((attr
= ippFindAttribute(con
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
9862 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
9864 if (stat(con
->filename
, &fileinfo
))
9867 kbytes
= (fileinfo
.st_size
+ 1023) / 1024;
9869 cupsdUpdateQuota(printer
, job
->username
, 0, kbytes
);
9871 job
->koctets
+= kbytes
;
9873 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) != NULL
)
9874 attr
->values
[0].integer
+= kbytes
;
9876 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
, job
->num_files
);
9877 if (rename(con
->filename
, filename
))
9879 cupsdLogJob(job
, CUPSD_LOG_ERROR
, "Unable to rename job document file \"%s\": %s", filename
, strerror(errno
));
9881 send_ipp_status(con
, IPP_INTERNAL_ERROR
, _("Unable to rename job document file."));
9885 cupsdClearString(&con
->filename
);
9887 cupsdLogJob(job
, CUPSD_LOG_INFO
, "File of type %s/%s queued by \"%s\".",
9888 filetype
->super
, filetype
->type
, job
->username
);
9891 * Start the job if this is the last document...
9896 if ((attr
= ippFindAttribute(con
->request
, "last-document",
9897 IPP_TAG_BOOLEAN
)) != NULL
&&
9898 attr
->values
[0].boolean
)
9901 * See if we need to add the ending sheet...
9904 if (cupsdTimeoutJob(job
))
9907 if (job
->state_value
== IPP_JOB_STOPPED
)
9909 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
9910 job
->state_value
= IPP_JOB_PENDING
;
9912 ippSetString(job
->attrs
, &job
->reasons
, 0, "none");
9914 else if (job
->state_value
== IPP_JOB_HELD
)
9916 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
9917 IPP_TAG_KEYWORD
)) == NULL
)
9918 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
9920 if (!attr
|| !strcmp(attr
->values
[0].string
.text
, "no-hold"))
9922 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
9923 job
->state_value
= IPP_JOB_PENDING
;
9925 ippSetString(job
->attrs
, &job
->reasons
, 0, "none");
9928 ippSetString(job
->attrs
, &job
->reasons
, 0, "job-hold-until-specified");
9932 cupsdMarkDirty(CUPSD_DIRTY_JOBS
);
9938 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
9939 IPP_TAG_KEYWORD
)) == NULL
)
9940 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
9942 if (!attr
|| !strcmp(attr
->values
[0].string
.text
, "no-hold"))
9944 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
9945 job
->state_value
= IPP_JOB_HELD
;
9946 job
->hold_until
= time(NULL
) + MultipleOperationTimeout
;
9948 ippSetString(job
->attrs
, &job
->reasons
, 0, "job-incoming");
9951 cupsdMarkDirty(CUPSD_DIRTY_JOBS
);
9958 * Fill in the response info...
9961 httpAssembleURIf(HTTP_URI_CODING_ALL
, job_uri
, sizeof(job_uri
), "ipp", NULL
,
9962 con
->clientname
, con
->clientport
, "/jobs/%d", jobid
);
9963 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
,
9966 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", jobid
);
9968 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
9970 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "job-state-reasons",
9971 NULL
, job
->reasons
->values
[0].string
.text
);
9973 con
->response
->request
.status
.status_code
= IPP_OK
;
9976 * Start the job if necessary...
9985 * 'send_http_error()' - Send a HTTP error back to the IPP client.
9990 cupsd_client_t
*con
, /* I - Client connection */
9991 http_status_t status
, /* I - HTTP status code */
9992 cupsd_printer_t
*printer
) /* I - Printer, if any */
9994 ipp_attribute_t
*uri
; /* Request URI, if any */
9997 if ((uri
= ippFindAttribute(con
->request
, "printer-uri",
9998 IPP_TAG_URI
)) == NULL
)
9999 uri
= ippFindAttribute(con
->request
, "job-uri", IPP_TAG_URI
);
10001 cupsdLogMessage(status
== HTTP_FORBIDDEN
? CUPSD_LOG_ERROR
: CUPSD_LOG_DEBUG
,
10002 "[Client %d] Returning HTTP %s for %s (%s) from %s",
10003 con
->number
, httpStatus(status
),
10005 ippOpString(con
->request
->request
.op
.operation_id
) :
10007 uri
? uri
->values
[0].string
.text
: "no URI",
10008 con
->http
->hostname
);
10012 int auth_type
; /* Type of authentication required */
10015 auth_type
= CUPSD_AUTH_NONE
;
10017 if (status
== HTTP_UNAUTHORIZED
&&
10018 printer
->num_auth_info_required
> 0 &&
10019 !strcmp(printer
->auth_info_required
[0], "negotiate") &&
10021 (con
->request
->request
.op
.operation_id
== IPP_PRINT_JOB
||
10022 con
->request
->request
.op
.operation_id
== IPP_CREATE_JOB
||
10023 con
->request
->request
.op
.operation_id
== CUPS_AUTHENTICATE_JOB
))
10026 * Creating and authenticating jobs requires Kerberos...
10029 auth_type
= CUPSD_AUTH_NEGOTIATE
;
10034 * Use policy/location-defined authentication requirements...
10037 char resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
10038 cupsd_location_t
*auth
; /* Pointer to authentication element */
10041 if (printer
->type
& CUPS_PRINTER_CLASS
)
10042 snprintf(resource
, sizeof(resource
), "/classes/%s", printer
->name
);
10044 snprintf(resource
, sizeof(resource
), "/printers/%s", printer
->name
);
10046 if ((auth
= cupsdFindBest(resource
, HTTP_POST
)) == NULL
||
10047 auth
->type
== CUPSD_AUTH_NONE
)
10048 auth
= cupsdFindPolicyOp(printer
->op_policy_ptr
,
10050 con
->request
->request
.op
.operation_id
:
10055 if (auth
->type
== CUPSD_AUTH_DEFAULT
)
10056 auth_type
= cupsdDefaultAuthType();
10058 auth_type
= auth
->type
;
10062 cupsdSendError(con
, status
, auth_type
);
10065 cupsdSendError(con
, status
, CUPSD_AUTH_NONE
);
10067 ippDelete(con
->response
);
10068 con
->response
= NULL
;
10075 * 'send_ipp_status()' - Send a status back to the IPP client.
10079 send_ipp_status(cupsd_client_t
*con
, /* I - Client connection */
10080 ipp_status_t status
, /* I - IPP status code */
10081 const char *message
,/* I - Status message */
10082 ...) /* I - Additional args as needed */
10084 va_list ap
; /* Pointer to additional args */
10085 char formatted
[1024]; /* Formatted errror message */
10088 va_start(ap
, message
);
10089 vsnprintf(formatted
, sizeof(formatted
),
10090 _cupsLangString(con
->language
, message
), ap
);
10093 cupsdLogMessage(CUPSD_LOG_DEBUG
, "%s %s: %s",
10094 ippOpString(con
->request
->request
.op
.operation_id
),
10095 ippErrorString(status
), formatted
);
10097 con
->response
->request
.status
.status_code
= status
;
10099 if (ippFindAttribute(con
->response
, "attributes-charset",
10100 IPP_TAG_ZERO
) == NULL
)
10101 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
10102 "attributes-charset", NULL
, "utf-8");
10104 if (ippFindAttribute(con
->response
, "attributes-natural-language",
10105 IPP_TAG_ZERO
) == NULL
)
10106 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
10107 "attributes-natural-language", NULL
, DefaultLanguage
);
10109 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
10110 "status-message", NULL
, formatted
);
10115 * 'set_default()' - Set the default destination...
10119 set_default(cupsd_client_t
*con
, /* I - Client connection */
10120 ipp_attribute_t
*uri
) /* I - Printer URI */
10122 http_status_t status
; /* Policy status */
10123 cups_ptype_t dtype
; /* Destination type (printer/class) */
10124 cupsd_printer_t
*printer
, /* Printer */
10125 *oldprinter
; /* Old default printer */
10128 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "set_default(%p[%d], %s)", con
,
10129 con
->number
, uri
->values
[0].string
.text
);
10132 * Is the destination valid?
10135 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
10141 send_ipp_status(con
, IPP_NOT_FOUND
,
10142 _("The printer or class does not exist."));
10150 if ((status
= cupsdCheckPolicy(DefaultPolicyPtr
, con
, NULL
)) != HTTP_OK
)
10152 send_http_error(con
, status
, NULL
);
10157 * Set it as the default...
10160 oldprinter
= DefaultPrinter
;
10161 DefaultPrinter
= printer
;
10164 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE
, oldprinter
, NULL
,
10165 "%s is no longer the default printer.", oldprinter
->name
);
10167 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE
, printer
, NULL
,
10168 "%s is now the default printer.", printer
->name
);
10170 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS
| CUPSD_DIRTY_CLASSES
|
10171 CUPSD_DIRTY_PRINTCAP
);
10173 cupsdLogMessage(CUPSD_LOG_INFO
,
10174 "Default destination set to \"%s\" by \"%s\".",
10175 printer
->name
, get_username(con
));
10178 * Everything was ok, so return OK status...
10181 con
->response
->request
.status
.status_code
= IPP_OK
;
10186 * 'set_job_attrs()' - Set job attributes.
10190 set_job_attrs(cupsd_client_t
*con
, /* I - Client connection */
10191 ipp_attribute_t
*uri
) /* I - Job URI */
10193 ipp_attribute_t
*attr
, /* Current attribute */
10194 *attr2
; /* Job attribute */
10195 int jobid
; /* Job ID */
10196 cupsd_job_t
*job
; /* Current job */
10197 char scheme
[HTTP_MAX_URI
],
10198 /* Method portion of URI */
10199 username
[HTTP_MAX_URI
],
10200 /* Username portion of URI */
10201 host
[HTTP_MAX_URI
],
10202 /* Host portion of URI */
10203 resource
[HTTP_MAX_URI
];
10204 /* Resource portion of URI */
10205 int port
; /* Port portion of URI */
10206 int event
; /* Events? */
10207 int check_jobs
; /* Check jobs? */
10210 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "set_job_attrs(%p[%d], %s)", con
,
10211 con
->number
, uri
->values
[0].string
.text
);
10214 * Start with "everything is OK" status...
10217 con
->response
->request
.status
.status_code
= IPP_OK
;
10220 * See if we have a job URI or a printer URI...
10223 if (!strcmp(uri
->name
, "printer-uri"))
10226 * Got a printer URI; see if we also have a job-id attribute...
10229 if ((attr
= ippFindAttribute(con
->request
, "job-id",
10230 IPP_TAG_INTEGER
)) == NULL
)
10232 send_ipp_status(con
, IPP_BAD_REQUEST
,
10233 _("Got a printer-uri attribute but no job-id."));
10237 jobid
= attr
->values
[0].integer
;
10242 * Got a job URI; parse it to get the job ID...
10245 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
, scheme
,
10246 sizeof(scheme
), username
, sizeof(username
), host
,
10247 sizeof(host
), &port
, resource
, sizeof(resource
));
10249 if (strncmp(resource
, "/jobs/", 6))
10255 send_ipp_status(con
, IPP_BAD_REQUEST
, _("Bad job-uri \"%s\"."),
10256 uri
->values
[0].string
.text
);
10260 jobid
= atoi(resource
+ 6);
10264 * See if the job exists...
10267 if ((job
= cupsdFindJob(jobid
)) == NULL
)
10270 * Nope - return a "not found" error...
10273 send_ipp_status(con
, IPP_NOT_FOUND
, _("Job #%d does not exist."), jobid
);
10278 * See if the job has been completed...
10281 if (job
->state_value
> IPP_JOB_STOPPED
)
10284 * Return a "not-possible" error...
10287 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
10288 _("Job #%d is finished and cannot be altered."), jobid
);
10293 * See if the job is owned by the requesting user...
10296 if (!validate_user(job
, con
, job
->username
, username
, sizeof(username
)))
10298 send_http_error(con
, con
->username
[0] ? HTTP_FORBIDDEN
: HTTP_UNAUTHORIZED
,
10299 cupsdFindDest(job
->dest
));
10304 * See what the user wants to change.
10312 for (attr
= con
->request
->attrs
; attr
; attr
= attr
->next
)
10314 if (attr
->group_tag
!= IPP_TAG_JOB
|| !attr
->name
)
10317 if (!strcmp(attr
->name
, "attributes-charset") ||
10318 !strcmp(attr
->name
, "attributes-natural-language") ||
10319 !strncmp(attr
->name
, "date-time-at-", 13) ||
10320 !strncmp(attr
->name
, "document-compression", 20) ||
10321 !strncmp(attr
->name
, "document-format", 15) ||
10322 !strcmp(attr
->name
, "job-detailed-status-messages") ||
10323 !strcmp(attr
->name
, "job-document-access-errors") ||
10324 !strcmp(attr
->name
, "job-id") ||
10325 !strcmp(attr
->name
, "job-impressions-completed") ||
10326 !strcmp(attr
->name
, "job-k-octets-completed") ||
10327 !strcmp(attr
->name
, "job-media-sheets-completed") ||
10328 !strcmp(attr
->name
, "job-originating-host-name") ||
10329 !strcmp(attr
->name
, "job-originating-user-name") ||
10330 !strcmp(attr
->name
, "job-pages-completed") ||
10331 !strcmp(attr
->name
, "job-printer-up-time") ||
10332 !strcmp(attr
->name
, "job-printer-uri") ||
10333 !strcmp(attr
->name
, "job-sheets") ||
10334 !strcmp(attr
->name
, "job-state-message") ||
10335 !strcmp(attr
->name
, "job-state-reasons") ||
10336 !strcmp(attr
->name
, "job-uri") ||
10337 !strcmp(attr
->name
, "number-of-documents") ||
10338 !strcmp(attr
->name
, "number-of-intervening-jobs") ||
10339 !strcmp(attr
->name
, "output-device-assigned") ||
10340 !strncmp(attr
->name
, "time-at-", 8))
10346 send_ipp_status(con
, IPP_ATTRIBUTES_NOT_SETTABLE
,
10347 _("%s cannot be changed."), attr
->name
);
10349 attr2
= ippCopyAttribute(con
->response
, attr
, 0);
10350 ippSetGroupTag(con
->response
, &attr2
, IPP_TAG_UNSUPPORTED_GROUP
);
10354 if (!ippValidateAttribute(attr
))
10356 send_ipp_status(con
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, _("Bad '%s' value."), attr
->name
);
10357 ippCopyAttribute(con
->response
, attr
, 0);
10361 if (!strcmp(attr
->name
, "job-hold-until"))
10363 const char *when
= ippGetString(attr
, 0, NULL
);
10364 /* job-hold-until value */
10366 if ((ippGetValueTag(attr
) != IPP_TAG_KEYWORD
&& ippGetValueTag(attr
) != IPP_TAG_NAME
&& ippGetValueTag(attr
) != IPP_TAG_NAMELANG
) || ippGetCount(attr
) != 1)
10368 send_ipp_status(con
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, _("Unsupported 'job-hold-until' value."));
10369 ippCopyAttribute(con
->response
, attr
, 0);
10373 cupsdLogJob(job
, CUPSD_LOG_DEBUG
, "Setting job-hold-until to %s", when
);
10374 cupsdSetJobHoldUntil(job
, when
, 0);
10376 if (!strcmp(when
, "no-hold"))
10378 cupsdReleaseJob(job
);
10382 cupsdSetJobState(job
, IPP_JOB_HELD
, CUPSD_JOB_DEFAULT
, "Job held by \"%s\".", username
);
10384 event
|= CUPSD_EVENT_JOB_CONFIG_CHANGED
| CUPSD_EVENT_JOB_STATE
;
10386 else if (!strcmp(attr
->name
, "job-priority"))
10389 * Change the job priority...
10392 if (attr
->value_tag
!= IPP_TAG_INTEGER
)
10394 send_ipp_status(con
, IPP_REQUEST_VALUE
, _("Bad job-priority value."));
10396 attr2
= ippCopyAttribute(con
->response
, attr
, 0);
10397 ippSetGroupTag(con
->response
, &attr2
, IPP_TAG_UNSUPPORTED_GROUP
);
10399 else if (job
->state_value
>= IPP_JOB_PROCESSING
)
10401 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
10402 _("Job is completed and cannot be changed."));
10405 else if (con
->response
->request
.status
.status_code
== IPP_OK
)
10407 cupsdLogJob(job
, CUPSD_LOG_DEBUG
, "Setting job-priority to %d",
10408 attr
->values
[0].integer
);
10409 cupsdSetJobPriority(job
, attr
->values
[0].integer
);
10412 event
|= CUPSD_EVENT_JOB_CONFIG_CHANGED
|
10413 CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED
;
10416 else if (!strcmp(attr
->name
, "job-state"))
10419 * Change the job state...
10422 if (attr
->value_tag
!= IPP_TAG_ENUM
)
10424 send_ipp_status(con
, IPP_REQUEST_VALUE
, _("Bad job-state value."));
10426 attr2
= ippCopyAttribute(con
->response
, attr
, 0);
10427 ippSetGroupTag(con
->response
, &attr2
, IPP_TAG_UNSUPPORTED_GROUP
);
10431 switch (attr
->values
[0].integer
)
10433 case IPP_JOB_PENDING
:
10434 case IPP_JOB_HELD
:
10435 if (job
->state_value
> IPP_JOB_HELD
)
10437 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
10438 _("Job state cannot be changed."));
10441 else if (con
->response
->request
.status
.status_code
== IPP_OK
)
10443 cupsdLogJob(job
, CUPSD_LOG_DEBUG
, "Setting job-state to %d",
10444 attr
->values
[0].integer
);
10445 cupsdSetJobState(job
, (ipp_jstate_t
)attr
->values
[0].integer
, CUPSD_JOB_DEFAULT
, "Job state changed by \"%s\"", username
);
10450 case IPP_JOB_PROCESSING
:
10451 case IPP_JOB_STOPPED
:
10452 if (job
->state_value
!= attr
->values
[0].integer
)
10454 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
10455 _("Job state cannot be changed."));
10460 case IPP_JOB_CANCELED
:
10461 case IPP_JOB_ABORTED
:
10462 case IPP_JOB_COMPLETED
:
10463 if (job
->state_value
> IPP_JOB_PROCESSING
)
10465 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
10466 _("Job state cannot be changed."));
10469 else if (con
->response
->request
.status
.status_code
== IPP_OK
)
10471 cupsdLogJob(job
, CUPSD_LOG_DEBUG
, "Setting job-state to %d",
10472 attr
->values
[0].integer
);
10473 cupsdSetJobState(job
, (ipp_jstate_t
)attr
->values
[0].integer
,
10475 "Job state changed by \"%s\"", username
);
10482 else if (con
->response
->request
.status
.status_code
!= IPP_OK
)
10484 else if ((attr2
= ippFindAttribute(job
->attrs
, attr
->name
,
10485 IPP_TAG_ZERO
)) != NULL
)
10488 * Some other value; first free the old value...
10491 if (job
->attrs
->prev
)
10492 job
->attrs
->prev
->next
= attr2
->next
;
10494 job
->attrs
->attrs
= attr2
->next
;
10496 if (job
->attrs
->last
== attr2
)
10497 job
->attrs
->last
= job
->attrs
->prev
;
10499 ippDeleteAttribute(NULL
, attr2
);
10502 * Then copy the attribute...
10505 ippCopyAttribute(job
->attrs
, attr
, 0);
10507 else if (attr
->value_tag
== IPP_TAG_DELETEATTR
)
10510 * Delete the attribute...
10513 if ((attr2
= ippFindAttribute(job
->attrs
, attr
->name
,
10514 IPP_TAG_ZERO
)) != NULL
)
10516 if (job
->attrs
->prev
)
10517 job
->attrs
->prev
->next
= attr2
->next
;
10519 job
->attrs
->attrs
= attr2
->next
;
10521 if (attr2
== job
->attrs
->last
)
10522 job
->attrs
->last
= job
->attrs
->prev
;
10524 ippDeleteAttribute(NULL
, attr2
);
10526 event
|= CUPSD_EVENT_JOB_CONFIG_CHANGED
;
10532 * Add new option by copying it...
10535 ippCopyAttribute(job
->attrs
, attr
, 0);
10537 event
|= CUPSD_EVENT_JOB_CONFIG_CHANGED
;
10546 cupsdMarkDirty(CUPSD_DIRTY_JOBS
);
10549 * Send events as needed...
10552 if (event
& CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED
)
10553 cupsdAddEvent(CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED
,
10554 cupsdFindDest(job
->dest
), job
,
10555 "Job priority changed by user.");
10557 if (event
& CUPSD_EVENT_JOB_STATE
)
10558 cupsdAddEvent(CUPSD_EVENT_JOB_STATE
, cupsdFindDest(job
->dest
), job
,
10559 job
->state_value
== IPP_JOB_HELD
?
10560 "Job held by user." : "Job restarted by user.");
10562 if (event
& CUPSD_EVENT_JOB_CONFIG_CHANGED
)
10563 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED
, cupsdFindDest(job
->dest
), job
,
10564 "Job options changed by user.");
10567 * Start jobs if possible...
10576 * 'set_printer_attrs()' - Set printer attributes.
10580 set_printer_attrs(cupsd_client_t
*con
, /* I - Client connection */
10581 ipp_attribute_t
*uri
) /* I - Printer */
10583 http_status_t status
; /* Policy status */
10584 cups_ptype_t dtype
; /* Destination type (printer/class) */
10585 cupsd_printer_t
*printer
; /* Printer/class */
10586 ipp_attribute_t
*attr
; /* Printer attribute */
10587 int changed
= 0; /* Was anything changed? */
10590 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "set_printer_attrs(%p[%d], %s)", con
,
10591 con
->number
, uri
->values
[0].string
.text
);
10594 * Is the destination valid?
10597 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
10603 send_ipp_status(con
, IPP_NOT_FOUND
,
10604 _("The printer or class does not exist."));
10612 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
10614 send_http_error(con
, status
, printer
);
10619 * Return a list of attributes that can be set via Set-Printer-Attributes.
10622 if ((attr
= ippFindAttribute(con
->request
, "printer-location",
10623 IPP_TAG_TEXT
)) != NULL
)
10625 cupsdSetString(&printer
->location
, attr
->values
[0].string
.text
);
10629 if ((attr
= ippFindAttribute(con
->request
, "printer-geo-location", IPP_TAG_URI
)) != NULL
&& !strncmp(attr
->values
[0].string
.text
, "geo:", 4))
10631 cupsdSetString(&printer
->geo_location
, attr
->values
[0].string
.text
);
10635 if ((attr
= ippFindAttribute(con
->request
, "printer-organization", IPP_TAG_TEXT
)) != NULL
)
10637 cupsdSetString(&printer
->organization
, attr
->values
[0].string
.text
);
10641 if ((attr
= ippFindAttribute(con
->request
, "printer-organizational-unit", IPP_TAG_TEXT
)) != NULL
)
10643 cupsdSetString(&printer
->organizational_unit
, attr
->values
[0].string
.text
);
10647 if ((attr
= ippFindAttribute(con
->request
, "printer-info",
10648 IPP_TAG_TEXT
)) != NULL
)
10650 cupsdSetString(&printer
->info
, attr
->values
[0].string
.text
);
10655 * Update the printer attributes and return...
10660 printer
->config_time
= time(NULL
);
10662 cupsdSetPrinterAttrs(printer
);
10663 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS
);
10665 cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG
, printer
, NULL
,
10666 "Printer \"%s\" description or location changed by \"%s\".",
10667 printer
->name
, get_username(con
));
10669 cupsdLogMessage(CUPSD_LOG_INFO
,
10670 "Printer \"%s\" description or location changed by \"%s\".",
10671 printer
->name
, get_username(con
));
10674 con
->response
->request
.status
.status_code
= IPP_OK
;
10679 * 'set_printer_defaults()' - Set printer default options from a request.
10682 static int /* O - 1 on success, 0 on failure */
10683 set_printer_defaults(
10684 cupsd_client_t
*con
, /* I - Client connection */
10685 cupsd_printer_t
*printer
) /* I - Printer */
10687 int i
; /* Looping var */
10688 ipp_attribute_t
*attr
; /* Current attribute */
10689 size_t namelen
; /* Length of attribute name */
10690 char name
[256], /* New attribute name */
10691 value
[256]; /* String version of integer attrs */
10694 for (attr
= con
->request
->attrs
; attr
; attr
= attr
->next
)
10697 * Skip non-printer attributes...
10700 if (attr
->group_tag
!= IPP_TAG_PRINTER
|| !attr
->name
)
10703 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "set_printer_defaults: %s", attr
->name
);
10705 if (!strcmp(attr
->name
, "job-sheets-default"))
10708 * Only allow keywords and names...
10711 if (attr
->value_tag
!= IPP_TAG_NAME
&& attr
->value_tag
!= IPP_TAG_KEYWORD
)
10715 * Only allow job-sheets-default to be set when running without a
10716 * system high classification level...
10719 if (Classification
)
10722 cupsdSetString(&printer
->job_sheets
[0], attr
->values
[0].string
.text
);
10724 if (attr
->num_values
> 1)
10725 cupsdSetString(&printer
->job_sheets
[1], attr
->values
[1].string
.text
);
10727 cupsdSetString(&printer
->job_sheets
[1], "none");
10729 else if (!strcmp(attr
->name
, "requesting-user-name-allowed"))
10731 cupsdFreeStrings(&(printer
->users
));
10733 printer
->deny_users
= 0;
10735 if (attr
->value_tag
== IPP_TAG_NAME
&&
10736 (attr
->num_values
> 1 ||
10737 strcmp(attr
->values
[0].string
.text
, "all")))
10739 for (i
= 0; i
< attr
->num_values
; i
++)
10740 cupsdAddString(&(printer
->users
), attr
->values
[i
].string
.text
);
10743 else if (!strcmp(attr
->name
, "requesting-user-name-denied"))
10745 cupsdFreeStrings(&(printer
->users
));
10747 printer
->deny_users
= 1;
10749 if (attr
->value_tag
== IPP_TAG_NAME
&&
10750 (attr
->num_values
> 1 ||
10751 strcmp(attr
->values
[0].string
.text
, "none")))
10753 for (i
= 0; i
< attr
->num_values
; i
++)
10754 cupsdAddString(&(printer
->users
), attr
->values
[i
].string
.text
);
10757 else if (!strcmp(attr
->name
, "job-quota-period"))
10759 if (attr
->value_tag
!= IPP_TAG_INTEGER
)
10762 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Setting job-quota-period to %d...",
10763 attr
->values
[0].integer
);
10764 cupsdFreeQuotas(printer
);
10766 printer
->quota_period
= attr
->values
[0].integer
;
10768 else if (!strcmp(attr
->name
, "job-k-limit"))
10770 if (attr
->value_tag
!= IPP_TAG_INTEGER
)
10773 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Setting job-k-limit to %d...",
10774 attr
->values
[0].integer
);
10775 cupsdFreeQuotas(printer
);
10777 printer
->k_limit
= attr
->values
[0].integer
;
10779 else if (!strcmp(attr
->name
, "job-page-limit"))
10781 if (attr
->value_tag
!= IPP_TAG_INTEGER
)
10784 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Setting job-page-limit to %d...",
10785 attr
->values
[0].integer
);
10786 cupsdFreeQuotas(printer
);
10788 printer
->page_limit
= attr
->values
[0].integer
;
10790 else if (!strcmp(attr
->name
, "printer-op-policy"))
10792 cupsd_policy_t
*p
; /* Policy */
10795 if (attr
->value_tag
!= IPP_TAG_NAME
)
10798 if ((p
= cupsdFindPolicy(attr
->values
[0].string
.text
)) != NULL
)
10800 cupsdLogMessage(CUPSD_LOG_DEBUG
,
10801 "Setting printer-op-policy to \"%s\"...",
10802 attr
->values
[0].string
.text
);
10803 cupsdSetString(&printer
->op_policy
, attr
->values
[0].string
.text
);
10804 printer
->op_policy_ptr
= p
;
10808 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
10809 _("Unknown printer-op-policy \"%s\"."),
10810 attr
->values
[0].string
.text
);
10814 else if (!strcmp(attr
->name
, "printer-error-policy"))
10816 if (attr
->value_tag
!= IPP_TAG_NAME
&& attr
->value_tag
!= IPP_TAG_KEYWORD
)
10819 if (strcmp(attr
->values
[0].string
.text
, "retry-current-job") &&
10820 ((printer
->type
& CUPS_PRINTER_CLASS
) ||
10821 (strcmp(attr
->values
[0].string
.text
, "abort-job") &&
10822 strcmp(attr
->values
[0].string
.text
, "retry-job") &&
10823 strcmp(attr
->values
[0].string
.text
, "stop-printer"))))
10825 send_ipp_status(con
, IPP_NOT_POSSIBLE
,
10826 _("Unknown printer-error-policy \"%s\"."),
10827 attr
->values
[0].string
.text
);
10831 cupsdLogMessage(CUPSD_LOG_DEBUG
,
10832 "Setting printer-error-policy to \"%s\"...",
10833 attr
->values
[0].string
.text
);
10834 cupsdSetString(&printer
->error_policy
, attr
->values
[0].string
.text
);
10838 * Skip any other non-default attributes...
10841 namelen
= strlen(attr
->name
);
10842 if (namelen
< 9 || strcmp(attr
->name
+ namelen
- 8, "-default") ||
10843 namelen
> (sizeof(name
) - 1) || attr
->num_values
!= 1)
10847 * OK, anything else must be a user-defined default...
10850 strlcpy(name
, attr
->name
, sizeof(name
));
10851 name
[namelen
- 8] = '\0'; /* Strip "-default" */
10853 switch (attr
->value_tag
)
10855 case IPP_TAG_DELETEATTR
:
10856 printer
->num_options
= cupsRemoveOption(name
,
10857 printer
->num_options
,
10858 &(printer
->options
));
10859 cupsdLogMessage(CUPSD_LOG_DEBUG
,
10860 "Deleting %s", attr
->name
);
10863 case IPP_TAG_NAME
:
10864 case IPP_TAG_TEXT
:
10865 case IPP_TAG_KEYWORD
:
10867 printer
->num_options
= cupsAddOption(name
,
10868 attr
->values
[0].string
.text
,
10869 printer
->num_options
,
10870 &(printer
->options
));
10871 cupsdLogMessage(CUPSD_LOG_DEBUG
,
10872 "Setting %s to \"%s\"...", attr
->name
,
10873 attr
->values
[0].string
.text
);
10876 case IPP_TAG_BOOLEAN
:
10877 printer
->num_options
= cupsAddOption(name
,
10878 attr
->values
[0].boolean
?
10880 printer
->num_options
,
10881 &(printer
->options
));
10882 cupsdLogMessage(CUPSD_LOG_DEBUG
,
10883 "Setting %s to %s...", attr
->name
,
10884 attr
->values
[0].boolean
? "true" : "false");
10887 case IPP_TAG_INTEGER
:
10888 case IPP_TAG_ENUM
:
10889 sprintf(value
, "%d", attr
->values
[0].integer
);
10890 printer
->num_options
= cupsAddOption(name
, value
,
10891 printer
->num_options
,
10892 &(printer
->options
));
10893 cupsdLogMessage(CUPSD_LOG_DEBUG
,
10894 "Setting %s to %s...", attr
->name
, value
);
10897 case IPP_TAG_RANGE
:
10898 sprintf(value
, "%d-%d", attr
->values
[0].range
.lower
,
10899 attr
->values
[0].range
.upper
);
10900 printer
->num_options
= cupsAddOption(name
, value
,
10901 printer
->num_options
,
10902 &(printer
->options
));
10903 cupsdLogMessage(CUPSD_LOG_DEBUG
,
10904 "Setting %s to %s...", attr
->name
, value
);
10907 case IPP_TAG_RESOLUTION
:
10908 sprintf(value
, "%dx%d%s", attr
->values
[0].resolution
.xres
,
10909 attr
->values
[0].resolution
.yres
,
10910 attr
->values
[0].resolution
.units
== IPP_RES_PER_INCH
?
10912 printer
->num_options
= cupsAddOption(name
, value
,
10913 printer
->num_options
,
10914 &(printer
->options
));
10915 cupsdLogMessage(CUPSD_LOG_DEBUG
,
10916 "Setting %s to %s...", attr
->name
, value
);
10920 /* Do nothing for other values */
10930 * 'start_printer()' - Start a printer.
10934 start_printer(cupsd_client_t
*con
, /* I - Client connection */
10935 ipp_attribute_t
*uri
) /* I - Printer URI */
10937 int i
; /* Temporary variable */
10938 http_status_t status
; /* Policy status */
10939 cups_ptype_t dtype
; /* Destination type (printer/class) */
10940 cupsd_printer_t
*printer
; /* Printer data */
10943 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_printer(%p[%d], %s)", con
,
10944 con
->number
, uri
->values
[0].string
.text
);
10947 * Is the destination valid?
10950 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
10956 send_ipp_status(con
, IPP_NOT_FOUND
,
10957 _("The printer or class does not exist."));
10965 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
10967 send_http_error(con
, status
, printer
);
10972 * Start the printer...
10975 printer
->state_message
[0] = '\0';
10977 cupsdStartPrinter(printer
, 1);
10979 if (dtype
& CUPS_PRINTER_CLASS
)
10980 cupsdLogMessage(CUPSD_LOG_INFO
, "Class \"%s\" started by \"%s\".",
10981 printer
->name
, get_username(con
));
10983 cupsdLogMessage(CUPSD_LOG_INFO
, "Printer \"%s\" started by \"%s\".",
10984 printer
->name
, get_username(con
));
10992 if ((i
= check_quotas(con
, printer
)) < 0)
10994 send_ipp_status(con
, IPP_NOT_POSSIBLE
, _("Quota limit reached."));
10999 send_ipp_status(con
, IPP_NOT_AUTHORIZED
, _("Not allowed to print."));
11004 * Everything was ok, so return OK status...
11007 con
->response
->request
.status
.status_code
= IPP_OK
;
11012 * 'stop_printer()' - Stop a printer.
11016 stop_printer(cupsd_client_t
*con
, /* I - Client connection */
11017 ipp_attribute_t
*uri
) /* I - Printer URI */
11019 http_status_t status
; /* Policy status */
11020 cups_ptype_t dtype
; /* Destination type (printer/class) */
11021 cupsd_printer_t
*printer
; /* Printer data */
11022 ipp_attribute_t
*attr
; /* printer-state-message attribute */
11025 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "stop_printer(%p[%d], %s)", con
,
11026 con
->number
, uri
->values
[0].string
.text
);
11029 * Is the destination valid?
11032 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
11038 send_ipp_status(con
, IPP_NOT_FOUND
,
11039 _("The printer or class does not exist."));
11047 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
11049 send_http_error(con
, status
, printer
);
11054 * Stop the printer...
11057 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
11058 IPP_TAG_TEXT
)) == NULL
)
11059 strlcpy(printer
->state_message
, "Paused", sizeof(printer
->state_message
));
11062 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
11063 sizeof(printer
->state_message
));
11066 cupsdStopPrinter(printer
, 1);
11068 if (dtype
& CUPS_PRINTER_CLASS
)
11069 cupsdLogMessage(CUPSD_LOG_INFO
, "Class \"%s\" stopped by \"%s\".",
11070 printer
->name
, get_username(con
));
11072 cupsdLogMessage(CUPSD_LOG_INFO
, "Printer \"%s\" stopped by \"%s\".",
11073 printer
->name
, get_username(con
));
11076 * Everything was ok, so return OK status...
11079 con
->response
->request
.status
.status_code
= IPP_OK
;
11084 * 'url_encode_attr()' - URL-encode a string attribute.
11088 url_encode_attr(ipp_attribute_t
*attr
, /* I - Attribute */
11089 char *buffer
,/* I - String buffer */
11090 size_t bufsize
)/* I - Size of buffer */
11092 int i
; /* Looping var */
11093 char *bufptr
, /* Pointer into buffer */
11094 *bufend
; /* End of buffer */
11097 strlcpy(buffer
, attr
->name
, bufsize
);
11098 bufptr
= buffer
+ strlen(buffer
);
11099 bufend
= buffer
+ bufsize
- 1;
11101 for (i
= 0; i
< attr
->num_values
; i
++)
11103 if (bufptr
>= bufend
)
11111 if (bufptr
>= bufend
)
11116 bufptr
= url_encode_string(attr
->values
[i
].string
.text
, bufptr
, (size_t)(bufend
- bufptr
+ 1));
11118 if (bufptr
>= bufend
)
11129 * 'url_encode_string()' - URL-encode a string.
11132 static char * /* O - End of string */
11133 url_encode_string(const char *s
, /* I - String */
11134 char *buffer
, /* I - String buffer */
11135 size_t bufsize
) /* I - Size of buffer */
11137 char *bufptr
, /* Pointer into buffer */
11138 *bufend
; /* End of buffer */
11139 static const char *hex
= "0123456789ABCDEF";
11144 bufend
= buffer
+ bufsize
- 1;
11146 while (*s
&& bufptr
< bufend
)
11148 if (*s
== ' ' || *s
== '%' || *s
== '+')
11150 if (bufptr
>= (bufend
- 2))
11154 *bufptr
++ = hex
[(*s
>> 4) & 15];
11155 *bufptr
++ = hex
[*s
& 15];
11159 else if (*s
== '\'' || *s
== '\\')
11161 if (bufptr
>= (bufend
- 1))
11178 * 'user_allowed()' - See if a user is allowed to print to a queue.
11181 static int /* O - 0 if not allowed, 1 if allowed */
11182 user_allowed(cupsd_printer_t
*p
, /* I - Printer or class */
11183 const char *username
) /* I - Username */
11185 struct passwd
*pw
; /* User password data */
11186 char baseuser
[256], /* Base username */
11187 *baseptr
, /* Pointer to "@" in base username */
11188 *name
; /* Current user name */
11191 if (cupsArrayCount(p
->users
) == 0)
11194 if (!strcmp(username
, "root"))
11197 if (strchr(username
, '@'))
11200 * Strip @REALM for username check...
11203 strlcpy(baseuser
, username
, sizeof(baseuser
));
11205 if ((baseptr
= strchr(baseuser
, '@')) != NULL
)
11208 username
= baseuser
;
11211 pw
= getpwnam(username
);
11214 for (name
= (char *)cupsArrayFirst(p
->users
);
11216 name
= (char *)cupsArrayNext(p
->users
))
11218 if (name
[0] == '@')
11221 * Check group membership...
11224 if (cupsdCheckGroup(username
, pw
, name
+ 1))
11227 else if (name
[0] == '#')
11233 if (cupsdCheckGroup(username
, pw
, name
))
11236 else if (!_cups_strcasecmp(username
, name
))
11240 return ((name
!= NULL
) != p
->deny_users
);
11245 * 'validate_job()' - Validate printer options and destination.
11249 validate_job(cupsd_client_t
*con
, /* I - Client connection */
11250 ipp_attribute_t
*uri
) /* I - Printer URI */
11252 http_status_t status
; /* Policy status */
11253 ipp_attribute_t
*attr
; /* Current attribute */
11255 ipp_attribute_t
*auth_info
; /* auth-info attribute */
11256 #endif /* HAVE_SSL */
11257 ipp_attribute_t
*format
, /* Document-format attribute */
11258 *name
; /* Job-name attribute */
11259 cups_ptype_t dtype
; /* Destination type (printer/class) */
11260 char super
[MIME_MAX_SUPER
],
11261 /* Supertype of file */
11262 type
[MIME_MAX_TYPE
];
11263 /* Subtype of file */
11264 cupsd_printer_t
*printer
; /* Printer */
11267 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "validate_job(%p[%d], %s)", con
,
11268 con
->number
, uri
->values
[0].string
.text
);
11271 * OK, see if the client is sending the document compressed - CUPS
11272 * doesn't support compression yet...
11275 if ((attr
= ippFindAttribute(con
->request
, "compression",
11276 IPP_TAG_KEYWORD
)) != NULL
)
11278 if (strcmp(attr
->values
[0].string
.text
, "none")
11280 && strcmp(attr
->values
[0].string
.text
, "gzip")
11281 #endif /* HAVE_LIBZ */
11284 send_ipp_status(con
, IPP_ATTRIBUTES
,
11285 _("Unsupported 'compression' value \"%s\"."),
11286 attr
->values
[0].string
.text
);
11287 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
11288 "compression", NULL
, attr
->values
[0].string
.text
);
11294 * Is it a format we support?
11297 if ((format
= ippFindAttribute(con
->request
, "document-format",
11298 IPP_TAG_MIMETYPE
)) != NULL
)
11300 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%255[^;]",
11303 send_ipp_status(con
, IPP_BAD_REQUEST
,
11304 _("Bad 'document-format' value \"%s\"."),
11305 format
->values
[0].string
.text
);
11309 if ((strcmp(super
, "application") || strcmp(type
, "octet-stream")) &&
11310 !mimeType(MimeDatabase
, super
, type
))
11312 cupsdLogMessage(CUPSD_LOG_INFO
,
11313 "Hint: Do you have the raw file printing rules enabled?");
11314 send_ipp_status(con
, IPP_DOCUMENT_FORMAT
,
11315 _("Unsupported 'document-format' value \"%s\"."),
11316 format
->values
[0].string
.text
);
11317 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
11318 "document-format", NULL
, format
->values
[0].string
.text
);
11324 * Is the job-hold-until value valid?
11327 if ((attr
= ippFindAttribute(con
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
&& ((ippGetValueTag(attr
) != IPP_TAG_KEYWORD
&& ippGetValueTag(attr
) != IPP_TAG_NAME
&& ippGetValueTag(attr
) != IPP_TAG_NAMELANG
) || ippGetCount(attr
) != 1 || !ippValidateAttribute(attr
)))
11329 send_ipp_status(con
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, _("Unsupported 'job-hold-until' value."));
11330 ippCopyAttribute(con
->response
, attr
, 0);
11335 * Is the job-name valid?
11338 if ((name
= ippFindAttribute(con
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
11340 if ((name
->value_tag
!= IPP_TAG_NAME
&& name
->value_tag
!= IPP_TAG_NAMELANG
) ||
11341 name
->num_values
!= 1 || !ippValidateAttribute(name
))
11343 if (StrictConformance
)
11345 send_ipp_status(con
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, _("Unsupported 'job-name' value."));
11346 ippCopyAttribute(con
->response
, name
, 0);
11351 cupsdLogMessage(CUPSD_LOG_WARN
, "Unsupported 'job-name' value, deleting from request.");
11352 ippDeleteAttribute(con
->request
, name
);
11358 * Is the destination valid?
11361 if (!cupsdValidateDest(uri
->values
[0].string
.text
, &dtype
, &printer
))
11367 send_ipp_status(con
, IPP_NOT_FOUND
,
11368 _("The printer or class does not exist."));
11377 auth_info
= ippFindAttribute(con
->request
, "auth-info", IPP_TAG_TEXT
);
11378 #endif /* HAVE_SSL */
11380 if ((status
= cupsdCheckPolicy(printer
->op_policy_ptr
, con
, NULL
)) != HTTP_OK
)
11382 send_http_error(con
, status
, printer
);
11385 else if (printer
->num_auth_info_required
== 1 &&
11386 !strcmp(printer
->auth_info_required
[0], "negotiate") &&
11389 send_http_error(con
, HTTP_UNAUTHORIZED
, printer
);
11393 else if (auth_info
&& !con
->http
->tls
&&
11394 !httpAddrLocalhost(con
->http
->hostaddr
))
11397 * Require encryption of auth-info over non-local connections...
11400 send_http_error(con
, HTTP_UPGRADE_REQUIRED
, printer
);
11403 #endif /* HAVE_SSL */
11406 * Everything was ok, so return OK status...
11409 con
->response
->request
.status
.status_code
= IPP_OK
;
11414 * 'validate_name()' - Make sure the printer name only contains valid chars.
11417 static int /* O - 0 if name is no good, 1 if good */
11418 validate_name(const char *name
) /* I - Name to check */
11420 const char *ptr
; /* Pointer into name */
11424 * Scan the whole name...
11427 for (ptr
= name
; *ptr
; ptr
++)
11428 if ((*ptr
> 0 && *ptr
<= ' ') || *ptr
== 127 || *ptr
== '/' || *ptr
== '#')
11432 * All the characters are good; validate the length, too...
11435 return ((ptr
- name
) < 128);
11440 * 'validate_user()' - Validate the user for the request.
11443 static int /* O - 1 if permitted, 0 otherwise */
11444 validate_user(cupsd_job_t
*job
, /* I - Job */
11445 cupsd_client_t
*con
, /* I - Client connection */
11446 const char *owner
, /* I - Owner of job/resource */
11447 char *username
, /* O - Authenticated username */
11448 size_t userlen
) /* I - Length of username */
11450 cupsd_printer_t
*printer
; /* Printer for job */
11453 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, userlen=" CUPS_LLFMT
")", job
->id
, con
? con
->number
: 0, owner
? owner
: "(null)", username
, CUPS_LLCAST userlen
);
11456 * Validate input...
11459 if (!con
|| !owner
|| !username
|| userlen
<= 0)
11463 * Get the best authenticated username that is available.
11466 strlcpy(username
, get_username(con
), userlen
);
11469 * Check the username against the owner...
11472 printer
= cupsdFindDest(job
->dest
);
11474 return (cupsdCheckPolicy(printer
? printer
->op_policy_ptr
: DefaultPolicyPtr
,
11475 con
, owner
) == HTTP_OK
);