2 * "$Id: ipp.c 10274 2012-02-13 20:42:51Z mike $"
4 * IPP routines for the CUPS scheduler.
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
9 * This file contains Kerberos support code, copyright 2006 by
12 * These coded instructions, statements, and computer programs are the
13 * property of Apple Inc. and are protected by Federal copyright
14 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
15 * which should have been included with this file. If this file is
16 * file is missing or damaged, see the license at "http://www.cups.org/".
20 * cupsdProcessIPPRequest() - Process an incoming IPP request.
21 * cupsdTimeoutJob() - Timeout a job waiting on job files.
22 * accept_jobs() - Accept print jobs to a printer.
23 * add_class() - Add a class to the system.
24 * add_file() - Add a file to a job.
25 * add_job() - Add a job to a print queue.
26 * add_job_state_reasons() - Add the "job-state-reasons" attribute based
27 * upon the job and printer state...
28 * add_job_subscriptions() - Add any subscriptions for a job.
29 * add_job_uuid() - Add job-uuid attribute to a job.
30 * add_printer() - Add a printer to the system.
31 * add_printer_state_reasons() - Add the "printer-state-reasons" attribute
32 * based upon the printer state...
33 * add_queued_job_count() - Add the "queued-job-count" attribute for the
34 * specified printer or class.
35 * apple_init_profile() - Initialize a color profile.
36 * apple_register_profiles() - Register color profiles for a printer.
37 * apple_unregister_profiles() - Remove color profiles for the specified
39 * apply_printer_defaults() - Apply printer default options to a job.
40 * authenticate_job() - Set job authentication info.
41 * cancel_all_jobs() - Cancel all or selected print jobs.
42 * cancel_job() - Cancel a print job.
43 * cancel_subscription() - Cancel a subscription.
44 * check_rss_recipient() - Check that we do not have a duplicate RSS
46 * check_quotas() - Check quotas for a printer and user.
47 * close_job() - Close a multi-file job.
48 * copy_attribute() - Copy a single attribute.
49 * copy_attrs() - Copy attributes from one request to another.
50 * copy_banner() - Copy a banner file to the requests directory
51 * for the specified job.
52 * copy_file() - Copy a PPD file or interface script...
53 * copy_model() - Copy a PPD model file, substituting default
55 * copy_job_attrs() - Copy job attributes.
56 * copy_printer_attrs() - Copy printer attributes.
57 * copy_subscription_attrs() - Copy subscription attributes.
58 * create_job() - Print a file to a printer or class.
59 * create_requested_array() - Create an array for the requested-attributes.
60 * create_subscription() - Create a notification subscription.
61 * delete_printer() - Remove a printer or class from the system.
62 * get_default() - Get the default destination.
63 * get_devices() - Get the list of available devices on the
65 * get_document() - Get a copy of a job file.
66 * get_job_attrs() - Get job attributes.
67 * get_jobs() - Get a list of jobs for the specified printer.
68 * get_notifications() - Get events for a subscription.
69 * get_ppd() - Get a named PPD from the local system.
70 * get_ppds() - Get the list of PPD files on the local
72 * get_printer_attrs() - Get printer attributes.
73 * get_printer_supported() - Get printer supported values.
74 * get_printers() - Get a list of printers or classes.
75 * get_subscription_attrs() - Get subscription attributes.
76 * get_subscriptions() - Get subscriptions.
77 * get_username() - Get the username associated with a request.
78 * hold_job() - Hold a print job.
79 * hold_new_jobs() - Hold pending/new jobs on a printer or class.
80 * move_job() - Move a job to a new destination.
81 * ppd_parse_line() - Parse a PPD default line.
82 * print_job() - Print a file to a printer or class.
83 * read_job_ticket() - Read a job ticket embedded in a print file.
84 * reject_jobs() - Reject print jobs to a printer.
85 * release_held_new_jobs() - Release pending/new jobs on a printer or
87 * release_job() - Release a held print job.
88 * renew_subscription() - Renew an existing subscription...
89 * restart_job() - Restart an old print job.
90 * save_auth_info() - Save authentication information for a job.
91 * send_document() - Send a file to a printer or class.
92 * send_http_error() - Send a HTTP error back to the IPP client.
93 * send_ipp_status() - Send a status back to the IPP client.
94 * set_default() - Set the default destination...
95 * set_job_attrs() - Set job attributes.
96 * set_printer_attrs() - Set printer attributes.
97 * set_printer_defaults() - Set printer default options from a request.
98 * start_printer() - Start a printer.
99 * stop_printer() - Stop a printer.
100 * url_encode_attr() - URL-encode a string attribute.
101 * url_encode_string() - URL-encode a string.
102 * user_allowed() - See if a user is allowed to print to a queue.
103 * validate_job() - Validate printer options and destination.
104 * validate_name() - Make sure the printer name only contains
106 * validate_user() - Validate the user for the request.
110 * Include necessary headers...
114 #include <cups/ppd-private.h>
117 # include <ApplicationServices/ApplicationServices.h>
118 # ifdef HAVE_COLORSYNCREGISTERDEVICE
119 extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id);
120 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
121 # include <CoreFoundation/CoreFoundation.h>
122 # ifdef HAVE_MEMBERSHIP_H
123 # include <membership.h>
124 # endif /* HAVE_MEMBERSHIP_H */
125 # ifdef HAVE_MEMBERSHIPPRIV_H
126 # include <membershipPriv.h>
128 extern int mbr_user_name_to_uuid(const char* name, uuid_t uu);
129 extern int mbr_group_name_to_uuid(const char* name, uuid_t uu);
130 extern int mbr_check_membership_by_id(uuid_t user, gid_t group, int* ismember);
131 # endif /* HAVE_MEMBERSHIPPRIV_H */
132 #endif /* __APPLE__ */
139 static void accept_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
140 static void add_class(cupsd_client_t *con, ipp_attribute_t *uri);
141 static int add_file(cupsd_client_t *con, cupsd_job_t *job,
142 mime_type_t *filetype, int compression);
143 static cupsd_job_t *add_job(cupsd_client_t *con, cupsd_printer_t *printer,
144 mime_type_t *filetype);
145 static void add_job_state_reasons(cupsd_client_t *con, cupsd_job_t *job);
146 static void add_job_subscriptions(cupsd_client_t *con, cupsd_job_t *job);
147 static void add_job_uuid(cupsd_job_t *job);
148 static void add_printer(cupsd_client_t *con, ipp_attribute_t *uri);
149 static void add_printer_state_reasons(cupsd_client_t *con,
151 static void add_queued_job_count(cupsd_client_t *con, cupsd_printer_t *p);
153 static void apple_init_profile(ppd_file_t *ppd, cups_array_t *languages,
154 # ifdef HAVE_COLORSYNCREGISTERDEVICE
155 CFMutableDictionaryRef profile,
157 CMDeviceProfileInfo *profile,
158 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
159 unsigned id, const char *name,
160 const char *text, const char *iccfile);
161 static void apple_register_profiles(cupsd_printer_t *p);
162 static void apple_unregister_profiles(cupsd_printer_t *p);
163 #endif /* __APPLE__ */
164 static void apply_printer_defaults(cupsd_printer_t *printer,
166 static void authenticate_job(cupsd_client_t *con, ipp_attribute_t *uri);
167 static void cancel_all_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
168 static void cancel_job(cupsd_client_t *con, ipp_attribute_t *uri);
169 static void cancel_subscription(cupsd_client_t *con, int id);
170 static int check_rss_recipient(const char *recipient);
171 static int check_quotas(cupsd_client_t *con, cupsd_printer_t *p);
172 static ipp_attribute_t *copy_attribute(ipp_t *to, ipp_attribute_t *attr,
174 static void close_job(cupsd_client_t *con, ipp_attribute_t *uri);
175 static void copy_attrs(ipp_t *to, ipp_t *from, cups_array_t *ra,
176 ipp_tag_t group, int quickcopy,
177 cups_array_t *exclude);
178 static int copy_banner(cupsd_client_t *con, cupsd_job_t *job,
180 static int copy_file(const char *from, const char *to);
181 static int copy_model(cupsd_client_t *con, const char *from,
183 static void copy_job_attrs(cupsd_client_t *con,
185 cups_array_t *ra, cups_array_t *exclude);
186 static void copy_printer_attrs(cupsd_client_t *con,
187 cupsd_printer_t *printer,
189 static void copy_subscription_attrs(cupsd_client_t *con,
190 cupsd_subscription_t *sub,
192 cups_array_t *exclude);
193 static void create_job(cupsd_client_t *con, ipp_attribute_t *uri);
194 static cups_array_t *create_requested_array(ipp_t *request);
195 static void create_subscription(cupsd_client_t *con, ipp_attribute_t *uri);
196 static void delete_printer(cupsd_client_t *con, ipp_attribute_t *uri);
197 static void get_default(cupsd_client_t *con);
198 static void get_devices(cupsd_client_t *con);
199 static void get_document(cupsd_client_t *con, ipp_attribute_t *uri);
200 static void get_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
201 static void get_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
202 static void get_notifications(cupsd_client_t *con);
203 static void get_ppd(cupsd_client_t *con, ipp_attribute_t *uri);
204 static void get_ppds(cupsd_client_t *con);
205 static void get_printers(cupsd_client_t *con, int type);
206 static void get_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
207 static void get_printer_supported(cupsd_client_t *con, ipp_attribute_t *uri);
208 static void get_subscription_attrs(cupsd_client_t *con, int sub_id);
209 static void get_subscriptions(cupsd_client_t *con, ipp_attribute_t *uri);
210 static const char *get_username(cupsd_client_t *con);
211 static void hold_job(cupsd_client_t *con, ipp_attribute_t *uri);
212 static void hold_new_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
213 static void move_job(cupsd_client_t *con, ipp_attribute_t *uri);
214 static int ppd_parse_line(const char *line, char *option, int olen,
215 char *choice, int clen);
216 static void print_job(cupsd_client_t *con, ipp_attribute_t *uri);
217 static void read_job_ticket(cupsd_client_t *con);
218 static void reject_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
219 static void release_held_new_jobs(cupsd_client_t *con,
220 ipp_attribute_t *uri);
221 static void release_job(cupsd_client_t *con, ipp_attribute_t *uri);
222 static void renew_subscription(cupsd_client_t *con, int sub_id);
223 static void restart_job(cupsd_client_t *con, ipp_attribute_t *uri);
224 static void save_auth_info(cupsd_client_t *con, cupsd_job_t *job,
225 ipp_attribute_t *auth_info);
226 static void send_document(cupsd_client_t *con, ipp_attribute_t *uri);
227 static void send_http_error(cupsd_client_t *con, http_status_t status,
228 cupsd_printer_t *printer);
229 static void send_ipp_status(cupsd_client_t *con, ipp_status_t status,
230 const char *message, ...)
232 __attribute__ ((__format__ (__printf__, 3, 4)))
233 # endif /* __GNUC__ */
235 static void set_default(cupsd_client_t *con, ipp_attribute_t *uri);
236 static void set_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
237 static void set_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
238 static void set_printer_defaults(cupsd_client_t *con,
239 cupsd_printer_t *printer);
240 static void start_printer(cupsd_client_t *con, ipp_attribute_t *uri);
241 static void stop_printer(cupsd_client_t *con, ipp_attribute_t *uri);
242 static void url_encode_attr(ipp_attribute_t *attr, char *buffer,
244 static char *url_encode_string(const char *s, char *buffer, int bufsize);
245 static int user_allowed(cupsd_printer_t *p, const char *username);
246 static void validate_job(cupsd_client_t *con, ipp_attribute_t *uri);
247 static int validate_name(const char *name);
248 static int validate_user(cupsd_job_t *job, cupsd_client_t *con,
249 const char *owner, char *username,
254 * 'cupsdProcessIPPRequest()' - Process an incoming IPP request.
257 int /* O - 1 on success, 0 on failure */
258 cupsdProcessIPPRequest(
259 cupsd_client_t *con) /* I - Client connection */
261 ipp_tag_t group; /* Current group tag */
262 ipp_attribute_t *attr; /* Current attribute */
263 ipp_attribute_t *charset; /* Character set attribute */
264 ipp_attribute_t *language; /* Language attribute */
265 ipp_attribute_t *uri = NULL; /* Printer or job URI attribute */
266 ipp_attribute_t *username; /* requesting-user-name attr */
267 int sub_id; /* Subscription ID */
270 cupsdLogMessage(CUPSD_LOG_DEBUG2,
271 "cupsdProcessIPPRequest(%p[%d]): operation_id = %04x",
272 con, con->http.fd, con->request->request.op.operation_id);
275 * First build an empty response message for this request...
278 con->response = ippNew();
280 con->response->request.status.version[0] =
281 con->request->request.op.version[0];
282 con->response->request.status.version[1] =
283 con->request->request.op.version[1];
284 con->response->request.status.request_id =
285 con->request->request.op.request_id;
288 * Then validate the request header and required attributes...
291 if (con->request->request.any.version[0] != 1 &&
292 con->request->request.any.version[0] != 2)
295 * Return an error, since we only support IPP 1.x and 2.x.
298 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
299 "%04X %s Bad request version number %d.%d",
300 IPP_VERSION_NOT_SUPPORTED, con->http.hostname,
301 con->request->request.any.version[0],
302 con->request->request.any.version[1]);
304 send_ipp_status(con, IPP_VERSION_NOT_SUPPORTED,
305 _("Bad request version number %d.%d."),
306 con->request->request.any.version[0],
307 con->request->request.any.version[1]);
309 else if (con->request->request.any.request_id < 1)
312 * Return an error, since request IDs must be between 1 and 2^31-1
315 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
316 "%04X %s Bad request ID %d",
317 IPP_BAD_REQUEST, con->http.hostname,
318 con->request->request.any.request_id);
320 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad request ID %d."),
321 con->request->request.any.request_id);
323 else if (!con->request->attrs)
325 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
326 "%04X %s No attributes in request",
327 IPP_BAD_REQUEST, con->http.hostname);
329 send_ipp_status(con, IPP_BAD_REQUEST, _("No attributes in request."));
334 * Make sure that the attributes are provided in the correct order and
335 * don't repeat groups...
338 for (attr = con->request->attrs, group = attr->group_tag;
341 if (attr->group_tag < group && attr->group_tag != IPP_TAG_ZERO)
344 * Out of order; return an error...
347 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
348 "%04X %s Attribute groups are out of order",
349 IPP_BAD_REQUEST, con->http.hostname);
351 send_ipp_status(con, IPP_BAD_REQUEST,
352 _("Attribute groups are out of order (%x < %x)."),
353 attr->group_tag, group);
357 group = attr->group_tag;
362 * Then make sure that the first three attributes are:
365 * attributes-natural-language
366 * printer-uri/job-uri
369 attr = con->request->attrs;
370 if (attr && attr->name &&
371 !strcmp(attr->name, "attributes-charset") &&
372 (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_CHARSET)
380 if (attr && attr->name &&
381 !strcmp(attr->name, "attributes-natural-language") &&
382 (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_LANGUAGE)
387 if ((attr = ippFindAttribute(con->request, "printer-uri",
388 IPP_TAG_URI)) != NULL)
390 else if ((attr = ippFindAttribute(con->request, "job-uri",
391 IPP_TAG_URI)) != NULL)
393 else if (con->request->request.op.operation_id == CUPS_GET_PPD)
394 uri = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME);
399 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
400 "attributes-charset", NULL,
401 charset->values[0].string.text);
403 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
404 "attributes-charset", NULL, "utf-8");
407 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
408 "attributes-natural-language", NULL,
409 language->values[0].string.text);
411 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
412 "attributes-natural-language", NULL, DefaultLanguage);
415 _cups_strcasecmp(charset->values[0].string.text, "us-ascii") &&
416 _cups_strcasecmp(charset->values[0].string.text, "utf-8"))
419 * Bad character set...
422 cupsdLogMessage(CUPSD_LOG_ERROR, "Unsupported character set \"%s\"",
423 charset->values[0].string.text);
424 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
425 "%04X %s Unsupported attributes-charset value \"%s\"",
426 IPP_CHARSET, con->http.hostname,
427 charset->values[0].string.text);
428 send_ipp_status(con, IPP_BAD_REQUEST,
429 _("Unsupported character set \"%s\"."),
430 charset->values[0].string.text);
432 else if (!charset || !language ||
434 con->request->request.op.operation_id != CUPS_GET_DEFAULT &&
435 con->request->request.op.operation_id != CUPS_GET_PRINTERS &&
436 con->request->request.op.operation_id != CUPS_GET_CLASSES &&
437 con->request->request.op.operation_id != CUPS_GET_DEVICES &&
438 con->request->request.op.operation_id != CUPS_GET_PPDS))
441 * Return an error, since attributes-charset,
442 * attributes-natural-language, and printer-uri/job-uri are required
443 * for all operations.
448 cupsdLogMessage(CUPSD_LOG_ERROR,
449 "Missing attributes-charset attribute");
451 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
452 "%04X %s Missing attributes-charset attribute",
453 IPP_BAD_REQUEST, con->http.hostname);
458 cupsdLogMessage(CUPSD_LOG_ERROR,
459 "Missing attributes-natural-language attribute");
461 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
462 "%04X %s Missing attributes-natural-language attribute",
463 IPP_BAD_REQUEST, con->http.hostname);
468 cupsdLogMessage(CUPSD_LOG_ERROR,
469 "Missing printer-uri, job-uri, or ppd-name "
472 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
473 "%04X %s Missing printer-uri, job-uri, or ppd-name "
474 "attribute", IPP_BAD_REQUEST, con->http.hostname);
477 cupsdLogMessage(CUPSD_LOG_DEBUG, "Request attributes follow...");
479 for (attr = con->request->attrs; attr; attr = attr->next)
480 cupsdLogMessage(CUPSD_LOG_DEBUG,
481 "attr \"%s\": group_tag = %x, value_tag = %x",
482 attr->name ? attr->name : "(null)", attr->group_tag,
485 cupsdLogMessage(CUPSD_LOG_DEBUG, "End of attributes...");
487 send_ipp_status(con, IPP_BAD_REQUEST,
488 _("Missing required attributes."));
493 * OK, all the checks pass so far; make sure requesting-user-name is
494 * not "root" from a remote host...
497 if ((username = ippFindAttribute(con->request, "requesting-user-name",
498 IPP_TAG_NAME)) != NULL)
501 * Check for root user...
504 if (!strcmp(username->values[0].string.text, "root") &&
505 _cups_strcasecmp(con->http.hostname, "localhost") &&
506 strcmp(con->username, "root"))
509 * Remote unauthenticated user masquerading as local root...
512 _cupsStrFree(username->values[0].string.text);
513 username->values[0].string.text = _cupsStrAlloc(RemoteRoot);
517 if ((attr = ippFindAttribute(con->request, "notify-subscription-id",
518 IPP_TAG_INTEGER)) != NULL)
519 sub_id = attr->values[0].integer;
524 * Then try processing the operation...
528 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s",
529 ippOpString(con->request->request.op.operation_id),
530 uri->values[0].string.text);
532 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s",
533 ippOpString(con->request->request.op.operation_id));
535 switch (con->request->request.op.operation_id)
541 case IPP_VALIDATE_JOB :
542 validate_job(con, uri);
545 case IPP_CREATE_JOB :
546 create_job(con, uri);
549 case IPP_SEND_DOCUMENT :
550 send_document(con, uri);
553 case IPP_CANCEL_JOB :
554 cancel_job(con, uri);
557 case IPP_GET_JOB_ATTRIBUTES :
558 get_job_attrs(con, uri);
565 case IPP_GET_PRINTER_ATTRIBUTES :
566 get_printer_attrs(con, uri);
569 case IPP_GET_PRINTER_SUPPORTED_VALUES :
570 get_printer_supported(con, uri);
577 case IPP_RELEASE_JOB :
578 release_job(con, uri);
581 case IPP_RESTART_JOB :
582 restart_job(con, uri);
585 case IPP_PAUSE_PRINTER :
586 stop_printer(con, uri);
589 case IPP_RESUME_PRINTER :
590 start_printer(con, uri);
593 case IPP_PURGE_JOBS :
594 case IPP_CANCEL_JOBS :
595 case IPP_CANCEL_MY_JOBS :
596 cancel_all_jobs(con, uri);
599 case IPP_SET_JOB_ATTRIBUTES :
600 set_job_attrs(con, uri);
603 case IPP_SET_PRINTER_ATTRIBUTES :
604 set_printer_attrs(con, uri);
607 case IPP_HOLD_NEW_JOBS :
608 hold_new_jobs(con, uri);
611 case IPP_RELEASE_HELD_NEW_JOBS :
612 release_held_new_jobs(con, uri);
619 case CUPS_GET_DEFAULT :
623 case CUPS_GET_PRINTERS :
624 get_printers(con, 0);
627 case CUPS_GET_CLASSES :
628 get_printers(con, CUPS_PRINTER_CLASS);
631 case CUPS_ADD_PRINTER :
632 add_printer(con, uri);
635 case CUPS_DELETE_PRINTER :
636 delete_printer(con, uri);
639 case CUPS_ADD_CLASS :
643 case CUPS_DELETE_CLASS :
644 delete_printer(con, uri);
647 case CUPS_ACCEPT_JOBS :
648 case IPP_ENABLE_PRINTER :
649 accept_jobs(con, uri);
652 case CUPS_REJECT_JOBS :
653 case IPP_DISABLE_PRINTER :
654 reject_jobs(con, uri);
657 case CUPS_SET_DEFAULT :
658 set_default(con, uri);
661 case CUPS_GET_DEVICES :
665 case CUPS_GET_DOCUMENT :
666 get_document(con, uri);
681 case CUPS_AUTHENTICATE_JOB :
682 authenticate_job(con, uri);
685 case IPP_CREATE_PRINTER_SUBSCRIPTION :
686 case IPP_CREATE_JOB_SUBSCRIPTION :
687 create_subscription(con, uri);
690 case IPP_GET_SUBSCRIPTION_ATTRIBUTES :
691 get_subscription_attrs(con, sub_id);
694 case IPP_GET_SUBSCRIPTIONS :
695 get_subscriptions(con, uri);
698 case IPP_RENEW_SUBSCRIPTION :
699 renew_subscription(con, sub_id);
702 case IPP_CANCEL_SUBSCRIPTION :
703 cancel_subscription(con, sub_id);
706 case IPP_GET_NOTIFICATIONS :
707 get_notifications(con);
711 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
712 "%04X %s Operation %04X (%s) not supported",
713 IPP_OPERATION_NOT_SUPPORTED, con->http.hostname,
714 con->request->request.op.operation_id,
715 ippOpString(con->request->request.op.operation_id));
717 send_ipp_status(con, IPP_OPERATION_NOT_SUPPORTED,
718 _("%s not supported."),
720 con->request->request.op.operation_id));
730 * Sending data from the scheduler...
733 cupsdLogMessage(con->response->request.status.status_code
734 >= IPP_BAD_REQUEST &&
735 con->response->request.status.status_code
736 != IPP_NOT_FOUND ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
737 "Returning IPP %s for %s (%s) from %s",
738 ippErrorString(con->response->request.status.status_code),
739 ippOpString(con->request->request.op.operation_id),
740 uri ? uri->values[0].string.text : "no URI",
743 if (LogLevel == CUPSD_LOG_DEBUG2)
744 cupsdLogMessage(CUPSD_LOG_DEBUG2,
745 "cupsdProcessIPPRequest: ippLength(response)=%ld",
746 (long)ippLength(con->response));
748 if (cupsdSendHeader(con, HTTP_OK, "application/ipp", CUPSD_AUTH_NONE))
750 #ifdef CUPSD_USE_CHUNKING
752 * Because older versions of CUPS (1.1.17 and older) and some IPP
753 * clients do not implement chunking properly, we cannot use
754 * chunking by default. This may become the default in future
755 * CUPS releases, or we might add a configuration directive for
759 if (con->http.version == HTTP_1_1)
761 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n") < 0)
764 if (cupsdFlushHeader(con) < 0)
767 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
770 #endif /* CUPSD_USE_CHUNKING */
772 size_t length; /* Length of response */
775 length = ippLength(con->response);
777 if (con->file >= 0 && !con->pipe_pid)
779 struct stat fileinfo; /* File information */
782 if (!fstat(con->file, &fileinfo))
783 length += fileinfo.st_size;
786 if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n\r\n",
787 CUPS_LLCAST length) < 0)
790 if (cupsdFlushHeader(con) < 0)
793 con->http.data_encoding = HTTP_ENCODE_LENGTH;
794 con->http.data_remaining = length;
796 if (con->http.data_remaining <= INT_MAX)
797 con->http._data_remaining = con->http.data_remaining;
799 con->http._data_remaining = INT_MAX;
802 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
803 (cupsd_selfunc_t)cupsdWriteClient, con);
806 * Tell the caller the response header was sent successfully...
814 * Tell the caller the response header could not be sent...
823 * Sending data from a subprocess like cups-deviced; tell the caller
824 * everything is A-OK so far...
833 * 'cupsdTimeoutJob()' - Timeout a job waiting on job files.
836 int /* O - 0 on success, -1 on error */
837 cupsdTimeoutJob(cupsd_job_t *job) /* I - Job to timeout */
839 cupsd_printer_t *printer; /* Destination printer or class */
840 ipp_attribute_t *attr; /* job-sheets attribute */
841 int kbytes; /* Kilobytes in banner */
844 job->pending_timeout = 0;
847 * See if we need to add the ending sheet...
850 if (!cupsdLoadJob(job))
853 printer = cupsdFindDest(job->dest);
854 attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
857 !(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
858 attr && attr->num_values > 1)
864 cupsdLogJob(job, CUPSD_LOG_INFO, "Adding end banner page \"%s\".",
865 attr->values[1].string.text);
867 if ((kbytes = copy_banner(NULL, job, attr->values[1].string.text)) < 0)
870 cupsdUpdateQuota(printer, job->username, 0, kbytes);
878 * 'accept_jobs()' - Accept print jobs to a printer.
882 accept_jobs(cupsd_client_t *con, /* I - Client connection */
883 ipp_attribute_t *uri) /* I - Printer or class URI */
885 http_status_t status; /* Policy status */
886 cups_ptype_t dtype; /* Destination type (printer/class) */
887 cupsd_printer_t *printer; /* Printer data */
890 cupsdLogMessage(CUPSD_LOG_DEBUG2, "accept_jobs(%p[%d], %s)", con,
891 con->http.fd, uri->values[0].string.text);
894 * Is the destination valid?
897 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
903 send_ipp_status(con, IPP_NOT_FOUND,
904 _("The printer or class does not exist."));
912 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
914 send_http_error(con, status, printer);
919 * Accept jobs sent to the printer...
922 printer->accepting = 1;
923 printer->state_message[0] = '\0';
925 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
926 "Now accepting jobs.");
928 if (dtype & CUPS_PRINTER_CLASS)
930 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
932 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" now accepting jobs (\"%s\").",
933 printer->name, get_username(con));
937 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
939 cupsdLogMessage(CUPSD_LOG_INFO,
940 "Printer \"%s\" now accepting jobs (\"%s\").",
941 printer->name, get_username(con));
945 * Everything was ok, so return OK status...
948 con->response->request.status.status_code = IPP_OK;
953 * 'add_class()' - Add a class to the system.
957 add_class(cupsd_client_t *con, /* I - Client connection */
958 ipp_attribute_t *uri) /* I - URI of class */
960 http_status_t status; /* Policy status */
961 int i; /* Looping var */
962 char scheme[HTTP_MAX_URI], /* Method portion of URI */
963 username[HTTP_MAX_URI], /* Username portion of URI */
964 host[HTTP_MAX_URI], /* Host portion of URI */
965 resource[HTTP_MAX_URI]; /* Resource portion of URI */
966 int port; /* Port portion of URI */
967 cupsd_printer_t *pclass, /* Class */
968 *member; /* Member printer/class */
969 cups_ptype_t dtype; /* Destination type */
970 ipp_attribute_t *attr; /* Printer attribute */
971 int modify; /* Non-zero if we just modified */
972 char newname[IPP_MAX_NAME]; /* New class name */
973 int need_restart_job; /* Need to restart job? */
976 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_class(%p[%d], %s)", con,
977 con->http.fd, uri->values[0].string.text);
980 * Do we have a valid URI?
983 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
984 sizeof(scheme), username, sizeof(username), host,
985 sizeof(host), &port, resource, sizeof(resource));
988 if (strncmp(resource, "/classes/", 9) || strlen(resource) == 9)
991 * No, return an error...
994 send_ipp_status(con, IPP_BAD_REQUEST,
995 _("The printer-uri must be of the form "
996 "\"ipp://HOSTNAME/classes/CLASSNAME\"."));
1001 * Do we have a valid printer name?
1004 if (!validate_name(resource + 9))
1007 * No, return an error...
1010 send_ipp_status(con, IPP_BAD_REQUEST,
1011 _("The printer-uri \"%s\" contains invalid characters."),
1012 uri->values[0].string.text);
1017 * See if the class already exists; if not, create a new class...
1020 if ((pclass = cupsdFindClass(resource + 9)) == NULL)
1023 * Class doesn't exist; see if we have a printer of the same name...
1026 if ((pclass = cupsdFindPrinter(resource + 9)) != NULL &&
1027 !(pclass->type & CUPS_PRINTER_DISCOVERED))
1030 * Yes, return an error...
1033 send_ipp_status(con, IPP_NOT_POSSIBLE,
1034 _("A printer named \"%s\" already exists."),
1040 * No, check the default policy and then add the class...
1043 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
1045 send_http_error(con, status, NULL);
1049 pclass = cupsdAddClass(resource + 9);
1052 else if (pclass->type & CUPS_PRINTER_IMPLICIT)
1055 * Check the default policy, then rename the implicit class to "AnyClass"
1059 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
1061 send_http_error(con, status, NULL);
1065 if (ImplicitAnyClasses)
1067 snprintf(newname, sizeof(newname), "Any%s", resource + 9);
1068 cupsdRenamePrinter(pclass, newname);
1071 cupsdDeletePrinter(pclass, 1);
1074 * Add the class as a new local class...
1077 pclass = cupsdAddClass(resource + 9);
1080 else if (pclass->type & CUPS_PRINTER_DISCOVERED)
1083 * Check the default policy, then rename the remote class to "Class"...
1086 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
1088 send_http_error(con, status, NULL);
1092 snprintf(newname, sizeof(newname), "%s@%s", resource + 9, pclass->hostname);
1093 cupsdRenamePrinter(pclass, newname);
1096 * Add the class as a new local class...
1099 pclass = cupsdAddClass(resource + 9);
1102 else if ((status = cupsdCheckPolicy(pclass->op_policy_ptr, con,
1105 send_http_error(con, status, pclass);
1112 * Look for attributes and copy them over as needed...
1115 need_restart_job = 0;
1117 if ((attr = ippFindAttribute(con->request, "printer-location",
1118 IPP_TAG_TEXT)) != NULL)
1119 cupsdSetString(&pclass->location, attr->values[0].string.text);
1121 if ((attr = ippFindAttribute(con->request, "printer-info",
1122 IPP_TAG_TEXT)) != NULL)
1123 cupsdSetString(&pclass->info, attr->values[0].string.text);
1125 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs",
1126 IPP_TAG_BOOLEAN)) != NULL &&
1127 attr->values[0].boolean != pclass->accepting)
1129 cupsdLogMessage(CUPSD_LOG_INFO,
1130 "Setting %s printer-is-accepting-jobs to %d (was %d.)",
1131 pclass->name, attr->values[0].boolean, pclass->accepting);
1133 pclass->accepting = attr->values[0].boolean;
1135 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, pclass, NULL, "%s accepting jobs.",
1136 pclass->accepting ? "Now" : "No longer");
1139 if ((attr = ippFindAttribute(con->request, "printer-is-shared",
1140 IPP_TAG_BOOLEAN)) != NULL)
1142 if (pclass->shared && !attr->values[0].boolean)
1143 cupsdDeregisterPrinter(pclass, 1);
1145 cupsdLogMessage(CUPSD_LOG_INFO,
1146 "Setting %s printer-is-shared to %d (was %d.)",
1147 pclass->name, attr->values[0].boolean, pclass->shared);
1149 pclass->shared = attr->values[0].boolean;
1152 if ((attr = ippFindAttribute(con->request, "printer-state",
1153 IPP_TAG_ENUM)) != NULL)
1155 if (attr->values[0].integer != IPP_PRINTER_IDLE &&
1156 attr->values[0].integer != IPP_PRINTER_STOPPED)
1158 send_ipp_status(con, IPP_BAD_REQUEST,
1159 _("Attempt to set %s printer-state to bad value %d."),
1160 pclass->name, attr->values[0].integer);
1164 cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)",
1165 pclass->name, attr->values[0].integer, pclass->state);
1167 if (attr->values[0].integer == IPP_PRINTER_STOPPED)
1168 cupsdStopPrinter(pclass, 0);
1171 cupsdSetPrinterState(pclass, (ipp_pstate_t)(attr->values[0].integer), 0);
1172 need_restart_job = 1;
1175 if ((attr = ippFindAttribute(con->request, "printer-state-message",
1176 IPP_TAG_TEXT)) != NULL)
1178 strlcpy(pclass->state_message, attr->values[0].string.text,
1179 sizeof(pclass->state_message));
1181 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, pclass, NULL, "%s",
1182 pclass->state_message);
1184 if ((attr = ippFindAttribute(con->request, "member-uris",
1185 IPP_TAG_URI)) != NULL)
1188 * Clear the printer array as needed...
1191 need_restart_job = 1;
1193 if (pclass->num_printers > 0)
1195 free(pclass->printers);
1196 pclass->num_printers = 0;
1200 * Add each printer or class that is listed...
1203 for (i = 0; i < attr->num_values; i ++)
1206 * Search for the printer or class URI...
1209 if (!cupsdValidateDest(attr->values[i].string.text, &dtype, &member))
1215 send_ipp_status(con, IPP_NOT_FOUND,
1216 _("The printer or class does not exist."));
1219 else if (dtype & CUPS_PRINTER_CLASS)
1221 send_ipp_status(con, IPP_BAD_REQUEST,
1222 _("Nested classes are not allowed."));
1227 * Add it to the class...
1230 cupsdAddPrinterToClass(pclass, member);
1234 set_printer_defaults(con, pclass);
1236 if ((attr = ippFindAttribute(con->request, "auth-info-required",
1237 IPP_TAG_KEYWORD)) != NULL)
1238 cupsdSetAuthInfoRequired(pclass, NULL, attr);
1241 * Update the printer class attributes and return...
1244 cupsdSetPrinterAttrs(pclass);
1245 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
1247 if (need_restart_job && pclass->job)
1250 * Reset the current job to a "pending" status...
1253 cupsdSetJobState(pclass->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
1254 "Job restarted because the class was modified.");
1257 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
1261 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED,
1262 pclass, NULL, "Class \"%s\" modified by \"%s\".",
1263 pclass->name, get_username(con));
1265 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" modified by \"%s\".",
1266 pclass->name, get_username(con));
1270 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
1271 pclass, NULL, "New class \"%s\" added by \"%s\".",
1272 pclass->name, get_username(con));
1274 cupsdLogMessage(CUPSD_LOG_INFO, "New class \"%s\" added by \"%s\".",
1275 pclass->name, get_username(con));
1278 con->response->request.status.status_code = IPP_OK;
1283 * 'add_file()' - Add a file to a job.
1286 static int /* O - 0 on success, -1 on error */
1287 add_file(cupsd_client_t *con, /* I - Connection to client */
1288 cupsd_job_t *job, /* I - Job to add to */
1289 mime_type_t *filetype, /* I - Type of file */
1290 int compression) /* I - Compression */
1292 mime_type_t **filetypes; /* New filetypes array... */
1293 int *compressions; /* New compressions array... */
1296 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1297 "add_file(con=%p[%d], job=%d, filetype=%s/%s, "
1298 "compression=%d)", con, con ? con->http.fd : -1, job->id,
1299 filetype->super, filetype->type, compression);
1302 * Add the file to the job...
1305 if (job->num_files == 0)
1307 compressions = (int *)malloc(sizeof(int));
1308 filetypes = (mime_type_t **)malloc(sizeof(mime_type_t *));
1312 compressions = (int *)realloc(job->compressions,
1313 (job->num_files + 1) * sizeof(int));
1314 filetypes = (mime_type_t **)realloc(job->filetypes,
1315 (job->num_files + 1) *
1316 sizeof(mime_type_t *));
1319 if (!compressions || !filetypes)
1321 cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
1322 "Job aborted because the scheduler ran out of memory.");
1325 send_ipp_status(con, IPP_INTERNAL_ERROR,
1326 _("Unable to allocate memory for file types."));
1331 job->compressions = compressions;
1332 job->compressions[job->num_files] = compression;
1333 job->filetypes = filetypes;
1334 job->filetypes[job->num_files] = filetype;
1339 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
1346 * 'add_job()' - Add a job to a print queue.
1349 static cupsd_job_t * /* O - Job object */
1350 add_job(cupsd_client_t *con, /* I - Client connection */
1351 cupsd_printer_t *printer, /* I - Destination printer */
1352 mime_type_t *filetype) /* I - First print file type, if any */
1354 http_status_t status; /* Policy status */
1355 ipp_attribute_t *attr, /* Current attribute */
1356 *auth_info; /* auth-info attribute */
1357 const char *val; /* Default option value */
1358 int priority; /* Job priority */
1359 cupsd_job_t *job; /* Current job */
1360 char job_uri[HTTP_MAX_URI]; /* Job URI */
1361 int kbytes; /* Size of print file */
1362 int i; /* Looping var */
1363 int lowerpagerange; /* Page range bound */
1364 int exact; /* Did we have an exact match? */
1365 ipp_attribute_t *media_col, /* media-col attribute */
1366 *media_margin; /* media-*-margin attribute */
1367 ipp_t *unsup_col; /* media-col in unsupported response */
1370 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %p(%s), %p(%s/%s))",
1371 con, con->http.fd, printer, printer->name,
1372 filetype, filetype ? filetype->super : "none",
1373 filetype ? filetype->type : "none");
1376 * Check remote printing to non-shared printer...
1379 if (!printer->shared &&
1380 _cups_strcasecmp(con->http.hostname, "localhost") &&
1381 _cups_strcasecmp(con->http.hostname, ServerName))
1383 send_ipp_status(con, IPP_NOT_AUTHORIZED,
1384 _("The printer or class is not shared."));
1392 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
1394 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
1396 send_http_error(con, status, printer);
1399 else if (printer->num_auth_info_required == 1 &&
1400 !strcmp(printer->auth_info_required[0], "negotiate") &&
1403 send_http_error(con, HTTP_UNAUTHORIZED, printer);
1407 else if (auth_info && !con->http.tls &&
1408 !httpAddrLocalhost(con->http.hostaddr))
1411 * Require encryption of auth-info over non-local connections...
1414 send_http_error(con, HTTP_UPGRADE_REQUIRED, printer);
1417 #endif /* HAVE_SSL */
1420 * See if the printer is accepting jobs...
1423 if (!printer->accepting)
1425 send_ipp_status(con, IPP_NOT_ACCEPTING,
1426 _("Destination \"%s\" is not accepting jobs."),
1432 * Validate job template attributes; for now just document-format,
1433 * copies, number-up, and page-ranges...
1436 if (filetype && printer->filetypes &&
1437 !cupsArrayFind(printer->filetypes, filetype))
1439 char mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
1440 /* MIME media type string */
1443 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
1446 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
1447 _("Unsupported format \"%s\"."), mimetype);
1449 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
1450 "document-format", NULL, mimetype);
1455 if ((attr = ippFindAttribute(con->request, "copies",
1456 IPP_TAG_INTEGER)) != NULL)
1458 if (attr->values[0].integer < 1 || attr->values[0].integer > MaxCopies)
1460 send_ipp_status(con, IPP_ATTRIBUTES, _("Bad copies value %d."),
1461 attr->values[0].integer);
1462 ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER,
1463 "copies", attr->values[0].integer);
1468 if ((attr = ippFindAttribute(con->request, "job-sheets",
1469 IPP_TAG_ZERO)) != NULL)
1471 if (attr->value_tag != IPP_TAG_KEYWORD &&
1472 attr->value_tag != IPP_TAG_NAME)
1474 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value type."));
1478 if (attr->num_values > 2)
1480 send_ipp_status(con, IPP_BAD_REQUEST,
1481 _("Too many job-sheets values (%d > 2)."),
1486 for (i = 0; i < attr->num_values; i ++)
1487 if (strcmp(attr->values[i].string.text, "none") &&
1488 !cupsdFindBanner(attr->values[i].string.text))
1490 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value \"%s\"."),
1491 attr->values[i].string.text);
1496 if ((attr = ippFindAttribute(con->request, "number-up",
1497 IPP_TAG_INTEGER)) != NULL)
1499 if (attr->values[0].integer != 1 &&
1500 attr->values[0].integer != 2 &&
1501 attr->values[0].integer != 4 &&
1502 attr->values[0].integer != 6 &&
1503 attr->values[0].integer != 9 &&
1504 attr->values[0].integer != 16)
1506 send_ipp_status(con, IPP_ATTRIBUTES, _("Bad number-up value %d."),
1507 attr->values[0].integer);
1508 ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER,
1509 "number-up", attr->values[0].integer);
1514 if ((attr = ippFindAttribute(con->request, "page-ranges",
1515 IPP_TAG_RANGE)) != NULL)
1517 for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
1519 if (attr->values[i].range.lower < lowerpagerange ||
1520 attr->values[i].range.lower > attr->values[i].range.upper)
1522 send_ipp_status(con, IPP_BAD_REQUEST,
1523 _("Bad page-ranges values %d-%d."),
1524 attr->values[i].range.lower,
1525 attr->values[i].range.upper);
1529 lowerpagerange = attr->values[i].range.upper + 1;
1534 * Do media selection as needed...
1537 if (!ippFindAttribute(con->request, "PageRegion", IPP_TAG_ZERO) &&
1538 !ippFindAttribute(con->request, "PageSize", IPP_TAG_ZERO) &&
1539 _ppdCacheGetPageSize(printer->pc, con->request, NULL, &exact))
1542 (media_col = ippFindAttribute(con->request, "media-col",
1543 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1545 send_ipp_status(con, IPP_OK_SUBST, _("Unsupported margins."));
1547 unsup_col = ippNew();
1548 if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1549 "media-bottom-margin",
1550 IPP_TAG_INTEGER)) != NULL)
1551 ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1552 "media-bottom-margin", media_margin->values[0].integer);
1554 if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1555 "media-left-margin",
1556 IPP_TAG_INTEGER)) != NULL)
1557 ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1558 "media-left-margin", media_margin->values[0].integer);
1560 if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1561 "media-right-margin",
1562 IPP_TAG_INTEGER)) != NULL)
1563 ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1564 "media-right-margin", media_margin->values[0].integer);
1566 if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1568 IPP_TAG_INTEGER)) != NULL)
1569 ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1570 "media-top-margin", media_margin->values[0].integer);
1572 ippAddCollection(con->response, IPP_TAG_UNSUPPORTED_GROUP, "media-col",
1574 ippDelete(unsup_col);
1579 * Make sure we aren't over our limit...
1582 if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs)
1585 if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs)
1587 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Too many active jobs."));
1591 if ((i = check_quotas(con, printer)) < 0)
1593 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
1598 send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Not allowed to print."));
1603 * Create the job and set things up...
1606 if ((attr = ippFindAttribute(con->request, "job-priority",
1607 IPP_TAG_INTEGER)) != NULL)
1608 priority = attr->values[0].integer;
1611 if ((val = cupsGetOption("job-priority", printer->num_options,
1612 printer->options)) != NULL)
1613 priority = atoi(val);
1617 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
1621 if (!ippFindAttribute(con->request, "job-name", IPP_TAG_NAME))
1622 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
1625 if ((job = cupsdAddJob(priority, printer->name)) == NULL)
1627 send_ipp_status(con, IPP_INTERNAL_ERROR,
1628 _("Unable to add job for destination \"%s\"."),
1633 job->dtype = printer->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT |
1634 CUPS_PRINTER_REMOTE);
1635 job->attrs = con->request;
1637 con->request = ippNewRequest(job->attrs->request.op.operation_id);
1639 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
1642 apply_printer_defaults(printer, job);
1644 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
1646 if (con->username[0])
1648 cupsdSetString(&job->username, con->username);
1651 cupsdSetString(&attr->values[0].string.text, con->username);
1655 cupsdLogMessage(CUPSD_LOG_DEBUG,
1656 "add_job: requesting-user-name=\"%s\"",
1657 attr->values[0].string.text);
1659 cupsdSetString(&job->username, attr->values[0].string.text);
1662 cupsdSetString(&job->username, "anonymous");
1665 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
1666 "job-originating-user-name", NULL, job->username);
1669 attr->group_tag = IPP_TAG_JOB;
1670 _cupsStrFree(attr->name);
1671 attr->name = _cupsStrAlloc("job-originating-user-name");
1674 if (con->username[0] || auth_info)
1676 save_auth_info(con, job, auth_info);
1679 * Remove the auth-info attribute from the attribute data...
1683 ippDeleteAttribute(job->attrs, auth_info);
1686 if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
1687 IPP_TAG_ZERO)) != NULL)
1690 * Request contains a job-originating-host-name attribute; validate it...
1693 if (attr->value_tag != IPP_TAG_NAME ||
1694 attr->num_values != 1 ||
1695 strcmp(con->http.hostname, "localhost"))
1698 * Can't override the value if we aren't connected via localhost.
1699 * Also, we can only have 1 value and it must be a name value.
1702 switch (attr->value_tag)
1704 case IPP_TAG_STRING :
1705 case IPP_TAG_TEXTLANG :
1706 case IPP_TAG_NAMELANG :
1709 case IPP_TAG_KEYWORD :
1711 case IPP_TAG_URISCHEME :
1712 case IPP_TAG_CHARSET :
1713 case IPP_TAG_LANGUAGE :
1714 case IPP_TAG_MIMETYPE :
1716 * Free old strings...
1719 for (i = 0; i < attr->num_values; i ++)
1721 _cupsStrFree(attr->values[i].string.text);
1722 attr->values[i].string.text = NULL;
1723 if (attr->values[i].string.charset)
1725 _cupsStrFree(attr->values[i].string.charset);
1726 attr->values[i].string.charset = NULL;
1735 * Use the default connection hostname instead...
1738 attr->value_tag = IPP_TAG_NAME;
1739 attr->num_values = 1;
1740 attr->values[0].string.text = _cupsStrAlloc(con->http.hostname);
1743 attr->group_tag = IPP_TAG_JOB;
1748 * No job-originating-host-name attribute, so use the hostname from
1752 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
1753 "job-originating-host-name", NULL, con->http.hostname);
1756 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
1758 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1759 "time-at-processing", 0);
1760 attr->value_tag = IPP_TAG_NOVALUE;
1761 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1762 "time-at-completed", 0);
1763 attr->value_tag = IPP_TAG_NOVALUE;
1766 * Add remaining job attributes...
1769 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1770 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
1771 "job-state", IPP_JOB_STOPPED);
1772 job->state_value = (ipp_jstate_t)job->state->values[0].integer;
1773 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1774 "job-media-sheets-completed", 0);
1775 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
1778 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
1779 IPP_TAG_INTEGER)) != NULL)
1780 attr->values[0].integer = 0;
1782 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-k-octets", 0);
1784 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
1785 IPP_TAG_KEYWORD)) == NULL)
1786 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
1789 if ((val = cupsGetOption("job-hold-until", printer->num_options,
1790 printer->options)) == NULL)
1793 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1794 "job-hold-until", NULL, val);
1796 if (attr && strcmp(attr->values[0].string.text, "no-hold"))
1799 * Hold job until specified time...
1802 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
1804 job->state->values[0].integer = IPP_JOB_HELD;
1805 job->state_value = IPP_JOB_HELD;
1807 else if (job->attrs->request.op.operation_id == IPP_CREATE_JOB)
1809 job->hold_until = time(NULL) + MultipleOperationTimeout;
1810 job->state->values[0].integer = IPP_JOB_HELD;
1811 job->state_value = IPP_JOB_HELD;
1815 job->state->values[0].integer = IPP_JOB_PENDING;
1816 job->state_value = IPP_JOB_PENDING;
1819 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
1823 * Add job sheets options...
1826 if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1827 IPP_TAG_ZERO)) == NULL)
1829 cupsdLogMessage(CUPSD_LOG_DEBUG,
1830 "Adding default job-sheets values \"%s,%s\"...",
1831 printer->job_sheets[0], printer->job_sheets[1]);
1833 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
1835 attr->values[0].string.text = _cupsStrRetain(printer->job_sheets[0]);
1836 attr->values[1].string.text = _cupsStrRetain(printer->job_sheets[1]);
1839 job->job_sheets = attr;
1842 * Enforce classification level if set...
1847 cupsdLogMessage(CUPSD_LOG_INFO,
1848 "Classification=\"%s\", ClassifyOverride=%d",
1849 Classification ? Classification : "(null)",
1852 if (ClassifyOverride)
1854 if (!strcmp(attr->values[0].string.text, "none") &&
1855 (attr->num_values == 1 ||
1856 !strcmp(attr->values[1].string.text, "none")))
1859 * Force the leading banner to have the classification on it...
1862 cupsdSetString(&attr->values[0].string.text, Classification);
1864 cupsdLogJob(job, CUPSD_LOG_NOTICE, "CLASSIFICATION FORCED "
1865 "job-sheets=\"%s,none\", "
1866 "job-originating-user-name=\"%s\"",
1867 Classification, job->username);
1869 else if (attr->num_values == 2 &&
1870 strcmp(attr->values[0].string.text,
1871 attr->values[1].string.text) &&
1872 strcmp(attr->values[0].string.text, "none") &&
1873 strcmp(attr->values[1].string.text, "none"))
1876 * Can't put two different security markings on the same document!
1879 cupsdSetString(&attr->values[1].string.text, attr->values[0].string.text);
1881 cupsdLogJob(job, CUPSD_LOG_NOTICE, "CLASSIFICATION FORCED "
1882 "job-sheets=\"%s,%s\", "
1883 "job-originating-user-name=\"%s\"",
1884 attr->values[0].string.text,
1885 attr->values[1].string.text, job->username);
1887 else if (strcmp(attr->values[0].string.text, Classification) &&
1888 strcmp(attr->values[0].string.text, "none") &&
1889 (attr->num_values == 1 ||
1890 (strcmp(attr->values[1].string.text, Classification) &&
1891 strcmp(attr->values[1].string.text, "none"))))
1893 if (attr->num_values == 1)
1894 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1895 "CLASSIFICATION OVERRIDDEN "
1896 "job-sheets=\"%s\", "
1897 "job-originating-user-name=\"%s\"",
1898 attr->values[0].string.text, job->username);
1900 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1901 "CLASSIFICATION OVERRIDDEN "
1902 "job-sheets=\"%s,%s\",fffff "
1903 "job-originating-user-name=\"%s\"",
1904 attr->values[0].string.text,
1905 attr->values[1].string.text, job->username);
1908 else if (strcmp(attr->values[0].string.text, Classification) &&
1909 (attr->num_values == 1 ||
1910 strcmp(attr->values[1].string.text, Classification)))
1913 * Force the banner to have the classification on it...
1916 if (attr->num_values > 1 &&
1917 !strcmp(attr->values[0].string.text, attr->values[1].string.text))
1919 cupsdSetString(&(attr->values[0].string.text), Classification);
1920 cupsdSetString(&(attr->values[1].string.text), Classification);
1924 if (attr->num_values == 1 ||
1925 strcmp(attr->values[0].string.text, "none"))
1926 cupsdSetString(&(attr->values[0].string.text), Classification);
1928 if (attr->num_values > 1 &&
1929 strcmp(attr->values[1].string.text, "none"))
1930 cupsdSetString(&(attr->values[1].string.text), Classification);
1933 if (attr->num_values > 1)
1934 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1935 "CLASSIFICATION FORCED "
1936 "job-sheets=\"%s,%s\", "
1937 "job-originating-user-name=\"%s\"",
1938 attr->values[0].string.text,
1939 attr->values[1].string.text, job->username);
1941 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1942 "CLASSIFICATION FORCED "
1943 "job-sheets=\"%s\", "
1944 "job-originating-user-name=\"%s\"",
1945 Classification, job->username);
1950 * See if we need to add the starting sheet...
1953 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
1955 cupsdLogJob(job, CUPSD_LOG_INFO, "Adding start banner page \"%s\".",
1956 attr->values[0].string.text);
1958 if ((kbytes = copy_banner(con, job, attr->values[0].string.text)) < 0)
1960 cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
1961 "Aborting job because the start banner could not be "
1966 cupsdUpdateQuota(printer, job->username, 0, kbytes);
1969 else if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1970 IPP_TAG_ZERO)) != NULL)
1971 job->job_sheets = attr;
1974 * Fill in the response info...
1977 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
1978 con->servername, con->serverport, "/jobs/%d", job->id);
1979 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
1982 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1984 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
1986 add_job_state_reasons(con, job);
1988 con->response->request.status.status_code = IPP_OK;
1991 * Add any job subscriptions...
1994 add_job_subscriptions(con, job);
1997 * Set all but the first two attributes to the job attributes group...
2000 for (attr = job->attrs->attrs->next->next; attr; attr = attr->next)
2001 attr->group_tag = IPP_TAG_JOB;
2004 * Fire the "job created" event...
2007 cupsdAddEvent(CUPSD_EVENT_JOB_CREATED, printer, job, "Job created.");
2010 * Return the new job...
2018 * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
2019 * upon the job and printer state...
2023 add_job_state_reasons(
2024 cupsd_client_t *con, /* I - Client connection */
2025 cupsd_job_t *job) /* I - Job info */
2027 cupsd_printer_t *dest; /* Destination printer */
2028 ipp_attribute_t *attr; /* job-hold attribute */
2030 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job_state_reasons(%p[%d], %d)",
2031 con, con->http.fd, job ? job->id : 0);
2033 switch (job ? job->state_value : IPP_JOB_CANCELED)
2035 case IPP_JOB_PENDING :
2036 dest = cupsdFindDest(job->dest);
2038 if (dest && dest->state == IPP_PRINTER_STOPPED)
2039 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2040 "job-state-reasons", NULL, "printer-stopped");
2042 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2043 "job-state-reasons", NULL, "none");
2047 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
2048 IPP_TAG_KEYWORD)) == NULL)
2049 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
2051 if (!attr || strcmp(attr->values[0].string.text, "no-hold"))
2052 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2053 "job-state-reasons", NULL, "job-hold-until-specified");
2055 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2056 "job-state-reasons", NULL, "job-incoming");
2059 case IPP_JOB_PROCESSING :
2060 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2061 "job-state-reasons", NULL, "job-printing");
2064 case IPP_JOB_STOPPED :
2065 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2066 "job-state-reasons", NULL, "job-stopped");
2069 case IPP_JOB_CANCELED :
2070 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2071 "job-state-reasons", NULL, "job-canceled-by-user");
2074 case IPP_JOB_ABORTED :
2075 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2076 "job-state-reasons", NULL, "aborted-by-system");
2079 case IPP_JOB_COMPLETED :
2080 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2081 "job-state-reasons", NULL, "job-completed-successfully");
2088 * 'add_job_subscriptions()' - Add any subscriptions for a job.
2092 add_job_subscriptions(
2093 cupsd_client_t *con, /* I - Client connection */
2094 cupsd_job_t *job) /* I - Newly created job */
2096 int i; /* Looping var */
2097 ipp_attribute_t *prev, /* Previous attribute */
2098 *next, /* Next attribute */
2099 *attr; /* Current attribute */
2100 cupsd_subscription_t *sub; /* Subscription object */
2101 const char *recipient, /* notify-recipient-uri */
2102 *pullmethod; /* notify-pull-method */
2103 ipp_attribute_t *user_data; /* notify-user-data */
2104 int interval; /* notify-time-interval */
2105 unsigned mask; /* notify-events */
2109 * Find the first subscription group attribute; return if we have
2113 for (attr = job->attrs->attrs; attr; attr = attr->next)
2114 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
2121 * Process the subscription attributes in the request...
2130 mask = CUPSD_EVENT_NONE;
2132 while (attr && attr->group_tag != IPP_TAG_ZERO)
2134 if (!strcmp(attr->name, "notify-recipient-uri") &&
2135 attr->value_tag == IPP_TAG_URI)
2138 * Validate the recipient scheme against the ServerBin/notifier
2142 char notifier[1024], /* Notifier filename */
2143 scheme[HTTP_MAX_URI], /* Scheme portion of URI */
2144 userpass[HTTP_MAX_URI], /* Username portion of URI */
2145 host[HTTP_MAX_URI], /* Host portion of URI */
2146 resource[HTTP_MAX_URI]; /* Resource portion of URI */
2147 int port; /* Port portion of URI */
2150 recipient = attr->values[0].string.text;
2152 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
2153 scheme, sizeof(scheme), userpass, sizeof(userpass),
2154 host, sizeof(host), &port,
2155 resource, sizeof(resource)) < HTTP_URI_OK)
2157 send_ipp_status(con, IPP_NOT_POSSIBLE,
2158 _("Bad notify-recipient-uri \"%s\"."), recipient);
2159 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2160 "notify-status-code", IPP_URI_SCHEME);
2164 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
2166 if (access(notifier, X_OK))
2168 send_ipp_status(con, IPP_NOT_POSSIBLE,
2169 _("notify-recipient-uri URI \"%s\" uses unknown "
2170 "scheme."), recipient);
2171 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2172 "notify-status-code", IPP_URI_SCHEME);
2176 if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
2178 send_ipp_status(con, IPP_NOT_POSSIBLE,
2179 _("notify-recipient-uri URI \"%s\" is already used."),
2181 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2182 "notify-status-code", IPP_ATTRIBUTES);
2186 else if (!strcmp(attr->name, "notify-pull-method") &&
2187 attr->value_tag == IPP_TAG_KEYWORD)
2189 pullmethod = attr->values[0].string.text;
2191 if (strcmp(pullmethod, "ippget"))
2193 send_ipp_status(con, IPP_NOT_POSSIBLE,
2194 _("Bad notify-pull-method \"%s\"."), pullmethod);
2195 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2196 "notify-status-code", IPP_ATTRIBUTES);
2200 else if (!strcmp(attr->name, "notify-charset") &&
2201 attr->value_tag == IPP_TAG_CHARSET &&
2202 strcmp(attr->values[0].string.text, "us-ascii") &&
2203 strcmp(attr->values[0].string.text, "utf-8"))
2205 send_ipp_status(con, IPP_CHARSET,
2206 _("Character set \"%s\" not supported."),
2207 attr->values[0].string.text);
2210 else if (!strcmp(attr->name, "notify-natural-language") &&
2211 (attr->value_tag != IPP_TAG_LANGUAGE ||
2212 strcmp(attr->values[0].string.text, DefaultLanguage)))
2214 send_ipp_status(con, IPP_CHARSET,
2215 _("Language \"%s\" not supported."),
2216 attr->values[0].string.text);
2219 else if (!strcmp(attr->name, "notify-user-data") &&
2220 attr->value_tag == IPP_TAG_STRING)
2222 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
2224 send_ipp_status(con, IPP_REQUEST_VALUE,
2225 _("The notify-user-data value is too large "
2226 "(%d > 63 octets)."),
2227 attr->values[0].unknown.length);
2233 else if (!strcmp(attr->name, "notify-events") &&
2234 attr->value_tag == IPP_TAG_KEYWORD)
2236 for (i = 0; i < attr->num_values; i ++)
2237 mask |= cupsdEventValue(attr->values[i].string.text);
2239 else if (!strcmp(attr->name, "notify-lease-duration"))
2241 send_ipp_status(con, IPP_BAD_REQUEST,
2242 _("The notify-lease-duration attribute cannot be "
2243 "used with job subscriptions."));
2246 else if (!strcmp(attr->name, "notify-time-interval") &&
2247 attr->value_tag == IPP_TAG_INTEGER)
2248 interval = attr->values[0].integer;
2253 if (!recipient && !pullmethod)
2256 if (mask == CUPSD_EVENT_NONE)
2257 mask = CUPSD_EVENT_JOB_COMPLETED;
2259 if ((sub = cupsdAddSubscription(mask, cupsdFindDest(job->dest), job,
2260 recipient, 0)) != NULL)
2262 sub->interval = interval;
2264 cupsdSetString(&sub->owner, job->username);
2268 sub->user_data_len = user_data->values[0].unknown.length;
2269 memcpy(sub->user_data, user_data->values[0].unknown.data,
2270 sub->user_data_len);
2273 ippAddSeparator(con->response);
2274 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
2275 "notify-subscription-id", sub->id);
2277 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d",
2285 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
2288 * Remove all of the subscription attributes from the job request...
2290 * TODO: Optimize this since subscription groups have to come at the
2291 * end of the request...
2294 for (attr = job->attrs->attrs, prev = NULL; attr; attr = next)
2298 if (attr->group_tag == IPP_TAG_SUBSCRIPTION ||
2299 attr->group_tag == IPP_TAG_ZERO)
2302 * Free and remove this attribute...
2310 job->attrs->attrs = next;
2316 job->attrs->last = prev;
2317 job->attrs->current = prev;
2322 * 'add_job_uuid()' - Add job-uuid attribute to a job.
2324 * See RFC 4122 for the definition of UUIDs and the format.
2328 add_job_uuid(cupsd_job_t *job) /* I - Job */
2330 char uuid[64]; /* job-uuid string */
2334 * Add a job-uuid attribute if none exists...
2337 if (!ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI))
2338 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL,
2339 _httpAssembleUUID(ServerName, RemotePort, job->dest, job->id,
2340 uuid, sizeof(uuid)));
2345 * 'add_printer()' - Add a printer to the system.
2349 add_printer(cupsd_client_t *con, /* I - Client connection */
2350 ipp_attribute_t *uri) /* I - URI of printer */
2352 http_status_t status; /* Policy status */
2353 int i; /* Looping var */
2354 char scheme[HTTP_MAX_URI], /* Method portion of URI */
2355 username[HTTP_MAX_URI], /* Username portion of URI */
2356 host[HTTP_MAX_URI], /* Host portion of URI */
2357 resource[HTTP_MAX_URI]; /* Resource portion of URI */
2358 int port; /* Port portion of URI */
2359 cupsd_printer_t *printer; /* Printer/class */
2360 ipp_attribute_t *attr; /* Printer attribute */
2361 cups_file_t *fp; /* Script/PPD file */
2362 char line[1024]; /* Line from file... */
2363 char srcfile[1024], /* Source Script/PPD file */
2364 dstfile[1024]; /* Destination Script/PPD file */
2365 int modify; /* Non-zero if we are modifying */
2366 char newname[IPP_MAX_NAME]; /* New printer name */
2367 int changed_driver, /* Changed the PPD/interface script? */
2368 need_restart_job, /* Need to restart job? */
2369 set_device_uri, /* Did we set the device URI? */
2370 set_port_monitor; /* Did we set the port monitor? */
2373 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer(%p[%d], %s)", con,
2374 con->http.fd, uri->values[0].string.text);
2377 * Do we have a valid URI?
2380 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
2381 sizeof(scheme), username, sizeof(username), host,
2382 sizeof(host), &port, resource, sizeof(resource));
2384 if (strncmp(resource, "/printers/", 10) || strlen(resource) == 10)
2387 * No, return an error...
2390 send_ipp_status(con, IPP_BAD_REQUEST,
2391 _("The printer-uri must be of the form "
2392 "\"ipp://HOSTNAME/printers/PRINTERNAME\"."));
2397 * Do we have a valid printer name?
2400 if (!validate_name(resource + 10))
2403 * No, return an error...
2406 send_ipp_status(con, IPP_BAD_REQUEST,
2407 _("The printer-uri \"%s\" contains invalid characters."),
2408 uri->values[0].string.text);
2413 * See if the printer already exists; if not, create a new printer...
2416 if ((printer = cupsdFindPrinter(resource + 10)) == NULL)
2419 * Printer doesn't exist; see if we have a class of the same name...
2422 if ((printer = cupsdFindClass(resource + 10)) != NULL &&
2423 !(printer->type & CUPS_PRINTER_DISCOVERED))
2426 * Yes, return an error...
2429 send_ipp_status(con, IPP_NOT_POSSIBLE,
2430 _("A class named \"%s\" already exists."),
2436 * No, check the default policy then add the printer...
2439 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
2441 send_http_error(con, status, NULL);
2445 printer = cupsdAddPrinter(resource + 10);
2448 else if (printer->type & CUPS_PRINTER_IMPLICIT)
2451 * Check the default policy, then rename the implicit printer to
2452 * "AnyPrinter" or delete it...
2455 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
2457 send_http_error(con, status, NULL);
2461 if (ImplicitAnyClasses)
2463 snprintf(newname, sizeof(newname), "Any%s", resource + 10);
2464 cupsdRenamePrinter(printer, newname);
2467 cupsdDeletePrinter(printer, 1);
2470 * Add the printer as a new local printer...
2473 printer = cupsdAddPrinter(resource + 10);
2476 else if (printer->type & CUPS_PRINTER_DISCOVERED)
2479 * Check the default policy, then rename the remote printer to
2480 * "Printer@server"...
2483 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
2485 send_http_error(con, status, NULL);
2489 snprintf(newname, sizeof(newname), "%s@%s", resource + 10,
2491 cupsdRenamePrinter(printer, newname);
2494 * Add the printer as a new local printer...
2497 printer = cupsdAddPrinter(resource + 10);
2500 else if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
2503 send_http_error(con, status, printer);
2510 * Look for attributes and copy them over as needed...
2514 need_restart_job = 0;
2516 if ((attr = ippFindAttribute(con->request, "printer-location",
2517 IPP_TAG_TEXT)) != NULL)
2518 cupsdSetString(&printer->location, attr->values[0].string.text);
2520 if ((attr = ippFindAttribute(con->request, "printer-info",
2521 IPP_TAG_TEXT)) != NULL)
2522 cupsdSetString(&printer->info, attr->values[0].string.text);
2526 if ((attr = ippFindAttribute(con->request, "device-uri",
2527 IPP_TAG_URI)) != NULL)
2530 * Do we have a valid device URI?
2533 http_uri_status_t uri_status; /* URI separation status */
2534 char old_device_uri[1024];
2535 /* Old device URI */
2538 need_restart_job = 1;
2540 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
2541 attr->values[0].string.text,
2542 scheme, sizeof(scheme),
2543 username, sizeof(username),
2544 host, sizeof(host), &port,
2545 resource, sizeof(resource));
2547 if (uri_status < HTTP_URI_OK)
2549 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri \"%s\"."),
2550 attr->values[0].string.text);
2551 cupsdLogMessage(CUPSD_LOG_DEBUG,
2552 "add_printer: httpSeparateURI returned %d", uri_status);
2556 if (!strcmp(scheme, "file"))
2559 * See if the administrator has enabled file devices...
2562 if (!FileDevice && strcmp(resource, "/dev/null"))
2565 * File devices are disabled and the URL is not file:/dev/null...
2568 send_ipp_status(con, IPP_NOT_POSSIBLE,
2569 _("File device URIs have been disabled. "
2570 "To enable, see the FileDevice directive in "
2571 "\"%s/cupsd.conf\"."),
2579 * See if the backend exists and is executable...
2582 snprintf(srcfile, sizeof(srcfile), "%s/backend/%s", ServerBin, scheme);
2583 if (access(srcfile, X_OK))
2586 * Could not find device in list!
2589 send_ipp_status(con, IPP_NOT_POSSIBLE,
2590 _("Bad device-uri scheme \"%s\"."), scheme);
2595 if (printer->sanitized_device_uri)
2596 strlcpy(old_device_uri, printer->sanitized_device_uri,
2597 sizeof(old_device_uri));
2599 old_device_uri[0] = '\0';
2601 cupsdSetDeviceURI(printer, attr->values[0].string.text);
2603 cupsdLogMessage(CUPSD_LOG_INFO,
2604 "Setting %s device-uri to \"%s\" (was \"%s\".)",
2605 printer->name, printer->sanitized_device_uri,
2611 set_port_monitor = 0;
2613 if ((attr = ippFindAttribute(con->request, "port-monitor",
2614 IPP_TAG_NAME)) != NULL)
2616 ipp_attribute_t *supported; /* port-monitor-supported attribute */
2619 need_restart_job = 1;
2621 supported = ippFindAttribute(printer->ppd_attrs, "port-monitor-supported",
2625 for (i = 0; i < supported->num_values; i ++)
2626 if (!strcmp(supported->values[i].string.text,
2627 attr->values[0].string.text))
2631 if (!supported || i >= supported->num_values)
2633 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad port-monitor \"%s\"."),
2634 attr->values[0].string.text);
2638 cupsdLogMessage(CUPSD_LOG_INFO,
2639 "Setting %s port-monitor to \"%s\" (was \"%s\".)",
2640 printer->name, attr->values[0].string.text,
2641 printer->port_monitor ? printer->port_monitor : "none");
2643 if (strcmp(attr->values[0].string.text, "none"))
2644 cupsdSetString(&printer->port_monitor, attr->values[0].string.text);
2646 cupsdClearString(&printer->port_monitor);
2648 set_port_monitor = 1;
2651 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs",
2652 IPP_TAG_BOOLEAN)) != NULL &&
2653 attr->values[0].boolean != printer->accepting)
2655 cupsdLogMessage(CUPSD_LOG_INFO,
2656 "Setting %s printer-is-accepting-jobs to %d (was %d.)",
2657 printer->name, attr->values[0].boolean, printer->accepting);
2659 printer->accepting = attr->values[0].boolean;
2661 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
2662 "%s accepting jobs.",
2663 printer->accepting ? "Now" : "No longer");
2666 if ((attr = ippFindAttribute(con->request, "printer-is-shared",
2667 IPP_TAG_BOOLEAN)) != NULL)
2669 if (attr->values[0].boolean &&
2670 printer->num_auth_info_required == 1 &&
2671 !strcmp(printer->auth_info_required[0], "negotiate"))
2673 send_ipp_status(con, IPP_BAD_REQUEST,
2674 _("Cannot share a remote Kerberized printer."));
2678 if (printer->shared && !attr->values[0].boolean)
2679 cupsdDeregisterPrinter(printer, 1);
2681 cupsdLogMessage(CUPSD_LOG_INFO,
2682 "Setting %s printer-is-shared to %d (was %d.)",
2683 printer->name, attr->values[0].boolean, printer->shared);
2685 printer->shared = attr->values[0].boolean;
2688 if ((attr = ippFindAttribute(con->request, "printer-state",
2689 IPP_TAG_ENUM)) != NULL)
2691 if (attr->values[0].integer != IPP_PRINTER_IDLE &&
2692 attr->values[0].integer != IPP_PRINTER_STOPPED)
2694 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad printer-state value %d."),
2695 attr->values[0].integer);
2699 cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)",
2700 printer->name, attr->values[0].integer, printer->state);
2702 if (attr->values[0].integer == IPP_PRINTER_STOPPED)
2703 cupsdStopPrinter(printer, 0);
2706 need_restart_job = 1;
2707 cupsdSetPrinterState(printer, (ipp_pstate_t)(attr->values[0].integer), 0);
2711 if ((attr = ippFindAttribute(con->request, "printer-state-message",
2712 IPP_TAG_TEXT)) != NULL)
2714 strlcpy(printer->state_message, attr->values[0].string.text,
2715 sizeof(printer->state_message));
2717 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL, "%s",
2718 printer->state_message);
2721 if ((attr = ippFindAttribute(con->request, "printer-state-reasons",
2722 IPP_TAG_KEYWORD)) != NULL)
2724 if (attr->num_values >
2725 (int)(sizeof(printer->reasons) / sizeof(printer->reasons[0])))
2727 send_ipp_status(con, IPP_NOT_POSSIBLE,
2728 _("Too many printer-state-reasons values (%d > %d)."),
2730 (int)(sizeof(printer->reasons) /
2731 sizeof(printer->reasons[0])));
2735 for (i = 0; i < printer->num_reasons; i ++)
2736 _cupsStrFree(printer->reasons[i]);
2738 printer->num_reasons = 0;
2739 for (i = 0; i < attr->num_values; i ++)
2741 if (!strcmp(attr->values[i].string.text, "none"))
2744 printer->reasons[printer->num_reasons] =
2745 _cupsStrRetain(attr->values[i].string.text);
2746 printer->num_reasons ++;
2748 if (!strcmp(attr->values[i].string.text, "paused") &&
2749 printer->state != IPP_PRINTER_STOPPED)
2751 cupsdLogMessage(CUPSD_LOG_INFO,
2752 "Setting %s printer-state to %d (was %d.)",
2753 printer->name, IPP_PRINTER_STOPPED, printer->state);
2754 cupsdStopPrinter(printer, 0);
2758 if (PrintcapFormat == PRINTCAP_PLIST)
2759 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
2761 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
2762 "Printer \"%s\" state changed.", printer->name);
2765 set_printer_defaults(con, printer);
2767 if ((attr = ippFindAttribute(con->request, "auth-info-required",
2768 IPP_TAG_KEYWORD)) != NULL)
2769 cupsdSetAuthInfoRequired(printer, NULL, attr);
2772 * See if we have all required attributes...
2775 if (!printer->device_uri)
2776 cupsdSetString(&printer->device_uri, "file:///dev/null");
2779 * See if we have an interface script or PPD file attached to the request...
2784 need_restart_job = 1;
2787 strlcpy(srcfile, con->filename, sizeof(srcfile));
2789 if ((fp = cupsFileOpen(srcfile, "rb")))
2792 * Yes; get the first line from it...
2796 cupsFileGets(fp, line, sizeof(line));
2800 * Then see what kind of file it is...
2803 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
2806 if (!strncmp(line, "*PPD-Adobe", 10))
2809 * The new file is a PPD file, so remove any old interface script
2810 * that might be lying around...
2818 * This must be an interface script, so move the file over to the
2819 * interfaces directory and make it executable...
2822 if (copy_file(srcfile, dstfile))
2824 send_ipp_status(con, IPP_INTERNAL_ERROR,
2825 _("Unable to copy interface script - %s"),
2830 cupsdLogMessage(CUPSD_LOG_DEBUG,
2831 "Copied interface script successfully");
2832 chmod(dstfile, 0755);
2835 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
2838 if (!strncmp(line, "*PPD-Adobe", 10))
2841 * The new file is a PPD file, so move the file over to the
2842 * ppd directory and make it readable by all...
2845 if (copy_file(srcfile, dstfile))
2847 send_ipp_status(con, IPP_INTERNAL_ERROR,
2848 _("Unable to copy PPD file - %s"),
2853 cupsdLogMessage(CUPSD_LOG_DEBUG,
2854 "Copied PPD file successfully");
2855 chmod(dstfile, 0644);
2860 * This must be an interface script, so remove any old PPD file that
2861 * may be lying around...
2868 else if ((attr = ippFindAttribute(con->request, "ppd-name",
2869 IPP_TAG_NAME)) != NULL)
2871 need_restart_job = 1;
2874 if (!strcmp(attr->values[0].string.text, "raw"))
2877 * Raw driver, remove any existing PPD or interface script files.
2880 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
2884 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
2894 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
2898 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
2901 if (copy_model(con, attr->values[0].string.text, dstfile))
2903 send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to copy PPD file."));
2907 cupsdLogMessage(CUPSD_LOG_DEBUG,
2908 "Copied PPD file successfully");
2909 chmod(dstfile, 0644);
2916 * If we changed the PPD/interface script, then remove the printer's cache
2917 * file and clear the printer-state-reasons...
2920 char cache_name[1024]; /* Cache filename for printer attrs */
2922 snprintf(cache_name, sizeof(cache_name), "%s/%s.data", CacheDir,
2926 cupsdSetPrinterReasons(printer, "none");
2929 * (Re)register color profiles...
2934 cupsdCmsRegisterPrinter(printer);
2937 * FIXME: ideally the ColorSync stuff would be moved to colorsync.c
2938 * and the colorsyncRegisterProfiles() would be called from
2939 * cupsdCmsRegisterPrinter() in printers.c
2941 apple_unregister_profiles(printer);
2942 apple_register_profiles(printer);
2943 #endif /* __APPLE__ */
2948 * If we set the device URI but not the port monitor, check which port
2949 * monitor to use by default...
2952 if (set_device_uri && !set_port_monitor)
2954 ppd_file_t *ppd; /* PPD file */
2955 ppd_attr_t *ppdattr; /* cupsPortMonitor attribute */
2958 httpSeparateURI(HTTP_URI_CODING_ALL, printer->device_uri, scheme,
2959 sizeof(scheme), username, sizeof(username), host,
2960 sizeof(host), &port, resource, sizeof(resource));
2962 snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot,
2964 if ((ppd = ppdOpenFile(srcfile)) != NULL)
2966 for (ppdattr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
2968 ppdattr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL))
2969 if (!strcmp(scheme, ppdattr->spec))
2971 cupsdLogMessage(CUPSD_LOG_INFO,
2972 "Setting %s port-monitor to \"%s\" (was \"%s\".)",
2973 printer->name, ppdattr->value,
2974 printer->port_monitor ? printer->port_monitor
2977 if (strcmp(ppdattr->value, "none"))
2978 cupsdSetString(&printer->port_monitor, ppdattr->value);
2980 cupsdClearString(&printer->port_monitor);
2990 * Update the printer attributes and return...
2993 cupsdSetPrinterAttrs(printer);
2994 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
2996 if (need_restart_job && printer->job)
2999 * Restart the current job...
3002 cupsdSetJobState(printer->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
3003 "Job restarted because the printer was modified.");
3006 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
3010 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED,
3011 printer, NULL, "Printer \"%s\" modified by \"%s\".",
3012 printer->name, get_username(con));
3014 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" modified by \"%s\".",
3015 printer->name, get_username(con));
3019 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
3020 printer, NULL, "New printer \"%s\" added by \"%s\".",
3021 printer->name, get_username(con));
3023 cupsdLogMessage(CUPSD_LOG_INFO, "New printer \"%s\" added by \"%s\".",
3024 printer->name, get_username(con));
3027 con->response->request.status.status_code = IPP_OK;
3032 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
3033 * based upon the printer state...
3037 add_printer_state_reasons(
3038 cupsd_client_t *con, /* I - Client connection */
3039 cupsd_printer_t *p) /* I - Printer info */
3041 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3042 "add_printer_state_reasons(%p[%d], %p[%s])",
3043 con, con->http.fd, p, p->name);
3045 if (p->num_reasons == 0)
3046 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3047 "printer-state-reasons", NULL, "none");
3049 ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3050 "printer-state-reasons", p->num_reasons, NULL,
3051 (const char * const *)p->reasons);
3056 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
3057 * the specified printer or class.
3061 add_queued_job_count(
3062 cupsd_client_t *con, /* I - Client connection */
3063 cupsd_printer_t *p) /* I - Printer or class */
3065 int count; /* Number of jobs on destination */
3068 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])",
3069 con, con->http.fd, p, p->name);
3071 count = cupsdGetPrinterJobCount(p->name);
3073 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3074 "queued-job-count", count);
3080 * 'apple_init_profile()' - Initialize a color profile.
3085 ppd_file_t *ppd, /* I - PPD file */
3086 cups_array_t *languages, /* I - Languages in the PPD file */
3087 # ifdef HAVE_COLORSYNCREGISTERDEVICE
3088 CFMutableDictionaryRef profile, /* I - Profile dictionary */
3090 CMDeviceProfileInfo *profile, /* I - Profile record */
3091 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3092 unsigned id, /* I - Profile ID */
3093 const char *name, /* I - Profile name */
3094 const char *text, /* I - Profile UI text */
3095 const char *iccfile) /* I - ICC filename */
3097 # ifdef HAVE_COLORSYNCREGISTERDEVICE
3098 CFURLRef url; /* URL for profile filename */
3099 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3100 CFMutableDictionaryRef dict; /* Dictionary for name */
3101 char *language; /* Current language */
3102 ppd_attr_t *attr; /* Profile attribute */
3103 CFStringRef cflang, /* Language string */
3104 cftext; /* Localized text */
3110 * Build the profile name dictionary...
3113 dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3114 &kCFTypeDictionaryKeyCallBacks,
3115 &kCFTypeDictionaryValueCallBacks);
3118 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize profile \"%s\".",
3123 cftext = CFStringCreateWithCString(kCFAllocatorDefault, text,
3124 kCFStringEncodingUTF8);
3128 CFDictionarySetValue(dict, CFSTR("en_US"), cftext);
3135 * Find localized names for the color profiles...
3138 cupsArraySave(ppd->sorted_attrs);
3140 for (language = (char *)cupsArrayFirst(languages);
3142 language = (char *)cupsArrayNext(languages))
3146 if ((attr = _ppdLocalizedAttr(ppd, "cupsICCProfile", name,
3148 attr = _ppdLocalizedAttr(ppd, "APTiogaProfile", name, language);
3151 attr = _ppdLocalizedAttr(ppd, "ColorModel", name, language);
3153 if (attr && attr->text[0])
3155 cflang = CFStringCreateWithCString(kCFAllocatorDefault, language,
3156 kCFStringEncodingUTF8);
3157 cftext = CFStringCreateWithCString(kCFAllocatorDefault, attr->text,
3158 kCFStringEncodingUTF8);
3160 if (cflang && cftext)
3161 CFDictionarySetValue(dict, cflang, cftext);
3171 cupsArrayRestore(ppd->sorted_attrs);
3175 * Fill in the profile data...
3178 # ifdef HAVE_COLORSYNCREGISTERDEVICE
3181 url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
3182 (const UInt8 *)iccfile,
3183 strlen(iccfile), false);
3187 CFDictionarySetValue(profile, kColorSyncDeviceProfileURL, url);
3192 CFDictionarySetValue(profile, kColorSyncDeviceModeDescriptions, dict);
3196 profile->dataVersion = cmDeviceProfileInfoVersion1;
3197 profile->profileID = id;
3198 profile->profileLoc.locType = iccfile ? cmPathBasedProfile : cmNoProfileBase;
3199 profile->profileName = dict;
3202 strlcpy(profile->profileLoc.u.pathLoc.path, iccfile,
3203 sizeof(profile->profileLoc.u.pathLoc.path));
3204 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3209 * 'apple_register_profiles()' - Register color profiles for a printer.
3213 apple_register_profiles(
3214 cupsd_printer_t *p) /* I - Printer */
3216 int i; /* Looping var */
3217 char ppdfile[1024], /* PPD filename */
3218 iccfile[1024], /* ICC filename */
3219 selector[PPD_MAX_NAME];
3220 /* Profile selection string */
3221 ppd_file_t *ppd; /* PPD file */
3222 ppd_attr_t *attr, /* Profile attributes */
3223 *profileid_attr,/* cupsProfileID attribute */
3224 *q1_attr, /* ColorModel (or other) qualifier */
3225 *q2_attr, /* MediaType (or other) qualifier */
3226 *q3_attr; /* Resolution (or other) qualifier */
3227 char q_keyword[PPD_MAX_NAME];
3228 /* Qualifier keyword */
3229 const char *q1_choice, /* ColorModel (or other) choice */
3230 *q2_choice, /* MediaType (or other) choice */
3231 *q3_choice; /* Resolution (or other) choice */
3232 const char *profile_key; /* Profile keyword */
3233 ppd_option_t *cm_option; /* Color model option */
3234 ppd_choice_t *cm_choice; /* Color model choice */
3235 int num_profiles; /* Number of profiles */
3236 OSStatus error = 0; /* Last error */
3237 unsigned device_id, /* Printer device ID */
3238 profile_id = 0, /* Profile ID */
3239 default_profile_id = 0;
3240 /* Default profile ID */
3241 CFMutableDictionaryRef device_name; /* Printer device name dictionary */
3242 CFStringRef printer_name; /* Printer name string */
3243 cups_array_t *languages; /* Languages array */
3244 # ifdef HAVE_COLORSYNCREGISTERDEVICE
3245 CFMutableDictionaryRef profiles, /* Dictionary of profiles */
3246 profile; /* Current profile info dictionary */
3247 CFStringRef dict_key; /* Key in factory profile dictionary */
3249 CMDeviceScope scope = /* Scope of the registration */
3251 kCFPreferencesAnyUser,
3252 kCFPreferencesCurrentHost
3254 CMDeviceProfileArrayPtr profiles; /* Profiles */
3255 CMDeviceProfileInfo *profile; /* Current profile */
3256 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3260 * Make sure ColorSync is available...
3263 # ifdef HAVE_COLORSYNCREGISTERDEVICE
3264 if (ColorSyncRegisterDevice == NULL)
3268 if (CMRegisterColorDevice == NULL)
3270 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3273 * Try opening the PPD file for this printer...
3276 snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
3277 if ((ppd = ppdOpenFile(ppdfile)) == NULL)
3281 * See if we have any profiles...
3284 if ((attr = ppdFindAttr(ppd, "APTiogaProfile", NULL)) != NULL)
3285 profile_key = "APTiogaProfile";
3288 attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
3289 profile_key = "cupsICCProfile";
3292 for (num_profiles = 0; attr; attr = ppdFindNextAttr(ppd, profile_key, NULL))
3293 if (attr->spec[0] && attr->value && attr->value[0])
3295 if (attr->value[0] != '/')
3296 snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
3299 strlcpy(iccfile, attr->value, sizeof(iccfile));
3301 if (access(iccfile, 0))
3303 cupsdLogMessage(CUPSD_LOG_ERROR,
3304 "%s: ICC Profile \"%s\" does not exist.", p->name,
3312 # ifdef HAVE_COLORSYNCREGISTERDEVICE
3314 * Create a dictionary for the factory profiles...
3317 profiles = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3318 &kCFTypeDictionaryKeyCallBacks,
3319 &kCFTypeDictionaryValueCallBacks);
3322 cupsdLogMessage(CUPSD_LOG_ERROR,
3323 "Unable to allocate memory for factory profiles.");
3327 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3330 * If we have profiles, add them...
3333 if (num_profiles > 0)
3335 if (profile_key[0] == 'A')
3338 * For Tioga PPDs, get the default profile using the DefaultAPTiogaProfile
3342 if ((attr = ppdFindAttr(ppd, "DefaultAPTiogaProfile", NULL)) != NULL &&
3344 default_profile_id = atoi(attr->value);
3346 q1_choice = q2_choice = q3_choice = NULL;
3351 * For CUPS PPDs, figure out the default profile selector values...
3354 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL &&
3355 attr->value && attr->value[0])
3357 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
3358 q1_attr = ppdFindAttr(ppd, q_keyword, NULL);
3360 else if ((q1_attr = ppdFindAttr(ppd, "DefaultColorModel", NULL)) == NULL)
3361 q1_attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
3363 if (q1_attr && q1_attr->value && q1_attr->value[0])
3364 q1_choice = q1_attr->value;
3368 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL &&
3369 attr->value && attr->value[0])
3371 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
3372 q2_attr = ppdFindAttr(ppd, q_keyword, NULL);
3375 q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL);
3377 if (q2_attr && q2_attr->value && q2_attr->value[0])
3378 q2_choice = q2_attr->value;
3382 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL &&
3383 attr->value && attr->value[0])
3385 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
3386 q3_attr = ppdFindAttr(ppd, q_keyword, NULL);
3389 q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL);
3391 if (q3_attr && q3_attr->value && q3_attr->value[0])
3392 q3_choice = q3_attr->value;
3397 # ifndef HAVE_COLORSYNCREGISTERDEVICE
3399 * Build the array of profiles...
3401 * Note: This calloc actually requests slightly more memory than needed.
3404 if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
3406 cupsdLogMessage(CUPSD_LOG_ERROR,
3407 "Unable to allocate memory for factory profiles.");
3412 profiles->profileCount = num_profiles;
3413 profile = profiles->profiles;
3414 # endif /* !HAVE_COLORSYNCREGISTERDEVICE */
3417 * Loop through the profiles listed in the PPD...
3420 languages = _ppdGetLanguages(ppd);
3422 for (attr = ppdFindAttr(ppd, profile_key, NULL);
3424 attr = ppdFindNextAttr(ppd, profile_key, NULL))
3425 if (attr->spec[0] && attr->value && attr->value[0])
3428 * Add this profile...
3431 if (attr->value[0] != '/')
3432 snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
3435 strlcpy(iccfile, attr->value, sizeof(iccfile));
3437 if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser,
3438 cupsdLogFCMessage, p))
3441 if (profile_key[0] == 'c')
3443 cupsArraySave(ppd->sorted_attrs);
3445 if ((profileid_attr = ppdFindAttr(ppd, "cupsProfileID",
3446 attr->spec)) != NULL &&
3447 profileid_attr->value && isdigit(profileid_attr->value[0] & 255))
3448 profile_id = (unsigned)strtoul(profileid_attr->value, NULL, 10);
3450 profile_id = _ppdHashName(attr->spec);
3452 cupsArrayRestore(ppd->sorted_attrs);
3455 profile_id = atoi(attr->spec);
3457 # ifdef HAVE_COLORSYNCREGISTERDEVICE
3458 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3459 &kCFTypeDictionaryKeyCallBacks,
3460 &kCFTypeDictionaryValueCallBacks);
3463 cupsdLogMessage(CUPSD_LOG_ERROR,
3464 "Unable to allocate memory for color profile.");
3465 CFRelease(profiles);
3470 apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
3471 attr->text[0] ? attr->text : attr->spec, iccfile);
3473 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
3474 CFSTR("%u"), profile_id);
3477 CFDictionarySetValue(profiles, dict_key, profile);
3478 CFRelease(dict_key);
3484 apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
3485 attr->text[0] ? attr->text : attr->spec, iccfile);
3488 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3491 * See if this is the default profile...
3494 if (!default_profile_id && q1_choice && q2_choice && q3_choice)
3496 snprintf(selector, sizeof(selector), "%s.%s.%s", q1_choice, q2_choice,
3498 if (!strcmp(selector, attr->spec))
3499 default_profile_id = profile_id;
3502 if (!default_profile_id && q1_choice && q2_choice)
3504 snprintf(selector, sizeof(selector), "%s.%s.", q1_choice, q2_choice);
3505 if (!strcmp(selector, attr->spec))
3506 default_profile_id = profile_id;
3509 if (!default_profile_id && q1_choice && q3_choice)
3511 snprintf(selector, sizeof(selector), "%s..%s", q1_choice, q3_choice);
3512 if (!strcmp(selector, attr->spec))
3513 default_profile_id = profile_id;
3516 if (!default_profile_id && q1_choice)
3518 snprintf(selector, sizeof(selector), "%s..", q1_choice);
3519 if (!strcmp(selector, attr->spec))
3520 default_profile_id = profile_id;
3523 if (!default_profile_id && q2_choice && q3_choice)
3525 snprintf(selector, sizeof(selector), ".%s.%s", q2_choice, q3_choice);
3526 if (!strcmp(selector, attr->spec))
3527 default_profile_id = profile_id;
3530 if (!default_profile_id && q2_choice)
3532 snprintf(selector, sizeof(selector), ".%s.", q2_choice);
3533 if (!strcmp(selector, attr->spec))
3534 default_profile_id = profile_id;
3537 if (!default_profile_id && q3_choice)
3539 snprintf(selector, sizeof(selector), "..%s", q3_choice);
3540 if (!strcmp(selector, attr->spec))
3541 default_profile_id = profile_id;
3545 _ppdFreeLanguages(languages);
3547 else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL)
3550 * Extract profiles from ColorModel option...
3553 const char *profile_name; /* Name of generic profile */
3556 num_profiles = cm_option->num_choices;
3558 # ifndef HAVE_COLORSYNCREGISTERDEVICE
3560 * Create an array for the factory profiles...
3563 if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
3565 cupsdLogMessage(CUPSD_LOG_ERROR,
3566 "Unable to allocate memory for factory profiles.");
3571 profiles->profileCount = num_profiles;
3572 profile = profiles->profiles;
3573 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3575 for (i = cm_option->num_choices, cm_choice = cm_option->choices;
3579 if (!strcmp(cm_choice->choice, "Gray") ||
3580 !strcmp(cm_choice->choice, "Black"))
3581 profile_name = "Gray";
3582 else if (!strcmp(cm_choice->choice, "RGB") ||
3583 !strcmp(cm_choice->choice, "CMY"))
3584 profile_name = "RGB";
3585 else if (!strcmp(cm_choice->choice, "CMYK") ||
3586 !strcmp(cm_choice->choice, "KCMY"))
3587 profile_name = "CMYK";
3589 profile_name = "DeviceN";
3591 snprintf(selector, sizeof(selector), "%s..", profile_name);
3592 profile_id = _ppdHashName(selector);
3594 # ifdef HAVE_COLORSYNCREGISTERDEVICE
3595 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3596 &kCFTypeDictionaryKeyCallBacks,
3597 &kCFTypeDictionaryValueCallBacks);
3600 cupsdLogMessage(CUPSD_LOG_ERROR,
3601 "Unable to allocate memory for color profile.");
3602 CFRelease(profiles);
3607 apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
3608 cm_choice->text, NULL);
3610 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
3611 CFSTR("%u"), profile_id);
3614 CFDictionarySetValue(profiles, dict_key, profile);
3615 CFRelease(dict_key);
3621 apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
3622 cm_choice->text, NULL);
3624 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3626 if (cm_choice->marked)
3627 default_profile_id = profile_id;
3633 * Use the default colorspace...
3636 attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
3638 num_profiles = (attr && ppd->colorspace == PPD_CS_GRAY) ? 1 : 2;
3640 # ifdef HAVE_COLORSYNCREGISTERDEVICE
3642 * Add the grayscale profile first. We always have a grayscale profile.
3645 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3646 &kCFTypeDictionaryKeyCallBacks,
3647 &kCFTypeDictionaryValueCallBacks);
3651 cupsdLogMessage(CUPSD_LOG_ERROR,
3652 "Unable to allocate memory for color profile.");
3653 CFRelease(profiles);
3658 profile_id = _ppdHashName("Gray..");
3659 apple_init_profile(ppd, NULL, profile, profile_id, "Gray", "Gray", NULL);
3661 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
3665 CFDictionarySetValue(profiles, dict_key, profile);
3666 CFRelease(dict_key);
3672 * Then add the RGB/CMYK/DeviceN color profile...
3675 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3676 &kCFTypeDictionaryKeyCallBacks,
3677 &kCFTypeDictionaryValueCallBacks);
3681 cupsdLogMessage(CUPSD_LOG_ERROR,
3682 "Unable to allocate memory for color profile.");
3683 CFRelease(profiles);
3688 switch (ppd->colorspace)
3693 profile_id = _ppdHashName("RGB..");
3694 apple_init_profile(ppd, NULL, profile, profile_id, "RGB", "RGB",
3700 profile_id = _ppdHashName("CMYK..");
3701 apple_init_profile(ppd, NULL, profile, profile_id, "CMYK", "CMYK",
3710 profile_id = _ppdHashName("DeviceN..");
3711 apple_init_profile(ppd, NULL, profile, profile_id, "DeviceN",
3716 if (CFDictionaryGetCount(profile) > 0)
3718 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
3719 CFSTR("%u"), profile_id);
3722 CFDictionarySetValue(profiles, dict_key, profile);
3723 CFRelease(dict_key);
3731 * Create an array for the factory profiles...
3734 if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
3736 cupsdLogMessage(CUPSD_LOG_ERROR,
3737 "Unable to allocate memory for factory profiles.");
3742 profiles->profileCount = num_profiles;
3745 * Add the grayscale profile first. We always have a grayscale profile.
3748 profile_id = _ppdHashName("Gray..");
3749 apple_init_profile(ppd, NULL, profiles->profiles, profile_id, "Gray",
3753 * Then add the RGB/CMYK/DeviceN color profile...
3756 switch (ppd->colorspace)
3761 profile_id = _ppdHashName("RGB..");
3762 apple_init_profile(ppd, NULL, profiles->profiles + 1, profile_id,
3763 "RGB", "RGB", NULL);
3767 profile_id = _ppdHashName("CMYK..");
3768 apple_init_profile(ppd, NULL, profiles->profiles + 1, profile_id,
3769 "CMYK", "CMYK", NULL);
3777 profile_id = _ppdHashName("DeviceN..");
3778 apple_init_profile(ppd, NULL, profiles->profiles + 1, profile_id,
3779 "DeviceN", "DeviceN", NULL);
3782 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3785 if (num_profiles > 0)
3788 * Make sure we have a default profile ID...
3791 if (!default_profile_id)
3792 default_profile_id = profile_id; /* Last profile */
3794 # ifdef HAVE_COLORSYNCREGISTERDEVICE
3795 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
3796 default_profile_id);
3799 CFDictionarySetValue(profiles, kColorSyncDeviceDefaultProfileID,
3801 CFRelease(dict_key);
3803 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3806 * Get the device ID hash and pathelogical name dictionary.
3809 cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\"",
3812 device_id = _ppdHashName(p->name);
3813 device_name = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3814 &kCFTypeDictionaryKeyCallBacks,
3815 &kCFTypeDictionaryValueCallBacks);
3816 printer_name = CFStringCreateWithCString(kCFAllocatorDefault,
3817 p->name, kCFStringEncodingUTF8);
3819 if (device_name && printer_name)
3821 CFDictionarySetValue(device_name, CFSTR("en_US"), printer_name);
3824 * Register the device with ColorSync...
3827 # ifdef HAVE_COLORSYNCREGISTERDEVICE
3828 CFTypeRef deviceDictKeys[] =
3830 kColorSyncDeviceDescriptions,
3831 kColorSyncFactoryProfiles,
3832 kColorSyncDeviceUserScope,
3833 kColorSyncDeviceHostScope
3835 CFTypeRef deviceDictVals[] =
3836 { /* Device values */
3839 kCFPreferencesAnyUser,
3840 kCFPreferencesCurrentHost
3842 CFDictionaryRef deviceDict; /* Device dictionary */
3843 CFUUIDRef deviceUUID; /* Device UUID */
3845 deviceDict = CFDictionaryCreate(kCFAllocatorDefault,
3846 (const void **)deviceDictKeys,
3847 (const void **)deviceDictVals,
3848 sizeof(deviceDictKeys) /
3849 sizeof(deviceDictKeys[0]),
3850 &kCFTypeDictionaryKeyCallBacks,
3851 &kCFTypeDictionaryValueCallBacks);
3852 deviceUUID = ColorSyncCreateUUIDFromUInt32(device_id);
3854 if (!deviceDict || !deviceUUID ||
3855 !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass, deviceUUID,
3860 CFRelease(deviceUUID);
3863 CFRelease(deviceDict);
3866 error = CMRegisterColorDevice(cmPrinterDeviceClass, device_id,
3867 device_name, &scope);
3870 * Register the profiles...
3874 error = CMSetDeviceFactoryProfiles(cmPrinterDeviceClass, device_id,
3875 default_profile_id, profiles);
3876 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3886 cupsdLogMessage(CUPSD_LOG_ERROR,
3887 "Unable to register ICC color profiles for \"%s\": %d",
3888 p->name, (int)error);
3891 CFRelease(printer_name);
3894 CFRelease(device_name);
3898 * Free any memory we used...
3901 # ifdef HAVE_COLORSYNCREGISTERDEVICE
3902 CFRelease(profiles);
3905 if (num_profiles > 0)
3907 for (profile = profiles->profiles;
3909 profile ++, num_profiles --)
3910 CFRelease(profile->profileName);
3914 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3921 * 'apple_unregister_profiles()' - Remove color profiles for the specified
3926 apple_unregister_profiles(
3927 cupsd_printer_t *p) /* I - Printer */
3930 * Make sure ColorSync is available...
3933 # ifdef HAVE_COLORSYNCREGISTERDEVICE
3934 if (ColorSyncUnregisterDevice != NULL)
3937 * Because we may have registered the printer profiles using a prior device
3938 * ID-based UUID, remove both the old style UUID and current UUID for the
3942 CFUUIDRef deviceUUID; /* Device UUID */
3944 deviceUUID = ColorSyncCreateUUIDFromUInt32(_ppdHashName(p->name));
3947 ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass, deviceUUID);
3948 CFRelease(deviceUUID);
3953 if (CMUnregisterColorDevice != NULL)
3954 CMUnregisterColorDevice(cmPrinterDeviceClass, _ppdHashName(p->name));
3955 # endif /* HAVE_COLORSYNCREGISTERDEVICE */
3957 #endif /* __APPLE__ */
3961 * 'apply_printer_defaults()' - Apply printer default options to a job.
3965 apply_printer_defaults(
3966 cupsd_printer_t *printer, /* I - Printer */
3967 cupsd_job_t *job) /* I - Job */
3969 int i, /* Looping var */
3970 num_options; /* Number of default options */
3971 cups_option_t *options, /* Default options */
3972 *option; /* Current option */
3976 * Collect all of the default options and add the missing ones to the
3980 for (i = printer->num_options, num_options = 0, options = NULL,
3981 option = printer->options;
3984 if (!ippFindAttribute(job->attrs, option->name, IPP_TAG_ZERO))
3986 num_options = cupsAddOption(option->name, option->value, num_options,
3991 * Encode these options as attributes in the job object...
3994 cupsEncodeOptions2(job->attrs, num_options, options, IPP_TAG_JOB);
3995 cupsFreeOptions(num_options, options);
4000 * 'authenticate_job()' - Set job authentication info.
4004 authenticate_job(cupsd_client_t *con, /* I - Client connection */
4005 ipp_attribute_t *uri) /* I - Job URI */
4007 ipp_attribute_t *attr, /* job-id attribute */
4008 *auth_info; /* auth-info attribute */
4009 int jobid; /* Job ID */
4010 cupsd_job_t *job; /* Current job */
4011 char scheme[HTTP_MAX_URI],
4012 /* Method portion of URI */
4013 username[HTTP_MAX_URI],
4014 /* Username portion of URI */
4016 /* Host portion of URI */
4017 resource[HTTP_MAX_URI];
4018 /* Resource portion of URI */
4019 int port; /* Port portion of URI */
4022 cupsdLogMessage(CUPSD_LOG_DEBUG2, "authenticate_job(%p[%d], %s)",
4023 con, con->http.fd, uri->values[0].string.text);
4026 * Start with "everything is OK" status...
4029 con->response->request.status.status_code = IPP_OK;
4032 * See if we have a job URI or a printer URI...
4035 if (!strcmp(uri->name, "printer-uri"))
4038 * Got a printer URI; see if we also have a job-id attribute...
4041 if ((attr = ippFindAttribute(con->request, "job-id",
4042 IPP_TAG_INTEGER)) == NULL)
4044 send_ipp_status(con, IPP_BAD_REQUEST,
4045 _("Got a printer-uri attribute but no job-id."));
4049 jobid = attr->values[0].integer;
4054 * Got a job URI; parse it to get the job ID...
4057 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
4058 sizeof(scheme), username, sizeof(username), host,
4059 sizeof(host), &port, resource, sizeof(resource));
4061 if (strncmp(resource, "/jobs/", 6))
4067 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
4068 uri->values[0].string.text);
4072 jobid = atoi(resource + 6);
4076 * See if the job exists...
4079 if ((job = cupsdFindJob(jobid)) == NULL)
4082 * Nope - return a "not found" error...
4085 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
4090 * See if the job has been completed...
4093 if (job->state_value != IPP_JOB_HELD)
4096 * Return a "not-possible" error...
4099 send_ipp_status(con, IPP_NOT_POSSIBLE,
4100 _("Job #%d is not held for authentication."),
4106 * See if we have already authenticated...
4109 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
4111 if (!con->username[0] && !auth_info)
4113 cupsd_printer_t *printer; /* Job destination */
4116 * No auth data. If we need to authenticate via Kerberos, send a
4117 * HTTP auth challenge, otherwise just return an IPP error...
4120 printer = cupsdFindDest(job->dest);
4122 if (printer && printer->num_auth_info_required > 0 &&
4123 !strcmp(printer->auth_info_required[0], "negotiate"))
4124 send_http_error(con, HTTP_UNAUTHORIZED, printer);
4126 send_ipp_status(con, IPP_NOT_AUTHORIZED,
4127 _("No authentication information provided."));
4132 * See if the job is owned by the requesting user...
4135 if (!validate_user(job, con, job->username, username, sizeof(username)))
4137 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
4138 cupsdFindDest(job->dest));
4143 * Save the authentication information for this job...
4146 save_auth_info(con, job, auth_info);
4149 * Reset the job-hold-until value to "no-hold"...
4152 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
4153 IPP_TAG_KEYWORD)) == NULL)
4154 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4158 attr->value_tag = IPP_TAG_KEYWORD;
4159 cupsdSetString(&(attr->values[0].string.text), "no-hold");
4163 * Release the job and return...
4166 cupsdReleaseJob(job);
4168 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, NULL, job, "Job authenticated by user");
4170 cupsdLogJob(job, CUPSD_LOG_INFO, "Authenticated by \"%s\".", con->username);
4177 * 'cancel_all_jobs()' - Cancel all or selected print jobs.
4181 cancel_all_jobs(cupsd_client_t *con, /* I - Client connection */
4182 ipp_attribute_t *uri) /* I - Job or Printer URI */
4184 int i; /* Looping var */
4185 http_status_t status; /* Policy status */
4186 cups_ptype_t dtype; /* Destination type */
4187 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
4188 userpass[HTTP_MAX_URI], /* Username portion of URI */
4189 hostname[HTTP_MAX_URI], /* Host portion of URI */
4190 resource[HTTP_MAX_URI]; /* Resource portion of URI */
4191 int port; /* Port portion of URI */
4192 ipp_attribute_t *attr; /* Attribute in request */
4193 const char *username = NULL; /* Username */
4194 cupsd_jobaction_t purge = CUPSD_JOB_DEFAULT;
4196 cupsd_printer_t *printer; /* Printer */
4197 ipp_attribute_t *job_ids; /* job-ids attribute */
4198 cupsd_job_t *job; /* Job */
4201 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_all_jobs(%p[%d], %s)", con,
4202 con->http.fd, uri->values[0].string.text);
4205 * Get the jobs to cancel/purge...
4208 switch (con->request->request.op.operation_id)
4210 case IPP_PURGE_JOBS :
4212 * Get the username (if any) for the jobs we want to cancel (only if
4213 * "my-jobs" is specified...
4216 if ((attr = ippFindAttribute(con->request, "my-jobs",
4217 IPP_TAG_BOOLEAN)) != NULL &&
4218 attr->values[0].boolean)
4220 if ((attr = ippFindAttribute(con->request, "requesting-user-name",
4221 IPP_TAG_NAME)) != NULL)
4222 username = attr->values[0].string.text;
4225 send_ipp_status(con, IPP_BAD_REQUEST,
4226 _("Missing requesting-user-name attribute."));
4232 * Look for the "purge-jobs" attribute...
4235 if ((attr = ippFindAttribute(con->request, "purge-jobs",
4236 IPP_TAG_BOOLEAN)) != NULL)
4237 purge = attr->values[0].boolean ? CUPSD_JOB_PURGE : CUPSD_JOB_DEFAULT;
4239 purge = CUPSD_JOB_PURGE;
4242 case IPP_CANCEL_MY_JOBS :
4243 if (con->username[0])
4244 username = con->username;
4245 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
4246 IPP_TAG_NAME)) != NULL)
4247 username = attr->values[0].string.text;
4250 send_ipp_status(con, IPP_BAD_REQUEST,
4251 _("Missing requesting-user-name attribute."));
4259 job_ids = ippFindAttribute(con->request, "job-ids", IPP_TAG_INTEGER);
4262 * See if we have a printer URI...
4265 if (strcmp(uri->name, "printer-uri"))
4267 send_ipp_status(con, IPP_BAD_REQUEST,
4268 _("The printer-uri attribute is required."));
4273 * And if the destination is valid...
4276 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
4282 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text,
4283 scheme, sizeof(scheme), userpass, sizeof(userpass),
4284 hostname, sizeof(hostname), &port,
4285 resource, sizeof(resource));
4287 if ((!strncmp(resource, "/printers/", 10) && resource[10]) ||
4288 (!strncmp(resource, "/classes/", 9) && resource[9]))
4290 send_ipp_status(con, IPP_NOT_FOUND,
4291 _("The printer or class does not exist."));
4299 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
4301 send_http_error(con, status, NULL);
4307 for (i = 0; i < job_ids->num_values; i ++)
4309 if (!cupsdFindJob(job_ids->values[i].integer))
4313 if (i < job_ids->num_values)
4315 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
4316 job_ids->values[i].integer);
4320 for (i = 0; i < job_ids->num_values; i ++)
4322 job = cupsdFindJob(job_ids->values[i].integer);
4324 cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
4325 purge == CUPSD_JOB_PURGE ? "Job purged by user." :
4326 "Job canceled by user.");
4329 cupsdLogMessage(CUPSD_LOG_INFO, "Selected jobs were %s by \"%s\".",
4330 purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
4336 * Cancel all jobs on all printers...
4339 cupsdCancelJobs(NULL, username, purge);
4341 cupsdLogMessage(CUPSD_LOG_INFO, "All jobs were %s by \"%s\".",
4342 purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
4352 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
4355 send_http_error(con, status, printer);
4361 for (i = 0; i < job_ids->num_values; i ++)
4363 if ((job = cupsdFindJob(job_ids->values[i].integer)) == NULL ||
4364 _cups_strcasecmp(job->dest, printer->name))
4368 if (i < job_ids->num_values)
4370 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
4371 job_ids->values[i].integer);
4375 for (i = 0; i < job_ids->num_values; i ++)
4377 job = cupsdFindJob(job_ids->values[i].integer);
4379 cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
4380 purge == CUPSD_JOB_PURGE ? "Job purged by user." :
4381 "Job canceled by user.");
4384 cupsdLogMessage(CUPSD_LOG_INFO, "Selected jobs were %s by \"%s\".",
4385 purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
4391 * Cancel all of the jobs on the named printer...
4394 cupsdCancelJobs(printer->name, username, purge);
4396 cupsdLogMessage(CUPSD_LOG_INFO, "All jobs on \"%s\" were %s by \"%s\".",
4398 purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
4403 con->response->request.status.status_code = IPP_OK;
4408 * 'cancel_job()' - Cancel a print job.
4412 cancel_job(cupsd_client_t *con, /* I - Client connection */
4413 ipp_attribute_t *uri) /* I - Job or Printer URI */
4415 ipp_attribute_t *attr; /* Current attribute */
4416 int jobid; /* Job ID */
4417 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
4418 username[HTTP_MAX_URI], /* Username portion of URI */
4419 host[HTTP_MAX_URI], /* Host portion of URI */
4420 resource[HTTP_MAX_URI]; /* Resource portion of URI */
4421 int port; /* Port portion of URI */
4422 cupsd_job_t *job; /* Job information */
4423 cups_ptype_t dtype; /* Destination type (printer/class) */
4424 cupsd_printer_t *printer; /* Printer data */
4425 cupsd_jobaction_t purge; /* Purge the job? */
4428 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_job(%p[%d], %s)", con,
4429 con->http.fd, uri->values[0].string.text);
4432 * See if we have a job URI or a printer URI...
4435 if (!strcmp(uri->name, "printer-uri"))
4438 * Got a printer URI; see if we also have a job-id attribute...
4441 if ((attr = ippFindAttribute(con->request, "job-id",
4442 IPP_TAG_INTEGER)) == NULL)
4444 send_ipp_status(con, IPP_BAD_REQUEST,
4445 _("Got a printer-uri attribute but no job-id."));
4449 if ((jobid = attr->values[0].integer) == 0)
4452 * Find the current job on the specified printer...
4455 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
4461 send_ipp_status(con, IPP_NOT_FOUND,
4462 _("The printer or class does not exist."));
4467 * See if there are any pending jobs...
4470 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
4472 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
4473 if (job->state_value <= IPP_JOB_PROCESSING &&
4474 !_cups_strcasecmp(job->dest, printer->name))
4482 * No, try stopped jobs...
4485 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
4487 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
4488 if (job->state_value == IPP_JOB_STOPPED &&
4489 !_cups_strcasecmp(job->dest, printer->name))
4496 send_ipp_status(con, IPP_NOT_POSSIBLE, _("No active jobs on %s."),
4506 * Got a job URI; parse it to get the job ID...
4509 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
4510 sizeof(scheme), username, sizeof(username), host,
4511 sizeof(host), &port, resource, sizeof(resource));
4513 if (strncmp(resource, "/jobs/", 6))
4519 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
4520 uri->values[0].string.text);
4524 jobid = atoi(resource + 6);
4528 * Look for the "purge-job" attribute...
4531 if ((attr = ippFindAttribute(con->request, "purge-job",
4532 IPP_TAG_BOOLEAN)) != NULL)
4533 purge = attr->values[0].boolean ? CUPSD_JOB_PURGE : CUPSD_JOB_DEFAULT;
4535 purge = CUPSD_JOB_DEFAULT;
4538 * See if the job exists...
4541 if ((job = cupsdFindJob(jobid)) == NULL)
4544 * Nope - return a "not found" error...
4547 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
4552 * See if the job is owned by the requesting user...
4555 if (!validate_user(job, con, job->username, username, sizeof(username)))
4557 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
4558 cupsdFindDest(job->dest));
4563 * See if the job is already completed, canceled, or aborted; if so,
4564 * we can't cancel...
4567 if (job->state_value >= IPP_JOB_CANCELED && purge != CUPSD_JOB_PURGE)
4569 switch (job->state_value)
4571 case IPP_JOB_CANCELED :
4572 send_ipp_status(con, IPP_NOT_POSSIBLE,
4573 _("Job #%d is already canceled - can\'t cancel."),
4577 case IPP_JOB_ABORTED :
4578 send_ipp_status(con, IPP_NOT_POSSIBLE,
4579 _("Job #%d is already aborted - can\'t cancel."),
4584 send_ipp_status(con, IPP_NOT_POSSIBLE,
4585 _("Job #%d is already completed - can\'t cancel."),
4594 * Cancel the job and return...
4597 cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
4598 purge == CUPSD_JOB_PURGE ? "Job purged by \"%s\"" :
4599 "Job canceled by \"%s\"",
4603 if (purge == CUPSD_JOB_PURGE)
4604 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Purged by \"%s\".", jobid,
4607 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Canceled by \"%s\".", jobid,
4610 con->response->request.status.status_code = IPP_OK;
4615 * 'cancel_subscription()' - Cancel a subscription.
4619 cancel_subscription(
4620 cupsd_client_t *con, /* I - Client connection */
4621 int sub_id) /* I - Subscription ID */
4623 http_status_t status; /* Policy status */
4624 cupsd_subscription_t *sub; /* Subscription */
4627 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4628 "cancel_subscription(con=%p[%d], sub_id=%d)",
4629 con, con->http.fd, sub_id);
4632 * Is the subscription ID valid?
4635 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
4638 * Bad subscription ID...
4641 send_ipp_status(con, IPP_NOT_FOUND,
4642 _("Subscription #%d does not exist."), sub_id);
4650 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
4652 con, sub->owner)) != HTTP_OK)
4654 send_http_error(con, status, sub->dest);
4659 * Cancel the subscription...
4662 cupsdDeleteSubscription(sub, 1);
4664 con->response->request.status.status_code = IPP_OK;
4669 * 'check_rss_recipient()' - Check that we do not have a duplicate RSS feed URI.
4672 static int /* O - 1 if OK, 0 if not */
4673 check_rss_recipient(
4674 const char *recipient) /* I - Recipient URI */
4676 cupsd_subscription_t *sub; /* Current subscription */
4679 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
4681 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
4685 * Compare the URIs up to the first ?...
4688 const char *r1, *r2;
4690 for (r1 = recipient, r2 = sub->recipient;
4691 *r1 == *r2 && *r1 && *r1 != '?' && *r2 && *r2 != '?';
4703 * 'check_quotas()' - Check quotas for a printer and user.
4706 static int /* O - 1 if OK, 0 if forbidden,
4707 -1 if limit reached */
4708 check_quotas(cupsd_client_t *con, /* I - Client connection */
4709 cupsd_printer_t *p) /* I - Printer or class */
4711 char username[33], /* Username */
4712 *name; /* Current user name */
4713 cupsd_quota_t *q; /* Quota data */
4714 #ifdef HAVE_MBR_UID_TO_UUID
4716 * Use Apple membership APIs which require that all names represent
4717 * valid user account or group records accessible by the server.
4720 uuid_t usr_uuid; /* UUID for job requesting user */
4721 uuid_t usr2_uuid; /* UUID for ACL user name entry */
4722 uuid_t grp_uuid; /* UUID for ACL group name entry */
4723 int mbr_err; /* Error from membership function */
4724 int is_member; /* Is this user a member? */
4727 * Use standard POSIX APIs for checking users and groups...
4730 struct passwd *pw; /* User password data */
4731 #endif /* HAVE_MBR_UID_TO_UUID */
4734 cupsdLogMessage(CUPSD_LOG_DEBUG2, "check_quotas(%p[%d], %p[%s])",
4735 con, con->http.fd, p, p->name);
4738 * Figure out who is printing...
4741 strlcpy(username, get_username(con), sizeof(username));
4743 if ((name = strchr(username, '@')) != NULL)
4744 *name = '\0'; /* Strip @REALM */
4747 * Check global active job limits for printers and users...
4750 if (MaxJobsPerPrinter)
4753 * Check if there are too many pending jobs on this printer...
4756 if (cupsdGetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
4758 cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for printer \"%s\"...",
4767 * Check if there are too many pending jobs for this user...
4770 if (cupsdGetUserJobCount(username) >= MaxJobsPerUser)
4772 cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for user \"%s\"...",
4779 * Check against users...
4782 if (cupsArrayCount(p->users) == 0 && p->k_limit == 0 && p->page_limit == 0)
4785 if (cupsArrayCount(p->users))
4787 #ifdef HAVE_MBR_UID_TO_UUID
4789 * Get UUID for job requesting user...
4792 if (mbr_user_name_to_uuid((char *)username, usr_uuid))
4798 cupsdLogMessage(CUPSD_LOG_DEBUG,
4799 "check_quotas: UUID lookup failed for user \"%s\"",
4801 cupsdLogMessage(CUPSD_LOG_INFO,
4802 "Denying user \"%s\" access to printer \"%s\" "
4803 "(unknown user)...",
4809 * Get UID and GID of requesting user...
4812 pw = getpwnam(username);
4814 #endif /* HAVE_MBR_UID_TO_UUID */
4816 for (name = (char *)cupsArrayFirst(p->users);
4818 name = (char *)cupsArrayNext(p->users))
4822 * Check group membership...
4825 #ifdef HAVE_MBR_UID_TO_UUID
4828 if (uuid_parse(name + 2, grp_uuid))
4829 uuid_clear(grp_uuid);
4831 else if ((mbr_err = mbr_group_name_to_uuid(name + 1, grp_uuid)) != 0)
4834 * Invalid ACL entries are ignored for matching; just record a
4835 * warning in the log...
4838 cupsdLogMessage(CUPSD_LOG_DEBUG,
4839 "check_quotas: UUID lookup failed for ACL entry "
4840 "\"%s\" (err=%d)", name, mbr_err);
4841 cupsdLogMessage(CUPSD_LOG_WARN,
4842 "Access control entry \"%s\" not a valid group name; "
4843 "entry ignored", name);
4846 if ((mbr_err = mbr_check_membership(usr_uuid, grp_uuid,
4850 * At this point, there should be no errors, but check anyways...
4853 cupsdLogMessage(CUPSD_LOG_DEBUG,
4854 "check_quotas: group \"%s\" membership check "
4855 "failed (err=%d)", name + 1, mbr_err);
4860 * Stop if we found a match...
4867 if (cupsdCheckGroup(username, pw, name + 1))
4869 #endif /* HAVE_MBR_UID_TO_UUID */
4871 #ifdef HAVE_MBR_UID_TO_UUID
4876 if (uuid_parse(name + 1, usr2_uuid))
4877 uuid_clear(usr2_uuid);
4879 else if ((mbr_err = mbr_user_name_to_uuid(name, usr2_uuid)) != 0)
4882 * Invalid ACL entries are ignored for matching; just record a
4883 * warning in the log...
4886 cupsdLogMessage(CUPSD_LOG_DEBUG,
4887 "check_quotas: UUID lookup failed for ACL entry "
4888 "\"%s\" (err=%d)", name, mbr_err);
4889 cupsdLogMessage(CUPSD_LOG_WARN,
4890 "Access control entry \"%s\" not a valid user name; "
4891 "entry ignored", name);
4894 if (!uuid_compare(usr_uuid, usr2_uuid))
4898 else if (!_cups_strcasecmp(username, name))
4900 #endif /* HAVE_MBR_UID_TO_UUID */
4902 if ((name != NULL) == p->deny_users)
4904 cupsdLogMessage(CUPSD_LOG_INFO,
4905 "Denying user \"%s\" access to printer \"%s\"...",
4915 if (p->k_limit || p->page_limit)
4917 if ((q = cupsdUpdateQuota(p, username, 0, 0)) == NULL)
4919 cupsdLogMessage(CUPSD_LOG_ERROR,
4920 "Unable to allocate quota data for user \"%s\"",
4925 if ((q->k_count >= p->k_limit && p->k_limit) ||
4926 (q->page_count >= p->page_limit && p->page_limit))
4928 cupsdLogMessage(CUPSD_LOG_INFO, "User \"%s\" is over the quota limit...",
4935 * If we have gotten this far, we're done!
4943 * 'close_job()' - Close a multi-file job.
4947 close_job(cupsd_client_t *con, /* I - Client connection */
4948 ipp_attribute_t *uri) /* I - Printer URI */
4950 cupsd_job_t *job; /* Job */
4951 ipp_attribute_t *attr; /* Attribute */
4952 char job_uri[HTTP_MAX_URI],
4954 username[256]; /* User name */
4957 cupsdLogMessage(CUPSD_LOG_DEBUG2, "close_job(%p[%d], %s)", con,
4958 con->http.fd, uri->values[0].string.text);
4961 * See if we have a job URI or a printer URI...
4964 if (strcmp(uri->name, "printer-uri"))
4967 * job-uri is not supported by Close-Job!
4970 send_ipp_status(con, IPP_BAD_REQUEST,
4971 _("Close-Job doesn't support the job-uri attribute."));
4976 * Got a printer URI; see if we also have a job-id attribute...
4979 if ((attr = ippFindAttribute(con->request, "job-id",
4980 IPP_TAG_INTEGER)) == NULL)
4982 send_ipp_status(con, IPP_BAD_REQUEST,
4983 _("Got a printer-uri attribute but no job-id."));
4987 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
4990 * Nope - return a "not found" error...
4993 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
4994 attr->values[0].integer);
4999 * See if the job is owned by the requesting user...
5002 if (!validate_user(job, con, job->username, username, sizeof(username)))
5004 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
5005 cupsdFindDest(job->dest));
5010 * Add any ending sheet...
5013 if (cupsdTimeoutJob(job))
5016 if (job->state_value == IPP_JOB_STOPPED)
5018 job->state->values[0].integer = IPP_JOB_PENDING;
5019 job->state_value = IPP_JOB_PENDING;
5021 else if (job->state_value == IPP_JOB_HELD)
5023 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
5024 IPP_TAG_KEYWORD)) == NULL)
5025 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
5027 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
5029 job->state->values[0].integer = IPP_JOB_PENDING;
5030 job->state_value = IPP_JOB_PENDING;
5035 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
5038 * Fill in the response info...
5041 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
5042 con->servername, con->serverport, "/jobs/%d", job->id);
5043 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
5046 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
5048 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
5051 add_job_state_reasons(con, job);
5053 con->response->request.status.status_code = IPP_OK;
5056 * Start the job if necessary...
5064 * 'copy_attribute()' - Copy a single attribute.
5067 static ipp_attribute_t * /* O - New attribute */
5069 ipp_t *to, /* O - Destination request/response */
5070 ipp_attribute_t *attr, /* I - Attribute to copy */
5071 int quickcopy) /* I - Do a quick copy? */
5073 int i; /* Looping var */
5074 ipp_attribute_t *toattr; /* Destination attribute */
5077 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5078 "copy_attribute(%p, %p[%s,%x,%x])", to, attr,
5079 attr->name ? attr->name : "(null)", attr->group_tag,
5082 switch (attr->value_tag & ~IPP_TAG_COPY)
5085 toattr = ippAddSeparator(to);
5088 case IPP_TAG_INTEGER :
5090 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
5091 attr->name, attr->num_values, NULL);
5093 for (i = 0; i < attr->num_values; i ++)
5094 toattr->values[i].integer = attr->values[i].integer;
5097 case IPP_TAG_BOOLEAN :
5098 toattr = ippAddBooleans(to, attr->group_tag, attr->name,
5099 attr->num_values, NULL);
5101 for (i = 0; i < attr->num_values; i ++)
5102 toattr->values[i].boolean = attr->values[i].boolean;
5105 case IPP_TAG_STRING :
5108 case IPP_TAG_KEYWORD :
5110 case IPP_TAG_URISCHEME :
5111 case IPP_TAG_CHARSET :
5112 case IPP_TAG_LANGUAGE :
5113 case IPP_TAG_MIMETYPE :
5114 toattr = ippAddStrings(to, attr->group_tag,
5115 (ipp_tag_t)(attr->value_tag | quickcopy),
5116 attr->name, attr->num_values, NULL, NULL);
5120 for (i = 0; i < attr->num_values; i ++)
5121 toattr->values[i].string.text = attr->values[i].string.text;
5123 else if (attr->value_tag & IPP_TAG_COPY)
5125 for (i = 0; i < attr->num_values; i ++)
5126 toattr->values[i].string.text =
5127 _cupsStrAlloc(attr->values[i].string.text);
5131 for (i = 0; i < attr->num_values; i ++)
5132 toattr->values[i].string.text =
5133 _cupsStrRetain(attr->values[i].string.text);
5138 toattr = ippAddDate(to, attr->group_tag, attr->name,
5139 attr->values[0].date);
5142 case IPP_TAG_RESOLUTION :
5143 toattr = ippAddResolutions(to, attr->group_tag, attr->name,
5144 attr->num_values, IPP_RES_PER_INCH,
5147 for (i = 0; i < attr->num_values; i ++)
5149 toattr->values[i].resolution.xres = attr->values[i].resolution.xres;
5150 toattr->values[i].resolution.yres = attr->values[i].resolution.yres;
5151 toattr->values[i].resolution.units = attr->values[i].resolution.units;
5155 case IPP_TAG_RANGE :
5156 toattr = ippAddRanges(to, attr->group_tag, attr->name,
5157 attr->num_values, NULL, NULL);
5159 for (i = 0; i < attr->num_values; i ++)
5161 toattr->values[i].range.lower = attr->values[i].range.lower;
5162 toattr->values[i].range.upper = attr->values[i].range.upper;
5166 case IPP_TAG_TEXTLANG :
5167 case IPP_TAG_NAMELANG :
5168 toattr = ippAddStrings(to, attr->group_tag,
5169 (ipp_tag_t)(attr->value_tag | quickcopy),
5170 attr->name, attr->num_values, NULL, NULL);
5174 for (i = 0; i < attr->num_values; i ++)
5176 toattr->values[i].string.charset = attr->values[i].string.charset;
5177 toattr->values[i].string.text = attr->values[i].string.text;
5180 else if (attr->value_tag & IPP_TAG_COPY)
5182 for (i = 0; i < attr->num_values; i ++)
5185 toattr->values[i].string.charset =
5186 _cupsStrAlloc(attr->values[i].string.charset);
5188 toattr->values[i].string.charset =
5189 toattr->values[0].string.charset;
5191 toattr->values[i].string.text =
5192 _cupsStrAlloc(attr->values[i].string.text);
5197 for (i = 0; i < attr->num_values; i ++)
5200 toattr->values[i].string.charset =
5201 _cupsStrRetain(attr->values[i].string.charset);
5203 toattr->values[i].string.charset =
5204 toattr->values[0].string.charset;
5206 toattr->values[i].string.text =
5207 _cupsStrRetain(attr->values[i].string.text);
5212 case IPP_TAG_BEGIN_COLLECTION :
5213 toattr = ippAddCollections(to, attr->group_tag, attr->name,
5214 attr->num_values, NULL);
5216 for (i = 0; i < attr->num_values; i ++)
5218 toattr->values[i].collection = attr->values[i].collection;
5219 attr->values[i].collection->use ++;
5224 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
5225 attr->name, attr->num_values, NULL);
5227 for (i = 0; i < attr->num_values; i ++)
5229 toattr->values[i].unknown.length = attr->values[i].unknown.length;
5231 if (toattr->values[i].unknown.length > 0)
5233 if ((toattr->values[i].unknown.data =
5234 malloc(toattr->values[i].unknown.length)) == NULL)
5235 toattr->values[i].unknown.length = 0;
5237 memcpy(toattr->values[i].unknown.data,
5238 attr->values[i].unknown.data,
5239 toattr->values[i].unknown.length);
5242 break; /* anti-compiler-warning-code */
5250 * 'copy_attrs()' - Copy attributes from one request to another.
5254 copy_attrs(ipp_t *to, /* I - Destination request */
5255 ipp_t *from, /* I - Source request */
5256 cups_array_t *ra, /* I - Requested attributes */
5257 ipp_tag_t group, /* I - Group to copy */
5258 int quickcopy, /* I - Do a quick copy? */
5259 cups_array_t *exclude) /* I - Attributes to exclude? */
5261 ipp_attribute_t *fromattr; /* Source attribute */
5264 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5265 "copy_attrs(to=%p, from=%p, ra=%p, group=%x, quickcopy=%d)",
5266 to, from, ra, group, quickcopy);
5271 for (fromattr = from->attrs; fromattr; fromattr = fromattr->next)
5274 * Filter attributes as needed...
5277 if ((group != IPP_TAG_ZERO && fromattr->group_tag != group &&
5278 fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
5281 if (!strcmp(fromattr->name, "job-printer-uri"))
5285 (cupsArrayFind(exclude, fromattr->name) ||
5286 cupsArrayFind(exclude, "all")))
5289 * We need to exclude this attribute for security reasons; we require the
5290 * job-id attribute regardless of the security settings for IPP
5293 * The job-printer-uri attribute is handled by copy_job_attrs().
5295 * Subscription attribute security is handled by copy_subscription_attrs().
5298 if (strcmp(fromattr->name, "job-id"))
5302 if (!ra || cupsArrayFind(ra, fromattr->name))
5305 * Don't send collection attributes by default to IPP/1.x clients
5306 * since many do not support collections. Also don't send
5307 * media-col-database unless specifically requested by the client.
5310 if (fromattr->value_tag == IPP_TAG_BEGIN_COLLECTION &&
5312 (to->request.status.version[0] == 1 ||
5313 !strcmp(fromattr->name, "media-col-database")))
5316 copy_attribute(to, fromattr, quickcopy);
5323 * 'copy_banner()' - Copy a banner file to the requests directory for the
5327 static int /* O - Size of banner file in kbytes */
5328 copy_banner(cupsd_client_t *con, /* I - Client connection */
5329 cupsd_job_t *job, /* I - Job information */
5330 const char *name) /* I - Name of banner */
5332 int i; /* Looping var */
5333 int kbytes; /* Size of banner file in kbytes */
5334 char filename[1024]; /* Job filename */
5335 cupsd_banner_t *banner; /* Pointer to banner */
5336 cups_file_t *in; /* Input file */
5337 cups_file_t *out; /* Output file */
5338 int ch; /* Character from file */
5339 char attrname[255], /* Name of attribute */
5340 *s; /* Pointer into name */
5341 ipp_attribute_t *attr; /* Attribute */
5344 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5345 "copy_banner(con=%p[%d], job=%p[%d], name=\"%s\")",
5346 con, con ? con->http.fd : -1, job, job->id,
5347 name ? name : "(null)");
5350 * Find the banner; return if not found or "none"...
5353 if (!name || !strcmp(name, "none") ||
5354 (banner = cupsdFindBanner(name)) == NULL)
5358 * Open the banner and job files...
5361 if (add_file(con, job, banner->filetype, 0))
5364 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
5366 if ((out = cupsFileOpen(filename, "w")) == NULL)
5368 cupsdLogMessage(CUPSD_LOG_ERROR,
5369 "Unable to create banner job file %s - %s",
5370 filename, strerror(errno));
5375 fchmod(cupsFileNumber(out), 0640);
5376 fchown(cupsFileNumber(out), RunUser, Group);
5379 * Try the localized banner file under the subdirectory...
5382 strlcpy(attrname, job->attrs->attrs->next->values[0].string.text,
5384 if (strlen(attrname) > 2 && attrname[2] == '-')
5387 * Convert ll-cc to ll_CC...
5391 attrname[3] = toupper(attrname[3] & 255);
5392 attrname[4] = toupper(attrname[4] & 255);
5395 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
5398 if (access(filename, 0) && strlen(attrname) > 2)
5401 * Wasn't able to find "ll_CC" locale file; try the non-national
5402 * localization banner directory.
5407 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
5411 if (access(filename, 0))
5414 * Use the non-localized banner file.
5417 snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
5420 if ((in = cupsFileOpen(filename, "r")) == NULL)
5424 cupsdLogMessage(CUPSD_LOG_ERROR,
5425 "Unable to open banner template file %s - %s",
5426 filename, strerror(errno));
5432 * Parse the file to the end...
5435 while ((ch = cupsFileGetChar(in)) != EOF)
5439 * Get an attribute name...
5442 for (s = attrname; (ch = cupsFileGetChar(in)) != EOF;)
5443 if (!isalpha(ch & 255) && ch != '-' && ch != '?')
5445 else if (s < (attrname + sizeof(attrname) - 1))
5455 * Ignore { followed by stuff that is not an attribute name...
5458 cupsFilePrintf(out, "{%s%c", attrname, ch);
5463 * See if it is defined...
5466 if (attrname[0] == '?')
5471 if (!strcmp(s, "printer-name"))
5473 cupsFilePuts(out, job->dest);
5476 else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
5479 * See if we have a leading question mark...
5482 if (attrname[0] != '?')
5485 * Nope, write to file as-is; probably a PostScript procedure...
5488 cupsFilePrintf(out, "{%s}", attrname);
5495 * Output value(s)...
5498 for (i = 0; i < attr->num_values; i ++)
5501 cupsFilePutChar(out, ',');
5503 switch (attr->value_tag)
5505 case IPP_TAG_INTEGER :
5507 if (!strncmp(s, "time-at-", 8))
5509 struct timeval tv; /* Time value */
5511 tv.tv_sec = attr->values[i].integer;
5514 cupsFilePuts(out, cupsdGetDateTime(&tv, CUPSD_TIME_STANDARD));
5517 cupsFilePrintf(out, "%d", attr->values[i].integer);
5520 case IPP_TAG_BOOLEAN :
5521 cupsFilePrintf(out, "%d", attr->values[i].boolean);
5524 case IPP_TAG_NOVALUE :
5525 cupsFilePuts(out, "novalue");
5528 case IPP_TAG_RANGE :
5529 cupsFilePrintf(out, "%d-%d", attr->values[i].range.lower,
5530 attr->values[i].range.upper);
5533 case IPP_TAG_RESOLUTION :
5534 cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
5535 attr->values[i].resolution.yres,
5536 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5541 case IPP_TAG_STRING :
5544 case IPP_TAG_KEYWORD :
5545 case IPP_TAG_CHARSET :
5546 case IPP_TAG_LANGUAGE :
5547 if (!_cups_strcasecmp(banner->filetype->type, "postscript"))
5550 * Need to quote strings for PS banners...
5555 for (p = attr->values[i].string.text; *p; p ++)
5557 if (*p == '(' || *p == ')' || *p == '\\')
5559 cupsFilePutChar(out, '\\');
5560 cupsFilePutChar(out, *p);
5562 else if (*p < 32 || *p > 126)
5563 cupsFilePrintf(out, "\\%03o", *p & 255);
5565 cupsFilePutChar(out, *p);
5569 cupsFilePuts(out, attr->values[i].string.text);
5573 break; /* anti-compiler-warning-code */
5577 else if (ch == '\\') /* Quoted char */
5579 ch = cupsFileGetChar(in);
5581 if (ch != '{') /* Only do special handling for \{ */
5582 cupsFilePutChar(out, '\\');
5584 cupsFilePutChar(out, ch);
5587 cupsFilePutChar(out, ch);
5591 kbytes = (cupsFileTell(out) + 1023) / 1024;
5593 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
5594 IPP_TAG_INTEGER)) != NULL)
5595 attr->values[0].integer += kbytes;
5604 * 'copy_file()' - Copy a PPD file or interface script...
5607 static int /* O - 0 = success, -1 = error */
5608 copy_file(const char *from, /* I - Source file */
5609 const char *to) /* I - Destination file */
5611 cups_file_t *src, /* Source file */
5612 *dst; /* Destination file */
5613 int bytes; /* Bytes to read/write */
5614 char buffer[2048]; /* Copy buffer */
5617 cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_file(\"%s\", \"%s\")", from, to);
5620 * Open the source and destination file for a copy...
5623 if ((src = cupsFileOpen(from, "rb")) == NULL)
5626 if ((dst = cupsFileOpen(to, "wb")) == NULL)
5633 * Copy the source file to the destination...
5636 while ((bytes = cupsFileRead(src, buffer, sizeof(buffer))) > 0)
5637 if (cupsFileWrite(dst, buffer, bytes) < bytes)
5645 * Close both files and return...
5650 return (cupsFileClose(dst));
5655 * 'copy_model()' - Copy a PPD model file, substituting default values
5659 static int /* O - 0 = success, -1 = error */
5660 copy_model(cupsd_client_t *con, /* I - Client connection */
5661 const char *from, /* I - Source file */
5662 const char *to) /* I - Destination file */
5664 fd_set input; /* select() input set */
5665 struct timeval timeout; /* select() timeout */
5666 int maxfd; /* Max file descriptor for select() */
5667 char tempfile[1024]; /* Temporary PPD file */
5668 int tempfd; /* Temporary PPD file descriptor */
5669 int temppid; /* Process ID of cups-driverd */
5670 int temppipe[2]; /* Temporary pipes */
5671 char *argv[4], /* Command-line arguments */
5672 *envp[MAX_ENV]; /* Environment */
5673 cups_file_t *src, /* Source file */
5674 *dst; /* Destination file */
5675 ppd_file_t *ppd; /* PPD file */
5676 int bytes, /* Bytes from pipe */
5677 total; /* Total bytes from pipe */
5678 char buffer[2048]; /* Copy buffer */
5679 int i; /* Looping var */
5680 char option[PPD_MAX_NAME], /* Option name */
5681 choice[PPD_MAX_NAME]; /* Choice name */
5682 ppd_size_t *size; /* Default size */
5683 int num_defaults; /* Number of default options */
5684 cups_option_t *defaults; /* Default options */
5685 char cups_protocol[PPD_MAX_LINE];
5686 /* cupsProtocol attribute */
5689 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5690 "copy_model(con=%p, from=\"%s\", to=\"%s\")",
5694 * Run cups-driverd to get the PPD file...
5697 argv[0] = "cups-driverd";
5699 argv[2] = (char *)from;
5702 cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
5704 snprintf(buffer, sizeof(buffer), "%s/daemon/cups-driverd", ServerBin);
5705 snprintf(tempfile, sizeof(tempfile), "%s/%d.ppd", TempDir, con->http.fd);
5706 tempfd = open(tempfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
5707 if (tempfd < 0 || cupsdOpenPipe(temppipe))
5710 cupsdLogMessage(CUPSD_LOG_DEBUG,
5711 "copy_model: Running \"cups-driverd cat %s\"...", from);
5713 if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1],
5714 -1, -1, 0, DefaultProfile, NULL, &temppid))
5725 * Wait up to 30 seconds for the PPD file to be copied...
5730 if (temppipe[0] > CGIPipes[0])
5731 maxfd = temppipe[0] + 1;
5733 maxfd = CGIPipes[0] + 1;
5738 * See if we have data ready...
5742 FD_SET(temppipe[0], &input);
5743 FD_SET(CGIPipes[0], &input);
5745 timeout.tv_sec = 30;
5746 timeout.tv_usec = 0;
5748 if ((i = select(maxfd, &input, NULL, NULL, &timeout)) < 0)
5758 * We have timed out...
5764 if (FD_ISSET(temppipe[0], &input))
5767 * Read the PPD file from the pipe, and write it to the PPD file.
5770 if ((bytes = read(temppipe[0], buffer, sizeof(buffer))) > 0)
5772 if (write(tempfd, buffer, bytes) < bytes)
5781 if (FD_ISSET(CGIPipes[0], &input))
5791 * No data from cups-deviced...
5794 cupsdLogMessage(CUPSD_LOG_ERROR, "copy_model: empty PPD file");
5800 * Read the source file and see what page sizes are supported...
5803 if ((ppd = ppdOpenFile(tempfile)) == NULL)
5810 * Open the destination (if possible) and set the default options...
5815 cups_protocol[0] = '\0';
5817 if ((dst = cupsFileOpen(to, "rb")) != NULL)
5820 * Read all of the default lines from the old PPD...
5823 while (cupsFileGets(dst, buffer, sizeof(buffer)))
5824 if (!strncmp(buffer, "*Default", 8))
5827 * Add the default option...
5830 if (!ppd_parse_line(buffer, option, sizeof(option),
5831 choice, sizeof(choice)))
5833 ppd_option_t *ppdo; /* PPD option */
5837 * Only add the default if the default hasn't already been
5838 * set and the choice exists in the new PPD...
5841 if (!cupsGetOption(option, num_defaults, defaults) &&
5842 (ppdo = ppdFindOption(ppd, option)) != NULL &&
5843 ppdFindChoice(ppdo, choice))
5844 num_defaults = cupsAddOption(option, choice, num_defaults,
5848 else if (!strncmp(buffer, "*cupsProtocol:", 14))
5849 strlcpy(cups_protocol, buffer, sizeof(cups_protocol));
5853 else if ((size = ppdPageSize(ppd, DefaultPaperSize)) != NULL)
5856 * Add the default media sizes...
5859 num_defaults = cupsAddOption("PageSize", size->name,
5860 num_defaults, &defaults);
5861 num_defaults = cupsAddOption("PageRegion", size->name,
5862 num_defaults, &defaults);
5863 num_defaults = cupsAddOption("PaperDimension", size->name,
5864 num_defaults, &defaults);
5865 num_defaults = cupsAddOption("ImageableArea", size->name,
5866 num_defaults, &defaults);
5872 * Open the source file for a copy...
5875 if ((src = cupsFileOpen(tempfile, "rb")) == NULL)
5877 cupsFreeOptions(num_defaults, defaults);
5883 * Open the destination file for a copy...
5886 if ((dst = cupsFileOpen(to, "wb")) == NULL)
5888 cupsFreeOptions(num_defaults, defaults);
5895 * Copy the source file to the destination...
5898 while (cupsFileGets(src, buffer, sizeof(buffer)))
5900 if (!strncmp(buffer, "*Default", 8))
5903 * Check for an previous default option choice...
5906 if (!ppd_parse_line(buffer, option, sizeof(option),
5907 choice, sizeof(choice)))
5909 const char *val; /* Default option value */
5912 if ((val = cupsGetOption(option, num_defaults, defaults)) != NULL)
5915 * Substitute the previous choice...
5918 snprintf(buffer, sizeof(buffer), "*Default%s: %s", option, val);
5923 cupsFilePrintf(dst, "%s\n", buffer);
5926 if (cups_protocol[0])
5927 cupsFilePrintf(dst, "%s\n", cups_protocol);
5929 cupsFreeOptions(num_defaults, defaults);
5932 * Close both files and return...
5939 return (cupsFileClose(dst));
5944 * 'copy_job_attrs()' - Copy job attributes.
5948 copy_job_attrs(cupsd_client_t *con, /* I - Client connection */
5949 cupsd_job_t *job, /* I - Job */
5950 cups_array_t *ra, /* I - Requested attributes array */
5951 cups_array_t *exclude) /* I - Private attributes array */
5953 char job_uri[HTTP_MAX_URI]; /* Job URI */
5957 * Send the requested attributes for each job...
5960 if (!cupsArrayFind(exclude, "all"))
5962 if ((!exclude || !cupsArrayFind(exclude, "document-count")) &&
5963 (!ra || cupsArrayFind(ra, "document-count")))
5964 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
5965 "document-count", job->num_files);
5967 if ((!exclude || !cupsArrayFind(exclude, "job-media-progress")) &&
5968 (!ra || cupsArrayFind(ra, "job-media-progress")))
5969 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
5970 "job-media-progress", job->progress);
5972 if ((!exclude || !cupsArrayFind(exclude, "job-more-info")) &&
5973 (!ra || cupsArrayFind(ra, "job-more-info")))
5975 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "http",
5976 NULL, con->servername, con->serverport, "/jobs/%d",
5978 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
5979 "job-more-info", NULL, job_uri);
5982 if (job->state_value > IPP_JOB_PROCESSING &&
5983 (!exclude || !cupsArrayFind(exclude, "job-preserved")) &&
5984 (!ra || cupsArrayFind(ra, "job-preserved")))
5985 ippAddBoolean(con->response, IPP_TAG_JOB, "job-preserved",
5986 job->num_files > 0);
5988 if ((!exclude || !cupsArrayFind(exclude, "job-printer-up-time")) &&
5989 (!ra || cupsArrayFind(ra, "job-printer-up-time")))
5990 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
5991 "job-printer-up-time", time(NULL));
5994 if (!ra || cupsArrayFind(ra, "job-printer-uri"))
5996 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
5997 con->servername, con->serverport,
5998 job->dtype & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_CLASS) ?
5999 "/classes/%s" : "/printers/%s",
6001 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
6002 "job-printer-uri", NULL, job_uri);
6005 if (!ra || cupsArrayFind(ra, "job-state-reasons"))
6006 add_job_state_reasons(con, job);
6008 if (!ra || cupsArrayFind(ra, "job-uri"))
6010 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
6011 con->servername, con->serverport, "/jobs/%d",
6013 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
6014 "job-uri", NULL, job_uri);
6017 copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0, exclude);
6022 * 'copy_printer_attrs()' - Copy printer attributes.
6027 cupsd_client_t *con, /* I - Client connection */
6028 cupsd_printer_t *printer, /* I - Printer */
6029 cups_array_t *ra) /* I - Requested attributes array */
6031 char printer_uri[HTTP_MAX_URI];
6033 char printer_icons[HTTP_MAX_URI];
6035 time_t curtime; /* Current time */
6036 int i; /* Looping var */
6040 * Copy the printer attributes to the response using requested-attributes
6041 * and document-format attributes that may be provided by the client.
6044 curtime = time(NULL);
6046 if (!ra || cupsArrayFind(ra, "marker-change-time"))
6047 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
6048 "marker-change-time", printer->marker_time);
6050 if (printer->num_printers > 0 &&
6051 (!ra || cupsArrayFind(ra, "member-uris")))
6053 ipp_attribute_t *member_uris; /* member-uris attribute */
6054 cupsd_printer_t *p2; /* Printer in class */
6055 ipp_attribute_t *p2_uri; /* printer-uri-supported for class printer */
6058 if ((member_uris = ippAddStrings(con->response, IPP_TAG_PRINTER,
6059 IPP_TAG_URI, "member-uris",
6060 printer->num_printers, NULL,
6063 for (i = 0; i < printer->num_printers; i ++)
6065 p2 = printer->printers[i];
6067 if ((p2_uri = ippFindAttribute(p2->attrs, "printer-uri-supported",
6068 IPP_TAG_URI)) != NULL)
6069 member_uris->values[i].string.text =
6070 _cupsStrRetain(p2_uri->values[0].string.text);
6073 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri,
6074 sizeof(printer_uri), "ipp", NULL, con->servername,
6076 (p2->type & CUPS_PRINTER_CLASS) ?
6077 "/classes/%s" : "/printers/%s", p2->name);
6078 member_uris->values[i].string.text = _cupsStrAlloc(printer_uri);
6084 if (printer->alert && (!ra || cupsArrayFind(ra, "printer-alert")))
6085 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_STRING,
6086 "printer-alert", NULL, printer->alert);
6088 if (printer->alert_description &&
6089 (!ra || cupsArrayFind(ra, "printer-alert-description")))
6090 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
6091 "printer-alert-description", NULL,
6092 printer->alert_description);
6094 if (!ra || cupsArrayFind(ra, "printer-current-time"))
6095 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
6096 ippTimeToDate(curtime));
6098 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6099 if (!ra || cupsArrayFind(ra, "printer-dns-sd-name"))
6101 if (printer->reg_name)
6102 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
6103 "printer-dns-sd-name", NULL, printer->reg_name);
6105 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
6106 "printer-dns-sd-name", 0);
6108 #endif /* defined(HAVE_DNSSD) || defined(HAVE_AVAHI) */
6110 if (!ra || cupsArrayFind(ra, "printer-error-policy"))
6111 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
6112 "printer-error-policy", NULL, printer->error_policy);
6114 if (!ra || cupsArrayFind(ra, "printer-error-policy-supported"))
6116 static const char * const errors[] =/* printer-error-policy-supported values */
6119 "retry-current-job",
6124 if (printer->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_CLASS))
6125 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
6126 "printer-error-policy-supported", NULL, "retry-current-job");
6128 ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
6129 "printer-error-policy-supported",
6130 sizeof(errors) / sizeof(errors[0]), NULL, errors);
6133 if (!ra || cupsArrayFind(ra, "printer-icons"))
6135 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_icons, sizeof(printer_icons),
6136 "http", NULL, con->servername, con->serverport,
6137 "/icons/%s.png", printer->name);
6138 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons",
6139 NULL, printer_icons);
6140 cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-icons=\"%s\"", printer_icons);
6143 if (!ra || cupsArrayFind(ra, "printer-is-accepting-jobs"))
6144 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
6145 printer->accepting);
6147 if (!ra || cupsArrayFind(ra, "printer-is-shared"))
6148 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-shared",
6151 if ((!ra || cupsArrayFind(ra, "printer-more-info")) &&
6152 !(printer->type & CUPS_PRINTER_DISCOVERED))
6154 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
6155 "http", NULL, con->servername, con->serverport,
6156 (printer->type & CUPS_PRINTER_CLASS) ?
6157 "/classes/%s" : "/printers/%s", printer->name);
6158 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
6159 "printer-more-info", NULL, printer_uri);
6162 if (!ra || cupsArrayFind(ra, "printer-op-policy"))
6163 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
6164 "printer-op-policy", NULL, printer->op_policy);
6166 if (!ra || cupsArrayFind(ra, "printer-state"))
6167 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
6170 if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
6171 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
6172 "printer-state-change-time", printer->state_time);
6174 if (!ra || cupsArrayFind(ra, "printer-state-message"))
6175 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
6176 "printer-state-message", NULL, printer->state_message);
6178 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
6179 add_printer_state_reasons(con, printer);
6181 if (!ra || cupsArrayFind(ra, "printer-type"))
6183 int type; /* printer-type value */
6186 * Add the CUPS-specific printer-type attribute...
6189 type = printer->type;
6191 if (printer == DefaultPrinter)
6192 type |= CUPS_PRINTER_DEFAULT;
6194 if (!printer->accepting)
6195 type |= CUPS_PRINTER_REJECTING;
6197 if (!printer->shared)
6198 type |= CUPS_PRINTER_NOT_SHARED;
6200 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-type",
6204 if (!ra || cupsArrayFind(ra, "printer-up-time"))
6205 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
6206 "printer-up-time", curtime);
6208 if ((!ra || cupsArrayFind(ra, "printer-uri-supported")) &&
6209 !(printer->type & CUPS_PRINTER_DISCOVERED))
6211 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
6212 "ipp", NULL, con->servername, con->serverport,
6213 (printer->type & CUPS_PRINTER_CLASS) ?
6214 "/classes/%s" : "/printers/%s", printer->name);
6215 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
6216 "printer-uri-supported", NULL, printer_uri);
6217 cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-uri-supported=\"%s\"",
6221 if (!ra || cupsArrayFind(ra, "queued-job-count"))
6222 add_queued_job_count(con, printer);
6224 copy_attrs(con->response, printer->attrs, ra, IPP_TAG_ZERO, 0, NULL);
6225 if (printer->ppd_attrs)
6226 copy_attrs(con->response, printer->ppd_attrs, ra, IPP_TAG_ZERO, 0, NULL);
6227 copy_attrs(con->response, CommonData, ra, IPP_TAG_ZERO, IPP_TAG_COPY, NULL);
6232 * 'copy_subscription_attrs()' - Copy subscription attributes.
6236 copy_subscription_attrs(
6237 cupsd_client_t *con, /* I - Client connection */
6238 cupsd_subscription_t *sub, /* I - Subscription */
6239 cups_array_t *ra, /* I - Requested attributes array */
6240 cups_array_t *exclude) /* I - Private attributes array */
6242 ipp_attribute_t *attr; /* Current attribute */
6243 char printer_uri[HTTP_MAX_URI];
6245 int count; /* Number of events */
6246 unsigned mask; /* Current event mask */
6247 const char *name; /* Current event name */
6250 cupsdLogMessage(CUPSD_LOG_DEBUG2,
6251 "copy_subscription_attrs(con=%p, sub=%p, ra=%p, exclude=%p)",
6252 con, sub, ra, exclude);
6255 * Copy the subscription attributes to the response using the
6256 * requested-attributes attribute that may be provided by the client.
6259 if (!exclude || !cupsArrayFind(exclude, "all"))
6261 if ((!exclude || !cupsArrayFind(exclude, "notify-events")) &&
6262 (!ra || cupsArrayFind(ra, "notify-events")))
6264 cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_subscription_attrs: notify-events");
6266 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
6269 * Simple event list...
6272 ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
6273 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
6274 "notify-events", NULL, name);
6279 * Complex event list...
6282 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
6283 if (sub->mask & mask)
6286 attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
6287 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
6288 "notify-events", count, NULL, NULL);
6290 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
6291 if (sub->mask & mask)
6293 attr->values[count].string.text =
6294 (char *)cupsdEventName((cupsd_eventmask_t)mask);
6301 if ((!exclude || !cupsArrayFind(exclude, "notify-lease-duration")) &&
6302 (!sub->job && (!ra || cupsArrayFind(ra, "notify-lease-duration"))))
6303 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
6304 "notify-lease-duration", sub->lease);
6306 if ((!exclude || !cupsArrayFind(exclude, "notify-recipient-uri")) &&
6307 (sub->recipient && (!ra || cupsArrayFind(ra, "notify-recipient-uri"))))
6308 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
6309 "notify-recipient-uri", NULL, sub->recipient);
6310 else if ((!exclude || !cupsArrayFind(exclude, "notify-pull-method")) &&
6311 (!ra || cupsArrayFind(ra, "notify-pull-method")))
6312 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
6313 "notify-pull-method", NULL, "ippget");
6315 if ((!exclude || !cupsArrayFind(exclude, "notify-subscriber-user-name")) &&
6316 (!ra || cupsArrayFind(ra, "notify-subscriber-user-name")))
6317 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
6318 "notify-subscriber-user-name", NULL, sub->owner);
6320 if ((!exclude || !cupsArrayFind(exclude, "notify-time-interval")) &&
6321 (!ra || cupsArrayFind(ra, "notify-time-interval")))
6322 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
6323 "notify-time-interval", sub->interval);
6325 if (sub->user_data_len > 0 &&
6326 (!exclude || !cupsArrayFind(exclude, "notify-user-data")) &&
6327 (!ra || cupsArrayFind(ra, "notify-user-data")))
6328 ippAddOctetString(con->response, IPP_TAG_SUBSCRIPTION, "notify-user-data",
6329 sub->user_data, sub->user_data_len);
6332 if (sub->job && (!ra || cupsArrayFind(ra, "notify-job-id")))
6333 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
6334 "notify-job-id", sub->job->id);
6336 if (sub->dest && (!ra || cupsArrayFind(ra, "notify-printer-uri")))
6338 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
6339 "ipp", NULL, con->servername, con->serverport,
6340 "/printers/%s", sub->dest->name);
6341 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
6342 "notify-printer-uri", NULL, printer_uri);
6345 if (!ra || cupsArrayFind(ra, "notify-subscription-id"))
6346 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
6347 "notify-subscription-id", sub->id);
6352 * 'create_job()' - Print a file to a printer or class.
6356 create_job(cupsd_client_t *con, /* I - Client connection */
6357 ipp_attribute_t *uri) /* I - Printer URI */
6359 cupsd_printer_t *printer; /* Printer */
6360 cupsd_job_t *job; /* New job */
6363 cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con,
6364 con->http.fd, uri->values[0].string.text);
6367 * Is the destination valid?
6370 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
6376 send_ipp_status(con, IPP_NOT_FOUND,
6377 _("The printer or class does not exist."));
6382 * Create the job object...
6385 if ((job = add_job(con, printer, NULL)) == NULL)
6388 job->pending_timeout = 1;
6391 * Save and log the job...
6394 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
6395 job->dest, job->username);
6400 * 'create_requested_array()' - Create an array for the requested-attributes.
6403 static cups_array_t * /* O - Array of attributes or NULL */
6404 create_requested_array(ipp_t *request) /* I - IPP request */
6406 int i; /* Looping var */
6407 ipp_attribute_t *requested; /* requested-attributes attribute */
6408 cups_array_t *ra; /* Requested attributes array */
6409 char *value; /* Current value */
6413 * Get the requested-attributes attribute, and return NULL if we don't
6417 if ((requested = ippFindAttribute(request, "requested-attributes",
6418 IPP_TAG_KEYWORD)) == NULL)
6422 * If the attribute contains a single "all" keyword, return NULL...
6425 if (requested->num_values == 1 &&
6426 !strcmp(requested->values[0].string.text, "all"))
6430 * Create an array using "strcmp" as the comparison function...
6433 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
6435 for (i = 0; i < requested->num_values; i ++)
6437 value = requested->values[i].string.text;
6439 if (!strcmp(value, "job-template"))
6441 cupsArrayAdd(ra, "copies");
6442 cupsArrayAdd(ra, "copies-default");
6443 cupsArrayAdd(ra, "copies-supported");
6444 cupsArrayAdd(ra, "finishings");
6445 cupsArrayAdd(ra, "finishings-default");
6446 cupsArrayAdd(ra, "finishings-supported");
6447 cupsArrayAdd(ra, "job-hold-until");
6448 cupsArrayAdd(ra, "job-hold-until-default");
6449 cupsArrayAdd(ra, "job-hold-until-supported");
6450 cupsArrayAdd(ra, "job-priority");
6451 cupsArrayAdd(ra, "job-priority-default");
6452 cupsArrayAdd(ra, "job-priority-supported");
6453 cupsArrayAdd(ra, "job-sheets");
6454 cupsArrayAdd(ra, "job-sheets-default");
6455 cupsArrayAdd(ra, "job-sheets-supported");
6456 cupsArrayAdd(ra, "media");
6457 cupsArrayAdd(ra, "media-default");
6458 cupsArrayAdd(ra, "media-supported");
6459 cupsArrayAdd(ra, "multiple-document-handling");
6460 cupsArrayAdd(ra, "multiple-document-handling-default");
6461 cupsArrayAdd(ra, "multiple-document-handling-supported");
6462 cupsArrayAdd(ra, "number-up");
6463 cupsArrayAdd(ra, "number-up-default");
6464 cupsArrayAdd(ra, "number-up-supported");
6465 cupsArrayAdd(ra, "orientation-requested");
6466 cupsArrayAdd(ra, "orientation-requested-default");
6467 cupsArrayAdd(ra, "orientation-requested-supported");
6468 cupsArrayAdd(ra, "page-ranges");
6469 cupsArrayAdd(ra, "page-ranges-supported");
6470 cupsArrayAdd(ra, "printer-resolution");
6471 cupsArrayAdd(ra, "printer-resolution-default");
6472 cupsArrayAdd(ra, "printer-resolution-supported");
6473 cupsArrayAdd(ra, "print-quality");
6474 cupsArrayAdd(ra, "print-quality-default");
6475 cupsArrayAdd(ra, "print-quality-supported");
6476 cupsArrayAdd(ra, "sides");
6477 cupsArrayAdd(ra, "sides-default");
6478 cupsArrayAdd(ra, "sides-supported");
6480 else if (!strcmp(value, "job-description"))
6482 cupsArrayAdd(ra, "date-time-at-completed");
6483 cupsArrayAdd(ra, "date-time-at-creation");
6484 cupsArrayAdd(ra, "date-time-at-processing");
6485 cupsArrayAdd(ra, "job-detailed-status-message");
6486 cupsArrayAdd(ra, "job-document-access-errors");
6487 cupsArrayAdd(ra, "job-id");
6488 cupsArrayAdd(ra, "job-impressions");
6489 cupsArrayAdd(ra, "job-impressions-completed");
6490 cupsArrayAdd(ra, "job-k-octets");
6491 cupsArrayAdd(ra, "job-k-octets-processed");
6492 cupsArrayAdd(ra, "job-media-progress");
6493 cupsArrayAdd(ra, "job-media-sheets");
6494 cupsArrayAdd(ra, "job-media-sheets-completed");
6495 cupsArrayAdd(ra, "job-message-from-operator");
6496 cupsArrayAdd(ra, "job-more-info");
6497 cupsArrayAdd(ra, "job-name");
6498 cupsArrayAdd(ra, "job-originating-user-name");
6499 cupsArrayAdd(ra, "job-printer-up-time");
6500 cupsArrayAdd(ra, "job-printer-uri");
6501 cupsArrayAdd(ra, "job-state");
6502 cupsArrayAdd(ra, "job-state-message");
6503 cupsArrayAdd(ra, "job-state-reasons");
6504 cupsArrayAdd(ra, "job-uri");
6505 cupsArrayAdd(ra, "number-of-documents");
6506 cupsArrayAdd(ra, "number-of-intervening-jobs");
6507 cupsArrayAdd(ra, "output-device-assigned");
6508 cupsArrayAdd(ra, "time-at-completed");
6509 cupsArrayAdd(ra, "time-at-creation");
6510 cupsArrayAdd(ra, "time-at-processing");
6512 else if (!strcmp(value, "printer-description"))
6514 cupsArrayAdd(ra, "charset-configured");
6515 cupsArrayAdd(ra, "charset-supported");
6516 cupsArrayAdd(ra, "color-supported");
6517 cupsArrayAdd(ra, "compression-supported");
6518 cupsArrayAdd(ra, "document-format-default");
6519 cupsArrayAdd(ra, "document-format-supported");
6520 cupsArrayAdd(ra, "generated-natural-language-supported");
6521 cupsArrayAdd(ra, "ipp-versions-supported");
6522 cupsArrayAdd(ra, "job-impressions-supported");
6523 cupsArrayAdd(ra, "job-k-octets-supported");
6524 cupsArrayAdd(ra, "job-media-sheets-supported");
6525 cupsArrayAdd(ra, "job-settable-attributes-supported");
6526 cupsArrayAdd(ra, "multiple-document-jobs-supported");
6527 cupsArrayAdd(ra, "multiple-operation-time-out");
6528 cupsArrayAdd(ra, "natural-language-configured");
6529 cupsArrayAdd(ra, "notify-attributes-supported");
6530 cupsArrayAdd(ra, "notify-lease-duration-default");
6531 cupsArrayAdd(ra, "notify-lease-duration-supported");
6532 cupsArrayAdd(ra, "notify-max-events-supported");
6533 cupsArrayAdd(ra, "notify-events-default");
6534 cupsArrayAdd(ra, "notify-events-supported");
6535 cupsArrayAdd(ra, "notify-pull-method-supported");
6536 cupsArrayAdd(ra, "notify-schemes-supported");
6537 cupsArrayAdd(ra, "operations-supported");
6538 cupsArrayAdd(ra, "pages-per-minute");
6539 cupsArrayAdd(ra, "pages-per-minute-color");
6540 cupsArrayAdd(ra, "pdl-override-supported");
6541 cupsArrayAdd(ra, "printer-alert");
6542 cupsArrayAdd(ra, "printer-alert-description");
6543 cupsArrayAdd(ra, "printer-commands");
6544 cupsArrayAdd(ra, "printer-current-time");
6545 cupsArrayAdd(ra, "printer-driver-installer");
6546 cupsArrayAdd(ra, "printer-dns-sd-name");
6547 cupsArrayAdd(ra, "printer-info");
6548 cupsArrayAdd(ra, "printer-is-accepting-jobs");
6549 cupsArrayAdd(ra, "printer-location");
6550 cupsArrayAdd(ra, "printer-make-and-model");
6551 cupsArrayAdd(ra, "printer-message-from-operator");
6552 cupsArrayAdd(ra, "printer-more-info");
6553 cupsArrayAdd(ra, "printer-more-info-manufacturer");
6554 cupsArrayAdd(ra, "printer-name");
6555 cupsArrayAdd(ra, "printer-state");
6556 cupsArrayAdd(ra, "printer-state-message");
6557 cupsArrayAdd(ra, "printer-state-reasons");
6558 cupsArrayAdd(ra, "printer-settable-attributes-supported");
6559 cupsArrayAdd(ra, "printer-type");
6560 cupsArrayAdd(ra, "printer-up-time");
6561 cupsArrayAdd(ra, "printer-uri-supported");
6562 cupsArrayAdd(ra, "queued-job-count");
6563 cupsArrayAdd(ra, "reference-uri-schemes-supported");
6564 cupsArrayAdd(ra, "uri-authentication-supported");
6565 cupsArrayAdd(ra, "uri-security-supported");
6567 else if (!strcmp(value, "printer-defaults"))
6569 char *name; /* Option name */
6572 for (name = (char *)cupsArrayFirst(CommonDefaults);
6574 name = (char *)cupsArrayNext(CommonDefaults))
6575 cupsArrayAdd(ra, name);
6577 else if (!strcmp(value, "subscription-template"))
6579 cupsArrayAdd(ra, "notify-attributes");
6580 cupsArrayAdd(ra, "notify-charset");
6581 cupsArrayAdd(ra, "notify-events");
6582 cupsArrayAdd(ra, "notify-lease-duration");
6583 cupsArrayAdd(ra, "notify-natural-language");
6584 cupsArrayAdd(ra, "notify-pull-method");
6585 cupsArrayAdd(ra, "notify-recipient-uri");
6586 cupsArrayAdd(ra, "notify-time-interval");
6587 cupsArrayAdd(ra, "notify-user-data");
6590 cupsArrayAdd(ra, value);
6598 * 'create_subscription()' - Create a notification subscription.
6602 create_subscription(
6603 cupsd_client_t *con, /* I - Client connection */
6604 ipp_attribute_t *uri) /* I - Printer URI */
6606 http_status_t status; /* Policy status */
6607 int i; /* Looping var */
6608 ipp_attribute_t *attr; /* Current attribute */
6609 cups_ptype_t dtype; /* Destination type (printer/class) */
6610 char scheme[HTTP_MAX_URI],
6611 /* Scheme portion of URI */
6612 userpass[HTTP_MAX_URI],
6613 /* Username portion of URI */
6615 /* Host portion of URI */
6616 resource[HTTP_MAX_URI];
6617 /* Resource portion of URI */
6618 int port; /* Port portion of URI */
6619 cupsd_printer_t *printer; /* Printer/class */
6620 cupsd_job_t *job; /* Job */
6621 int jobid; /* Job ID */
6622 cupsd_subscription_t *sub; /* Subscription object */
6623 const char *username, /* requesting-user-name or
6624 authenticated username */
6625 *recipient, /* notify-recipient-uri */
6626 *pullmethod; /* notify-pull-method */
6627 ipp_attribute_t *user_data; /* notify-user-data */
6628 int interval, /* notify-time-interval */
6629 lease; /* notify-lease-duration */
6630 unsigned mask; /* notify-events */
6631 ipp_attribute_t *notify_events,/* notify-events(-default) */
6632 *notify_lease; /* notify-lease-duration(-default) */
6636 for (attr = con->request->attrs; attr; attr = attr->next)
6638 if (attr->group_tag != IPP_TAG_ZERO)
6639 cupsdLogMessage(CUPSD_LOG_DEBUG2, "g%04x v%04x %s", attr->group_tag,
6640 attr->value_tag, attr->name);
6642 cupsdLogMessage(CUPSD_LOG_DEBUG2, "----SEP----");
6647 * Is the destination valid?
6650 cupsdLogMessage(CUPSD_LOG_DEBUG,
6651 "cupsdCreateSubscription(con=%p(%d), uri=\"%s\")",
6652 con, con->http.fd, uri->values[0].string.text);
6654 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6655 sizeof(scheme), userpass, sizeof(userpass), host,
6656 sizeof(host), &port, resource, sizeof(resource));
6658 if (!strcmp(resource, "/"))
6660 dtype = (cups_ptype_t)0;
6663 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
6665 dtype = (cups_ptype_t)0;
6668 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
6670 dtype = CUPS_PRINTER_CLASS;
6673 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
6679 send_ipp_status(con, IPP_NOT_FOUND,
6680 _("The printer or class does not exist."));
6690 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
6693 send_http_error(con, status, printer);
6697 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6699 send_http_error(con, status, NULL);
6704 * Get the user that is requesting the subscription...
6707 username = get_username(con);
6710 * Find the first subscription group attribute; return if we have
6714 for (attr = con->request->attrs; attr; attr = attr->next)
6715 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
6720 send_ipp_status(con, IPP_BAD_REQUEST,
6721 _("No subscription attributes in request."));
6726 * Process the subscription attributes in the request...
6729 con->response->request.status.status_code = IPP_BAD_REQUEST;
6737 lease = DefaultLeaseDuration;
6739 mask = CUPSD_EVENT_NONE;
6743 notify_events = ippFindAttribute(printer->attrs, "notify-events-default",
6745 notify_lease = ippFindAttribute(printer->attrs,
6746 "notify-lease-duration-default",
6750 lease = notify_lease->values[0].integer;
6754 notify_events = NULL;
6755 notify_lease = NULL;
6758 while (attr && attr->group_tag != IPP_TAG_ZERO)
6760 if (!strcmp(attr->name, "notify-recipient-uri") &&
6761 attr->value_tag == IPP_TAG_URI)
6764 * Validate the recipient scheme against the ServerBin/notifier
6768 char notifier[1024]; /* Notifier filename */
6771 recipient = attr->values[0].string.text;
6773 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
6774 scheme, sizeof(scheme), userpass, sizeof(userpass),
6775 host, sizeof(host), &port,
6776 resource, sizeof(resource)) < HTTP_URI_OK)
6778 send_ipp_status(con, IPP_NOT_POSSIBLE,
6779 _("Bad notify-recipient-uri \"%s\"."), recipient);
6780 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6781 "notify-status-code", IPP_URI_SCHEME);
6785 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
6787 if (access(notifier, X_OK))
6789 send_ipp_status(con, IPP_NOT_POSSIBLE,
6790 _("notify-recipient-uri URI \"%s\" uses unknown "
6791 "scheme."), recipient);
6792 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6793 "notify-status-code", IPP_URI_SCHEME);
6797 if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
6799 send_ipp_status(con, IPP_NOT_POSSIBLE,
6800 _("notify-recipient-uri URI \"%s\" is already used."),
6802 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6803 "notify-status-code", IPP_ATTRIBUTES);
6807 else if (!strcmp(attr->name, "notify-pull-method") &&
6808 attr->value_tag == IPP_TAG_KEYWORD)
6810 pullmethod = attr->values[0].string.text;
6812 if (strcmp(pullmethod, "ippget"))
6814 send_ipp_status(con, IPP_NOT_POSSIBLE,
6815 _("Bad notify-pull-method \"%s\"."), pullmethod);
6816 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6817 "notify-status-code", IPP_ATTRIBUTES);
6821 else if (!strcmp(attr->name, "notify-charset") &&
6822 attr->value_tag == IPP_TAG_CHARSET &&
6823 strcmp(attr->values[0].string.text, "us-ascii") &&
6824 strcmp(attr->values[0].string.text, "utf-8"))
6826 send_ipp_status(con, IPP_CHARSET,
6827 _("Character set \"%s\" not supported."),
6828 attr->values[0].string.text);
6831 else if (!strcmp(attr->name, "notify-natural-language") &&
6832 (attr->value_tag != IPP_TAG_LANGUAGE ||
6833 strcmp(attr->values[0].string.text, DefaultLanguage)))
6835 send_ipp_status(con, IPP_CHARSET,
6836 _("Language \"%s\" not supported."),
6837 attr->values[0].string.text);
6840 else if (!strcmp(attr->name, "notify-user-data") &&
6841 attr->value_tag == IPP_TAG_STRING)
6843 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
6845 send_ipp_status(con, IPP_REQUEST_VALUE,
6846 _("The notify-user-data value is too large "
6847 "(%d > 63 octets)."),
6848 attr->values[0].unknown.length);
6854 else if (!strcmp(attr->name, "notify-events") &&
6855 attr->value_tag == IPP_TAG_KEYWORD)
6856 notify_events = attr;
6857 else if (!strcmp(attr->name, "notify-lease-duration") &&
6858 attr->value_tag == IPP_TAG_INTEGER)
6859 lease = attr->values[0].integer;
6860 else if (!strcmp(attr->name, "notify-time-interval") &&
6861 attr->value_tag == IPP_TAG_INTEGER)
6862 interval = attr->values[0].integer;
6863 else if (!strcmp(attr->name, "notify-job-id") &&
6864 attr->value_tag == IPP_TAG_INTEGER)
6865 jobid = attr->values[0].integer;
6872 for (i = 0; i < notify_events->num_values; i ++)
6873 mask |= cupsdEventValue(notify_events->values[i].string.text);
6877 cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
6879 cupsdLogMessage(CUPSD_LOG_DEBUG, "pullmethod=\"%s\"", pullmethod);
6880 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-lease-duration=%d", lease);
6881 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-time-interval=%d", interval);
6883 if (!recipient && !pullmethod)
6886 if (mask == CUPSD_EVENT_NONE)
6889 mask = CUPSD_EVENT_JOB_COMPLETED;
6891 mask = CUPSD_EVENT_PRINTER_STATE_CHANGED;
6894 send_ipp_status(con, IPP_BAD_REQUEST,
6895 _("notify-events not specified."));
6900 if (MaxLeaseDuration && (lease == 0 || lease > MaxLeaseDuration))
6902 cupsdLogMessage(CUPSD_LOG_INFO,
6903 "create_subscription: Limiting notify-lease-duration to "
6906 lease = MaxLeaseDuration;
6911 if ((job = cupsdFindJob(jobid)) == NULL)
6913 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
6921 if ((sub = cupsdAddSubscription(mask, printer, job, recipient, 0)) == NULL)
6923 send_ipp_status(con, IPP_TOO_MANY_SUBSCRIPTIONS,
6924 _("There are too many subscriptions."));
6929 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for job %d.",
6932 cupsdLogMessage(CUPSD_LOG_DEBUG,
6933 "Added subscription #%d for printer \"%s\".",
6934 sub->id, printer->name);
6936 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for server.",
6939 sub->interval = interval;
6941 sub->expire = lease ? time(NULL) + lease : 0;
6943 cupsdSetString(&sub->owner, username);
6947 sub->user_data_len = user_data->values[0].unknown.length;
6948 memcpy(sub->user_data, user_data->values[0].unknown.data,
6949 sub->user_data_len);
6952 ippAddSeparator(con->response);
6953 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
6954 "notify-subscription-id", sub->id);
6956 con->response->request.status.status_code = IPP_OK;
6962 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
6967 * 'delete_printer()' - Remove a printer or class from the system.
6971 delete_printer(cupsd_client_t *con, /* I - Client connection */
6972 ipp_attribute_t *uri) /* I - URI of printer or class */
6974 http_status_t status; /* Policy status */
6975 cups_ptype_t dtype; /* Destination type (printer/class) */
6976 cupsd_printer_t *printer; /* Printer/class */
6977 char filename[1024]; /* Script/PPD filename */
6980 cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", con,
6981 con->http.fd, uri->values[0].string.text);
6984 * Do we have a valid URI?
6987 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
6993 send_ipp_status(con, IPP_NOT_FOUND,
6994 _("The printer or class does not exist."));
7002 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7004 send_http_error(con, status, NULL);
7009 * Remove old jobs...
7012 cupsdCancelJobs(printer->name, NULL, 1);
7015 * Remove old subscriptions and send a "deleted printer" event...
7018 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
7019 "%s \"%s\" deleted by \"%s\".",
7020 (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
7021 printer->name, get_username(con));
7023 cupsdExpireSubscriptions(printer, NULL);
7026 * Remove any old PPD or script files...
7029 snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot,
7033 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
7037 snprintf(filename, sizeof(filename), "%s/%s.png", CacheDir, printer->name);
7040 snprintf(filename, sizeof(filename), "%s/%s.data", CacheDir, printer->name);
7044 * Unregister color profiles...
7047 cupsdCmsUnregisterPrinter(printer);
7050 * FIXME: ideally the ColorSync stuff would be moved to colorsync.c
7051 * and the colorsyncUnregisterPrinter() would be called from
7052 * cupsdCmsUnregisterPrinter() in printers.c
7054 apple_unregister_profiles(printer);
7055 #endif /* __APPLE__ */
7057 if (dtype & CUPS_PRINTER_CLASS)
7059 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".",
7060 printer->name, get_username(con));
7062 cupsdDeletePrinter(printer, 0);
7063 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
7067 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".",
7068 printer->name, get_username(con));
7070 if (cupsdDeletePrinter(printer, 0))
7071 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
7073 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
7076 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
7079 * Return with no errors...
7082 con->response->request.status.status_code = IPP_OK;
7087 * 'get_default()' - Get the default destination.
7091 get_default(cupsd_client_t *con) /* I - Client connection */
7093 http_status_t status; /* Policy status */
7094 cups_array_t *ra; /* Requested attributes array */
7097 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->http.fd);
7103 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7105 send_http_error(con, status, NULL);
7111 ra = create_requested_array(con->request);
7113 copy_printer_attrs(con, DefaultPrinter, ra);
7115 cupsArrayDelete(ra);
7117 con->response->request.status.status_code = IPP_OK;
7120 send_ipp_status(con, IPP_NOT_FOUND, _("No default printer."));
7125 * 'get_devices()' - Get the list of available devices on the local system.
7129 get_devices(cupsd_client_t *con) /* I - Client connection */
7131 http_status_t status; /* Policy status */
7132 ipp_attribute_t *limit, /* limit attribute */
7133 *timeout, /* timeout attribute */
7134 *requested, /* requested-attributes attribute */
7135 *exclude, /* exclude-schemes attribute */
7136 *include; /* include-schemes attribute */
7137 char command[1024], /* cups-deviced command */
7138 options[2048], /* Options to pass to command */
7140 /* String for requested attributes */
7142 /* String for excluded schemes */
7144 /* String for included schemes */
7147 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->http.fd);
7153 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7155 send_http_error(con, status, NULL);
7160 * Run cups-deviced command with the given options...
7163 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
7164 timeout = ippFindAttribute(con->request, "timeout", IPP_TAG_INTEGER);
7165 requested = ippFindAttribute(con->request, "requested-attributes",
7167 exclude = ippFindAttribute(con->request, "exclude-schemes", IPP_TAG_NAME);
7168 include = ippFindAttribute(con->request, "include-schemes", IPP_TAG_NAME);
7171 url_encode_attr(requested, requested_str, sizeof(requested_str));
7173 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
7176 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
7178 exclude_str[0] = '\0';
7181 url_encode_attr(include, include_str, sizeof(include_str));
7183 include_str[0] = '\0';
7185 snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
7186 snprintf(options, sizeof(options),
7187 "%d+%d+%d+%d+%s%s%s%s%s",
7188 con->request->request.op.request_id,
7189 limit ? limit->values[0].integer : 0,
7190 timeout ? timeout->values[0].integer : 15,
7193 exclude_str[0] ? "%20" : "", exclude_str,
7194 include_str[0] ? "%20" : "", include_str);
7196 if (cupsdSendCommand(con, command, options, 1))
7199 * Command started successfully, don't send an IPP response here...
7202 ippDelete(con->response);
7203 con->response = NULL;
7208 * Command failed, return "internal error" so the user knows something
7212 send_ipp_status(con, IPP_INTERNAL_ERROR,
7213 _("cups-deviced failed to execute."));
7219 * 'get_document()' - Get a copy of a job file.
7223 get_document(cupsd_client_t *con, /* I - Client connection */
7224 ipp_attribute_t *uri) /* I - Job URI */
7226 http_status_t status; /* Policy status */
7227 ipp_attribute_t *attr; /* Current attribute */
7228 int jobid; /* Job ID */
7229 int docnum; /* Document number */
7230 cupsd_job_t *job; /* Current job */
7231 char scheme[HTTP_MAX_URI], /* Method portion of URI */
7232 username[HTTP_MAX_URI], /* Username portion of URI */
7233 host[HTTP_MAX_URI], /* Host portion of URI */
7234 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7235 int port; /* Port portion of URI */
7236 char filename[1024], /* Filename for document */
7237 format[1024]; /* Format for document */
7240 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", con,
7241 con->http.fd, uri->values[0].string.text);
7244 * See if we have a job URI or a printer URI...
7247 if (!strcmp(uri->name, "printer-uri"))
7250 * Got a printer URI; see if we also have a job-id attribute...
7253 if ((attr = ippFindAttribute(con->request, "job-id",
7254 IPP_TAG_INTEGER)) == NULL)
7256 send_ipp_status(con, IPP_BAD_REQUEST,
7257 _("Got a printer-uri attribute but no job-id."));
7261 jobid = attr->values[0].integer;
7266 * Got a job URI; parse it to get the job ID...
7269 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7270 sizeof(scheme), username, sizeof(username), host,
7271 sizeof(host), &port, resource, sizeof(resource));
7273 if (strncmp(resource, "/jobs/", 6))
7279 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
7280 uri->values[0].string.text);
7284 jobid = atoi(resource + 6);
7288 * See if the job exists...
7291 if ((job = cupsdFindJob(jobid)) == NULL)
7294 * Nope - return a "not found" error...
7297 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
7305 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con,
7306 job->username)) != HTTP_OK)
7308 send_http_error(con, status, NULL);
7313 * Get the document number...
7316 if ((attr = ippFindAttribute(con->request, "document-number",
7317 IPP_TAG_INTEGER)) == NULL)
7319 send_ipp_status(con, IPP_BAD_REQUEST,
7320 _("Missing document-number attribute."));
7324 if ((docnum = attr->values[0].integer) < 1 || docnum > job->num_files ||
7325 attr->num_values > 1)
7327 send_ipp_status(con, IPP_NOT_FOUND,
7328 _("Document #%d does not exist in job #%d."), docnum,
7333 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, jobid,
7335 if ((con->file = open(filename, O_RDONLY)) == -1)
7337 cupsdLogMessage(CUPSD_LOG_ERROR,
7338 "Unable to open document %d in job %d - %s", docnum, jobid,
7340 send_ipp_status(con, IPP_NOT_FOUND,
7341 _("Unable to open document #%d in job #%d."), docnum,
7346 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
7350 snprintf(format, sizeof(format), "%s/%s", job->filetypes[docnum - 1]->super,
7351 job->filetypes[docnum - 1]->type);
7353 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
7355 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "document-number",
7357 if ((attr = ippFindAttribute(job->attrs, "document-name",
7358 IPP_TAG_NAME)) != NULL)
7359 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "document-name",
7360 NULL, attr->values[0].string.text);
7365 * 'get_job_attrs()' - Get job attributes.
7369 get_job_attrs(cupsd_client_t *con, /* I - Client connection */
7370 ipp_attribute_t *uri) /* I - Job URI */
7372 http_status_t status; /* Policy status */
7373 ipp_attribute_t *attr; /* Current attribute */
7374 int jobid; /* Job ID */
7375 cupsd_job_t *job; /* Current job */
7376 cupsd_printer_t *printer; /* Current printer */
7377 cupsd_policy_t *policy; /* Current security policy */
7378 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
7379 username[HTTP_MAX_URI], /* Username portion of URI */
7380 host[HTTP_MAX_URI], /* Host portion of URI */
7381 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7382 int port; /* Port portion of URI */
7383 cups_array_t *ra, /* Requested attributes array */
7384 *exclude; /* Private attributes array */
7387 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", con,
7388 con->http.fd, uri->values[0].string.text);
7391 * See if we have a job URI or a printer URI...
7394 if (!strcmp(uri->name, "printer-uri"))
7397 * Got a printer URI; see if we also have a job-id attribute...
7400 if ((attr = ippFindAttribute(con->request, "job-id",
7401 IPP_TAG_INTEGER)) == NULL)
7403 send_ipp_status(con, IPP_BAD_REQUEST,
7404 _("Got a printer-uri attribute but no job-id."));
7408 jobid = attr->values[0].integer;
7413 * Got a job URI; parse it to get the job ID...
7416 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7417 sizeof(scheme), username, sizeof(username), host,
7418 sizeof(host), &port, resource, sizeof(resource));
7420 if (strncmp(resource, "/jobs/", 6))
7426 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
7427 uri->values[0].string.text);
7431 jobid = atoi(resource + 6);
7435 * See if the job exists...
7438 if ((job = cupsdFindJob(jobid)) == NULL)
7441 * Nope - return a "not found" error...
7444 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
7452 if ((printer = job->printer) == NULL)
7453 printer = cupsdFindDest(job->dest);
7456 policy = printer->op_policy_ptr;
7458 policy = DefaultPolicyPtr;
7460 if ((status = cupsdCheckPolicy(policy, con, job->username)) != HTTP_OK)
7462 send_http_error(con, status, NULL);
7466 exclude = cupsdGetPrivateAttrs(policy, con, printer, job->username);
7469 * Copy attributes...
7474 ra = create_requested_array(con->request);
7475 copy_job_attrs(con, job, ra, exclude);
7476 cupsArrayDelete(ra);
7478 con->response->request.status.status_code = IPP_OK;
7483 * 'get_jobs()' - Get a list of jobs for the specified printer.
7487 get_jobs(cupsd_client_t *con, /* I - Client connection */
7488 ipp_attribute_t *uri) /* I - Printer URI */
7490 http_status_t status; /* Policy status */
7491 ipp_attribute_t *attr; /* Current attribute */
7492 const char *dest; /* Destination */
7493 cups_ptype_t dtype; /* Destination type (printer/class) */
7494 cups_ptype_t dmask; /* Destination type mask */
7495 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
7496 username[HTTP_MAX_URI], /* Username portion of URI */
7497 host[HTTP_MAX_URI], /* Host portion of URI */
7498 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7499 int port; /* Port portion of URI */
7500 int job_comparison; /* Job comparison */
7501 ipp_jstate_t job_state; /* job-state value */
7502 int first_job_id; /* First job ID */
7503 int limit; /* Maximum number of jobs to return */
7504 int count; /* Number of jobs that match */
7505 ipp_attribute_t *job_ids; /* job-ids attribute */
7506 cupsd_job_t *job; /* Current job pointer */
7507 cupsd_printer_t *printer; /* Printer */
7508 cups_array_t *list; /* Which job list... */
7509 cups_array_t *ra, /* Requested attributes array */
7510 *exclude; /* Private attributes array */
7511 cupsd_policy_t *policy; /* Current policy */
7514 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", con, con->http.fd,
7515 uri->values[0].string.text);
7518 * Is the destination valid?
7521 if (strcmp(uri->name, "printer-uri"))
7523 send_ipp_status(con, IPP_BAD_REQUEST, _("No printer-uri in request."));
7527 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7528 sizeof(scheme), username, sizeof(username), host,
7529 sizeof(host), &port, resource, sizeof(resource));
7531 if (!strcmp(resource, "/") || !strcmp(resource, "/jobs"))
7534 dtype = (cups_ptype_t)0;
7535 dmask = (cups_ptype_t)0;
7538 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
7541 dtype = (cups_ptype_t)0;
7542 dmask = CUPS_PRINTER_CLASS;
7545 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
7548 dtype = CUPS_PRINTER_CLASS;
7549 dmask = CUPS_PRINTER_CLASS;
7552 else if ((dest = cupsdValidateDest(uri->values[0].string.text, &dtype,
7559 send_ipp_status(con, IPP_NOT_FOUND,
7560 _("The printer or class does not exist."));
7565 dtype &= CUPS_PRINTER_CLASS;
7566 dmask = CUPS_PRINTER_CLASS;
7574 policy = printer->op_policy_ptr;
7576 policy = DefaultPolicyPtr;
7578 if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
7580 send_http_error(con, status, NULL);
7584 job_ids = ippFindAttribute(con->request, "job-ids", IPP_TAG_INTEGER);
7587 * See if the "which-jobs" attribute have been specified...
7590 if ((attr = ippFindAttribute(con->request, "which-jobs",
7591 IPP_TAG_KEYWORD)) != NULL && job_ids)
7593 send_ipp_status(con, IPP_CONFLICT,
7594 _("The %s attribute cannot be provided with job-ids."),
7598 else if (!attr || !strcmp(attr->values[0].string.text, "not-completed"))
7600 job_comparison = -1;
7601 job_state = IPP_JOB_STOPPED;
7604 else if (!strcmp(attr->values[0].string.text, "completed"))
7607 job_state = IPP_JOB_CANCELED;
7610 else if (!strcmp(attr->values[0].string.text, "aborted"))
7613 job_state = IPP_JOB_ABORTED;
7616 else if (!strcmp(attr->values[0].string.text, "all"))
7619 job_state = IPP_JOB_PENDING;
7622 else if (!strcmp(attr->values[0].string.text, "canceled"))
7625 job_state = IPP_JOB_CANCELED;
7628 else if (!strcmp(attr->values[0].string.text, "pending"))
7631 job_state = IPP_JOB_PENDING;
7634 else if (!strcmp(attr->values[0].string.text, "pending-held"))
7637 job_state = IPP_JOB_HELD;
7640 else if (!strcmp(attr->values[0].string.text, "processing"))
7643 job_state = IPP_JOB_PROCESSING;
7644 list = PrintingJobs;
7646 else if (!strcmp(attr->values[0].string.text, "processing-stopped"))
7649 job_state = IPP_JOB_STOPPED;
7654 send_ipp_status(con, IPP_ATTRIBUTES,
7655 _("The which-jobs value \"%s\" is not supported."),
7656 attr->values[0].string.text);
7657 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
7658 "which-jobs", NULL, attr->values[0].string.text);
7663 * See if they want to limit the number of jobs reported...
7666 if ((attr = ippFindAttribute(con->request, "limit",
7667 IPP_TAG_INTEGER)) != NULL)
7671 send_ipp_status(con, IPP_CONFLICT,
7672 _("The %s attribute cannot be provided with job-ids."),
7677 limit = attr->values[0].integer;
7682 if ((attr = ippFindAttribute(con->request, "first-job-id",
7683 IPP_TAG_INTEGER)) != NULL)
7687 send_ipp_status(con, IPP_CONFLICT,
7688 _("The %s attribute cannot be provided with job-ids."),
7693 first_job_id = attr->values[0].integer;
7699 * See if we only want to see jobs for a specific user...
7702 if ((attr = ippFindAttribute(con->request, "my-jobs",
7703 IPP_TAG_BOOLEAN)) != NULL && job_ids)
7705 send_ipp_status(con, IPP_CONFLICT,
7706 _("The %s attribute cannot be provided with job-ids."),
7710 else if (attr && attr->values[0].boolean)
7711 strlcpy(username, get_username(con), sizeof(username));
7715 if ((ra = create_requested_array(con->request)) == NULL &&
7716 !ippFindAttribute(con->request, "requested-attributes", IPP_TAG_KEYWORD))
7719 * IPP conformance - Get-Jobs has a default requested-attributes value of
7720 * "job-id" and "job-uri".
7723 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
7724 cupsArrayAdd(ra, "job-id");
7725 cupsArrayAdd(ra, "job-uri");
7729 * OK, build a list of jobs for this printer...
7734 int i; /* Looping var */
7736 for (i = 0; i < job_ids->num_values; i ++)
7738 if (!cupsdFindJob(job_ids->values[i].integer))
7742 if (i < job_ids->num_values)
7744 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
7745 job_ids->values[i].integer);
7749 for (i = 0; i < job_ids->num_values; i ++)
7751 job = cupsdFindJob(job_ids->values[i].integer);
7757 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d",
7763 ippAddSeparator(con->response);
7765 exclude = cupsdGetPrivateAttrs(job->printer ?
7766 job->printer->op_policy_ptr :
7767 policy, con, job->printer,
7770 copy_job_attrs(con, job, ra, exclude);
7775 for (count = 0, job = (cupsd_job_t *)cupsArrayFirst(list);
7776 (limit <= 0 || count < limit) && job;
7777 job = (cupsd_job_t *)cupsArrayNext(list))
7780 * Filter out jobs that don't match...
7783 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7784 "get_jobs: job->id=%d, dest=\"%s\", username=\"%s\", "
7785 "state_value=%d, attrs=%p", job->id, job->dest,
7786 job->username, job->state_value, job->attrs);
7788 if (!job->dest || !job->username)
7791 if (!job->dest || !job->username)
7794 if ((dest && strcmp(job->dest, dest)) &&
7795 (!job->printer || !dest || strcmp(job->printer->name, dest)))
7797 if ((job->dtype & dmask) != dtype &&
7798 (!job->printer || (job->printer->type & dmask) != dtype))
7801 if ((job_comparison < 0 && job->state_value > job_state) ||
7802 (job_comparison == 0 && job->state_value != job_state) ||
7803 (job_comparison > 0 && job->state_value < job_state))
7806 if (job->id < first_job_id)
7813 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d",
7818 if (username[0] && _cups_strcasecmp(username, job->username))
7822 ippAddSeparator(con->response);
7826 exclude = cupsdGetPrivateAttrs(job->printer ?
7827 job->printer->op_policy_ptr :
7828 policy, con, job->printer,
7831 copy_job_attrs(con, job, ra, exclude);
7834 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count=%d", count);
7837 cupsArrayDelete(ra);
7839 con->response->request.status.status_code = IPP_OK;
7844 * 'get_notifications()' - Get events for a subscription.
7848 get_notifications(cupsd_client_t *con) /* I - Client connection */
7850 int i, j; /* Looping vars */
7851 http_status_t status; /* Policy status */
7852 cupsd_subscription_t *sub; /* Subscription */
7853 ipp_attribute_t *ids, /* notify-subscription-ids */
7854 *sequences; /* notify-sequence-numbers */
7855 int min_seq; /* Minimum sequence number */
7856 int interval; /* Poll interval */
7859 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_notifications(con=%p[%d])",
7863 * Get subscription attributes...
7866 ids = ippFindAttribute(con->request, "notify-subscription-ids",
7868 sequences = ippFindAttribute(con->request, "notify-sequence-numbers",
7873 send_ipp_status(con, IPP_BAD_REQUEST,
7874 _("Missing notify-subscription-ids attribute."));
7879 * Are the subscription IDs valid?
7882 for (i = 0, interval = 60; i < ids->num_values; i ++)
7884 if ((sub = cupsdFindSubscription(ids->values[i].integer)) == NULL)
7887 * Bad subscription ID...
7890 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
7891 ids->values[i].integer);
7899 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
7901 con, sub->owner)) != HTTP_OK)
7903 send_http_error(con, status, sub->dest);
7908 * Check the subscription type and update the interval accordingly.
7911 if (sub->job && sub->job->state_value == IPP_JOB_PROCESSING &&
7914 else if (sub->job && sub->job->state_value >= IPP_JOB_STOPPED)
7916 else if (sub->dest && sub->dest->state == IPP_PRINTER_PROCESSING &&
7922 * Tell the client to poll again in N seconds...
7926 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
7927 "notify-get-interval", interval);
7929 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
7930 "printer-up-time", time(NULL));
7933 * Copy the subscription event attributes to the response.
7936 con->response->request.status.status_code =
7937 interval ? IPP_OK : IPP_OK_EVENTS_COMPLETE;
7939 for (i = 0; i < ids->num_values; i ++)
7942 * Get the subscription and sequence number...
7945 sub = cupsdFindSubscription(ids->values[i].integer);
7947 if (sequences && i < sequences->num_values)
7948 min_seq = sequences->values[i].integer;
7953 * If we don't have any new events, nothing to do here...
7956 if (min_seq > (sub->first_event_id + cupsArrayCount(sub->events)))
7960 * Otherwise copy all of the new events...
7963 if (sub->first_event_id > min_seq)
7966 j = min_seq - sub->first_event_id;
7968 for (; j < cupsArrayCount(sub->events); j ++)
7970 ippAddSeparator(con->response);
7972 copy_attrs(con->response,
7973 ((cupsd_event_t *)cupsArrayIndex(sub->events, j))->attrs, NULL,
7974 IPP_TAG_EVENT_NOTIFICATION, 0, NULL);
7981 * 'get_ppd()' - Get a named PPD from the local system.
7985 get_ppd(cupsd_client_t *con, /* I - Client connection */
7986 ipp_attribute_t *uri) /* I - Printer URI or PPD name */
7988 http_status_t status; /* Policy status */
7989 cupsd_printer_t *dest; /* Destination */
7990 cups_ptype_t dtype; /* Destination type */
7993 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppd(%p[%d], %p[%s=%s])", con,
7994 con->http.fd, uri, uri->name, uri->values[0].string.text);
7996 if (!strcmp(uri->name, "ppd-name"))
7999 * Return a PPD file from cups-driverd...
8002 char command[1024], /* cups-driverd command */
8003 options[1024], /* Options to pass to command */
8004 ppd_name[1024]; /* ppd-name */
8011 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
8013 send_http_error(con, status, NULL);
8018 * Run cups-driverd command with the given options...
8021 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
8022 url_encode_string(uri->values[0].string.text, ppd_name, sizeof(ppd_name));
8023 snprintf(options, sizeof(options), "get+%d+%s",
8024 con->request->request.op.request_id, ppd_name);
8026 if (cupsdSendCommand(con, command, options, 0))
8029 * Command started successfully, don't send an IPP response here...
8032 ippDelete(con->response);
8033 con->response = NULL;
8038 * Command failed, return "internal error" so the user knows something
8042 send_ipp_status(con, IPP_INTERNAL_ERROR,
8043 _("cups-driverd failed to execute."));
8046 else if (!strcmp(uri->name, "printer-uri") &&
8047 cupsdValidateDest(uri->values[0].string.text, &dtype, &dest))
8049 int i; /* Looping var */
8050 char filename[1024]; /* PPD filename */
8057 if ((status = cupsdCheckPolicy(dest->op_policy_ptr, con, NULL)) != HTTP_OK)
8059 send_http_error(con, status, dest);
8064 * See if we need the PPD for a class or remote printer...
8067 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
8070 if ((dtype & CUPS_PRINTER_REMOTE) && access(filename, 0))
8072 con->response->request.status.status_code = CUPS_SEE_OTHER;
8073 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
8074 "printer-uri", NULL, dest->uri);
8077 else if (dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
8079 for (i = 0; i < dest->num_printers; i ++)
8080 if (!(dest->printers[i]->type &
8081 (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)))
8083 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
8084 dest->printers[i]->name);
8086 if (!access(filename, 0))
8090 if (i < dest->num_printers)
8091 dest = dest->printers[i];
8094 con->response->request.status.status_code = CUPS_SEE_OTHER;
8095 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
8096 "printer-uri", NULL, dest->printers[0]->uri);
8102 * Found the printer with the PPD file, now see if there is one...
8105 if ((con->file = open(filename, O_RDONLY)) < 0)
8107 send_ipp_status(con, IPP_NOT_FOUND,
8108 _("The PPD file \"%s\" could not be opened: %s"),
8109 uri->values[0].string.text, strerror(errno));
8113 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
8117 con->response->request.status.status_code = IPP_OK;
8120 send_ipp_status(con, IPP_NOT_FOUND,
8121 _("The PPD file \"%s\" could not be found."),
8122 uri->values[0].string.text);
8127 * 'get_ppds()' - Get the list of PPD files on the local system.
8131 get_ppds(cupsd_client_t *con) /* I - Client connection */
8133 http_status_t status; /* Policy status */
8134 ipp_attribute_t *limit, /* Limit attribute */
8135 *device, /* ppd-device-id attribute */
8136 *language, /* ppd-natural-language attribute */
8137 *make, /* ppd-make attribute */
8138 *model, /* ppd-make-and-model attribute */
8139 *model_number, /* ppd-model-number attribute */
8140 *product, /* ppd-product attribute */
8141 *psversion, /* ppd-psverion attribute */
8142 *type, /* ppd-type attribute */
8143 *requested, /* requested-attributes attribute */
8144 *exclude, /* exclude-schemes attribute */
8145 *include; /* include-schemes attribute */
8146 char command[1024], /* cups-driverd command */
8147 options[4096], /* Options to pass to command */
8148 device_str[256],/* Escaped ppd-device-id string */
8150 /* Escaped ppd-natural-language */
8151 make_str[256], /* Escaped ppd-make string */
8152 model_str[256], /* Escaped ppd-make-and-model string */
8153 model_number_str[256],
8154 /* ppd-model-number string */
8156 /* Escaped ppd-product string */
8158 /* Escaped ppd-psversion string */
8159 type_str[256], /* Escaped ppd-type string */
8161 /* String for requested attributes */
8163 /* String for excluded schemes */
8165 /* String for included schemes */
8168 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->http.fd);
8174 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
8176 send_http_error(con, status, NULL);
8181 * Run cups-driverd command with the given options...
8184 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
8185 device = ippFindAttribute(con->request, "ppd-device-id", IPP_TAG_TEXT);
8186 language = ippFindAttribute(con->request, "ppd-natural-language",
8188 make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
8189 model = ippFindAttribute(con->request, "ppd-make-and-model",
8191 model_number = ippFindAttribute(con->request, "ppd-model-number",
8193 product = ippFindAttribute(con->request, "ppd-product", IPP_TAG_TEXT);
8194 psversion = ippFindAttribute(con->request, "ppd-psversion", IPP_TAG_TEXT);
8195 type = ippFindAttribute(con->request, "ppd-type", IPP_TAG_KEYWORD);
8196 requested = ippFindAttribute(con->request, "requested-attributes",
8198 exclude = ippFindAttribute(con->request, "exclude-schemes",
8200 include = ippFindAttribute(con->request, "include-schemes",
8204 url_encode_attr(requested, requested_str, sizeof(requested_str));
8206 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
8209 url_encode_attr(device, device_str, sizeof(device_str));
8211 device_str[0] = '\0';
8214 url_encode_attr(language, language_str, sizeof(language_str));
8216 language_str[0] = '\0';
8219 url_encode_attr(make, make_str, sizeof(make_str));
8224 url_encode_attr(model, model_str, sizeof(model_str));
8226 model_str[0] = '\0';
8229 snprintf(model_number_str, sizeof(model_number_str), "ppd-model-number=%d",
8230 model_number->values[0].integer);
8232 model_number_str[0] = '\0';
8235 url_encode_attr(product, product_str, sizeof(product_str));
8237 product_str[0] = '\0';
8240 url_encode_attr(psversion, psversion_str, sizeof(psversion_str));
8242 psversion_str[0] = '\0';
8245 url_encode_attr(type, type_str, sizeof(type_str));
8250 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
8252 exclude_str[0] = '\0';
8255 url_encode_attr(include, include_str, sizeof(include_str));
8257 include_str[0] = '\0';
8259 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
8260 snprintf(options, sizeof(options),
8261 "list+%d+%d+%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
8262 con->request->request.op.request_id,
8263 limit ? limit->values[0].integer : 0,
8265 device ? "%20" : "", device_str,
8266 language ? "%20" : "", language_str,
8267 make ? "%20" : "", make_str,
8268 model ? "%20" : "", model_str,
8269 model_number ? "%20" : "", model_number_str,
8270 product ? "%20" : "", product_str,
8271 psversion ? "%20" : "", psversion_str,
8272 type ? "%20" : "", type_str,
8273 exclude_str[0] ? "%20" : "", exclude_str,
8274 include_str[0] ? "%20" : "", include_str);
8276 if (cupsdSendCommand(con, command, options, 0))
8279 * Command started successfully, don't send an IPP response here...
8282 ippDelete(con->response);
8283 con->response = NULL;
8288 * Command failed, return "internal error" so the user knows something
8292 send_ipp_status(con, IPP_INTERNAL_ERROR,
8293 _("cups-driverd failed to execute."));
8299 * 'get_printer_attrs()' - Get printer attributes.
8303 get_printer_attrs(cupsd_client_t *con, /* I - Client connection */
8304 ipp_attribute_t *uri) /* I - Printer URI */
8306 http_status_t status; /* Policy status */
8307 cups_ptype_t dtype; /* Destination type (printer/class) */
8308 cupsd_printer_t *printer; /* Printer/class */
8309 cups_array_t *ra; /* Requested attributes array */
8312 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", con,
8313 con->http.fd, uri->values[0].string.text);
8316 * Is the destination valid?
8319 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8325 send_ipp_status(con, IPP_NOT_FOUND,
8326 _("The printer or class does not exist."));
8334 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8336 send_http_error(con, status, printer);
8341 * Send the attributes...
8344 ra = create_requested_array(con->request);
8346 copy_printer_attrs(con, printer, ra);
8348 cupsArrayDelete(ra);
8350 con->response->request.status.status_code = IPP_OK;
8355 * 'get_printer_supported()' - Get printer supported values.
8359 get_printer_supported(
8360 cupsd_client_t *con, /* I - Client connection */
8361 ipp_attribute_t *uri) /* I - Printer URI */
8363 http_status_t status; /* Policy status */
8364 cups_ptype_t dtype; /* Destination type (printer/class) */
8365 cupsd_printer_t *printer; /* Printer/class */
8368 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_supported(%p[%d], %s)", con,
8369 con->http.fd, uri->values[0].string.text);
8372 * Is the destination valid?
8375 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8381 send_ipp_status(con, IPP_NOT_FOUND,
8382 _("The printer or class does not exist."));
8390 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8392 send_http_error(con, status, printer);
8397 * Return a list of attributes that can be set via Set-Printer-Attributes.
8400 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
8402 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
8403 "printer-location", 0);
8405 con->response->request.status.status_code = IPP_OK;
8410 * 'get_printers()' - Get a list of printers or classes.
8414 get_printers(cupsd_client_t *con, /* I - Client connection */
8415 int type) /* I - 0 or CUPS_PRINTER_CLASS */
8417 http_status_t status; /* Policy status */
8418 ipp_attribute_t *attr; /* Current attribute */
8419 int limit; /* Max number of printers to return */
8420 int count; /* Number of printers that match */
8421 cupsd_printer_t *printer; /* Current printer pointer */
8422 int printer_type, /* printer-type attribute */
8423 printer_mask; /* printer-type-mask attribute */
8424 char *location; /* Location string */
8425 const char *username; /* Current user */
8426 char *first_printer_name; /* first-printer-name attribute */
8427 cups_array_t *ra; /* Requested attributes array */
8428 int local; /* Local connection? */
8431 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con,
8432 con->http.fd, type);
8438 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
8440 send_http_error(con, status, NULL);
8445 * Check for printers...
8448 if (!Printers || !cupsArrayCount(Printers))
8450 send_ipp_status(con, IPP_NOT_FOUND, _("No destinations added."));
8455 * See if they want to limit the number of printers reported...
8458 if ((attr = ippFindAttribute(con->request, "limit",
8459 IPP_TAG_INTEGER)) != NULL)
8460 limit = attr->values[0].integer;
8464 if ((attr = ippFindAttribute(con->request, "first-printer-name",
8465 IPP_TAG_NAME)) != NULL)
8466 first_printer_name = attr->values[0].string.text;
8468 first_printer_name = NULL;
8471 * Support filtering...
8474 if ((attr = ippFindAttribute(con->request, "printer-type",
8475 IPP_TAG_ENUM)) != NULL)
8476 printer_type = attr->values[0].integer;
8480 if ((attr = ippFindAttribute(con->request, "printer-type-mask",
8481 IPP_TAG_ENUM)) != NULL)
8482 printer_mask = attr->values[0].integer;
8486 local = httpAddrLocalhost(&(con->clientaddr));
8488 if ((attr = ippFindAttribute(con->request, "printer-location",
8489 IPP_TAG_TEXT)) != NULL)
8490 location = attr->values[0].string.text;
8494 if (con->username[0])
8495 username = con->username;
8496 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
8497 IPP_TAG_NAME)) != NULL)
8498 username = attr->values[0].string.text;
8502 ra = create_requested_array(con->request);
8505 * OK, build a list of printers for this printer...
8508 if (first_printer_name)
8510 if ((printer = cupsdFindDest(first_printer_name)) == NULL)
8511 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
8514 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
8517 count < limit && printer;
8518 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
8520 if (!local && !printer->shared)
8523 if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
8524 (printer->type & printer_mask) == printer_type &&
8526 (printer->location && !_cups_strcasecmp(printer->location, location))))
8529 * If HideImplicitMembers is enabled, see if this printer or class
8530 * is a member of an implicit class...
8533 if (ImplicitClasses && HideImplicitMembers &&
8534 printer->in_implicit_class)
8538 * If a username is specified, see if it is allowed or denied
8542 if (cupsArrayCount(printer->users) && username &&
8543 !user_allowed(printer, username))
8547 * Add the group separator as needed...
8551 ippAddSeparator(con->response);
8556 * Send the attributes...
8559 copy_printer_attrs(con, printer, ra);
8563 cupsArrayDelete(ra);
8565 con->response->request.status.status_code = IPP_OK;
8570 * 'get_subscription_attrs()' - Get subscription attributes.
8574 get_subscription_attrs(
8575 cupsd_client_t *con, /* I - Client connection */
8576 int sub_id) /* I - Subscription ID */
8578 http_status_t status; /* Policy status */
8579 cupsd_subscription_t *sub; /* Subscription */
8580 cupsd_policy_t *policy; /* Current security policy */
8581 cups_array_t *ra, /* Requested attributes array */
8582 *exclude; /* Private attributes array */
8585 cupsdLogMessage(CUPSD_LOG_DEBUG2,
8586 "get_subscription_attrs(con=%p[%d], sub_id=%d)",
8587 con, con->http.fd, sub_id);
8590 * Is the subscription ID valid?
8593 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
8596 * Bad subscription ID...
8599 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
8609 policy = sub->dest->op_policy_ptr;
8611 policy = DefaultPolicyPtr;
8613 if ((status = cupsdCheckPolicy(policy, con, sub->owner)) != HTTP_OK)
8615 send_http_error(con, status, sub->dest);
8619 exclude = cupsdGetPrivateAttrs(policy, con, sub->dest, sub->owner);
8622 * Copy the subscription attributes to the response using the
8623 * requested-attributes attribute that may be provided by the client.
8626 ra = create_requested_array(con->request);
8628 copy_subscription_attrs(con, sub, ra, exclude);
8630 cupsArrayDelete(ra);
8632 con->response->request.status.status_code = IPP_OK;
8637 * 'get_subscriptions()' - Get subscriptions.
8641 get_subscriptions(cupsd_client_t *con, /* I - Client connection */
8642 ipp_attribute_t *uri) /* I - Printer/job URI */
8644 http_status_t status; /* Policy status */
8645 int count; /* Number of subscriptions */
8646 int limit; /* Limit */
8647 cupsd_subscription_t *sub; /* Subscription */
8648 cups_array_t *ra; /* Requested attributes array */
8649 ipp_attribute_t *attr; /* Attribute */
8650 cups_ptype_t dtype; /* Destination type (printer/class) */
8651 char scheme[HTTP_MAX_URI],
8652 /* Scheme portion of URI */
8653 username[HTTP_MAX_URI],
8654 /* Username portion of URI */
8656 /* Host portion of URI */
8657 resource[HTTP_MAX_URI];
8658 /* Resource portion of URI */
8659 int port; /* Port portion of URI */
8660 cupsd_job_t *job; /* Job pointer */
8661 cupsd_printer_t *printer; /* Printer */
8662 cupsd_policy_t *policy; /* Policy */
8663 cups_array_t *exclude; /* Private attributes array */
8666 cupsdLogMessage(CUPSD_LOG_DEBUG2,
8667 "get_subscriptions(con=%p[%d], uri=%s)",
8668 con, con->http.fd, uri->values[0].string.text);
8671 * Is the destination valid?
8674 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8675 sizeof(scheme), username, sizeof(username), host,
8676 sizeof(host), &port, resource, sizeof(resource));
8678 if (!strcmp(resource, "/") ||
8679 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6) ||
8680 (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) ||
8681 (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9))
8686 else if (!strncmp(resource, "/jobs/", 6) && resource[6])
8689 job = cupsdFindJob(atoi(resource + 6));
8693 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
8694 atoi(resource + 6));
8698 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8704 send_ipp_status(con, IPP_NOT_FOUND,
8705 _("The printer or class does not exist."));
8708 else if ((attr = ippFindAttribute(con->request, "notify-job-id",
8709 IPP_TAG_INTEGER)) != NULL)
8711 job = cupsdFindJob(attr->values[0].integer);
8715 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
8716 attr->values[0].integer);
8728 policy = printer->op_policy_ptr;
8730 policy = DefaultPolicyPtr;
8732 if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
8734 send_http_error(con, status, printer);
8739 * Copy the subscription attributes to the response using the
8740 * requested-attributes attribute that may be provided by the client.
8743 ra = create_requested_array(con->request);
8745 if ((attr = ippFindAttribute(con->request, "limit",
8746 IPP_TAG_INTEGER)) != NULL)
8747 limit = attr->values[0].integer;
8752 * See if we only want to see subscriptions for a specific user...
8755 if ((attr = ippFindAttribute(con->request, "my-subscriptions",
8756 IPP_TAG_BOOLEAN)) != NULL &&
8757 attr->values[0].boolean)
8758 strlcpy(username, get_username(con), sizeof(username));
8762 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0;
8764 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
8765 if ((!printer || sub->dest == printer) && (!job || sub->job == job) &&
8766 (!username[0] || !_cups_strcasecmp(username, sub->owner)))
8768 ippAddSeparator(con->response);
8770 exclude = cupsdGetPrivateAttrs(sub->dest ? sub->dest->op_policy_ptr :
8771 policy, con, sub->dest,
8774 copy_subscription_attrs(con, sub, ra, exclude);
8777 if (limit && count >= limit)
8781 cupsArrayDelete(ra);
8784 con->response->request.status.status_code = IPP_OK;
8786 send_ipp_status(con, IPP_NOT_FOUND, _("No subscriptions found."));
8791 * 'get_username()' - Get the username associated with a request.
8794 static const char * /* O - Username */
8795 get_username(cupsd_client_t *con) /* I - Connection */
8797 ipp_attribute_t *attr; /* Attribute */
8800 if (con->username[0])
8801 return (con->username);
8802 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
8803 IPP_TAG_NAME)) != NULL)
8804 return (attr->values[0].string.text);
8806 return ("anonymous");
8811 * 'hold_job()' - Hold a print job.
8815 hold_job(cupsd_client_t *con, /* I - Client connection */
8816 ipp_attribute_t *uri) /* I - Job or Printer URI */
8818 ipp_attribute_t *attr; /* Current job-hold-until */
8819 const char *when; /* New value */
8820 int jobid; /* Job ID */
8821 char scheme[HTTP_MAX_URI], /* Method portion of URI */
8822 username[HTTP_MAX_URI], /* Username portion of URI */
8823 host[HTTP_MAX_URI], /* Host portion of URI */
8824 resource[HTTP_MAX_URI]; /* Resource portion of URI */
8825 int port; /* Port portion of URI */
8826 cupsd_job_t *job; /* Job information */
8829 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", con, con->http.fd,
8830 uri->values[0].string.text);
8833 * See if we have a job URI or a printer URI...
8836 if (!strcmp(uri->name, "printer-uri"))
8839 * Got a printer URI; see if we also have a job-id attribute...
8842 if ((attr = ippFindAttribute(con->request, "job-id",
8843 IPP_TAG_INTEGER)) == NULL)
8845 send_ipp_status(con, IPP_BAD_REQUEST,
8846 _("Got a printer-uri attribute but no job-id."));
8850 jobid = attr->values[0].integer;
8855 * Got a job URI; parse it to get the job ID...
8858 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8859 sizeof(scheme), username, sizeof(username), host,
8860 sizeof(host), &port, resource, sizeof(resource));
8862 if (strncmp(resource, "/jobs/", 6))
8868 send_ipp_status(con, IPP_BAD_REQUEST,
8869 _("Bad job-uri \"%s\"."),
8870 uri->values[0].string.text);
8874 jobid = atoi(resource + 6);
8878 * See if the job exists...
8881 if ((job = cupsdFindJob(jobid)) == NULL)
8884 * Nope - return a "not found" error...
8887 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
8892 * See if the job is owned by the requesting user...
8895 if (!validate_user(job, con, job->username, username, sizeof(username)))
8897 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
8898 cupsdFindDest(job->dest));
8903 * See if the job is in a state that allows holding...
8906 if (job->state_value > IPP_JOB_STOPPED)
8909 * Return a "not-possible" error...
8912 send_ipp_status(con, IPP_NOT_POSSIBLE,
8913 _("Job #%d is finished and cannot be altered."),
8919 * Hold the job and return...
8922 if ((attr = ippFindAttribute(con->request, "job-hold-until",
8923 IPP_TAG_KEYWORD)) == NULL)
8924 attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
8928 when = attr->values[0].string.text;
8930 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
8931 "Job job-hold-until value changed by user.");
8934 when = "indefinite";
8936 cupsdSetJobHoldUntil(job, when, 1);
8937 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT, "Job held by \"%s\".",
8940 con->response->request.status.status_code = IPP_OK;
8945 * 'hold_new_jobs()' - Hold pending/new jobs on a printer or class.
8949 hold_new_jobs(cupsd_client_t *con, /* I - Connection */
8950 ipp_attribute_t *uri) /* I - Printer URI */
8952 http_status_t status; /* Policy status */
8953 cups_ptype_t dtype; /* Destination type (printer/class) */
8954 cupsd_printer_t *printer; /* Printer data */
8957 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_new_jobs(%p[%d], %s)", con,
8958 con->http.fd, uri->values[0].string.text);
8961 * Is the destination valid?
8964 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8970 send_ipp_status(con, IPP_NOT_FOUND,
8971 _("The printer or class does not exist."));
8979 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8981 send_http_error(con, status, printer);
8986 * Hold pending/new jobs sent to the printer...
8989 printer->holding_new_jobs = 1;
8991 cupsdSetPrinterReasons(printer, "+hold-new-jobs");
8993 if (dtype & CUPS_PRINTER_CLASS)
8994 cupsdLogMessage(CUPSD_LOG_INFO,
8995 "Class \"%s\" now holding pending/new jobs (\"%s\").",
8996 printer->name, get_username(con));
8998 cupsdLogMessage(CUPSD_LOG_INFO,
8999 "Printer \"%s\" now holding pending/new jobs (\"%s\").",
9000 printer->name, get_username(con));
9003 * Everything was ok, so return OK status...
9006 con->response->request.status.status_code = IPP_OK;
9011 * 'move_job()' - Move a job to a new destination.
9015 move_job(cupsd_client_t *con, /* I - Client connection */
9016 ipp_attribute_t *uri) /* I - Job URI */
9018 http_status_t status; /* Policy status */
9019 ipp_attribute_t *attr; /* Current attribute */
9020 int jobid; /* Job ID */
9021 cupsd_job_t *job; /* Current job */
9022 const char *src; /* Source printer/class */
9023 cups_ptype_t stype, /* Source type (printer or class) */
9024 dtype; /* Destination type (printer/class) */
9025 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
9026 username[HTTP_MAX_URI], /* Username portion of URI */
9027 host[HTTP_MAX_URI], /* Host portion of URI */
9028 resource[HTTP_MAX_URI]; /* Resource portion of URI */
9029 int port; /* Port portion of URI */
9030 cupsd_printer_t *sprinter, /* Source printer */
9031 *dprinter; /* Destination printer */
9034 cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", con, con->http.fd,
9035 uri->values[0].string.text);
9038 * Get the new printer or class...
9041 if ((attr = ippFindAttribute(con->request, "job-printer-uri",
9042 IPP_TAG_URI)) == NULL)
9045 * Need job-printer-uri...
9048 send_ipp_status(con, IPP_BAD_REQUEST,
9049 _("job-printer-uri attribute missing."));
9053 if (!cupsdValidateDest(attr->values[0].string.text, &dtype, &dprinter))
9059 send_ipp_status(con, IPP_NOT_FOUND,
9060 _("The printer or class does not exist."));
9065 * See if we have a job URI or a printer URI...
9068 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9069 sizeof(scheme), username, sizeof(username), host,
9070 sizeof(host), &port, resource, sizeof(resource));
9072 if (!strcmp(uri->name, "printer-uri"))
9075 * Got a printer URI; see if we also have a job-id attribute...
9078 if ((attr = ippFindAttribute(con->request, "job-id",
9079 IPP_TAG_INTEGER)) == NULL)
9085 if ((src = cupsdValidateDest(uri->values[0].string.text, &stype,
9086 &sprinter)) == NULL)
9092 send_ipp_status(con, IPP_NOT_FOUND,
9093 _("The printer or class does not exist."));
9102 * Otherwise, just move a single job...
9105 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
9108 * Nope - return a "not found" error...
9111 send_ipp_status(con, IPP_NOT_FOUND,
9112 _("Job #%d does not exist."), attr->values[0].integer);
9118 * Job found, initialize source pointers...
9129 * Got a job URI; parse it to get the job ID...
9132 if (strncmp(resource, "/jobs/", 6))
9138 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
9139 uri->values[0].string.text);
9144 * See if the job exists...
9147 jobid = atoi(resource + 6);
9149 if ((job = cupsdFindJob(jobid)) == NULL)
9152 * Nope - return a "not found" error...
9155 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
9161 * Job found, initialize source pointers...
9170 * Check the policy of the destination printer...
9173 if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con,
9174 job ? job->username : NULL)) != HTTP_OK)
9176 send_http_error(con, status, dprinter);
9181 * Now move the job or jobs...
9187 * See if the job has been completed...
9190 if (job->state_value > IPP_JOB_STOPPED)
9193 * Return a "not-possible" error...
9196 send_ipp_status(con, IPP_NOT_POSSIBLE,
9197 _("Job #%d is finished and cannot be altered."),
9203 * See if the job is owned by the requesting user...
9206 if (!validate_user(job, con, job->username, username, sizeof(username)))
9208 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9209 cupsdFindDest(job->dest));
9214 * Move the job to a different printer or class...
9217 cupsdMoveJob(job, dprinter);
9222 * Got the source printer, now look through the jobs...
9225 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
9227 job = (cupsd_job_t *)cupsArrayNext(Jobs))
9230 * See if the job is pointing at the source printer or has not been
9234 if (_cups_strcasecmp(job->dest, src) ||
9235 job->state_value > IPP_JOB_STOPPED)
9239 * See if the job can be moved by the requesting user...
9242 if (!validate_user(job, con, job->username, username, sizeof(username)))
9246 * Move the job to a different printer or class...
9249 cupsdMoveJob(job, dprinter);
9254 * Start jobs if possible...
9260 * Return with "everything is OK" status...
9263 con->response->request.status.status_code = IPP_OK;
9268 * 'ppd_parse_line()' - Parse a PPD default line.
9271 static int /* O - 0 on success, -1 on failure */
9272 ppd_parse_line(const char *line, /* I - Line */
9273 char *option, /* O - Option name */
9274 int olen, /* I - Size of option name */
9275 char *choice, /* O - Choice name */
9276 int clen) /* I - Size of choice name */
9279 * Verify this is a default option line...
9282 if (strncmp(line, "*Default", 8))
9286 * Read the option name...
9289 for (line += 8, olen --;
9290 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
9301 * Skip everything else up to the colon (:)...
9304 while (*line && *line != ':')
9313 * Now grab the option choice, skipping leading whitespace...
9316 while (isspace(*line & 255))
9320 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
9331 * Return with no errors...
9339 * 'print_job()' - Print a file to a printer or class.
9343 print_job(cupsd_client_t *con, /* I - Client connection */
9344 ipp_attribute_t *uri) /* I - Printer URI */
9346 ipp_attribute_t *attr; /* Current attribute */
9347 ipp_attribute_t *format; /* Document-format attribute */
9348 const char *default_format; /* document-format-default value */
9349 cupsd_job_t *job; /* New job */
9350 char filename[1024]; /* Job filename */
9351 mime_type_t *filetype; /* Type of file */
9352 char super[MIME_MAX_SUPER], /* Supertype of file */
9353 type[MIME_MAX_TYPE], /* Subtype of file */
9354 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
9355 /* Textual name of mime type */
9356 cupsd_printer_t *printer; /* Printer data */
9357 struct stat fileinfo; /* File information */
9358 int kbytes; /* Size of file */
9359 int compression; /* Document compression */
9362 cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", con, con->http.fd,
9363 uri->values[0].string.text);
9366 * Validate print file attributes, for now just document-format and
9367 * compression (CUPS only supports "none" and "gzip")...
9370 compression = CUPS_FILE_NONE;
9372 if ((attr = ippFindAttribute(con->request, "compression",
9373 IPP_TAG_KEYWORD)) != NULL)
9375 if (strcmp(attr->values[0].string.text, "none")
9377 && strcmp(attr->values[0].string.text, "gzip")
9378 #endif /* HAVE_LIBZ */
9381 send_ipp_status(con, IPP_ATTRIBUTES,
9382 _("Unsupported compression \"%s\"."),
9383 attr->values[0].string.text);
9384 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
9385 "compression", NULL, attr->values[0].string.text);
9390 if (!strcmp(attr->values[0].string.text, "gzip"))
9391 compression = CUPS_FILE_GZIP;
9392 #endif /* HAVE_LIBZ */
9396 * Do we have a file to print?
9401 send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
9406 * Is the destination valid?
9409 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
9415 send_ipp_status(con, IPP_NOT_FOUND,
9416 _("The printer or class does not exist."));
9421 * Is it a format we support?
9424 if ((format = ippFindAttribute(con->request, "document-format",
9425 IPP_TAG_MIMETYPE)) != NULL)
9428 * Grab format from client...
9431 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super,
9434 send_ipp_status(con, IPP_BAD_REQUEST,
9435 _("Bad document-format \"%s\"."),
9436 format->values[0].string.text);
9440 else if ((default_format = cupsGetOption("document-format",
9441 printer->num_options,
9442 printer->options)) != NULL)
9445 * Use default document format...
9448 if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
9450 send_ipp_status(con, IPP_BAD_REQUEST,
9451 _("Bad document-format \"%s\"."),
9462 strcpy(super, "application");
9463 strcpy(type, "octet-stream");
9466 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
9469 * Auto-type the file...
9472 ipp_attribute_t *doc_name; /* document-name attribute */
9475 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job ???] Auto-typing file...");
9477 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
9478 filetype = mimeFileType(MimeDatabase, con->filename,
9479 doc_name ? doc_name->values[0].string.text : NULL,
9483 filetype = mimeType(MimeDatabase, super, type);
9485 cupsdLogMessage(CUPSD_LOG_INFO, "[Job ???] Request file type is %s/%s.",
9486 filetype->super, filetype->type);
9489 filetype = mimeType(MimeDatabase, super, type);
9493 (!strcmp(super, "application") && !strcmp(type, "octet-stream"))))
9496 * Replace the document-format attribute value with the auto-typed or
9500 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9505 _cupsStrFree(format->values[0].string.text);
9507 format->values[0].string.text = _cupsStrAlloc(mimetype);
9510 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
9511 "document-format", NULL, mimetype);
9515 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
9516 _("Unsupported document-format \"%s\"."),
9517 format ? format->values[0].string.text :
9518 "application/octet-stream");
9519 cupsdLogMessage(CUPSD_LOG_INFO,
9520 "Hint: Do you have the raw file printing rules enabled?");
9523 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9524 "document-format", NULL, format->values[0].string.text);
9530 * Read any embedded job ticket info from PS files...
9533 if (!_cups_strcasecmp(filetype->super, "application") &&
9534 (!_cups_strcasecmp(filetype->type, "postscript") ||
9535 !_cups_strcasecmp(filetype->type, "pdf")))
9536 read_job_ticket(con);
9539 * Create the job object...
9542 if ((job = add_job(con, printer, filetype)) == NULL)
9546 * Update quota data...
9549 if (stat(con->filename, &fileinfo))
9552 kbytes = (fileinfo.st_size + 1023) / 1024;
9554 cupsdUpdateQuota(printer, job->username, 0, kbytes);
9556 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
9557 IPP_TAG_INTEGER)) != NULL)
9558 attr->values[0].integer += kbytes;
9561 * Add the job file...
9564 if (add_file(con, job, filetype, compression))
9567 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
9569 rename(con->filename, filename);
9570 cupsdClearString(&con->filename);
9573 * See if we need to add the ending sheet...
9576 if (cupsdTimeoutJob(job))
9580 * Log and save the job...
9583 cupsdLogJob(job, CUPSD_LOG_INFO,
9584 "File of type %s/%s queued by \"%s\".",
9585 filetype->super, filetype->type, job->username);
9586 cupsdLogJob(job, CUPSD_LOG_DEBUG, "hold_until=%d", (int)job->hold_until);
9587 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
9588 job->dest, job->username);
9591 * Start the job if possible...
9599 * 'read_job_ticket()' - Read a job ticket embedded in a print file.
9601 * This function only gets called when printing a single PDF or PostScript
9602 * file using the Print-Job operation. It doesn't work for Create-Job +
9603 * Send-File, since the job attributes need to be set at job creation
9604 * time for banners to work. The embedded job ticket stuff is here
9605 * primarily to allow the Windows printer driver for CUPS to pass in JCL
9606 * options and IPP attributes which otherwise would be lost.
9608 * The format of a job ticket is simple:
9610 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
9612 * %cupsJobTicket: attr1=value1
9613 * %cupsJobTicket: attr2=value2
9615 * %cupsJobTicket: attrN=valueN
9617 * Job ticket lines must appear immediately after the first line that
9618 * specifies PostScript (%!PS-Adobe-3.0) or PDF (%PDF) format, and CUPS
9619 * stops looking for job ticket info when it finds a line that does not begin
9620 * with "%cupsJobTicket:".
9622 * The maximum length of a job ticket line, including the prefix, is
9623 * 255 characters to conform with the Adobe DSC.
9625 * Read-only attributes are rejected with a notice to the error log in
9626 * case a malicious user tries anything. Since the job ticket is read
9627 * prior to attribute validation in print_job(), job ticket attributes
9628 * will go through the same validation as IPP attributes...
9632 read_job_ticket(cupsd_client_t *con) /* I - Client connection */
9634 cups_file_t *fp; /* File to read from */
9635 char line[256]; /* Line data */
9636 int num_options; /* Number of options */
9637 cups_option_t *options; /* Options */
9638 ipp_t *ticket; /* New attributes */
9639 ipp_attribute_t *attr, /* Current attribute */
9640 *attr2, /* Job attribute */
9641 *prev2; /* Previous job attribute */
9642 int foundfirstpage; /* Did we find the first page already
9643 in the PostScript input? */
9644 int num_copies; /* Number of copies according to
9645 PostScript command in input file */
9646 char *s, *t, buffer[10];
9650 * First open the print file...
9653 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
9655 cupsdLogMessage(CUPSD_LOG_ERROR,
9656 "Unable to open print file for job ticket - %s",
9662 * Skip the first line...
9665 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
9667 cupsdLogMessage(CUPSD_LOG_ERROR,
9668 "Unable to read from print file for job ticket - %s",
9674 if (strncmp(line, "%!PS-Adobe-", 11) && strncmp(line, "%PDF-", 5))
9677 * Not a DSC-compliant file, so no job ticket info will be available...
9685 * Read job ticket info from the file...
9691 while (cupsFileGets(fp, line, sizeof(line)))
9694 * Stop at the first non-ticket line...
9697 if (strncmp(line, "%cupsJobTicket:", 15))
9701 * Add the options to the option array...
9704 num_options = cupsParseOptions(line + 15, num_options, &options);
9708 * Read option settings embedded in the file...
9713 while (cupsFileGets(fp, line, sizeof(line)))
9716 * Stop at the second page, we read also the settings of the first PageSetup
9717 * to work around a bug in OpenOffice.org. This app puts options intended
9718 * for the whole document into the page setup of the first page
9721 if (!strncmp(line, "%%Page:", 7))
9723 if (foundfirstpage == 1)
9729 * Add the embedded option settings to the option array...
9733 if (!strncmp(line, "%%BeginFeature:", 15))
9735 else if (!strncmp(line, "%%IncludeFeature:", 17))
9737 else if (!strncmp(line, "%%BeginNonPPDFeature:", 21))
9740 if (s && (t = strstr(s, "NumCopies")) != NULL)
9743 while ((*t == ' ') || (*t == '\t')) t++;
9744 if (sscanf(t, "%9d", &num_copies) == 1)
9746 sprintf(buffer, "%d", num_copies);
9747 num_options = cupsAddOption("copies", buffer, num_options, &options);
9752 while ((*s == ' ') || (*s == '\t')) s++;
9755 while (*t && (*t != ' ') && (*t != '\t')) t++;
9756 if ((*t == ' ') || (*t == '\t')) *t = '=';
9757 num_options = cupsParseOptions(s, num_options, &options);
9761 * Read out "/#copies XXX def" and "/NumCopies XXX def" expressions from
9762 * PostScript input. Some apps insert these expressions to set the
9767 if ((s = strstr(line, "/#copies")) != NULL)
9769 else if ((s = strstr(line, "/NumCopies")) != NULL)
9773 while ((*s == ' ') || (*s == '\t')) s++;
9774 if (sscanf(s, "%9d %as ", &num_copies, &t) == 2)
9776 if (!strncmp(t, "def", 3))
9778 sprintf(buffer, "%d", num_copies);
9779 num_options = cupsAddOption("copies", buffer, num_options, &options);
9787 * Done with the file; see if we have any options...
9792 if (num_options == 0)
9796 * OK, convert the options to an attribute list, and apply them to
9801 cupsEncodeOptions(ticket, num_options, options);
9804 * See what the user wants to change.
9807 for (attr = ticket->attrs; attr; attr = attr->next)
9809 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
9812 if (!strcmp(attr->name, "job-originating-host-name") ||
9813 !strcmp(attr->name, "job-originating-user-name") ||
9814 !strcmp(attr->name, "job-media-sheets-completed") ||
9815 !strcmp(attr->name, "job-k-octets") ||
9816 !strcmp(attr->name, "job-id") ||
9817 !strncmp(attr->name, "job-state", 9) ||
9818 !strncmp(attr->name, "time-at-", 8))
9819 continue; /* Read-only attrs */
9821 if ((attr2 = ippFindAttribute(con->request, attr->name,
9822 IPP_TAG_ZERO)) != NULL)
9825 * Some other value; first free the old value...
9828 if (con->request->attrs == attr2)
9830 con->request->attrs = attr2->next;
9835 for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
9836 if (prev2->next == attr2)
9838 prev2->next = attr2->next;
9843 if (con->request->last == attr2)
9844 con->request->last = prev2;
9846 _ippFreeAttr(attr2);
9850 * Add new option by copying it...
9853 copy_attribute(con->request, attr, 0);
9857 * Then free the attribute list and option array...
9861 cupsFreeOptions(num_options, options);
9866 * 'reject_jobs()' - Reject print jobs to a printer.
9870 reject_jobs(cupsd_client_t *con, /* I - Client connection */
9871 ipp_attribute_t *uri) /* I - Printer or class URI */
9873 http_status_t status; /* Policy status */
9874 cups_ptype_t dtype; /* Destination type (printer/class) */
9875 cupsd_printer_t *printer; /* Printer data */
9876 ipp_attribute_t *attr; /* printer-state-message text */
9879 cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", con,
9880 con->http.fd, uri->values[0].string.text);
9883 * Is the destination valid?
9886 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
9892 send_ipp_status(con, IPP_NOT_FOUND,
9893 _("The printer or class does not exist."));
9901 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
9903 send_http_error(con, status, printer);
9908 * Reject jobs sent to the printer...
9911 printer->accepting = 0;
9913 if ((attr = ippFindAttribute(con->request, "printer-state-message",
9914 IPP_TAG_TEXT)) == NULL)
9915 strcpy(printer->state_message, "Rejecting Jobs");
9917 strlcpy(printer->state_message, attr->values[0].string.text,
9918 sizeof(printer->state_message));
9920 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
9921 "No longer accepting jobs.");
9923 if (dtype & CUPS_PRINTER_CLASS)
9925 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
9927 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
9928 printer->name, get_username(con));
9932 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
9934 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
9935 printer->name, get_username(con));
9939 * Everything was ok, so return OK status...
9942 con->response->request.status.status_code = IPP_OK;
9947 * 'release_held_new_jobs()' - Release pending/new jobs on a printer or class.
9951 release_held_new_jobs(
9952 cupsd_client_t *con, /* I - Connection */
9953 ipp_attribute_t *uri) /* I - Printer URI */
9955 http_status_t status; /* Policy status */
9956 cups_ptype_t dtype; /* Destination type (printer/class) */
9957 cupsd_printer_t *printer; /* Printer data */
9960 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_held_new_jobs(%p[%d], %s)", con,
9961 con->http.fd, uri->values[0].string.text);
9964 * Is the destination valid?
9967 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
9973 send_ipp_status(con, IPP_NOT_FOUND,
9974 _("The printer or class does not exist."));
9982 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
9984 send_http_error(con, status, printer);
9989 * Hold pending/new jobs sent to the printer...
9992 printer->holding_new_jobs = 0;
9994 cupsdSetPrinterReasons(printer, "-hold-new-jobs");
9996 if (dtype & CUPS_PRINTER_CLASS)
9997 cupsdLogMessage(CUPSD_LOG_INFO,
9998 "Class \"%s\" now printing pending/new jobs (\"%s\").",
9999 printer->name, get_username(con));
10001 cupsdLogMessage(CUPSD_LOG_INFO,
10002 "Printer \"%s\" now printing pending/new jobs (\"%s\").",
10003 printer->name, get_username(con));
10006 * Everything was ok, so return OK status...
10009 con->response->request.status.status_code = IPP_OK;
10014 * 'release_job()' - Release a held print job.
10018 release_job(cupsd_client_t *con, /* I - Client connection */
10019 ipp_attribute_t *uri) /* I - Job or Printer URI */
10021 ipp_attribute_t *attr; /* Current attribute */
10022 int jobid; /* Job ID */
10023 char scheme[HTTP_MAX_URI], /* Method portion of URI */
10024 username[HTTP_MAX_URI], /* Username portion of URI */
10025 host[HTTP_MAX_URI], /* Host portion of URI */
10026 resource[HTTP_MAX_URI]; /* Resource portion of URI */
10027 int port; /* Port portion of URI */
10028 cupsd_job_t *job; /* Job information */
10031 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", con,
10032 con->http.fd, uri->values[0].string.text);
10035 * See if we have a job URI or a printer URI...
10038 if (!strcmp(uri->name, "printer-uri"))
10041 * Got a printer URI; see if we also have a job-id attribute...
10044 if ((attr = ippFindAttribute(con->request, "job-id",
10045 IPP_TAG_INTEGER)) == NULL)
10047 send_ipp_status(con, IPP_BAD_REQUEST,
10048 _("Got a printer-uri attribute but no job-id."));
10052 jobid = attr->values[0].integer;
10057 * Got a job URI; parse it to get the job ID...
10060 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
10061 sizeof(scheme), username, sizeof(username), host,
10062 sizeof(host), &port, resource, sizeof(resource));
10064 if (strncmp(resource, "/jobs/", 6))
10070 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
10071 uri->values[0].string.text);
10075 jobid = atoi(resource + 6);
10079 * See if the job exists...
10082 if ((job = cupsdFindJob(jobid)) == NULL)
10085 * Nope - return a "not found" error...
10088 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
10093 * See if job is "held"...
10096 if (job->state_value != IPP_JOB_HELD)
10099 * Nope - return a "not possible" error...
10102 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held."), jobid);
10107 * See if the job is owned by the requesting user...
10110 if (!validate_user(job, con, job->username, username, sizeof(username)))
10112 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
10113 cupsdFindDest(job->dest));
10118 * Reset the job-hold-until value to "no-hold"...
10121 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
10122 IPP_TAG_KEYWORD)) == NULL)
10123 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
10127 _cupsStrFree(attr->values[0].string.text);
10129 attr->value_tag = IPP_TAG_KEYWORD;
10130 attr->values[0].string.text = _cupsStrAlloc("no-hold");
10132 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
10133 "Job job-hold-until value changed by user.");
10137 * Release the job and return...
10140 cupsdReleaseJob(job);
10142 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
10143 "Job released by user.");
10145 cupsdLogJob(job, CUPSD_LOG_INFO, "Released by \"%s\".", username);
10147 con->response->request.status.status_code = IPP_OK;
10154 * 'renew_subscription()' - Renew an existing subscription...
10158 renew_subscription(
10159 cupsd_client_t *con, /* I - Client connection */
10160 int sub_id) /* I - Subscription ID */
10162 http_status_t status; /* Policy status */
10163 cupsd_subscription_t *sub; /* Subscription */
10164 ipp_attribute_t *lease; /* notify-lease-duration */
10167 cupsdLogMessage(CUPSD_LOG_DEBUG2,
10168 "renew_subscription(con=%p[%d], sub_id=%d)",
10169 con, con->http.fd, sub_id);
10172 * Is the subscription ID valid?
10175 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
10178 * Bad subscription ID...
10181 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
10189 * Job subscriptions cannot be renewed...
10192 send_ipp_status(con, IPP_NOT_POSSIBLE,
10193 _("Job subscriptions cannot be renewed."));
10201 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
10203 con, sub->owner)) != HTTP_OK)
10205 send_http_error(con, status, sub->dest);
10210 * Renew the subscription...
10213 lease = ippFindAttribute(con->request, "notify-lease-duration",
10216 sub->lease = lease ? lease->values[0].integer : DefaultLeaseDuration;
10218 if (MaxLeaseDuration && (sub->lease == 0 || sub->lease > MaxLeaseDuration))
10220 cupsdLogMessage(CUPSD_LOG_INFO,
10221 "renew_subscription: Limiting notify-lease-duration to "
10224 sub->lease = MaxLeaseDuration;
10227 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
10229 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
10231 con->response->request.status.status_code = IPP_OK;
10233 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
10234 "notify-lease-duration", sub->lease);
10239 * 'restart_job()' - Restart an old print job.
10243 restart_job(cupsd_client_t *con, /* I - Client connection */
10244 ipp_attribute_t *uri) /* I - Job or Printer URI */
10246 ipp_attribute_t *attr; /* Current attribute */
10247 int jobid; /* Job ID */
10248 cupsd_job_t *job; /* Job information */
10249 char scheme[HTTP_MAX_URI], /* Method portion of URI */
10250 username[HTTP_MAX_URI], /* Username portion of URI */
10251 host[HTTP_MAX_URI], /* Host portion of URI */
10252 resource[HTTP_MAX_URI]; /* Resource portion of URI */
10253 int port; /* Port portion of URI */
10256 cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con,
10257 con->http.fd, uri->values[0].string.text);
10260 * See if we have a job URI or a printer URI...
10263 if (!strcmp(uri->name, "printer-uri"))
10266 * Got a printer URI; see if we also have a job-id attribute...
10269 if ((attr = ippFindAttribute(con->request, "job-id",
10270 IPP_TAG_INTEGER)) == NULL)
10272 send_ipp_status(con, IPP_BAD_REQUEST,
10273 _("Got a printer-uri attribute but no job-id."));
10277 jobid = attr->values[0].integer;
10282 * Got a job URI; parse it to get the job ID...
10285 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
10286 sizeof(scheme), username, sizeof(username), host,
10287 sizeof(host), &port, resource, sizeof(resource));
10289 if (strncmp(resource, "/jobs/", 6))
10295 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
10296 uri->values[0].string.text);
10300 jobid = atoi(resource + 6);
10304 * See if the job exists...
10307 if ((job = cupsdFindJob(jobid)) == NULL)
10310 * Nope - return a "not found" error...
10313 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
10318 * See if job is in any of the "completed" states...
10321 if (job->state_value <= IPP_JOB_PROCESSING)
10324 * Nope - return a "not possible" error...
10327 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete."),
10333 * See if we have retained the job files...
10338 if (!job->attrs || job->num_files == 0)
10341 * Nope - return a "not possible" error...
10344 send_ipp_status(con, IPP_NOT_POSSIBLE,
10345 _("Job #%d cannot be restarted - no files."), jobid);
10350 * See if the job is owned by the requesting user...
10353 if (!validate_user(job, con, job->username, username, sizeof(username)))
10355 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
10356 cupsdFindDest(job->dest));
10361 * See if the job-hold-until attribute is specified...
10364 if ((attr = ippFindAttribute(con->request, "job-hold-until",
10365 IPP_TAG_KEYWORD)) == NULL)
10366 attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
10368 if (attr && strcmp(attr->values[0].string.text, "no-hold"))
10371 * Return the job to a held state...
10374 cupsdLogJob(job, CUPSD_LOG_DEBUG,
10375 "Restarted by \"%s\" with job-hold-until=%s.",
10376 username, attr->values[0].string.text);
10377 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
10379 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE,
10380 NULL, job, "Job restarted by user with job-hold-until=%s",
10381 attr->values[0].string.text);
10386 * Restart the job...
10389 cupsdRestartJob(job);
10393 cupsdLogJob(job, CUPSD_LOG_INFO, "Restarted by \"%s\".", username);
10395 con->response->request.status.status_code = IPP_OK;
10400 * 'save_auth_info()' - Save authentication information for a job.
10405 cupsd_client_t *con, /* I - Client connection */
10406 cupsd_job_t *job, /* I - Job */
10407 ipp_attribute_t *auth_info) /* I - auth-info attribute, if any */
10409 int i; /* Looping var */
10410 char filename[1024]; /* Job authentication filename */
10411 cups_file_t *fp; /* Job authentication file */
10412 char line[65536]; /* Line for file */
10413 cupsd_printer_t *dest; /* Destination printer/class */
10417 * This function saves the in-memory authentication information for
10418 * a job so that it can be used to authenticate with a remote host.
10419 * The information is stored in a file that is readable only by the
10420 * root user. The fields are Base-64 encoded, each on a separate line,
10421 * followed by random number (up to 1024) of newlines to limit the
10422 * amount of information that is exposed.
10424 * Because of the potential for exposing of authentication information,
10425 * this functionality is only enabled when running cupsd as root.
10427 * This caching only works for the Basic and BasicDigest authentication
10428 * types. Digest authentication cannot be cached this way, and in
10429 * the future Kerberos authentication may make all of this obsolete.
10431 * Authentication information is saved whenever an authenticated
10432 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
10435 * This information is deleted after a job is completed or canceled,
10436 * so reprints may require subsequent re-authentication.
10442 if ((dest = cupsdFindDest(job->dest)) == NULL)
10446 * Create the authentication file and change permissions...
10449 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
10450 if ((fp = cupsFileOpen(filename, "w")) == NULL)
10452 cupsdLogMessage(CUPSD_LOG_ERROR,
10453 "Unable to save authentication info to \"%s\" - %s",
10454 filename, strerror(errno));
10458 fchown(cupsFileNumber(fp), 0, 0);
10459 fchmod(cupsFileNumber(fp), 0400);
10461 cupsFilePuts(fp, "CUPSD-AUTH-V2\n");
10464 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
10466 cupsdClearString(job->auth_env + i);
10468 if (auth_info && auth_info->num_values == dest->num_auth_info_required)
10471 * Write 1 to 3 auth values...
10475 i < auth_info->num_values &&
10476 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
10479 httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text,
10480 strlen(auth_info->values[i].string.text));
10481 cupsFilePutConf(fp, dest->auth_info_required[i], line);
10483 if (!strcmp(dest->auth_info_required[i], "username"))
10484 cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s",
10485 auth_info->values[i].string.text);
10486 else if (!strcmp(dest->auth_info_required[i], "domain"))
10487 cupsdSetStringf(job->auth_env + i, "AUTH_DOMAIN=%s",
10488 auth_info->values[i].string.text);
10489 else if (!strcmp(dest->auth_info_required[i], "password"))
10490 cupsdSetStringf(job->auth_env + i, "AUTH_PASSWORD=%s",
10491 auth_info->values[i].string.text);
10492 else if (!strcmp(dest->auth_info_required[i], "negotiate"))
10493 cupsdSetStringf(job->auth_env + i, "AUTH_NEGOTIATE=%s",
10494 auth_info->values[i].string.text);
10499 else if (auth_info && auth_info->num_values == 2 &&
10500 dest->num_auth_info_required == 1 &&
10501 !strcmp(dest->auth_info_required[0], "negotiate"))
10504 * Allow fallback to username+password for Kerberized queues...
10507 httpEncode64_2(line, sizeof(line), auth_info->values[0].string.text,
10508 strlen(auth_info->values[0].string.text));
10509 cupsFilePutConf(fp, "username", line);
10511 cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s",
10512 auth_info->values[0].string.text);
10514 httpEncode64_2(line, sizeof(line), auth_info->values[1].string.text,
10515 strlen(auth_info->values[1].string.text));
10516 cupsFilePutConf(fp, "password", line);
10518 cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s",
10519 auth_info->values[1].string.text);
10521 else if (con->username[0])
10524 * Write the authenticated username...
10527 httpEncode64_2(line, sizeof(line), con->username, strlen(con->username));
10528 cupsFilePutConf(fp, "username", line);
10530 cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s", con->username);
10533 * Write the authenticated password...
10536 httpEncode64_2(line, sizeof(line), con->password, strlen(con->password));
10537 cupsFilePutConf(fp, "password", line);
10539 cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s", con->password);
10543 if (con->gss_uid > 0)
10545 cupsFilePrintf(fp, "uid %d\n", (int)con->gss_uid);
10546 cupsdSetStringf(&job->auth_uid, "AUTH_UID=%d", (int)con->gss_uid);
10548 #endif /* HAVE_GSSAPI */
10551 * Write a random number of newlines to the end of the file...
10554 for (i = (CUPS_RAND() % 1024); i >= 0; i --)
10555 cupsFilePutChar(fp, '\n');
10558 * Close the file and return...
10566 * 'send_document()' - Send a file to a printer or class.
10570 send_document(cupsd_client_t *con, /* I - Client connection */
10571 ipp_attribute_t *uri) /* I - Printer URI */
10573 ipp_attribute_t *attr; /* Current attribute */
10574 ipp_attribute_t *format; /* Request's document-format attribute */
10575 ipp_attribute_t *jformat; /* Job's document-format attribute */
10576 const char *default_format;/* document-format-default value */
10577 int jobid; /* Job ID number */
10578 cupsd_job_t *job; /* Current job */
10579 char job_uri[HTTP_MAX_URI],
10581 scheme[HTTP_MAX_URI],
10582 /* Method portion of URI */
10583 username[HTTP_MAX_URI],
10584 /* Username portion of URI */
10585 host[HTTP_MAX_URI],
10586 /* Host portion of URI */
10587 resource[HTTP_MAX_URI];
10588 /* Resource portion of URI */
10589 int port; /* Port portion of URI */
10590 mime_type_t *filetype; /* Type of file */
10591 char super[MIME_MAX_SUPER],
10592 /* Supertype of file */
10593 type[MIME_MAX_TYPE],
10594 /* Subtype of file */
10595 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
10596 /* Textual name of mime type */
10597 char filename[1024]; /* Job filename */
10598 cupsd_printer_t *printer; /* Current printer */
10599 struct stat fileinfo; /* File information */
10600 int kbytes; /* Size of file */
10601 int compression; /* Type of compression */
10602 int start_job; /* Start the job? */
10605 cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con,
10606 con->http.fd, uri->values[0].string.text);
10609 * See if we have a job URI or a printer URI...
10612 if (!strcmp(uri->name, "printer-uri"))
10615 * Got a printer URI; see if we also have a job-id attribute...
10618 if ((attr = ippFindAttribute(con->request, "job-id",
10619 IPP_TAG_INTEGER)) == NULL)
10621 send_ipp_status(con, IPP_BAD_REQUEST,
10622 _("Got a printer-uri attribute but no job-id."));
10626 jobid = attr->values[0].integer;
10631 * Got a job URI; parse it to get the job ID...
10634 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
10635 sizeof(scheme), username, sizeof(username), host,
10636 sizeof(host), &port, resource, sizeof(resource));
10638 if (strncmp(resource, "/jobs/", 6))
10644 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
10645 uri->values[0].string.text);
10649 jobid = atoi(resource + 6);
10653 * See if the job exists...
10656 if ((job = cupsdFindJob(jobid)) == NULL)
10659 * Nope - return a "not found" error...
10662 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
10666 printer = cupsdFindDest(job->dest);
10669 * See if the job is owned by the requesting user...
10672 if (!validate_user(job, con, job->username, username, sizeof(username)))
10674 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
10675 cupsdFindDest(job->dest));
10680 * OK, see if the client is sending the document compressed - CUPS
10681 * only supports "none" and "gzip".
10684 compression = CUPS_FILE_NONE;
10686 if ((attr = ippFindAttribute(con->request, "compression",
10687 IPP_TAG_KEYWORD)) != NULL)
10689 if (strcmp(attr->values[0].string.text, "none")
10691 && strcmp(attr->values[0].string.text, "gzip")
10692 #endif /* HAVE_LIBZ */
10695 send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\"."),
10696 attr->values[0].string.text);
10697 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
10698 "compression", NULL, attr->values[0].string.text);
10703 if (!strcmp(attr->values[0].string.text, "gzip"))
10704 compression = CUPS_FILE_GZIP;
10705 #endif /* HAVE_LIBZ */
10709 * Do we have a file to print?
10712 if ((attr = ippFindAttribute(con->request, "last-document",
10713 IPP_TAG_BOOLEAN)) == NULL)
10715 send_ipp_status(con, IPP_BAD_REQUEST,
10716 _("Missing last-document attribute in request."));
10720 if (!con->filename)
10723 * Check for an empty request with "last-document" set to true, which is
10724 * used to close an "open" job by RFC 2911, section 3.3.2.
10727 if (job->num_files > 0 && attr->values[0].boolean)
10728 goto last_document;
10730 send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
10735 * Is it a format we support?
10738 if ((format = ippFindAttribute(con->request, "document-format",
10739 IPP_TAG_MIMETYPE)) != NULL)
10742 * Grab format from client...
10745 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
10748 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"."),
10749 format->values[0].string.text);
10753 else if ((default_format = cupsGetOption("document-format",
10754 printer->num_options,
10755 printer->options)) != NULL)
10758 * Use default document format...
10761 if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
10763 send_ipp_status(con, IPP_BAD_REQUEST,
10764 _("Bad document-format-default \"%s\"."), default_format);
10771 * No document format attribute? Auto-type it!
10774 strcpy(super, "application");
10775 strcpy(type, "octet-stream");
10778 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
10781 * Auto-type the file...
10784 ipp_attribute_t *doc_name; /* document-name attribute */
10787 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Auto-typing file...");
10789 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
10790 filetype = mimeFileType(MimeDatabase, con->filename,
10791 doc_name ? doc_name->values[0].string.text : NULL,
10795 filetype = mimeType(MimeDatabase, super, type);
10798 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Request file type is %s/%s.",
10799 filetype->super, filetype->type);
10802 filetype = mimeType(MimeDatabase, super, type);
10807 * Replace the document-format attribute value with the auto-typed or
10811 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
10814 if ((jformat = ippFindAttribute(job->attrs, "document-format",
10815 IPP_TAG_MIMETYPE)) != NULL)
10817 _cupsStrFree(jformat->values[0].string.text);
10819 jformat->values[0].string.text = _cupsStrAlloc(mimetype);
10822 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
10823 "document-format", NULL, mimetype);
10825 else if (!filetype)
10827 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
10828 _("Unsupported document-format \"%s/%s\"."), super, type);
10829 cupsdLogMessage(CUPSD_LOG_INFO,
10830 "Hint: Do you have the raw file printing rules enabled?");
10833 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
10834 "document-format", NULL, format->values[0].string.text);
10839 if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
10841 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
10844 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
10845 _("Unsupported document-format \"%s\"."), mimetype);
10847 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
10848 "document-format", NULL, mimetype);
10854 * Add the file to the job...
10859 if (add_file(con, job, filetype, compression))
10862 if (stat(con->filename, &fileinfo))
10865 kbytes = (fileinfo.st_size + 1023) / 1024;
10867 cupsdUpdateQuota(printer, job->username, 0, kbytes);
10869 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
10870 IPP_TAG_INTEGER)) != NULL)
10871 attr->values[0].integer += kbytes;
10873 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
10875 rename(con->filename, filename);
10877 cupsdClearString(&con->filename);
10879 cupsdLogJob(job, CUPSD_LOG_INFO, "File of type %s/%s queued by \"%s\".",
10880 filetype->super, filetype->type, job->username);
10883 * Start the job if this is the last document...
10888 if ((attr = ippFindAttribute(con->request, "last-document",
10889 IPP_TAG_BOOLEAN)) != NULL &&
10890 attr->values[0].boolean)
10893 * See if we need to add the ending sheet...
10896 if (cupsdTimeoutJob(job))
10899 if (job->state_value == IPP_JOB_STOPPED)
10901 job->state->values[0].integer = IPP_JOB_PENDING;
10902 job->state_value = IPP_JOB_PENDING;
10904 else if (job->state_value == IPP_JOB_HELD)
10906 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
10907 IPP_TAG_KEYWORD)) == NULL)
10908 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
10910 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
10912 job->state->values[0].integer = IPP_JOB_PENDING;
10913 job->state_value = IPP_JOB_PENDING;
10918 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
10924 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
10925 IPP_TAG_KEYWORD)) == NULL)
10926 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
10928 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
10930 job->state->values[0].integer = IPP_JOB_HELD;
10931 job->state_value = IPP_JOB_HELD;
10932 job->hold_until = time(NULL) + MultipleOperationTimeout;
10935 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
10942 * Fill in the response info...
10945 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
10946 con->servername, con->serverport, "/jobs/%d", jobid);
10947 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
10950 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
10952 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
10954 add_job_state_reasons(con, job);
10956 con->response->request.status.status_code = IPP_OK;
10959 * Start the job if necessary...
10968 * 'send_http_error()' - Send a HTTP error back to the IPP client.
10973 cupsd_client_t *con, /* I - Client connection */
10974 http_status_t status, /* I - HTTP status code */
10975 cupsd_printer_t *printer) /* I - Printer, if any */
10977 ipp_attribute_t *uri; /* Request URI, if any */
10980 if ((uri = ippFindAttribute(con->request, "printer-uri",
10981 IPP_TAG_URI)) == NULL)
10982 uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI);
10984 cupsdLogMessage(status == HTTP_FORBIDDEN ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
10985 "Returning HTTP %s for %s (%s) from %s",
10986 httpStatus(status),
10988 ippOpString(con->request->request.op.operation_id) :
10990 uri ? uri->values[0].string.text : "no URI",
10991 con->http.hostname);
10995 int auth_type; /* Type of authentication required */
10998 auth_type = CUPSD_AUTH_NONE;
11000 if (status == HTTP_UNAUTHORIZED &&
11001 printer->num_auth_info_required > 0 &&
11002 !strcmp(printer->auth_info_required[0], "negotiate") &&
11004 (con->request->request.op.operation_id == IPP_PRINT_JOB ||
11005 con->request->request.op.operation_id == IPP_CREATE_JOB ||
11006 con->request->request.op.operation_id == CUPS_AUTHENTICATE_JOB))
11009 * Creating and authenticating jobs requires Kerberos...
11012 auth_type = CUPSD_AUTH_NEGOTIATE;
11017 * Use policy/location-defined authentication requirements...
11020 char resource[HTTP_MAX_URI]; /* Resource portion of URI */
11021 cupsd_location_t *auth; /* Pointer to authentication element */
11024 if (printer->type & CUPS_PRINTER_CLASS)
11025 snprintf(resource, sizeof(resource), "/classes/%s", printer->name);
11027 snprintf(resource, sizeof(resource), "/printers/%s", printer->name);
11029 if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
11030 auth->type == CUPSD_AUTH_NONE)
11031 auth = cupsdFindPolicyOp(printer->op_policy_ptr,
11033 con->request->request.op.operation_id :
11038 if (auth->type == CUPSD_AUTH_DEFAULT)
11039 auth_type = DefaultAuthType;
11041 auth_type = auth->type;
11045 cupsdSendError(con, status, auth_type);
11048 cupsdSendError(con, status, CUPSD_AUTH_NONE);
11050 ippDelete(con->response);
11051 con->response = NULL;
11058 * 'send_ipp_status()' - Send a status back to the IPP client.
11062 send_ipp_status(cupsd_client_t *con, /* I - Client connection */
11063 ipp_status_t status, /* I - IPP status code */
11064 const char *message,/* I - Status message */
11065 ...) /* I - Additional args as needed */
11067 va_list ap; /* Pointer to additional args */
11068 char formatted[1024]; /* Formatted errror message */
11071 va_start(ap, message);
11072 vsnprintf(formatted, sizeof(formatted),
11073 _cupsLangString(con->language, message), ap);
11076 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
11077 ippOpString(con->request->request.op.operation_id),
11078 ippErrorString(status), formatted);
11080 con->response->request.status.status_code = status;
11082 if (ippFindAttribute(con->response, "attributes-charset",
11083 IPP_TAG_ZERO) == NULL)
11084 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
11085 "attributes-charset", NULL, "utf-8");
11087 if (ippFindAttribute(con->response, "attributes-natural-language",
11088 IPP_TAG_ZERO) == NULL)
11089 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
11090 "attributes-natural-language", NULL, DefaultLanguage);
11092 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
11093 "status-message", NULL, formatted);
11098 * 'set_default()' - Set the default destination...
11102 set_default(cupsd_client_t *con, /* I - Client connection */
11103 ipp_attribute_t *uri) /* I - Printer URI */
11105 http_status_t status; /* Policy status */
11106 cups_ptype_t dtype; /* Destination type (printer/class) */
11107 cupsd_printer_t *printer, /* Printer */
11108 *oldprinter; /* Old default printer */
11111 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con,
11112 con->http.fd, uri->values[0].string.text);
11115 * Is the destination valid?
11118 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
11124 send_ipp_status(con, IPP_NOT_FOUND,
11125 _("The printer or class does not exist."));
11133 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
11135 send_http_error(con, status, NULL);
11140 * Set it as the default...
11143 oldprinter = DefaultPrinter;
11144 DefaultPrinter = printer;
11147 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, oldprinter, NULL,
11148 "%s is no longer the default printer.", oldprinter->name);
11150 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
11151 "%s is now the default printer.", printer->name);
11153 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS | CUPSD_DIRTY_CLASSES |
11154 CUPSD_DIRTY_REMOTE | CUPSD_DIRTY_PRINTCAP);
11156 cupsdLogMessage(CUPSD_LOG_INFO,
11157 "Default destination set to \"%s\" by \"%s\".",
11158 printer->name, get_username(con));
11161 * Everything was ok, so return OK status...
11164 con->response->request.status.status_code = IPP_OK;
11169 * 'set_job_attrs()' - Set job attributes.
11173 set_job_attrs(cupsd_client_t *con, /* I - Client connection */
11174 ipp_attribute_t *uri) /* I - Job URI */
11176 ipp_attribute_t *attr, /* Current attribute */
11177 *attr2; /* Job attribute */
11178 int jobid; /* Job ID */
11179 cupsd_job_t *job; /* Current job */
11180 char scheme[HTTP_MAX_URI],
11181 /* Method portion of URI */
11182 username[HTTP_MAX_URI],
11183 /* Username portion of URI */
11184 host[HTTP_MAX_URI],
11185 /* Host portion of URI */
11186 resource[HTTP_MAX_URI];
11187 /* Resource portion of URI */
11188 int port; /* Port portion of URI */
11189 int event; /* Events? */
11190 int check_jobs; /* Check jobs? */
11193 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
11194 con->http.fd, uri->values[0].string.text);
11197 * Start with "everything is OK" status...
11200 con->response->request.status.status_code = IPP_OK;
11203 * See if we have a job URI or a printer URI...
11206 if (!strcmp(uri->name, "printer-uri"))
11209 * Got a printer URI; see if we also have a job-id attribute...
11212 if ((attr = ippFindAttribute(con->request, "job-id",
11213 IPP_TAG_INTEGER)) == NULL)
11215 send_ipp_status(con, IPP_BAD_REQUEST,
11216 _("Got a printer-uri attribute but no job-id."));
11220 jobid = attr->values[0].integer;
11225 * Got a job URI; parse it to get the job ID...
11228 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
11229 sizeof(scheme), username, sizeof(username), host,
11230 sizeof(host), &port, resource, sizeof(resource));
11232 if (strncmp(resource, "/jobs/", 6))
11238 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
11239 uri->values[0].string.text);
11243 jobid = atoi(resource + 6);
11247 * See if the job exists...
11250 if ((job = cupsdFindJob(jobid)) == NULL)
11253 * Nope - return a "not found" error...
11256 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
11261 * See if the job has been completed...
11264 if (job->state_value > IPP_JOB_STOPPED)
11267 * Return a "not-possible" error...
11270 send_ipp_status(con, IPP_NOT_POSSIBLE,
11271 _("Job #%d is finished and cannot be altered."), jobid);
11276 * See if the job is owned by the requesting user...
11279 if (!validate_user(job, con, job->username, username, sizeof(username)))
11281 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
11282 cupsdFindDest(job->dest));
11287 * See what the user wants to change.
11295 for (attr = con->request->attrs; attr; attr = attr->next)
11297 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
11300 if (!strcmp(attr->name, "attributes-charset") ||
11301 !strcmp(attr->name, "attributes-natural-language") ||
11302 !strcmp(attr->name, "document-compression") ||
11303 !strcmp(attr->name, "document-format") ||
11304 !strcmp(attr->name, "job-detailed-status-messages") ||
11305 !strcmp(attr->name, "job-document-access-errors") ||
11306 !strcmp(attr->name, "job-id") ||
11307 !strcmp(attr->name, "job-impressions-completed") ||
11308 !strcmp(attr->name, "job-k-octets") ||
11309 !strcmp(attr->name, "job-originating-host-name") ||
11310 !strcmp(attr->name, "job-originating-user-name") ||
11311 !strcmp(attr->name, "job-printer-up-time") ||
11312 !strcmp(attr->name, "job-printer-uri") ||
11313 !strcmp(attr->name, "job-sheets") ||
11314 !strcmp(attr->name, "job-state-message") ||
11315 !strcmp(attr->name, "job-state-reasons") ||
11316 !strcmp(attr->name, "job-uri") ||
11317 !strcmp(attr->name, "number-of-documents") ||
11318 !strcmp(attr->name, "number-of-intervening-jobs") ||
11319 !strcmp(attr->name, "output-device-assigned") ||
11320 !strncmp(attr->name, "date-time-at-", 13) ||
11321 !strncmp(attr->name, "job-k-octets", 12) ||
11322 !strncmp(attr->name, "job-media-sheets", 16) ||
11323 !strncmp(attr->name, "time-at-", 8))
11329 send_ipp_status(con, IPP_ATTRIBUTES_NOT_SETTABLE,
11330 _("%s cannot be changed."), attr->name);
11332 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
11333 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
11338 if (!strcmp(attr->name, "job-priority"))
11341 * Change the job priority...
11344 if (attr->value_tag != IPP_TAG_INTEGER)
11346 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value."));
11348 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
11349 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
11351 else if (job->state_value >= IPP_JOB_PROCESSING)
11353 send_ipp_status(con, IPP_NOT_POSSIBLE,
11354 _("Job is completed and cannot be changed."));
11357 else if (con->response->request.status.status_code == IPP_OK)
11359 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-priority to %d",
11360 attr->values[0].integer);
11361 cupsdSetJobPriority(job, attr->values[0].integer);
11364 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED |
11365 CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED;
11368 else if (!strcmp(attr->name, "job-state"))
11371 * Change the job state...
11374 if (attr->value_tag != IPP_TAG_ENUM)
11376 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value."));
11378 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
11379 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
11383 switch (attr->values[0].integer)
11385 case IPP_JOB_PENDING :
11386 case IPP_JOB_HELD :
11387 if (job->state_value > IPP_JOB_HELD)
11389 send_ipp_status(con, IPP_NOT_POSSIBLE,
11390 _("Job state cannot be changed."));
11393 else if (con->response->request.status.status_code == IPP_OK)
11395 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
11396 attr->values[0].integer);
11397 cupsdSetJobState(job, attr->values[0].integer,
11399 "Job state changed by \"%s\"", username);
11404 case IPP_JOB_PROCESSING :
11405 case IPP_JOB_STOPPED :
11406 if (job->state_value != attr->values[0].integer)
11408 send_ipp_status(con, IPP_NOT_POSSIBLE,
11409 _("Job state cannot be changed."));
11414 case IPP_JOB_CANCELED :
11415 case IPP_JOB_ABORTED :
11416 case IPP_JOB_COMPLETED :
11417 if (job->state_value > IPP_JOB_PROCESSING)
11419 send_ipp_status(con, IPP_NOT_POSSIBLE,
11420 _("Job state cannot be changed."));
11423 else if (con->response->request.status.status_code == IPP_OK)
11425 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
11426 attr->values[0].integer);
11427 cupsdSetJobState(job, (ipp_jstate_t)attr->values[0].integer,
11429 "Job state changed by \"%s\"", username);
11436 else if (con->response->request.status.status_code != IPP_OK)
11438 else if ((attr2 = ippFindAttribute(job->attrs, attr->name,
11439 IPP_TAG_ZERO)) != NULL)
11442 * Some other value; first free the old value...
11445 if (job->attrs->prev)
11446 job->attrs->prev->next = attr2->next;
11448 job->attrs->attrs = attr2->next;
11450 if (job->attrs->last == attr2)
11451 job->attrs->last = job->attrs->prev;
11453 _ippFreeAttr(attr2);
11456 * Then copy the attribute...
11459 copy_attribute(job->attrs, attr, 0);
11462 * See if the job-name or job-hold-until is being changed.
11465 if (!strcmp(attr->name, "job-hold-until"))
11467 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-hold-until to %s",
11468 attr->values[0].string.text);
11469 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
11471 if (!strcmp(attr->values[0].string.text, "no-hold"))
11473 cupsdReleaseJob(job);
11477 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT,
11478 "Job held by \"%s\".", username);
11480 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
11483 else if (attr->value_tag == IPP_TAG_DELETEATTR)
11486 * Delete the attribute...
11489 if ((attr2 = ippFindAttribute(job->attrs, attr->name,
11490 IPP_TAG_ZERO)) != NULL)
11492 if (job->attrs->prev)
11493 job->attrs->prev->next = attr2->next;
11495 job->attrs->attrs = attr2->next;
11497 if (attr2 == job->attrs->last)
11498 job->attrs->last = job->attrs->prev;
11500 _ippFreeAttr(attr2);
11502 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
11508 * Add new option by copying it...
11511 copy_attribute(job->attrs, attr, 0);
11513 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
11522 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
11525 * Send events as needed...
11528 if (event & CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED)
11529 cupsdAddEvent(CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED,
11530 cupsdFindDest(job->dest), job,
11531 "Job priority changed by user.");
11533 if (event & CUPSD_EVENT_JOB_STATE)
11534 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
11535 job->state_value == IPP_JOB_HELD ?
11536 "Job held by user." : "Job restarted by user.");
11538 if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
11539 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
11540 "Job options changed by user.");
11543 * Start jobs if possible...
11552 * 'set_printer_attrs()' - Set printer attributes.
11556 set_printer_attrs(cupsd_client_t *con, /* I - Client connection */
11557 ipp_attribute_t *uri) /* I - Printer */
11559 http_status_t status; /* Policy status */
11560 cups_ptype_t dtype; /* Destination type (printer/class) */
11561 cupsd_printer_t *printer; /* Printer/class */
11562 ipp_attribute_t *attr; /* Printer attribute */
11563 int changed = 0; /* Was anything changed? */
11566 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_attrs(%p[%d], %s)", con,
11567 con->http.fd, uri->values[0].string.text);
11570 * Is the destination valid?
11573 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
11579 send_ipp_status(con, IPP_NOT_FOUND,
11580 _("The printer or class does not exist."));
11588 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11590 send_http_error(con, status, printer);
11595 * Return a list of attributes that can be set via Set-Printer-Attributes.
11598 if ((attr = ippFindAttribute(con->request, "printer-location",
11599 IPP_TAG_TEXT)) != NULL)
11601 cupsdSetString(&printer->location, attr->values[0].string.text);
11605 if ((attr = ippFindAttribute(con->request, "printer-info",
11606 IPP_TAG_TEXT)) != NULL)
11608 cupsdSetString(&printer->info, attr->values[0].string.text);
11613 * Update the printer attributes and return...
11618 cupsdSetPrinterAttrs(printer);
11619 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
11621 cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG, printer, NULL,
11622 "Printer \"%s\" description or location changed by \"%s\".",
11623 printer->name, get_username(con));
11625 cupsdLogMessage(CUPSD_LOG_INFO,
11626 "Printer \"%s\" description or location changed by \"%s\".",
11627 printer->name, get_username(con));
11630 con->response->request.status.status_code = IPP_OK;
11635 * 'set_printer_defaults()' - Set printer default options from a request.
11639 set_printer_defaults(
11640 cupsd_client_t *con, /* I - Client connection */
11641 cupsd_printer_t *printer) /* I - Printer */
11643 int i; /* Looping var */
11644 ipp_attribute_t *attr; /* Current attribute */
11645 int namelen; /* Length of attribute name */
11646 char name[256], /* New attribute name */
11647 value[256]; /* String version of integer attrs */
11650 for (attr = con->request->attrs; attr; attr = attr->next)
11653 * Skip non-printer attributes...
11656 if (attr->group_tag != IPP_TAG_PRINTER || !attr->name)
11659 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_defaults: %s", attr->name);
11661 if (!strcmp(attr->name, "job-sheets-default"))
11664 * Only allow keywords and names...
11667 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
11671 * Only allow job-sheets-default to be set when running without a
11672 * system high classification level...
11675 if (Classification)
11678 cupsdSetString(&printer->job_sheets[0], attr->values[0].string.text);
11680 if (attr->num_values > 1)
11681 cupsdSetString(&printer->job_sheets[1], attr->values[1].string.text);
11683 cupsdSetString(&printer->job_sheets[1], "none");
11685 else if (!strcmp(attr->name, "requesting-user-name-allowed"))
11687 cupsdFreeStrings(&(printer->users));
11689 printer->deny_users = 0;
11691 if (attr->value_tag == IPP_TAG_NAME &&
11692 (attr->num_values > 1 ||
11693 strcmp(attr->values[0].string.text, "all")))
11695 for (i = 0; i < attr->num_values; i ++)
11696 cupsdAddString(&(printer->users), attr->values[i].string.text);
11699 else if (!strcmp(attr->name, "requesting-user-name-denied"))
11701 cupsdFreeStrings(&(printer->users));
11703 printer->deny_users = 1;
11705 if (attr->value_tag == IPP_TAG_NAME &&
11706 (attr->num_values > 1 ||
11707 strcmp(attr->values[0].string.text, "none")))
11709 for (i = 0; i < attr->num_values; i ++)
11710 cupsdAddString(&(printer->users), attr->values[i].string.text);
11713 else if (!strcmp(attr->name, "job-quota-period"))
11715 if (attr->value_tag != IPP_TAG_INTEGER)
11718 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-quota-period to %d...",
11719 attr->values[0].integer);
11720 cupsdFreeQuotas(printer);
11722 printer->quota_period = attr->values[0].integer;
11724 else if (!strcmp(attr->name, "job-k-limit"))
11726 if (attr->value_tag != IPP_TAG_INTEGER)
11729 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-k-limit to %d...",
11730 attr->values[0].integer);
11731 cupsdFreeQuotas(printer);
11733 printer->k_limit = attr->values[0].integer;
11735 else if (!strcmp(attr->name, "job-page-limit"))
11737 if (attr->value_tag != IPP_TAG_INTEGER)
11740 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-page-limit to %d...",
11741 attr->values[0].integer);
11742 cupsdFreeQuotas(printer);
11744 printer->page_limit = attr->values[0].integer;
11746 else if (!strcmp(attr->name, "printer-op-policy"))
11748 cupsd_policy_t *p; /* Policy */
11751 if (attr->value_tag != IPP_TAG_NAME)
11754 if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
11756 cupsdLogMessage(CUPSD_LOG_DEBUG,
11757 "Setting printer-op-policy to \"%s\"...",
11758 attr->values[0].string.text);
11759 cupsdSetString(&printer->op_policy, attr->values[0].string.text);
11760 printer->op_policy_ptr = p;
11764 send_ipp_status(con, IPP_NOT_POSSIBLE,
11765 _("Unknown printer-op-policy \"%s\"."),
11766 attr->values[0].string.text);
11770 else if (!strcmp(attr->name, "printer-error-policy"))
11772 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
11775 if (strcmp(attr->values[0].string.text, "retry-current-job") &&
11776 ((printer->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_CLASS)) ||
11777 (strcmp(attr->values[0].string.text, "abort-job") &&
11778 strcmp(attr->values[0].string.text, "retry-job") &&
11779 strcmp(attr->values[0].string.text, "stop-printer"))))
11781 send_ipp_status(con, IPP_NOT_POSSIBLE,
11782 _("Unknown printer-error-policy \"%s\"."),
11783 attr->values[0].string.text);
11787 cupsdLogMessage(CUPSD_LOG_DEBUG,
11788 "Setting printer-error-policy to \"%s\"...",
11789 attr->values[0].string.text);
11790 cupsdSetString(&printer->error_policy, attr->values[0].string.text);
11794 * Skip any other non-default attributes...
11797 namelen = strlen(attr->name);
11798 if (namelen < 9 || strcmp(attr->name + namelen - 8, "-default") ||
11799 namelen > (sizeof(name) - 1) || attr->num_values != 1)
11803 * OK, anything else must be a user-defined default...
11806 strlcpy(name, attr->name, sizeof(name));
11807 name[namelen - 8] = '\0'; /* Strip "-default" */
11809 switch (attr->value_tag)
11811 case IPP_TAG_DELETEATTR :
11812 printer->num_options = cupsRemoveOption(name,
11813 printer->num_options,
11814 &(printer->options));
11815 cupsdLogMessage(CUPSD_LOG_DEBUG,
11816 "Deleting %s", attr->name);
11819 case IPP_TAG_NAME :
11820 case IPP_TAG_KEYWORD :
11822 printer->num_options = cupsAddOption(name,
11823 attr->values[0].string.text,
11824 printer->num_options,
11825 &(printer->options));
11826 cupsdLogMessage(CUPSD_LOG_DEBUG,
11827 "Setting %s to \"%s\"...", attr->name,
11828 attr->values[0].string.text);
11831 case IPP_TAG_BOOLEAN :
11832 printer->num_options = cupsAddOption(name,
11833 attr->values[0].boolean ?
11835 printer->num_options,
11836 &(printer->options));
11837 cupsdLogMessage(CUPSD_LOG_DEBUG,
11838 "Setting %s to %s...", attr->name,
11839 attr->values[0].boolean ? "true" : "false");
11842 case IPP_TAG_INTEGER :
11843 case IPP_TAG_ENUM :
11844 sprintf(value, "%d", attr->values[0].integer);
11845 printer->num_options = cupsAddOption(name, value,
11846 printer->num_options,
11847 &(printer->options));
11848 cupsdLogMessage(CUPSD_LOG_DEBUG,
11849 "Setting %s to %s...", attr->name, value);
11852 case IPP_TAG_RANGE :
11853 sprintf(value, "%d-%d", attr->values[0].range.lower,
11854 attr->values[0].range.upper);
11855 printer->num_options = cupsAddOption(name, value,
11856 printer->num_options,
11857 &(printer->options));
11858 cupsdLogMessage(CUPSD_LOG_DEBUG,
11859 "Setting %s to %s...", attr->name, value);
11862 case IPP_TAG_RESOLUTION :
11863 sprintf(value, "%dx%d%s", attr->values[0].resolution.xres,
11864 attr->values[0].resolution.yres,
11865 attr->values[0].resolution.units == IPP_RES_PER_INCH ?
11867 printer->num_options = cupsAddOption(name, value,
11868 printer->num_options,
11869 &(printer->options));
11870 cupsdLogMessage(CUPSD_LOG_DEBUG,
11871 "Setting %s to %s...", attr->name, value);
11875 /* Do nothing for other values */
11883 * 'start_printer()' - Start a printer.
11887 start_printer(cupsd_client_t *con, /* I - Client connection */
11888 ipp_attribute_t *uri) /* I - Printer URI */
11890 int i; /* Temporary variable */
11891 http_status_t status; /* Policy status */
11892 cups_ptype_t dtype; /* Destination type (printer/class) */
11893 cupsd_printer_t *printer; /* Printer data */
11896 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", con,
11897 con->http.fd, uri->values[0].string.text);
11900 * Is the destination valid?
11903 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
11909 send_ipp_status(con, IPP_NOT_FOUND,
11910 _("The printer or class does not exist."));
11918 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11920 send_http_error(con, status, printer);
11925 * Start the printer...
11928 printer->state_message[0] = '\0';
11930 cupsdStartPrinter(printer, 1);
11932 if (dtype & CUPS_PRINTER_CLASS)
11933 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".",
11934 printer->name, get_username(con));
11936 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".",
11937 printer->name, get_username(con));
11945 if ((i = check_quotas(con, printer)) < 0)
11947 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
11952 send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Not allowed to print."));
11957 * Everything was ok, so return OK status...
11960 con->response->request.status.status_code = IPP_OK;
11965 * 'stop_printer()' - Stop a printer.
11969 stop_printer(cupsd_client_t *con, /* I - Client connection */
11970 ipp_attribute_t *uri) /* I - Printer URI */
11972 http_status_t status; /* Policy status */
11973 cups_ptype_t dtype; /* Destination type (printer/class) */
11974 cupsd_printer_t *printer; /* Printer data */
11975 ipp_attribute_t *attr; /* printer-state-message attribute */
11978 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", con,
11979 con->http.fd, uri->values[0].string.text);
11982 * Is the destination valid?
11985 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
11991 send_ipp_status(con, IPP_NOT_FOUND,
11992 _("The printer or class does not exist."));
12000 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
12002 send_http_error(con, status, printer);
12007 * Stop the printer...
12010 if ((attr = ippFindAttribute(con->request, "printer-state-message",
12011 IPP_TAG_TEXT)) == NULL)
12012 strcpy(printer->state_message, "Paused");
12015 strlcpy(printer->state_message, attr->values[0].string.text,
12016 sizeof(printer->state_message));
12019 cupsdStopPrinter(printer, 1);
12021 if (dtype & CUPS_PRINTER_CLASS)
12022 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".",
12023 printer->name, get_username(con));
12025 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".",
12026 printer->name, get_username(con));
12029 * Everything was ok, so return OK status...
12032 con->response->request.status.status_code = IPP_OK;
12037 * 'url_encode_attr()' - URL-encode a string attribute.
12041 url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */
12042 char *buffer,/* I - String buffer */
12043 int bufsize)/* I - Size of buffer */
12045 int i; /* Looping var */
12046 char *bufptr, /* Pointer into buffer */
12047 *bufend; /* End of buffer */
12050 strlcpy(buffer, attr->name, bufsize);
12051 bufptr = buffer + strlen(buffer);
12052 bufend = buffer + bufsize - 1;
12054 for (i = 0; i < attr->num_values; i ++)
12056 if (bufptr >= bufend)
12064 if (bufptr >= bufend)
12069 bufptr = url_encode_string(attr->values[i].string.text,
12070 bufptr, bufend - bufptr + 1);
12072 if (bufptr >= bufend)
12083 * 'url_encode_string()' - URL-encode a string.
12086 static char * /* O - End of string */
12087 url_encode_string(const char *s, /* I - String */
12088 char *buffer, /* I - String buffer */
12089 int bufsize) /* I - Size of buffer */
12091 char *bufptr, /* Pointer into buffer */
12092 *bufend; /* End of buffer */
12093 static const char *hex = "0123456789ABCDEF";
12098 bufend = buffer + bufsize - 1;
12100 while (*s && bufptr < bufend)
12102 if (*s == ' ' || *s == '%' || *s == '+')
12104 if (bufptr >= (bufend - 2))
12108 *bufptr++ = hex[(*s >> 4) & 15];
12109 *bufptr++ = hex[*s & 15];
12113 else if (*s == '\'' || *s == '\\')
12115 if (bufptr >= (bufend - 1))
12132 * 'user_allowed()' - See if a user is allowed to print to a queue.
12135 static int /* O - 0 if not allowed, 1 if allowed */
12136 user_allowed(cupsd_printer_t *p, /* I - Printer or class */
12137 const char *username) /* I - Username */
12139 struct passwd *pw; /* User password data */
12140 char baseuser[256], /* Base username */
12141 *baseptr, /* Pointer to "@" in base username */
12142 *name; /* Current user name */
12145 if (cupsArrayCount(p->users) == 0)
12148 if (!strcmp(username, "root"))
12151 if (strchr(username, '@'))
12154 * Strip @REALM for username check...
12157 strlcpy(baseuser, username, sizeof(baseuser));
12159 if ((baseptr = strchr(baseuser, '@')) != NULL)
12162 username = baseuser;
12165 pw = getpwnam(username);
12168 for (name = (char *)cupsArrayFirst(p->users);
12170 name = (char *)cupsArrayNext(p->users))
12172 if (name[0] == '@')
12175 * Check group membership...
12178 if (cupsdCheckGroup(username, pw, name + 1))
12181 else if (name[0] == '#')
12187 if (cupsdCheckGroup(username, pw, name))
12190 else if (!_cups_strcasecmp(username, name))
12194 return ((name != NULL) != p->deny_users);
12199 * 'validate_job()' - Validate printer options and destination.
12203 validate_job(cupsd_client_t *con, /* I - Client connection */
12204 ipp_attribute_t *uri) /* I - Printer URI */
12206 http_status_t status; /* Policy status */
12207 ipp_attribute_t *attr, /* Current attribute */
12208 *auth_info; /* auth-info attribute */
12209 ipp_attribute_t *format; /* Document-format attribute */
12210 cups_ptype_t dtype; /* Destination type (printer/class) */
12211 char super[MIME_MAX_SUPER],
12212 /* Supertype of file */
12213 type[MIME_MAX_TYPE];
12214 /* Subtype of file */
12215 cupsd_printer_t *printer; /* Printer */
12218 cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", con,
12219 con->http.fd, uri->values[0].string.text);
12222 * OK, see if the client is sending the document compressed - CUPS
12223 * doesn't support compression yet...
12226 if ((attr = ippFindAttribute(con->request, "compression",
12227 IPP_TAG_KEYWORD)) != NULL)
12229 if (strcmp(attr->values[0].string.text, "none")
12231 && strcmp(attr->values[0].string.text, "gzip")
12232 #endif /* HAVE_LIBZ */
12235 send_ipp_status(con, IPP_ATTRIBUTES,
12236 _("Unsupported compression \"%s\"."),
12237 attr->values[0].string.text);
12238 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
12239 "compression", NULL, attr->values[0].string.text);
12245 * Is it a format we support?
12248 if ((format = ippFindAttribute(con->request, "document-format",
12249 IPP_TAG_MIMETYPE)) != NULL)
12251 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
12254 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"."),
12255 format->values[0].string.text);
12259 if ((strcmp(super, "application") || strcmp(type, "octet-stream")) &&
12260 !mimeType(MimeDatabase, super, type))
12262 cupsdLogMessage(CUPSD_LOG_INFO,
12263 "Hint: Do you have the raw file printing rules enabled?");
12264 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
12265 _("Unsupported document-format \"%s\"."),
12266 format->values[0].string.text);
12267 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
12268 "document-format", NULL, format->values[0].string.text);
12274 * Is the destination valid?
12277 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
12283 send_ipp_status(con, IPP_NOT_FOUND,
12284 _("The printer or class does not exist."));
12292 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
12294 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
12296 send_http_error(con, status, printer);
12299 else if (printer->num_auth_info_required == 1 &&
12300 !strcmp(printer->auth_info_required[0], "negotiate") &&
12303 send_http_error(con, HTTP_UNAUTHORIZED, printer);
12307 else if (auth_info && !con->http.tls &&
12308 !httpAddrLocalhost(con->http.hostaddr))
12311 * Require encryption of auth-info over non-local connections...
12314 send_http_error(con, HTTP_UPGRADE_REQUIRED, printer);
12317 #endif /* HAVE_SSL */
12320 * Everything was ok, so return OK status...
12323 con->response->request.status.status_code = IPP_OK;
12328 * 'validate_name()' - Make sure the printer name only contains valid chars.
12331 static int /* O - 0 if name is no good, 1 if good */
12332 validate_name(const char *name) /* I - Name to check */
12334 const char *ptr; /* Pointer into name */
12338 * Scan the whole name...
12341 for (ptr = name; *ptr; ptr ++)
12342 if ((*ptr > 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
12346 * All the characters are good; validate the length, too...
12349 return ((ptr - name) < 128);
12354 * 'validate_user()' - Validate the user for the request.
12357 static int /* O - 1 if permitted, 0 otherwise */
12358 validate_user(cupsd_job_t *job, /* I - Job */
12359 cupsd_client_t *con, /* I - Client connection */
12360 const char *owner, /* I - Owner of job/resource */
12361 char *username, /* O - Authenticated username */
12362 int userlen) /* I - Length of username */
12364 cupsd_printer_t *printer; /* Printer for job */
12367 cupsdLogMessage(CUPSD_LOG_DEBUG2,
12368 "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, "
12370 job->id, con ? con->http.fd : 0,
12371 owner ? owner : "(null)", username, userlen);
12374 * Validate input...
12377 if (!con || !owner || !username || userlen <= 0)
12381 * Get the best authenticated username that is available.
12384 strlcpy(username, get_username(con), userlen);
12387 * Check the username against the owner...
12390 printer = cupsdFindDest(job->dest);
12392 return (cupsdCheckPolicy(printer ? printer->op_policy_ptr : DefaultPolicyPtr,
12393 con, owner) == HTTP_OK);
12398 * End of "$Id: ipp.c 10274 2012-02-13 20:42:51Z mike $".