]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/ipp.c
Merge changes from CUPS 1.5svn-r9400
[thirdparty/cups.git] / scheduler / ipp.c
CommitLineData
ef416fc2 1/*
b19ccc9e 2 * "$Id: ipp.c 7944 2008-09-16 22:32:42Z mike $"
ef416fc2 3 *
aaf19ab0 4 * IPP routines for the CUPS scheduler.
ef416fc2 5 *
f8b3a85b 6 * Copyright 2007-2010 by Apple Inc.
f7deaa1a 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 * This file contains Kerberos support code, copyright 2006 by
10 * Jelmer Vernooij.
ef416fc2 11 *
12 * These coded instructions, statements, and computer programs are the
bc44d920 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/".
ef416fc2 17 *
18 * Contents:
19 *
a603edef
MS
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...
9a4f8274 28 * add_job_subscriptions() - Add any subscriptions for a job.
a603edef
MS
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
38 * printer.
39 * apply_printer_defaults() - Apply printer default options to a job.
40 * authenticate_job() - Set job authentication info.
aaf19ab0 41 * cancel_all_jobs() - Cancel all or selected print jobs.
a603edef
MS
42 * cancel_job() - Cancel a print job.
43 * cancel_subscription() - Cancel a subscription.
3dfe78b3
MS
44 * check_rss_recipient() - Check that we do not have a duplicate RSS
45 * feed URI.
aaf19ab0
MS
46 * check_quotas() - Check quotas for a printer and user.
47 * close_job() - Close a multi-file job.
a603edef
MS
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
54 * values as needed...
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
64 * local system.
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
71 * system.
72 * get_printer_attrs() - Get printer attributes.
aaf19ab0 73 * get_printer_supported() - Get printer supported values.
a603edef
MS
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.
aaf19ab0 79 * hold_new_jobs() - Hold pending/new jobs on a printer or class.
a603edef
MS
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.
c5571a1d 83 * read_job_ticket() - Read a job ticket embedded in a print file.
a603edef 84 * reject_jobs() - Reject print jobs to a printer.
aaf19ab0
MS
85 * release_held_new_jobs() - Release pending/new jobs on a printer or
86 * class.
a603edef
MS
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 * save_krb5_creds() - Save Kerberos credentials for the job.
92 * send_document() - Send a file to a printer or class.
93 * send_http_error() - Send a HTTP error back to the IPP client.
94 * send_ipp_status() - Send a status back to the IPP client.
95 * set_default() - Set the default destination...
96 * set_job_attrs() - Set job attributes.
c168a833 97 * set_printer_attrs() - Set printer attributes.
a603edef
MS
98 * set_printer_defaults() - Set printer default options from a request.
99 * start_printer() - Start a printer.
100 * stop_printer() - Stop a printer.
101 * url_encode_attr() - URL-encode a string attribute.
102 * url_encode_string() - URL-encode a string.
103 * user_allowed() - See if a user is allowed to print to a queue.
104 * validate_job() - Validate printer options and destination.
105 * validate_name() - Make sure the printer name only contains
106 * valid chars.
107 * validate_user() - Validate the user for the request.
ef416fc2 108 */
109
110/*
111 * Include necessary headers...
112 */
113
114#include "cupsd.h"
a603edef 115#include <cups/ppd-private.h>
ef416fc2 116
f899b121 117#ifdef __APPLE__
568fa3fa 118# include <ApplicationServices/ApplicationServices.h>
229681c1
MS
119# ifdef HAVE_COLORSYNCREGISTERDEVICE
120extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id);
121# endif /* HAVE_COLORSYNCREGISTERDEVICE */
568fa3fa 122# include <CoreFoundation/CoreFoundation.h>
f899b121 123# ifdef HAVE_MEMBERSHIP_H
124# include <membership.h>
125# endif /* HAVE_MEMBERSHIP_H */
126# ifdef HAVE_MEMBERSHIPPRIV_H
127# include <membershipPriv.h>
128# else
7594b224 129extern int mbr_user_name_to_uuid(const char* name, uuid_t uu);
130extern int mbr_group_name_to_uuid(const char* name, uuid_t uu);
131extern int mbr_check_membership_by_id(uuid_t user, gid_t group, int* ismember);
f899b121 132# endif /* HAVE_MEMBERSHIPPRIV_H */
133#endif /* __APPLE__ */
7594b224 134
ef416fc2 135
ef416fc2 136/*
137 * Local functions...
138 */
139
140static void accept_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
141static void add_class(cupsd_client_t *con, ipp_attribute_t *uri);
b423cd4c 142static int add_file(cupsd_client_t *con, cupsd_job_t *job,
143 mime_type_t *filetype, int compression);
f7deaa1a 144static cupsd_job_t *add_job(cupsd_client_t *con, cupsd_printer_t *printer,
80ca4592 145 mime_type_t *filetype);
ef416fc2 146static void add_job_state_reasons(cupsd_client_t *con, cupsd_job_t *job);
147static void add_job_subscriptions(cupsd_client_t *con, cupsd_job_t *job);
bd7854cb 148static void add_job_uuid(cupsd_client_t *con, cupsd_job_t *job);
ef416fc2 149static void add_printer(cupsd_client_t *con, ipp_attribute_t *uri);
b423cd4c 150static void add_printer_state_reasons(cupsd_client_t *con,
151 cupsd_printer_t *p);
ef416fc2 152static void add_queued_job_count(cupsd_client_t *con, cupsd_printer_t *p);
568fa3fa 153#ifdef __APPLE__
a603edef 154static void apple_init_profile(ppd_file_t *ppd, cups_array_t *languages,
229681c1
MS
155# ifdef HAVE_COLORSYNCREGISTERDEVICE
156 CFMutableDictionaryRef profile,
157# else
158 CMDeviceProfileInfo *profile,
159# endif /* HAVE_COLORSYNCREGISTERDEVICE */
160 unsigned id, const char *name,
161 const char *text, const char *iccfile);
568fa3fa
MS
162static void apple_register_profiles(cupsd_printer_t *p);
163static void apple_unregister_profiles(cupsd_printer_t *p);
164#endif /* __APPLE__ */
b423cd4c 165static void apply_printer_defaults(cupsd_printer_t *printer,
166 cupsd_job_t *job);
ef416fc2 167static void authenticate_job(cupsd_client_t *con, ipp_attribute_t *uri);
168static void cancel_all_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
169static void cancel_job(cupsd_client_t *con, ipp_attribute_t *uri);
170static void cancel_subscription(cupsd_client_t *con, int id);
3dfe78b3 171static int check_rss_recipient(const char *recipient);
ef416fc2 172static int check_quotas(cupsd_client_t *con, cupsd_printer_t *p);
173static ipp_attribute_t *copy_attribute(ipp_t *to, ipp_attribute_t *attr,
174 int quickcopy);
aaf19ab0 175static void close_job(cupsd_client_t *con, ipp_attribute_t *uri);
fa73b229 176static void copy_attrs(ipp_t *to, ipp_t *from, cups_array_t *ra,
10d09e33
MS
177 ipp_tag_t group, int quickcopy,
178 cups_array_t *exclude);
b423cd4c 179static int copy_banner(cupsd_client_t *con, cupsd_job_t *job,
180 const char *name);
ef416fc2 181static int copy_file(const char *from, const char *to);
b423cd4c 182static int copy_model(cupsd_client_t *con, const char *from,
183 const char *to);
fa73b229 184static void copy_job_attrs(cupsd_client_t *con,
185 cupsd_job_t *job,
10d09e33 186 cups_array_t *ra, cups_array_t *exclude);
fa73b229 187static void copy_printer_attrs(cupsd_client_t *con,
188 cupsd_printer_t *printer,
189 cups_array_t *ra);
ef416fc2 190static void copy_subscription_attrs(cupsd_client_t *con,
191 cupsd_subscription_t *sub,
10d09e33
MS
192 cups_array_t *ra,
193 cups_array_t *exclude);
ef416fc2 194static void create_job(cupsd_client_t *con, ipp_attribute_t *uri);
195static cups_array_t *create_requested_array(ipp_t *request);
196static void create_subscription(cupsd_client_t *con, ipp_attribute_t *uri);
197static void delete_printer(cupsd_client_t *con, ipp_attribute_t *uri);
198static void get_default(cupsd_client_t *con);
199static void get_devices(cupsd_client_t *con);
2e4ff8af 200static void get_document(cupsd_client_t *con, ipp_attribute_t *uri);
ef416fc2 201static void get_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
202static void get_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
bd7854cb 203static void get_notifications(cupsd_client_t *con);
b94498cf 204static void get_ppd(cupsd_client_t *con, ipp_attribute_t *uri);
ef416fc2 205static void get_ppds(cupsd_client_t *con);
206static void get_printers(cupsd_client_t *con, int type);
207static void get_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
c168a833 208static void get_printer_supported(cupsd_client_t *con, ipp_attribute_t *uri);
ef416fc2 209static void get_subscription_attrs(cupsd_client_t *con, int sub_id);
210static void get_subscriptions(cupsd_client_t *con, ipp_attribute_t *uri);
e00b005a 211static const char *get_username(cupsd_client_t *con);
ef416fc2 212static void hold_job(cupsd_client_t *con, ipp_attribute_t *uri);
61cf44e2 213static void hold_new_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
ef416fc2 214static void move_job(cupsd_client_t *con, ipp_attribute_t *uri);
ef416fc2 215static int ppd_parse_line(const char *line, char *option, int olen,
216 char *choice, int clen);
217static void print_job(cupsd_client_t *con, ipp_attribute_t *uri);
c5571a1d 218static void read_job_ticket(cupsd_client_t *con);
ef416fc2 219static void reject_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
61cf44e2
MS
220static void release_held_new_jobs(cupsd_client_t *con,
221 ipp_attribute_t *uri);
ef416fc2 222static void release_job(cupsd_client_t *con, ipp_attribute_t *uri);
223static void renew_subscription(cupsd_client_t *con, int sub_id);
224static void restart_job(cupsd_client_t *con, ipp_attribute_t *uri);
f7deaa1a 225static void save_auth_info(cupsd_client_t *con, cupsd_job_t *job,
226 ipp_attribute_t *auth_info);
227#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
228static void save_krb5_creds(cupsd_client_t *con, cupsd_job_t *job);
229#endif /* HAVE_GSSAPI && HAVE_KRB5_H */
ef416fc2 230static void send_document(cupsd_client_t *con, ipp_attribute_t *uri);
f899b121 231static void send_http_error(cupsd_client_t *con, http_status_t status,
232 cupsd_printer_t *printer);
ef416fc2 233static void send_ipp_status(cupsd_client_t *con, ipp_status_t status,
234 const char *message, ...)
235# ifdef __GNUC__
236__attribute__ ((__format__ (__printf__, 3, 4)))
237# endif /* __GNUC__ */
238;
239static void set_default(cupsd_client_t *con, ipp_attribute_t *uri);
240static void set_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
c168a833 241static void set_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
b423cd4c 242static void set_printer_defaults(cupsd_client_t *con,
243 cupsd_printer_t *printer);
ef416fc2 244static void start_printer(cupsd_client_t *con, ipp_attribute_t *uri);
245static void stop_printer(cupsd_client_t *con, ipp_attribute_t *uri);
89d46774 246static void url_encode_attr(ipp_attribute_t *attr, char *buffer,
247 int bufsize);
b94498cf 248static char *url_encode_string(const char *s, char *buffer, int bufsize);
ef416fc2 249static int user_allowed(cupsd_printer_t *p, const char *username);
250static void validate_job(cupsd_client_t *con, ipp_attribute_t *uri);
251static int validate_name(const char *name);
252static int validate_user(cupsd_job_t *job, cupsd_client_t *con,
253 const char *owner, char *username,
254 int userlen);
255
256
257/*
09a101d6 258 * 'cupsdProcessIPPRequest()' - Process an incoming IPP request.
ef416fc2 259 */
260
261int /* O - 1 on success, 0 on failure */
262cupsdProcessIPPRequest(
263 cupsd_client_t *con) /* I - Client connection */
264{
265 ipp_tag_t group; /* Current group tag */
266 ipp_attribute_t *attr; /* Current attribute */
267 ipp_attribute_t *charset; /* Character set attribute */
268 ipp_attribute_t *language; /* Language attribute */
9f5eb9be 269 ipp_attribute_t *uri = NULL; /* Printer or job URI attribute */
ef416fc2 270 ipp_attribute_t *username; /* requesting-user-name attr */
271 int sub_id; /* Subscription ID */
272
273
274 cupsdLogMessage(CUPSD_LOG_DEBUG2,
275 "cupsdProcessIPPRequest(%p[%d]): operation_id = %04x",
276 con, con->http.fd, con->request->request.op.operation_id);
277
278 /*
279 * First build an empty response message for this request...
280 */
281
282 con->response = ippNew();
283
bc44d920 284 con->response->request.status.version[0] =
285 con->request->request.op.version[0];
286 con->response->request.status.version[1] =
287 con->request->request.op.version[1];
288 con->response->request.status.request_id =
289 con->request->request.op.request_id;
ef416fc2 290
291 /*
292 * Then validate the request header and required attributes...
293 */
f7deaa1a 294
c168a833
MS
295 if (con->request->request.any.version[0] != 1 &&
296 con->request->request.any.version[0] != 2)
ef416fc2 297 {
298 /*
c168a833 299 * Return an error, since we only support IPP 1.x and 2.x.
ef416fc2 300 */
301
302 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
303 "%04X %s Bad request version number %d.%d",
304 IPP_VERSION_NOT_SUPPORTED, con->http.hostname,
305 con->request->request.any.version[0],
306 con->request->request.any.version[1]);
307
308 send_ipp_status(con, IPP_VERSION_NOT_SUPPORTED,
4d301e69 309 _("Bad request version number %d.%d"),
ef416fc2 310 con->request->request.any.version[0],
311 con->request->request.any.version[1]);
f7deaa1a 312 }
f0ab5bff
MS
313 else if (con->request->request.any.request_id < 1)
314 {
315 /*
316 * Return an error, since request IDs must be between 1 and 2^31-1
317 */
318
319 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
320 "%04X %s Bad request ID %d",
321 IPP_BAD_REQUEST, con->http.hostname,
322 con->request->request.any.request_id);
323
4d301e69 324 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad request ID %d"),
f0ab5bff
MS
325 con->request->request.any.request_id);
326 }
fa73b229 327 else if (!con->request->attrs)
ef416fc2 328 {
329 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
330 "%04X %s No attributes in request",
331 IPP_BAD_REQUEST, con->http.hostname);
332
4d301e69 333 send_ipp_status(con, IPP_BAD_REQUEST, _("No attributes in request"));
ef416fc2 334 }
335 else
336 {
337 /*
338 * Make sure that the attributes are provided in the correct order and
339 * don't repeat groups...
340 */
341
342 for (attr = con->request->attrs, group = attr->group_tag;
fa73b229 343 attr;
ef416fc2 344 attr = attr->next)
345 if (attr->group_tag < group && attr->group_tag != IPP_TAG_ZERO)
346 {
347 /*
348 * Out of order; return an error...
349 */
350
351 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
352 "%04X %s Attribute groups are out of order",
353 IPP_BAD_REQUEST, con->http.hostname);
354
355 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 356 _("Attribute groups are out of order (%x < %x)"),
ef416fc2 357 attr->group_tag, group);
358 break;
359 }
360 else
361 group = attr->group_tag;
362
fa73b229 363 if (!attr)
ef416fc2 364 {
365 /*
366 * Then make sure that the first three attributes are:
367 *
368 * attributes-charset
369 * attributes-natural-language
370 * printer-uri/job-uri
371 */
372
373 attr = con->request->attrs;
1340db2d
MS
374 if (attr && attr->name &&
375 !strcmp(attr->name, "attributes-charset") &&
fa73b229 376 (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_CHARSET)
ef416fc2 377 charset = attr;
378 else
379 charset = NULL;
380
381 if (attr)
382 attr = attr->next;
383
1340db2d
MS
384 if (attr && attr->name &&
385 !strcmp(attr->name, "attributes-natural-language") &&
fa73b229 386 (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_LANGUAGE)
ef416fc2 387 language = attr;
388 else
389 language = NULL;
390
fa73b229 391 if ((attr = ippFindAttribute(con->request, "printer-uri",
392 IPP_TAG_URI)) != NULL)
ef416fc2 393 uri = attr;
fa73b229 394 else if ((attr = ippFindAttribute(con->request, "job-uri",
395 IPP_TAG_URI)) != NULL)
ef416fc2 396 uri = attr;
b94498cf 397 else if (con->request->request.op.operation_id == CUPS_GET_PPD)
398 uri = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME);
ef416fc2 399 else
400 uri = NULL;
401
402 if (charset)
403 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
bc44d920 404 "attributes-charset", NULL,
405 charset->values[0].string.text);
ef416fc2 406 else
407 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
f8b3a85b 408 "attributes-charset", NULL, "utf-8");
ef416fc2 409
410 if (language)
411 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
412 "attributes-natural-language", NULL,
413 language->values[0].string.text);
414 else
415 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
416 "attributes-natural-language", NULL, DefaultLanguage);
417
2e4ff8af
MS
418 if (charset &&
419 strcasecmp(charset->values[0].string.text, "us-ascii") &&
420 strcasecmp(charset->values[0].string.text, "utf-8"))
421 {
422 /*
423 * Bad character set...
424 */
425
4d301e69 426 cupsdLogMessage(CUPSD_LOG_ERROR, "Unsupported character set \"%s\"",
2e4ff8af
MS
427 charset->values[0].string.text);
428 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
429 "%04X %s Unsupported attributes-charset value \"%s\"",
430 IPP_CHARSET, con->http.hostname,
431 charset->values[0].string.text);
432 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 433 _("Unsupported character set \"%s\""),
2e4ff8af
MS
434 charset->values[0].string.text);
435 }
436 else if (!charset || !language ||
437 (!uri &&
438 con->request->request.op.operation_id != CUPS_GET_DEFAULT &&
439 con->request->request.op.operation_id != CUPS_GET_PRINTERS &&
440 con->request->request.op.operation_id != CUPS_GET_CLASSES &&
441 con->request->request.op.operation_id != CUPS_GET_DEVICES &&
442 con->request->request.op.operation_id != CUPS_GET_PPDS))
ef416fc2 443 {
444 /*
445 * Return an error, since attributes-charset,
446 * attributes-natural-language, and printer-uri/job-uri are required
447 * for all operations.
448 */
449
450 if (!charset)
451 {
452 cupsdLogMessage(CUPSD_LOG_ERROR,
4d301e69 453 "Missing attributes-charset attribute");
ef416fc2 454
455 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
456 "%04X %s Missing attributes-charset attribute",
457 IPP_BAD_REQUEST, con->http.hostname);
458 }
459
460 if (!language)
461 {
462 cupsdLogMessage(CUPSD_LOG_ERROR,
4d301e69 463 "Missing attributes-natural-language attribute");
ef416fc2 464
465 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
466 "%04X %s Missing attributes-natural-language attribute",
467 IPP_BAD_REQUEST, con->http.hostname);
468 }
469
470 if (!uri)
471 {
472 cupsdLogMessage(CUPSD_LOG_ERROR,
b94498cf 473 "Missing printer-uri, job-uri, or ppd-name "
4d301e69 474 "attribute");
ef416fc2 475
476 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
b94498cf 477 "%04X %s Missing printer-uri, job-uri, or ppd-name "
478 "attribute", IPP_BAD_REQUEST, con->http.hostname);
ef416fc2 479 }
480
481 cupsdLogMessage(CUPSD_LOG_DEBUG, "Request attributes follow...");
482
fa73b229 483 for (attr = con->request->attrs; attr; attr = attr->next)
f7deaa1a 484 cupsdLogMessage(CUPSD_LOG_DEBUG,
ef416fc2 485 "attr \"%s\": group_tag = %x, value_tag = %x",
486 attr->name ? attr->name : "(null)", attr->group_tag,
487 attr->value_tag);
488
489 cupsdLogMessage(CUPSD_LOG_DEBUG, "End of attributes...");
490
491 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 492 _("Missing required attributes"));
ef416fc2 493 }
494 else
495 {
496 /*
497 * OK, all the checks pass so far; make sure requesting-user-name is
498 * not "root" from a remote host...
499 */
500
501 if ((username = ippFindAttribute(con->request, "requesting-user-name",
502 IPP_TAG_NAME)) != NULL)
503 {
504 /*
505 * Check for root user...
506 */
507
508 if (!strcmp(username->values[0].string.text, "root") &&
509 strcasecmp(con->http.hostname, "localhost") &&
510 strcmp(con->username, "root"))
511 {
512 /*
513 * Remote unauthenticated user masquerading as local root...
514 */
515
757d2cad 516 _cupsStrFree(username->values[0].string.text);
517 username->values[0].string.text = _cupsStrAlloc(RemoteRoot);
ef416fc2 518 }
519 }
520
521 if ((attr = ippFindAttribute(con->request, "notify-subscription-id",
522 IPP_TAG_INTEGER)) != NULL)
523 sub_id = attr->values[0].integer;
524 else
525 sub_id = 0;
526
527 /*
528 * Then try processing the operation...
529 */
530
531 if (uri)
bd7854cb 532 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s",
533 ippOpString(con->request->request.op.operation_id),
534 uri->values[0].string.text);
535 else
536 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s",
537 ippOpString(con->request->request.op.operation_id));
ef416fc2 538
539 switch (con->request->request.op.operation_id)
540 {
541 case IPP_PRINT_JOB :
542 print_job(con, uri);
543 break;
544
545 case IPP_VALIDATE_JOB :
546 validate_job(con, uri);
547 break;
548
549 case IPP_CREATE_JOB :
550 create_job(con, uri);
551 break;
552
553 case IPP_SEND_DOCUMENT :
554 send_document(con, uri);
555 break;
556
557 case IPP_CANCEL_JOB :
558 cancel_job(con, uri);
559 break;
560
561 case IPP_GET_JOB_ATTRIBUTES :
562 get_job_attrs(con, uri);
563 break;
564
565 case IPP_GET_JOBS :
566 get_jobs(con, uri);
567 break;
568
569 case IPP_GET_PRINTER_ATTRIBUTES :
570 get_printer_attrs(con, uri);
571 break;
572
c168a833
MS
573 case IPP_GET_PRINTER_SUPPORTED_VALUES :
574 get_printer_supported(con, uri);
575 break;
576
ef416fc2 577 case IPP_HOLD_JOB :
578 hold_job(con, uri);
579 break;
580
581 case IPP_RELEASE_JOB :
582 release_job(con, uri);
583 break;
584
585 case IPP_RESTART_JOB :
586 restart_job(con, uri);
587 break;
588
589 case IPP_PAUSE_PRINTER :
590 stop_printer(con, uri);
591 break;
592
593 case IPP_RESUME_PRINTER :
594 start_printer(con, uri);
595 break;
596
597 case IPP_PURGE_JOBS :
aaf19ab0
MS
598 case IPP_CANCEL_JOBS :
599 case IPP_CANCEL_MY_JOBS :
ef416fc2 600 cancel_all_jobs(con, uri);
601 break;
602
603 case IPP_SET_JOB_ATTRIBUTES :
604 set_job_attrs(con, uri);
605 break;
606
c168a833
MS
607 case IPP_SET_PRINTER_ATTRIBUTES :
608 set_printer_attrs(con, uri);
609 break;
610
61cf44e2
MS
611 case IPP_HOLD_NEW_JOBS :
612 hold_new_jobs(con, uri);
613 break;
614
615 case IPP_RELEASE_HELD_NEW_JOBS :
616 release_held_new_jobs(con, uri);
617 break;
618
aaf19ab0
MS
619 case IPP_CLOSE_JOB :
620 close_job(con, uri);
621 break;
622
ef416fc2 623 case CUPS_GET_DEFAULT :
624 get_default(con);
625 break;
626
627 case CUPS_GET_PRINTERS :
628 get_printers(con, 0);
629 break;
630
631 case CUPS_GET_CLASSES :
632 get_printers(con, CUPS_PRINTER_CLASS);
633 break;
634
635 case CUPS_ADD_PRINTER :
636 add_printer(con, uri);
637 break;
638
639 case CUPS_DELETE_PRINTER :
640 delete_printer(con, uri);
641 break;
642
643 case CUPS_ADD_CLASS :
644 add_class(con, uri);
645 break;
646
647 case CUPS_DELETE_CLASS :
648 delete_printer(con, uri);
649 break;
650
651 case CUPS_ACCEPT_JOBS :
652 case IPP_ENABLE_PRINTER :
653 accept_jobs(con, uri);
654 break;
655
656 case CUPS_REJECT_JOBS :
657 case IPP_DISABLE_PRINTER :
658 reject_jobs(con, uri);
659 break;
660
661 case CUPS_SET_DEFAULT :
662 set_default(con, uri);
663 break;
664
665 case CUPS_GET_DEVICES :
666 get_devices(con);
667 break;
668
2e4ff8af
MS
669 case CUPS_GET_DOCUMENT :
670 get_document(con, uri);
671 break;
672
b94498cf 673 case CUPS_GET_PPD :
674 get_ppd(con, uri);
675 break;
676
ef416fc2 677 case CUPS_GET_PPDS :
678 get_ppds(con);
679 break;
680
681 case CUPS_MOVE_JOB :
682 move_job(con, uri);
683 break;
684
685 case CUPS_AUTHENTICATE_JOB :
686 authenticate_job(con, uri);
687 break;
688
689 case IPP_CREATE_PRINTER_SUBSCRIPTION :
690 case IPP_CREATE_JOB_SUBSCRIPTION :
691 create_subscription(con, uri);
692 break;
693
694 case IPP_GET_SUBSCRIPTION_ATTRIBUTES :
695 get_subscription_attrs(con, sub_id);
696 break;
697
698 case IPP_GET_SUBSCRIPTIONS :
699 get_subscriptions(con, uri);
700 break;
701
702 case IPP_RENEW_SUBSCRIPTION :
703 renew_subscription(con, sub_id);
704 break;
705
706 case IPP_CANCEL_SUBSCRIPTION :
707 cancel_subscription(con, sub_id);
708 break;
709
710 case IPP_GET_NOTIFICATIONS :
bd7854cb 711 get_notifications(con);
ef416fc2 712 break;
713
714 default :
715 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
716 "%04X %s Operation %04X (%s) not supported",
717 IPP_OPERATION_NOT_SUPPORTED, con->http.hostname,
718 con->request->request.op.operation_id,
719 ippOpString(con->request->request.op.operation_id));
720
721 send_ipp_status(con, IPP_OPERATION_NOT_SUPPORTED,
4d301e69 722 _("%s not supported"),
bc44d920 723 ippOpString(
724 con->request->request.op.operation_id));
ef416fc2 725 break;
726 }
727 }
728 }
729 }
730
731 if (con->response)
732 {
733 /*
734 * Sending data from the scheduler...
735 */
736
ee571f26 737 cupsdLogMessage(con->response->request.status.status_code
c5571a1d
MS
738 >= IPP_BAD_REQUEST &&
739 con->response->request.status.status_code
740 != IPP_NOT_FOUND ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
9f5eb9be 741 "Returning IPP %s for %s (%s) from %s",
ee571f26
MS
742 ippErrorString(con->response->request.status.status_code),
743 ippOpString(con->request->request.op.operation_id),
9f5eb9be 744 uri ? uri->values[0].string.text : "no URI",
ee571f26 745 con->http.hostname);
ef416fc2 746
d1c13e16
MS
747 if (LogLevel == CUPSD_LOG_DEBUG2)
748 cupsdLogMessage(CUPSD_LOG_DEBUG2,
749 "cupsdProcessIPPRequest: ippLength(response)=%ld",
750 (long)ippLength(con->response));
751
5bd77a73 752 if (cupsdSendHeader(con, HTTP_OK, "application/ipp", CUPSD_AUTH_NONE))
ef416fc2 753 {
f301802f 754#ifdef CUPSD_USE_CHUNKING
755 /*
756 * Because older versions of CUPS (1.1.17 and older) and some IPP
d09495fa 757 * clients do not implement chunking properly, we cannot use
f301802f 758 * chunking by default. This may become the default in future
759 * CUPS releases, or we might add a configuration directive for
760 * it.
761 */
762
ef416fc2 763 if (con->http.version == HTTP_1_1)
764 {
07725fee 765 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n") < 0)
766 return (0);
767
768 if (cupsdFlushHeader(con) < 0)
769 return (0);
d09495fa 770
771 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
ef416fc2 772 }
773 else
f301802f 774#endif /* CUPSD_USE_CHUNKING */
ef416fc2 775 {
d09495fa 776 size_t length; /* Length of response */
ef416fc2 777
d09495fa 778
779 length = ippLength(con->response);
ef416fc2 780
b94498cf 781 if (con->file >= 0 && !con->pipe_pid)
782 {
783 struct stat fileinfo; /* File information */
784
785
786 if (!fstat(con->file, &fileinfo))
787 length += fileinfo.st_size;
788 }
789
07725fee 790 if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n\r\n",
791 CUPS_LLCAST length) < 0)
792 return (0);
793
794 if (cupsdFlushHeader(con) < 0)
795 return (0);
d09495fa 796
797 con->http.data_encoding = HTTP_ENCODE_LENGTH;
798 con->http.data_remaining = length;
b94498cf 799
800 if (con->http.data_remaining <= INT_MAX)
801 con->http._data_remaining = con->http.data_remaining;
802 else
803 con->http._data_remaining = INT_MAX;
ef416fc2 804 }
805
f7deaa1a 806 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
807 (cupsd_selfunc_t)cupsdWriteClient, con);
ef416fc2 808
809 /*
810 * Tell the caller the response header was sent successfully...
811 */
812
813 return (1);
814 }
815 else
816 {
817 /*
818 * Tell the caller the response header could not be sent...
819 */
820
821 return (0);
822 }
823 }
824 else
825 {
826 /*
827 * Sending data from a subprocess like cups-deviced; tell the caller
828 * everything is A-OK so far...
829 */
830
831 return (1);
832 }
833}
834
835
09a101d6 836/*
837 * 'cupsdTimeoutJob()' - Timeout a job waiting on job files.
838 */
839
91c84a35 840int /* O - 0 on success, -1 on error */
09a101d6 841cupsdTimeoutJob(cupsd_job_t *job) /* I - Job to timeout */
842{
843 cupsd_printer_t *printer; /* Destination printer or class */
844 ipp_attribute_t *attr; /* job-sheets attribute */
845 int kbytes; /* Kilobytes in banner */
846
847
848 job->pending_timeout = 0;
849
850 /*
851 * See if we need to add the ending sheet...
852 */
853
6d2f911b
MS
854 if (!cupsdLoadJob(job))
855 return (-1);
856
09a101d6 857 printer = cupsdFindDest(job->dest);
858 attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
859
860 if (printer &&
861 !(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
862 attr && attr->num_values > 1)
863 {
864 /*
865 * Yes...
866 */
867
75bd9771
MS
868 cupsdLogJob(job, CUPSD_LOG_INFO, "Adding end banner page \"%s\".",
869 attr->values[1].string.text);
09a101d6 870
91c84a35
MS
871 if ((kbytes = copy_banner(NULL, job, attr->values[1].string.text)) < 0)
872 return (-1);
09a101d6 873
874 cupsdUpdateQuota(printer, job->username, 0, kbytes);
875 }
91c84a35
MS
876
877 return (0);
09a101d6 878}
879
880
ef416fc2 881/*
882 * 'accept_jobs()' - Accept print jobs to a printer.
883 */
884
885static void
886accept_jobs(cupsd_client_t *con, /* I - Client connection */
887 ipp_attribute_t *uri) /* I - Printer or class URI */
888{
889 http_status_t status; /* Policy status */
bc44d920 890 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 891 cupsd_printer_t *printer; /* Printer data */
892
893
894 cupsdLogMessage(CUPSD_LOG_DEBUG2, "accept_jobs(%p[%d], %s)", con,
895 con->http.fd, uri->values[0].string.text);
896
897 /*
898 * Is the destination valid?
899 */
900
f7deaa1a 901 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 902 {
903 /*
904 * Bad URI...
905 */
906
907 send_ipp_status(con, IPP_NOT_FOUND,
908 _("The printer or class was not found."));
909 return;
910 }
911
912 /*
913 * Check policy...
914 */
915
916 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
917 {
f899b121 918 send_http_error(con, status, printer);
ef416fc2 919 return;
920 }
921
922 /*
923 * Accept jobs sent to the printer...
924 */
925
926 printer->accepting = 1;
927 printer->state_message[0] = '\0';
928
929 cupsdAddPrinterHistory(printer);
930
f8b3a85b
MS
931 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
932 "Now accepting jobs.");
933
ef416fc2 934 if (dtype & CUPS_PRINTER_CLASS)
f7deaa1a 935 {
3dfe78b3 936 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
f7deaa1a 937
938 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" now accepting jobs (\"%s\").",
939 printer->name, get_username(con));
940 }
ef416fc2 941 else
f7deaa1a 942 {
3dfe78b3 943 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 944
bc44d920 945 cupsdLogMessage(CUPSD_LOG_INFO,
946 "Printer \"%s\" now accepting jobs (\"%s\").",
f7deaa1a 947 printer->name, get_username(con));
948 }
ef416fc2 949
950 /*
951 * Everything was ok, so return OK status...
952 */
953
954 con->response->request.status.status_code = IPP_OK;
955}
956
957
958/*
959 * 'add_class()' - Add a class to the system.
960 */
961
962static void
963add_class(cupsd_client_t *con, /* I - Client connection */
964 ipp_attribute_t *uri) /* I - URI of class */
965{
966 http_status_t status; /* Policy status */
967 int i; /* Looping var */
61cf44e2 968 char scheme[HTTP_MAX_URI], /* Method portion of URI */
ef416fc2 969 username[HTTP_MAX_URI], /* Username portion of URI */
970 host[HTTP_MAX_URI], /* Host portion of URI */
971 resource[HTTP_MAX_URI]; /* Resource portion of URI */
972 int port; /* Port portion of URI */
973 cupsd_printer_t *pclass, /* Class */
974 *member; /* Member printer/class */
975 cups_ptype_t dtype; /* Destination type */
ef416fc2 976 ipp_attribute_t *attr; /* Printer attribute */
977 int modify; /* Non-zero if we just modified */
b423cd4c 978 char newname[IPP_MAX_NAME]; /* New class name */
e00b005a 979 int need_restart_job; /* Need to restart job? */
ef416fc2 980
981
982 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_class(%p[%d], %s)", con,
983 con->http.fd, uri->values[0].string.text);
984
985 /*
986 * Do we have a valid URI?
987 */
988
61cf44e2
MS
989 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
990 sizeof(scheme), username, sizeof(username), host,
a4d04587 991 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 992
993
fa73b229 994 if (strncmp(resource, "/classes/", 9) || strlen(resource) == 9)
ef416fc2 995 {
996 /*
997 * No, return an error...
998 */
999
1000 send_ipp_status(con, IPP_BAD_REQUEST,
1001 _("The printer-uri must be of the form "
1002 "\"ipp://HOSTNAME/classes/CLASSNAME\"."));
1003 return;
1004 }
1005
1006 /*
1007 * Do we have a valid printer name?
1008 */
1009
1010 if (!validate_name(resource + 9))
1011 {
1012 /*
1013 * No, return an error...
1014 */
1015
1016 send_ipp_status(con, IPP_BAD_REQUEST,
1017 _("The printer-uri \"%s\" contains invalid characters."),
1018 uri->values[0].string.text);
1019 return;
1020 }
1021
ef416fc2 1022 /*
1023 * See if the class already exists; if not, create a new class...
1024 */
1025
1026 if ((pclass = cupsdFindClass(resource + 9)) == NULL)
1027 {
1028 /*
1029 * Class doesn't exist; see if we have a printer of the same name...
1030 */
1031
1032 if ((pclass = cupsdFindPrinter(resource + 9)) != NULL &&
bc44d920 1033 !(pclass->type & CUPS_PRINTER_DISCOVERED))
ef416fc2 1034 {
1035 /*
1036 * Yes, return an error...
1037 */
1038
1039 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 1040 _("A printer named \"%s\" already exists"),
ef416fc2 1041 resource + 9);
1042 return;
1043 }
1044
1045 /*
2e4ff8af 1046 * No, check the default policy and then add the class...
ef416fc2 1047 */
1048
2e4ff8af
MS
1049 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
1050 {
1051 send_http_error(con, status, NULL);
1052 return;
1053 }
1054
ef416fc2 1055 pclass = cupsdAddClass(resource + 9);
1056 modify = 0;
1057 }
1058 else if (pclass->type & CUPS_PRINTER_IMPLICIT)
1059 {
1060 /*
2fb76298 1061 * Check the default policy, then rename the implicit class to "AnyClass"
2e4ff8af 1062 * or remove it...
ef416fc2 1063 */
1064
2e4ff8af
MS
1065 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
1066 {
1067 send_http_error(con, status, NULL);
1068 return;
1069 }
1070
ef416fc2 1071 if (ImplicitAnyClasses)
1072 {
b423cd4c 1073 snprintf(newname, sizeof(newname), "Any%s", resource + 9);
1074 cupsdRenamePrinter(pclass, newname);
ef416fc2 1075 }
1076 else
1077 cupsdDeletePrinter(pclass, 1);
1078
1079 /*
1080 * Add the class as a new local class...
1081 */
1082
1083 pclass = cupsdAddClass(resource + 9);
1084 modify = 0;
1085 }
bc44d920 1086 else if (pclass->type & CUPS_PRINTER_DISCOVERED)
ef416fc2 1087 {
1088 /*
2e4ff8af 1089 * Check the default policy, then rename the remote class to "Class"...
ef416fc2 1090 */
1091
2e4ff8af
MS
1092 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
1093 {
1094 send_http_error(con, status, NULL);
1095 return;
1096 }
1097
b423cd4c 1098 snprintf(newname, sizeof(newname), "%s@%s", resource + 9, pclass->hostname);
1099 cupsdRenamePrinter(pclass, newname);
ef416fc2 1100
1101 /*
1102 * Add the class as a new local class...
1103 */
1104
1105 pclass = cupsdAddClass(resource + 9);
1106 modify = 0;
1107 }
2e4ff8af
MS
1108 else if ((status = cupsdCheckPolicy(pclass->op_policy_ptr, con,
1109 NULL)) != HTTP_OK)
1110 {
2fb76298 1111 send_http_error(con, status, pclass);
2e4ff8af
MS
1112 return;
1113 }
ef416fc2 1114 else
1115 modify = 1;
1116
1117 /*
1118 * Look for attributes and copy them over as needed...
1119 */
1120
e00b005a 1121 need_restart_job = 0;
1122
fa73b229 1123 if ((attr = ippFindAttribute(con->request, "printer-location",
1124 IPP_TAG_TEXT)) != NULL)
ef416fc2 1125 cupsdSetString(&pclass->location, attr->values[0].string.text);
1126
fa73b229 1127 if ((attr = ippFindAttribute(con->request, "printer-info",
1128 IPP_TAG_TEXT)) != NULL)
ef416fc2 1129 cupsdSetString(&pclass->info, attr->values[0].string.text);
1130
fa73b229 1131 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs",
f8b3a85b
MS
1132 IPP_TAG_BOOLEAN)) != NULL &&
1133 attr->values[0].boolean != pclass->accepting)
ef416fc2 1134 {
bc44d920 1135 cupsdLogMessage(CUPSD_LOG_INFO,
1136 "Setting %s printer-is-accepting-jobs to %d (was %d.)",
1137 pclass->name, attr->values[0].boolean, pclass->accepting);
ef416fc2 1138
1139 pclass->accepting = attr->values[0].boolean;
1140 cupsdAddPrinterHistory(pclass);
f8b3a85b
MS
1141
1142 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, pclass, NULL, "%s accepting jobs.",
1143 pclass->accepting ? "Now" : "No longer");
ef416fc2 1144 }
1145
fa73b229 1146 if ((attr = ippFindAttribute(con->request, "printer-is-shared",
1147 IPP_TAG_BOOLEAN)) != NULL)
ef416fc2 1148 {
1149 if (pclass->shared && !attr->values[0].boolean)
f7deaa1a 1150 cupsdDeregisterPrinter(pclass, 1);
ef416fc2 1151
1152 cupsdLogMessage(CUPSD_LOG_INFO,
1153 "Setting %s printer-is-shared to %d (was %d.)",
1154 pclass->name, attr->values[0].boolean, pclass->shared);
1155
1156 pclass->shared = attr->values[0].boolean;
1157 }
1158
fa73b229 1159 if ((attr = ippFindAttribute(con->request, "printer-state",
1160 IPP_TAG_ENUM)) != NULL)
ef416fc2 1161 {
1162 if (attr->values[0].integer != IPP_PRINTER_IDLE &&
1163 attr->values[0].integer != IPP_PRINTER_STOPPED)
1164 {
1165 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 1166 _("Attempt to set %s printer-state to bad value %d"),
ef416fc2 1167 pclass->name, attr->values[0].integer);
1168 return;
1169 }
1170
bc44d920 1171 cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)",
1172 pclass->name, attr->values[0].integer, pclass->state);
ef416fc2 1173
1174 if (attr->values[0].integer == IPP_PRINTER_STOPPED)
1175 cupsdStopPrinter(pclass, 0);
1176 else
e00b005a 1177 {
ef416fc2 1178 cupsdSetPrinterState(pclass, (ipp_pstate_t)(attr->values[0].integer), 0);
e00b005a 1179 need_restart_job = 1;
1180 }
ef416fc2 1181 }
fa73b229 1182 if ((attr = ippFindAttribute(con->request, "printer-state-message",
1183 IPP_TAG_TEXT)) != NULL)
ef416fc2 1184 {
1185 strlcpy(pclass->state_message, attr->values[0].string.text,
1186 sizeof(pclass->state_message));
1187 cupsdAddPrinterHistory(pclass);
f8b3a85b
MS
1188
1189 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, pclass, NULL, "%s",
1190 pclass->state_message);
ef416fc2 1191 }
fa73b229 1192 if ((attr = ippFindAttribute(con->request, "member-uris",
1193 IPP_TAG_URI)) != NULL)
ef416fc2 1194 {
1195 /*
1196 * Clear the printer array as needed...
1197 */
1198
e00b005a 1199 need_restart_job = 1;
1200
ef416fc2 1201 if (pclass->num_printers > 0)
1202 {
1203 free(pclass->printers);
1204 pclass->num_printers = 0;
1205 }
1206
1207 /*
1208 * Add each printer or class that is listed...
1209 */
1210
1211 for (i = 0; i < attr->num_values; i ++)
1212 {
1213 /*
1214 * Search for the printer or class URI...
1215 */
1216
f7deaa1a 1217 if (!cupsdValidateDest(attr->values[i].string.text, &dtype, &member))
ef416fc2 1218 {
1219 /*
1220 * Bad URI...
1221 */
1222
1223 send_ipp_status(con, IPP_NOT_FOUND,
1224 _("The printer or class was not found."));
1225 return;
1226 }
acb056cb
MS
1227 else if (dtype & CUPS_PRINTER_CLASS)
1228 {
1229 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 1230 _("Nested classes are not allowed"));
acb056cb
MS
1231 return;
1232 }
ef416fc2 1233
1234 /*
1235 * Add it to the class...
1236 */
1237
1238 cupsdAddPrinterToClass(pclass, member);
1239 }
1240 }
1241
b423cd4c 1242 set_printer_defaults(con, pclass);
1243
09a101d6 1244 if ((attr = ippFindAttribute(con->request, "auth-info-required",
1245 IPP_TAG_KEYWORD)) != NULL)
1246 cupsdSetAuthInfoRequired(pclass, NULL, attr);
1247
ef416fc2 1248 /*
1249 * Update the printer class attributes and return...
1250 */
1251
1252 cupsdSetPrinterAttrs(pclass);
3dfe78b3 1253 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
e00b005a 1254
1255 if (need_restart_job && pclass->job)
1256 {
e00b005a 1257 /*
b9faaae1 1258 * Reset the current job to a "pending" status...
e00b005a 1259 */
1260
b9faaae1
MS
1261 cupsdSetJobState(pclass->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
1262 "Job restarted because the class was modified.");
e00b005a 1263 }
1264
3dfe78b3 1265 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
ef416fc2 1266
1267 if (modify)
1268 {
c7017ecc 1269 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED,
f8b3a85b
MS
1270 pclass, NULL, "Class \"%s\" modified by \"%s\".",
1271 pclass->name, get_username(con));
ef416fc2 1272
1273 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" modified by \"%s\".",
e00b005a 1274 pclass->name, get_username(con));
ef416fc2 1275 }
1276 else
1277 {
1278 cupsdAddPrinterHistory(pclass);
1279
c7017ecc 1280 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
f8b3a85b
MS
1281 pclass, NULL, "New class \"%s\" added by \"%s\".",
1282 pclass->name, get_username(con));
ef416fc2 1283
1284 cupsdLogMessage(CUPSD_LOG_INFO, "New class \"%s\" added by \"%s\".",
e00b005a 1285 pclass->name, get_username(con));
ef416fc2 1286 }
1287
1288 con->response->request.status.status_code = IPP_OK;
1289}
1290
1291
1292/*
1293 * 'add_file()' - Add a file to a job.
1294 */
1295
1296static int /* O - 0 on success, -1 on error */
1297add_file(cupsd_client_t *con, /* I - Connection to client */
1298 cupsd_job_t *job, /* I - Job to add to */
1299 mime_type_t *filetype, /* I - Type of file */
1300 int compression) /* I - Compression */
1301{
1302 mime_type_t **filetypes; /* New filetypes array... */
1303 int *compressions; /* New compressions array... */
1304
1305
1306 cupsdLogMessage(CUPSD_LOG_DEBUG2,
bc44d920 1307 "add_file(con=%p[%d], job=%d, filetype=%s/%s, "
1308 "compression=%d)", con, con ? con->http.fd : -1, job->id,
1309 filetype->super, filetype->type, compression);
ef416fc2 1310
1311 /*
1312 * Add the file to the job...
1313 */
1314
1315 if (job->num_files == 0)
1316 {
1317 compressions = (int *)malloc(sizeof(int));
1318 filetypes = (mime_type_t **)malloc(sizeof(mime_type_t *));
1319 }
1320 else
1321 {
1322 compressions = (int *)realloc(job->compressions,
1323 (job->num_files + 1) * sizeof(int));
1324 filetypes = (mime_type_t **)realloc(job->filetypes,
1325 (job->num_files + 1) *
1326 sizeof(mime_type_t *));
1327 }
1328
fa73b229 1329 if (!compressions || !filetypes)
ef416fc2 1330 {
b9faaae1
MS
1331 cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
1332 "Job aborted because the scheduler ran out of memory.");
ef416fc2 1333
09a101d6 1334 if (con)
1335 send_ipp_status(con, IPP_INTERNAL_ERROR,
4d301e69 1336 _("Unable to allocate memory for file types"));
09a101d6 1337
ef416fc2 1338 return (-1);
1339 }
1340
1341 job->compressions = compressions;
1342 job->compressions[job->num_files] = compression;
1343 job->filetypes = filetypes;
1344 job->filetypes[job->num_files] = filetype;
1345
1346 job->num_files ++;
1347
3dfe78b3
MS
1348 job->dirty = 1;
1349 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
1350
ef416fc2 1351 return (0);
1352}
1353
1354
1355/*
b423cd4c 1356 * 'add_job()' - Add a job to a print queue.
ef416fc2 1357 */
1358
b423cd4c 1359static cupsd_job_t * /* O - Job object */
1360add_job(cupsd_client_t *con, /* I - Client connection */
f7deaa1a 1361 cupsd_printer_t *printer, /* I - Destination printer */
80ca4592 1362 mime_type_t *filetype) /* I - First print file type, if any */
ef416fc2 1363{
b423cd4c 1364 http_status_t status; /* Policy status */
f7deaa1a 1365 ipp_attribute_t *attr, /* Current attribute */
1366 *auth_info; /* auth-info attribute */
b423cd4c 1367 const char *val; /* Default option value */
1368 int priority; /* Job priority */
b423cd4c 1369 cupsd_job_t *job; /* Current job */
f7deaa1a 1370 char job_uri[HTTP_MAX_URI]; /* Job URI */
b423cd4c 1371 int kbytes; /* Size of print file */
1372 int i; /* Looping var */
1373 int lowerpagerange; /* Page range bound */
54afec33
MS
1374 int exact; /* Did we have an exact match? */
1375 ipp_attribute_t *media_col, /* media-col attribute */
1376 *media_margin; /* media-*-margin attribute */
1377 ipp_t *unsup_col; /* media-col in unsupported response */
ef416fc2 1378
1379
f7deaa1a 1380 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %p(%s), %p(%s/%s))",
1381 con, con->http.fd, printer, printer->name,
0a682745
MS
1382 filetype, filetype ? filetype->super : "none",
1383 filetype ? filetype->type : "none");
ef416fc2 1384
b423cd4c 1385 /*
1386 * Check remote printing to non-shared printer...
1387 */
ef416fc2 1388
b423cd4c 1389 if (!printer->shared &&
1390 strcasecmp(con->http.hostname, "localhost") &&
1391 strcasecmp(con->http.hostname, ServerName))
1392 {
1393 send_ipp_status(con, IPP_NOT_AUTHORIZED,
4d301e69 1394 _("The printer or class is not shared"));
b423cd4c 1395 return (NULL);
1396 }
ef416fc2 1397
b423cd4c 1398 /*
1399 * Check policy...
1400 */
ef416fc2 1401
f899b121 1402 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
7594b224 1403
b423cd4c 1404 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
1405 {
f899b121 1406 send_http_error(con, status, printer);
b423cd4c 1407 return (NULL);
1408 }
f11a948a
MS
1409 else if (printer->num_auth_info_required == 1 &&
1410 !strcmp(printer->auth_info_required[0], "negotiate") &&
1411 !con->username[0])
b423cd4c 1412 {
f899b121 1413 send_http_error(con, HTTP_UNAUTHORIZED, printer);
b423cd4c 1414 return (NULL);
1415 }
7594b224 1416#ifdef HAVE_SSL
1417 else if (auth_info && !con->http.tls &&
1418 !httpAddrLocalhost(con->http.hostaddr))
1419 {
1420 /*
1421 * Require encryption of auth-info over non-local connections...
1422 */
1423
f899b121 1424 send_http_error(con, HTTP_UPGRADE_REQUIRED, printer);
7594b224 1425 return (NULL);
1426 }
1427#endif /* HAVE_SSL */
ef416fc2 1428
b423cd4c 1429 /*
1430 * See if the printer is accepting jobs...
1431 */
1432
1433 if (!printer->accepting)
1434 {
1435 send_ipp_status(con, IPP_NOT_ACCEPTING,
1436 _("Destination \"%s\" is not accepting jobs."),
f7deaa1a 1437 printer->name);
b423cd4c 1438 return (NULL);
ef416fc2 1439 }
ef416fc2 1440
b423cd4c 1441 /*
80ca4592 1442 * Validate job template attributes; for now just document-format,
f7deaa1a 1443 * copies, number-up, and page-ranges...
b423cd4c 1444 */
ef416fc2 1445
80ca4592 1446 if (filetype && printer->filetypes &&
1447 !cupsArrayFind(printer->filetypes, filetype))
1448 {
1449 char mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
1450 /* MIME media type string */
1451
1452
1453 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
1454 filetype->type);
1455
1456 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
4d301e69 1457 _("Unsupported format \'%s\'"), mimetype);
80ca4592 1458
1459 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
1460 "document-format", NULL, mimetype);
1461
1462 return (NULL);
1463 }
1464
b423cd4c 1465 if ((attr = ippFindAttribute(con->request, "copies",
1466 IPP_TAG_INTEGER)) != NULL)
1467 {
1468 if (attr->values[0].integer < 1 || attr->values[0].integer > MaxCopies)
1469 {
1470 send_ipp_status(con, IPP_ATTRIBUTES, _("Bad copies value %d."),
1471 attr->values[0].integer);
1472 ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER,
1473 "copies", attr->values[0].integer);
1474 return (NULL);
1475 }
1476 }
ef416fc2 1477
3dfe78b3
MS
1478 if ((attr = ippFindAttribute(con->request, "job-sheets",
1479 IPP_TAG_ZERO)) != NULL)
1480 {
1481 if (attr->value_tag != IPP_TAG_KEYWORD &&
1482 attr->value_tag != IPP_TAG_NAME)
1483 {
4d301e69 1484 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value type"));
3dfe78b3
MS
1485 return (NULL);
1486 }
1487
1488 if (attr->num_values > 2)
1489 {
1490 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 1491 _("Too many job-sheets values (%d > 2)"),
3dfe78b3
MS
1492 attr->num_values);
1493 return (NULL);
1494 }
1495
1496 for (i = 0; i < attr->num_values; i ++)
1497 if (strcmp(attr->values[i].string.text, "none") &&
1498 !cupsdFindBanner(attr->values[i].string.text))
1499 {
4d301e69 1500 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value \"%s\""),
3dfe78b3
MS
1501 attr->values[i].string.text);
1502 return (NULL);
1503 }
1504 }
1505
f7deaa1a 1506 if ((attr = ippFindAttribute(con->request, "number-up",
1507 IPP_TAG_INTEGER)) != NULL)
1508 {
1509 if (attr->values[0].integer != 1 &&
1510 attr->values[0].integer != 2 &&
1511 attr->values[0].integer != 4 &&
1512 attr->values[0].integer != 6 &&
1513 attr->values[0].integer != 9 &&
1514 attr->values[0].integer != 16)
1515 {
1516 send_ipp_status(con, IPP_ATTRIBUTES, _("Bad number-up value %d."),
1517 attr->values[0].integer);
1518 ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER,
1519 "number-up", attr->values[0].integer);
1520 return (NULL);
1521 }
1522 }
1523
b423cd4c 1524 if ((attr = ippFindAttribute(con->request, "page-ranges",
1525 IPP_TAG_RANGE)) != NULL)
1526 {
1527 for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
1528 {
f7deaa1a 1529 if (attr->values[i].range.lower < lowerpagerange ||
b423cd4c 1530 attr->values[i].range.lower > attr->values[i].range.upper)
1531 {
1532 send_ipp_status(con, IPP_BAD_REQUEST,
1533 _("Bad page-ranges values %d-%d."),
1534 attr->values[i].range.lower,
1535 attr->values[i].range.upper);
1536 return (NULL);
1537 }
ef416fc2 1538
b423cd4c 1539 lowerpagerange = attr->values[i].range.upper + 1;
1540 }
1541 }
ef416fc2 1542
54afec33
MS
1543 /*
1544 * Do media selection as needed...
1545 */
1546
c7017ecc
MS
1547 if (!ippFindAttribute(con->request, "PageRegion", IPP_TAG_ZERO) &&
1548 !ippFindAttribute(con->request, "PageSize", IPP_TAG_ZERO) &&
4220952d 1549 _pwgGetPageSize(printer->pwg, con->request, NULL, &exact))
54afec33 1550 {
54afec33
MS
1551 if (!exact &&
1552 (media_col = ippFindAttribute(con->request, "media-col",
1553 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1554 {
1555 send_ipp_status(con, IPP_OK_SUBST, _("Unsupported margins."));
1556
1557 unsup_col = ippNew();
54afec33
MS
1558 if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1559 "media-bottom-margin",
1560 IPP_TAG_INTEGER)) != NULL)
1561 ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1562 "media-bottom-margin", media_margin->values[0].integer);
1563
1564 if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1565 "media-left-margin",
1566 IPP_TAG_INTEGER)) != NULL)
1567 ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1568 "media-left-margin", media_margin->values[0].integer);
1569
1570 if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1571 "media-right-margin",
1572 IPP_TAG_INTEGER)) != NULL)
1573 ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1574 "media-right-margin", media_margin->values[0].integer);
1575
1576 if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1577 "media-top-margin",
1578 IPP_TAG_INTEGER)) != NULL)
1579 ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1580 "media-top-margin", media_margin->values[0].integer);
aaf19ab0
MS
1581
1582 ippAddCollection(con->response, IPP_TAG_UNSUPPORTED_GROUP, "media-col",
1583 unsup_col);
1584 ippDelete(unsup_col);
54afec33
MS
1585 }
1586 }
1587
ef416fc2 1588 /*
b423cd4c 1589 * Make sure we aren't over our limit...
ef416fc2 1590 */
1591
b423cd4c 1592 if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs)
1593 cupsdCleanJobs();
ef416fc2 1594
f7deaa1a 1595 if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs)
b423cd4c 1596 {
1597 send_ipp_status(con, IPP_NOT_POSSIBLE,
1598 _("Too many active jobs."));
1599 return (NULL);
1600 }
1601
bc44d920 1602 if ((i = check_quotas(con, printer)) < 0)
b423cd4c 1603 {
1604 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
1605 return (NULL);
1606 }
bc44d920 1607 else if (i == 0)
1608 {
1609 send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Not allowed to print."));
1610 return (NULL);
1611 }
ef416fc2 1612
1613 /*
b423cd4c 1614 * Create the job and set things up...
ef416fc2 1615 */
1616
b423cd4c 1617 if ((attr = ippFindAttribute(con->request, "job-priority",
1618 IPP_TAG_INTEGER)) != NULL)
1619 priority = attr->values[0].integer;
1620 else
ef416fc2 1621 {
b423cd4c 1622 if ((val = cupsGetOption("job-priority", printer->num_options,
1623 printer->options)) != NULL)
1624 priority = atoi(val);
1625 else
1626 priority = 50;
ef416fc2 1627
b423cd4c 1628 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
1629 priority);
1630 }
ef416fc2 1631
41681883 1632 if (!ippFindAttribute(con->request, "job-name", IPP_TAG_NAME))
b423cd4c 1633 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
41681883 1634 "Untitled");
ef416fc2 1635
b423cd4c 1636 if ((job = cupsdAddJob(priority, printer->name)) == NULL)
1637 {
1638 send_ipp_status(con, IPP_INTERNAL_ERROR,
4d301e69 1639 _("Unable to add job for destination \"%s\""),
f7deaa1a 1640 printer->name);
b423cd4c 1641 return (NULL);
1642 }
ef416fc2 1643
f7deaa1a 1644 job->dtype = printer->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT |
1645 CUPS_PRINTER_REMOTE);
b423cd4c 1646 job->attrs = con->request;
3dfe78b3 1647 job->dirty = 1;
2abf387c 1648 con->request = ippNewRequest(job->attrs->request.op.operation_id);
ef416fc2 1649
3dfe78b3
MS
1650 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
1651
b423cd4c 1652 add_job_uuid(con, job);
1653 apply_printer_defaults(printer, job);
ef416fc2 1654
b423cd4c 1655 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
ef416fc2 1656
b423cd4c 1657 if (con->username[0])
1658 {
1659 cupsdSetString(&job->username, con->username);
ef416fc2 1660
1661 if (attr)
b423cd4c 1662 cupsdSetString(&attr->values[0].string.text, con->username);
ef416fc2 1663 }
b423cd4c 1664 else if (attr)
1665 {
1666 cupsdLogMessage(CUPSD_LOG_DEBUG,
1667 "add_job: requesting-user-name=\"%s\"",
1668 attr->values[0].string.text);
ef416fc2 1669
b423cd4c 1670 cupsdSetString(&job->username, attr->values[0].string.text);
1671 }
1672 else
1673 cupsdSetString(&job->username, "anonymous");
ef416fc2 1674
b423cd4c 1675 if (!attr)
1676 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
1677 "job-originating-user-name", NULL, job->username);
1678 else
1679 {
1680 attr->group_tag = IPP_TAG_JOB;
757d2cad 1681 _cupsStrFree(attr->name);
1682 attr->name = _cupsStrAlloc("job-originating-user-name");
b423cd4c 1683 }
ef416fc2 1684
f7deaa1a 1685 if (con->username[0] || auth_info)
1686 {
1687 save_auth_info(con, job, auth_info);
1688
1689 /*
1690 * Remove the auth-info attribute from the attribute data...
1691 */
1692
1693 if (auth_info)
a4924f6c 1694 ippDeleteAttribute(job->attrs, auth_info);
f7deaa1a 1695 }
1696
b423cd4c 1697 if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
1698 IPP_TAG_ZERO)) != NULL)
ef416fc2 1699 {
b423cd4c 1700 /*
1701 * Request contains a job-originating-host-name attribute; validate it...
1702 */
ef416fc2 1703
b423cd4c 1704 if (attr->value_tag != IPP_TAG_NAME ||
1705 attr->num_values != 1 ||
1706 strcmp(con->http.hostname, "localhost"))
ef416fc2 1707 {
1708 /*
b423cd4c 1709 * Can't override the value if we aren't connected via localhost.
1710 * Also, we can only have 1 value and it must be a name value.
ef416fc2 1711 */
1712
b423cd4c 1713 switch (attr->value_tag)
1714 {
1715 case IPP_TAG_STRING :
1716 case IPP_TAG_TEXTLANG :
1717 case IPP_TAG_NAMELANG :
1718 case IPP_TAG_TEXT :
1719 case IPP_TAG_NAME :
1720 case IPP_TAG_KEYWORD :
1721 case IPP_TAG_URI :
1722 case IPP_TAG_URISCHEME :
1723 case IPP_TAG_CHARSET :
1724 case IPP_TAG_LANGUAGE :
1725 case IPP_TAG_MIMETYPE :
1726 /*
1727 * Free old strings...
1728 */
ef416fc2 1729
b423cd4c 1730 for (i = 0; i < attr->num_values; i ++)
1731 {
757d2cad 1732 _cupsStrFree(attr->values[i].string.text);
b423cd4c 1733 attr->values[i].string.text = NULL;
1734 if (attr->values[i].string.charset)
1735 {
757d2cad 1736 _cupsStrFree(attr->values[i].string.charset);
b423cd4c 1737 attr->values[i].string.charset = NULL;
1738 }
1739 }
ef416fc2 1740
b423cd4c 1741 default :
1742 break;
1743 }
ef416fc2 1744
b423cd4c 1745 /*
1746 * Use the default connection hostname instead...
1747 */
bd7854cb 1748
b423cd4c 1749 attr->value_tag = IPP_TAG_NAME;
1750 attr->num_values = 1;
757d2cad 1751 attr->values[0].string.text = _cupsStrAlloc(con->http.hostname);
b423cd4c 1752 }
bd7854cb 1753
b423cd4c 1754 attr->group_tag = IPP_TAG_JOB;
1755 }
1756 else
1757 {
1758 /*
1759 * No job-originating-host-name attribute, so use the hostname from
1760 * the connection...
1761 */
bd7854cb 1762
f7deaa1a 1763 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
b423cd4c 1764 "job-originating-host-name", NULL, con->http.hostname);
1765 }
bd7854cb 1766
b423cd4c 1767 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
1768 time(NULL));
1769 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1770 "time-at-processing", 0);
1771 attr->value_tag = IPP_TAG_NOVALUE;
1772 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1773 "time-at-completed", 0);
1774 attr->value_tag = IPP_TAG_NOVALUE;
bd7854cb 1775
1776 /*
b423cd4c 1777 * Add remaining job attributes...
bd7854cb 1778 */
1779
b423cd4c 1780 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1781 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
1782 "job-state", IPP_JOB_STOPPED);
d09495fa 1783 job->state_value = (ipp_jstate_t)job->state->values[0].integer;
b423cd4c 1784 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1785 "job-media-sheets-completed", 0);
1786 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
1787 printer->uri);
bd7854cb 1788
b423cd4c 1789 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
1790 IPP_TAG_INTEGER)) != NULL)
1791 attr->values[0].integer = 0;
1792 else
1f0275e3 1793 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-k-octets", 0);
bd7854cb 1794
b423cd4c 1795 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
1796 IPP_TAG_KEYWORD)) == NULL)
1797 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
1798 if (!attr)
ef416fc2 1799 {
b423cd4c 1800 if ((val = cupsGetOption("job-hold-until", printer->num_options,
1801 printer->options)) == NULL)
1802 val = "no-hold";
ef416fc2 1803
b423cd4c 1804 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1805 "job-hold-until", NULL, val);
ef416fc2 1806 }
4a4b4f99 1807 if (attr && strcmp(attr->values[0].string.text, "no-hold"))
ef416fc2 1808 {
1809 /*
b423cd4c 1810 * Hold job until specified time...
ef416fc2 1811 */
1812
b9faaae1 1813 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
e53920b9 1814
1815 job->state->values[0].integer = IPP_JOB_HELD;
1816 job->state_value = IPP_JOB_HELD;
ef416fc2 1817 }
b423cd4c 1818 else if (job->attrs->request.op.operation_id == IPP_CREATE_JOB)
ef416fc2 1819 {
dfd5680b 1820 job->hold_until = time(NULL) + MultipleOperationTimeout;
b423cd4c 1821 job->state->values[0].integer = IPP_JOB_HELD;
1822 job->state_value = IPP_JOB_HELD;
1823 }
1824 else
1825 {
1826 job->state->values[0].integer = IPP_JOB_PENDING;
1827 job->state_value = IPP_JOB_PENDING;
ef416fc2 1828 }
1829
b423cd4c 1830 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
1831 Classification)
ef416fc2 1832 {
1833 /*
b423cd4c 1834 * Add job sheets options...
ef416fc2 1835 */
1836
b423cd4c 1837 if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1838 IPP_TAG_ZERO)) == NULL)
ef416fc2 1839 {
b423cd4c 1840 cupsdLogMessage(CUPSD_LOG_DEBUG,
1841 "Adding default job-sheets values \"%s,%s\"...",
1842 printer->job_sheets[0], printer->job_sheets[1]);
ef416fc2 1843
b423cd4c 1844 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
1845 2, NULL, NULL);
426c6a59
MS
1846 attr->values[0].string.text = _cupsStrRetain(printer->job_sheets[0]);
1847 attr->values[1].string.text = _cupsStrRetain(printer->job_sheets[1]);
ef416fc2 1848 }
1849
b423cd4c 1850 job->job_sheets = attr;
ef416fc2 1851
ef416fc2 1852 /*
b423cd4c 1853 * Enforce classification level if set...
ef416fc2 1854 */
1855
b423cd4c 1856 if (Classification)
ef416fc2 1857 {
b423cd4c 1858 cupsdLogMessage(CUPSD_LOG_INFO,
1859 "Classification=\"%s\", ClassifyOverride=%d",
1860 Classification ? Classification : "(null)",
1861 ClassifyOverride);
ef416fc2 1862
b423cd4c 1863 if (ClassifyOverride)
1864 {
1865 if (!strcmp(attr->values[0].string.text, "none") &&
1866 (attr->num_values == 1 ||
1867 !strcmp(attr->values[1].string.text, "none")))
1868 {
1869 /*
1870 * Force the leading banner to have the classification on it...
1871 */
ef416fc2 1872
b423cd4c 1873 cupsdSetString(&attr->values[0].string.text, Classification);
e00b005a 1874
75bd9771
MS
1875 cupsdLogJob(job, CUPSD_LOG_NOTICE, "CLASSIFICATION FORCED "
1876 "job-sheets=\"%s,none\", "
1877 "job-originating-user-name=\"%s\"",
1878 Classification, job->username);
b423cd4c 1879 }
1880 else if (attr->num_values == 2 &&
1881 strcmp(attr->values[0].string.text,
1882 attr->values[1].string.text) &&
1883 strcmp(attr->values[0].string.text, "none") &&
1884 strcmp(attr->values[1].string.text, "none"))
1885 {
1886 /*
1887 * Can't put two different security markings on the same document!
1888 */
ef416fc2 1889
b423cd4c 1890 cupsdSetString(&attr->values[1].string.text, attr->values[0].string.text);
ef416fc2 1891
75bd9771
MS
1892 cupsdLogJob(job, CUPSD_LOG_NOTICE, "CLASSIFICATION FORCED "
1893 "job-sheets=\"%s,%s\", "
1894 "job-originating-user-name=\"%s\"",
1895 attr->values[0].string.text,
1896 attr->values[1].string.text, job->username);
b423cd4c 1897 }
1898 else if (strcmp(attr->values[0].string.text, Classification) &&
1899 strcmp(attr->values[0].string.text, "none") &&
1900 (attr->num_values == 1 ||
1901 (strcmp(attr->values[1].string.text, Classification) &&
1902 strcmp(attr->values[1].string.text, "none"))))
1903 {
1904 if (attr->num_values == 1)
75bd9771
MS
1905 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1906 "CLASSIFICATION OVERRIDDEN "
1907 "job-sheets=\"%s\", "
1908 "job-originating-user-name=\"%s\"",
1909 attr->values[0].string.text, job->username);
b423cd4c 1910 else
75bd9771
MS
1911 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1912 "CLASSIFICATION OVERRIDDEN "
1913 "job-sheets=\"%s,%s\",fffff "
1914 "job-originating-user-name=\"%s\"",
1915 attr->values[0].string.text,
1916 attr->values[1].string.text, job->username);
b423cd4c 1917 }
1918 }
1919 else if (strcmp(attr->values[0].string.text, Classification) &&
1920 (attr->num_values == 1 ||
1921 strcmp(attr->values[1].string.text, Classification)))
ef416fc2 1922 {
1923 /*
b423cd4c 1924 * Force the banner to have the classification on it...
ef416fc2 1925 */
1926
b423cd4c 1927 if (attr->num_values > 1 &&
1928 !strcmp(attr->values[0].string.text, attr->values[1].string.text))
1929 {
1930 cupsdSetString(&(attr->values[0].string.text), Classification);
1931 cupsdSetString(&(attr->values[1].string.text), Classification);
1932 }
1933 else
1934 {
1935 if (attr->num_values == 1 ||
1936 strcmp(attr->values[0].string.text, "none"))
1937 cupsdSetString(&(attr->values[0].string.text), Classification);
ef416fc2 1938
b423cd4c 1939 if (attr->num_values > 1 &&
1940 strcmp(attr->values[1].string.text, "none"))
1941 cupsdSetString(&(attr->values[1].string.text), Classification);
1942 }
ef416fc2 1943
b423cd4c 1944 if (attr->num_values > 1)
75bd9771
MS
1945 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1946 "CLASSIFICATION FORCED "
1947 "job-sheets=\"%s,%s\", "
1948 "job-originating-user-name=\"%s\"",
1949 attr->values[0].string.text,
1950 attr->values[1].string.text, job->username);
b423cd4c 1951 else
75bd9771
MS
1952 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1953 "CLASSIFICATION FORCED "
1954 "job-sheets=\"%s\", "
1955 "job-originating-user-name=\"%s\"",
1956 Classification, job->username);
ef416fc2 1957 }
1958 }
1959
b423cd4c 1960 /*
1961 * See if we need to add the starting sheet...
1962 */
ef416fc2 1963
b423cd4c 1964 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
ef416fc2 1965 {
75bd9771
MS
1966 cupsdLogJob(job, CUPSD_LOG_INFO, "Adding start banner page \"%s\".",
1967 attr->values[0].string.text);
ef416fc2 1968
91c84a35 1969 if ((kbytes = copy_banner(con, job, attr->values[0].string.text)) < 0)
3dfe78b3 1970 {
b9faaae1
MS
1971 cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
1972 "Aborting job because the start banner could not be "
1973 "copied.");
91c84a35 1974 return (NULL);
3dfe78b3 1975 }
ef416fc2 1976
b423cd4c 1977 cupsdUpdateQuota(printer, job->username, 0, kbytes);
1978 }
ef416fc2 1979 }
b423cd4c 1980 else if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1981 IPP_TAG_ZERO)) != NULL)
1982 job->sheets = attr;
ef416fc2 1983
b423cd4c 1984 /*
1985 * Fill in the response info...
1986 */
ef416fc2 1987
b423cd4c 1988 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
1989 LocalPort, job->id);
ef416fc2 1990
b423cd4c 1991 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
1992 job_uri);
ef416fc2 1993
b423cd4c 1994 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
ef416fc2 1995
b423cd4c 1996 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
1997 job->state_value);
1998 add_job_state_reasons(con, job);
ef416fc2 1999
b423cd4c 2000 con->response->request.status.status_code = IPP_OK;
ef416fc2 2001
b423cd4c 2002 /*
2003 * Add any job subscriptions...
2004 */
ef416fc2 2005
b423cd4c 2006 add_job_subscriptions(con, job);
ef416fc2 2007
b423cd4c 2008 /*
2009 * Set all but the first two attributes to the job attributes group...
2010 */
ef416fc2 2011
b423cd4c 2012 for (attr = job->attrs->attrs->next->next; attr; attr = attr->next)
2013 attr->group_tag = IPP_TAG_JOB;
ef416fc2 2014
2015 /*
b423cd4c 2016 * Fire the "job created" event...
ef416fc2 2017 */
2018
b423cd4c 2019 cupsdAddEvent(CUPSD_EVENT_JOB_CREATED, printer, job, "Job created.");
ef416fc2 2020
2021 /*
b423cd4c 2022 * Return the new job...
ef416fc2 2023 */
2024
b423cd4c 2025 return (job);
2026}
ef416fc2 2027
ef416fc2 2028
b423cd4c 2029/*
2030 * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
2031 * upon the job and printer state...
2032 */
ef416fc2 2033
b423cd4c 2034static void
2035add_job_state_reasons(
2036 cupsd_client_t *con, /* I - Client connection */
2037 cupsd_job_t *job) /* I - Job info */
2038{
2039 cupsd_printer_t *dest; /* Destination printer */
ef416fc2 2040
ef416fc2 2041
b423cd4c 2042 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job_state_reasons(%p[%d], %d)",
2043 con, con->http.fd, job ? job->id : 0);
ef416fc2 2044
d09495fa 2045 switch (job ? job->state_value : IPP_JOB_CANCELED)
ef416fc2 2046 {
b423cd4c 2047 case IPP_JOB_PENDING :
80ca4592 2048 dest = cupsdFindDest(job->dest);
bd7854cb 2049
b423cd4c 2050 if (dest && dest->state == IPP_PRINTER_STOPPED)
2051 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2052 "job-state-reasons", NULL, "printer-stopped");
2053 else
2054 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2055 "job-state-reasons", NULL, "none");
2056 break;
ef416fc2 2057
b423cd4c 2058 case IPP_JOB_HELD :
2059 if (ippFindAttribute(job->attrs, "job-hold-until",
2060 IPP_TAG_KEYWORD) != NULL ||
2061 ippFindAttribute(job->attrs, "job-hold-until",
2062 IPP_TAG_NAME) != NULL)
2063 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2064 "job-state-reasons", NULL, "job-hold-until-specified");
2065 else
2066 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2067 "job-state-reasons", NULL, "job-incoming");
2068 break;
ef416fc2 2069
b423cd4c 2070 case IPP_JOB_PROCESSING :
2071 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2072 "job-state-reasons", NULL, "job-printing");
2073 break;
ef416fc2 2074
b423cd4c 2075 case IPP_JOB_STOPPED :
2076 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2077 "job-state-reasons", NULL, "job-stopped");
2078 break;
ef416fc2 2079
d09495fa 2080 case IPP_JOB_CANCELED :
b423cd4c 2081 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2082 "job-state-reasons", NULL, "job-canceled-by-user");
2083 break;
ef416fc2 2084
b423cd4c 2085 case IPP_JOB_ABORTED :
2086 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2087 "job-state-reasons", NULL, "aborted-by-system");
2088 break;
ef416fc2 2089
b423cd4c 2090 case IPP_JOB_COMPLETED :
2091 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2092 "job-state-reasons", NULL, "job-completed-successfully");
2093 break;
ef416fc2 2094 }
ef416fc2 2095}
2096
2097
2098/*
9a4f8274 2099 * 'add_job_subscriptions()' - Add any subscriptions for a job.
ef416fc2 2100 */
2101
2102static void
b423cd4c 2103add_job_subscriptions(
2104 cupsd_client_t *con, /* I - Client connection */
2105 cupsd_job_t *job) /* I - Newly created job */
ef416fc2 2106{
b423cd4c 2107 int i; /* Looping var */
2108 ipp_attribute_t *prev, /* Previous attribute */
2109 *next, /* Next attribute */
2110 *attr; /* Current attribute */
2111 cupsd_subscription_t *sub; /* Subscription object */
2112 const char *recipient, /* notify-recipient-uri */
2113 *pullmethod; /* notify-pull-method */
2114 ipp_attribute_t *user_data; /* notify-user-data */
2115 int interval; /* notify-time-interval */
2116 unsigned mask; /* notify-events */
ef416fc2 2117
ef416fc2 2118
b423cd4c 2119 /*
2120 * Find the first subscription group attribute; return if we have
2121 * none...
2122 */
ef416fc2 2123
1f0275e3 2124 for (attr = job->attrs->attrs; attr; attr = attr->next)
b423cd4c 2125 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
2126 break;
ef416fc2 2127
b423cd4c 2128 if (!attr)
2129 return;
ef416fc2 2130
b423cd4c 2131 /*
2132 * Process the subscription attributes in the request...
2133 */
ef416fc2 2134
b423cd4c 2135 while (attr)
2136 {
2137 recipient = NULL;
2138 pullmethod = NULL;
2139 user_data = NULL;
2140 interval = 0;
2141 mask = CUPSD_EVENT_NONE;
ef416fc2 2142
b423cd4c 2143 while (attr && attr->group_tag != IPP_TAG_ZERO)
2144 {
f899b121 2145 if (!strcmp(attr->name, "notify-recipient-uri") &&
b423cd4c 2146 attr->value_tag == IPP_TAG_URI)
3dfe78b3
MS
2147 {
2148 /*
2149 * Validate the recipient scheme against the ServerBin/notifier
2150 * directory...
2151 */
2152
2153 char notifier[1024], /* Notifier filename */
2154 scheme[HTTP_MAX_URI], /* Scheme portion of URI */
2155 userpass[HTTP_MAX_URI], /* Username portion of URI */
2156 host[HTTP_MAX_URI], /* Host portion of URI */
2157 resource[HTTP_MAX_URI]; /* Resource portion of URI */
2158 int port; /* Port portion of URI */
2159
2160
b423cd4c 2161 recipient = attr->values[0].string.text;
3dfe78b3
MS
2162
2163 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
2164 scheme, sizeof(scheme), userpass, sizeof(userpass),
2165 host, sizeof(host), &port,
2166 resource, sizeof(resource)) < HTTP_URI_OK)
2167 {
2168 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 2169 _("Bad notify-recipient-uri URI \"%s\""), recipient);
3dfe78b3
MS
2170 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2171 "notify-status-code", IPP_URI_SCHEME);
2172 return;
2173 }
2174
2175 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
2176 scheme);
2177 if (access(notifier, X_OK))
2178 {
2179 send_ipp_status(con, IPP_NOT_POSSIBLE,
2180 _("notify-recipient-uri URI \"%s\" uses unknown "
4d301e69 2181 "scheme"), recipient);
3dfe78b3
MS
2182 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2183 "notify-status-code", IPP_URI_SCHEME);
2184 return;
2185 }
2186
2187 if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
2188 {
2189 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 2190 _("notify-recipient-uri URI \"%s\" is already used"),
3dfe78b3
MS
2191 recipient);
2192 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2193 "notify-status-code", IPP_ATTRIBUTES);
2194 return;
2195 }
2196 }
b423cd4c 2197 else if (!strcmp(attr->name, "notify-pull-method") &&
2198 attr->value_tag == IPP_TAG_KEYWORD)
3dfe78b3 2199 {
b423cd4c 2200 pullmethod = attr->values[0].string.text;
3dfe78b3
MS
2201
2202 if (strcmp(pullmethod, "ippget"))
2203 {
2204 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 2205 _("Bad notify-pull-method \"%s\""), pullmethod);
3dfe78b3
MS
2206 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2207 "notify-status-code", IPP_ATTRIBUTES);
2208 return;
2209 }
2210 }
b423cd4c 2211 else if (!strcmp(attr->name, "notify-charset") &&
2212 attr->value_tag == IPP_TAG_CHARSET &&
2213 strcmp(attr->values[0].string.text, "us-ascii") &&
2214 strcmp(attr->values[0].string.text, "utf-8"))
2215 {
2216 send_ipp_status(con, IPP_CHARSET,
4d301e69 2217 _("Character set \"%s\" not supported"),
b423cd4c 2218 attr->values[0].string.text);
2219 return;
2220 }
2221 else if (!strcmp(attr->name, "notify-natural-language") &&
2222 (attr->value_tag != IPP_TAG_LANGUAGE ||
2223 strcmp(attr->values[0].string.text, DefaultLanguage)))
2224 {
2225 send_ipp_status(con, IPP_CHARSET,
4d301e69 2226 _("Language \"%s\" not supported"),
b423cd4c 2227 attr->values[0].string.text);
2228 return;
2229 }
2230 else if (!strcmp(attr->name, "notify-user-data") &&
2231 attr->value_tag == IPP_TAG_STRING)
2232 {
2233 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
2234 {
2235 send_ipp_status(con, IPP_REQUEST_VALUE,
2236 _("The notify-user-data value is too large "
4d301e69 2237 "(%d > 63 octets)"),
b423cd4c 2238 attr->values[0].unknown.length);
2239 return;
2240 }
ef416fc2 2241
b423cd4c 2242 user_data = attr;
2243 }
2244 else if (!strcmp(attr->name, "notify-events") &&
2245 attr->value_tag == IPP_TAG_KEYWORD)
2246 {
2247 for (i = 0; i < attr->num_values; i ++)
2248 mask |= cupsdEventValue(attr->values[i].string.text);
2249 }
2250 else if (!strcmp(attr->name, "notify-lease-duration"))
2251 {
2252 send_ipp_status(con, IPP_BAD_REQUEST,
2253 _("The notify-lease-duration attribute cannot be "
2254 "used with job subscriptions."));
2255 return;
2256 }
2257 else if (!strcmp(attr->name, "notify-time-interval") &&
2258 attr->value_tag == IPP_TAG_INTEGER)
2259 interval = attr->values[0].integer;
ef416fc2 2260
b423cd4c 2261 attr = attr->next;
2262 }
ef416fc2 2263
b423cd4c 2264 if (!recipient && !pullmethod)
2265 break;
ef416fc2 2266
b423cd4c 2267 if (mask == CUPSD_EVENT_NONE)
2268 mask = CUPSD_EVENT_JOB_COMPLETED;
ef416fc2 2269
52f6f666
MS
2270 if ((sub = cupsdAddSubscription(mask, cupsdFindDest(job->dest), job,
2271 recipient, 0)) != NULL)
2272 {
2273 sub->interval = interval;
ef416fc2 2274
52f6f666 2275 cupsdSetString(&sub->owner, job->username);
ef416fc2 2276
52f6f666
MS
2277 if (user_data)
2278 {
2279 sub->user_data_len = user_data->values[0].unknown.length;
2280 memcpy(sub->user_data, user_data->values[0].unknown.data,
2281 sub->user_data_len);
2282 }
ef416fc2 2283
52f6f666
MS
2284 ippAddSeparator(con->response);
2285 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
2286 "notify-subscription-id", sub->id);
f8b3a85b
MS
2287
2288 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d",
2289 sub->id, job->id);
ef416fc2 2290 }
2291
b423cd4c 2292 if (attr)
2293 attr = attr->next;
ef416fc2 2294 }
2295
3dfe78b3 2296 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
b423cd4c 2297
ef416fc2 2298 /*
b423cd4c 2299 * Remove all of the subscription attributes from the job request...
1f0275e3
MS
2300 *
2301 * TODO: Optimize this since subscription groups have to come at the
2302 * end of the request...
ef416fc2 2303 */
2304
b423cd4c 2305 for (attr = job->attrs->attrs, prev = NULL; attr; attr = next)
ef416fc2 2306 {
b423cd4c 2307 next = attr->next;
ef416fc2 2308
b423cd4c 2309 if (attr->group_tag == IPP_TAG_SUBSCRIPTION ||
2310 attr->group_tag == IPP_TAG_ZERO)
2311 {
2312 /*
2313 * Free and remove this attribute...
2314 */
ef416fc2 2315
757d2cad 2316 _ippFreeAttr(attr);
ef416fc2 2317
b423cd4c 2318 if (prev)
2319 prev->next = next;
2320 else
2321 job->attrs->attrs = next;
2322 }
2323 else
2324 prev = attr;
ef416fc2 2325 }
2326
b423cd4c 2327 job->attrs->last = prev;
2328 job->attrs->current = prev;
2329}
ef416fc2 2330
ef416fc2 2331
b423cd4c 2332/*
2333 * 'add_job_uuid()' - Add job-uuid attribute to a job.
2334 *
2335 * See RFC 4122 for the definition of UUIDs and the format.
2336 */
2337
2338static void
2339add_job_uuid(cupsd_client_t *con, /* I - Client connection */
2340 cupsd_job_t *job) /* I - Job */
2341{
2342 char uuid[1024]; /* job-uuid string */
b423cd4c 2343 _cups_md5_state_t md5state; /* MD5 state */
2344 unsigned char md5sum[16]; /* MD5 digest/sum */
ef416fc2 2345
ef416fc2 2346
2347 /*
b423cd4c 2348 * First see if the job already has a job-uuid attribute; if so, return...
ef416fc2 2349 */
2350
d09495fa 2351 if (ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI))
b423cd4c 2352 return;
ef416fc2 2353
2354 /*
b423cd4c 2355 * No job-uuid attribute, so build a version 3 UUID with the local job
2356 * ID at the end; see RFC 4122 for details. Start with the MD5 sum of
2357 * the ServerName, server name and port that the client connected to,
2358 * and local job ID...
ef416fc2 2359 */
2360
b423cd4c 2361 snprintf(uuid, sizeof(uuid), "%s:%s:%d:%d", ServerName, con->servername,
2362 con->serverport, job->id);
ef416fc2 2363
757d2cad 2364 _cupsMD5Init(&md5state);
2365 _cupsMD5Append(&md5state, (unsigned char *)uuid, strlen(uuid));
2366 _cupsMD5Finish(&md5state, md5sum);
ef416fc2 2367
2368 /*
b423cd4c 2369 * Format the UUID URI using the MD5 sum and job ID.
ef416fc2 2370 */
2371
b423cd4c 2372 snprintf(uuid, sizeof(uuid),
2373 "urn:uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
2374 "%02x%02x%02x%02x%02x%02x",
2375 md5sum[0], md5sum[1], md5sum[2], md5sum[3], md5sum[4], md5sum[5],
2376 (md5sum[6] & 15) | 0x30, md5sum[7], (md5sum[8] & 0x3f) | 0x40,
2377 md5sum[9], md5sum[10], md5sum[11], md5sum[12], md5sum[13],
2378 md5sum[14], md5sum[15]);
ef416fc2 2379
b423cd4c 2380 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL, uuid);
ef416fc2 2381}
2382
2383
2384/*
b423cd4c 2385 * 'add_printer()' - Add a printer to the system.
ef416fc2 2386 */
2387
2388static void
b423cd4c 2389add_printer(cupsd_client_t *con, /* I - Client connection */
2390 ipp_attribute_t *uri) /* I - URI of printer */
ef416fc2 2391{
2392 http_status_t status; /* Policy status */
b423cd4c 2393 int i; /* Looping var */
09a101d6 2394 char scheme[HTTP_MAX_URI], /* Method portion of URI */
b423cd4c 2395 username[HTTP_MAX_URI], /* Username portion of URI */
ef416fc2 2396 host[HTTP_MAX_URI], /* Host portion of URI */
2397 resource[HTTP_MAX_URI]; /* Resource portion of URI */
2398 int port; /* Port portion of URI */
b423cd4c 2399 cupsd_printer_t *printer; /* Printer/class */
2400 ipp_attribute_t *attr; /* Printer attribute */
2401 cups_file_t *fp; /* Script/PPD file */
2402 char line[1024]; /* Line from file... */
2403 char srcfile[1024], /* Source Script/PPD file */
2404 dstfile[1024]; /* Destination Script/PPD file */
2405 int modify; /* Non-zero if we are modifying */
2406 char newname[IPP_MAX_NAME]; /* New printer name */
b226ab99
MS
2407 int changed_driver, /* Changed the PPD/interface script? */
2408 need_restart_job, /* Need to restart job? */
2409 set_device_uri, /* Did we set the device URI? */
09a101d6 2410 set_port_monitor; /* Did we set the port monitor? */
ef416fc2 2411
2412
b423cd4c 2413 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer(%p[%d], %s)", con,
ef416fc2 2414 con->http.fd, uri->values[0].string.text);
2415
2416 /*
b423cd4c 2417 * Do we have a valid URI?
ef416fc2 2418 */
2419
09a101d6 2420 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
2421 sizeof(scheme), username, sizeof(username), host,
b423cd4c 2422 sizeof(host), &port, resource, sizeof(resource));
2423
2424 if (strncmp(resource, "/printers/", 10) || strlen(resource) == 10)
ef416fc2 2425 {
b423cd4c 2426 /*
2427 * No, return an error...
2428 */
2429
ef416fc2 2430 send_ipp_status(con, IPP_BAD_REQUEST,
b423cd4c 2431 _("The printer-uri must be of the form "
2432 "\"ipp://HOSTNAME/printers/PRINTERNAME\"."));
ef416fc2 2433 return;
2434 }
2435
2436 /*
b423cd4c 2437 * Do we have a valid printer name?
ef416fc2 2438 */
2439
b423cd4c 2440 if (!validate_name(resource + 10))
ef416fc2 2441 {
b423cd4c 2442 /*
2443 * No, return an error...
2444 */
2445
2446 send_ipp_status(con, IPP_BAD_REQUEST,
2447 _("The printer-uri \"%s\" contains invalid characters."),
2448 uri->values[0].string.text);
2449 return;
ef416fc2 2450 }
ef416fc2 2451
ef416fc2 2452 /*
b423cd4c 2453 * See if the printer already exists; if not, create a new printer...
2454 */
ef416fc2 2455
b423cd4c 2456 if ((printer = cupsdFindPrinter(resource + 10)) == NULL)
ef416fc2 2457 {
2458 /*
b423cd4c 2459 * Printer doesn't exist; see if we have a class of the same name...
ef416fc2 2460 */
2461
b423cd4c 2462 if ((printer = cupsdFindClass(resource + 10)) != NULL &&
bc44d920 2463 !(printer->type & CUPS_PRINTER_DISCOVERED))
ef416fc2 2464 {
b423cd4c 2465 /*
2466 * Yes, return an error...
2467 */
ef416fc2 2468
b423cd4c 2469 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 2470 _("A class named \"%s\" already exists"),
b423cd4c 2471 resource + 10);
ef416fc2 2472 return;
2473 }
2474
2475 /*
2e4ff8af 2476 * No, check the default policy then add the printer...
ef416fc2 2477 */
2478
2e4ff8af
MS
2479 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
2480 {
2481 send_http_error(con, status, NULL);
2482 return;
2483 }
2484
b423cd4c 2485 printer = cupsdAddPrinter(resource + 10);
2486 modify = 0;
ef416fc2 2487 }
b423cd4c 2488 else if (printer->type & CUPS_PRINTER_IMPLICIT)
ef416fc2 2489 {
2490 /*
2e4ff8af
MS
2491 * Check the default policy, then rename the implicit printer to
2492 * "AnyPrinter" or delete it...
ef416fc2 2493 */
2494
2e4ff8af
MS
2495 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
2496 {
2497 send_http_error(con, status, NULL);
2498 return;
2499 }
2500
b423cd4c 2501 if (ImplicitAnyClasses)
ef416fc2 2502 {
b423cd4c 2503 snprintf(newname, sizeof(newname), "Any%s", resource + 10);
2504 cupsdRenamePrinter(printer, newname);
ef416fc2 2505 }
b423cd4c 2506 else
2507 cupsdDeletePrinter(printer, 1);
ef416fc2 2508
2509 /*
b423cd4c 2510 * Add the printer as a new local printer...
ef416fc2 2511 */
2512
b423cd4c 2513 printer = cupsdAddPrinter(resource + 10);
2514 modify = 0;
ef416fc2 2515 }
bc44d920 2516 else if (printer->type & CUPS_PRINTER_DISCOVERED)
b423cd4c 2517 {
2518 /*
2e4ff8af
MS
2519 * Check the default policy, then rename the remote printer to
2520 * "Printer@server"...
b423cd4c 2521 */
ef416fc2 2522
2e4ff8af
MS
2523 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
2524 {
2525 send_http_error(con, status, NULL);
2526 return;
2527 }
2528
b423cd4c 2529 snprintf(newname, sizeof(newname), "%s@%s", resource + 10,
2530 printer->hostname);
2531 cupsdRenamePrinter(printer, newname);
ef416fc2 2532
b423cd4c 2533 /*
2534 * Add the printer as a new local printer...
2535 */
ef416fc2 2536
b423cd4c 2537 printer = cupsdAddPrinter(resource + 10);
2538 modify = 0;
2539 }
2e4ff8af
MS
2540 else if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
2541 NULL)) != HTTP_OK)
2542 {
2fb76298 2543 send_http_error(con, status, printer);
2e4ff8af
MS
2544 return;
2545 }
b423cd4c 2546 else
2547 modify = 1;
ef416fc2 2548
b423cd4c 2549 /*
2550 * Look for attributes and copy them over as needed...
2551 */
ef416fc2 2552
b226ab99 2553 changed_driver = 0;
b423cd4c 2554 need_restart_job = 0;
ef416fc2 2555
b423cd4c 2556 if ((attr = ippFindAttribute(con->request, "printer-location",
2557 IPP_TAG_TEXT)) != NULL)
2558 cupsdSetString(&printer->location, attr->values[0].string.text);
ef416fc2 2559
b423cd4c 2560 if ((attr = ippFindAttribute(con->request, "printer-info",
2561 IPP_TAG_TEXT)) != NULL)
2562 cupsdSetString(&printer->info, attr->values[0].string.text);
ef416fc2 2563
09a101d6 2564 set_device_uri = 0;
2565
b423cd4c 2566 if ((attr = ippFindAttribute(con->request, "device-uri",
2567 IPP_TAG_URI)) != NULL)
ef416fc2 2568 {
2569 /*
b423cd4c 2570 * Do we have a valid device URI?
ef416fc2 2571 */
2572
0af14961
MS
2573 http_uri_status_t uri_status; /* URI separation status */
2574 char old_device_uri[1024];
2575 /* Old device URI */
2fb76298
MS
2576
2577
b423cd4c 2578 need_restart_job = 1;
ef416fc2 2579
2fb76298
MS
2580 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
2581 attr->values[0].string.text,
2582 scheme, sizeof(scheme),
2583 username, sizeof(username),
2584 host, sizeof(host), &port,
2585 resource, sizeof(resource));
2586
2587 if (uri_status < HTTP_URI_OK)
2588 {
4d301e69 2589 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri \"%s\""),
2fb76298
MS
2590 attr->values[0].string.text);
2591 cupsdLogMessage(CUPSD_LOG_DEBUG,
2592 "add_printer: httpSeparateURI returned %d", uri_status);
2593 return;
2594 }
b423cd4c 2595
09a101d6 2596 if (!strcmp(scheme, "file"))
ef416fc2 2597 {
2598 /*
b423cd4c 2599 * See if the administrator has enabled file devices...
ef416fc2 2600 */
2601
b423cd4c 2602 if (!FileDevice && strcmp(resource, "/dev/null"))
ef416fc2 2603 {
2604 /*
b423cd4c 2605 * File devices are disabled and the URL is not file:/dev/null...
ef416fc2 2606 */
2607
b423cd4c 2608 send_ipp_status(con, IPP_NOT_POSSIBLE,
2609 _("File device URIs have been disabled! "
2610 "To enable, see the FileDevice directive in "
2611 "\"%s/cupsd.conf\"."),
2612 ServerRoot);
ef416fc2 2613 return;
2614 }
b423cd4c 2615 }
2616 else
2617 {
ef416fc2 2618 /*
b423cd4c 2619 * See if the backend exists and is executable...
ef416fc2 2620 */
2621
09a101d6 2622 snprintf(srcfile, sizeof(srcfile), "%s/backend/%s", ServerBin, scheme);
b423cd4c 2623 if (access(srcfile, X_OK))
ef416fc2 2624 {
2625 /*
b423cd4c 2626 * Could not find device in list!
ef416fc2 2627 */
ef416fc2 2628
4d301e69 2629 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri scheme \"%s\""),
1f6f3dbc 2630 scheme);
b423cd4c 2631 return;
ef416fc2 2632 }
2633 }
ef416fc2 2634
0af14961
MS
2635 if (printer->sanitized_device_uri)
2636 strlcpy(old_device_uri, printer->sanitized_device_uri,
2637 sizeof(old_device_uri));
2638 else
2639 old_device_uri[0] = '\0';
2640
2641 cupsdSetDeviceURI(printer, attr->values[0].string.text);
2642
b423cd4c 2643 cupsdLogMessage(CUPSD_LOG_INFO,
2644 "Setting %s device-uri to \"%s\" (was \"%s\".)",
0af14961
MS
2645 printer->name, printer->sanitized_device_uri,
2646 old_device_uri);
ef416fc2 2647
09a101d6 2648 set_device_uri = 1;
ef416fc2 2649 }
2650
09a101d6 2651 set_port_monitor = 0;
2652
b423cd4c 2653 if ((attr = ippFindAttribute(con->request, "port-monitor",
09a101d6 2654 IPP_TAG_NAME)) != NULL)
ef416fc2 2655 {
b423cd4c 2656 ipp_attribute_t *supported; /* port-monitor-supported attribute */
ef416fc2 2657
ef416fc2 2658
b423cd4c 2659 need_restart_job = 1;
ef416fc2 2660
e4572d57 2661 supported = ippFindAttribute(printer->ppd_attrs, "port-monitor-supported",
a41f09e2 2662 IPP_TAG_NAME);
080811b1
MS
2663 if (supported)
2664 {
2665 for (i = 0; i < supported->num_values; i ++)
2666 if (!strcmp(supported->values[i].string.text,
2667 attr->values[0].string.text))
2668 break;
2669 }
ef416fc2 2670
080811b1 2671 if (!supported || i >= supported->num_values)
b423cd4c 2672 {
4d301e69 2673 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad port-monitor \"%s\""),
b423cd4c 2674 attr->values[0].string.text);
2675 return;
2676 }
ef416fc2 2677
b423cd4c 2678 cupsdLogMessage(CUPSD_LOG_INFO,
2679 "Setting %s port-monitor to \"%s\" (was \"%s\".)",
2680 printer->name, attr->values[0].string.text,
09a101d6 2681 printer->port_monitor ? printer->port_monitor : "none");
ef416fc2 2682
b423cd4c 2683 if (strcmp(attr->values[0].string.text, "none"))
2684 cupsdSetString(&printer->port_monitor, attr->values[0].string.text);
2685 else
2686 cupsdClearString(&printer->port_monitor);
09a101d6 2687
2688 set_port_monitor = 1;
b423cd4c 2689 }
ef416fc2 2690
b423cd4c 2691 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs",
f8b3a85b
MS
2692 IPP_TAG_BOOLEAN)) != NULL &&
2693 attr->values[0].boolean != printer->accepting)
b423cd4c 2694 {
2695 cupsdLogMessage(CUPSD_LOG_INFO,
2696 "Setting %s printer-is-accepting-jobs to %d (was %d.)",
2697 printer->name, attr->values[0].boolean, printer->accepting);
ef416fc2 2698
b423cd4c 2699 printer->accepting = attr->values[0].boolean;
2700 cupsdAddPrinterHistory(printer);
f8b3a85b
MS
2701
2702 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
2703 "%s accepting jobs.",
2704 printer->accepting ? "Now" : "No longer");
b423cd4c 2705 }
bd7854cb 2706
b423cd4c 2707 if ((attr = ippFindAttribute(con->request, "printer-is-shared",
2708 IPP_TAG_BOOLEAN)) != NULL)
2709 {
2710 if (printer->shared && !attr->values[0].boolean)
f7deaa1a 2711 cupsdDeregisterPrinter(printer, 1);
bd7854cb 2712
b423cd4c 2713 cupsdLogMessage(CUPSD_LOG_INFO,
2714 "Setting %s printer-is-shared to %d (was %d.)",
2715 printer->name, attr->values[0].boolean, printer->shared);
bd7854cb 2716
b423cd4c 2717 printer->shared = attr->values[0].boolean;
2718 }
bd7854cb 2719
b423cd4c 2720 if ((attr = ippFindAttribute(con->request, "printer-state",
2721 IPP_TAG_ENUM)) != NULL)
bd7854cb 2722 {
b423cd4c 2723 if (attr->values[0].integer != IPP_PRINTER_IDLE &&
2724 attr->values[0].integer != IPP_PRINTER_STOPPED)
2725 {
4d301e69 2726 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad printer-state value %d"),
b423cd4c 2727 attr->values[0].integer);
2728 return;
2729 }
bd7854cb 2730
bc44d920 2731 cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)",
2732 printer->name, attr->values[0].integer, printer->state);
b423cd4c 2733
2734 if (attr->values[0].integer == IPP_PRINTER_STOPPED)
2735 cupsdStopPrinter(printer, 0);
2736 else
2737 {
2738 need_restart_job = 1;
2739 cupsdSetPrinterState(printer, (ipp_pstate_t)(attr->values[0].integer), 0);
2740 }
2741 }
745129be 2742
b423cd4c 2743 if ((attr = ippFindAttribute(con->request, "printer-state-message",
2744 IPP_TAG_TEXT)) != NULL)
2745 {
2746 strlcpy(printer->state_message, attr->values[0].string.text,
2747 sizeof(printer->state_message));
2748 cupsdAddPrinterHistory(printer);
f8b3a85b
MS
2749
2750 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL, "%s",
2751 printer->state_message);
bd7854cb 2752 }
2753
1f6f3dbc
MS
2754 if ((attr = ippFindAttribute(con->request, "printer-state-reasons",
2755 IPP_TAG_KEYWORD)) != NULL)
2756 {
2757 if (attr->num_values >
2758 (int)(sizeof(printer->reasons) / sizeof(printer->reasons[0])))
2759 {
2760 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 2761 _("Too many printer-state-reasons values (%d > %d)"),
1f6f3dbc
MS
2762 attr->num_values,
2763 (int)(sizeof(printer->reasons) /
2764 sizeof(printer->reasons[0])));
2765 return;
2766 }
2767
2768 for (i = 0; i < printer->num_reasons; i ++)
2769 _cupsStrFree(printer->reasons[i]);
2770
2771 printer->num_reasons = 0;
2772 for (i = 0; i < attr->num_values; i ++)
2773 {
2774 if (!strcmp(attr->values[i].string.text, "none"))
2775 continue;
2776
2777 printer->reasons[printer->num_reasons] =
426c6a59 2778 _cupsStrRetain(attr->values[i].string.text);
745129be 2779 printer->num_reasons ++;
1f6f3dbc 2780
745129be 2781 if (!strcmp(attr->values[i].string.text, "paused") &&
1f6f3dbc
MS
2782 printer->state != IPP_PRINTER_STOPPED)
2783 {
2784 cupsdLogMessage(CUPSD_LOG_INFO,
2785 "Setting %s printer-state to %d (was %d.)",
2786 printer->name, IPP_PRINTER_STOPPED, printer->state);
2787 cupsdStopPrinter(printer, 0);
2788 }
1f6f3dbc
MS
2789 }
2790
2791 if (PrintcapFormat == PRINTCAP_PLIST)
2792 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
f8b3a85b
MS
2793
2794 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
2795 "Printer \"%s\" state changed.", printer->name);
1f6f3dbc
MS
2796 }
2797
b423cd4c 2798 set_printer_defaults(con, printer);
2799
09a101d6 2800 if ((attr = ippFindAttribute(con->request, "auth-info-required",
2801 IPP_TAG_KEYWORD)) != NULL)
2802 cupsdSetAuthInfoRequired(printer, NULL, attr);
2803
bd7854cb 2804 /*
b423cd4c 2805 * See if we have all required attributes...
bd7854cb 2806 */
2807
b423cd4c 2808 if (!printer->device_uri)
2809 cupsdSetString(&printer->device_uri, "file:///dev/null");
bd7854cb 2810
2811 /*
b423cd4c 2812 * See if we have an interface script or PPD file attached to the request...
bd7854cb 2813 */
2814
b423cd4c 2815 if (con->filename)
2816 {
2817 need_restart_job = 1;
b226ab99 2818 changed_driver = 1;
bd7854cb 2819
b423cd4c 2820 strlcpy(srcfile, con->filename, sizeof(srcfile));
ef416fc2 2821
b423cd4c 2822 if ((fp = cupsFileOpen(srcfile, "rb")))
2823 {
2824 /*
2825 * Yes; get the first line from it...
2826 */
ef416fc2 2827
b423cd4c 2828 line[0] = '\0';
2829 cupsFileGets(fp, line, sizeof(line));
2830 cupsFileClose(fp);
ef416fc2 2831
b423cd4c 2832 /*
2833 * Then see what kind of file it is...
2834 */
ef416fc2 2835
b423cd4c 2836 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
2837 printer->name);
ef416fc2 2838
b423cd4c 2839 if (!strncmp(line, "*PPD-Adobe", 10))
2840 {
2841 /*
2842 * The new file is a PPD file, so remove any old interface script
2843 * that might be lying around...
2844 */
ef416fc2 2845
b423cd4c 2846 unlink(dstfile);
2847 }
2848 else
2849 {
2850 /*
2851 * This must be an interface script, so move the file over to the
2852 * interfaces directory and make it executable...
2853 */
ef416fc2 2854
b423cd4c 2855 if (copy_file(srcfile, dstfile))
2856 {
2857 send_ipp_status(con, IPP_INTERNAL_ERROR,
4d301e69 2858 _("Unable to copy interface script - %s"),
b423cd4c 2859 strerror(errno));
2860 return;
2861 }
568fa3fa
MS
2862
2863 cupsdLogMessage(CUPSD_LOG_DEBUG,
4d301e69 2864 "Copied interface script successfully");
568fa3fa 2865 chmod(dstfile, 0755);
b423cd4c 2866 }
ef416fc2 2867
b423cd4c 2868 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
2869 printer->name);
ef416fc2 2870
b423cd4c 2871 if (!strncmp(line, "*PPD-Adobe", 10))
2872 {
2873 /*
2874 * The new file is a PPD file, so move the file over to the
2875 * ppd directory and make it readable by all...
2876 */
ef416fc2 2877
b423cd4c 2878 if (copy_file(srcfile, dstfile))
2879 {
2880 send_ipp_status(con, IPP_INTERNAL_ERROR,
4d301e69 2881 _("Unable to copy PPD file - %s"),
b423cd4c 2882 strerror(errno));
2883 return;
2884 }
568fa3fa
MS
2885
2886 cupsdLogMessage(CUPSD_LOG_DEBUG,
4d301e69 2887 "Copied PPD file successfully");
568fa3fa 2888 chmod(dstfile, 0644);
b423cd4c 2889 }
2890 else
2891 {
2892 /*
2893 * This must be an interface script, so remove any old PPD file that
2894 * may be lying around...
2895 */
ef416fc2 2896
b423cd4c 2897 unlink(dstfile);
2898 }
2899 }
2900 }
2901 else if ((attr = ippFindAttribute(con->request, "ppd-name",
2902 IPP_TAG_NAME)) != NULL)
ef416fc2 2903 {
b423cd4c 2904 need_restart_job = 1;
b226ab99 2905 changed_driver = 1;
ef416fc2 2906
b423cd4c 2907 if (!strcmp(attr->values[0].string.text, "raw"))
ef416fc2 2908 {
b423cd4c 2909 /*
2910 * Raw driver, remove any existing PPD or interface script files.
2911 */
ef416fc2 2912
b423cd4c 2913 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
2914 printer->name);
2915 unlink(dstfile);
ef416fc2 2916
b423cd4c 2917 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
2918 printer->name);
2919 unlink(dstfile);
2920 }
2921 else
ef416fc2 2922 {
b423cd4c 2923 /*
2924 * PPD model file...
2925 */
2926
2927 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
2928 printer->name);
2929 unlink(dstfile);
2930
2931 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
2932 printer->name);
2933
2934 if (copy_model(con, attr->values[0].string.text, dstfile))
2935 {
4d301e69 2936 send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to copy PPD file"));
b423cd4c 2937 return;
2938 }
568fa3fa
MS
2939
2940 cupsdLogMessage(CUPSD_LOG_DEBUG,
4d301e69 2941 "Copied PPD file successfully");
568fa3fa 2942 chmod(dstfile, 0644);
b226ab99
MS
2943 }
2944 }
2945
2946 if (changed_driver)
2947 {
2948 /*
2949 * If we changed the PPD/interface script, then remove the printer's cache
c7017ecc 2950 * file and clear the printer-state-reasons...
b226ab99
MS
2951 */
2952
2953 char cache_name[1024]; /* Cache filename for printer attrs */
2954
7cf5915e 2955 snprintf(cache_name, sizeof(cache_name), "%s/%s.ipp4", CacheDir,
b226ab99
MS
2956 printer->name);
2957 unlink(cache_name);
568fa3fa 2958
7cf5915e 2959 snprintf(cache_name, sizeof(cache_name), "%s/%s.pwg3", CacheDir,
54afec33
MS
2960 printer->name);
2961 unlink(cache_name);
2962
c7017ecc
MS
2963 cupsdSetPrinterReasons(printer, "none");
2964
568fa3fa 2965#ifdef __APPLE__
b226ab99
MS
2966 /*
2967 * (Re)register color profiles...
2968 */
568fa3fa 2969
b226ab99
MS
2970 if (!RunUser)
2971 {
2972 apple_unregister_profiles(printer);
2973 apple_register_profiles(printer);
ef416fc2 2974 }
b226ab99 2975#endif /* __APPLE__ */
ef416fc2 2976 }
2977
09a101d6 2978 /*
2979 * If we set the device URI but not the port monitor, check which port
2980 * monitor to use by default...
2981 */
2982
2983 if (set_device_uri && !set_port_monitor)
2984 {
2985 ppd_file_t *ppd; /* PPD file */
2986 ppd_attr_t *ppdattr; /* cupsPortMonitor attribute */
2987
2988
2989 httpSeparateURI(HTTP_URI_CODING_ALL, printer->device_uri, scheme,
2990 sizeof(scheme), username, sizeof(username), host,
2991 sizeof(host), &port, resource, sizeof(resource));
2992
2993 snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot,
2994 printer->name);
2995 if ((ppd = ppdOpenFile(srcfile)) != NULL)
2996 {
2997 for (ppdattr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
2998 ppdattr;
2999 ppdattr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL))
3000 if (!strcmp(scheme, ppdattr->spec))
3001 {
3002 cupsdLogMessage(CUPSD_LOG_INFO,
3003 "Setting %s port-monitor to \"%s\" (was \"%s\".)",
3004 printer->name, ppdattr->value,
bc44d920 3005 printer->port_monitor ? printer->port_monitor
3006 : "none");
09a101d6 3007
3008 if (strcmp(ppdattr->value, "none"))
3009 cupsdSetString(&printer->port_monitor, ppdattr->value);
3010 else
3011 cupsdClearString(&printer->port_monitor);
3012
3013 break;
3014 }
3015
3016 ppdClose(ppd);
3017 }
3018 }
3019
ef416fc2 3020 /*
b423cd4c 3021 * Update the printer attributes and return...
ef416fc2 3022 */
3023
b423cd4c 3024 cupsdSetPrinterAttrs(printer);
3dfe78b3 3025 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 3026
b423cd4c 3027 if (need_restart_job && printer->job)
ef416fc2 3028 {
b423cd4c 3029 /*
b9faaae1 3030 * Restart the current job...
b423cd4c 3031 */
ef416fc2 3032
b9faaae1
MS
3033 cupsdSetJobState(printer->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
3034 "Job restarted because the printer was modified.");
ef416fc2 3035 }
3036
3dfe78b3 3037 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
b423cd4c 3038
3039 if (modify)
ef416fc2 3040 {
c7017ecc 3041 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED,
f8b3a85b
MS
3042 printer, NULL, "Printer \"%s\" modified by \"%s\".",
3043 printer->name, get_username(con));
ef416fc2 3044
b423cd4c 3045 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" modified by \"%s\".",
3046 printer->name, get_username(con));
ef416fc2 3047 }
b423cd4c 3048 else
3049 {
3050 cupsdAddPrinterHistory(printer);
ef416fc2 3051
c7017ecc 3052 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
f8b3a85b
MS
3053 printer, NULL, "New printer \"%s\" added by \"%s\".",
3054 printer->name, get_username(con));
ef416fc2 3055
b423cd4c 3056 cupsdLogMessage(CUPSD_LOG_INFO, "New printer \"%s\" added by \"%s\".",
3057 printer->name, get_username(con));
3058 }
3059
3060 con->response->request.status.status_code = IPP_OK;
ef416fc2 3061}
3062
3063
3064/*
b423cd4c 3065 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
3066 * based upon the printer state...
ef416fc2 3067 */
3068
b423cd4c 3069static void
3070add_printer_state_reasons(
3071 cupsd_client_t *con, /* I - Client connection */
3072 cupsd_printer_t *p) /* I - Printer info */
ef416fc2 3073{
b423cd4c 3074 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3075 "add_printer_state_reasons(%p[%d], %p[%s])",
3076 con, con->http.fd, p, p->name);
ef416fc2 3077
b423cd4c 3078 if (p->num_reasons == 0)
3079 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
745129be 3080 "printer-state-reasons", NULL, "none");
b423cd4c 3081 else
3082 ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3083 "printer-state-reasons", p->num_reasons, NULL,
3084 (const char * const *)p->reasons);
3085}
ef416fc2 3086
ef416fc2 3087
b423cd4c 3088/*
3089 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
3090 * the specified printer or class.
3091 */
ef416fc2 3092
b423cd4c 3093static void
3094add_queued_job_count(
3095 cupsd_client_t *con, /* I - Client connection */
3096 cupsd_printer_t *p) /* I - Printer or class */
3097{
3098 int count; /* Number of jobs on destination */
ef416fc2 3099
ef416fc2 3100
b423cd4c 3101 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])",
3102 con, con->http.fd, p, p->name);
ef416fc2 3103
b423cd4c 3104 count = cupsdGetPrinterJobCount(p->name);
ef416fc2 3105
b423cd4c 3106 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3107 "queued-job-count", count);
3108}
ef416fc2 3109
ef416fc2 3110
568fa3fa 3111#ifdef __APPLE__
568fa3fa
MS
3112/*
3113 * 'apple_init_profile()' - Initialize a color profile.
3114 */
3115
3116static void
3117apple_init_profile(
229681c1
MS
3118 ppd_file_t *ppd, /* I - PPD file */
3119 cups_array_t *languages, /* I - Languages in the PPD file */
3120# ifdef HAVE_COLORSYNCREGISTERDEVICE
3121 CFMutableDictionaryRef profile, /* I - Profile dictionary */
3122# else
3123 CMDeviceProfileInfo *profile, /* I - Profile record */
3124# endif /* HAVE_COLORSYNCREGISTERDEVICE */
3125 unsigned id, /* I - Profile ID */
3126 const char *name, /* I - Profile name */
3127 const char *text, /* I - Profile UI text */
3128 const char *iccfile) /* I - ICC filename */
568fa3fa 3129{
229681c1
MS
3130# ifdef HAVE_COLORSYNCREGISTERDEVICE
3131 CFURLRef url; /* URL for profile filename */
3132# endif /* HAVE_COLORSYNCREGISTERDEVICE */
568fa3fa 3133 CFMutableDictionaryRef dict; /* Dictionary for name */
a603edef
MS
3134 char *language; /* Current language */
3135 ppd_attr_t *attr; /* Profile attribute */
db0bd74a
MS
3136 CFStringRef cflang, /* Language string */
3137 cftext; /* Localized text */
568fa3fa
MS
3138
3139
a603edef
MS
3140 /*
3141 * Build the profile name dictionary...
3142 */
568fa3fa
MS
3143
3144 dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3145 &kCFTypeDictionaryKeyCallBacks,
3146 &kCFTypeDictionaryValueCallBacks);
3147
db0bd74a
MS
3148 cftext = CFStringCreateWithCString(kCFAllocatorDefault, text,
3149 kCFStringEncodingUTF8);
a603edef 3150
db0bd74a
MS
3151 if (cftext)
3152 {
229681c1 3153 CFDictionarySetValue(dict, CFSTR("en_US"), cftext);
db0bd74a
MS
3154 CFRelease(cftext);
3155 }
3156
3157 if (languages)
a603edef
MS
3158 {
3159 /*
3160 * Find localized names for the color profiles...
3161 */
3162
3163 cupsArraySave(ppd->sorted_attrs);
3164
3165 for (language = (char *)cupsArrayFirst(languages);
3166 language;
3167 language = (char *)cupsArrayNext(languages))
3168 {
db0bd74a 3169 if (iccfile)
c9fc04c6
MS
3170 {
3171 if ((attr = _ppdLocalizedAttr(ppd, "cupsICCProfile", name,
3172 language)) == NULL)
3173 attr = _ppdLocalizedAttr(ppd, "APTiogaProfile", name, language);
3174 }
db0bd74a
MS
3175 else
3176 attr = _ppdLocalizedAttr(ppd, "ColorModel", name, language);
3177
3178 if (attr && attr->text[0])
3179 {
3180 cflang = CFStringCreateWithCString(kCFAllocatorDefault, language,
3181 kCFStringEncodingUTF8);
3182 cftext = CFStringCreateWithCString(kCFAllocatorDefault, attr->text,
3183 kCFStringEncodingUTF8);
3184
3185 if (cflang && cftext)
3186 CFDictionarySetValue(dict, cflang, cftext);
3187
3188 if (cflang)
3189 CFRelease(cflang);
3190
3191 if (cftext)
3192 CFRelease(cftext);
3193 }
a603edef
MS
3194 }
3195
3196 cupsArrayRestore(ppd->sorted_attrs);
3197 }
3198
3199 /*
3200 * Fill in the profile data...
3201 */
3202
229681c1
MS
3203# ifdef HAVE_COLORSYNCREGISTERDEVICE
3204 if (iccfile)
3205 {
3206 url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
3207 (const UInt8 *)iccfile,
3208 strlen(iccfile), false);
a603edef 3209
229681c1
MS
3210 if (url)
3211 {
3212 CFDictionarySetValue(profile, kColorSyncDeviceProfileURL, url);
3213 CFRelease(url);
3214 }
3215 }
3216
3217 CFDictionarySetValue(profile, kColorSyncDeviceModeDescriptions, dict);
3218 CFRelease(dict);
3219
3220# else
568fa3fa
MS
3221 profile->dataVersion = cmDeviceProfileInfoVersion1;
3222 profile->profileID = id;
3223 profile->profileLoc.locType = iccfile ? cmPathBasedProfile : cmNoProfileBase;
3224 profile->profileName = dict;
3225
3226 if (iccfile)
3227 strlcpy(profile->profileLoc.u.pathLoc.path, iccfile,
3228 sizeof(profile->profileLoc.u.pathLoc.path));
229681c1 3229# endif /* HAVE_COLORSYNCREGISTERDEVICE */
568fa3fa
MS
3230}
3231
3232
3233/*
3234 * 'apple_register_profiles()' - Register color profiles for a printer.
3235 */
3236
3237static void
3238apple_register_profiles(
3239 cupsd_printer_t *p) /* I - Printer */
3240{
3241 int i; /* Looping var */
3242 char ppdfile[1024], /* PPD filename */
db0bd74a
MS
3243 iccfile[1024], /* ICC filename */
3244 selector[PPD_MAX_NAME];
3245 /* Profile selection string */
568fa3fa 3246 ppd_file_t *ppd; /* PPD file */
01ce6322
MS
3247 ppd_attr_t *attr, /* Profile attributes */
3248 *profileid_attr,/* cupsProfileID attribute */
3249 *q1_attr, /* ColorModel (or other) qualifier */
3250 *q2_attr, /* MediaType (or other) qualifier */
3251 *q3_attr; /* Resolution (or other) qualifier */
3252 char q_keyword[PPD_MAX_NAME];
3253 /* Qualifier keyword */
3254 const char *q1_choice, /* ColorModel (or other) choice */
3255 *q2_choice, /* MediaType (or other) choice */
3256 *q3_choice; /* Resolution (or other) choice */
c9fc04c6 3257 const char *profile_key; /* Profile keyword */
568fa3fa 3258 ppd_option_t *cm_option; /* Color model option */
01ce6322 3259 ppd_choice_t *cm_choice; /* Color model choice */
568fa3fa 3260 int num_profiles; /* Number of profiles */
229681c1 3261 CMError error = 0; /* Last error */
db0bd74a
MS
3262 unsigned device_id, /* Printer device ID */
3263 profile_id, /* Profile ID */
3264 default_profile_id = 0;
3265 /* Default profile ID */
3266 CFMutableDictionaryRef device_name; /* Printer device name dictionary */
3267 CFStringRef printer_name; /* Printer name string */
229681c1
MS
3268 cups_array_t *languages; /* Languages array */
3269# ifdef HAVE_COLORSYNCREGISTERDEVICE
3270 CFMutableDictionaryRef profiles, /* Dictionary of profiles */
3271 profile; /* Current profile info dictionary */
3272 CFStringRef dict_key; /* Key in factory profile dictionary */
3273# else
568fa3fa
MS
3274 CMDeviceScope scope = /* Scope of the registration */
3275 {
3276 kCFPreferencesAnyUser,
3277 kCFPreferencesCurrentHost
3278 };
3279 CMDeviceProfileArrayPtr profiles; /* Profiles */
3280 CMDeviceProfileInfo *profile; /* Current profile */
229681c1 3281# endif /* HAVE_COLORSYNCREGISTERDEVICE */
568fa3fa
MS
3282
3283
64a69576
MS
3284 /*
3285 * Make sure ColorSync is available...
3286 */
3287
229681c1
MS
3288# ifdef HAVE_COLORSYNCREGISTERDEVICE
3289 if (ColorSyncRegisterDevice == NULL)
3290 return;
3291
3292# else
64a69576
MS
3293 if (CMRegisterColorDevice == NULL)
3294 return;
229681c1 3295# endif /* HAVE_COLORSYNCREGISTERDEVICE */
64a69576 3296
568fa3fa
MS
3297 /*
3298 * Try opening the PPD file for this printer...
3299 */
3300
3301 snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
3302 if ((ppd = ppdOpenFile(ppdfile)) == NULL)
3303 return;
3304
3305 /*
3306 * See if we have any profiles...
3307 */
3308
c9fc04c6
MS
3309 if ((attr = ppdFindAttr(ppd, "APTiogaProfile", NULL)) != NULL)
3310 profile_key = "APTiogaProfile";
3311 else
3312 {
3313 attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
3314 profile_key = "cupsICCProfile";
3315 }
3316
3317 for (num_profiles = 0; attr; attr = ppdFindNextAttr(ppd, profile_key, NULL))
568fa3fa
MS
3318 if (attr->spec[0] && attr->value && attr->value[0])
3319 {
3320 if (attr->value[0] != '/')
3321 snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
3322 attr->value);
3323 else
3324 strlcpy(iccfile, attr->value, sizeof(iccfile));
3325
3326 if (access(iccfile, 0))
3327 continue;
3328
3329 num_profiles ++;
3330 }
3331
229681c1
MS
3332# ifdef HAVE_COLORSYNCREGISTERDEVICE
3333 /*
3334 * Create a dictionary for the factory profiles...
3335 */
3336
3337 profiles = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3338 &kCFTypeDictionaryKeyCallBacks,
3339 &kCFTypeDictionaryValueCallBacks);
3340 if (!profiles)
3341 {
3342 cupsdLogMessage(CUPSD_LOG_ERROR,
3343 "Unable to allocate memory for factory profiles.");
3344 ppdClose(ppd);
3345 return;
3346 }
3347# endif /* HAVE_COLORSYNCREGISTERDEVICE */
ef55b745 3348
568fa3fa
MS
3349 /*
3350 * If we have profiles, add them...
3351 */
3352
3353 if (num_profiles > 0)
3354 {
01ce6322
MS
3355 if (profile_key[0] == 'A')
3356 {
3357 /*
3358 * For Tioga PPDs, get the default profile using the DefaultAPTiogaProfile
3359 * attribute...
3360 */
db0bd74a 3361
01ce6322
MS
3362 if ((attr = ppdFindAttr(ppd, "DefaultAPTiogaProfile", NULL)) != NULL &&
3363 attr->value)
3364 default_profile_id = atoi(attr->value);
db0bd74a 3365
01ce6322
MS
3366 q1_choice = q2_choice = q3_choice = NULL;
3367 }
db0bd74a 3368 else
01ce6322
MS
3369 {
3370 /*
3371 * For CUPS PPDs, figure out the default profile selector values...
3372 */
db0bd74a 3373
01ce6322
MS
3374 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL &&
3375 attr->value && attr->value[0])
3376 {
3377 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
3378 q1_attr = ppdFindAttr(ppd, q_keyword, NULL);
3379 }
3380 else if ((q1_attr = ppdFindAttr(ppd, "DefaultColorModel", NULL)) == NULL)
3381 q1_attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
3382
3383 if (q1_attr && q1_attr->value && q1_attr->value[0])
3384 q1_choice = q1_attr->value;
3385 else
3386 q1_choice = "";
3387
3388 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL &&
3389 attr->value && attr->value[0])
3390 {
3391 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
3392 q2_attr = ppdFindAttr(ppd, q_keyword, NULL);
3393 }
ef55b745 3394 else
01ce6322
MS
3395 q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL);
3396
3397 if (q2_attr && q2_attr->value && q2_attr->value[0])
3398 q2_choice = q2_attr->value;
3399 else
3400 q2_choice = NULL;
3401
3402 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL &&
3403 attr->value && attr->value[0])
3404 {
3405 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
3406 q3_attr = ppdFindAttr(ppd, q_keyword, NULL);
3407 }
ef55b745 3408 else
01ce6322
MS
3409 q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL);
3410
3411 if (q3_attr && q3_attr->value && q3_attr->value[0])
3412 q3_choice = q3_attr->value;
3413 else
3414 q3_choice = NULL;
3415 }
db0bd74a 3416
229681c1 3417# ifndef HAVE_COLORSYNCREGISTERDEVICE
568fa3fa
MS
3418 /*
3419 * Build the array of profiles...
3420 *
3421 * Note: This calloc actually requests slightly more memory than needed.
3422 */
3423
3424 if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
3425 {
3426 cupsdLogMessage(CUPSD_LOG_ERROR,
229681c1 3427 "Unable to allocate memory for factory profiles.");
568fa3fa
MS
3428 ppdClose(ppd);
3429 return;
3430 }
3431
3432 profiles->profileCount = num_profiles;
229681c1
MS
3433 profile = profiles->profiles;
3434# endif /* !HAVE_COLORSYNCREGISTERDEVICE */
568fa3fa 3435
229681c1
MS
3436 /*
3437 * Loop through the profiles listed in the PPD...
3438 */
3439
3440 languages = _ppdGetLanguages(ppd);
3441
3442 for (attr = ppdFindAttr(ppd, profile_key, NULL);
568fa3fa 3443 attr;
c9fc04c6 3444 attr = ppdFindNextAttr(ppd, profile_key, NULL))
568fa3fa
MS
3445 if (attr->spec[0] && attr->value && attr->value[0])
3446 {
3447 /*
3448 * Add this profile...
3449 */
3450
3451 if (attr->value[0] != '/')
3452 snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
3453 attr->value);
3454 else
3455 strlcpy(iccfile, attr->value, sizeof(iccfile));
3456
3457 if (access(iccfile, 0))
3458 continue;
3459
c9fc04c6
MS
3460 if (profile_key[0] == 'c')
3461 {
3462 cupsArraySave(ppd->sorted_attrs);
db0bd74a 3463
c9fc04c6
MS
3464 if ((profileid_attr = ppdFindAttr(ppd, "cupsProfileID",
3465 attr->spec)) != NULL &&
3466 profileid_attr->value && isdigit(profileid_attr->value[0] & 255))
3467 profile_id = (unsigned)strtoul(profileid_attr->value, NULL, 10);
3468 else
3469 profile_id = _ppdHashName(attr->spec);
db0bd74a 3470
c9fc04c6
MS
3471 cupsArrayRestore(ppd->sorted_attrs);
3472 }
3473 else
3474 profile_id = atoi(attr->spec);
db0bd74a 3475
229681c1
MS
3476# ifdef HAVE_COLORSYNCREGISTERDEVICE
3477 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3478 &kCFTypeDictionaryKeyCallBacks,
3479 &kCFTypeDictionaryValueCallBacks);
3480 if (!profile)
3481 {
3482 cupsdLogMessage(CUPSD_LOG_ERROR,
3483 "Unable to allocate memory for color profile.");
3484 CFRelease(profiles);
3485 ppdClose(ppd);
3486 return;
3487 }
3488
3489 apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
3490 attr->text[0] ? attr->text : attr->spec, iccfile);
3491
3492 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
3493 CFSTR("%u"), profile_id);
3494 if (dict_key)
3495 {
3496 CFDictionarySetValue(profiles, dict_key, profile);
3497 CFRelease(dict_key);
3498 }
3499
3500 CFRelease(profile);
3501
3502# else
db0bd74a
MS
3503 apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
3504 attr->text[0] ? attr->text : attr->spec, iccfile);
568fa3fa
MS
3505
3506 profile ++;
229681c1 3507# endif /* HAVE_COLORSYNCREGISTERDEVICE */
db0bd74a
MS
3508
3509 /*
3510 * See if this is the default profile...
3511 */
3512
01ce6322 3513 if (!default_profile_id)
db0bd74a
MS
3514 {
3515 if (q2_choice)
3516 {
3517 if (q3_choice)
3518 {
3519 snprintf(selector, sizeof(selector), "%s.%s.%s",
01ce6322 3520 q1_choice, q2_choice, q3_choice);
db0bd74a
MS
3521 if (!strcmp(selector, attr->spec))
3522 default_profile_id = profile_id;
3523 }
3524
3525 if (!default_profile_id)
3526 {
01ce6322
MS
3527 snprintf(selector, sizeof(selector), "%s.%s.", q1_choice,
3528 q2_choice);
db0bd74a
MS
3529 if (!strcmp(selector, attr->spec))
3530 default_profile_id = profile_id;
3531 }
3532 }
3533
3534 if (!default_profile_id && q3_choice)
3535 {
01ce6322
MS
3536 snprintf(selector, sizeof(selector), "%s..%s", q1_choice,
3537 q3_choice);
db0bd74a
MS
3538 if (!strcmp(selector, attr->spec))
3539 default_profile_id = profile_id;
3540 }
3541
3542 if (!default_profile_id)
3543 {
01ce6322 3544 snprintf(selector, sizeof(selector), "%s..", q1_choice);
db0bd74a
MS
3545 if (!strcmp(selector, attr->spec))
3546 default_profile_id = profile_id;
3547 }
3548 }
568fa3fa 3549 }
a603edef
MS
3550
3551 _ppdFreeLanguages(languages);
568fa3fa
MS
3552 }
3553 else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL)
3554 {
3555 /*
db0bd74a 3556 * Extract profiles from ColorModel option...
568fa3fa
MS
3557 */
3558
db0bd74a 3559 const char *profile_name; /* Name of generic profile */
568fa3fa 3560
db0bd74a
MS
3561
3562 num_profiles = cm_option->num_choices;
568fa3fa 3563
229681c1
MS
3564# ifndef HAVE_COLORSYNCREGISTERDEVICE
3565 /*
3566 * Create an array for the factory profiles...
3567 */
3568
568fa3fa
MS
3569 if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
3570 {
3571 cupsdLogMessage(CUPSD_LOG_ERROR,
229681c1 3572 "Unable to allocate memory for factory profiles.");
568fa3fa
MS
3573 ppdClose(ppd);
3574 return;
3575 }
3576
3577 profiles->profileCount = num_profiles;
229681c1
MS
3578 profile = profiles->profiles;
3579# endif /* HAVE_COLORSYNCREGISTERDEVICE */
568fa3fa 3580
229681c1 3581 for (i = cm_option->num_choices, cm_choice = cm_option->choices;
568fa3fa 3582 i > 0;
229681c1 3583 i --, cm_choice ++)
db0bd74a
MS
3584 {
3585 if (!strcmp(cm_choice->choice, "Gray") ||
3586 !strcmp(cm_choice->choice, "Black"))
3587 profile_name = "Gray";
3588 else if (!strcmp(cm_choice->choice, "RGB") ||
3589 !strcmp(cm_choice->choice, "CMY"))
3590 profile_name = "RGB";
3591 else if (!strcmp(cm_choice->choice, "CMYK") ||
3592 !strcmp(cm_choice->choice, "KCMY"))
3593 profile_name = "CMYK";
3594 else
3595 profile_name = "DeviceN";
3596
3597 snprintf(selector, sizeof(selector), "%s..", profile_name);
3598 profile_id = _ppdHashName(selector);
3599
229681c1
MS
3600# ifdef HAVE_COLORSYNCREGISTERDEVICE
3601 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3602 &kCFTypeDictionaryKeyCallBacks,
3603 &kCFTypeDictionaryValueCallBacks);
3604 if (!profile)
3605 {
3606 cupsdLogMessage(CUPSD_LOG_ERROR,
3607 "Unable to allocate memory for color profile.");
3608 CFRelease(profiles);
3609 ppdClose(ppd);
3610 return;
3611 }
3612
3613 apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
3614 cm_choice->text, NULL);
3615
3616 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
3617 CFSTR("%u"), profile_id);
3618 if (dict_key)
3619 {
3620 CFDictionarySetValue(profiles, dict_key, profile);
3621 CFRelease(dict_key);
3622 }
3623
3624 CFRelease(profile);
3625
3626# else
db0bd74a
MS
3627 apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
3628 cm_choice->text, NULL);
229681c1
MS
3629 profile ++;
3630# endif /* HAVE_COLORSYNCREGISTERDEVICE */
db0bd74a
MS
3631
3632 if (cm_choice->marked)
3633 default_profile_id = profile_id;
3634 }
568fa3fa
MS
3635 }
3636 else
3637 {
3638 /*
3639 * Use the default colorspace...
3640 */
3641
745129be 3642 attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
d1c13e16 3643
745129be 3644 num_profiles = (attr && ppd->colorspace == PPD_CS_GRAY) ? 1 : 2;
ef55b745 3645
229681c1
MS
3646# ifdef HAVE_COLORSYNCREGISTERDEVICE
3647 /*
3648 * Add the grayscale profile first. We always have a grayscale profile.
3649 */
3650
3651 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3652 &kCFTypeDictionaryKeyCallBacks,
3653 &kCFTypeDictionaryValueCallBacks);
3654
3655 if (!profile)
3656 {
3657 cupsdLogMessage(CUPSD_LOG_ERROR,
3658 "Unable to allocate memory for color profile.");
3659 CFRelease(profiles);
3660 ppdClose(ppd);
3661 return;
3662 }
3663
3664 profile_id = _ppdHashName("Gray..");
3665 apple_init_profile(ppd, NULL, profile, profile_id, "Gray", "Gray", NULL);
3666
3667 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
3668 profile_id);
3669 if (dict_key)
3670 {
3671 CFDictionarySetValue(profiles, dict_key, profile);
3672 CFRelease(dict_key);
3673 }
3674
3675 CFRelease(profile);
3676
3677 /*
3678 * Then add the RGB/CMYK/DeviceN color profile...
3679 */
3680
3681 profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3682 &kCFTypeDictionaryKeyCallBacks,
3683 &kCFTypeDictionaryValueCallBacks);
3684
3685 if (!profile)
3686 {
3687 cupsdLogMessage(CUPSD_LOG_ERROR,
3688 "Unable to allocate memory for color profile.");
3689 CFRelease(profiles);
3690 ppdClose(ppd);
3691 return;
3692 }
3693
3694 switch (ppd->colorspace)
3695 {
3696 case PPD_CS_RGB :
3697 case PPD_CS_CMY :
3698 profile_id = _ppdHashName("RGB..");
3699 apple_init_profile(ppd, NULL, profile, profile_id, "RGB", "RGB",
3700 NULL);
3701 break;
3702 case PPD_CS_RGBK :
3703 case PPD_CS_CMYK :
3704 profile_id = _ppdHashName("CMYK..");
3705 apple_init_profile(ppd, NULL, profile, profile_id, "CMYK", "CMYK",
3706 NULL);
3707 break;
3708
3709 case PPD_CS_GRAY :
3710 if (attr)
3711 break;
3712
3713 case PPD_CS_N :
3714 profile_id = _ppdHashName("DeviceN..");
3715 apple_init_profile(ppd, NULL, profile, profile_id, "DeviceN",
3716 "DeviceN", NULL);
3717 break;
3718 }
3719
3720 if (CFDictionaryGetCount(profile) > 0)
3721 {
3722 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
3723 CFSTR("%u"), profile_id);
3724 if (dict_key)
3725 {
3726 CFDictionarySetValue(profiles, dict_key, profile);
3727 CFRelease(dict_key);
3728 }
3729 }
3730
3731 CFRelease(profile);
3732
3733# else
3734 /*
3735 * Create an array for the factory profiles...
3736 */
3737
db0bd74a 3738 if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
568fa3fa
MS
3739 {
3740 cupsdLogMessage(CUPSD_LOG_ERROR,
229681c1 3741 "Unable to allocate memory for factory profiles.");
568fa3fa
MS
3742 ppdClose(ppd);
3743 return;
3744 }
3745
db0bd74a
MS
3746 profiles->profileCount = num_profiles;
3747
229681c1
MS
3748 /*
3749 * Add the grayscale profile first. We always have a grayscale profile.
3750 */
3751
3752 profile_id = _ppdHashName("Gray..");
3753 apple_init_profile(ppd, NULL, profiles->profiles, profile_id, "Gray",
3754 "Gray", NULL);
3755
3756 /*
3757 * Then add the RGB/CMYK/DeviceN color profile...
3758 */
568fa3fa
MS
3759
3760 switch (ppd->colorspace)
3761 {
568fa3fa 3762 case PPD_CS_RGB :
db0bd74a 3763 case PPD_CS_CMY :
229681c1
MS
3764 profile_id = _ppdHashName("RGB..");
3765 apple_init_profile(ppd, NULL, profiles->profiles + 1, profile_id,
3766 "RGB", "RGB", NULL);
568fa3fa 3767 break;
db0bd74a 3768 case PPD_CS_RGBK :
568fa3fa 3769 case PPD_CS_CMYK :
229681c1
MS
3770 profile_id = _ppdHashName("CMYK..");
3771 apple_init_profile(ppd, NULL, profiles->profiles + 1, profile_id,
3772 "CMYK", "CMYK", NULL);
568fa3fa
MS
3773 break;
3774
745129be
MS
3775 case PPD_CS_GRAY :
3776 if (attr)
3777 break;
3778
db0bd74a 3779 case PPD_CS_N :
229681c1
MS
3780 profile_id = _ppdHashName("DeviceN..");
3781 apple_init_profile(ppd, NULL, profiles->profiles + 1, profile_id,
3782 "DeviceN", "DeviceN", NULL);
db0bd74a 3783 break;
568fa3fa 3784 }
229681c1 3785# endif /* HAVE_COLORSYNCREGISTERDEVICE */
568fa3fa
MS
3786 }
3787
3788 if (num_profiles > 0)
3789 {
db0bd74a
MS
3790 /*
3791 * Make sure we have a default profile ID...
3792 */
3793
3794 if (!default_profile_id)
229681c1
MS
3795 default_profile_id = profile_id; /* Last profile */
3796
3797# ifdef HAVE_COLORSYNCREGISTERDEVICE
3798 dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
3799 default_profile_id);
3800 if (dict_key)
3801 {
3802 CFDictionarySetValue(profiles, kColorSyncDeviceDefaultProfileID,
3803 dict_key);
3804 CFRelease(dict_key);
3805 }
3806# endif /* HAVE_COLORSYNCREGISTERDEVICE */
db0bd74a 3807
568fa3fa
MS
3808 /*
3809 * Get the device ID hash and pathelogical name dictionary.
3810 */
3811
3812 cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\"",
3813 p->name);
3814
db0bd74a
MS
3815 device_id = _ppdHashName(p->name);
3816 device_name = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3817 &kCFTypeDictionaryKeyCallBacks,
3818 &kCFTypeDictionaryValueCallBacks);
9a4f8274
MS
3819 printer_name = CFStringCreateWithCString(kCFAllocatorDefault,
3820 p->name, kCFStringEncodingUTF8);
568fa3fa 3821
db0bd74a
MS
3822 if (device_name && printer_name)
3823 {
229681c1 3824 CFDictionarySetValue(device_name, CFSTR("en_US"), printer_name);
568fa3fa 3825
db0bd74a
MS
3826 /*
3827 * Register the device with ColorSync...
3828 */
568fa3fa 3829
229681c1
MS
3830# ifdef HAVE_COLORSYNCREGISTERDEVICE
3831 CFTypeRef deviceDictKeys[] =
3832 { /* Device keys */
3833 kColorSyncDeviceDescriptions,
3834 kColorSyncFactoryProfiles,
3835 kColorSyncDeviceUserScope,
3836 kColorSyncDeviceHostScope
3837 };
3838 CFTypeRef deviceDictVals[] =
3839 { /* Device values */
3840 device_name,
3841 profiles,
3842 kCFPreferencesAnyUser,
3843 kCFPreferencesCurrentHost
3844 };
3845 CFDictionaryRef deviceDict; /* Device dictionary */
3846 CFUUIDRef deviceUUID; /* Device UUID (TODO: use printer-uuid value) */
3847
3848 deviceDict = CFDictionaryCreate(kCFAllocatorDefault,
3849 (const void **)deviceDictKeys,
3850 (const void **)deviceDictVals,
3851 sizeof(deviceDictKeys) /
3852 sizeof(deviceDictVals),
3853 &kCFTypeDictionaryKeyCallBacks,
3854 &kCFTypeDictionaryValueCallBacks);
3855 deviceUUID = ColorSyncCreateUUIDFromUInt32(device_id);
3856 if (deviceDict && deviceUUID &&
3857 !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass, deviceUUID,
3858 deviceDict))
3859 error = 1001;
3860
3861 if (deviceUUID)
3862 CFRelease(deviceUUID);
3863
3864 if (deviceDict)
3865 CFRelease(deviceDict);
3866
3867# else
db0bd74a
MS
3868 error = CMRegisterColorDevice(cmPrinterDeviceClass, device_id,
3869 device_name, &scope);
568fa3fa 3870
db0bd74a
MS
3871 /*
3872 * Register the profiles...
3873 */
568fa3fa 3874
db0bd74a
MS
3875 if (error == noErr)
3876 error = CMSetDeviceFactoryProfiles(cmPrinterDeviceClass, device_id,
3877 default_profile_id, profiles);
229681c1 3878# endif /* HAVE_COLORSYNCREGISTERDEVICE */
db0bd74a
MS
3879 }
3880 else
3881 error = 1000;
568fa3fa
MS
3882
3883 /*
3884 * Clean up...
3885 */
3886
3887 if (error != noErr)
3888 cupsdLogMessage(CUPSD_LOG_ERROR,
229681c1 3889 "Unable to register ICC color profiles for \"%s\": %d",
568fa3fa
MS
3890 p->name, (int)error);
3891
229681c1
MS
3892 if (printer_name)
3893 CFRelease(printer_name);
3894
3895 if (device_name)
3896 CFRelease(device_name);
3897 }
3898
3899 /*
3900 * Free any memory we used...
3901 */
3902
3903# ifdef HAVE_COLORSYNCREGISTERDEVICE
3904 CFRelease(profiles);
3905
3906# else
3907 if (num_profiles > 0)
3908 {
568fa3fa
MS
3909 for (profile = profiles->profiles;
3910 num_profiles > 0;
3911 profile ++, num_profiles --)
3912 CFRelease(profile->profileName);
3913
3914 free(profiles);
568fa3fa 3915 }
229681c1 3916# endif /* HAVE_COLORSYNCREGISTERDEVICE */
568fa3fa
MS
3917
3918 ppdClose(ppd);
3919}
3920
3921
3922/*
3923 * 'apple_unregister_profiles()' - Remove color profiles for the specified
3924 * printer.
3925 */
3926
3927static void
3928apple_unregister_profiles(
3929 cupsd_printer_t *p) /* I - Printer */
3930{
64a69576
MS
3931 /*
3932 * Make sure ColorSync is available...
3933 */
3934
229681c1
MS
3935# ifdef HAVE_COLORSYNCREGISTERDEVICE
3936 if (ColorSyncUnregisterDevice != NULL)
3937 {
3938 CFUUIDRef deviceUUID; /* Printer UUID */
3939
3940 deviceUUID = ColorSyncCreateUUIDFromUInt32(_ppdHashName(p->name));
3941 /* TODO: Use printer-uuid value */
3942
3943 if (deviceUUID)
3944 {
3945 ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass, deviceUUID);
3946 CFRelease(deviceUUID);
3947 }
3948 }
3949
3950# else
64a69576
MS
3951 if (CMUnregisterColorDevice != NULL)
3952 CMUnregisterColorDevice(cmPrinterDeviceClass, _ppdHashName(p->name));
229681c1 3953# endif /* HAVE_COLORSYNCREGISTERDEVICE */
568fa3fa
MS
3954}
3955#endif /* __APPLE__ */
3956
b423cd4c 3957/*
3958 * 'apply_printer_defaults()' - Apply printer default options to a job.
3959 */
ef416fc2 3960
b423cd4c 3961static void
3962apply_printer_defaults(
3963 cupsd_printer_t *printer, /* I - Printer */
3964 cupsd_job_t *job) /* I - Job */
3965{
3966 int i, /* Looping var */
3967 num_options; /* Number of default options */
3968 cups_option_t *options, /* Default options */
f7deaa1a 3969 *option; /* Current option */
ef416fc2 3970
ef416fc2 3971
b423cd4c 3972 /*
3973 * Collect all of the default options and add the missing ones to the
3974 * job object...
3975 */
ef416fc2 3976
1f0275e3
MS
3977 for (i = printer->num_options, num_options = 0, options = NULL,
3978 option = printer->options;
b423cd4c 3979 i > 0;
3980 i --, option ++)
3981 if (!ippFindAttribute(job->attrs, option->name, IPP_TAG_ZERO))
3982 {
3983 num_options = cupsAddOption(option->name, option->value, num_options,
3984 &options);
3985 }
ef416fc2 3986
b423cd4c 3987 /*
3988 * Encode these options as attributes in the job object...
3989 */
ef416fc2 3990
b423cd4c 3991 cupsEncodeOptions2(job->attrs, num_options, options, IPP_TAG_JOB);
3992 cupsFreeOptions(num_options, options);
3993}
3994
3995
3996/*
3997 * 'authenticate_job()' - Set job authentication info.
3998 */
3999
4000static void
4001authenticate_job(cupsd_client_t *con, /* I - Client connection */
4002 ipp_attribute_t *uri) /* I - Job URI */
4003{
f7deaa1a 4004 ipp_attribute_t *attr, /* job-id attribute */
4005 *auth_info; /* auth-info attribute */
b423cd4c 4006 int jobid; /* Job ID */
4007 cupsd_job_t *job; /* Current job */
61cf44e2 4008 char scheme[HTTP_MAX_URI],
b423cd4c 4009 /* Method portion of URI */
4010 username[HTTP_MAX_URI],
4011 /* Username portion of URI */
4012 host[HTTP_MAX_URI],
4013 /* Host portion of URI */
4014 resource[HTTP_MAX_URI];
4015 /* Resource portion of URI */
4016 int port; /* Port portion of URI */
ef416fc2 4017
ef416fc2 4018
b423cd4c 4019 cupsdLogMessage(CUPSD_LOG_DEBUG2, "authenticate_job(%p[%d], %s)",
4020 con, con->http.fd, uri->values[0].string.text);
ef416fc2 4021
b423cd4c 4022 /*
4023 * Start with "everything is OK" status...
4024 */
ef416fc2 4025
b423cd4c 4026 con->response->request.status.status_code = IPP_OK;
ef416fc2 4027
b423cd4c 4028 /*
4029 * See if we have a job URI or a printer URI...
4030 */
ef416fc2 4031
b423cd4c 4032 if (!strcmp(uri->name, "printer-uri"))
4033 {
4034 /*
4035 * Got a printer URI; see if we also have a job-id attribute...
4036 */
ef416fc2 4037
b423cd4c 4038 if ((attr = ippFindAttribute(con->request, "job-id",
4039 IPP_TAG_INTEGER)) == NULL)
4040 {
4041 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 4042 _("Got a printer-uri attribute but no job-id"));
b423cd4c 4043 return;
4044 }
4045
4046 jobid = attr->values[0].integer;
4047 }
4048 else
4049 {
4050 /*
4051 * Got a job URI; parse it to get the job ID...
4052 */
ef416fc2 4053
61cf44e2
MS
4054 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
4055 sizeof(scheme), username, sizeof(username), host,
b423cd4c 4056 sizeof(host), &port, resource, sizeof(resource));
f7deaa1a 4057
b423cd4c 4058 if (strncmp(resource, "/jobs/", 6))
4059 {
4060 /*
4061 * Not a valid URI!
4062 */
ef416fc2 4063
4d301e69 4064 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri attribute \"%s\""),
b423cd4c 4065 uri->values[0].string.text);
4066 return;
4067 }
ef416fc2 4068
b423cd4c 4069 jobid = atoi(resource + 6);
4070 }
ef416fc2 4071
b423cd4c 4072 /*
4073 * See if the job exists...
4074 */
ef416fc2 4075
b423cd4c 4076 if ((job = cupsdFindJob(jobid)) == NULL)
4077 {
4078 /*
4079 * Nope - return a "not found" error...
4080 */
ef416fc2 4081
b423cd4c 4082 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 4083 _("Job #%d does not exist"), jobid);
ef416fc2 4084 return;
b423cd4c 4085 }
ef416fc2 4086
b423cd4c 4087 /*
4088 * See if the job has been completed...
4089 */
4090
4091 if (job->state_value != IPP_JOB_HELD)
ef416fc2 4092 {
4093 /*
b423cd4c 4094 * Return a "not-possible" error...
ef416fc2 4095 */
4096
b423cd4c 4097 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 4098 _("Job #%d is not held for authentication"),
b423cd4c 4099 jobid);
4100 return;
4101 }
ef416fc2 4102
b423cd4c 4103 /*
4104 * See if we have already authenticated...
4105 */
4106
f7deaa1a 4107 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
4108
4109 if (!con->username[0] && !auth_info)
b423cd4c 4110 {
db1f069b
MS
4111 cupsd_printer_t *printer; /* Job destination */
4112
4113
4114 /*
4115 * No auth data. If we need to authenticate via Kerberos, send a
4116 * HTTP auth challenge, otherwise just return an IPP error...
4117 */
4118
4119 printer = cupsdFindDest(job->dest);
4120
4121 if (printer && printer->num_auth_info_required > 0 &&
4122 !strcmp(printer->auth_info_required[0], "negotiate"))
4123 send_http_error(con, HTTP_UNAUTHORIZED, printer);
4124 else
4125 send_ipp_status(con, IPP_NOT_AUTHORIZED,
4d301e69 4126 _("No authentication information provided"));
b423cd4c 4127 return;
4128 }
4129
4130 /*
4131 * See if the job is owned by the requesting user...
4132 */
4133
4134 if (!validate_user(job, con, job->username, username, sizeof(username)))
4135 {
b0f6947b
MS
4136 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
4137 cupsdFindDest(job->dest));
b423cd4c 4138 return;
4139 }
4140
4141 /*
4142 * Save the authentication information for this job...
4143 */
4144
f7deaa1a 4145 save_auth_info(con, job, auth_info);
b423cd4c 4146
4147 /*
4148 * Reset the job-hold-until value to "no-hold"...
4149 */
4150
4151 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
4152 IPP_TAG_KEYWORD)) == NULL)
4153 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4154
4155 if (attr)
4156 {
4157 attr->value_tag = IPP_TAG_KEYWORD;
4158 cupsdSetString(&(attr->values[0].string.text), "no-hold");
ef416fc2 4159 }
b423cd4c 4160
4161 /*
4162 * Release the job and return...
4163 */
4164
4165 cupsdReleaseJob(job);
4166
75bd9771 4167 cupsdLogJob(job, CUPSD_LOG_INFO, "Authenticated by \"%s\".", con->username);
ef416fc2 4168}
4169
4170
4171/*
aaf19ab0 4172 * 'cancel_all_jobs()' - Cancel all or selected print jobs.
ef416fc2 4173 */
4174
b423cd4c 4175static void
4176cancel_all_jobs(cupsd_client_t *con, /* I - Client connection */
4177 ipp_attribute_t *uri) /* I - Job or Printer URI */
ef416fc2 4178{
aaf19ab0 4179 int i; /* Looping var */
b423cd4c 4180 http_status_t status; /* Policy status */
b423cd4c 4181 cups_ptype_t dtype; /* Destination type */
f7deaa1a 4182 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
b423cd4c 4183 userpass[HTTP_MAX_URI], /* Username portion of URI */
f7deaa1a 4184 hostname[HTTP_MAX_URI], /* Host portion of URI */
b423cd4c 4185 resource[HTTP_MAX_URI]; /* Resource portion of URI */
4186 int port; /* Port portion of URI */
4187 ipp_attribute_t *attr; /* Attribute in request */
aaf19ab0
MS
4188 const char *username = NULL; /* Username */
4189 cupsd_jobaction_t purge = CUPSD_JOB_DEFAULT;
4190 /* Purge? */
b423cd4c 4191 cupsd_printer_t *printer; /* Printer */
aaf19ab0
MS
4192 ipp_attribute_t *job_ids; /* job-ids attribute */
4193 cupsd_job_t *job; /* Job */
ef416fc2 4194
4195
b423cd4c 4196 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_all_jobs(%p[%d], %s)", con,
4197 con->http.fd, uri->values[0].string.text);
ef416fc2 4198
4199 /*
aaf19ab0 4200 * Get the jobs to cancel/purge...
ef416fc2 4201 */
4202
aaf19ab0 4203 switch (con->request->request.op.operation_id)
b423cd4c 4204 {
aaf19ab0
MS
4205 case IPP_PURGE_JOBS :
4206 /*
4207 * Get the username (if any) for the jobs we want to cancel (only if
4208 * "my-jobs" is specified...
4209 */
ef416fc2 4210
aaf19ab0
MS
4211 if ((attr = ippFindAttribute(con->request, "my-jobs",
4212 IPP_TAG_BOOLEAN)) != NULL &&
4213 attr->values[0].boolean)
4214 {
4215 if ((attr = ippFindAttribute(con->request, "requesting-user-name",
4216 IPP_TAG_NAME)) != NULL)
4217 username = attr->values[0].string.text;
4218 else
4219 {
4220 send_ipp_status(con, IPP_BAD_REQUEST,
4221 _("Missing requesting-user-name attribute"));
4222 return;
4223 }
4224 }
ef416fc2 4225
aaf19ab0
MS
4226 /*
4227 * Look for the "purge-jobs" attribute...
4228 */
4229
4230 if ((attr = ippFindAttribute(con->request, "purge-jobs",
4231 IPP_TAG_BOOLEAN)) != NULL)
4232 purge = attr->values[0].boolean ? CUPSD_JOB_PURGE : CUPSD_JOB_DEFAULT;
4233 else
4234 purge = CUPSD_JOB_PURGE;
4235 break;
4236
4237 case IPP_CANCEL_MY_JOBS :
4238 if (con->username[0])
4239 username = con->username;
4240 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
4241 IPP_TAG_NAME)) != NULL)
4242 username = attr->values[0].string.text;
4243 else
4244 {
4245 send_ipp_status(con, IPP_BAD_REQUEST,
4246 _("Missing requesting-user-name attribute"));
4247 return;
4248 }
4249
4250 default :
4251 break;
ef416fc2 4252 }
aaf19ab0
MS
4253
4254 job_ids = ippFindAttribute(con->request, "job-ids", IPP_TAG_INTEGER);
ef416fc2 4255
b423cd4c 4256 /*
aaf19ab0 4257 * See if we have a printer URI...
b423cd4c 4258 */
4259
aaf19ab0
MS
4260 if (strcmp(uri->name, "printer-uri"))
4261 {
4262 send_ipp_status(con, IPP_BAD_REQUEST,
4263 _("The printer-uri attribute is required"));
4264 return;
4265 }
ef416fc2 4266
4267 /*
b423cd4c 4268 * And if the destination is valid...
ef416fc2 4269 */
4270
f7deaa1a 4271 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 4272 {
4273 /*
b423cd4c 4274 * Bad URI?
ef416fc2 4275 */
4276
f7deaa1a 4277 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text,
4278 scheme, sizeof(scheme), userpass, sizeof(userpass),
4279 hostname, sizeof(hostname), &port,
4280 resource, sizeof(resource));
4281
b423cd4c 4282 if ((!strncmp(resource, "/printers/", 10) && resource[10]) ||
4283 (!strncmp(resource, "/classes/", 9) && resource[9]))
4284 {
4285 send_ipp_status(con, IPP_NOT_FOUND,
4286 _("The printer or class was not found."));
4287 return;
4288 }
ef416fc2 4289
b423cd4c 4290 /*
4291 * Check policy...
4292 */
4293
4294 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
4295 {
f899b121 4296 send_http_error(con, status, NULL);
b423cd4c 4297 return;
4298 }
ef416fc2 4299
aaf19ab0
MS
4300 if (job_ids)
4301 {
4302 for (i = 0; i < job_ids->num_values; i ++)
4303 {
4220952d 4304 if (!cupsdFindJob(job_ids->values[i].integer))
aaf19ab0
MS
4305 break;
4306 }
4307
4308 if (i < job_ids->num_values)
4309 {
4310 send_ipp_status(con, IPP_NOT_FOUND, _("job-id %d not found."),
4311 job_ids->values[i].integer);
4312 return;
4313 }
ef416fc2 4314
aaf19ab0
MS
4315 for (i = 0; i < job_ids->num_values; i ++)
4316 {
4317 job = cupsdFindJob(job_ids->values[i].integer);
ef416fc2 4318
aaf19ab0
MS
4319 cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
4320 purge == CUPSD_JOB_PURGE ? "Job purged by user." :
4321 "Job canceled by user.");
4322 }
4323
4324 cupsdLogMessage(CUPSD_LOG_INFO, "Selected jobs were %s by \"%s\".",
4325 purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
4326 get_username(con));
4327 }
4328 else
4329 {
4330 /*
4331 * Cancel all jobs on all printers...
4332 */
4333
4334 cupsdCancelJobs(NULL, username, purge);
4335
4336 cupsdLogMessage(CUPSD_LOG_INFO, "All jobs were %s by \"%s\".",
4337 purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
4338 get_username(con));
4339 }
ef416fc2 4340 }
b423cd4c 4341 else
ef416fc2 4342 {
4343 /*
b423cd4c 4344 * Check policy...
ef416fc2 4345 */
4346
f7deaa1a 4347 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
4348 NULL)) != HTTP_OK)
b423cd4c 4349 {
f899b121 4350 send_http_error(con, status, printer);
b423cd4c 4351 return;
4352 }
ef416fc2 4353
aaf19ab0
MS
4354 if (job_ids)
4355 {
4356 for (i = 0; i < job_ids->num_values; i ++)
4357 {
4358 if ((job = cupsdFindJob(job_ids->values[i].integer)) == NULL ||
4359 strcasecmp(job->dest, printer->name))
4360 break;
4361 }
b423cd4c 4362
aaf19ab0
MS
4363 if (i < job_ids->num_values)
4364 {
4365 send_ipp_status(con, IPP_NOT_FOUND, _("job-id %d not found."),
4366 job_ids->values[i].integer);
4367 return;
4368 }
b423cd4c 4369
aaf19ab0
MS
4370 for (i = 0; i < job_ids->num_values; i ++)
4371 {
4372 job = cupsdFindJob(job_ids->values[i].integer);
4373
4374 cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
4375 purge == CUPSD_JOB_PURGE ? "Job purged by user." :
4376 "Job canceled by user.");
4377 }
4378
4379 cupsdLogMessage(CUPSD_LOG_INFO, "Selected jobs were %s by \"%s\".",
4380 purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
4381 get_username(con));
4382 }
4383 else
4384 {
4385 /*
4386 * Cancel all of the jobs on the named printer...
4387 */
4388
4389 cupsdCancelJobs(printer->name, username, purge);
4390
4391 cupsdLogMessage(CUPSD_LOG_INFO, "All jobs on \"%s\" were %s by \"%s\".",
4392 printer->name,
4393 purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
4394 get_username(con));
4395 }
ef416fc2 4396 }
4397
b423cd4c 4398 con->response->request.status.status_code = IPP_OK;
4399}
4400
4401
4402/*
4403 * 'cancel_job()' - Cancel a print job.
4404 */
4405
4406static void
4407cancel_job(cupsd_client_t *con, /* I - Client connection */
4408 ipp_attribute_t *uri) /* I - Job or Printer URI */
4409{
4410 ipp_attribute_t *attr; /* Current attribute */
4411 int jobid; /* Job ID */
f7deaa1a 4412 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
b423cd4c 4413 username[HTTP_MAX_URI], /* Username portion of URI */
4414 host[HTTP_MAX_URI], /* Host portion of URI */
4415 resource[HTTP_MAX_URI]; /* Resource portion of URI */
4416 int port; /* Port portion of URI */
4417 cupsd_job_t *job; /* Job information */
bc44d920 4418 cups_ptype_t dtype; /* Destination type (printer/class) */
b423cd4c 4419 cupsd_printer_t *printer; /* Printer data */
68b10830 4420 cupsd_jobaction_t purge; /* Purge the job? */
b423cd4c 4421
4422
4423 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_job(%p[%d], %s)", con,
4424 con->http.fd, uri->values[0].string.text);
4425
ef416fc2 4426 /*
b423cd4c 4427 * See if we have a job URI or a printer URI...
ef416fc2 4428 */
4429
b423cd4c 4430 if (!strcmp(uri->name, "printer-uri"))
4431 {
4432 /*
4433 * Got a printer URI; see if we also have a job-id attribute...
4434 */
4435
4436 if ((attr = ippFindAttribute(con->request, "job-id",
4437 IPP_TAG_INTEGER)) == NULL)
4438 {
4439 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 4440 _("Got a printer-uri attribute but no job-id"));
b423cd4c 4441 return;
4442 }
4443
4444 if ((jobid = attr->values[0].integer) == 0)
ef416fc2 4445 {
4446 /*
b423cd4c 4447 * Find the current job on the specified printer...
ef416fc2 4448 */
4449
f7deaa1a 4450 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 4451 {
4452 /*
b423cd4c 4453 * Bad URI...
ef416fc2 4454 */
4455
b423cd4c 4456 send_ipp_status(con, IPP_NOT_FOUND,
4457 _("The printer or class was not found."));
4458 return;
ef416fc2 4459 }
4460
4461 /*
b9faaae1 4462 * See if there are any pending jobs...
ef416fc2 4463 */
4464
b9faaae1
MS
4465 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
4466 job;
4467 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
4468 if (job->state_value <= IPP_JOB_PROCESSING &&
4469 !strcasecmp(job->dest, printer->name))
4470 break;
4471
4472 if (job)
4473 jobid = job->id;
ef416fc2 4474 else
ef416fc2 4475 {
4476 /*
b9faaae1 4477 * No, try stopped jobs...
ef416fc2 4478 */
f7deaa1a 4479
b9faaae1 4480 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
b423cd4c 4481 job;
4482 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
b9faaae1 4483 if (job->state_value == IPP_JOB_STOPPED &&
f7deaa1a 4484 !strcasecmp(job->dest, printer->name))
b423cd4c 4485 break;
ef416fc2 4486
b423cd4c 4487 if (job)
4488 jobid = job->id;
4489 else
ef416fc2 4490 {
4d301e69 4491 send_ipp_status(con, IPP_NOT_POSSIBLE, _("No active jobs on %s"),
b9faaae1
MS
4492 printer->name);
4493 return;
b423cd4c 4494 }
4495 }
4496 }
4497 }
4498 else
4499 {
4500 /*
4501 * Got a job URI; parse it to get the job ID...
4502 */
ef416fc2 4503
f7deaa1a 4504 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
4505 sizeof(scheme), username, sizeof(username), host,
b423cd4c 4506 sizeof(host), &port, resource, sizeof(resource));
f7deaa1a 4507
b423cd4c 4508 if (strncmp(resource, "/jobs/", 6))
4509 {
4510 /*
4511 * Not a valid URI!
4512 */
ef416fc2 4513
b423cd4c 4514 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 4515 _("Bad job-uri attribute \"%s\""),
b423cd4c 4516 uri->values[0].string.text);
4517 return;
4518 }
4519
4520 jobid = atoi(resource + 6);
4521 }
4522
0a682745
MS
4523 /*
4524 * Look for the "purge-job" attribute...
4525 */
4526
4527 if ((attr = ippFindAttribute(con->request, "purge-job",
4528 IPP_TAG_BOOLEAN)) != NULL)
68b10830 4529 purge = attr->values[0].boolean ? CUPSD_JOB_PURGE : CUPSD_JOB_DEFAULT;
0a682745 4530 else
68b10830 4531 purge = CUPSD_JOB_DEFAULT;
0a682745 4532
b423cd4c 4533 /*
4534 * See if the job exists...
4535 */
4536
4537 if ((job = cupsdFindJob(jobid)) == NULL)
4538 {
4539 /*
4540 * Nope - return a "not found" error...
4541 */
4542
4d301e69 4543 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
b423cd4c 4544 return;
4545 }
4546
4547 /*
4548 * See if the job is owned by the requesting user...
4549 */
4550
4551 if (!validate_user(job, con, job->username, username, sizeof(username)))
4552 {
b0f6947b
MS
4553 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
4554 cupsdFindDest(job->dest));
b423cd4c 4555 return;
4556 }
4557
4558 /*
d09495fa 4559 * See if the job is already completed, canceled, or aborted; if so,
b423cd4c 4560 * we can't cancel...
4561 */
4562
68b10830 4563 if (job->state_value >= IPP_JOB_CANCELED && purge != CUPSD_JOB_PURGE)
b423cd4c 4564 {
e1d6a774 4565 switch (job->state_value)
4566 {
d09495fa 4567 case IPP_JOB_CANCELED :
e1d6a774 4568 send_ipp_status(con, IPP_NOT_POSSIBLE,
d09495fa 4569 _("Job #%d is already canceled - can\'t cancel."),
e1d6a774 4570 jobid);
4571 break;
4572
4573 case IPP_JOB_ABORTED :
4574 send_ipp_status(con, IPP_NOT_POSSIBLE,
4575 _("Job #%d is already aborted - can\'t cancel."),
4576 jobid);
4577 break;
4578
4579 default :
4580 send_ipp_status(con, IPP_NOT_POSSIBLE,
4581 _("Job #%d is already completed - can\'t cancel."),
4582 jobid);
4583 break;
4584 }
4585
b423cd4c 4586 return;
4587 }
ef416fc2 4588
b423cd4c 4589 /*
4590 * Cancel the job and return...
4591 */
ef416fc2 4592
b9faaae1 4593 cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
68b10830
MS
4594 purge == CUPSD_JOB_PURGE ? "Job purged by \"%s\"" :
4595 "Job canceled by \"%s\"",
b9faaae1 4596 username);
b423cd4c 4597 cupsdCheckJobs();
ef416fc2 4598
68b10830 4599 if (purge == CUPSD_JOB_PURGE)
0a682745 4600 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Purged by \"%s\".", jobid,
75bd9771 4601 username);
0a682745
MS
4602 else
4603 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Canceled by \"%s\".", jobid,
75bd9771 4604 username);
ef416fc2 4605
b423cd4c 4606 con->response->request.status.status_code = IPP_OK;
4607}
ef416fc2 4608
ef416fc2 4609
b423cd4c 4610/*
4611 * 'cancel_subscription()' - Cancel a subscription.
4612 */
ef416fc2 4613
b423cd4c 4614static void
4615cancel_subscription(
4616 cupsd_client_t *con, /* I - Client connection */
4617 int sub_id) /* I - Subscription ID */
4618{
4619 http_status_t status; /* Policy status */
4620 cupsd_subscription_t *sub; /* Subscription */
ef416fc2 4621
ef416fc2 4622
b423cd4c 4623 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4624 "cancel_subscription(con=%p[%d], sub_id=%d)",
4625 con, con->http.fd, sub_id);
ef416fc2 4626
b423cd4c 4627 /*
4628 * Is the subscription ID valid?
4629 */
ef416fc2 4630
b423cd4c 4631 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
4632 {
4633 /*
4634 * Bad subscription ID...
4635 */
ef416fc2 4636
b423cd4c 4637 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 4638 _("notify-subscription-id %d no good"), sub_id);
b423cd4c 4639 return;
4640 }
ef416fc2 4641
b423cd4c 4642 /*
4643 * Check policy...
4644 */
ef416fc2 4645
b423cd4c 4646 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
4647 DefaultPolicyPtr,
4648 con, sub->owner)) != HTTP_OK)
4649 {
f899b121 4650 send_http_error(con, status, sub->dest);
b423cd4c 4651 return;
4652 }
ef416fc2 4653
b423cd4c 4654 /*
4655 * Cancel the subscription...
4656 */
ef416fc2 4657
b423cd4c 4658 cupsdDeleteSubscription(sub, 1);
ef416fc2 4659
b423cd4c 4660 con->response->request.status.status_code = IPP_OK;
ef416fc2 4661}
4662
4663
3dfe78b3
MS
4664/*
4665 * 'check_rss_recipient()' - Check that we do not have a duplicate RSS feed URI.
4666 */
4667
4668static int /* O - 1 if OK, 0 if not */
4669check_rss_recipient(
4670 const char *recipient) /* I - Recipient URI */
4671{
4672 cupsd_subscription_t *sub; /* Current subscription */
4673
4674
4675 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
4676 sub;
4677 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
4678 if (sub->recipient)
4679 {
4680 /*
4681 * Compare the URIs up to the first ?...
4682 */
4683
4684 const char *r1, *r2;
4685
4686 for (r1 = recipient, r2 = sub->recipient;
4687 *r1 == *r2 && *r1 && *r1 != '?' && *r2 && *r2 != '?';
4688 r1 ++, r2 ++);
4689
4690 if (*r1 == *r2)
4691 return (0);
4692 }
4693
4694 return (1);
4695}
4696
4697
ef416fc2 4698/*
b423cd4c 4699 * 'check_quotas()' - Check quotas for a printer and user.
ef416fc2 4700 */
4701
6c48a6ca
MS
4702static int /* O - 1 if OK, 0 if forbidden,
4703 -1 if limit reached */
b423cd4c 4704check_quotas(cupsd_client_t *con, /* I - Client connection */
4705 cupsd_printer_t *p) /* I - Printer or class */
ef416fc2 4706{
10d09e33
MS
4707 char username[33], /* Username */
4708 *name; /* Current user name */
b423cd4c 4709 cupsd_quota_t *q; /* Quota data */
7594b224 4710#ifdef HAVE_MBR_UID_TO_UUID
4711 /*
4712 * Use Apple membership APIs which require that all names represent
4713 * valid user account or group records accessible by the server.
4714 */
4715
4716 uuid_t usr_uuid; /* UUID for job requesting user */
4717 uuid_t usr2_uuid; /* UUID for ACL user name entry */
4718 uuid_t grp_uuid; /* UUID for ACL group name entry */
4719 int mbr_err; /* Error from membership function */
4720 int is_member; /* Is this user a member? */
4721#else
4722 /*
4723 * Use standard POSIX APIs for checking users and groups...
4724 */
4725
b423cd4c 4726 struct passwd *pw; /* User password data */
7594b224 4727#endif /* HAVE_MBR_UID_TO_UUID */
ef416fc2 4728
4729
b423cd4c 4730 cupsdLogMessage(CUPSD_LOG_DEBUG2, "check_quotas(%p[%d], %p[%s])",
4731 con, con->http.fd, p, p->name);
ef416fc2 4732
b423cd4c 4733 /*
4734 * Figure out who is printing...
4735 */
4736
4737 strlcpy(username, get_username(con), sizeof(username));
4738
4739 /*
4740 * Check global active job limits for printers and users...
4741 */
4742
4743 if (MaxJobsPerPrinter)
ef416fc2 4744 {
b423cd4c 4745 /*
4746 * Check if there are too many pending jobs on this printer...
4747 */
4748
4749 if (cupsdGetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
4750 {
4751 cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for printer \"%s\"...",
4752 p->name);
6c48a6ca 4753 return (-1);
b423cd4c 4754 }
4755 }
4756
4757 if (MaxJobsPerUser)
4758 {
4759 /*
4760 * Check if there are too many pending jobs for this user...
4761 */
4762
4763 if (cupsdGetUserJobCount(username) >= MaxJobsPerUser)
4764 {
4765 cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for user \"%s\"...",
4766 username);
6c48a6ca 4767 return (-1);
b423cd4c 4768 }
ef416fc2 4769 }
4770
4771 /*
b423cd4c 4772 * Check against users...
ef416fc2 4773 */
4774
10d09e33 4775 if (cupsArrayCount(p->users) == 0 && p->k_limit == 0 && p->page_limit == 0)
b423cd4c 4776 return (1);
4777
10d09e33 4778 if (cupsArrayCount(p->users))
b423cd4c 4779 {
7594b224 4780#ifdef HAVE_MBR_UID_TO_UUID
4781 /*
4782 * Get UUID for job requesting user...
4783 */
4784
4785 if (mbr_user_name_to_uuid((char *)username, usr_uuid))
4786 {
4787 /*
4788 * Unknown user...
4789 */
4790
4791 cupsdLogMessage(CUPSD_LOG_DEBUG,
4792 "check_quotas: UUID lookup failed for user \"%s\"",
4793 username);
4794 cupsdLogMessage(CUPSD_LOG_INFO,
4795 "Denying user \"%s\" access to printer \"%s\" "
4796 "(unknown user)...",
4797 username, p->name);
4798 return (0);
4799 }
4800#else
4801 /*
4802 * Get UID and GID of requesting user...
4803 */
4804
b423cd4c 4805 pw = getpwnam(username);
4806 endpwent();
7594b224 4807#endif /* HAVE_MBR_UID_TO_UUID */
b423cd4c 4808
10d09e33
MS
4809 for (name = (char *)cupsArrayFirst(p->users);
4810 name;
4811 name = (char *)cupsArrayNext(p->users))
4812 if (name[0] == '@')
b423cd4c 4813 {
4814 /*
4815 * Check group membership...
4816 */
4817
7594b224 4818#ifdef HAVE_MBR_UID_TO_UUID
10d09e33 4819 if (name[1] == '#')
c9fc04c6 4820 {
10d09e33 4821 if (uuid_parse(name + 2, grp_uuid))
c9fc04c6
MS
4822 uuid_clear(grp_uuid);
4823 }
10d09e33 4824 else if ((mbr_err = mbr_group_name_to_uuid(name + 1, grp_uuid)) != 0)
7594b224 4825 {
4826 /*
4827 * Invalid ACL entries are ignored for matching; just record a
4828 * warning in the log...
4829 */
4830
4831 cupsdLogMessage(CUPSD_LOG_DEBUG,
4832 "check_quotas: UUID lookup failed for ACL entry "
10d09e33 4833 "\"%s\" (err=%d)", name, mbr_err);
7594b224 4834 cupsdLogMessage(CUPSD_LOG_WARN,
4835 "Access control entry \"%s\" not a valid group name; "
10d09e33 4836 "entry ignored", name);
7594b224 4837 }
7594b224 4838
c9fc04c6
MS
4839 if ((mbr_err = mbr_check_membership(usr_uuid, grp_uuid,
4840 &is_member)) != 0)
4841 {
4842 /*
4843 * At this point, there should be no errors, but check anyways...
7594b224 4844 */
4845
c9fc04c6
MS
4846 cupsdLogMessage(CUPSD_LOG_DEBUG,
4847 "check_quotas: group \"%s\" membership check "
10d09e33 4848 "failed (err=%d)", name + 1, mbr_err);
c9fc04c6 4849 is_member = 0;
7594b224 4850 }
c9fc04c6
MS
4851
4852 /*
4853 * Stop if we found a match...
4854 */
4855
4856 if (is_member)
4857 break;
4858
7594b224 4859#else
10d09e33 4860 if (cupsdCheckGroup(username, pw, name + 1))
b423cd4c 4861 break;
7594b224 4862#endif /* HAVE_MBR_UID_TO_UUID */
4863 }
4864#ifdef HAVE_MBR_UID_TO_UUID
4865 else
4866 {
10d09e33 4867 if (name[0] == '#')
c9fc04c6 4868 {
10d09e33 4869 if (uuid_parse(name + 1, usr2_uuid))
c9fc04c6
MS
4870 uuid_clear(usr2_uuid);
4871 }
10d09e33 4872 else if ((mbr_err = mbr_user_name_to_uuid(name, usr2_uuid)) != 0)
7594b224 4873 {
4874 /*
4875 * Invalid ACL entries are ignored for matching; just record a
4876 * warning in the log...
4877 */
4878
4879 cupsdLogMessage(CUPSD_LOG_DEBUG,
4880 "check_quotas: UUID lookup failed for ACL entry "
10d09e33 4881 "\"%s\" (err=%d)", name, mbr_err);
7594b224 4882 cupsdLogMessage(CUPSD_LOG_WARN,
4883 "Access control entry \"%s\" not a valid user name; "
10d09e33 4884 "entry ignored", name);
7594b224 4885 }
7594b224 4886
d1c13e16 4887 if (!uuid_compare(usr_uuid, usr2_uuid))
c9fc04c6 4888 break;
b423cd4c 4889 }
7594b224 4890#else
10d09e33 4891 else if (!strcasecmp(username, name))
b423cd4c 4892 break;
7594b224 4893#endif /* HAVE_MBR_UID_TO_UUID */
b423cd4c 4894
10d09e33 4895 if ((name != NULL) == p->deny_users)
ef416fc2 4896 {
b423cd4c 4897 cupsdLogMessage(CUPSD_LOG_INFO,
4898 "Denying user \"%s\" access to printer \"%s\"...",
4899 username, p->name);
4900 return (0);
ef416fc2 4901 }
b423cd4c 4902 }
ef416fc2 4903
4904 /*
b423cd4c 4905 * Check quotas...
ef416fc2 4906 */
4907
b423cd4c 4908 if (p->k_limit || p->page_limit)
4909 {
4910 if ((q = cupsdUpdateQuota(p, username, 0, 0)) == NULL)
4911 {
4912 cupsdLogMessage(CUPSD_LOG_ERROR,
4d301e69 4913 "Unable to allocate quota data for user \"%s\"",
b423cd4c 4914 username);
bc44d920 4915 return (-1);
b423cd4c 4916 }
ef416fc2 4917
b423cd4c 4918 if ((q->k_count >= p->k_limit && p->k_limit) ||
4919 (q->page_count >= p->page_limit && p->page_limit))
4920 {
4921 cupsdLogMessage(CUPSD_LOG_INFO, "User \"%s\" is over the quota limit...",
4922 username);
bc44d920 4923 return (-1);
b423cd4c 4924 }
4925 }
4926
4927 /*
4928 * If we have gotten this far, we're done!
4929 */
4930
4931 return (1);
ef416fc2 4932}
4933
4934
aaf19ab0
MS
4935/*
4936 * 'close_job()' - Close a multi-file job.
4937 */
4938
4939static void
4940close_job(cupsd_client_t *con, /* I - Client connection */
4941 ipp_attribute_t *uri) /* I - Printer URI */
4942{
4943 cupsd_job_t *job; /* Job */
aaf19ab0
MS
4944 ipp_attribute_t *attr; /* Attribute */
4945 char job_uri[HTTP_MAX_URI],
4946 /* Job URI */
4947 username[256]; /* User name */
4948
4949
4950 cupsdLogMessage(CUPSD_LOG_DEBUG2, "close_job(%p[%d], %s)", con,
4951 con->http.fd, uri->values[0].string.text);
4952
4953 /*
4954 * See if we have a job URI or a printer URI...
4955 */
4956
4957 if (strcmp(uri->name, "printer-uri"))
4958 {
4959 /*
4960 * job-uri is not supported by Close-Job!
4961 */
4962
4963 send_ipp_status(con, IPP_BAD_REQUEST,
4964 _("Close-Job doesn't support the job-uri attribute."));
4965 return;
4966 }
4967
4968 /*
4969 * Got a printer URI; see if we also have a job-id attribute...
4970 */
4971
4972 if ((attr = ippFindAttribute(con->request, "job-id",
4973 IPP_TAG_INTEGER)) == NULL)
4974 {
4975 send_ipp_status(con, IPP_BAD_REQUEST,
4976 _("Got a printer-uri attribute but no job-id"));
4977 return;
4978 }
4979
4980 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
4981 {
4982 /*
4983 * Nope - return a "not found" error...
4984 */
4985
4986 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"),
4987 attr->values[0].integer);
4988 return;
4989 }
4990
aaf19ab0
MS
4991 /*
4992 * See if the job is owned by the requesting user...
4993 */
4994
4995 if (!validate_user(job, con, job->username, username, sizeof(username)))
4996 {
4997 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
4998 cupsdFindDest(job->dest));
4999 return;
5000 }
5001
5002 /*
5003 * Add any ending sheet...
5004 */
5005
5006 if (cupsdTimeoutJob(job))
5007 return;
5008
5009 if (job->state_value == IPP_JOB_STOPPED)
5010 {
5011 job->state->values[0].integer = IPP_JOB_PENDING;
5012 job->state_value = IPP_JOB_PENDING;
5013 }
5014 else if (job->state_value == IPP_JOB_HELD)
5015 {
5016 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
5017 IPP_TAG_KEYWORD)) == NULL)
5018 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
5019
5020 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
5021 {
5022 job->state->values[0].integer = IPP_JOB_PENDING;
5023 job->state_value = IPP_JOB_PENDING;
5024 }
5025 }
5026
5027 job->dirty = 1;
5028 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
5029
5030 /*
5031 * Fill in the response info...
5032 */
5033
5034 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
5035 LocalPort, job->id);
5036 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
5037 job_uri);
5038
5039 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
5040
5041 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
5042 job->state_value);
5043
5044 add_job_state_reasons(con, job);
5045
5046 con->response->request.status.status_code = IPP_OK;
5047
5048 /*
5049 * Start the job if necessary...
5050 */
5051
5052 cupsdCheckJobs();
5053}
5054
5055
ef416fc2 5056/*
b423cd4c 5057 * 'copy_attribute()' - Copy a single attribute.
ef416fc2 5058 */
5059
b423cd4c 5060static ipp_attribute_t * /* O - New attribute */
5061copy_attribute(
5062 ipp_t *to, /* O - Destination request/response */
5063 ipp_attribute_t *attr, /* I - Attribute to copy */
5064 int quickcopy) /* I - Do a quick copy? */
ef416fc2 5065{
b423cd4c 5066 int i; /* Looping var */
5067 ipp_attribute_t *toattr; /* Destination attribute */
ef416fc2 5068
5069
5070 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b423cd4c 5071 "copy_attribute(%p, %p[%s,%x,%x])", to, attr,
5072 attr->name ? attr->name : "(null)", attr->group_tag,
5073 attr->value_tag);
ef416fc2 5074
b423cd4c 5075 switch (attr->value_tag & ~IPP_TAG_COPY)
5076 {
5077 case IPP_TAG_ZERO :
5078 toattr = ippAddSeparator(to);
5079 break;
ef416fc2 5080
b423cd4c 5081 case IPP_TAG_INTEGER :
5082 case IPP_TAG_ENUM :
5083 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
5084 attr->name, attr->num_values, NULL);
ef416fc2 5085
b423cd4c 5086 for (i = 0; i < attr->num_values; i ++)
5087 toattr->values[i].integer = attr->values[i].integer;
5088 break;
ef416fc2 5089
b423cd4c 5090 case IPP_TAG_BOOLEAN :
5091 toattr = ippAddBooleans(to, attr->group_tag, attr->name,
5092 attr->num_values, NULL);
ef416fc2 5093
b423cd4c 5094 for (i = 0; i < attr->num_values; i ++)
5095 toattr->values[i].boolean = attr->values[i].boolean;
5096 break;
ef416fc2 5097
b423cd4c 5098 case IPP_TAG_STRING :
5099 case IPP_TAG_TEXT :
5100 case IPP_TAG_NAME :
5101 case IPP_TAG_KEYWORD :
5102 case IPP_TAG_URI :
5103 case IPP_TAG_URISCHEME :
5104 case IPP_TAG_CHARSET :
5105 case IPP_TAG_LANGUAGE :
5106 case IPP_TAG_MIMETYPE :
5107 toattr = ippAddStrings(to, attr->group_tag,
5108 (ipp_tag_t)(attr->value_tag | quickcopy),
5109 attr->name, attr->num_values, NULL, NULL);
ef416fc2 5110
b423cd4c 5111 if (quickcopy)
5112 {
5113 for (i = 0; i < attr->num_values; i ++)
5114 toattr->values[i].string.text = attr->values[i].string.text;
5115 }
426c6a59 5116 else if (attr->value_tag & IPP_TAG_COPY)
b423cd4c 5117 {
5118 for (i = 0; i < attr->num_values; i ++)
bc44d920 5119 toattr->values[i].string.text =
5120 _cupsStrAlloc(attr->values[i].string.text);
b423cd4c 5121 }
426c6a59
MS
5122 else
5123 {
5124 for (i = 0; i < attr->num_values; i ++)
5125 toattr->values[i].string.text =
5126 _cupsStrRetain(attr->values[i].string.text);
5127 }
b423cd4c 5128 break;
ef416fc2 5129
b423cd4c 5130 case IPP_TAG_DATE :
5131 toattr = ippAddDate(to, attr->group_tag, attr->name,
5132 attr->values[0].date);
5133 break;
ef416fc2 5134
b423cd4c 5135 case IPP_TAG_RESOLUTION :
5136 toattr = ippAddResolutions(to, attr->group_tag, attr->name,
5137 attr->num_values, IPP_RES_PER_INCH,
5138 NULL, NULL);
ef416fc2 5139
b423cd4c 5140 for (i = 0; i < attr->num_values; i ++)
5141 {
5142 toattr->values[i].resolution.xres = attr->values[i].resolution.xres;
5143 toattr->values[i].resolution.yres = attr->values[i].resolution.yres;
5144 toattr->values[i].resolution.units = attr->values[i].resolution.units;
5145 }
5146 break;
ef416fc2 5147
b423cd4c 5148 case IPP_TAG_RANGE :
5149 toattr = ippAddRanges(to, attr->group_tag, attr->name,
5150 attr->num_values, NULL, NULL);
ef416fc2 5151
b423cd4c 5152 for (i = 0; i < attr->num_values; i ++)
5153 {
5154 toattr->values[i].range.lower = attr->values[i].range.lower;
5155 toattr->values[i].range.upper = attr->values[i].range.upper;
5156 }
5157 break;
ef416fc2 5158
b423cd4c 5159 case IPP_TAG_TEXTLANG :
5160 case IPP_TAG_NAMELANG :
5161 toattr = ippAddStrings(to, attr->group_tag,
5162 (ipp_tag_t)(attr->value_tag | quickcopy),
5163 attr->name, attr->num_values, NULL, NULL);
ef416fc2 5164
b423cd4c 5165 if (quickcopy)
5166 {
5167 for (i = 0; i < attr->num_values; i ++)
5168 {
5169 toattr->values[i].string.charset = attr->values[i].string.charset;
5170 toattr->values[i].string.text = attr->values[i].string.text;
5171 }
5172 }
426c6a59 5173 else if (attr->value_tag & IPP_TAG_COPY)
b423cd4c 5174 {
5175 for (i = 0; i < attr->num_values; i ++)
5176 {
5177 if (!i)
5178 toattr->values[i].string.charset =
757d2cad 5179 _cupsStrAlloc(attr->values[i].string.charset);
b423cd4c 5180 else
5181 toattr->values[i].string.charset =
5182 toattr->values[0].string.charset;
ef416fc2 5183
bc44d920 5184 toattr->values[i].string.text =
5185 _cupsStrAlloc(attr->values[i].string.text);
b423cd4c 5186 }
5187 }
426c6a59
MS
5188 else
5189 {
5190 for (i = 0; i < attr->num_values; i ++)
5191 {
5192 if (!i)
5193 toattr->values[i].string.charset =
5194 _cupsStrRetain(attr->values[i].string.charset);
5195 else
5196 toattr->values[i].string.charset =
5197 toattr->values[0].string.charset;
5198
5199 toattr->values[i].string.text =
5200 _cupsStrRetain(attr->values[i].string.text);
5201 }
5202 }
b423cd4c 5203 break;
ef416fc2 5204
b423cd4c 5205 case IPP_TAG_BEGIN_COLLECTION :
5206 toattr = ippAddCollections(to, attr->group_tag, attr->name,
5207 attr->num_values, NULL);
ef416fc2 5208
b423cd4c 5209 for (i = 0; i < attr->num_values; i ++)
5210 {
aaf19ab0
MS
5211 toattr->values[i].collection = attr->values[i].collection;
5212 attr->values[i].collection->use ++;
b423cd4c 5213 }
ef416fc2 5214 break;
ef416fc2 5215
b423cd4c 5216 default :
5217 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
5218 attr->name, attr->num_values, NULL);
ef416fc2 5219
b423cd4c 5220 for (i = 0; i < attr->num_values; i ++)
5221 {
5222 toattr->values[i].unknown.length = attr->values[i].unknown.length;
ef416fc2 5223
b423cd4c 5224 if (toattr->values[i].unknown.length > 0)
5225 {
5226 if ((toattr->values[i].unknown.data =
5227 malloc(toattr->values[i].unknown.length)) == NULL)
5228 toattr->values[i].unknown.length = 0;
5229 else
5230 memcpy(toattr->values[i].unknown.data,
5231 attr->values[i].unknown.data,
5232 toattr->values[i].unknown.length);
5233 }
5234 }
5235 break; /* anti-compiler-warning-code */
5236 }
ef416fc2 5237
b423cd4c 5238 return (toattr);
5239}
5240
5241
5242/*
5243 * 'copy_attrs()' - Copy attributes from one request to another.
5244 */
5245
5246static void
5247copy_attrs(ipp_t *to, /* I - Destination request */
5248 ipp_t *from, /* I - Source request */
5249 cups_array_t *ra, /* I - Requested attributes */
5250 ipp_tag_t group, /* I - Group to copy */
10d09e33
MS
5251 int quickcopy, /* I - Do a quick copy? */
5252 cups_array_t *exclude) /* I - Attributes to exclude? */
b423cd4c 5253{
5254 ipp_attribute_t *fromattr; /* Source attribute */
ef416fc2 5255
ef416fc2 5256
b423cd4c 5257 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5258 "copy_attrs(to=%p, from=%p, ra=%p, group=%x, quickcopy=%d)",
5259 to, from, ra, group, quickcopy);
ef416fc2 5260
b423cd4c 5261 if (!to || !from)
5262 return;
e00b005a 5263
b423cd4c 5264 for (fromattr = from->attrs; fromattr; fromattr = fromattr->next)
ef416fc2 5265 {
5266 /*
b423cd4c 5267 * Filter attributes as needed...
ef416fc2 5268 */
5269
2abf387c 5270 if ((group != IPP_TAG_ZERO && fromattr->group_tag != group &&
5271 fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
b423cd4c 5272 continue;
5273
10d09e33
MS
5274 if (exclude &&
5275 (cupsArrayFind(exclude, fromattr->name) ||
5276 cupsArrayFind(exclude, "all")))
5277 {
5278 /*
5279 * We need to exclude this attribute for security reasons; we require the
5280 * job-id and job-printer-uri attributes regardless of the security
5281 * settings for IPP conformance.
5282 *
5283 * Subscription attribute security is handled by copy_subscription_attrs().
5284 */
5285
5286 if (strcmp(fromattr->name, "job-id") &&
5287 strcmp(fromattr->name, "job-printer-uri"))
5288 continue;
5289 }
5290
b423cd4c 5291 if (!ra || cupsArrayFind(ra, fromattr->name))
d2354e63
MS
5292 {
5293 /*
5294 * Don't send collection attributes by default to IPP/1.x clients
54afec33
MS
5295 * since many do not support collections. Also don't send
5296 * media-col-database unless specifically requested by the client.
d2354e63
MS
5297 */
5298
5299 if (fromattr->value_tag == IPP_TAG_BEGIN_COLLECTION &&
54afec33
MS
5300 !ra &&
5301 (to->request.status.version[0] == 1 ||
5302 !strcmp(fromattr->name, "media-col-database")))
d2354e63
MS
5303 continue;
5304
b423cd4c 5305 copy_attribute(to, fromattr, quickcopy);
d2354e63 5306 }
ef416fc2 5307 }
b423cd4c 5308}
ef416fc2 5309
ef416fc2 5310
b423cd4c 5311/*
5312 * 'copy_banner()' - Copy a banner file to the requests directory for the
5313 * specified job.
5314 */
ef416fc2 5315
b423cd4c 5316static int /* O - Size of banner file in kbytes */
5317copy_banner(cupsd_client_t *con, /* I - Client connection */
5318 cupsd_job_t *job, /* I - Job information */
5319 const char *name) /* I - Name of banner */
5320{
5321 int i; /* Looping var */
5322 int kbytes; /* Size of banner file in kbytes */
5323 char filename[1024]; /* Job filename */
5324 cupsd_banner_t *banner; /* Pointer to banner */
5325 cups_file_t *in; /* Input file */
5326 cups_file_t *out; /* Output file */
5327 int ch; /* Character from file */
5328 char attrname[255], /* Name of attribute */
5329 *s; /* Pointer into name */
5330 ipp_attribute_t *attr; /* Attribute */
ef416fc2 5331
ef416fc2 5332
3dfe78b3
MS
5333 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5334 "copy_banner(con=%p[%d], job=%p[%d], name=\"%s\")",
09a101d6 5335 con, con ? con->http.fd : -1, job, job->id,
5336 name ? name : "(null)");
ef416fc2 5337
b423cd4c 5338 /*
5339 * Find the banner; return if not found or "none"...
5340 */
ef416fc2 5341
b423cd4c 5342 if (!name || !strcmp(name, "none") ||
5343 (banner = cupsdFindBanner(name)) == NULL)
5344 return (0);
ef416fc2 5345
b423cd4c 5346 /*
5347 * Open the banner and job files...
5348 */
ef416fc2 5349
b423cd4c 5350 if (add_file(con, job, banner->filetype, 0))
91c84a35 5351 return (-1);
ef416fc2 5352
b423cd4c 5353 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
5354 job->num_files);
5355 if ((out = cupsFileOpen(filename, "w")) == NULL)
5356 {
5357 cupsdLogMessage(CUPSD_LOG_ERROR,
3dfe78b3 5358 "Unable to create banner job file %s - %s",
b423cd4c 5359 filename, strerror(errno));
5360 job->num_files --;
5361 return (0);
5362 }
5363
5364 fchmod(cupsFileNumber(out), 0640);
5365 fchown(cupsFileNumber(out), RunUser, Group);
ef416fc2 5366
5367 /*
b423cd4c 5368 * Try the localized banner file under the subdirectory...
ef416fc2 5369 */
5370
80ca4592 5371 strlcpy(attrname, job->attrs->attrs->next->values[0].string.text,
b423cd4c 5372 sizeof(attrname));
5373 if (strlen(attrname) > 2 && attrname[2] == '-')
ef416fc2 5374 {
5375 /*
b423cd4c 5376 * Convert ll-cc to ll_CC...
ef416fc2 5377 */
5378
b423cd4c 5379 attrname[2] = '_';
5380 attrname[3] = toupper(attrname[3] & 255);
5381 attrname[4] = toupper(attrname[4] & 255);
5382 }
ef416fc2 5383
b423cd4c 5384 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
5385 attrname, name);
ef416fc2 5386
b423cd4c 5387 if (access(filename, 0) && strlen(attrname) > 2)
ef416fc2 5388 {
5389 /*
b423cd4c 5390 * Wasn't able to find "ll_CC" locale file; try the non-national
5391 * localization banner directory.
ef416fc2 5392 */
5393
b423cd4c 5394 attrname[2] = '\0';
ef416fc2 5395
b423cd4c 5396 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
5397 attrname, name);
ef416fc2 5398 }
b423cd4c 5399
5400 if (access(filename, 0))
ef416fc2 5401 {
5402 /*
b423cd4c 5403 * Use the non-localized banner file.
ef416fc2 5404 */
5405
b423cd4c 5406 snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
5407 }
ef416fc2 5408
b423cd4c 5409 if ((in = cupsFileOpen(filename, "r")) == NULL)
5410 {
5411 cupsFileClose(out);
5412 unlink(filename);
5413 cupsdLogMessage(CUPSD_LOG_ERROR,
3dfe78b3 5414 "Unable to open banner template file %s - %s",
b423cd4c 5415 filename, strerror(errno));
5416 job->num_files --;
5417 return (0);
ef416fc2 5418 }
5419
5420 /*
b423cd4c 5421 * Parse the file to the end...
ef416fc2 5422 */
5423
b423cd4c 5424 while ((ch = cupsFileGetChar(in)) != EOF)
5425 if (ch == '{')
5426 {
5427 /*
5428 * Get an attribute name...
5429 */
ef416fc2 5430
b423cd4c 5431 for (s = attrname; (ch = cupsFileGetChar(in)) != EOF;)
5432 if (!isalpha(ch & 255) && ch != '-' && ch != '?')
5433 break;
5434 else if (s < (attrname + sizeof(attrname) - 1))
5435 *s++ = ch;
5436 else
5437 break;
ef416fc2 5438
b423cd4c 5439 *s = '\0';
5440
5441 if (ch != '}')
5442 {
5443 /*
5444 * Ignore { followed by stuff that is not an attribute name...
5445 */
5446
5447 cupsFilePrintf(out, "{%s%c", attrname, ch);
5448 continue;
5449 }
ef416fc2 5450
ef416fc2 5451 /*
b423cd4c 5452 * See if it is defined...
ef416fc2 5453 */
5454
b423cd4c 5455 if (attrname[0] == '?')
5456 s = attrname + 1;
5457 else
5458 s = attrname;
ef416fc2 5459
b423cd4c 5460 if (!strcmp(s, "printer-name"))
5461 {
5462 cupsFilePuts(out, job->dest);
5463 continue;
ef416fc2 5464 }
b423cd4c 5465 else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
5466 {
5467 /*
5468 * See if we have a leading question mark...
5469 */
ef416fc2 5470
b423cd4c 5471 if (attrname[0] != '?')
5472 {
5473 /*
5474 * Nope, write to file as-is; probably a PostScript procedure...
5475 */
ef416fc2 5476
b423cd4c 5477 cupsFilePrintf(out, "{%s}", attrname);
5478 }
ef416fc2 5479
b423cd4c 5480 continue;
5481 }
ef416fc2 5482
b423cd4c 5483 /*
5484 * Output value(s)...
5485 */
ef416fc2 5486
b423cd4c 5487 for (i = 0; i < attr->num_values; i ++)
5488 {
5489 if (i)
5490 cupsFilePutChar(out, ',');
ef416fc2 5491
b423cd4c 5492 switch (attr->value_tag)
5493 {
5494 case IPP_TAG_INTEGER :
5495 case IPP_TAG_ENUM :
5496 if (!strncmp(s, "time-at-", 8))
dfd5680b
MS
5497 {
5498 struct timeval tv = { attr->values[i].integer, 0 };
5499 cupsFilePuts(out, cupsdGetDateTime(&tv, CUPSD_TIME_STANDARD));
5500 }
b423cd4c 5501 else
5502 cupsFilePrintf(out, "%d", attr->values[i].integer);
5503 break;
ef416fc2 5504
b423cd4c 5505 case IPP_TAG_BOOLEAN :
5506 cupsFilePrintf(out, "%d", attr->values[i].boolean);
5507 break;
ef416fc2 5508
b423cd4c 5509 case IPP_TAG_NOVALUE :
5510 cupsFilePuts(out, "novalue");
5511 break;
ef416fc2 5512
b423cd4c 5513 case IPP_TAG_RANGE :
5514 cupsFilePrintf(out, "%d-%d", attr->values[i].range.lower,
5515 attr->values[i].range.upper);
5516 break;
fa73b229 5517
b423cd4c 5518 case IPP_TAG_RESOLUTION :
5519 cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
5520 attr->values[i].resolution.yres,
5521 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5522 "dpi" : "dpc");
5523 break;
fa73b229 5524
b423cd4c 5525 case IPP_TAG_URI :
5526 case IPP_TAG_STRING :
5527 case IPP_TAG_TEXT :
5528 case IPP_TAG_NAME :
5529 case IPP_TAG_KEYWORD :
5530 case IPP_TAG_CHARSET :
5531 case IPP_TAG_LANGUAGE :
5532 if (!strcasecmp(banner->filetype->type, "postscript"))
5533 {
5534 /*
5535 * Need to quote strings for PS banners...
5536 */
fa73b229 5537
b423cd4c 5538 const char *p;
fa73b229 5539
b423cd4c 5540 for (p = attr->values[i].string.text; *p; p ++)
5541 {
5542 if (*p == '(' || *p == ')' || *p == '\\')
5543 {
5544 cupsFilePutChar(out, '\\');
5545 cupsFilePutChar(out, *p);
5546 }
5547 else if (*p < 32 || *p > 126)
5548 cupsFilePrintf(out, "\\%03o", *p & 255);
5549 else
5550 cupsFilePutChar(out, *p);
5551 }
5552 }
5553 else
5554 cupsFilePuts(out, attr->values[i].string.text);
5555 break;
fa73b229 5556
b423cd4c 5557 default :
5558 break; /* anti-compiler-warning-code */
5559 }
5560 }
5561 }
5562 else if (ch == '\\') /* Quoted char */
5563 {
5564 ch = cupsFileGetChar(in);
fa73b229 5565
b423cd4c 5566 if (ch != '{') /* Only do special handling for \{ */
5567 cupsFilePutChar(out, '\\');
fa73b229 5568
b423cd4c 5569 cupsFilePutChar(out, ch);
5570 }
5571 else
5572 cupsFilePutChar(out, ch);
fa73b229 5573
b423cd4c 5574 cupsFileClose(in);
fa73b229 5575
b423cd4c 5576 kbytes = (cupsFileTell(out) + 1023) / 1024;
fa73b229 5577
b423cd4c 5578 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
5579 IPP_TAG_INTEGER)) != NULL)
5580 attr->values[0].integer += kbytes;
5581
5582 cupsFileClose(out);
5583
5584 return (kbytes);
fa73b229 5585}
5586
5587
5588/*
b423cd4c 5589 * 'copy_file()' - Copy a PPD file or interface script...
fa73b229 5590 */
5591
b423cd4c 5592static int /* O - 0 = success, -1 = error */
5593copy_file(const char *from, /* I - Source file */
5594 const char *to) /* I - Destination file */
fa73b229 5595{
b423cd4c 5596 cups_file_t *src, /* Source file */
5597 *dst; /* Destination file */
5598 int bytes; /* Bytes to read/write */
5599 char buffer[2048]; /* Copy buffer */
fa73b229 5600
5601
b423cd4c 5602 cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_file(\"%s\", \"%s\")", from, to);
5603
fa73b229 5604 /*
b423cd4c 5605 * Open the source and destination file for a copy...
fa73b229 5606 */
5607
b423cd4c 5608 if ((src = cupsFileOpen(from, "rb")) == NULL)
5609 return (-1);
fa73b229 5610
b423cd4c 5611 if ((dst = cupsFileOpen(to, "wb")) == NULL)
5612 {
5613 cupsFileClose(src);
5614 return (-1);
5615 }
fa73b229 5616
b423cd4c 5617 /*
5618 * Copy the source file to the destination...
5619 */
fa73b229 5620
b423cd4c 5621 while ((bytes = cupsFileRead(src, buffer, sizeof(buffer))) > 0)
5622 if (cupsFileWrite(dst, buffer, bytes) < bytes)
5623 {
5624 cupsFileClose(src);
5625 cupsFileClose(dst);
5626 return (-1);
5627 }
fa73b229 5628
b423cd4c 5629 /*
5630 * Close both files and return...
5631 */
fa73b229 5632
b423cd4c 5633 cupsFileClose(src);
fa73b229 5634
b423cd4c 5635 return (cupsFileClose(dst));
5636}
fa73b229 5637
fa73b229 5638
b423cd4c 5639/*
5640 * 'copy_model()' - Copy a PPD model file, substituting default values
5641 * as needed...
5642 */
e00b005a 5643
b423cd4c 5644static int /* O - 0 = success, -1 = error */
5645copy_model(cupsd_client_t *con, /* I - Client connection */
5646 const char *from, /* I - Source file */
5647 const char *to) /* I - Destination file */
5648{
f7deaa1a 5649 fd_set input; /* select() input set */
b423cd4c 5650 struct timeval timeout; /* select() timeout */
bc44d920 5651 int maxfd; /* Max file descriptor for select() */
b423cd4c 5652 char tempfile[1024]; /* Temporary PPD file */
5653 int tempfd; /* Temporary PPD file descriptor */
5654 int temppid; /* Process ID of cups-driverd */
5655 int temppipe[2]; /* Temporary pipes */
5656 char *argv[4], /* Command-line arguments */
5657 *envp[MAX_ENV]; /* Environment */
5658 cups_file_t *src, /* Source file */
5659 *dst; /* Destination file */
2abf387c 5660 ppd_file_t *ppd; /* PPD file */
b423cd4c 5661 int bytes, /* Bytes from pipe */
5662 total; /* Total bytes from pipe */
2abf387c 5663 char buffer[2048]; /* Copy buffer */
b423cd4c 5664 int i; /* Looping var */
5665 char option[PPD_MAX_NAME], /* Option name */
5666 choice[PPD_MAX_NAME]; /* Choice name */
54afec33 5667 ppd_size_t *size; /* Default size */
b423cd4c 5668 int num_defaults; /* Number of default options */
2abf387c 5669 cups_option_t *defaults; /* Default options */
b423cd4c 5670 char cups_protocol[PPD_MAX_LINE];
5671 /* cupsProtocol attribute */
e00b005a 5672
e00b005a 5673
b423cd4c 5674 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5675 "copy_model(con=%p, from=\"%s\", to=\"%s\")",
5676 con, from, to);
e00b005a 5677
b423cd4c 5678 /*
5679 * Run cups-driverd to get the PPD file...
5680 */
e00b005a 5681
b423cd4c 5682 argv[0] = "cups-driverd";
5683 argv[1] = "cat";
5684 argv[2] = (char *)from;
5685 argv[3] = NULL;
e00b005a 5686
b423cd4c 5687 cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
e00b005a 5688
b423cd4c 5689 snprintf(buffer, sizeof(buffer), "%s/daemon/cups-driverd", ServerBin);
5690 snprintf(tempfile, sizeof(tempfile), "%s/%d.ppd", TempDir, con->http.fd);
5691 tempfd = open(tempfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
5692 if (tempfd < 0)
5693 return (-1);
5694
5695 cupsdOpenPipe(temppipe);
5696
b423cd4c 5697 cupsdLogMessage(CUPSD_LOG_DEBUG,
5698 "copy_model: Running \"cups-driverd cat %s\"...", from);
fa73b229 5699
b423cd4c 5700 if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1],
38e73f87 5701 -1, -1, 0, DefaultProfile, NULL, &temppid))
fa73b229 5702 {
b423cd4c 5703 close(tempfd);
5704 unlink(tempfile);
a4924f6c 5705
b423cd4c 5706 return (-1);
fa73b229 5707 }
5708
b423cd4c 5709 close(temppipe[1]);
fa73b229 5710
b423cd4c 5711 /*
5712 * Wait up to 30 seconds for the PPD file to be copied...
5713 */
fa73b229 5714
b423cd4c 5715 total = 0;
fa73b229 5716
b423cd4c 5717 if (temppipe[0] > CGIPipes[0])
5718 maxfd = temppipe[0] + 1;
5719 else
5720 maxfd = CGIPipes[0] + 1;
ef416fc2 5721
b423cd4c 5722 for (;;)
5723 {
5724 /*
5725 * See if we have data ready...
5726 */
ef416fc2 5727
f7deaa1a 5728 FD_ZERO(&input);
5729 FD_SET(temppipe[0], &input);
5730 FD_SET(CGIPipes[0], &input);
ef416fc2 5731
b423cd4c 5732 timeout.tv_sec = 30;
5733 timeout.tv_usec = 0;
5734
f7deaa1a 5735 if ((i = select(maxfd, &input, NULL, NULL, &timeout)) < 0)
b423cd4c 5736 {
5737 if (errno == EINTR)
5738 continue;
5739 else
5740 break;
5741 }
5742 else if (i == 0)
ef416fc2 5743 {
5744 /*
b423cd4c 5745 * We have timed out...
ef416fc2 5746 */
5747
b423cd4c 5748 break;
ef416fc2 5749 }
b423cd4c 5750
f7deaa1a 5751 if (FD_ISSET(temppipe[0], &input))
ef416fc2 5752 {
5753 /*
b423cd4c 5754 * Read the PPD file from the pipe, and write it to the PPD file.
ef416fc2 5755 */
5756
b423cd4c 5757 if ((bytes = read(temppipe[0], buffer, sizeof(buffer))) > 0)
5758 {
5759 if (write(tempfd, buffer, bytes) < bytes)
5760 break;
ef416fc2 5761
b423cd4c 5762 total += bytes;
5763 }
5764 else
5765 break;
ef416fc2 5766 }
b423cd4c 5767
f7deaa1a 5768 if (FD_ISSET(CGIPipes[0], &input))
b423cd4c 5769 cupsdUpdateCGI();
ef416fc2 5770 }
5771
b423cd4c 5772 close(temppipe[0]);
5773 close(tempfd);
ef416fc2 5774
b423cd4c 5775 if (!total)
ef416fc2 5776 {
b423cd4c 5777 /*
5778 * No data from cups-deviced...
5779 */
5780
4d301e69 5781 cupsdLogMessage(CUPSD_LOG_ERROR, "copy_model: empty PPD file");
b423cd4c 5782 unlink(tempfile);
5783 return (-1);
ef416fc2 5784 }
5785
b423cd4c 5786 /*
5787 * Read the source file and see what page sizes are supported...
5788 */
fa73b229 5789
2abf387c 5790 if ((ppd = ppdOpenFile(tempfile)) == NULL)
b423cd4c 5791 {
5792 unlink(tempfile);
5793 return (-1);
5794 }
fa73b229 5795
ef416fc2 5796 /*
b423cd4c 5797 * Open the destination (if possible) and set the default options...
ef416fc2 5798 */
5799
b423cd4c 5800 num_defaults = 0;
5801 defaults = NULL;
5802 cups_protocol[0] = '\0';
ef416fc2 5803
b423cd4c 5804 if ((dst = cupsFileOpen(to, "rb")) != NULL)
ef416fc2 5805 {
5806 /*
b423cd4c 5807 * Read all of the default lines from the old PPD...
ef416fc2 5808 */
5809
b423cd4c 5810 while (cupsFileGets(dst, buffer, sizeof(buffer)))
5811 if (!strncmp(buffer, "*Default", 8))
5812 {
5813 /*
5814 * Add the default option...
5815 */
ef416fc2 5816
b423cd4c 5817 if (!ppd_parse_line(buffer, option, sizeof(option),
5818 choice, sizeof(choice)))
2abf387c 5819 {
5820 ppd_option_t *ppdo; /* PPD option */
5821
5822
5823 /*
5824 * Only add the default if the default hasn't already been
5825 * set and the choice exists in the new PPD...
5826 */
5827
5828 if (!cupsGetOption(option, num_defaults, defaults) &&
5829 (ppdo = ppdFindOption(ppd, option)) != NULL &&
5830 ppdFindChoice(ppdo, choice))
5831 num_defaults = cupsAddOption(option, choice, num_defaults,
b423cd4c 5832 &defaults);
2abf387c 5833 }
b423cd4c 5834 }
5835 else if (!strncmp(buffer, "*cupsProtocol:", 14))
5836 strlcpy(cups_protocol, buffer, sizeof(cups_protocol));
ef416fc2 5837
b423cd4c 5838 cupsFileClose(dst);
ef416fc2 5839 }
54afec33 5840 else if ((size = ppdPageSize(ppd, DefaultPaperSize)) != NULL)
ef416fc2 5841 {
b423cd4c 5842 /*
5843 * Add the default media sizes...
b423cd4c 5844 */
5845
54afec33 5846 num_defaults = cupsAddOption("PageSize", size->name,
c5571a1d 5847 num_defaults, &defaults);
54afec33 5848 num_defaults = cupsAddOption("PageRegion", size->name,
c5571a1d 5849 num_defaults, &defaults);
54afec33 5850 num_defaults = cupsAddOption("PaperDimension", size->name,
c5571a1d 5851 num_defaults, &defaults);
54afec33 5852 num_defaults = cupsAddOption("ImageableArea", size->name,
c5571a1d 5853 num_defaults, &defaults);
ef416fc2 5854 }
5855
2abf387c 5856 ppdClose(ppd);
5857
5858 /*
5859 * Open the source file for a copy...
5860 */
5861
5862 if ((src = cupsFileOpen(tempfile, "rb")) == NULL)
5863 {
5864 cupsFreeOptions(num_defaults, defaults);
5865 unlink(tempfile);
5866 return (-1);
5867 }
5868
ef416fc2 5869 /*
b423cd4c 5870 * Open the destination file for a copy...
ef416fc2 5871 */
5872
b423cd4c 5873 if ((dst = cupsFileOpen(to, "wb")) == NULL)
ef416fc2 5874 {
2abf387c 5875 cupsFreeOptions(num_defaults, defaults);
b423cd4c 5876 cupsFileClose(src);
5877 unlink(tempfile);
5878 return (-1);
ef416fc2 5879 }
5880
5881 /*
b423cd4c 5882 * Copy the source file to the destination...
ef416fc2 5883 */
5884
b423cd4c 5885 while (cupsFileGets(src, buffer, sizeof(buffer)))
ef416fc2 5886 {
b423cd4c 5887 if (!strncmp(buffer, "*Default", 8))
ef416fc2 5888 {
b423cd4c 5889 /*
5890 * Check for an previous default option choice...
5891 */
ef416fc2 5892
b423cd4c 5893 if (!ppd_parse_line(buffer, option, sizeof(option),
5894 choice, sizeof(choice)))
ef416fc2 5895 {
2abf387c 5896 const char *val; /* Default option value */
ef416fc2 5897
2abf387c 5898
5899 if ((val = cupsGetOption(option, num_defaults, defaults)) != NULL)
5900 {
5901 /*
5902 * Substitute the previous choice...
5903 */
5904
5905 snprintf(buffer, sizeof(buffer), "*Default%s: %s", option, val);
5906 }
b423cd4c 5907 }
ef416fc2 5908 }
b423cd4c 5909
5910 cupsFilePrintf(dst, "%s\n", buffer);
ef416fc2 5911 }
5912
b423cd4c 5913 if (cups_protocol[0])
5914 cupsFilePrintf(dst, "%s\n", cups_protocol);
5915
2abf387c 5916 cupsFreeOptions(num_defaults, defaults);
b423cd4c 5917
ef416fc2 5918 /*
b423cd4c 5919 * Close both files and return...
ef416fc2 5920 */
5921
b423cd4c 5922 cupsFileClose(src);
5923
5924 unlink(tempfile);
5925
5926 return (cupsFileClose(dst));
5927}
5928
5929
5930/*
5931 * 'copy_job_attrs()' - Copy job attributes.
5932 */
ef416fc2 5933
b423cd4c 5934static void
5935copy_job_attrs(cupsd_client_t *con, /* I - Client connection */
5936 cupsd_job_t *job, /* I - Job */
10d09e33
MS
5937 cups_array_t *ra, /* I - Requested attributes array */
5938 cups_array_t *exclude) /* I - Private attributes array */
b423cd4c 5939{
5940 char job_uri[HTTP_MAX_URI]; /* Job URI */
ef416fc2 5941
ef416fc2 5942
5943 /*
b423cd4c 5944 * Send the requested attributes for each job...
ef416fc2 5945 */
5946
b423cd4c 5947 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
5948 con->servername, con->serverport, "/jobs/%d",
5949 job->id);
ef416fc2 5950
10d09e33
MS
5951 if (!cupsArrayFind(exclude, "all"))
5952 {
5953 if ((!exclude || !cupsArrayFind(exclude, "document-count")) &&
5954 (!ra || cupsArrayFind(ra, "document-count")))
5955 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
5956 "document-count", job->num_files);
2e4ff8af 5957
10d09e33
MS
5958 if ((!exclude || !cupsArrayFind(exclude, "job-media-progress")) &&
5959 (!ra || cupsArrayFind(ra, "job-media-progress")))
5960 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
5961 "job-media-progress", job->progress);
9a4f8274 5962
10d09e33
MS
5963 if ((!exclude || !cupsArrayFind(exclude, "job-more-info")) &&
5964 (!ra || cupsArrayFind(ra, "job-more-info")))
5965 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
5966 "job-more-info", NULL, job_uri);
ef416fc2 5967
10d09e33
MS
5968 if (job->state_value > IPP_JOB_PROCESSING &&
5969 (!exclude || !cupsArrayFind(exclude, "job-preserved")) &&
5970 (!ra || cupsArrayFind(ra, "job-preserved")))
5971 ippAddBoolean(con->response, IPP_TAG_JOB, "job-preserved",
5972 job->num_files > 0);
ef416fc2 5973
10d09e33
MS
5974 if ((!exclude || !cupsArrayFind(exclude, "job-printer-up-time")) &&
5975 (!ra || cupsArrayFind(ra, "job-printer-up-time")))
5976 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
5977 "job-printer-up-time", time(NULL));
5978 }
ef416fc2 5979
b423cd4c 5980 if (!ra || cupsArrayFind(ra, "job-state-reasons"))
5981 add_job_state_reasons(con, job);
bd7854cb 5982
b423cd4c 5983 if (!ra || cupsArrayFind(ra, "job-uri"))
5984 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
5985 "job-uri", NULL, job_uri);
ef416fc2 5986
10d09e33 5987 copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0, exclude);
b423cd4c 5988}
ef416fc2 5989
ef416fc2 5990
b423cd4c 5991/*
5992 * 'copy_printer_attrs()' - Copy printer attributes.
5993 */
ef416fc2 5994
b423cd4c 5995static void
5996copy_printer_attrs(
5997 cupsd_client_t *con, /* I - Client connection */
5998 cupsd_printer_t *printer, /* I - Printer */
5999 cups_array_t *ra) /* I - Requested attributes array */
6000{
6001 char printer_uri[HTTP_MAX_URI];
6002 /* Printer URI */
7cf5915e
MS
6003 char printer_icons[HTTP_MAX_URI];
6004 /* Printer icons */
b423cd4c 6005 time_t curtime; /* Current time */
6006 int i; /* Looping var */
6007 ipp_attribute_t *history; /* History collection */
ef416fc2 6008
ef416fc2 6009
b423cd4c 6010 /*
6011 * Copy the printer attributes to the response using requested-attributes
6012 * and document-format attributes that may be provided by the client.
6013 */
ef416fc2 6014
b423cd4c 6015 curtime = time(NULL);
ef416fc2 6016
5a738aea
MS
6017 if (!ra || cupsArrayFind(ra, "marker-change-time"))
6018 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
6019 "marker-change-time", printer->marker_time);
6020
dd1abb6b
MS
6021 if (printer->num_printers > 0 &&
6022 (!ra || cupsArrayFind(ra, "member-uris")))
6023 {
6024 ipp_attribute_t *member_uris; /* member-uris attribute */
6025 cupsd_printer_t *p2; /* Printer in class */
6026 ipp_attribute_t *p2_uri; /* printer-uri-supported for class printer */
6027
6028
6029 if ((member_uris = ippAddStrings(con->response, IPP_TAG_PRINTER,
6030 IPP_TAG_URI, "member-uris",
6031 printer->num_printers, NULL,
6032 NULL)) != NULL)
6033 {
6034 for (i = 0; i < printer->num_printers; i ++)
6035 {
6036 p2 = printer->printers[i];
6037
6038 if ((p2_uri = ippFindAttribute(p2->attrs, "printer-uri-supported",
6039 IPP_TAG_URI)) != NULL)
6040 member_uris->values[i].string.text =
426c6a59 6041 _cupsStrRetain(p2_uri->values[0].string.text);
dd1abb6b
MS
6042 else
6043 {
6044 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri,
6045 sizeof(printer_uri), "ipp", NULL, con->servername,
6046 con->serverport,
6047 (p2->type & CUPS_PRINTER_CLASS) ?
6048 "/classes/%s" : "/printers/%s", p2->name);
6049 member_uris->values[i].string.text = _cupsStrAlloc(printer_uri);
6050 }
6051 }
6052 }
6053 }
6054
323c5de1 6055 if (printer->alert && (!ra || cupsArrayFind(ra, "printer-alert")))
6056 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_STRING,
6057 "printer-alert", NULL, printer->alert);
6058
6059 if (printer->alert_description &&
6060 (!ra || cupsArrayFind(ra, "printer-alert-description")))
6061 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
6062 "printer-alert-description", NULL,
6063 printer->alert_description);
6064
b423cd4c 6065 if (!ra || cupsArrayFind(ra, "printer-current-time"))
6066 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
6067 ippTimeToDate(curtime));
ef416fc2 6068
d9bca400
MS
6069#ifdef HAVE_DNSSD
6070 if (!ra || cupsArrayFind(ra, "printer-dns-sd-name"))
6071 {
6072 if (printer->reg_name)
6073 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
6074 "printer-dns-sd-name", NULL, printer->reg_name);
6075 else
6076 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
6077 "printer-dns-sd-name", 0);
6078 }
6079#endif /* HAVE_DNSSD */
6080
b423cd4c 6081 if (!ra || cupsArrayFind(ra, "printer-error-policy"))
6082 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
6083 "printer-error-policy", NULL, printer->error_policy);
ef416fc2 6084
f11a948a
MS
6085 if (!ra || cupsArrayFind(ra, "printer-error-policy-supported"))
6086 {
6087 static const char * const errors[] =/* printer-error-policy-supported values */
6088 {
6089 "abort-job",
6090 "retry-current-job",
6091 "retry-job",
6092 "stop-printer"
6093 };
6094
6095 if (printer->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_CLASS))
6096 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
6097 "printer-error-policy-supported", NULL, "retry-current-job");
6098 else
6099 ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
6100 "printer-error-policy-supported",
6101 sizeof(errors) / sizeof(errors[0]), NULL, errors);
6102 }
6103
7cf5915e
MS
6104 if (!ra || cupsArrayFind(ra, "printer-icons"))
6105 {
6106 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_icons, sizeof(printer_icons),
6107 "http", NULL, con->servername, con->serverport,
6108 "/icons/%s.png", printer->name);
6109 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons",
6110 NULL, printer_icons);
6111 cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-icons=\"%s\"", printer_icons);
6112 }
6113
b423cd4c 6114 if (!ra || cupsArrayFind(ra, "printer-is-accepting-jobs"))
6115 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
6116 printer->accepting);
ef416fc2 6117
b423cd4c 6118 if (!ra || cupsArrayFind(ra, "printer-is-shared"))
6119 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-shared",
6120 printer->shared);
ef416fc2 6121
58dc1933
MS
6122 if ((!ra || cupsArrayFind(ra, "printer-more-info")) &&
6123 !(printer->type & CUPS_PRINTER_DISCOVERED))
6124 {
6125 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
6126 "http", NULL, con->servername, con->serverport,
6127 (printer->type & CUPS_PRINTER_CLASS) ?
6128 "/classes/%s" : "/printers/%s", printer->name);
6129 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
6130 "printer-more-info", NULL, printer_uri);
6131 }
6132
b423cd4c 6133 if (!ra || cupsArrayFind(ra, "printer-op-policy"))
6134 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
6135 "printer-op-policy", NULL, printer->op_policy);
6136
6137 if (!ra || cupsArrayFind(ra, "printer-state"))
6138 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
6139 printer->state);
6140
6141 if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
6142 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
6143 "printer-state-change-time", printer->state_time);
f7deaa1a 6144
b423cd4c 6145 if (MaxPrinterHistory > 0 && printer->num_history > 0 &&
6146 cupsArrayFind(ra, "printer-state-history"))
ef416fc2 6147 {
6148 /*
b423cd4c 6149 * Printer history is only sent if specifically requested, so that
6150 * older CUPS/IPP clients won't barf on the collection attributes.
ef416fc2 6151 */
6152
b423cd4c 6153 history = ippAddCollections(con->response, IPP_TAG_PRINTER,
6154 "printer-state-history",
6155 printer->num_history, NULL);
6156
6157 for (i = 0; i < printer->num_history; i ++)
6158 copy_attrs(history->values[i].collection = ippNew(), printer->history[i],
10d09e33 6159 NULL, IPP_TAG_ZERO, 0, NULL);
ef416fc2 6160 }
6161
b423cd4c 6162 if (!ra || cupsArrayFind(ra, "printer-state-message"))
6163 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
6164 "printer-state-message", NULL, printer->state_message);
ef416fc2 6165
b423cd4c 6166 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
6167 add_printer_state_reasons(con, printer);
ef416fc2 6168
b423cd4c 6169 if (!ra || cupsArrayFind(ra, "printer-type"))
6170 {
6171 int type; /* printer-type value */
ef416fc2 6172
ef416fc2 6173
ef416fc2 6174 /*
b423cd4c 6175 * Add the CUPS-specific printer-type attribute...
ef416fc2 6176 */
6177
09a101d6 6178 type = printer->type;
b423cd4c 6179
6180 if (printer == DefaultPrinter)
6181 type |= CUPS_PRINTER_DEFAULT;
6182
6183 if (!printer->accepting)
6184 type |= CUPS_PRINTER_REJECTING;
6185
6186 if (!printer->shared)
6187 type |= CUPS_PRINTER_NOT_SHARED;
6188
6189 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-type",
6190 type);
ef416fc2 6191 }
ef416fc2 6192
b423cd4c 6193 if (!ra || cupsArrayFind(ra, "printer-up-time"))
6194 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
6195 "printer-up-time", curtime);
ef416fc2 6196
b423cd4c 6197 if ((!ra || cupsArrayFind(ra, "printer-uri-supported")) &&
58dc1933 6198 !(printer->type & CUPS_PRINTER_DISCOVERED))
ef416fc2 6199 {
b423cd4c 6200 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
6201 "ipp", NULL, con->servername, con->serverport,
8ca02f3c 6202 (printer->type & CUPS_PRINTER_CLASS) ?
6203 "/classes/%s" : "/printers/%s", printer->name);
b423cd4c 6204 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
6205 "printer-uri-supported", NULL, printer_uri);
6206 cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-uri-supported=\"%s\"",
6207 printer_uri);
6208 }
ef416fc2 6209
b423cd4c 6210 if (!ra || cupsArrayFind(ra, "queued-job-count"))
6211 add_queued_job_count(con, printer);
ef416fc2 6212
10d09e33 6213 copy_attrs(con->response, printer->attrs, ra, IPP_TAG_ZERO, 0, NULL);
61cf44e2 6214 if (printer->ppd_attrs)
10d09e33
MS
6215 copy_attrs(con->response, printer->ppd_attrs, ra, IPP_TAG_ZERO, 0, NULL);
6216 copy_attrs(con->response, CommonData, ra, IPP_TAG_ZERO, IPP_TAG_COPY, NULL);
b423cd4c 6217}
ef416fc2 6218
ef416fc2 6219
b423cd4c 6220/*
6221 * 'copy_subscription_attrs()' - Copy subscription attributes.
6222 */
ef416fc2 6223
b423cd4c 6224static void
6225copy_subscription_attrs(
6226 cupsd_client_t *con, /* I - Client connection */
6227 cupsd_subscription_t *sub, /* I - Subscription */
10d09e33
MS
6228 cups_array_t *ra, /* I - Requested attributes array */
6229 cups_array_t *exclude) /* I - Private attributes array */
b423cd4c 6230{
6231 ipp_attribute_t *attr; /* Current attribute */
6232 char printer_uri[HTTP_MAX_URI];
6233 /* Printer URI */
6234 int count; /* Number of events */
6235 unsigned mask; /* Current event mask */
6236 const char *name; /* Current event name */
ef416fc2 6237
ef416fc2 6238
10d09e33
MS
6239 cupsdLogMessage(CUPSD_LOG_DEBUG2,
6240 "copy_subscription_attrs(con=%p, sub=%p, ra=%p, exclude=%p)",
6241 con, sub, ra, exclude);
6242
b423cd4c 6243 /*
6244 * Copy the subscription attributes to the response using the
6245 * requested-attributes attribute that may be provided by the client.
6246 */
ef416fc2 6247
10d09e33 6248 if (!exclude || !cupsArrayFind(exclude, "all"))
b423cd4c 6249 {
10d09e33
MS
6250 if ((!exclude || !cupsArrayFind(exclude, "notify-events")) &&
6251 (!ra || cupsArrayFind(ra, "notify-events")))
b423cd4c 6252 {
10d09e33 6253 cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_subscription_attrs: notify-events");
ef416fc2 6254
10d09e33
MS
6255 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
6256 {
6257 /*
6258 * Simple event list...
6259 */
6260
6261 ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
6262 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
6263 "notify-events", NULL, name);
6264 }
6265 else
6266 {
6267 /*
6268 * Complex event list...
6269 */
ef416fc2 6270
10d09e33
MS
6271 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
6272 if (sub->mask & mask)
6273 count ++;
ef416fc2 6274
10d09e33
MS
6275 attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
6276 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
6277 "notify-events", count, NULL, NULL);
ef416fc2 6278
10d09e33
MS
6279 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
6280 if (sub->mask & mask)
6281 {
6282 attr->values[count].string.text =
6283 (char *)cupsdEventName((cupsd_eventmask_t)mask);
ef416fc2 6284
10d09e33
MS
6285 count ++;
6286 }
6287 }
ef416fc2 6288 }
10d09e33
MS
6289
6290 if ((!exclude || !cupsArrayFind(exclude, "notify-lease-duration")) &&
6291 (!sub->job && (!ra || cupsArrayFind(ra, "notify-lease-duration"))))
6292 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
6293 "notify-lease-duration", sub->lease);
6294
6295 if ((!exclude || !cupsArrayFind(exclude, "notify-recipient-uri")) &&
6296 (sub->recipient && (!ra || cupsArrayFind(ra, "notify-recipient-uri"))))
6297 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
6298 "notify-recipient-uri", NULL, sub->recipient);
6299 else if ((!exclude || !cupsArrayFind(exclude, "notify-pull-method")) &&
6300 (!ra || cupsArrayFind(ra, "notify-pull-method")))
6301 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
6302 "notify-pull-method", NULL, "ippget");
6303
6304 if ((!exclude || !cupsArrayFind(exclude, "notify-subscriber-user-name")) &&
6305 (!ra || cupsArrayFind(ra, "notify-subscriber-user-name")))
6306 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
6307 "notify-subscriber-user-name", NULL, sub->owner);
6308
6309 if ((!exclude || !cupsArrayFind(exclude, "notify-time-interval")) &&
6310 (!ra || cupsArrayFind(ra, "notify-time-interval")))
6311 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
6312 "notify-time-interval", sub->interval);
6313
6314 if (sub->user_data_len > 0 &&
6315 (!exclude || !cupsArrayFind(exclude, "notify-user-data")) &&
6316 (!ra || cupsArrayFind(ra, "notify-user-data")))
6317 ippAddOctetString(con->response, IPP_TAG_SUBSCRIPTION, "notify-user-data",
6318 sub->user_data, sub->user_data_len);
b423cd4c 6319 }
ef416fc2 6320
b423cd4c 6321 if (sub->job && (!ra || cupsArrayFind(ra, "notify-job-id")))
6322 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
6323 "notify-job-id", sub->job->id);
ef416fc2 6324
b423cd4c 6325 if (sub->dest && (!ra || cupsArrayFind(ra, "notify-printer-uri")))
6326 {
6327 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
6328 "ipp", NULL, con->servername, con->serverport,
6329 "/printers/%s", sub->dest->name);
6330 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
6331 "notify-printer-uri", NULL, printer_uri);
ef416fc2 6332 }
ef416fc2 6333
b423cd4c 6334 if (!ra || cupsArrayFind(ra, "notify-subscription-id"))
6335 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
6336 "notify-subscription-id", sub->id);
b423cd4c 6337}
ef416fc2 6338
ef416fc2 6339
b423cd4c 6340/*
6341 * 'create_job()' - Print a file to a printer or class.
6342 */
6343
6344static void
6345create_job(cupsd_client_t *con, /* I - Client connection */
6346 ipp_attribute_t *uri) /* I - Printer URI */
6347{
f7deaa1a 6348 cupsd_printer_t *printer; /* Printer */
6349 cupsd_job_t *job; /* New job */
b423cd4c 6350
ef416fc2 6351
b423cd4c 6352 cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con,
6353 con->http.fd, uri->values[0].string.text);
ef416fc2 6354
f7deaa1a 6355 /*
6356 * Is the destination valid?
6357 */
6358
6359 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
6360 {
6361 /*
6362 * Bad URI...
6363 */
6364
6365 send_ipp_status(con, IPP_NOT_FOUND,
6366 _("The printer or class was not found."));
6367 return;
6368 }
6369
ef416fc2 6370 /*
b423cd4c 6371 * Create the job object...
ef416fc2 6372 */
6373
f7deaa1a 6374 if ((job = add_job(con, printer, NULL)) == NULL)
b423cd4c 6375 return;
ef416fc2 6376
09a101d6 6377 job->pending_timeout = 1;
6378
ef416fc2 6379 /*
6380 * Save and log the job...
6381 */
f7deaa1a 6382
75bd9771
MS
6383 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
6384 job->dest, job->username);
ef416fc2 6385}
6386
6387
6388/*
6389 * 'create_requested_array()' - Create an array for the requested-attributes.
6390 */
6391
6392static cups_array_t * /* O - Array of attributes or NULL */
6393create_requested_array(ipp_t *request) /* I - IPP request */
6394{
6395 int i; /* Looping var */
6396 ipp_attribute_t *requested; /* requested-attributes attribute */
6397 cups_array_t *ra; /* Requested attributes array */
6398 char *value; /* Current value */
6399
6400
6401 /*
6402 * Get the requested-attributes attribute, and return NULL if we don't
6403 * have one...
6404 */
6405
6406 if ((requested = ippFindAttribute(request, "requested-attributes",
6407 IPP_TAG_KEYWORD)) == NULL)
6408 return (NULL);
6409
6410 /*
6411 * If the attribute contains a single "all" keyword, return NULL...
6412 */
6413
6414 if (requested->num_values == 1 &&
6415 !strcmp(requested->values[0].string.text, "all"))
6416 return (NULL);
6417
6418 /*
6419 * Create an array using "strcmp" as the comparison function...
6420 */
6421
6422 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
6423
6424 for (i = 0; i < requested->num_values; i ++)
6425 {
6426 value = requested->values[i].string.text;
6427
6428 if (!strcmp(value, "job-template"))
6429 {
6430 cupsArrayAdd(ra, "copies");
6431 cupsArrayAdd(ra, "copies-default");
6432 cupsArrayAdd(ra, "copies-supported");
6433 cupsArrayAdd(ra, "finishings");
6434 cupsArrayAdd(ra, "finishings-default");
6435 cupsArrayAdd(ra, "finishings-supported");
6436 cupsArrayAdd(ra, "job-hold-until");
6437 cupsArrayAdd(ra, "job-hold-until-default");
6438 cupsArrayAdd(ra, "job-hold-until-supported");
6439 cupsArrayAdd(ra, "job-priority");
6440 cupsArrayAdd(ra, "job-priority-default");
6441 cupsArrayAdd(ra, "job-priority-supported");
6442 cupsArrayAdd(ra, "job-sheets");
6443 cupsArrayAdd(ra, "job-sheets-default");
6444 cupsArrayAdd(ra, "job-sheets-supported");
6445 cupsArrayAdd(ra, "media");
6446 cupsArrayAdd(ra, "media-default");
6447 cupsArrayAdd(ra, "media-supported");
6448 cupsArrayAdd(ra, "multiple-document-handling");
6449 cupsArrayAdd(ra, "multiple-document-handling-default");
6450 cupsArrayAdd(ra, "multiple-document-handling-supported");
6451 cupsArrayAdd(ra, "number-up");
6452 cupsArrayAdd(ra, "number-up-default");
6453 cupsArrayAdd(ra, "number-up-supported");
6454 cupsArrayAdd(ra, "orientation-requested");
6455 cupsArrayAdd(ra, "orientation-requested-default");
6456 cupsArrayAdd(ra, "orientation-requested-supported");
6457 cupsArrayAdd(ra, "page-ranges");
6458 cupsArrayAdd(ra, "page-ranges-supported");
6459 cupsArrayAdd(ra, "printer-resolution");
6460 cupsArrayAdd(ra, "printer-resolution-default");
6461 cupsArrayAdd(ra, "printer-resolution-supported");
6462 cupsArrayAdd(ra, "print-quality");
6463 cupsArrayAdd(ra, "print-quality-default");
6464 cupsArrayAdd(ra, "print-quality-supported");
6465 cupsArrayAdd(ra, "sides");
6466 cupsArrayAdd(ra, "sides-default");
6467 cupsArrayAdd(ra, "sides-supported");
6468 }
6469 else if (!strcmp(value, "job-description"))
6470 {
6471 cupsArrayAdd(ra, "date-time-at-completed");
6472 cupsArrayAdd(ra, "date-time-at-creation");
6473 cupsArrayAdd(ra, "date-time-at-processing");
6474 cupsArrayAdd(ra, "job-detailed-status-message");
6475 cupsArrayAdd(ra, "job-document-access-errors");
6476 cupsArrayAdd(ra, "job-id");
6477 cupsArrayAdd(ra, "job-impressions");
6478 cupsArrayAdd(ra, "job-impressions-completed");
6479 cupsArrayAdd(ra, "job-k-octets");
6480 cupsArrayAdd(ra, "job-k-octets-processed");
9a4f8274 6481 cupsArrayAdd(ra, "job-media-progress");
ef416fc2 6482 cupsArrayAdd(ra, "job-media-sheets");
6483 cupsArrayAdd(ra, "job-media-sheets-completed");
6484 cupsArrayAdd(ra, "job-message-from-operator");
6485 cupsArrayAdd(ra, "job-more-info");
6486 cupsArrayAdd(ra, "job-name");
6487 cupsArrayAdd(ra, "job-originating-user-name");
6488 cupsArrayAdd(ra, "job-printer-up-time");
6489 cupsArrayAdd(ra, "job-printer-uri");
6490 cupsArrayAdd(ra, "job-state");
6491 cupsArrayAdd(ra, "job-state-message");
6492 cupsArrayAdd(ra, "job-state-reasons");
6493 cupsArrayAdd(ra, "job-uri");
6494 cupsArrayAdd(ra, "number-of-documents");
6495 cupsArrayAdd(ra, "number-of-intervening-jobs");
6496 cupsArrayAdd(ra, "output-device-assigned");
6497 cupsArrayAdd(ra, "time-at-completed");
6498 cupsArrayAdd(ra, "time-at-creation");
6499 cupsArrayAdd(ra, "time-at-processing");
6500 }
6501 else if (!strcmp(value, "printer-description"))
6502 {
6503 cupsArrayAdd(ra, "charset-configured");
6504 cupsArrayAdd(ra, "charset-supported");
6505 cupsArrayAdd(ra, "color-supported");
6506 cupsArrayAdd(ra, "compression-supported");
6507 cupsArrayAdd(ra, "document-format-default");
6508 cupsArrayAdd(ra, "document-format-supported");
6509 cupsArrayAdd(ra, "generated-natural-language-supported");
6510 cupsArrayAdd(ra, "ipp-versions-supported");
6511 cupsArrayAdd(ra, "job-impressions-supported");
6512 cupsArrayAdd(ra, "job-k-octets-supported");
6513 cupsArrayAdd(ra, "job-media-sheets-supported");
1f6f3dbc 6514 cupsArrayAdd(ra, "job-settable-attributes-supported");
ef416fc2 6515 cupsArrayAdd(ra, "multiple-document-jobs-supported");
6516 cupsArrayAdd(ra, "multiple-operation-time-out");
6517 cupsArrayAdd(ra, "natural-language-configured");
6518 cupsArrayAdd(ra, "notify-attributes-supported");
6519 cupsArrayAdd(ra, "notify-lease-duration-default");
6520 cupsArrayAdd(ra, "notify-lease-duration-supported");
6521 cupsArrayAdd(ra, "notify-max-events-supported");
f301802f 6522 cupsArrayAdd(ra, "notify-events-default");
6523 cupsArrayAdd(ra, "notify-events-supported");
ef416fc2 6524 cupsArrayAdd(ra, "notify-pull-method-supported");
6525 cupsArrayAdd(ra, "notify-schemes-supported");
6526 cupsArrayAdd(ra, "operations-supported");
6527 cupsArrayAdd(ra, "pages-per-minute");
6528 cupsArrayAdd(ra, "pages-per-minute-color");
6529 cupsArrayAdd(ra, "pdl-override-supported");
323c5de1 6530 cupsArrayAdd(ra, "printer-alert");
6531 cupsArrayAdd(ra, "printer-alert-description");
01ce6322 6532 cupsArrayAdd(ra, "printer-commands");
ef416fc2 6533 cupsArrayAdd(ra, "printer-current-time");
6534 cupsArrayAdd(ra, "printer-driver-installer");
01ce6322 6535 cupsArrayAdd(ra, "printer-dns-sd-name");
ef416fc2 6536 cupsArrayAdd(ra, "printer-info");
6537 cupsArrayAdd(ra, "printer-is-accepting-jobs");
6538 cupsArrayAdd(ra, "printer-location");
6539 cupsArrayAdd(ra, "printer-make-and-model");
6540 cupsArrayAdd(ra, "printer-message-from-operator");
6541 cupsArrayAdd(ra, "printer-more-info");
6542 cupsArrayAdd(ra, "printer-more-info-manufacturer");
6543 cupsArrayAdd(ra, "printer-name");
6544 cupsArrayAdd(ra, "printer-state");
6545 cupsArrayAdd(ra, "printer-state-message");
6546 cupsArrayAdd(ra, "printer-state-reasons");
1f6f3dbc 6547 cupsArrayAdd(ra, "printer-settable-attributes-supported");
01ce6322 6548 cupsArrayAdd(ra, "printer-type");
ef416fc2 6549 cupsArrayAdd(ra, "printer-up-time");
6550 cupsArrayAdd(ra, "printer-uri-supported");
6551 cupsArrayAdd(ra, "queued-job-count");
6552 cupsArrayAdd(ra, "reference-uri-schemes-supported");
6553 cupsArrayAdd(ra, "uri-authentication-supported");
6554 cupsArrayAdd(ra, "uri-security-supported");
6555 }
f7deaa1a 6556 else if (!strcmp(value, "printer-defaults"))
6557 {
6558 char *name; /* Option name */
6559
6560
6561 for (name = (char *)cupsArrayFirst(CommonDefaults);
6562 name;
6563 name = (char *)cupsArrayNext(CommonDefaults))
6564 cupsArrayAdd(ra, name);
6565 }
ef416fc2 6566 else if (!strcmp(value, "subscription-template"))
6567 {
6568 cupsArrayAdd(ra, "notify-attributes");
6569 cupsArrayAdd(ra, "notify-charset");
6570 cupsArrayAdd(ra, "notify-events");
6571 cupsArrayAdd(ra, "notify-lease-duration");
6572 cupsArrayAdd(ra, "notify-natural-language");
6573 cupsArrayAdd(ra, "notify-pull-method");
6574 cupsArrayAdd(ra, "notify-recipient-uri");
6575 cupsArrayAdd(ra, "notify-time-interval");
6576 cupsArrayAdd(ra, "notify-user-data");
6577 }
6578 else
6579 cupsArrayAdd(ra, value);
6580 }
6581
6582 return (ra);
6583}
6584
6585
6586/*
6587 * 'create_subscription()' - Create a notification subscription.
6588 */
6589
6590static void
6591create_subscription(
6592 cupsd_client_t *con, /* I - Client connection */
6593 ipp_attribute_t *uri) /* I - Printer URI */
6594{
6595 http_status_t status; /* Policy status */
6596 int i; /* Looping var */
6597 ipp_attribute_t *attr; /* Current attribute */
bc44d920 6598 cups_ptype_t dtype; /* Destination type (printer/class) */
ed486911 6599 char scheme[HTTP_MAX_URI],
6600 /* Scheme portion of URI */
ef416fc2 6601 userpass[HTTP_MAX_URI],
6602 /* Username portion of URI */
6603 host[HTTP_MAX_URI],
6604 /* Host portion of URI */
6605 resource[HTTP_MAX_URI];
6606 /* Resource portion of URI */
6607 int port; /* Port portion of URI */
6608 cupsd_printer_t *printer; /* Printer/class */
6609 cupsd_job_t *job; /* Job */
6610 int jobid; /* Job ID */
6611 cupsd_subscription_t *sub; /* Subscription object */
bc44d920 6612 const char *username, /* requesting-user-name or
6613 authenticated username */
ef416fc2 6614 *recipient, /* notify-recipient-uri */
6615 *pullmethod; /* notify-pull-method */
6616 ipp_attribute_t *user_data; /* notify-user-data */
6617 int interval, /* notify-time-interval */
6618 lease; /* notify-lease-duration */
6619 unsigned mask; /* notify-events */
f7deaa1a 6620 ipp_attribute_t *notify_events,/* notify-events(-default) */
6621 *notify_lease; /* notify-lease-duration(-default) */
ef416fc2 6622
6623
bd7854cb 6624#ifdef DEBUG
6625 for (attr = con->request->attrs; attr; attr = attr->next)
6626 {
6627 if (attr->group_tag != IPP_TAG_ZERO)
3dfe78b3 6628 cupsdLogMessage(CUPSD_LOG_DEBUG2, "g%04x v%04x %s", attr->group_tag,
bd7854cb 6629 attr->value_tag, attr->name);
6630 else
3dfe78b3 6631 cupsdLogMessage(CUPSD_LOG_DEBUG2, "----SEP----");
bd7854cb 6632 }
6633#endif /* DEBUG */
6634
ef416fc2 6635 /*
6636 * Is the destination valid?
6637 */
6638
fa73b229 6639 cupsdLogMessage(CUPSD_LOG_DEBUG,
6640 "cupsdCreateSubscription(con=%p(%d), uri=\"%s\")",
6641 con, con->http.fd, uri->values[0].string.text);
6642
ed486911 6643 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6644 sizeof(scheme), userpass, sizeof(userpass), host,
a4d04587 6645 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 6646
6647 if (!strcmp(resource, "/"))
6648 {
ef416fc2 6649 dtype = (cups_ptype_t)0;
6650 printer = NULL;
6651 }
6652 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
6653 {
ef416fc2 6654 dtype = (cups_ptype_t)0;
6655 printer = NULL;
6656 }
6657 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
6658 {
ef416fc2 6659 dtype = CUPS_PRINTER_CLASS;
6660 printer = NULL;
6661 }
f7deaa1a 6662 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 6663 {
6664 /*
6665 * Bad URI...
6666 */
6667
6668 send_ipp_status(con, IPP_NOT_FOUND,
6669 _("The printer or class was not found."));
6670 return;
6671 }
6672
6673 /*
6674 * Check policy...
6675 */
6676
6677 if (printer)
6678 {
f7deaa1a 6679 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
6680 NULL)) != HTTP_OK)
ef416fc2 6681 {
f899b121 6682 send_http_error(con, status, printer);
ef416fc2 6683 return;
6684 }
6685 }
6686 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6687 {
f899b121 6688 send_http_error(con, status, NULL);
ef416fc2 6689 return;
6690 }
6691
6692 /*
6693 * Get the user that is requesting the subscription...
6694 */
6695
e00b005a 6696 username = get_username(con);
ef416fc2 6697
6698 /*
6699 * Find the first subscription group attribute; return if we have
6700 * none...
6701 */
6702
6703 for (attr = con->request->attrs; attr; attr = attr->next)
6704 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
6705 break;
6706
6707 if (!attr)
6708 {
6709 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 6710 _("No subscription attributes in request"));
ef416fc2 6711 return;
6712 }
6713
6714 /*
6715 * Process the subscription attributes in the request...
6716 */
6717
fa73b229 6718 con->response->request.status.status_code = IPP_BAD_REQUEST;
6719
ef416fc2 6720 while (attr)
6721 {
6722 recipient = NULL;
6723 pullmethod = NULL;
6724 user_data = NULL;
6725 interval = 0;
6726 lease = DefaultLeaseDuration;
6727 jobid = 0;
6728 mask = CUPSD_EVENT_NONE;
6729
f7deaa1a 6730 if (printer)
6731 {
6732 notify_events = ippFindAttribute(printer->attrs, "notify-events-default",
6733 IPP_TAG_KEYWORD);
6734 notify_lease = ippFindAttribute(printer->attrs,
6735 "notify-lease-duration-default",
6736 IPP_TAG_INTEGER);
6737
6738 if (notify_lease)
6739 lease = notify_lease->values[0].integer;
6740 }
6741 else
6742 {
6743 notify_events = NULL;
6744 notify_lease = NULL;
6745 }
6746
ef416fc2 6747 while (attr && attr->group_tag != IPP_TAG_ZERO)
6748 {
7594b224 6749 if (!strcmp(attr->name, "notify-recipient-uri") &&
ef416fc2 6750 attr->value_tag == IPP_TAG_URI)
ed486911 6751 {
6752 /*
6753 * Validate the recipient scheme against the ServerBin/notifier
6754 * directory...
6755 */
6756
6757 char notifier[1024]; /* Notifier filename */
6758
6759
ef416fc2 6760 recipient = attr->values[0].string.text;
ed486911 6761
8ca02f3c 6762 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
6763 scheme, sizeof(scheme), userpass, sizeof(userpass),
6764 host, sizeof(host), &port,
6765 resource, sizeof(resource)) < HTTP_URI_OK)
ed486911 6766 {
6767 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 6768 _("Bad notify-recipient-uri URI \"%s\""), recipient);
ed486911 6769 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6770 "notify-status-code", IPP_URI_SCHEME);
6771 return;
6772 }
6773
6774 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
6775 scheme);
6776 if (access(notifier, X_OK))
6777 {
6778 send_ipp_status(con, IPP_NOT_POSSIBLE,
bc44d920 6779 _("notify-recipient-uri URI \"%s\" uses unknown "
4d301e69 6780 "scheme"), recipient);
ed486911 6781 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6782 "notify-status-code", IPP_URI_SCHEME);
6783 return;
6784 }
3dfe78b3
MS
6785
6786 if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
6787 {
6788 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 6789 _("notify-recipient-uri URI \"%s\" is already used"),
3dfe78b3
MS
6790 recipient);
6791 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6792 "notify-status-code", IPP_ATTRIBUTES);
6793 return;
6794 }
ed486911 6795 }
ef416fc2 6796 else if (!strcmp(attr->name, "notify-pull-method") &&
6797 attr->value_tag == IPP_TAG_KEYWORD)
ed486911 6798 {
ef416fc2 6799 pullmethod = attr->values[0].string.text;
ed486911 6800
6801 if (strcmp(pullmethod, "ippget"))
6802 {
6803 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 6804 _("Bad notify-pull-method \"%s\""), pullmethod);
ed486911 6805 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6806 "notify-status-code", IPP_ATTRIBUTES);
6807 return;
6808 }
6809 }
ef416fc2 6810 else if (!strcmp(attr->name, "notify-charset") &&
6811 attr->value_tag == IPP_TAG_CHARSET &&
6812 strcmp(attr->values[0].string.text, "us-ascii") &&
6813 strcmp(attr->values[0].string.text, "utf-8"))
6814 {
6815 send_ipp_status(con, IPP_CHARSET,
4d301e69 6816 _("Character set \"%s\" not supported"),
ef416fc2 6817 attr->values[0].string.text);
6818 return;
6819 }
6820 else if (!strcmp(attr->name, "notify-natural-language") &&
6821 (attr->value_tag != IPP_TAG_LANGUAGE ||
6822 strcmp(attr->values[0].string.text, DefaultLanguage)))
6823 {
6824 send_ipp_status(con, IPP_CHARSET,
4d301e69 6825 _("Language \"%s\" not supported"),
ef416fc2 6826 attr->values[0].string.text);
6827 return;
6828 }
6829 else if (!strcmp(attr->name, "notify-user-data") &&
6830 attr->value_tag == IPP_TAG_STRING)
6831 {
6832 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
6833 {
6834 send_ipp_status(con, IPP_REQUEST_VALUE,
6835 _("The notify-user-data value is too large "
4d301e69 6836 "(%d > 63 octets)"),
ef416fc2 6837 attr->values[0].unknown.length);
6838 return;
6839 }
6840
6841 user_data = attr;
6842 }
6843 else if (!strcmp(attr->name, "notify-events") &&
6844 attr->value_tag == IPP_TAG_KEYWORD)
f7deaa1a 6845 notify_events = attr;
ef416fc2 6846 else if (!strcmp(attr->name, "notify-lease-duration") &&
6847 attr->value_tag == IPP_TAG_INTEGER)
6848 lease = attr->values[0].integer;
6849 else if (!strcmp(attr->name, "notify-time-interval") &&
6850 attr->value_tag == IPP_TAG_INTEGER)
6851 interval = attr->values[0].integer;
6852 else if (!strcmp(attr->name, "notify-job-id") &&
6853 attr->value_tag == IPP_TAG_INTEGER)
6854 jobid = attr->values[0].integer;
6855
6856 attr = attr->next;
6857 }
6858
f7deaa1a 6859 if (notify_events)
6860 {
6861 for (i = 0; i < notify_events->num_values; i ++)
6862 mask |= cupsdEventValue(notify_events->values[i].string.text);
6863 }
6864
bd7854cb 6865 if (recipient)
6866 cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
6867 if (pullmethod)
6868 cupsdLogMessage(CUPSD_LOG_DEBUG, "pullmethod=\"%s\"", pullmethod);
6869 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-lease-duration=%d", lease);
6870 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-time-interval=%d", interval);
fa73b229 6871
ef416fc2 6872 if (!recipient && !pullmethod)
6873 break;
6874
6875 if (mask == CUPSD_EVENT_NONE)
6876 {
6877 if (jobid)
6878 mask = CUPSD_EVENT_JOB_COMPLETED;
6879 else if (printer)
6880 mask = CUPSD_EVENT_PRINTER_STATE_CHANGED;
6881 else
6882 {
6883 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 6884 _("notify-events not specified"));
ef416fc2 6885 return;
6886 }
6887 }
6888
bd7854cb 6889 if (MaxLeaseDuration && (lease == 0 || lease > MaxLeaseDuration))
ef416fc2 6890 {
6891 cupsdLogMessage(CUPSD_LOG_INFO,
6892 "create_subscription: Limiting notify-lease-duration to "
6893 "%d seconds.",
6894 MaxLeaseDuration);
6895 lease = MaxLeaseDuration;
6896 }
6897
6898 if (jobid)
6899 {
6900 if ((job = cupsdFindJob(jobid)) == NULL)
6901 {
4d301e69 6902 send_ipp_status(con, IPP_NOT_FOUND, _("Job %d not found"), jobid);
ef416fc2 6903 return;
6904 }
6905 }
6906 else
6907 job = NULL;
6908
52f6f666
MS
6909 if ((sub = cupsdAddSubscription(mask, printer, job, recipient, 0)) == NULL)
6910 {
6911 send_ipp_status(con, IPP_TOO_MANY_SUBSCRIPTIONS,
6912 _("There are too many subscriptions."));
6913 return;
6914 }
ef416fc2 6915
fa73b229 6916 if (job)
6917 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d",
6918 sub->id, job->id);
6919 else if (printer)
6920 cupsdLogMessage(CUPSD_LOG_DEBUG,
6921 "Added subscription %d for printer \"%s\"",
6922 sub->id, printer->name);
6923 else
6924 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for server",
6925 sub->id);
6926
ef416fc2 6927 sub->interval = interval;
6928 sub->lease = lease;
bd7854cb 6929 sub->expire = lease ? time(NULL) + lease : 0;
ef416fc2 6930
6931 cupsdSetString(&sub->owner, username);
6932
6933 if (user_data)
6934 {
6935 sub->user_data_len = user_data->values[0].unknown.length;
6936 memcpy(sub->user_data, user_data->values[0].unknown.data,
6937 sub->user_data_len);
6938 }
6939
6940 ippAddSeparator(con->response);
6941 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
6942 "notify-subscription-id", sub->id);
6943
fa73b229 6944 con->response->request.status.status_code = IPP_OK;
6945
ef416fc2 6946 if (attr)
6947 attr = attr->next;
6948 }
6949
3dfe78b3 6950 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
ef416fc2 6951}
6952
6953
6954/*
6955 * 'delete_printer()' - Remove a printer or class from the system.
6956 */
6957
6958static void
6959delete_printer(cupsd_client_t *con, /* I - Client connection */
6960 ipp_attribute_t *uri) /* I - URI of printer or class */
6961{
6962 http_status_t status; /* Policy status */
bc44d920 6963 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 6964 cupsd_printer_t *printer; /* Printer/class */
6965 char filename[1024]; /* Script/PPD filename */
6966
6967
6968 cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", con,
6969 con->http.fd, uri->values[0].string.text);
6970
6971 /*
6972 * Do we have a valid URI?
6973 */
6974
f7deaa1a 6975 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 6976 {
6977 /*
6978 * Bad URI...
6979 */
6980
6981 send_ipp_status(con, IPP_NOT_FOUND,
6982 _("The printer or class was not found."));
6983 return;
6984 }
6985
6986 /*
6987 * Check policy...
6988 */
6989
6990 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6991 {
f899b121 6992 send_http_error(con, status, NULL);
ef416fc2 6993 return;
6994 }
6995
6996 /*
6997 * Remove old jobs...
6998 */
6999
f7deaa1a 7000 cupsdCancelJobs(printer->name, NULL, 1);
ef416fc2 7001
7002 /*
7003 * Remove old subscriptions and send a "deleted printer" event...
7004 */
7005
7006 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
7007 "%s \"%s\" deleted by \"%s\".",
7008 (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
f7deaa1a 7009 printer->name, get_username(con));
ef416fc2 7010
7011 cupsdExpireSubscriptions(printer, NULL);
f7deaa1a 7012
ef416fc2 7013 /*
7014 * Remove any old PPD or script files...
7015 */
7016
f7deaa1a 7017 snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot,
7018 printer->name);
ef416fc2 7019 unlink(filename);
7020
f7deaa1a 7021 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
7022 printer->name);
ef416fc2 7023 unlink(filename);
7024
7cf5915e
MS
7025 snprintf(filename, sizeof(filename), "%s/%s.ipp4", CacheDir, printer->name);
7026 unlink(filename);
7027
7028 snprintf(filename, sizeof(filename), "%s/%s.png", CacheDir, printer->name);
61cf44e2
MS
7029 unlink(filename);
7030
7cf5915e 7031 snprintf(filename, sizeof(filename), "%s/%s.pwg3", CacheDir, printer->name);
54afec33
MS
7032 unlink(filename);
7033
568fa3fa
MS
7034#ifdef __APPLE__
7035 /*
7036 * Unregister color profiles...
7037 */
7038
7039 apple_unregister_profiles(printer);
7040#endif /* __APPLE__ */
7041
ef416fc2 7042 if (dtype & CUPS_PRINTER_CLASS)
7043 {
f7deaa1a 7044 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".",
7045 printer->name, get_username(con));
ef416fc2 7046
7047 cupsdDeletePrinter(printer, 0);
3dfe78b3 7048 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
ef416fc2 7049 }
7050 else
7051 {
f7deaa1a 7052 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".",
7053 printer->name, get_username(con));
ef416fc2 7054
f8b3a85b
MS
7055 if (cupsdDeletePrinter(printer, 0))
7056 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
7057
3dfe78b3 7058 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 7059 }
7060
3dfe78b3 7061 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
ef416fc2 7062
7063 /*
7064 * Return with no errors...
7065 */
7066
7067 con->response->request.status.status_code = IPP_OK;
7068}
7069
7070
7071/*
7072 * 'get_default()' - Get the default destination.
7073 */
7074
7075static void
7076get_default(cupsd_client_t *con) /* I - Client connection */
7077{
fa73b229 7078 http_status_t status; /* Policy status */
7079 cups_array_t *ra; /* Requested attributes array */
ef416fc2 7080
7081
7082 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->http.fd);
7083
7084 /*
7085 * Check policy...
7086 */
7087
7088 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7089 {
f899b121 7090 send_http_error(con, status, NULL);
ef416fc2 7091 return;
7092 }
7093
fa73b229 7094 if (DefaultPrinter)
ef416fc2 7095 {
fa73b229 7096 ra = create_requested_array(con->request);
ef416fc2 7097
fa73b229 7098 copy_printer_attrs(con, DefaultPrinter, ra);
ef416fc2 7099
fa73b229 7100 cupsArrayDelete(ra);
ef416fc2 7101
fa73b229 7102 con->response->request.status.status_code = IPP_OK;
ef416fc2 7103 }
7104 else
7105 send_ipp_status(con, IPP_NOT_FOUND, _("No default printer"));
7106}
7107
7108
7109/*
7110 * 'get_devices()' - Get the list of available devices on the local system.
7111 */
7112
7113static void
7114get_devices(cupsd_client_t *con) /* I - Client connection */
7115{
7116 http_status_t status; /* Policy status */
ae71f5de
MS
7117 ipp_attribute_t *limit, /* limit attribute */
7118 *timeout, /* timeout attribute */
7119 *requested, /* requested-attributes attribute */
ed6e7faf
MS
7120 *exclude, /* exclude-schemes attribute */
7121 *include; /* include-schemes attribute */
ef416fc2 7122 char command[1024], /* cups-deviced command */
ed6e7faf 7123 options[2048], /* Options to pass to command */
ae71f5de 7124 requested_str[256],
89d46774 7125 /* String for requested attributes */
ed6e7faf
MS
7126 exclude_str[512],
7127 /* String for excluded schemes */
7128 include_str[512];
7129 /* String for included schemes */
ef416fc2 7130
7131
7132 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->http.fd);
7133
7134 /*
7135 * Check policy...
7136 */
7137
7138 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7139 {
f899b121 7140 send_http_error(con, status, NULL);
ef416fc2 7141 return;
7142 }
7143
7144 /*
7145 * Run cups-deviced command with the given options...
7146 */
7147
ae71f5de
MS
7148 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
7149 timeout = ippFindAttribute(con->request, "timeout", IPP_TAG_INTEGER);
ef416fc2 7150 requested = ippFindAttribute(con->request, "requested-attributes",
7151 IPP_TAG_KEYWORD);
ae71f5de 7152 exclude = ippFindAttribute(con->request, "exclude-schemes", IPP_TAG_NAME);
ed6e7faf 7153 include = ippFindAttribute(con->request, "include-schemes", IPP_TAG_NAME);
ef416fc2 7154
7155 if (requested)
89d46774 7156 url_encode_attr(requested, requested_str, sizeof(requested_str));
ef416fc2 7157 else
89d46774 7158 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
ef416fc2 7159
ae71f5de
MS
7160 if (exclude)
7161 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
7162 else
7163 exclude_str[0] = '\0';
7164
ed6e7faf
MS
7165 if (include)
7166 url_encode_attr(include, include_str, sizeof(include_str));
7167 else
7168 include_str[0] = '\0';
7169
ef416fc2 7170 snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
7171 snprintf(options, sizeof(options),
ed6e7faf 7172 "%d+%d+%d+%d+%s%s%s%s%s",
ef416fc2 7173 con->request->request.op.request_id,
ae71f5de 7174 limit ? limit->values[0].integer : 0,
7a0cbd5e 7175 timeout ? timeout->values[0].integer : 15,
ae71f5de
MS
7176 (int)User,
7177 requested_str,
ed6e7faf
MS
7178 exclude_str[0] ? "%20" : "", exclude_str,
7179 include_str[0] ? "%20" : "", include_str);
ef416fc2 7180
7181 if (cupsdSendCommand(con, command, options, 1))
7182 {
7183 /*
7184 * Command started successfully, don't send an IPP response here...
7185 */
7186
7187 ippDelete(con->response);
7188 con->response = NULL;
7189 }
7190 else
7191 {
7192 /*
7193 * Command failed, return "internal error" so the user knows something
7194 * went wrong...
7195 */
7196
7197 send_ipp_status(con, IPP_INTERNAL_ERROR,
7198 _("cups-deviced failed to execute."));
7199 }
7200}
7201
7202
2e4ff8af
MS
7203/*
7204 * 'get_document()' - Get a copy of a job file.
7205 */
7206
7207static void
7208get_document(cupsd_client_t *con, /* I - Client connection */
7209 ipp_attribute_t *uri) /* I - Job URI */
7210{
7211 http_status_t status; /* Policy status */
7212 ipp_attribute_t *attr; /* Current attribute */
7213 int jobid; /* Job ID */
7214 int docnum; /* Document number */
7215 cupsd_job_t *job; /* Current job */
61cf44e2 7216 char scheme[HTTP_MAX_URI], /* Method portion of URI */
2e4ff8af
MS
7217 username[HTTP_MAX_URI], /* Username portion of URI */
7218 host[HTTP_MAX_URI], /* Host portion of URI */
7219 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7220 int port; /* Port portion of URI */
7221 char filename[1024], /* Filename for document */
7222 format[1024]; /* Format for document */
7223
7224
7225 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", con,
7226 con->http.fd, uri->values[0].string.text);
7227
7228 /*
7229 * See if we have a job URI or a printer URI...
7230 */
7231
7232 if (!strcmp(uri->name, "printer-uri"))
7233 {
7234 /*
7235 * Got a printer URI; see if we also have a job-id attribute...
7236 */
7237
7238 if ((attr = ippFindAttribute(con->request, "job-id",
7239 IPP_TAG_INTEGER)) == NULL)
7240 {
7241 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 7242 _("Got a printer-uri attribute but no job-id"));
2e4ff8af
MS
7243 return;
7244 }
7245
7246 jobid = attr->values[0].integer;
7247 }
7248 else
7249 {
7250 /*
7251 * Got a job URI; parse it to get the job ID...
7252 */
7253
61cf44e2
MS
7254 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7255 sizeof(scheme), username, sizeof(username), host,
2e4ff8af
MS
7256 sizeof(host), &port, resource, sizeof(resource));
7257
7258 if (strncmp(resource, "/jobs/", 6))
7259 {
7260 /*
7261 * Not a valid URI!
7262 */
7263
7264 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 7265 _("Bad job-uri attribute \"%s\""),
2e4ff8af
MS
7266 uri->values[0].string.text);
7267 return;
7268 }
7269
7270 jobid = atoi(resource + 6);
7271 }
7272
7273 /*
7274 * See if the job exists...
7275 */
7276
7277 if ((job = cupsdFindJob(jobid)) == NULL)
7278 {
7279 /*
7280 * Nope - return a "not found" error...
7281 */
7282
4d301e69 7283 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
2e4ff8af
MS
7284 return;
7285 }
7286
7287 /*
7288 * Check policy...
7289 */
7290
7291 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7292 {
7293 send_http_error(con, status, NULL);
7294 return;
7295 }
7296
7297 /*
7298 * Get the document number...
7299 */
7300
7301 if ((attr = ippFindAttribute(con->request, "document-number",
7302 IPP_TAG_INTEGER)) == NULL)
7303 {
7304 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 7305 _("Missing document-number attribute"));
2e4ff8af
MS
7306 return;
7307 }
7308
7309 if ((docnum = attr->values[0].integer) < 1 || docnum > job->num_files ||
7310 attr->num_values > 1)
7311 {
7312 send_ipp_status(con, IPP_NOT_FOUND, _("Document %d not found in job %d."),
7313 docnum, jobid);
7314 return;
7315 }
7316
7317 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, jobid,
7318 docnum);
7319 if ((con->file = open(filename, O_RDONLY)) == -1)
7320 {
7321 cupsdLogMessage(CUPSD_LOG_ERROR,
7322 "Unable to open document %d in job %d - %s", docnum, jobid,
7323 strerror(errno));
7324 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 7325 _("Unable to open document %d in job %d"), docnum, jobid);
2e4ff8af
MS
7326 return;
7327 }
7328
7329 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
7330
7331 cupsdLoadJob(job);
7332
7333 snprintf(format, sizeof(format), "%s/%s", job->filetypes[docnum - 1]->super,
7334 job->filetypes[docnum - 1]->type);
7335
7336 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
7337 NULL, format);
7338 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "document-number",
7339 docnum);
7340 if ((attr = ippFindAttribute(job->attrs, "document-name",
7341 IPP_TAG_NAME)) != NULL)
7342 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "document-name",
7343 NULL, attr->values[0].string.text);
7344}
7345
7346
ef416fc2 7347/*
fa73b229 7348 * 'get_job_attrs()' - Get job attributes.
ef416fc2 7349 */
7350
7351static void
fa73b229 7352get_job_attrs(cupsd_client_t *con, /* I - Client connection */
7353 ipp_attribute_t *uri) /* I - Job URI */
ef416fc2 7354{
7355 http_status_t status; /* Policy status */
fa73b229 7356 ipp_attribute_t *attr; /* Current attribute */
7357 int jobid; /* Job ID */
7358 cupsd_job_t *job; /* Current job */
ef55b745 7359 cupsd_printer_t *printer; /* Current printer */
10d09e33
MS
7360 cupsd_policy_t *policy; /* Current security policy */
7361 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
ef416fc2 7362 username[HTTP_MAX_URI], /* Username portion of URI */
7363 host[HTTP_MAX_URI], /* Host portion of URI */
7364 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7365 int port; /* Port portion of URI */
10d09e33
MS
7366 cups_array_t *ra, /* Requested attributes array */
7367 *exclude; /* Private attributes array */
ef416fc2 7368
7369
fa73b229 7370 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", con,
7371 con->http.fd, uri->values[0].string.text);
ef416fc2 7372
7373 /*
fa73b229 7374 * See if we have a job URI or a printer URI...
7375 */
7376
7377 if (!strcmp(uri->name, "printer-uri"))
7378 {
7379 /*
7380 * Got a printer URI; see if we also have a job-id attribute...
7381 */
7382
7383 if ((attr = ippFindAttribute(con->request, "job-id",
7384 IPP_TAG_INTEGER)) == NULL)
7385 {
7386 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 7387 _("Got a printer-uri attribute but no job-id"));
fa73b229 7388 return;
7389 }
7390
7391 jobid = attr->values[0].integer;
7392 }
7393 else
7394 {
7395 /*
7396 * Got a job URI; parse it to get the job ID...
7397 */
7398
61cf44e2
MS
7399 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7400 sizeof(scheme), username, sizeof(username), host,
a4d04587 7401 sizeof(host), &port, resource, sizeof(resource));
fa73b229 7402
7403 if (strncmp(resource, "/jobs/", 6))
7404 {
7405 /*
7406 * Not a valid URI!
7407 */
7408
7409 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 7410 _("Bad job-uri attribute \"%s\""),
fa73b229 7411 uri->values[0].string.text);
7412 return;
7413 }
7414
7415 jobid = atoi(resource + 6);
7416 }
7417
7418 /*
7419 * See if the job exists...
7420 */
7421
7422 if ((job = cupsdFindJob(jobid)) == NULL)
7423 {
7424 /*
7425 * Nope - return a "not found" error...
7426 */
7427
4d301e69 7428 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
fa73b229 7429 return;
7430 }
7431
7432 /*
7433 * Check policy...
7434 */
7435
ef55b745
MS
7436 if ((printer = job->printer) == NULL)
7437 printer = cupsdFindDest(job->dest);
7438
7439 if (printer)
10d09e33
MS
7440 policy = printer->op_policy_ptr;
7441 else
7442 policy = DefaultPolicyPtr;
7443
7444 if ((status = cupsdCheckPolicy(policy, con, job->username)) != HTTP_OK)
fa73b229 7445 {
f899b121 7446 send_http_error(con, status, NULL);
fa73b229 7447 return;
7448 }
7449
10d09e33
MS
7450 exclude = cupsdGetPrivateAttrs(policy, con, printer, job->username);
7451
fa73b229 7452 /*
7453 * Copy attributes...
7454 */
7455
bd7854cb 7456 cupsdLoadJob(job);
7457
fa73b229 7458 ra = create_requested_array(con->request);
10d09e33 7459 copy_job_attrs(con, job, ra, exclude);
fa73b229 7460 cupsArrayDelete(ra);
7461
7462 con->response->request.status.status_code = IPP_OK;
7463}
7464
7465
7466/*
7467 * 'get_jobs()' - Get a list of jobs for the specified printer.
7468 */
7469
7470static void
7471get_jobs(cupsd_client_t *con, /* I - Client connection */
7472 ipp_attribute_t *uri) /* I - Printer URI */
7473{
7474 http_status_t status; /* Policy status */
7475 ipp_attribute_t *attr; /* Current attribute */
7476 const char *dest; /* Destination */
bc44d920 7477 cups_ptype_t dtype; /* Destination type (printer/class) */
fa73b229 7478 cups_ptype_t dmask; /* Destination type mask */
f7deaa1a 7479 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
fa73b229 7480 username[HTTP_MAX_URI], /* Username portion of URI */
7481 host[HTTP_MAX_URI], /* Host portion of URI */
7482 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7483 int port; /* Port portion of URI */
aaf19ab0
MS
7484 int job_comparison; /* Job comparison */
7485 ipp_jstate_t job_state; /* job-state value */
fa73b229 7486 int first_job_id; /* First job ID */
7487 int limit; /* Maximum number of jobs to return */
7488 int count; /* Number of jobs that match */
aaf19ab0 7489 ipp_attribute_t *job_ids; /* job-ids attribute */
fa73b229 7490 cupsd_job_t *job; /* Current job pointer */
7491 cupsd_printer_t *printer; /* Printer */
7492 cups_array_t *list; /* Which job list... */
10d09e33
MS
7493 cups_array_t *ra, /* Requested attributes array */
7494 *exclude; /* Private attributes array */
7495 cupsd_policy_t *policy; /* Current policy */
fa73b229 7496
7497
7498 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", con, con->http.fd,
7499 uri->values[0].string.text);
7500
7501 /*
7502 * Is the destination valid?
ef416fc2 7503 */
7504
4b3f67ff
MS
7505 if (strcmp(uri->name, "printer-uri"))
7506 {
4d301e69 7507 send_ipp_status(con, IPP_BAD_REQUEST, _("No printer-uri in request"));
4b3f67ff
MS
7508 return;
7509 }
7510
f7deaa1a 7511 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7512 sizeof(scheme), username, sizeof(username), host,
a4d04587 7513 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 7514
b9faaae1 7515 if (!strcmp(resource, "/") || !strcmp(resource, "/jobs"))
ef416fc2 7516 {
7517 dest = NULL;
7518 dtype = (cups_ptype_t)0;
7519 dmask = (cups_ptype_t)0;
7520 printer = NULL;
7521 }
fa73b229 7522 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
ef416fc2 7523 {
7524 dest = NULL;
7525 dtype = (cups_ptype_t)0;
7526 dmask = CUPS_PRINTER_CLASS;
7527 printer = NULL;
7528 }
fa73b229 7529 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
ef416fc2 7530 {
7531 dest = NULL;
7532 dtype = CUPS_PRINTER_CLASS;
7533 dmask = CUPS_PRINTER_CLASS;
7534 printer = NULL;
7535 }
f7deaa1a 7536 else if ((dest = cupsdValidateDest(uri->values[0].string.text, &dtype,
7537 &printer)) == NULL)
ef416fc2 7538 {
7539 /*
7540 * Bad URI...
7541 */
7542
7543 send_ipp_status(con, IPP_NOT_FOUND,
7544 _("The printer or class was not found."));
7545 return;
7546 }
7547 else
b86bc4cf 7548 {
7549 dtype &= CUPS_PRINTER_CLASS;
ef416fc2 7550 dmask = CUPS_PRINTER_CLASS;
b86bc4cf 7551 }
ef416fc2 7552
7553 /*
7554 * Check policy...
7555 */
7556
7557 if (printer)
10d09e33
MS
7558 policy = printer->op_policy_ptr;
7559 else
7560 policy = DefaultPolicyPtr;
7561
7562 if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
ef416fc2 7563 {
f899b121 7564 send_http_error(con, status, NULL);
ef416fc2 7565 return;
7566 }
7567
aaf19ab0
MS
7568 job_ids = ippFindAttribute(con->request, "job-ids", IPP_TAG_INTEGER);
7569
ef416fc2 7570 /*
7571 * See if the "which-jobs" attribute have been specified...
7572 */
7573
fa73b229 7574 if ((attr = ippFindAttribute(con->request, "which-jobs",
aaf19ab0
MS
7575 IPP_TAG_KEYWORD)) != NULL && job_ids)
7576 {
7577 send_ipp_status(con, IPP_CONFLICT,
7578 _("The %s attribute cannot be provided with job-ids."),
7579 "which-jobs");
7580 return;
7581 }
7582 else if (!attr || !strcmp(attr->values[0].string.text, "not-completed"))
7583 {
7584 job_comparison = -1;
7585 job_state = IPP_JOB_STOPPED;
7586 list = Jobs;
7587 }
7588 else if (!strcmp(attr->values[0].string.text, "completed"))
7589 {
7590 job_comparison = 1;
7591 job_state = IPP_JOB_CANCELED;
7592 list = Jobs;
7593 }
7594 else if (!strcmp(attr->values[0].string.text, "aborted"))
7595 {
7596 job_comparison = 0;
7597 job_state = IPP_JOB_ABORTED;
7598 list = Jobs;
7599 }
7600 else if (!strcmp(attr->values[0].string.text, "all"))
7601 {
7602 job_comparison = 1;
7603 job_state = IPP_JOB_PENDING;
7604 list = Jobs;
7605 }
7606 else if (!strcmp(attr->values[0].string.text, "canceled"))
7607 {
7608 job_comparison = 0;
7609 job_state = IPP_JOB_CANCELED;
7610 list = Jobs;
7611 }
7612 else if (!strcmp(attr->values[0].string.text, "pending"))
ef416fc2 7613 {
aaf19ab0
MS
7614 job_comparison = 0;
7615 job_state = IPP_JOB_PENDING;
7616 list = ActiveJobs;
ef416fc2 7617 }
aaf19ab0 7618 else if (!strcmp(attr->values[0].string.text, "pending-held"))
ef416fc2 7619 {
aaf19ab0
MS
7620 job_comparison = 0;
7621 job_state = IPP_JOB_HELD;
7622 list = ActiveJobs;
ef416fc2 7623 }
aaf19ab0 7624 else if (!strcmp(attr->values[0].string.text, "processing"))
3dfe78b3 7625 {
aaf19ab0
MS
7626 job_comparison = 0;
7627 job_state = IPP_JOB_PROCESSING;
7628 list = PrintingJobs;
7629 }
7630 else if (!strcmp(attr->values[0].string.text, "processing-stopped"))
7631 {
7632 job_comparison = 0;
7633 job_state = IPP_JOB_STOPPED;
7634 list = ActiveJobs;
3dfe78b3 7635 }
ef416fc2 7636 else
7637 {
aaf19ab0
MS
7638 send_ipp_status(con, IPP_ATTRIBUTES,
7639 _("The which-jobs value \"%s\" is not supported."),
7640 attr->values[0].string.text);
7641 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
7642 "which-jobs", NULL, attr->values[0].string.text);
7643 return;
ef416fc2 7644 }
7645
7646 /*
7647 * See if they want to limit the number of jobs reported...
7648 */
7649
fa73b229 7650 if ((attr = ippFindAttribute(con->request, "limit",
7651 IPP_TAG_INTEGER)) != NULL)
aaf19ab0
MS
7652 {
7653 if (job_ids)
7654 {
7655 send_ipp_status(con, IPP_CONFLICT,
7656 _("The %s attribute cannot be provided with job-ids."),
7657 "limit");
7658 return;
7659 }
7660
ef416fc2 7661 limit = attr->values[0].integer;
aaf19ab0 7662 }
ef416fc2 7663 else
3dfe78b3 7664 limit = 0;
ef416fc2 7665
7666 if ((attr = ippFindAttribute(con->request, "first-job-id",
7667 IPP_TAG_INTEGER)) != NULL)
aaf19ab0
MS
7668 {
7669 if (job_ids)
7670 {
7671 send_ipp_status(con, IPP_CONFLICT,
7672 _("The %s attribute cannot be provided with job-ids."),
7673 "first-job-id");
7674 return;
7675 }
7676
ef416fc2 7677 first_job_id = attr->values[0].integer;
aaf19ab0 7678 }
ef416fc2 7679 else
7680 first_job_id = 1;
7681
7682 /*
7683 * See if we only want to see jobs for a specific user...
7684 */
7685
fa73b229 7686 if ((attr = ippFindAttribute(con->request, "my-jobs",
aaf19ab0
MS
7687 IPP_TAG_BOOLEAN)) != NULL && job_ids)
7688 {
7689 send_ipp_status(con, IPP_CONFLICT,
7690 _("The %s attribute cannot be provided with job-ids."),
7691 "my-jobs");
7692 return;
7693 }
7694 else if (attr && attr->values[0].boolean)
e00b005a 7695 strlcpy(username, get_username(con), sizeof(username));
ef416fc2 7696 else
7697 username[0] = '\0';
7698
5a662dc0
MS
7699 if ((ra = create_requested_array(con->request)) == NULL &&
7700 !ippFindAttribute(con->request, "requested-attributes", IPP_TAG_KEYWORD))
7701 {
7702 /*
7703 * IPP conformance - Get-Jobs has a default requested-attributes value of
7704 * "job-id" and "job-uri".
7705 */
7706
7707 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
7708 cupsArrayAdd(ra, "job-id");
7709 cupsArrayAdd(ra, "job-uri");
7710 }
ef416fc2 7711
7712 /*
7713 * OK, build a list of jobs for this printer...
7714 */
7715
aaf19ab0 7716 if (job_ids)
ef416fc2 7717 {
aaf19ab0 7718 int i; /* Looping var */
ef416fc2 7719
aaf19ab0
MS
7720 for (i = 0; i < job_ids->num_values; i ++)
7721 {
4220952d 7722 if (!cupsdFindJob(job_ids->values[i].integer))
aaf19ab0
MS
7723 break;
7724 }
ef416fc2 7725
aaf19ab0
MS
7726 if (i < job_ids->num_values)
7727 {
7728 send_ipp_status(con, IPP_NOT_FOUND, _("job-id %d not found."),
7729 job_ids->values[i].integer);
7730 return;
7731 }
0a682745 7732
aaf19ab0
MS
7733 for (i = 0; i < job_ids->num_values; i ++)
7734 {
7735 job = cupsdFindJob(job_ids->values[i].integer);
0a682745 7736
aaf19ab0 7737 cupsdLoadJob(job);
ef416fc2 7738
aaf19ab0
MS
7739 if (!job->attrs)
7740 {
7741 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d",
7742 job->id);
7743 continue;
7744 }
ef416fc2 7745
aaf19ab0
MS
7746 if (i > 0)
7747 ippAddSeparator(con->response);
bd7854cb 7748
10d09e33
MS
7749 exclude = cupsdGetPrivateAttrs(job->printer ?
7750 job->printer->op_policy_ptr :
7751 policy, con, job->printer,
7752 job->username);
7753
7754 copy_job_attrs(con, job, ra, exclude);
d1c13e16 7755 }
aaf19ab0
MS
7756 }
7757 else
7758 {
7759 for (count = 0, job = (cupsd_job_t *)cupsArrayFirst(list);
7760 (limit <= 0 || count < limit) && job;
7761 job = (cupsd_job_t *)cupsArrayNext(list))
7762 {
7763 /*
7764 * Filter out jobs that don't match...
7765 */
bd7854cb 7766
aaf19ab0
MS
7767 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7768 "get_jobs: job->id=%d, dest=\"%s\", username=\"%s\", "
7769 "state_value=%d, attrs=%p", job->id, job->dest,
7770 job->username, job->state_value, job->attrs);
7594b224 7771
aaf19ab0
MS
7772 if (!job->dest || !job->username)
7773 cupsdLoadJob(job);
fa73b229 7774
aaf19ab0
MS
7775 if (!job->dest || !job->username)
7776 continue;
ef416fc2 7777
aaf19ab0
MS
7778 if ((dest && strcmp(job->dest, dest)) &&
7779 (!job->printer || !dest || strcmp(job->printer->name, dest)))
7780 continue;
7781 if ((job->dtype & dmask) != dtype &&
7782 (!job->printer || (job->printer->type & dmask) != dtype))
7783 continue;
ef416fc2 7784
aaf19ab0
MS
7785 if ((job_comparison < 0 && job->state_value > job_state) ||
7786 (job_comparison == 0 && job->state_value != job_state) ||
7787 (job_comparison > 0 && job->state_value < job_state))
7788 continue;
7789
7790 if (job->id < first_job_id)
7791 continue;
7792
7793 cupsdLoadJob(job);
7794
7795 if (!job->attrs)
7796 {
7797 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d",
7798 job->id);
7799 continue;
7800 }
7801
7802 if (username[0] && strcasecmp(username, job->username))
7803 continue;
7804
7805 if (count > 0)
7806 ippAddSeparator(con->response);
7807
7808 count ++;
7809
10d09e33
MS
7810 exclude = cupsdGetPrivateAttrs(job->printer ?
7811 job->printer->op_policy_ptr :
7812 policy, con, job->printer,
7813 job->username);
7814
7815 copy_job_attrs(con, job, ra, exclude);
aaf19ab0
MS
7816 }
7817
7818 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count=%d", count);
7819 }
d1c13e16 7820
fa73b229 7821 cupsArrayDelete(ra);
ef416fc2 7822
fa73b229 7823 con->response->request.status.status_code = IPP_OK;
ef416fc2 7824}
7825
7826
7827/*
7828 * 'get_notifications()' - Get events for a subscription.
7829 */
7830
7831static void
bd7854cb 7832get_notifications(cupsd_client_t *con) /* I - Client connection */
ef416fc2 7833{
bd7854cb 7834 int i, j; /* Looping vars */
7835 http_status_t status; /* Policy status */
7836 cupsd_subscription_t *sub; /* Subscription */
7837 ipp_attribute_t *ids, /* notify-subscription-ids */
7838 *sequences; /* notify-sequence-numbers */
7839 int min_seq; /* Minimum sequence number */
7840 int interval; /* Poll interval */
7841
7842
f7deaa1a 7843 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_notifications(con=%p[%d])",
bd7854cb 7844 con, con->http.fd);
7845
7846 /*
7847 * Get subscription attributes...
7848 */
7849
7850 ids = ippFindAttribute(con->request, "notify-subscription-ids",
7851 IPP_TAG_INTEGER);
7852 sequences = ippFindAttribute(con->request, "notify-sequence-numbers",
7853 IPP_TAG_INTEGER);
7854
7855 if (!ids)
7856 {
7857 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 7858 _("Missing notify-subscription-ids attribute"));
bd7854cb 7859 return;
7860 }
7861
7862 /*
7863 * Are the subscription IDs valid?
7864 */
7865
7866 for (i = 0, interval = 60; i < ids->num_values; i ++)
7867 {
7868 if ((sub = cupsdFindSubscription(ids->values[i].integer)) == NULL)
7869 {
7870 /*
7871 * Bad subscription ID...
7872 */
7873
7874 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 7875 _("notify-subscription-id %d no good"),
bd7854cb 7876 ids->values[i].integer);
7877 return;
7878 }
7879
7880 /*
7881 * Check policy...
7882 */
7883
7884 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
7885 DefaultPolicyPtr,
7886 con, sub->owner)) != HTTP_OK)
7887 {
f899b121 7888 send_http_error(con, status, sub->dest);
bd7854cb 7889 return;
7890 }
7891
7892 /*
7893 * Check the subscription type and update the interval accordingly.
7894 */
7895
7896 if (sub->job && sub->job->state_value == IPP_JOB_PROCESSING &&
7897 interval > 10)
7898 interval = 10;
7899 else if (sub->job && sub->job->state_value >= IPP_JOB_STOPPED)
7900 interval = 0;
7901 else if (sub->dest && sub->dest->state == IPP_PRINTER_PROCESSING &&
7902 interval > 30)
7903 interval = 30;
7904 }
7905
7906 /*
7907 * Tell the client to poll again in N seconds...
7908 */
7909
7910 if (interval > 0)
7911 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
7912 "notify-get-interval", interval);
7913
7914 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
7915 "printer-up-time", time(NULL));
7916
7917 /*
7918 * Copy the subscription event attributes to the response.
7919 */
7920
7921 con->response->request.status.status_code =
7922 interval ? IPP_OK : IPP_OK_EVENTS_COMPLETE;
7923
7924 for (i = 0; i < ids->num_values; i ++)
7925 {
7926 /*
7927 * Get the subscription and sequence number...
7928 */
7929
7930 sub = cupsdFindSubscription(ids->values[i].integer);
7931
7932 if (sequences && i < sequences->num_values)
7933 min_seq = sequences->values[i].integer;
7934 else
7935 min_seq = 1;
7936
7937 /*
7938 * If we don't have any new events, nothing to do here...
7939 */
7940
10d09e33 7941 if (min_seq > (sub->first_event_id + cupsArrayCount(sub->events)))
bd7854cb 7942 continue;
7943
7944 /*
7945 * Otherwise copy all of the new events...
7946 */
7947
7948 if (sub->first_event_id > min_seq)
7949 j = 0;
7950 else
7951 j = min_seq - sub->first_event_id;
7952
10d09e33 7953 for (; j < cupsArrayCount(sub->events); j ++)
b423cd4c 7954 {
7955 ippAddSeparator(con->response);
ef416fc2 7956
10d09e33
MS
7957 copy_attrs(con->response,
7958 ((cupsd_event_t *)cupsArrayIndex(sub->events, j))->attrs, NULL,
7959 IPP_TAG_EVENT_NOTIFICATION, 0, NULL);
b423cd4c 7960 }
7961 }
7962}
ef416fc2 7963
ef416fc2 7964
b94498cf 7965/*
7966 * 'get_ppd()' - Get a named PPD from the local system.
7967 */
7968
7969static void
7970get_ppd(cupsd_client_t *con, /* I - Client connection */
7971 ipp_attribute_t *uri) /* I - Printer URI or PPD name */
7972{
7973 http_status_t status; /* Policy status */
7974 cupsd_printer_t *dest; /* Destination */
7975 cups_ptype_t dtype; /* Destination type */
7976
7977
7978 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppd(%p[%d], %p[%s=%s])", con,
7979 con->http.fd, uri, uri->name, uri->values[0].string.text);
7980
7981 if (!strcmp(uri->name, "ppd-name"))
7982 {
7983 /*
7984 * Return a PPD file from cups-driverd...
7985 */
7986
7987 char command[1024], /* cups-driverd command */
7988 options[1024], /* Options to pass to command */
7989 ppd_name[1024]; /* ppd-name */
7990
7991
7992 /*
7993 * Check policy...
7994 */
7995
7996 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7997 {
7998 send_http_error(con, status, NULL);
7999 return;
8000 }
8001
8002 /*
8003 * Run cups-driverd command with the given options...
8004 */
8005
8006 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
8007 url_encode_string(uri->values[0].string.text, ppd_name, sizeof(ppd_name));
8008 snprintf(options, sizeof(options), "get+%d+%s",
8009 con->request->request.op.request_id, ppd_name);
8010
8011 if (cupsdSendCommand(con, command, options, 0))
8012 {
8013 /*
8014 * Command started successfully, don't send an IPP response here...
8015 */
8016
8017 ippDelete(con->response);
8018 con->response = NULL;
8019 }
8020 else
8021 {
8022 /*
8023 * Command failed, return "internal error" so the user knows something
8024 * went wrong...
8025 */
8026
8027 send_ipp_status(con, IPP_INTERNAL_ERROR,
8028 _("cups-driverd failed to execute."));
8029 }
8030 }
8031 else if (!strcmp(uri->name, "printer-uri") &&
8032 cupsdValidateDest(uri->values[0].string.text, &dtype, &dest))
8033 {
8034 int i; /* Looping var */
8035 char filename[1024]; /* PPD filename */
8036
8037
8038 /*
8039 * Check policy...
8040 */
8041
8042 if ((status = cupsdCheckPolicy(dest->op_policy_ptr, con, NULL)) != HTTP_OK)
8043 {
2fb76298 8044 send_http_error(con, status, dest);
b94498cf 8045 return;
8046 }
8047
8048 /*
8049 * See if we need the PPD for a class or remote printer...
8050 */
8051
bc44d920 8052 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
8053 dest->name);
8054
8055 if ((dtype & CUPS_PRINTER_REMOTE) && access(filename, 0))
b94498cf 8056 {
3d8365b8 8057 con->response->request.status.status_code = CUPS_SEE_OTHER;
b94498cf 8058 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
8059 "printer-uri", NULL, dest->uri);
8060 return;
8061 }
8062 else if (dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
8063 {
8064 for (i = 0; i < dest->num_printers; i ++)
8065 if (!(dest->printers[i]->type &
bc44d920 8066 (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)))
8067 {
8068 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
8069 dest->printers[i]->name);
8070
8071 if (!access(filename, 0))
8072 break;
8073 }
b94498cf 8074
8075 if (i < dest->num_printers)
8076 dest = dest->printers[i];
8077 else
8078 {
3d8365b8 8079 con->response->request.status.status_code = CUPS_SEE_OTHER;
b94498cf 8080 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
8081 "printer-uri", NULL, dest->printers[0]->uri);
8082 return;
8083 }
8084 }
8085
8086 /*
8087 * Found the printer with the PPD file, now see if there is one...
8088 */
8089
b94498cf 8090 if ((con->file = open(filename, O_RDONLY)) < 0)
8091 {
8092 send_ipp_status(con, IPP_NOT_FOUND,
8093 _("The PPD file \"%s\" could not be opened: %s"),
3d8365b8 8094 uri->values[0].string.text, strerror(errno));
b94498cf 8095 return;
8096 }
8097
8098 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
8099
8100 con->pipe_pid = 0;
8101
3d8365b8 8102 con->response->request.status.status_code = IPP_OK;
b94498cf 8103 }
8104 else
8105 send_ipp_status(con, IPP_NOT_FOUND,
8106 _("The PPD file \"%s\" could not be found."),
8107 uri->values[0].string.text);
8108}
8109
8110
b423cd4c 8111/*
8112 * 'get_ppds()' - Get the list of PPD files on the local system.
8113 */
ef416fc2 8114
b423cd4c 8115static void
8116get_ppds(cupsd_client_t *con) /* I - Client connection */
8117{
8118 http_status_t status; /* Policy status */
b423cd4c 8119 ipp_attribute_t *limit, /* Limit attribute */
b94498cf 8120 *device, /* ppd-device-id attribute */
8121 *language, /* ppd-natural-language attribute */
b423cd4c 8122 *make, /* ppd-make attribute */
b94498cf 8123 *model, /* ppd-make-and-model attribute */
3d8365b8 8124 *model_number, /* ppd-model-number attribute */
b94498cf 8125 *product, /* ppd-product attribute */
8126 *psversion, /* ppd-psverion attribute */
3d8365b8 8127 *type, /* ppd-type attribute */
ed6e7faf
MS
8128 *requested, /* requested-attributes attribute */
8129 *exclude, /* exclude-schemes attribute */
8130 *include; /* include-schemes attribute */
b94498cf 8131 char command[1024], /* cups-driverd command */
ed6e7faf 8132 options[4096], /* Options to pass to command */
b94498cf 8133 device_str[256],/* Escaped ppd-device-id string */
8134 language_str[256],
bc44d920 8135 /* Escaped ppd-natural-language */
b94498cf 8136 make_str[256], /* Escaped ppd-make string */
8137 model_str[256], /* Escaped ppd-make-and-model string */
3d8365b8 8138 model_number_str[256],
8139 /* ppd-model-number string */
b94498cf 8140 product_str[256],
8141 /* Escaped ppd-product string */
8142 psversion_str[256],
8143 /* Escaped ppd-psversion string */
3d8365b8 8144 type_str[256], /* Escaped ppd-type string */
ed6e7faf 8145 requested_str[256],
89d46774 8146 /* String for requested attributes */
ed6e7faf
MS
8147 exclude_str[512],
8148 /* String for excluded schemes */
8149 include_str[512];
8150 /* String for included schemes */
ef416fc2 8151
ef416fc2 8152
b423cd4c 8153 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->http.fd);
ef416fc2 8154
8155 /*
b423cd4c 8156 * Check policy...
ef416fc2 8157 */
8158
b423cd4c 8159 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
ef416fc2 8160 {
f899b121 8161 send_http_error(con, status, NULL);
b423cd4c 8162 return;
ef416fc2 8163 }
ef416fc2 8164
b423cd4c 8165 /*
8166 * Run cups-driverd command with the given options...
8167 */
8168
3d8365b8 8169 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
8170 device = ippFindAttribute(con->request, "ppd-device-id", IPP_TAG_TEXT);
8171 language = ippFindAttribute(con->request, "ppd-natural-language",
8172 IPP_TAG_LANGUAGE);
8173 make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
8174 model = ippFindAttribute(con->request, "ppd-make-and-model",
8175 IPP_TAG_TEXT);
8176 model_number = ippFindAttribute(con->request, "ppd-model-number",
8177 IPP_TAG_INTEGER);
8178 product = ippFindAttribute(con->request, "ppd-product", IPP_TAG_TEXT);
8179 psversion = ippFindAttribute(con->request, "ppd-psversion", IPP_TAG_TEXT);
8180 type = ippFindAttribute(con->request, "ppd-type", IPP_TAG_KEYWORD);
8181 requested = ippFindAttribute(con->request, "requested-attributes",
8182 IPP_TAG_KEYWORD);
8b450588
MS
8183 exclude = ippFindAttribute(con->request, "exclude-schemes",
8184 IPP_TAG_NAME);
8185 include = ippFindAttribute(con->request, "include-schemes",
8186 IPP_TAG_NAME);
b423cd4c 8187
8188 if (requested)
89d46774 8189 url_encode_attr(requested, requested_str, sizeof(requested_str));
8190 else
8191 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
ef416fc2 8192
b94498cf 8193 if (device)
8194 url_encode_attr(device, device_str, sizeof(device_str));
8195 else
8196 device_str[0] = '\0';
8197
8198 if (language)
8199 url_encode_attr(language, language_str, sizeof(language_str));
8200 else
8201 language_str[0] = '\0';
8202
89d46774 8203 if (make)
8204 url_encode_attr(make, make_str, sizeof(make_str));
b423cd4c 8205 else
89d46774 8206 make_str[0] = '\0';
fa73b229 8207
b94498cf 8208 if (model)
8209 url_encode_attr(model, model_str, sizeof(model_str));
8210 else
8211 model_str[0] = '\0';
8212
3d8365b8 8213 if (model_number)
8214 snprintf(model_number_str, sizeof(model_number_str), "ppd-model-number=%d",
8215 model_number->values[0].integer);
8216 else
8217 model_number_str[0] = '\0';
8218
b94498cf 8219 if (product)
8220 url_encode_attr(product, product_str, sizeof(product_str));
8221 else
8222 product_str[0] = '\0';
8223
8224 if (psversion)
8225 url_encode_attr(psversion, psversion_str, sizeof(psversion_str));
8226 else
8227 psversion_str[0] = '\0';
8228
3d8365b8 8229 if (type)
8230 url_encode_attr(type, type_str, sizeof(type_str));
8231 else
8232 type_str[0] = '\0';
8233
ed6e7faf
MS
8234 if (exclude)
8235 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
8236 else
8237 exclude_str[0] = '\0';
8238
8239 if (include)
8240 url_encode_attr(include, include_str, sizeof(include_str));
8241 else
8242 include_str[0] = '\0';
8243
b423cd4c 8244 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
3d8365b8 8245 snprintf(options, sizeof(options),
ed6e7faf 8246 "list+%d+%d+%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
b423cd4c 8247 con->request->request.op.request_id,
8248 limit ? limit->values[0].integer : 0,
b94498cf 8249 requested_str,
8250 device ? "%20" : "", device_str,
8251 language ? "%20" : "", language_str,
8252 make ? "%20" : "", make_str,
8253 model ? "%20" : "", model_str,
3d8365b8 8254 model_number ? "%20" : "", model_number_str,
b94498cf 8255 product ? "%20" : "", product_str,
3d8365b8 8256 psversion ? "%20" : "", psversion_str,
ed6e7faf
MS
8257 type ? "%20" : "", type_str,
8258 exclude_str[0] ? "%20" : "", exclude_str,
8259 include_str[0] ? "%20" : "", include_str);
ef416fc2 8260
b423cd4c 8261 if (cupsdSendCommand(con, command, options, 0))
8262 {
8263 /*
8264 * Command started successfully, don't send an IPP response here...
8265 */
8266
8267 ippDelete(con->response);
8268 con->response = NULL;
8269 }
8270 else
8271 {
8272 /*
8273 * Command failed, return "internal error" so the user knows something
8274 * went wrong...
8275 */
8276
8277 send_ipp_status(con, IPP_INTERNAL_ERROR,
8278 _("cups-driverd failed to execute."));
8279 }
ef416fc2 8280}
8281
8282
8283/*
b423cd4c 8284 * 'get_printer_attrs()' - Get printer attributes.
ef416fc2 8285 */
8286
8287static void
b423cd4c 8288get_printer_attrs(cupsd_client_t *con, /* I - Client connection */
8289 ipp_attribute_t *uri) /* I - Printer URI */
ef416fc2 8290{
8291 http_status_t status; /* Policy status */
bc44d920 8292 cups_ptype_t dtype; /* Destination type (printer/class) */
b423cd4c 8293 cupsd_printer_t *printer; /* Printer/class */
ef416fc2 8294 cups_array_t *ra; /* Requested attributes array */
8295
8296
b423cd4c 8297 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", con,
8298 con->http.fd, uri->values[0].string.text);
ef416fc2 8299
8300 /*
b423cd4c 8301 * Is the destination valid?
ef416fc2 8302 */
8303
f7deaa1a 8304 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 8305 {
8306 /*
b423cd4c 8307 * Bad URI...
ef416fc2 8308 */
8309
8310 send_ipp_status(con, IPP_NOT_FOUND,
b423cd4c 8311 _("The printer or class was not found."));
ef416fc2 8312 return;
8313 }
8314
8315 /*
8316 * Check policy...
8317 */
8318
b423cd4c 8319 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
ef416fc2 8320 {
f899b121 8321 send_http_error(con, status, printer);
ef416fc2 8322 return;
8323 }
8324
8325 /*
b423cd4c 8326 * Send the attributes...
ef416fc2 8327 */
8328
8329 ra = create_requested_array(con->request);
8330
b423cd4c 8331 copy_printer_attrs(con, printer, ra);
ef416fc2 8332
8333 cupsArrayDelete(ra);
8334
8335 con->response->request.status.status_code = IPP_OK;
8336}
8337
8338
c168a833
MS
8339/*
8340 * 'get_printer_supported()' - Get printer supported values.
8341 */
8342
8343static void
8344get_printer_supported(
8345 cupsd_client_t *con, /* I - Client connection */
8346 ipp_attribute_t *uri) /* I - Printer URI */
8347{
8348 http_status_t status; /* Policy status */
8349 cups_ptype_t dtype; /* Destination type (printer/class) */
8350 cupsd_printer_t *printer; /* Printer/class */
8351
8352
8353 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_supported(%p[%d], %s)", con,
8354 con->http.fd, uri->values[0].string.text);
8355
8356 /*
8357 * Is the destination valid?
8358 */
8359
8360 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8361 {
8362 /*
8363 * Bad URI...
8364 */
8365
8366 send_ipp_status(con, IPP_NOT_FOUND,
8367 _("The printer or class was not found."));
8368 return;
8369 }
8370
8371 /*
8372 * Check policy...
8373 */
8374
8375 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8376 {
8377 send_http_error(con, status, printer);
8378 return;
8379 }
8380
8381 /*
8382 * Return a list of attributes that can be set via Set-Printer-Attributes.
8383 */
8384
8385 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
8386 "printer-info", 0);
8387 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
8388 "printer-location", 0);
8389
8390 con->response->request.status.status_code = IPP_OK;
8391}
8392
8393
ef416fc2 8394/*
b423cd4c 8395 * 'get_printers()' - Get a list of printers or classes.
ef416fc2 8396 */
8397
8398static void
b423cd4c 8399get_printers(cupsd_client_t *con, /* I - Client connection */
8400 int type) /* I - 0 or CUPS_PRINTER_CLASS */
ef416fc2 8401{
b423cd4c 8402 http_status_t status; /* Policy status */
8403 ipp_attribute_t *attr; /* Current attribute */
bc44d920 8404 int limit; /* Max number of printers to return */
b423cd4c 8405 int count; /* Number of printers that match */
8406 cupsd_printer_t *printer; /* Current printer pointer */
8407 int printer_type, /* printer-type attribute */
8408 printer_mask; /* printer-type-mask attribute */
8409 char *location; /* Location string */
8410 const char *username; /* Current user */
8411 char *first_printer_name; /* first-printer-name attribute */
8412 cups_array_t *ra; /* Requested attributes array */
e07d4801 8413 int local; /* Local connection? */
ef416fc2 8414
ef416fc2 8415
b423cd4c 8416 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con,
8417 con->http.fd, type);
ef416fc2 8418
8419 /*
8420 * Check policy...
8421 */
8422
b423cd4c 8423 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
ef416fc2 8424 {
f899b121 8425 send_http_error(con, status, NULL);
ef416fc2 8426 return;
8427 }
8428
8429 /*
b423cd4c 8430 * Check for printers...
ef416fc2 8431 */
8432
b423cd4c 8433 if (!Printers || !cupsArrayCount(Printers))
8434 {
8435 send_ipp_status(con, IPP_NOT_FOUND, _("No destinations added."));
8436 return;
8437 }
8438
8439 /*
8440 * See if they want to limit the number of printers reported...
8441 */
ef416fc2 8442
fa73b229 8443 if ((attr = ippFindAttribute(con->request, "limit",
8444 IPP_TAG_INTEGER)) != NULL)
ef416fc2 8445 limit = attr->values[0].integer;
8446 else
b423cd4c 8447 limit = 10000000;
8448
8449 if ((attr = ippFindAttribute(con->request, "first-printer-name",
8450 IPP_TAG_NAME)) != NULL)
8451 first_printer_name = attr->values[0].string.text;
8452 else
8453 first_printer_name = NULL;
ef416fc2 8454
8455 /*
b423cd4c 8456 * Support filtering...
ef416fc2 8457 */
8458
b423cd4c 8459 if ((attr = ippFindAttribute(con->request, "printer-type",
8460 IPP_TAG_ENUM)) != NULL)
8461 printer_type = attr->values[0].integer;
ef416fc2 8462 else
b423cd4c 8463 printer_type = 0;
ef416fc2 8464
b423cd4c 8465 if ((attr = ippFindAttribute(con->request, "printer-type-mask",
8466 IPP_TAG_ENUM)) != NULL)
8467 printer_mask = attr->values[0].integer;
ef416fc2 8468 else
b423cd4c 8469 printer_mask = 0;
e00b005a 8470
e07d4801
MS
8471 local = httpAddrLocalhost(&(con->clientaddr));
8472
b423cd4c 8473 if ((attr = ippFindAttribute(con->request, "printer-location",
8474 IPP_TAG_TEXT)) != NULL)
8475 location = attr->values[0].string.text;
8476 else
8477 location = NULL;
e00b005a 8478
8479 if (con->username[0])
b423cd4c 8480 username = con->username;
e00b005a 8481 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
8482 IPP_TAG_NAME)) != NULL)
b423cd4c 8483 username = attr->values[0].string.text;
e00b005a 8484 else
b423cd4c 8485 username = NULL;
ef416fc2 8486
b423cd4c 8487 ra = create_requested_array(con->request);
ef416fc2 8488
8489 /*
b423cd4c 8490 * OK, build a list of printers for this printer...
ef416fc2 8491 */
8492
b423cd4c 8493 if (first_printer_name)
ef416fc2 8494 {
b423cd4c 8495 if ((printer = cupsdFindDest(first_printer_name)) == NULL)
8496 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
ef416fc2 8497 }
8498 else
b423cd4c 8499 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
ef416fc2 8500
b423cd4c 8501 for (count = 0;
8502 count < limit && printer;
8503 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
8504 {
e07d4801
MS
8505 if (!local && !printer->shared)
8506 continue;
8507
b423cd4c 8508 if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
8509 (printer->type & printer_mask) == printer_type &&
b19ccc9e
MS
8510 (!location ||
8511 (printer->location && !strcasecmp(printer->location, location))))
ef416fc2 8512 {
8513 /*
b423cd4c 8514 * If HideImplicitMembers is enabled, see if this printer or class
8515 * is a member of an implicit class...
ef416fc2 8516 */
8517
b423cd4c 8518 if (ImplicitClasses && HideImplicitMembers &&
8519 printer->in_implicit_class)
8520 continue;
ef416fc2 8521
b423cd4c 8522 /*
8523 * If a username is specified, see if it is allowed or denied
8524 * access...
8525 */
ef416fc2 8526
10d09e33
MS
8527 if (cupsArrayCount(printer->users) && username &&
8528 !user_allowed(printer, username))
b423cd4c 8529 continue;
ef416fc2 8530
b423cd4c 8531 /*
8532 * Add the group separator as needed...
8533 */
ef416fc2 8534
b423cd4c 8535 if (count > 0)
8536 ippAddSeparator(con->response);
ef416fc2 8537
b423cd4c 8538 count ++;
ef416fc2 8539
b423cd4c 8540 /*
8541 * Send the attributes...
8542 */
ef416fc2 8543
b423cd4c 8544 copy_printer_attrs(con, printer, ra);
8545 }
ef416fc2 8546 }
8547
b423cd4c 8548 cupsArrayDelete(ra);
ef416fc2 8549
8550 con->response->request.status.status_code = IPP_OK;
8551}
8552
8553
8554/*
b423cd4c 8555 * 'get_subscription_attrs()' - Get subscription attributes.
ef416fc2 8556 */
8557
8558static void
b423cd4c 8559get_subscription_attrs(
8560 cupsd_client_t *con, /* I - Client connection */
8561 int sub_id) /* I - Subscription ID */
ef416fc2 8562{
b423cd4c 8563 http_status_t status; /* Policy status */
8564 cupsd_subscription_t *sub; /* Subscription */
10d09e33
MS
8565 cupsd_policy_t *policy; /* Current security policy */
8566 cups_array_t *ra, /* Requested attributes array */
8567 *exclude; /* Private attributes array */
ef416fc2 8568
8569
b423cd4c 8570 cupsdLogMessage(CUPSD_LOG_DEBUG2,
8571 "get_subscription_attrs(con=%p[%d], sub_id=%d)",
8572 con, con->http.fd, sub_id);
ef416fc2 8573
fa73b229 8574 /*
b423cd4c 8575 * Is the subscription ID valid?
fa73b229 8576 */
8577
b423cd4c 8578 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
fa73b229 8579 {
8580 /*
b423cd4c 8581 * Bad subscription ID...
fa73b229 8582 */
8583
8584 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 8585 _("notify-subscription-id %d no good"), sub_id);
fa73b229 8586 return;
8587 }
8588
8589 /*
8590 * Check policy...
8591 */
8592
10d09e33
MS
8593 if (sub->dest)
8594 policy = sub->dest->op_policy_ptr;
8595 else
8596 policy = DefaultPolicyPtr;
8597
8598 if ((status = cupsdCheckPolicy(policy, con, sub->owner)) != HTTP_OK)
fa73b229 8599 {
f899b121 8600 send_http_error(con, status, sub->dest);
fa73b229 8601 return;
8602 }
8603
10d09e33
MS
8604 exclude = cupsdGetPrivateAttrs(policy, con, sub->dest, sub->owner);
8605
ef416fc2 8606 /*
b423cd4c 8607 * Copy the subscription attributes to the response using the
8608 * requested-attributes attribute that may be provided by the client.
ef416fc2 8609 */
8610
b423cd4c 8611 ra = create_requested_array(con->request);
fa73b229 8612
10d09e33 8613 copy_subscription_attrs(con, sub, ra, exclude);
ef416fc2 8614
b423cd4c 8615 cupsArrayDelete(ra);
fa73b229 8616
b423cd4c 8617 con->response->request.status.status_code = IPP_OK;
8618}
fa73b229 8619
fa73b229 8620
b423cd4c 8621/*
8622 * 'get_subscriptions()' - Get subscriptions.
8623 */
ef416fc2 8624
b423cd4c 8625static void
8626get_subscriptions(cupsd_client_t *con, /* I - Client connection */
8627 ipp_attribute_t *uri) /* I - Printer/job URI */
8628{
8629 http_status_t status; /* Policy status */
8630 int count; /* Number of subscriptions */
8631 int limit; /* Limit */
8632 cupsd_subscription_t *sub; /* Subscription */
8633 cups_array_t *ra; /* Requested attributes array */
8634 ipp_attribute_t *attr; /* Attribute */
bc44d920 8635 cups_ptype_t dtype; /* Destination type (printer/class) */
f7deaa1a 8636 char scheme[HTTP_MAX_URI],
8637 /* Scheme portion of URI */
b423cd4c 8638 username[HTTP_MAX_URI],
8639 /* Username portion of URI */
8640 host[HTTP_MAX_URI],
8641 /* Host portion of URI */
8642 resource[HTTP_MAX_URI];
8643 /* Resource portion of URI */
8644 int port; /* Port portion of URI */
8645 cupsd_job_t *job; /* Job pointer */
8646 cupsd_printer_t *printer; /* Printer */
10d09e33
MS
8647 cupsd_policy_t *policy; /* Policy */
8648 cups_array_t *exclude; /* Private attributes array */
fa73b229 8649
fa73b229 8650
b423cd4c 8651 cupsdLogMessage(CUPSD_LOG_DEBUG2,
8652 "get_subscriptions(con=%p[%d], uri=%s)",
8653 con, con->http.fd, uri->values[0].string.text);
8654
8655 /*
8656 * Is the destination valid?
8657 */
8658
f7deaa1a 8659 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8660 sizeof(scheme), username, sizeof(username), host,
b423cd4c 8661 sizeof(host), &port, resource, sizeof(resource));
8662
8663 if (!strcmp(resource, "/") ||
8664 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6) ||
8665 (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) ||
8666 (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9))
8667 {
8668 printer = NULL;
8669 job = NULL;
ef416fc2 8670 }
b423cd4c 8671 else if (!strncmp(resource, "/jobs/", 6) && resource[6])
ef416fc2 8672 {
b423cd4c 8673 printer = NULL;
8674 job = cupsdFindJob(atoi(resource + 6));
ef416fc2 8675
b423cd4c 8676 if (!job)
ef416fc2 8677 {
4d301e69 8678 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%s does not exist"),
b423cd4c 8679 resource + 6);
ef416fc2 8680 return;
8681 }
b423cd4c 8682 }
f7deaa1a 8683 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
b423cd4c 8684 {
fa73b229 8685 /*
b423cd4c 8686 * Bad URI...
fa73b229 8687 */
8688
b423cd4c 8689 send_ipp_status(con, IPP_NOT_FOUND,
8690 _("The printer or class was not found."));
8691 return;
8692 }
8693 else if ((attr = ippFindAttribute(con->request, "notify-job-id",
8694 IPP_TAG_INTEGER)) != NULL)
8695 {
8696 job = cupsdFindJob(attr->values[0].integer);
ef416fc2 8697
b423cd4c 8698 if (!job)
fa73b229 8699 {
4d301e69 8700 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"),
b423cd4c 8701 attr->values[0].integer);
fa73b229 8702 return;
8703 }
ef416fc2 8704 }
b423cd4c 8705 else
8706 job = NULL;
ef416fc2 8707
8708 /*
b423cd4c 8709 * Check policy...
ef416fc2 8710 */
8711
10d09e33
MS
8712 if (printer)
8713 policy = printer->op_policy_ptr;
8714 else
8715 policy = DefaultPolicyPtr;
8716
8717 if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
ef416fc2 8718 {
f899b121 8719 send_http_error(con, status, printer);
b423cd4c 8720 return;
8721 }
ef416fc2 8722
b423cd4c 8723 /*
8724 * Copy the subscription attributes to the response using the
8725 * requested-attributes attribute that may be provided by the client.
8726 */
ef416fc2 8727
b423cd4c 8728 ra = create_requested_array(con->request);
ef416fc2 8729
b423cd4c 8730 if ((attr = ippFindAttribute(con->request, "limit",
8731 IPP_TAG_INTEGER)) != NULL)
8732 limit = attr->values[0].integer;
8733 else
8734 limit = 0;
ef416fc2 8735
b423cd4c 8736 /*
8737 * See if we only want to see subscriptions for a specific user...
8738 */
ef416fc2 8739
b423cd4c 8740 if ((attr = ippFindAttribute(con->request, "my-subscriptions",
8741 IPP_TAG_BOOLEAN)) != NULL &&
8742 attr->values[0].boolean)
8743 strlcpy(username, get_username(con), sizeof(username));
fa73b229 8744 else
b423cd4c 8745 username[0] = '\0';
ef416fc2 8746
b423cd4c 8747 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0;
8748 sub;
8749 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
8750 if ((!printer || sub->dest == printer) && (!job || sub->job == job) &&
8751 (!username[0] || !strcasecmp(username, sub->owner)))
fa73b229 8752 {
b423cd4c 8753 ippAddSeparator(con->response);
10d09e33
MS
8754
8755 exclude = cupsdGetPrivateAttrs(sub->dest ? sub->dest->op_policy_ptr :
8756 policy, con, sub->dest,
8757 sub->owner);
8758
8759 copy_subscription_attrs(con, sub, ra, exclude);
ef416fc2 8760
b423cd4c 8761 count ++;
8762 if (limit && count >= limit)
8763 break;
8764 }
ef416fc2 8765
b423cd4c 8766 cupsArrayDelete(ra);
fa73b229 8767
b423cd4c 8768 if (count)
8769 con->response->request.status.status_code = IPP_OK;
8770 else
8771 send_ipp_status(con, IPP_NOT_FOUND, _("No subscriptions found."));
8772}
ef416fc2 8773
ef416fc2 8774
b423cd4c 8775/*
8776 * 'get_username()' - Get the username associated with a request.
8777 */
ef416fc2 8778
b423cd4c 8779static const char * /* O - Username */
8780get_username(cupsd_client_t *con) /* I - Connection */
8781{
8782 ipp_attribute_t *attr; /* Attribute */
ef416fc2 8783
ef416fc2 8784
b423cd4c 8785 if (con->username[0])
8786 return (con->username);
8787 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
8788 IPP_TAG_NAME)) != NULL)
8789 return (attr->values[0].string.text);
8790 else
8791 return ("anonymous");
ef416fc2 8792}
8793
8794
8795/*
b423cd4c 8796 * 'hold_job()' - Hold a print job.
ef416fc2 8797 */
8798
b423cd4c 8799static void
8800hold_job(cupsd_client_t *con, /* I - Client connection */
8801 ipp_attribute_t *uri) /* I - Job or Printer URI */
ef416fc2 8802{
b9faaae1
MS
8803 ipp_attribute_t *attr; /* Current job-hold-until */
8804 const char *when; /* New value */
b423cd4c 8805 int jobid; /* Job ID */
61cf44e2 8806 char scheme[HTTP_MAX_URI], /* Method portion of URI */
b423cd4c 8807 username[HTTP_MAX_URI], /* Username portion of URI */
8808 host[HTTP_MAX_URI], /* Host portion of URI */
8809 resource[HTTP_MAX_URI]; /* Resource portion of URI */
8810 int port; /* Port portion of URI */
8811 cupsd_job_t *job; /* Job information */
8812
ef416fc2 8813
b423cd4c 8814 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", con, con->http.fd,
8815 uri->values[0].string.text);
ef416fc2 8816
8817 /*
b423cd4c 8818 * See if we have a job URI or a printer URI...
ef416fc2 8819 */
8820
b423cd4c 8821 if (!strcmp(uri->name, "printer-uri"))
8822 {
8823 /*
8824 * Got a printer URI; see if we also have a job-id attribute...
8825 */
ef416fc2 8826
b423cd4c 8827 if ((attr = ippFindAttribute(con->request, "job-id",
8828 IPP_TAG_INTEGER)) == NULL)
8829 {
8830 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 8831 _("Got a printer-uri attribute but no job-id"));
b423cd4c 8832 return;
8833 }
ef416fc2 8834
b423cd4c 8835 jobid = attr->values[0].integer;
8836 }
ef416fc2 8837 else
ef416fc2 8838 {
b423cd4c 8839 /*
8840 * Got a job URI; parse it to get the job ID...
8841 */
ef416fc2 8842
61cf44e2
MS
8843 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8844 sizeof(scheme), username, sizeof(username), host,
b423cd4c 8845 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 8846
b423cd4c 8847 if (strncmp(resource, "/jobs/", 6))
8848 {
8849 /*
8850 * Not a valid URI!
8851 */
ef416fc2 8852
b423cd4c 8853 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 8854 _("Bad job-uri attribute \"%s\""),
b423cd4c 8855 uri->values[0].string.text);
8856 return;
8857 }
ef416fc2 8858
b423cd4c 8859 jobid = atoi(resource + 6);
8860 }
ef416fc2 8861
ef416fc2 8862 /*
b423cd4c 8863 * See if the job exists...
ef416fc2 8864 */
8865
b423cd4c 8866 if ((job = cupsdFindJob(jobid)) == NULL)
8867 {
8868 /*
8869 * Nope - return a "not found" error...
8870 */
8871
4d301e69 8872 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
b423cd4c 8873 return;
8874 }
ef416fc2 8875
8876 /*
b423cd4c 8877 * See if the job is owned by the requesting user...
ef416fc2 8878 */
8879
b423cd4c 8880 if (!validate_user(job, con, job->username, username, sizeof(username)))
8881 {
b0f6947b
MS
8882 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
8883 cupsdFindDest(job->dest));
b423cd4c 8884 return;
8885 }
ef416fc2 8886
8887 /*
b423cd4c 8888 * Hold the job and return...
ef416fc2 8889 */
8890
b9faaae1
MS
8891 if ((attr = ippFindAttribute(con->request, "job-hold-until",
8892 IPP_TAG_KEYWORD)) == NULL)
8893 attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
ef416fc2 8894
b423cd4c 8895 if (attr)
8896 {
b9faaae1 8897 when = attr->values[0].string.text;
d09495fa 8898
01ce6322 8899 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
b9faaae1 8900 "Job job-hold-until value changed by user.");
b423cd4c 8901 }
b9faaae1
MS
8902 else
8903 when = "indefinite";
ef416fc2 8904
b9faaae1
MS
8905 cupsdSetJobHoldUntil(job, when, 1);
8906 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT, "Job held by \"%s\".",
8907 username);
b423cd4c 8908
8909 con->response->request.status.status_code = IPP_OK;
ef416fc2 8910}
8911
8912
61cf44e2
MS
8913/*
8914 * 'hold_new_jobs()' - Hold pending/new jobs on a printer or class.
8915 */
8916
8917static void
8918hold_new_jobs(cupsd_client_t *con, /* I - Connection */
8919 ipp_attribute_t *uri) /* I - Printer URI */
8920{
8921 http_status_t status; /* Policy status */
8922 cups_ptype_t dtype; /* Destination type (printer/class) */
8923 cupsd_printer_t *printer; /* Printer data */
8924
8925
8926 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_new_jobs(%p[%d], %s)", con,
8927 con->http.fd, uri->values[0].string.text);
8928
8929 /*
8930 * Is the destination valid?
8931 */
8932
8933 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8934 {
8935 /*
8936 * Bad URI...
8937 */
8938
8939 send_ipp_status(con, IPP_NOT_FOUND,
8940 _("The printer or class was not found."));
8941 return;
8942 }
8943
8944 /*
8945 * Check policy...
8946 */
8947
8948 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8949 {
8950 send_http_error(con, status, printer);
8951 return;
8952 }
8953
8954 /*
8955 * Hold pending/new jobs sent to the printer...
8956 */
8957
8958 printer->holding_new_jobs = 1;
8959
8960 cupsdSetPrinterReasons(printer, "+hold-new-jobs");
8961 cupsdAddPrinterHistory(printer);
8962
8963 if (dtype & CUPS_PRINTER_CLASS)
8964 cupsdLogMessage(CUPSD_LOG_INFO,
8965 "Class \"%s\" now holding pending/new jobs (\"%s\").",
8966 printer->name, get_username(con));
8967 else
8968 cupsdLogMessage(CUPSD_LOG_INFO,
8969 "Printer \"%s\" now holding pending/new jobs (\"%s\").",
8970 printer->name, get_username(con));
8971
8972 /*
8973 * Everything was ok, so return OK status...
8974 */
8975
8976 con->response->request.status.status_code = IPP_OK;
8977}
8978
8979
ef416fc2 8980/*
b423cd4c 8981 * 'move_job()' - Move a job to a new destination.
ef416fc2 8982 */
8983
8984static void
b423cd4c 8985move_job(cupsd_client_t *con, /* I - Client connection */
8986 ipp_attribute_t *uri) /* I - Job URI */
ef416fc2 8987{
8988 http_status_t status; /* Policy status */
8989 ipp_attribute_t *attr; /* Current attribute */
b423cd4c 8990 int jobid; /* Job ID */
ef416fc2 8991 cupsd_job_t *job; /* Current job */
d09495fa 8992 const char *src; /* Source printer/class */
b423cd4c 8993 cups_ptype_t stype, /* Source type (printer or class) */
bc44d920 8994 dtype; /* Destination type (printer/class) */
f7deaa1a 8995 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
ef416fc2 8996 username[HTTP_MAX_URI], /* Username portion of URI */
8997 host[HTTP_MAX_URI], /* Host portion of URI */
b423cd4c 8998 resource[HTTP_MAX_URI]; /* Resource portion of URI */
ef416fc2 8999 int port; /* Port portion of URI */
b423cd4c 9000 cupsd_printer_t *sprinter, /* Source printer */
9001 *dprinter; /* Destination printer */
ef416fc2 9002
9003
b423cd4c 9004 cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", con, con->http.fd,
ef416fc2 9005 uri->values[0].string.text);
9006
9007 /*
b423cd4c 9008 * Get the new printer or class...
ef416fc2 9009 */
9010
b423cd4c 9011 if ((attr = ippFindAttribute(con->request, "job-printer-uri",
9012 IPP_TAG_URI)) == NULL)
ef416fc2 9013 {
b423cd4c 9014 /*
9015 * Need job-printer-uri...
9016 */
ef416fc2 9017
b423cd4c 9018 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 9019 _("job-printer-uri attribute missing"));
b423cd4c 9020 return;
ef416fc2 9021 }
f7faf1f5 9022
f7deaa1a 9023 if (!cupsdValidateDest(attr->values[0].string.text, &dtype, &dprinter))
ef416fc2 9024 {
b423cd4c 9025 /*
9026 * Bad URI...
9027 */
ef416fc2 9028
b423cd4c 9029 send_ipp_status(con, IPP_NOT_FOUND,
9030 _("The printer or class was not found."));
9031 return;
ef416fc2 9032 }
9033
ef416fc2 9034 /*
b423cd4c 9035 * See if we have a job URI or a printer URI...
ef416fc2 9036 */
9037
f7deaa1a 9038 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9039 sizeof(scheme), username, sizeof(username), host,
b423cd4c 9040 sizeof(host), &port, resource, sizeof(resource));
9041
9042 if (!strcmp(uri->name, "printer-uri"))
ef416fc2 9043 {
9044 /*
b423cd4c 9045 * Got a printer URI; see if we also have a job-id attribute...
ef416fc2 9046 */
9047
b423cd4c 9048 if ((attr = ippFindAttribute(con->request, "job-id",
9049 IPP_TAG_INTEGER)) == NULL)
ef416fc2 9050 {
b423cd4c 9051 /*
9052 * Move all jobs...
9053 */
9054
f7deaa1a 9055 if ((src = cupsdValidateDest(uri->values[0].string.text, &stype,
9056 &sprinter)) == NULL)
b423cd4c 9057 {
9058 /*
9059 * Bad URI...
9060 */
9061
9062 send_ipp_status(con, IPP_NOT_FOUND,
9063 _("The printer or class was not found."));
9064 return;
9065 }
9066
9067 job = NULL;
9068 }
9069 else
9070 {
9071 /*
9072 * Otherwise, just move a single job...
9073 */
9074
9075 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
9076 {
9077 /*
9078 * Nope - return a "not found" error...
9079 */
9080
9081 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 9082 _("Job #%d does not exist"), attr->values[0].integer);
b423cd4c 9083 return;
9084 }
9085 else
9086 {
9087 /*
9088 * Job found, initialize source pointers...
9089 */
9090
9091 src = NULL;
9092 sprinter = NULL;
9093 }
ef416fc2 9094 }
9095 }
9096 else
9097 {
9098 /*
b423cd4c 9099 * Got a job URI; parse it to get the job ID...
ef416fc2 9100 */
9101
b423cd4c 9102 if (strncmp(resource, "/jobs/", 6))
9103 {
9104 /*
9105 * Not a valid URI!
9106 */
9107
9108 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 9109 _("Bad job-uri attribute \"%s\""),
b423cd4c 9110 uri->values[0].string.text);
9111 return;
9112 }
ef416fc2 9113
ef416fc2 9114 /*
b423cd4c 9115 * See if the job exists...
ef416fc2 9116 */
9117
b423cd4c 9118 jobid = atoi(resource + 6);
ef416fc2 9119
b423cd4c 9120 if ((job = cupsdFindJob(jobid)) == NULL)
ef416fc2 9121 {
9122 /*
b423cd4c 9123 * Nope - return a "not found" error...
ef416fc2 9124 */
9125
b423cd4c 9126 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 9127 _("Job #%d does not exist"), jobid);
b423cd4c 9128 return;
ef416fc2 9129 }
9130 else
b423cd4c 9131 {
9132 /*
9133 * Job found, initialize source pointers...
9134 */
ef416fc2 9135
b423cd4c 9136 src = NULL;
9137 sprinter = NULL;
9138 }
ef416fc2 9139 }
9140
ac884b6a
MS
9141 /*
9142 * Check the policy of the destination printer...
9143 */
9144
9145 if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con,
9146 job ? job->username : NULL)) != HTTP_OK)
9147 {
9148 send_http_error(con, status, dprinter);
9149 return;
9150 }
9151
ef416fc2 9152 /*
b423cd4c 9153 * Now move the job or jobs...
ef416fc2 9154 */
9155
b423cd4c 9156 if (job)
9157 {
9158 /*
9159 * See if the job has been completed...
9160 */
9161
9162 if (job->state_value > IPP_JOB_STOPPED)
9163 {
9164 /*
9165 * Return a "not-possible" error...
9166 */
9167
9168 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 9169 _("Job #%d is finished and cannot be altered"),
b423cd4c 9170 job->id);
9171 return;
9172 }
ef416fc2 9173
b423cd4c 9174 /*
9175 * See if the job is owned by the requesting user...
9176 */
ef416fc2 9177
b423cd4c 9178 if (!validate_user(job, con, job->username, username, sizeof(username)))
9179 {
b0f6947b
MS
9180 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9181 cupsdFindDest(job->dest));
b423cd4c 9182 return;
9183 }
ef416fc2 9184
ef416fc2 9185 /*
b423cd4c 9186 * Move the job to a different printer or class...
ef416fc2 9187 */
9188
e53920b9 9189 cupsdMoveJob(job, dprinter);
ef416fc2 9190 }
b423cd4c 9191 else
9192 {
9193 /*
9194 * Got the source printer, now look through the jobs...
9195 */
ef416fc2 9196
b423cd4c 9197 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
9198 job;
9199 job = (cupsd_job_t *)cupsArrayNext(Jobs))
9200 {
9201 /*
9202 * See if the job is pointing at the source printer or has not been
9203 * completed...
9204 */
ef416fc2 9205
b423cd4c 9206 if (strcasecmp(job->dest, src) ||
9207 job->state_value > IPP_JOB_STOPPED)
9208 continue;
ef416fc2 9209
b423cd4c 9210 /*
9211 * See if the job can be moved by the requesting user...
9212 */
ef416fc2 9213
b423cd4c 9214 if (!validate_user(job, con, job->username, username, sizeof(username)))
9215 continue;
ef416fc2 9216
b423cd4c 9217 /*
9218 * Move the job to a different printer or class...
9219 */
ef416fc2 9220
e53920b9 9221 cupsdMoveJob(job, dprinter);
b423cd4c 9222 }
ef416fc2 9223 }
9224
9225 /*
b423cd4c 9226 * Start jobs if possible...
ef416fc2 9227 */
9228
b423cd4c 9229 cupsdCheckJobs();
ef416fc2 9230
9231 /*
b423cd4c 9232 * Return with "everything is OK" status...
ef416fc2 9233 */
9234
b423cd4c 9235 con->response->request.status.status_code = IPP_OK;
9236}
ef416fc2 9237
ef416fc2 9238
b423cd4c 9239/*
9240 * 'ppd_parse_line()' - Parse a PPD default line.
9241 */
ef416fc2 9242
b423cd4c 9243static int /* O - 0 on success, -1 on failure */
9244ppd_parse_line(const char *line, /* I - Line */
9245 char *option, /* O - Option name */
9246 int olen, /* I - Size of option name */
9247 char *choice, /* O - Choice name */
9248 int clen) /* I - Size of choice name */
9249{
9250 /*
9251 * Verify this is a default option line...
9252 */
ef416fc2 9253
b423cd4c 9254 if (strncmp(line, "*Default", 8))
9255 return (-1);
ef416fc2 9256
b423cd4c 9257 /*
9258 * Read the option name...
9259 */
9260
18ecb428
MS
9261 for (line += 8, olen --;
9262 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
9263 line ++)
b423cd4c 9264 if (olen > 0)
9265 {
9266 *option++ = *line;
9267 olen --;
ef416fc2 9268 }
9269
b423cd4c 9270 *option = '\0';
ef416fc2 9271
b423cd4c 9272 /*
9273 * Skip everything else up to the colon (:)...
9274 */
ef416fc2 9275
b423cd4c 9276 while (*line && *line != ':')
9277 line ++;
ef416fc2 9278
b423cd4c 9279 if (!*line)
9280 return (-1);
ef416fc2 9281
b423cd4c 9282 line ++;
ef416fc2 9283
b423cd4c 9284 /*
9285 * Now grab the option choice, skipping leading whitespace...
9286 */
ef416fc2 9287
b423cd4c 9288 while (isspace(*line & 255))
9289 line ++;
ef416fc2 9290
18ecb428
MS
9291 for (clen --;
9292 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
9293 line ++)
b423cd4c 9294 if (clen > 0)
9295 {
9296 *choice++ = *line;
9297 clen --;
9298 }
ef416fc2 9299
b423cd4c 9300 *choice = '\0';
ef416fc2 9301
b423cd4c 9302 /*
9303 * Return with no errors...
9304 */
ef416fc2 9305
b423cd4c 9306 return (0);
9307}
ef416fc2 9308
ef416fc2 9309
b423cd4c 9310/*
9311 * 'print_job()' - Print a file to a printer or class.
9312 */
ef416fc2 9313
b423cd4c 9314static void
9315print_job(cupsd_client_t *con, /* I - Client connection */
9316 ipp_attribute_t *uri) /* I - Printer URI */
9317{
9318 ipp_attribute_t *attr; /* Current attribute */
9319 ipp_attribute_t *format; /* Document-format attribute */
f7deaa1a 9320 const char *default_format; /* document-format-default value */
b423cd4c 9321 cupsd_job_t *job; /* New job */
9322 char filename[1024]; /* Job filename */
9323 mime_type_t *filetype; /* Type of file */
9324 char super[MIME_MAX_SUPER], /* Supertype of file */
9325 type[MIME_MAX_TYPE], /* Subtype of file */
9326 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
9327 /* Textual name of mime type */
9328 cupsd_printer_t *printer; /* Printer data */
9329 struct stat fileinfo; /* File information */
9330 int kbytes; /* Size of file */
9331 int compression; /* Document compression */
ef416fc2 9332
ef416fc2 9333
b423cd4c 9334 cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", con, con->http.fd,
9335 uri->values[0].string.text);
ef416fc2 9336
b423cd4c 9337 /*
9338 * Validate print file attributes, for now just document-format and
9339 * compression (CUPS only supports "none" and "gzip")...
9340 */
ef416fc2 9341
b423cd4c 9342 compression = CUPS_FILE_NONE;
ef416fc2 9343
b423cd4c 9344 if ((attr = ippFindAttribute(con->request, "compression",
9345 IPP_TAG_KEYWORD)) != NULL)
9346 {
9347 if (strcmp(attr->values[0].string.text, "none")
9348#ifdef HAVE_LIBZ
9349 && strcmp(attr->values[0].string.text, "gzip")
9350#endif /* HAVE_LIBZ */
9351 )
9352 {
9353 send_ipp_status(con, IPP_ATTRIBUTES,
4d301e69 9354 _("Unsupported compression \"%s\""),
b423cd4c 9355 attr->values[0].string.text);
9356 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
9357 "compression", NULL, attr->values[0].string.text);
9358 return;
9359 }
ef416fc2 9360
b423cd4c 9361#ifdef HAVE_LIBZ
9362 if (!strcmp(attr->values[0].string.text, "gzip"))
9363 compression = CUPS_FILE_GZIP;
9364#endif /* HAVE_LIBZ */
9365 }
ef416fc2 9366
b423cd4c 9367 /*
9368 * Do we have a file to print?
9369 */
ef416fc2 9370
b423cd4c 9371 if (!con->filename)
9372 {
4d301e69 9373 send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?"));
b423cd4c 9374 return;
9375 }
ef416fc2 9376
f7deaa1a 9377 /*
9378 * Is the destination valid?
9379 */
9380
9381 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
9382 {
9383 /*
9384 * Bad URI...
9385 */
9386
9387 send_ipp_status(con, IPP_NOT_FOUND,
9388 _("The printer or class was not found."));
9389 return;
9390 }
9391
b423cd4c 9392 /*
9393 * Is it a format we support?
9394 */
ef416fc2 9395
b423cd4c 9396 if ((format = ippFindAttribute(con->request, "document-format",
9397 IPP_TAG_MIMETYPE)) != NULL)
9398 {
9399 /*
9400 * Grab format from client...
9401 */
9402
f7deaa1a 9403 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super,
9404 type) != 2)
b423cd4c 9405 {
9406 send_ipp_status(con, IPP_BAD_REQUEST,
c7017ecc 9407 _("Bad document-format \"%s\""),
b423cd4c 9408 format->values[0].string.text);
9409 return;
ef416fc2 9410 }
b423cd4c 9411 }
f7deaa1a 9412 else if ((default_format = cupsGetOption("document-format",
9413 printer->num_options,
9414 printer->options)) != NULL)
9415 {
9416 /*
9417 * Use default document format...
9418 */
9419
9420 if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
9421 {
9422 send_ipp_status(con, IPP_BAD_REQUEST,
c7017ecc 9423 _("Bad document-format \"%s\""),
f7deaa1a 9424 default_format);
9425 return;
9426 }
9427 }
b423cd4c 9428 else
9429 {
9430 /*
f7deaa1a 9431 * Auto-type it!
b423cd4c 9432 */
9433
9434 strcpy(super, "application");
9435 strcpy(type, "octet-stream");
9436 }
ef416fc2 9437
b423cd4c 9438 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
9439 {
ef416fc2 9440 /*
b423cd4c 9441 * Auto-type the file...
ef416fc2 9442 */
9443
b423cd4c 9444 ipp_attribute_t *doc_name; /* document-name attribute */
9445
9446
a0f6818e 9447 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job ???] Auto-typing file...");
b423cd4c 9448
9449 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
9450 filetype = mimeFileType(MimeDatabase, con->filename,
9451 doc_name ? doc_name->values[0].string.text : NULL,
9452 &compression);
9453
f7deaa1a 9454 if (!filetype)
9455 filetype = mimeType(MimeDatabase, super, type);
a0f6818e
MS
9456
9457 cupsdLogMessage(CUPSD_LOG_INFO, "[Job ???] Request file type is %s/%s.",
9458 filetype->super, filetype->type);
f7deaa1a 9459 }
9460 else
9461 filetype = mimeType(MimeDatabase, super, type);
ef416fc2 9462
f7deaa1a 9463 if (filetype &&
9464 (!format ||
9465 (!strcmp(super, "application") && !strcmp(type, "octet-stream"))))
9466 {
9467 /*
9468 * Replace the document-format attribute value with the auto-typed or
9469 * default one.
9470 */
b423cd4c 9471
f7deaa1a 9472 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9473 filetype->type);
ed486911 9474
f7deaa1a 9475 if (format)
9476 {
9477 _cupsStrFree(format->values[0].string.text);
9478
9479 format->values[0].string.text = _cupsStrAlloc(mimetype);
ef416fc2 9480 }
b423cd4c 9481 else
f7deaa1a 9482 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
9483 "document-format", NULL, mimetype);
ef416fc2 9484 }
f7deaa1a 9485 else if (!filetype)
b423cd4c 9486 {
9487 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
c7017ecc
MS
9488 _("Unsupported document-format \"%s\""),
9489 format ? format->values[0].string.text :
9490 "application/octet-stream");
b423cd4c 9491 cupsdLogMessage(CUPSD_LOG_INFO,
9492 "Hint: Do you have the raw file printing rules enabled?");
9493
9494 if (format)
9495 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9496 "document-format", NULL, format->values[0].string.text);
9497
9498 return;
9499 }
9500
b423cd4c 9501 /*
9502 * Read any embedded job ticket info from PS files...
9503 */
9504
9505 if (!strcasecmp(filetype->super, "application") &&
c5571a1d
MS
9506 (!strcasecmp(filetype->type, "postscript") ||
9507 !strcasecmp(filetype->type, "pdf")))
9508 read_job_ticket(con);
b423cd4c 9509
9510 /*
9511 * Create the job object...
9512 */
9513
f7deaa1a 9514 if ((job = add_job(con, printer, filetype)) == NULL)
b423cd4c 9515 return;
9516
9517 /*
9518 * Update quota data...
9519 */
9520
9521 if (stat(con->filename, &fileinfo))
9522 kbytes = 0;
9523 else
9524 kbytes = (fileinfo.st_size + 1023) / 1024;
9525
9526 cupsdUpdateQuota(printer, job->username, 0, kbytes);
9527
9528 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
9529 IPP_TAG_INTEGER)) != NULL)
9530 attr->values[0].integer += kbytes;
9531
ef416fc2 9532 /*
9533 * Add the job file...
9534 */
9535
9536 if (add_file(con, job, filetype, compression))
9537 return;
9538
9539 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
9540 job->num_files);
9541 rename(con->filename, filename);
9542 cupsdClearString(&con->filename);
9543
9544 /*
9545 * See if we need to add the ending sheet...
9546 */
9547
91c84a35
MS
9548 if (cupsdTimeoutJob(job))
9549 return;
ef416fc2 9550
ef416fc2 9551 /*
9552 * Log and save the job...
9553 */
9554
75bd9771
MS
9555 cupsdLogJob(job, CUPSD_LOG_INFO,
9556 "File of type %s/%s queued by \"%s\".",
9557 filetype->super, filetype->type, job->username);
9558 cupsdLogJob(job, CUPSD_LOG_DEBUG, "hold_until=%d", (int)job->hold_until);
b19ccc9e
MS
9559 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
9560 job->dest, job->username);
ef416fc2 9561
ef416fc2 9562 /*
9563 * Start the job if possible...
9564 */
9565
9566 cupsdCheckJobs();
9567}
9568
9569
9570/*
c5571a1d 9571 * 'read_job_ticket()' - Read a job ticket embedded in a print file.
ef416fc2 9572 *
c5571a1d 9573 * This function only gets called when printing a single PDF or PostScript
ef416fc2 9574 * file using the Print-Job operation. It doesn't work for Create-Job +
9575 * Send-File, since the job attributes need to be set at job creation
c5571a1d
MS
9576 * time for banners to work. The embedded job ticket stuff is here
9577 * primarily to allow the Windows printer driver for CUPS to pass in JCL
ef416fc2 9578 * options and IPP attributes which otherwise would be lost.
9579 *
c5571a1d 9580 * The format of a job ticket is simple:
ef416fc2 9581 *
9582 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
9583 *
9584 * %cupsJobTicket: attr1=value1
9585 * %cupsJobTicket: attr2=value2
9586 * ...
9587 * %cupsJobTicket: attrN=valueN
9588 *
9589 * Job ticket lines must appear immediately after the first line that
c5571a1d
MS
9590 * specifies PostScript (%!PS-Adobe-3.0) or PDF (%PDF) format, and CUPS
9591 * stops looking for job ticket info when it finds a line that does not begin
ef416fc2 9592 * with "%cupsJobTicket:".
9593 *
9594 * The maximum length of a job ticket line, including the prefix, is
9595 * 255 characters to conform with the Adobe DSC.
9596 *
9597 * Read-only attributes are rejected with a notice to the error log in
9598 * case a malicious user tries anything. Since the job ticket is read
9599 * prior to attribute validation in print_job(), job ticket attributes
9600 * will go through the same validation as IPP attributes...
9601 */
9602
9603static void
c5571a1d 9604read_job_ticket(cupsd_client_t *con) /* I - Client connection */
ef416fc2 9605{
9606 cups_file_t *fp; /* File to read from */
9607 char line[256]; /* Line data */
9608 int num_options; /* Number of options */
9609 cups_option_t *options; /* Options */
9610 ipp_t *ticket; /* New attributes */
9611 ipp_attribute_t *attr, /* Current attribute */
9612 *attr2, /* Job attribute */
9613 *prev2; /* Previous job attribute */
9614
9615
9616 /*
9617 * First open the print file...
9618 */
9619
9620 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
9621 {
9622 cupsdLogMessage(CUPSD_LOG_ERROR,
c5571a1d 9623 "Unable to open print file for job ticket - %s",
ef416fc2 9624 strerror(errno));
9625 return;
9626 }
9627
9628 /*
9629 * Skip the first line...
9630 */
9631
9632 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
9633 {
9634 cupsdLogMessage(CUPSD_LOG_ERROR,
c5571a1d 9635 "Unable to read from print file for job ticket - %s",
ef416fc2 9636 strerror(errno));
9637 cupsFileClose(fp);
9638 return;
9639 }
9640
c5571a1d 9641 if (strncmp(line, "%!PS-Adobe-", 11) && strncmp(line, "%PDF-", 5))
ef416fc2 9642 {
9643 /*
9644 * Not a DSC-compliant file, so no job ticket info will be available...
9645 */
9646
9647 cupsFileClose(fp);
9648 return;
9649 }
9650
9651 /*
9652 * Read job ticket info from the file...
9653 */
9654
9655 num_options = 0;
9656 options = NULL;
9657
fa73b229 9658 while (cupsFileGets(fp, line, sizeof(line)))
ef416fc2 9659 {
9660 /*
9661 * Stop at the first non-ticket line...
9662 */
9663
fa73b229 9664 if (strncmp(line, "%cupsJobTicket:", 15))
ef416fc2 9665 break;
9666
9667 /*
9668 * Add the options to the option array...
9669 */
9670
9671 num_options = cupsParseOptions(line + 15, num_options, &options);
9672 }
9673
9674 /*
9675 * Done with the file; see if we have any options...
9676 */
9677
9678 cupsFileClose(fp);
9679
9680 if (num_options == 0)
9681 return;
9682
9683 /*
9684 * OK, convert the options to an attribute list, and apply them to
9685 * the request...
9686 */
9687
9688 ticket = ippNew();
9689 cupsEncodeOptions(ticket, num_options, options);
9690
9691 /*
9692 * See what the user wants to change.
9693 */
9694
fa73b229 9695 for (attr = ticket->attrs; attr; attr = attr->next)
ef416fc2 9696 {
9697 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
9698 continue;
9699
9700 if (!strcmp(attr->name, "job-originating-host-name") ||
9701 !strcmp(attr->name, "job-originating-user-name") ||
9702 !strcmp(attr->name, "job-media-sheets-completed") ||
9703 !strcmp(attr->name, "job-k-octets") ||
9704 !strcmp(attr->name, "job-id") ||
9705 !strncmp(attr->name, "job-state", 9) ||
9706 !strncmp(attr->name, "time-at-", 8))
9707 continue; /* Read-only attrs */
9708
fa73b229 9709 if ((attr2 = ippFindAttribute(con->request, attr->name,
9710 IPP_TAG_ZERO)) != NULL)
ef416fc2 9711 {
9712 /*
9713 * Some other value; first free the old value...
9714 */
9715
9716 if (con->request->attrs == attr2)
9717 {
9718 con->request->attrs = attr2->next;
9719 prev2 = NULL;
9720 }
9721 else
9722 {
fa73b229 9723 for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
ef416fc2 9724 if (prev2->next == attr2)
9725 {
9726 prev2->next = attr2->next;
9727 break;
9728 }
9729 }
9730
9731 if (con->request->last == attr2)
9732 con->request->last = prev2;
9733
757d2cad 9734 _ippFreeAttr(attr2);
ef416fc2 9735 }
9736
9737 /*
9738 * Add new option by copying it...
9739 */
9740
9741 copy_attribute(con->request, attr, 0);
9742 }
9743
9744 /*
9745 * Then free the attribute list and option array...
9746 */
9747
9748 ippDelete(ticket);
9749 cupsFreeOptions(num_options, options);
9750}
9751
9752
9753/*
9754 * 'reject_jobs()' - Reject print jobs to a printer.
9755 */
9756
9757static void
9758reject_jobs(cupsd_client_t *con, /* I - Client connection */
9759 ipp_attribute_t *uri) /* I - Printer or class URI */
9760{
9761 http_status_t status; /* Policy status */
bc44d920 9762 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 9763 cupsd_printer_t *printer; /* Printer data */
9764 ipp_attribute_t *attr; /* printer-state-message text */
9765
9766
9767 cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", con,
9768 con->http.fd, uri->values[0].string.text);
9769
9770 /*
9771 * Is the destination valid?
9772 */
9773
f7deaa1a 9774 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 9775 {
9776 /*
9777 * Bad URI...
9778 */
9779
9780 send_ipp_status(con, IPP_NOT_FOUND,
9781 _("The printer or class was not found."));
9782 return;
9783 }
9784
9785 /*
9786 * Check policy...
9787 */
9788
9789 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
9790 {
f899b121 9791 send_http_error(con, status, printer);
ef416fc2 9792 return;
9793 }
9794
9795 /*
9796 * Reject jobs sent to the printer...
9797 */
9798
9799 printer->accepting = 0;
9800
9801 if ((attr = ippFindAttribute(con->request, "printer-state-message",
9802 IPP_TAG_TEXT)) == NULL)
9803 strcpy(printer->state_message, "Rejecting Jobs");
9804 else
9805 strlcpy(printer->state_message, attr->values[0].string.text,
9806 sizeof(printer->state_message));
9807
9808 cupsdAddPrinterHistory(printer);
9809
f8b3a85b
MS
9810 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
9811 "No longer accepting jobs.");
9812
ef416fc2 9813 if (dtype & CUPS_PRINTER_CLASS)
9814 {
3dfe78b3 9815 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
ef416fc2 9816
9817 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
f7deaa1a 9818 printer->name, get_username(con));
ef416fc2 9819 }
9820 else
9821 {
3dfe78b3 9822 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 9823
9824 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
f7deaa1a 9825 printer->name, get_username(con));
ef416fc2 9826 }
9827
9828 /*
9829 * Everything was ok, so return OK status...
9830 */
9831
9832 con->response->request.status.status_code = IPP_OK;
9833}
9834
9835
61cf44e2
MS
9836/*
9837 * 'release_held_new_jobs()' - Release pending/new jobs on a printer or class.
9838 */
9839
9840static void
9841release_held_new_jobs(
9842 cupsd_client_t *con, /* I - Connection */
9843 ipp_attribute_t *uri) /* I - Printer URI */
9844{
9845 http_status_t status; /* Policy status */
9846 cups_ptype_t dtype; /* Destination type (printer/class) */
9847 cupsd_printer_t *printer; /* Printer data */
9848
9849
9850 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_held_new_jobs(%p[%d], %s)", con,
9851 con->http.fd, uri->values[0].string.text);
9852
9853 /*
9854 * Is the destination valid?
9855 */
9856
9857 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
9858 {
9859 /*
9860 * Bad URI...
9861 */
9862
9863 send_ipp_status(con, IPP_NOT_FOUND,
9864 _("The printer or class was not found."));
9865 return;
9866 }
9867
9868 /*
9869 * Check policy...
9870 */
9871
9872 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
9873 {
9874 send_http_error(con, status, printer);
9875 return;
9876 }
9877
9878 /*
9879 * Hold pending/new jobs sent to the printer...
9880 */
9881
9882 printer->holding_new_jobs = 0;
9883
9884 cupsdSetPrinterReasons(printer, "-hold-new-jobs");
9885 cupsdAddPrinterHistory(printer);
9886
9887 if (dtype & CUPS_PRINTER_CLASS)
9888 cupsdLogMessage(CUPSD_LOG_INFO,
9889 "Class \"%s\" now printing pending/new jobs (\"%s\").",
9890 printer->name, get_username(con));
9891 else
9892 cupsdLogMessage(CUPSD_LOG_INFO,
9893 "Printer \"%s\" now printing pending/new jobs (\"%s\").",
9894 printer->name, get_username(con));
9895
9896 /*
9897 * Everything was ok, so return OK status...
9898 */
9899
9900 con->response->request.status.status_code = IPP_OK;
9901}
9902
9903
ef416fc2 9904/*
9905 * 'release_job()' - Release a held print job.
9906 */
9907
9908static void
9909release_job(cupsd_client_t *con, /* I - Client connection */
9910 ipp_attribute_t *uri) /* I - Job or Printer URI */
9911{
9912 ipp_attribute_t *attr; /* Current attribute */
9913 int jobid; /* Job ID */
61cf44e2 9914 char scheme[HTTP_MAX_URI], /* Method portion of URI */
ef416fc2 9915 username[HTTP_MAX_URI], /* Username portion of URI */
9916 host[HTTP_MAX_URI], /* Host portion of URI */
9917 resource[HTTP_MAX_URI]; /* Resource portion of URI */
9918 int port; /* Port portion of URI */
9919 cupsd_job_t *job; /* Job information */
9920
9921
9922 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", con,
9923 con->http.fd, uri->values[0].string.text);
9924
9925 /*
9926 * See if we have a job URI or a printer URI...
9927 */
9928
9929 if (!strcmp(uri->name, "printer-uri"))
9930 {
9931 /*
9932 * Got a printer URI; see if we also have a job-id attribute...
9933 */
9934
fa73b229 9935 if ((attr = ippFindAttribute(con->request, "job-id",
9936 IPP_TAG_INTEGER)) == NULL)
ef416fc2 9937 {
9938 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 9939 _("Got a printer-uri attribute but no job-id"));
ef416fc2 9940 return;
9941 }
9942
9943 jobid = attr->values[0].integer;
9944 }
9945 else
9946 {
9947 /*
9948 * Got a job URI; parse it to get the job ID...
9949 */
9950
61cf44e2
MS
9951 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9952 sizeof(scheme), username, sizeof(username), host,
a4d04587 9953 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 9954
9955 if (strncmp(resource, "/jobs/", 6))
9956 {
9957 /*
9958 * Not a valid URI!
9959 */
9960
9961 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 9962 _("Bad job-uri attribute \"%s\""),
ef416fc2 9963 uri->values[0].string.text);
9964 return;
9965 }
9966
9967 jobid = atoi(resource + 6);
9968 }
9969
9970 /*
9971 * See if the job exists...
9972 */
9973
9974 if ((job = cupsdFindJob(jobid)) == NULL)
9975 {
9976 /*
9977 * Nope - return a "not found" error...
9978 */
9979
4d301e69 9980 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
ef416fc2 9981 return;
9982 }
9983
9984 /*
9985 * See if job is "held"...
9986 */
9987
bd7854cb 9988 if (job->state_value != IPP_JOB_HELD)
ef416fc2 9989 {
9990 /*
9991 * Nope - return a "not possible" error...
9992 */
9993
4d301e69 9994 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held"), jobid);
ef416fc2 9995 return;
9996 }
9997
9998 /*
9999 * See if the job is owned by the requesting user...
10000 */
10001
10002 if (!validate_user(job, con, job->username, username, sizeof(username)))
10003 {
b0f6947b
MS
10004 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
10005 cupsdFindDest(job->dest));
ef416fc2 10006 return;
10007 }
10008
10009 /*
10010 * Reset the job-hold-until value to "no-hold"...
10011 */
10012
fa73b229 10013 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
10014 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 10015 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
10016
fa73b229 10017 if (attr)
ef416fc2 10018 {
757d2cad 10019 _cupsStrFree(attr->values[0].string.text);
4400e98d 10020
ef416fc2 10021 attr->value_tag = IPP_TAG_KEYWORD;
757d2cad 10022 attr->values[0].string.text = _cupsStrAlloc("no-hold");
d09495fa 10023
01ce6322 10024 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
d09495fa 10025 "Job job-hold-until value changed by user.");
ef416fc2 10026 }
10027
10028 /*
10029 * Release the job and return...
10030 */
10031
10032 cupsdReleaseJob(job);
10033
01ce6322 10034 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
d09495fa 10035 "Job released by user.");
10036
75bd9771 10037 cupsdLogJob(job, CUPSD_LOG_INFO, "Released by \"%s\".", username);
ef416fc2 10038
10039 con->response->request.status.status_code = IPP_OK;
005dd1eb
MS
10040
10041 cupsdCheckJobs();
ef416fc2 10042}
10043
10044
10045/*
10046 * 'renew_subscription()' - Renew an existing subscription...
10047 */
10048
10049static void
10050renew_subscription(
10051 cupsd_client_t *con, /* I - Client connection */
10052 int sub_id) /* I - Subscription ID */
10053{
bd7854cb 10054 http_status_t status; /* Policy status */
10055 cupsd_subscription_t *sub; /* Subscription */
10056 ipp_attribute_t *lease; /* notify-lease-duration */
10057
10058
10059 cupsdLogMessage(CUPSD_LOG_DEBUG2,
10060 "renew_subscription(con=%p[%d], sub_id=%d)",
10061 con, con->http.fd, sub_id);
10062
10063 /*
10064 * Is the subscription ID valid?
10065 */
10066
10067 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
10068 {
10069 /*
10070 * Bad subscription ID...
10071 */
10072
10073 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 10074 _("notify-subscription-id %d no good"), sub_id);
bd7854cb 10075 return;
10076 }
10077
10078 if (sub->job)
10079 {
10080 /*
10081 * Job subscriptions cannot be renewed...
10082 */
10083
10084 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 10085 _("Job subscriptions cannot be renewed"));
bd7854cb 10086 return;
10087 }
10088
10089 /*
10090 * Check policy...
10091 */
10092
10093 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
10094 DefaultPolicyPtr,
10095 con, sub->owner)) != HTTP_OK)
10096 {
f899b121 10097 send_http_error(con, status, sub->dest);
bd7854cb 10098 return;
10099 }
10100
10101 /*
10102 * Renew the subscription...
10103 */
10104
10105 lease = ippFindAttribute(con->request, "notify-lease-duration",
10106 IPP_TAG_INTEGER);
10107
10108 sub->lease = lease ? lease->values[0].integer : DefaultLeaseDuration;
10109
10110 if (MaxLeaseDuration && (sub->lease == 0 || sub->lease > MaxLeaseDuration))
10111 {
10112 cupsdLogMessage(CUPSD_LOG_INFO,
10113 "renew_subscription: Limiting notify-lease-duration to "
10114 "%d seconds.",
10115 MaxLeaseDuration);
10116 sub->lease = MaxLeaseDuration;
10117 }
10118
10119 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
10120
3dfe78b3 10121 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
bd7854cb 10122
10123 con->response->request.status.status_code = IPP_OK;
b94498cf 10124
10125 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
10126 "notify-lease-duration", sub->lease);
ef416fc2 10127}
10128
10129
10130/*
10131 * 'restart_job()' - Restart an old print job.
10132 */
10133
10134static void
10135restart_job(cupsd_client_t *con, /* I - Client connection */
10136 ipp_attribute_t *uri) /* I - Job or Printer URI */
10137{
10138 ipp_attribute_t *attr; /* Current attribute */
10139 int jobid; /* Job ID */
238c3832 10140 cupsd_job_t *job; /* Job information */
61cf44e2 10141 char scheme[HTTP_MAX_URI], /* Method portion of URI */
ef416fc2 10142 username[HTTP_MAX_URI], /* Username portion of URI */
10143 host[HTTP_MAX_URI], /* Host portion of URI */
10144 resource[HTTP_MAX_URI]; /* Resource portion of URI */
10145 int port; /* Port portion of URI */
ef416fc2 10146
10147
10148 cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con,
10149 con->http.fd, uri->values[0].string.text);
10150
10151 /*
10152 * See if we have a job URI or a printer URI...
10153 */
10154
10155 if (!strcmp(uri->name, "printer-uri"))
10156 {
10157 /*
10158 * Got a printer URI; see if we also have a job-id attribute...
10159 */
10160
fa73b229 10161 if ((attr = ippFindAttribute(con->request, "job-id",
10162 IPP_TAG_INTEGER)) == NULL)
ef416fc2 10163 {
10164 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 10165 _("Got a printer-uri attribute but no job-id"));
ef416fc2 10166 return;
10167 }
10168
10169 jobid = attr->values[0].integer;
10170 }
10171 else
10172 {
10173 /*
10174 * Got a job URI; parse it to get the job ID...
10175 */
10176
61cf44e2
MS
10177 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
10178 sizeof(scheme), username, sizeof(username), host,
a4d04587 10179 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 10180
fa73b229 10181 if (strncmp(resource, "/jobs/", 6))
ef416fc2 10182 {
10183 /*
10184 * Not a valid URI!
10185 */
10186
10187 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 10188 _("Bad job-uri attribute \"%s\""),
ef416fc2 10189 uri->values[0].string.text);
10190 return;
10191 }
10192
10193 jobid = atoi(resource + 6);
10194 }
10195
10196 /*
10197 * See if the job exists...
10198 */
10199
10200 if ((job = cupsdFindJob(jobid)) == NULL)
10201 {
10202 /*
10203 * Nope - return a "not found" error...
10204 */
10205
4d301e69 10206 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
ef416fc2 10207 return;
10208 }
10209
10210 /*
10211 * See if job is in any of the "completed" states...
10212 */
10213
bd7854cb 10214 if (job->state_value <= IPP_JOB_PROCESSING)
ef416fc2 10215 {
10216 /*
10217 * Nope - return a "not possible" error...
10218 */
10219
4d301e69 10220 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete"),
ef416fc2 10221 jobid);
10222 return;
10223 }
10224
10225 /*
10226 * See if we have retained the job files...
10227 */
10228
bd7854cb 10229 cupsdLoadJob(job);
10230
07725fee 10231 if (!job->attrs || job->num_files == 0)
ef416fc2 10232 {
10233 /*
10234 * Nope - return a "not possible" error...
10235 */
10236
10237 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 10238 _("Job #%d cannot be restarted - no files"), jobid);
ef416fc2 10239 return;
10240 }
10241
10242 /*
10243 * See if the job is owned by the requesting user...
10244 */
10245
10246 if (!validate_user(job, con, job->username, username, sizeof(username)))
10247 {
b0f6947b
MS
10248 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
10249 cupsdFindDest(job->dest));
ef416fc2 10250 return;
10251 }
10252
10253 /*
238c3832 10254 * See if the job-hold-until attribute is specified...
ef416fc2 10255 */
10256
238c3832
MS
10257 if ((attr = ippFindAttribute(con->request, "job-hold-until",
10258 IPP_TAG_KEYWORD)) == NULL)
10259 attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
10260
10261 if (attr && strcmp(attr->values[0].string.text, "no-hold"))
10262 {
10263 /*
10264 * Return the job to a held state...
10265 */
10266
10267 cupsdLogJob(job, CUPSD_LOG_DEBUG,
10268 "Restarted by \"%s\" with job-hold-until=%s.",
10269 username, attr->values[0].string.text);
10270 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
10271
10272 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE,
10273 NULL, job, "Job restarted by user with job-hold-until=%s",
10274 attr->values[0].string.text);
10275 }
10276 else
10277 {
10278 /*
10279 * Restart the job...
10280 */
10281
10282 cupsdRestartJob(job);
f11a948a 10283 cupsdCheckJobs();
238c3832 10284 }
ef416fc2 10285
75bd9771 10286 cupsdLogJob(job, CUPSD_LOG_INFO, "Restarted by \"%s\".", username);
ef416fc2 10287
10288 con->response->request.status.status_code = IPP_OK;
10289}
10290
10291
10292/*
10293 * 'save_auth_info()' - Save authentication information for a job.
10294 */
10295
10296static void
f7deaa1a 10297save_auth_info(
10298 cupsd_client_t *con, /* I - Client connection */
10299 cupsd_job_t *job, /* I - Job */
10300 ipp_attribute_t *auth_info) /* I - auth-info attribute, if any */
ef416fc2 10301{
09a101d6 10302 int i; /* Looping var */
10303 char filename[1024]; /* Job authentication filename */
10304 cups_file_t *fp; /* Job authentication file */
10305 char line[2048]; /* Line for file */
10306 cupsd_printer_t *dest; /* Destination printer/class */
ef416fc2 10307
10308
10309 /*
10310 * This function saves the in-memory authentication information for
10311 * a job so that it can be used to authenticate with a remote host.
10312 * The information is stored in a file that is readable only by the
f7deaa1a 10313 * root user. The fields are Base-64 encoded, each on a separate line,
10314 * followed by random number (up to 1024) of newlines to limit the
10315 * amount of information that is exposed.
ef416fc2 10316 *
10317 * Because of the potential for exposing of authentication information,
10318 * this functionality is only enabled when running cupsd as root.
10319 *
10320 * This caching only works for the Basic and BasicDigest authentication
10321 * types. Digest authentication cannot be cached this way, and in
10322 * the future Kerberos authentication may make all of this obsolete.
10323 *
10324 * Authentication information is saved whenever an authenticated
10325 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
10326 * performed.
10327 *
10328 * This information is deleted after a job is completed or canceled,
10329 * so reprints may require subsequent re-authentication.
10330 */
10331
10332 if (RunUser)
10333 return;
10334
09a101d6 10335 if ((dest = cupsdFindDest(job->dest)) == NULL)
10336 return;
10337
ef416fc2 10338 /*
10339 * Create the authentication file and change permissions...
10340 */
10341
10342 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
10343 if ((fp = cupsFileOpen(filename, "w")) == NULL)
10344 {
10345 cupsdLogMessage(CUPSD_LOG_ERROR,
10346 "Unable to save authentication info to \"%s\" - %s",
10347 filename, strerror(errno));
10348 return;
10349 }
10350
10351 fchown(cupsFileNumber(fp), 0, 0);
10352 fchmod(cupsFileNumber(fp), 0400);
10353
09a101d6 10354 if (auth_info && auth_info->num_values == dest->num_auth_info_required)
f7deaa1a 10355 {
10356 /*
09a101d6 10357 * Write 1 to 3 auth values...
f7deaa1a 10358 */
ef416fc2 10359
09a101d6 10360 cupsdClearString(&job->auth_username);
10361 cupsdClearString(&job->auth_domain);
10362 cupsdClearString(&job->auth_password);
10363
f7deaa1a 10364 for (i = 0; i < auth_info->num_values; i ++)
10365 {
10366 httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text,
10367 strlen(auth_info->values[i].string.text));
10368 cupsFilePrintf(fp, "%s\n", line);
09a101d6 10369
10370 if (!strcmp(dest->auth_info_required[i], "username"))
839a51c8 10371 cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s",
09a101d6 10372 auth_info->values[i].string.text);
10373 else if (!strcmp(dest->auth_info_required[i], "domain"))
839a51c8 10374 cupsdSetStringf(&job->auth_domain, "AUTH_DOMAIN=%s",
09a101d6 10375 auth_info->values[i].string.text);
10376 else if (!strcmp(dest->auth_info_required[i], "password"))
839a51c8 10377 cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s",
09a101d6 10378 auth_info->values[i].string.text);
f7deaa1a 10379 }
10380 }
09a101d6 10381 else if (con->username[0])
f7deaa1a 10382 {
10383 /*
10384 * Write the authenticated username...
10385 */
ef416fc2 10386
f7deaa1a 10387 httpEncode64_2(line, sizeof(line), con->username, strlen(con->username));
10388 cupsFilePrintf(fp, "%s\n", line);
ef416fc2 10389
839a51c8 10390 cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s", con->username);
09a101d6 10391 cupsdClearString(&job->auth_domain);
10392
f7deaa1a 10393 /*
10394 * Write the authenticated password...
10395 */
10396
10397 httpEncode64_2(line, sizeof(line), con->password, strlen(con->password));
10398 cupsFilePrintf(fp, "%s\n", line);
09a101d6 10399
839a51c8 10400 cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s", con->password);
f7deaa1a 10401 }
ef416fc2 10402
10403 /*
10404 * Write a random number of newlines to the end of the file...
10405 */
10406
41681883 10407 for (i = (CUPS_RAND() % 1024); i >= 0; i --)
ef416fc2 10408 cupsFilePutChar(fp, '\n');
10409
10410 /*
10411 * Close the file and return...
10412 */
10413
10414 cupsFileClose(fp);
f7deaa1a 10415
10416#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
68b10830 10417# ifdef HAVE_KRB5_IPC_CLIENT_SET_TARGET_UID
97c9a8d7
MS
10418 if (con->have_gss &&
10419 (con->http.hostaddr->addr.sa_family == AF_LOCAL || con->gss_creds))
68b10830 10420# else
97c9a8d7 10421 if (con->have_gss && con->gss_creds)
68b10830 10422# endif /* HAVE_KRB5_IPC_CLIENT_SET_TARGET_UID */
09a101d6 10423 save_krb5_creds(con, job);
10424 else if (job->ccname)
10425 cupsdClearString(&(job->ccname));
f7deaa1a 10426#endif /* HAVE_GSSAPI && HAVE_KRB5_H */
10427}
10428
10429
10430#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
10431/*
10432 * 'save_krb5_creds()' - Save Kerberos credentials for the job.
10433 */
10434
10435static void
10436save_krb5_creds(cupsd_client_t *con, /* I - Client connection */
10437 cupsd_job_t *job) /* I - Job */
10438{
f7deaa1a 10439 /*
e07d4801 10440 * Get the credentials...
f7deaa1a 10441 */
10442
e07d4801 10443 job->ccache = cupsdCopyKrb5Creds(con);
f7deaa1a 10444
7ff4fea9
MS
10445 /*
10446 * Add the KRB5CCNAME environment variable to the job so that the
10447 * backend can use the credentials when printing.
10448 */
10449
e07d4801
MS
10450 if (job->ccache)
10451 {
10452 cupsdSetStringf(&(job->ccname), "KRB5CCNAME=FILE:%s",
10453 krb5_cc_get_name(KerberosContext, job->ccache));
cc0d019f 10454
e07d4801
MS
10455 cupsdLogJob(job, CUPSD_LOG_DEBUG2, "save_krb5_creds: %s", job->ccname);
10456 }
10457 else
10458 cupsdClearString(&(job->ccname));
ef416fc2 10459}
f7deaa1a 10460#endif /* HAVE_GSSAPI && HAVE_KRB5_H */
ef416fc2 10461
10462
10463/*
10464 * 'send_document()' - Send a file to a printer or class.
10465 */
10466
10467static void
10468send_document(cupsd_client_t *con, /* I - Client connection */
10469 ipp_attribute_t *uri) /* I - Printer URI */
10470{
10471 ipp_attribute_t *attr; /* Current attribute */
a0f6818e
MS
10472 ipp_attribute_t *format; /* Request's document-format attribute */
10473 ipp_attribute_t *jformat; /* Job's document-format attribute */
f7deaa1a 10474 const char *default_format;/* document-format-default value */
ef416fc2 10475 int jobid; /* Job ID number */
10476 cupsd_job_t *job; /* Current job */
10477 char job_uri[HTTP_MAX_URI],
10478 /* Job URI */
61cf44e2 10479 scheme[HTTP_MAX_URI],
ef416fc2 10480 /* Method portion of URI */
10481 username[HTTP_MAX_URI],
10482 /* Username portion of URI */
10483 host[HTTP_MAX_URI],
10484 /* Host portion of URI */
10485 resource[HTTP_MAX_URI];
10486 /* Resource portion of URI */
10487 int port; /* Port portion of URI */
10488 mime_type_t *filetype; /* Type of file */
10489 char super[MIME_MAX_SUPER],
10490 /* Supertype of file */
10491 type[MIME_MAX_TYPE],
10492 /* Subtype of file */
10493 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
10494 /* Textual name of mime type */
10495 char filename[1024]; /* Job filename */
10496 cupsd_printer_t *printer; /* Current printer */
10497 struct stat fileinfo; /* File information */
10498 int kbytes; /* Size of file */
10499 int compression; /* Type of compression */
75bd9771 10500 int start_job; /* Start the job? */
ef416fc2 10501
10502
10503 cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con,
10504 con->http.fd, uri->values[0].string.text);
10505
10506 /*
10507 * See if we have a job URI or a printer URI...
10508 */
10509
10510 if (!strcmp(uri->name, "printer-uri"))
10511 {
10512 /*
10513 * Got a printer URI; see if we also have a job-id attribute...
10514 */
10515
fa73b229 10516 if ((attr = ippFindAttribute(con->request, "job-id",
10517 IPP_TAG_INTEGER)) == NULL)
ef416fc2 10518 {
10519 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 10520 _("Got a printer-uri attribute but no job-id"));
ef416fc2 10521 return;
10522 }
10523
10524 jobid = attr->values[0].integer;
10525 }
10526 else
10527 {
10528 /*
10529 * Got a job URI; parse it to get the job ID...
10530 */
10531
61cf44e2
MS
10532 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
10533 sizeof(scheme), username, sizeof(username), host,
a4d04587 10534 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 10535
10536 if (strncmp(resource, "/jobs/", 6))
10537 {
10538 /*
10539 * Not a valid URI!
10540 */
10541
10542 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 10543 _("Bad job-uri attribute \"%s\""),
ef416fc2 10544 uri->values[0].string.text);
10545 return;
10546 }
10547
10548 jobid = atoi(resource + 6);
10549 }
10550
10551 /*
10552 * See if the job exists...
10553 */
10554
10555 if ((job = cupsdFindJob(jobid)) == NULL)
10556 {
10557 /*
10558 * Nope - return a "not found" error...
10559 */
10560
4d301e69 10561 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
ef416fc2 10562 return;
10563 }
10564
f301802f 10565 printer = cupsdFindDest(job->dest);
10566
ef416fc2 10567 /*
10568 * See if the job is owned by the requesting user...
10569 */
10570
10571 if (!validate_user(job, con, job->username, username, sizeof(username)))
10572 {
b0f6947b
MS
10573 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
10574 cupsdFindDest(job->dest));
ef416fc2 10575 return;
10576 }
10577
10578 /*
10579 * OK, see if the client is sending the document compressed - CUPS
10580 * only supports "none" and "gzip".
10581 */
10582
10583 compression = CUPS_FILE_NONE;
10584
fa73b229 10585 if ((attr = ippFindAttribute(con->request, "compression",
10586 IPP_TAG_KEYWORD)) != NULL)
ef416fc2 10587 {
10588 if (strcmp(attr->values[0].string.text, "none")
10589#ifdef HAVE_LIBZ
10590 && strcmp(attr->values[0].string.text, "gzip")
10591#endif /* HAVE_LIBZ */
10592 )
10593 {
4d301e69 10594 send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\""),
ef416fc2 10595 attr->values[0].string.text);
10596 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
10597 "compression", NULL, attr->values[0].string.text);
10598 return;
10599 }
10600
10601#ifdef HAVE_LIBZ
10602 if (!strcmp(attr->values[0].string.text, "gzip"))
10603 compression = CUPS_FILE_GZIP;
10604#endif /* HAVE_LIBZ */
10605 }
10606
10607 /*
10608 * Do we have a file to print?
10609 */
10610
10611 if (!con->filename)
10612 {
6d2f911b
MS
10613 /*
10614 * Check for an empty request with "last-document" set to true, which is
10615 * used to close an "open" job by RFC 2911, section 3.3.2.
10616 */
10617
10618 if (job->num_files > 0 &&
10619 (attr = ippFindAttribute(con->request, "last-document",
10620 IPP_TAG_BOOLEAN)) != NULL &&
10621 attr->values[0].boolean)
10622 goto last_document;
10623
4d301e69 10624 send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?"));
ef416fc2 10625 return;
10626 }
10627
10628 /*
10629 * Is it a format we support?
10630 */
10631
10632 if ((format = ippFindAttribute(con->request, "document-format",
10633 IPP_TAG_MIMETYPE)) != NULL)
10634 {
10635 /*
10636 * Grab format from client...
10637 */
10638
bc44d920 10639 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
10640 super, type) != 2)
ef416fc2 10641 {
4d301e69 10642 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\""),
ef416fc2 10643 format->values[0].string.text);
10644 return;
10645 }
10646 }
f7deaa1a 10647 else if ((default_format = cupsGetOption("document-format",
10648 printer->num_options,
10649 printer->options)) != NULL)
10650 {
10651 /*
10652 * Use default document format...
10653 */
10654
10655 if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
10656 {
10657 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 10658 _("Could not scan type \"%s\""),
f7deaa1a 10659 default_format);
10660 return;
10661 }
10662 }
ef416fc2 10663 else
10664 {
10665 /*
10666 * No document format attribute? Auto-type it!
10667 */
10668
10669 strcpy(super, "application");
10670 strcpy(type, "octet-stream");
10671 }
10672
fa73b229 10673 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
ef416fc2 10674 {
10675 /*
10676 * Auto-type the file...
10677 */
10678
bd7854cb 10679 ipp_attribute_t *doc_name; /* document-name attribute */
10680
10681
75bd9771 10682 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Auto-typing file...");
ef416fc2 10683
bd7854cb 10684 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
10685 filetype = mimeFileType(MimeDatabase, con->filename,
10686 doc_name ? doc_name->values[0].string.text : NULL,
10687 &compression);
ef416fc2 10688
f7deaa1a 10689 if (!filetype)
f7faf1f5 10690 filetype = mimeType(MimeDatabase, super, type);
a0f6818e 10691
75bd9771
MS
10692 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Request file type is %s/%s.",
10693 filetype->super, filetype->type);
ed486911 10694 }
f7faf1f5 10695 else
10696 filetype = mimeType(MimeDatabase, super, type);
10697
49d87452 10698 if (filetype)
f7deaa1a 10699 {
10700 /*
10701 * Replace the document-format attribute value with the auto-typed or
10702 * default one.
10703 */
10704
10705 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
10706 filetype->type);
10707
49d87452
MS
10708 if ((jformat = ippFindAttribute(job->attrs, "document-format",
10709 IPP_TAG_MIMETYPE)) != NULL)
f7deaa1a 10710 {
a0f6818e 10711 _cupsStrFree(jformat->values[0].string.text);
f7deaa1a 10712
a0f6818e 10713 jformat->values[0].string.text = _cupsStrAlloc(mimetype);
f7deaa1a 10714 }
10715 else
a0f6818e 10716 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
f7deaa1a 10717 "document-format", NULL, mimetype);
10718 }
10719 else if (!filetype)
ef416fc2 10720 {
10721 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
4d301e69 10722 _("Unsupported format \'%s/%s\'"), super, type);
ef416fc2 10723 cupsdLogMessage(CUPSD_LOG_INFO,
10724 "Hint: Do you have the raw file printing rules enabled?");
10725
10726 if (format)
10727 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
10728 "document-format", NULL, format->values[0].string.text);
10729
10730 return;
10731 }
10732
80ca4592 10733 if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
10734 {
10735 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
10736 filetype->type);
10737
10738 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
4d301e69 10739 _("Unsupported format \'%s\'"), mimetype);
80ca4592 10740
10741 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
10742 "document-format", NULL, mimetype);
10743
10744 return;
10745 }
10746
ef416fc2 10747 /*
10748 * Add the file to the job...
10749 */
10750
bd7854cb 10751 cupsdLoadJob(job);
10752
ef416fc2 10753 if (add_file(con, job, filetype, compression))
10754 return;
10755
ef416fc2 10756 if (stat(con->filename, &fileinfo))
10757 kbytes = 0;
10758 else
10759 kbytes = (fileinfo.st_size + 1023) / 1024;
10760
10761 cupsdUpdateQuota(printer, job->username, 0, kbytes);
10762
fa73b229 10763 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
10764 IPP_TAG_INTEGER)) != NULL)
ef416fc2 10765 attr->values[0].integer += kbytes;
10766
10767 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
10768 job->num_files);
10769 rename(con->filename, filename);
10770
10771 cupsdClearString(&con->filename);
10772
75bd9771
MS
10773 cupsdLogJob(job, CUPSD_LOG_INFO, "File of type %s/%s queued by \"%s\".",
10774 filetype->super, filetype->type, job->username);
ef416fc2 10775
10776 /*
10777 * Start the job if this is the last document...
10778 */
10779
6d2f911b
MS
10780 last_document:
10781
fa73b229 10782 if ((attr = ippFindAttribute(con->request, "last-document",
10783 IPP_TAG_BOOLEAN)) != NULL &&
ef416fc2 10784 attr->values[0].boolean)
10785 {
10786 /*
10787 * See if we need to add the ending sheet...
10788 */
10789
91c84a35
MS
10790 if (cupsdTimeoutJob(job))
10791 return;
ef416fc2 10792
bd7854cb 10793 if (job->state_value == IPP_JOB_STOPPED)
10794 {
ef416fc2 10795 job->state->values[0].integer = IPP_JOB_PENDING;
bd7854cb 10796 job->state_value = IPP_JOB_PENDING;
10797 }
10798 else if (job->state_value == IPP_JOB_HELD)
ef416fc2 10799 {
fa73b229 10800 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
10801 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 10802 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
10803
fa73b229 10804 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
bd7854cb 10805 {
ef416fc2 10806 job->state->values[0].integer = IPP_JOB_PENDING;
bd7854cb 10807 job->state_value = IPP_JOB_PENDING;
10808 }
ef416fc2 10809 }
10810
3dfe78b3
MS
10811 job->dirty = 1;
10812 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 10813
75bd9771 10814 start_job = 1;
ef416fc2 10815 }
10816 else
10817 {
fa73b229 10818 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
10819 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 10820 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
10821
fa73b229 10822 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
ef416fc2 10823 {
10824 job->state->values[0].integer = IPP_JOB_HELD;
bd7854cb 10825 job->state_value = IPP_JOB_HELD;
dfd5680b 10826 job->hold_until = time(NULL) + MultipleOperationTimeout;
3dfe78b3
MS
10827 job->dirty = 1;
10828
10829 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 10830 }
75bd9771
MS
10831
10832 start_job = 0;
ef416fc2 10833 }
10834
10835 /*
10836 * Fill in the response info...
10837 */
10838
10839 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
10840 LocalPort, jobid);
10841
10842 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
10843 job_uri);
10844
10845 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
10846
10847 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
75bd9771 10848 job->state_value);
ef416fc2 10849 add_job_state_reasons(con, job);
10850
10851 con->response->request.status.status_code = IPP_OK;
75bd9771
MS
10852
10853 /*
10854 * Start the job if necessary...
10855 */
10856
10857 if (start_job)
10858 cupsdCheckJobs();
ef416fc2 10859}
10860
10861
10862/*
10863 * 'send_http_error()' - Send a HTTP error back to the IPP client.
10864 */
10865
10866static void
f899b121 10867send_http_error(
10868 cupsd_client_t *con, /* I - Client connection */
10869 http_status_t status, /* I - HTTP status code */
10870 cupsd_printer_t *printer) /* I - Printer, if any */
ef416fc2 10871{
9f5eb9be
MS
10872 ipp_attribute_t *uri; /* Request URI, if any */
10873
10874
10875 if ((uri = ippFindAttribute(con->request, "printer-uri",
10876 IPP_TAG_URI)) == NULL)
10877 uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI);
10878
10879 cupsdLogMessage(status == HTTP_FORBIDDEN ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
10880 "Returning HTTP %s for %s (%s) from %s",
10881 httpStatus(status),
10882 ippOpString(con->request->request.op.operation_id),
10883 uri ? uri->values[0].string.text : "no URI",
10884 con->http.hostname);
ef416fc2 10885
5f64df29
MS
10886 if (printer)
10887 {
db0bd74a 10888 int auth_type; /* Type of authentication required */
2fb76298
MS
10889
10890
5f64df29
MS
10891 auth_type = CUPSD_AUTH_NONE;
10892
10893 if (status == HTTP_UNAUTHORIZED &&
10894 printer->num_auth_info_required > 0 &&
10895 !strcmp(printer->auth_info_required[0], "negotiate") &&
10896 con->request &&
10897 (con->request->request.op.operation_id == IPP_PRINT_JOB ||
10898 con->request->request.op.operation_id == IPP_CREATE_JOB ||
10899 con->request->request.op.operation_id == CUPS_AUTHENTICATE_JOB))
10900 {
10901 /*
10902 * Creating and authenticating jobs requires Kerberos...
10903 */
10904
10905 auth_type = CUPSD_AUTH_NEGOTIATE;
10906 }
db0bd74a 10907 else
5f64df29
MS
10908 {
10909 /*
10910 * Use policy/location-defined authentication requirements...
10911 */
10912
10913 char resource[HTTP_MAX_URI]; /* Resource portion of URI */
10914 cupsd_location_t *auth; /* Pointer to authentication element */
10915
10916
10917 if (printer->type & CUPS_PRINTER_CLASS)
10918 snprintf(resource, sizeof(resource), "/classes/%s", printer->name);
10919 else
10920 snprintf(resource, sizeof(resource), "/printers/%s", printer->name);
10921
10922 if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
10923 auth->type == CUPSD_AUTH_NONE)
10924 auth = cupsdFindPolicyOp(printer->op_policy_ptr,
10925 con->request ?
10926 con->request->request.op.operation_id :
10927 IPP_PRINT_JOB);
10928
10929 if (auth)
10930 {
10931 if (auth->type == CUPSD_AUTH_DEFAULT)
10932 auth_type = DefaultAuthType;
10933 else
10934 auth_type = auth->type;
10935 }
10936 }
2fb76298 10937
db0bd74a 10938 cupsdSendError(con, status, auth_type);
2fb76298 10939 }
f899b121 10940 else
5bd77a73 10941 cupsdSendError(con, status, CUPSD_AUTH_NONE);
ef416fc2 10942
10943 ippDelete(con->response);
10944 con->response = NULL;
10945
10946 return;
10947}
10948
10949
10950/*
10951 * 'send_ipp_status()' - Send a status back to the IPP client.
10952 */
10953
10954static void
10955send_ipp_status(cupsd_client_t *con, /* I - Client connection */
1f0275e3
MS
10956 ipp_status_t status, /* I - IPP status code */
10957 const char *message,/* I - Status message */
10958 ...) /* I - Additional args as needed */
ef416fc2 10959{
10960 va_list ap; /* Pointer to additional args */
10961 char formatted[1024]; /* Formatted errror message */
10962
10963
3d8365b8 10964 va_start(ap, message);
10965 vsnprintf(formatted, sizeof(formatted),
10966 _cupsLangString(con->language, message), ap);
10967 va_end(ap);
ef416fc2 10968
3d8365b8 10969 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
10970 ippOpString(con->request->request.op.operation_id),
10971 ippErrorString(status), formatted);
ef416fc2 10972
10973 con->response->request.status.status_code = status;
10974
fa73b229 10975 if (ippFindAttribute(con->response, "attributes-charset",
10976 IPP_TAG_ZERO) == NULL)
ef416fc2 10977 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
f8b3a85b 10978 "attributes-charset", NULL, "utf-8");
ef416fc2 10979
10980 if (ippFindAttribute(con->response, "attributes-natural-language",
10981 IPP_TAG_ZERO) == NULL)
10982 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
10983 "attributes-natural-language", NULL, DefaultLanguage);
10984
3d8365b8 10985 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
10986 "status-message", NULL, formatted);
ef416fc2 10987}
10988
10989
10990/*
10991 * 'set_default()' - Set the default destination...
10992 */
10993
10994static void
10995set_default(cupsd_client_t *con, /* I - Client connection */
10996 ipp_attribute_t *uri) /* I - Printer URI */
10997{
10998 http_status_t status; /* Policy status */
bc44d920 10999 cups_ptype_t dtype; /* Destination type (printer/class) */
dd1abb6b
MS
11000 cupsd_printer_t *printer, /* Printer */
11001 *oldprinter; /* Old default printer */
ef416fc2 11002
11003
11004 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con,
11005 con->http.fd, uri->values[0].string.text);
11006
11007 /*
11008 * Is the destination valid?
11009 */
11010
f7deaa1a 11011 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 11012 {
11013 /*
11014 * Bad URI...
11015 */
11016
11017 send_ipp_status(con, IPP_NOT_FOUND,
11018 _("The printer or class was not found."));
11019 return;
11020 }
11021
11022 /*
11023 * Check policy...
11024 */
11025
11026 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
11027 {
f899b121 11028 send_http_error(con, status, NULL);
ef416fc2 11029 return;
11030 }
11031
11032 /*
11033 * Set it as the default...
11034 */
11035
dd1abb6b 11036 oldprinter = DefaultPrinter;
ef416fc2 11037 DefaultPrinter = printer;
11038
dd1abb6b
MS
11039 if (oldprinter)
11040 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, oldprinter, NULL,
11041 "%s is no longer the default printer.", oldprinter->name);
11042
11043 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
11044 "%s is now the default printer.", printer->name);
11045
3dfe78b3
MS
11046 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS | CUPSD_DIRTY_CLASSES |
11047 CUPSD_DIRTY_REMOTE | CUPSD_DIRTY_PRINTCAP);
ef416fc2 11048
11049 cupsdLogMessage(CUPSD_LOG_INFO,
f7deaa1a 11050 "Default destination set to \"%s\" by \"%s\".",
11051 printer->name, get_username(con));
ef416fc2 11052
11053 /*
11054 * Everything was ok, so return OK status...
11055 */
11056
11057 con->response->request.status.status_code = IPP_OK;
11058}
11059
11060
11061/*
11062 * 'set_job_attrs()' - Set job attributes.
11063 */
11064
11065static void
11066set_job_attrs(cupsd_client_t *con, /* I - Client connection */
11067 ipp_attribute_t *uri) /* I - Job URI */
11068{
11069 ipp_attribute_t *attr, /* Current attribute */
11070 *attr2; /* Job attribute */
11071 int jobid; /* Job ID */
11072 cupsd_job_t *job; /* Current job */
61cf44e2 11073 char scheme[HTTP_MAX_URI],
ef416fc2 11074 /* Method portion of URI */
11075 username[HTTP_MAX_URI],
11076 /* Username portion of URI */
11077 host[HTTP_MAX_URI],
11078 /* Host portion of URI */
11079 resource[HTTP_MAX_URI];
11080 /* Resource portion of URI */
11081 int port; /* Port portion of URI */
d09495fa 11082 int event; /* Events? */
005dd1eb 11083 int check_jobs; /* Check jobs? */
ef416fc2 11084
11085
11086 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
11087 con->http.fd, uri->values[0].string.text);
11088
11089 /*
11090 * Start with "everything is OK" status...
11091 */
11092
11093 con->response->request.status.status_code = IPP_OK;
11094
11095 /*
11096 * See if we have a job URI or a printer URI...
11097 */
11098
fa73b229 11099 if (!strcmp(uri->name, "printer-uri"))
ef416fc2 11100 {
11101 /*
11102 * Got a printer URI; see if we also have a job-id attribute...
11103 */
11104
fa73b229 11105 if ((attr = ippFindAttribute(con->request, "job-id",
11106 IPP_TAG_INTEGER)) == NULL)
ef416fc2 11107 {
11108 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 11109 _("Got a printer-uri attribute but no job-id"));
ef416fc2 11110 return;
11111 }
11112
11113 jobid = attr->values[0].integer;
11114 }
11115 else
11116 {
11117 /*
11118 * Got a job URI; parse it to get the job ID...
11119 */
11120
61cf44e2
MS
11121 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
11122 sizeof(scheme), username, sizeof(username), host,
a4d04587 11123 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 11124
fa73b229 11125 if (strncmp(resource, "/jobs/", 6))
ef416fc2 11126 {
11127 /*
11128 * Not a valid URI!
11129 */
11130
11131 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 11132 _("Bad job-uri attribute \"%s\""),
ef416fc2 11133 uri->values[0].string.text);
11134 return;
11135 }
11136
11137 jobid = atoi(resource + 6);
11138 }
11139
11140 /*
11141 * See if the job exists...
11142 */
11143
11144 if ((job = cupsdFindJob(jobid)) == NULL)
11145 {
11146 /*
11147 * Nope - return a "not found" error...
11148 */
11149
4d301e69 11150 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
ef416fc2 11151 return;
11152 }
11153
11154 /*
11155 * See if the job has been completed...
11156 */
11157
bd7854cb 11158 if (job->state_value > IPP_JOB_STOPPED)
ef416fc2 11159 {
11160 /*
11161 * Return a "not-possible" error...
11162 */
11163
11164 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 11165 _("Job #%d is finished and cannot be altered"), jobid);
ef416fc2 11166 return;
11167 }
11168
11169 /*
11170 * See if the job is owned by the requesting user...
11171 */
11172
11173 if (!validate_user(job, con, job->username, username, sizeof(username)))
11174 {
b0f6947b
MS
11175 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
11176 cupsdFindDest(job->dest));
ef416fc2 11177 return;
11178 }
11179
11180 /*
11181 * See what the user wants to change.
11182 */
11183
bd7854cb 11184 cupsdLoadJob(job);
11185
005dd1eb
MS
11186 check_jobs = 0;
11187 event = 0;
d09495fa 11188
fa73b229 11189 for (attr = con->request->attrs; attr; attr = attr->next)
ef416fc2 11190 {
11191 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
11192 continue;
11193
11194 if (!strcmp(attr->name, "attributes-charset") ||
11195 !strcmp(attr->name, "attributes-natural-language") ||
11196 !strcmp(attr->name, "document-compression") ||
11197 !strcmp(attr->name, "document-format") ||
11198 !strcmp(attr->name, "job-detailed-status-messages") ||
11199 !strcmp(attr->name, "job-document-access-errors") ||
11200 !strcmp(attr->name, "job-id") ||
5d6412a9 11201 !strcmp(attr->name, "job-impressions-completed") ||
ef416fc2 11202 !strcmp(attr->name, "job-k-octets") ||
11203 !strcmp(attr->name, "job-originating-host-name") ||
11204 !strcmp(attr->name, "job-originating-user-name") ||
11205 !strcmp(attr->name, "job-printer-up-time") ||
11206 !strcmp(attr->name, "job-printer-uri") ||
11207 !strcmp(attr->name, "job-sheets") ||
11208 !strcmp(attr->name, "job-state-message") ||
11209 !strcmp(attr->name, "job-state-reasons") ||
11210 !strcmp(attr->name, "job-uri") ||
11211 !strcmp(attr->name, "number-of-documents") ||
11212 !strcmp(attr->name, "number-of-intervening-jobs") ||
11213 !strcmp(attr->name, "output-device-assigned") ||
11214 !strncmp(attr->name, "date-time-at-", 13) ||
ef416fc2 11215 !strncmp(attr->name, "job-k-octets", 12) ||
11216 !strncmp(attr->name, "job-media-sheets", 16) ||
11217 !strncmp(attr->name, "time-at-", 8))
11218 {
11219 /*
11220 * Read-only attrs!
11221 */
11222
11223 send_ipp_status(con, IPP_ATTRIBUTES_NOT_SETTABLE,
11224 _("%s cannot be changed."), attr->name);
11225
11226 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
11227 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
11228
11229 continue;
11230 }
11231
11232 if (!strcmp(attr->name, "job-priority"))
11233 {
11234 /*
11235 * Change the job priority...
11236 */
11237
11238 if (attr->value_tag != IPP_TAG_INTEGER)
11239 {
4d301e69 11240 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value"));
ef416fc2 11241
11242 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
11243 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
11244 }
bd7854cb 11245 else if (job->state_value >= IPP_JOB_PROCESSING)
ef416fc2 11246 {
11247 send_ipp_status(con, IPP_NOT_POSSIBLE,
11248 _("Job is completed and cannot be changed."));
11249 return;
11250 }
11251 else if (con->response->request.status.status_code == IPP_OK)
d09495fa 11252 {
005dd1eb
MS
11253 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-priority to %d",
11254 attr->values[0].integer);
ef416fc2 11255 cupsdSetJobPriority(job, attr->values[0].integer);
005dd1eb
MS
11256
11257 check_jobs = 1;
11258 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED |
11259 CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED;
d09495fa 11260 }
ef416fc2 11261 }
11262 else if (!strcmp(attr->name, "job-state"))
11263 {
11264 /*
11265 * Change the job state...
11266 */
11267
11268 if (attr->value_tag != IPP_TAG_ENUM)
11269 {
4d301e69 11270 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value"));
ef416fc2 11271
11272 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
11273 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
11274 }
11275 else
11276 {
11277 switch (attr->values[0].integer)
11278 {
11279 case IPP_JOB_PENDING :
11280 case IPP_JOB_HELD :
bd7854cb 11281 if (job->state_value > IPP_JOB_HELD)
ef416fc2 11282 {
11283 send_ipp_status(con, IPP_NOT_POSSIBLE,
11284 _("Job state cannot be changed."));
11285 return;
11286 }
11287 else if (con->response->request.status.status_code == IPP_OK)
bd7854cb 11288 {
005dd1eb
MS
11289 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
11290 attr->values[0].integer);
b9faaae1
MS
11291 cupsdSetJobState(job, attr->values[0].integer,
11292 CUPSD_JOB_DEFAULT,
11293 "Job state changed by \"%s\"", username);
005dd1eb 11294 check_jobs = 1;
bd7854cb 11295 }
ef416fc2 11296 break;
11297
11298 case IPP_JOB_PROCESSING :
11299 case IPP_JOB_STOPPED :
bd7854cb 11300 if (job->state_value != attr->values[0].integer)
ef416fc2 11301 {
11302 send_ipp_status(con, IPP_NOT_POSSIBLE,
11303 _("Job state cannot be changed."));
11304 return;
11305 }
11306 break;
11307
d09495fa 11308 case IPP_JOB_CANCELED :
ef416fc2 11309 case IPP_JOB_ABORTED :
11310 case IPP_JOB_COMPLETED :
bd7854cb 11311 if (job->state_value > IPP_JOB_PROCESSING)
ef416fc2 11312 {
11313 send_ipp_status(con, IPP_NOT_POSSIBLE,
11314 _("Job state cannot be changed."));
11315 return;
11316 }
11317 else if (con->response->request.status.status_code == IPP_OK)
005dd1eb
MS
11318 {
11319 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
11320 attr->values[0].integer);
b9faaae1
MS
11321 cupsdSetJobState(job, (ipp_jstate_t)attr->values[0].integer,
11322 CUPSD_JOB_DEFAULT,
11323 "Job state changed by \"%s\"", username);
005dd1eb
MS
11324 check_jobs = 1;
11325 }
ef416fc2 11326 break;
11327 }
11328 }
11329 }
11330 else if (con->response->request.status.status_code != IPP_OK)
11331 continue;
fa73b229 11332 else if ((attr2 = ippFindAttribute(job->attrs, attr->name,
11333 IPP_TAG_ZERO)) != NULL)
ef416fc2 11334 {
11335 /*
11336 * Some other value; first free the old value...
11337 */
11338
11339 if (job->attrs->prev)
11340 job->attrs->prev->next = attr2->next;
11341 else
11342 job->attrs->attrs = attr2->next;
11343
11344 if (job->attrs->last == attr2)
11345 job->attrs->last = job->attrs->prev;
11346
757d2cad 11347 _ippFreeAttr(attr2);
ef416fc2 11348
11349 /*
11350 * Then copy the attribute...
11351 */
11352
11353 copy_attribute(job->attrs, attr, 0);
11354
11355 /*
11356 * See if the job-name or job-hold-until is being changed.
11357 */
11358
11359 if (!strcmp(attr->name, "job-hold-until"))
11360 {
005dd1eb
MS
11361 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-hold-until to %s",
11362 attr->values[0].string.text);
b9faaae1 11363 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
ef416fc2 11364
11365 if (!strcmp(attr->values[0].string.text, "no-hold"))
b9faaae1 11366 {
ef416fc2 11367 cupsdReleaseJob(job);
b9faaae1
MS
11368 check_jobs = 1;
11369 }
ef416fc2 11370 else
b9faaae1
MS
11371 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT,
11372 "Job held by \"%s\".", username);
d09495fa 11373
b9faaae1 11374 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
ef416fc2 11375 }
11376 }
11377 else if (attr->value_tag == IPP_TAG_DELETEATTR)
11378 {
11379 /*
11380 * Delete the attribute...
11381 */
11382
11383 if ((attr2 = ippFindAttribute(job->attrs, attr->name,
11384 IPP_TAG_ZERO)) != NULL)
11385 {
11386 if (job->attrs->prev)
11387 job->attrs->prev->next = attr2->next;
11388 else
11389 job->attrs->attrs = attr2->next;
11390
11391 if (attr2 == job->attrs->last)
11392 job->attrs->last = job->attrs->prev;
11393
757d2cad 11394 _ippFreeAttr(attr2);
d09495fa 11395
11396 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
ef416fc2 11397 }
11398 }
11399 else
11400 {
11401 /*
11402 * Add new option by copying it...
11403 */
11404
11405 copy_attribute(job->attrs, attr, 0);
d09495fa 11406
11407 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
ef416fc2 11408 }
11409 }
11410
11411 /*
11412 * Save the job...
11413 */
11414
3dfe78b3
MS
11415 job->dirty = 1;
11416 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 11417
d09495fa 11418 /*
11419 * Send events as needed...
11420 */
11421
d9bca400 11422 if (event & CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED)
01ce6322
MS
11423 cupsdAddEvent(CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED,
11424 cupsdFindDest(job->dest), job,
d9bca400
MS
11425 "Job priority changed by user.");
11426
d09495fa 11427 if (event & CUPSD_EVENT_JOB_STATE)
01ce6322 11428 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
d09495fa 11429 job->state_value == IPP_JOB_HELD ?
11430 "Job held by user." : "Job restarted by user.");
11431
11432 if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
01ce6322 11433 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
d09495fa 11434 "Job options changed by user.");
11435
ef416fc2 11436 /*
11437 * Start jobs if possible...
11438 */
11439
005dd1eb
MS
11440 if (check_jobs)
11441 cupsdCheckJobs();
ef416fc2 11442}
11443
11444
c168a833
MS
11445/*
11446 * 'set_printer_attrs()' - Set printer attributes.
11447 */
11448
11449static void
11450set_printer_attrs(cupsd_client_t *con, /* I - Client connection */
11451 ipp_attribute_t *uri) /* I - Printer */
11452{
11453 http_status_t status; /* Policy status */
11454 cups_ptype_t dtype; /* Destination type (printer/class) */
11455 cupsd_printer_t *printer; /* Printer/class */
11456 ipp_attribute_t *attr; /* Printer attribute */
bf3816c7 11457 int changed = 0; /* Was anything changed? */
c168a833
MS
11458
11459
11460 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_attrs(%p[%d], %s)", con,
11461 con->http.fd, uri->values[0].string.text);
11462
11463 /*
11464 * Is the destination valid?
11465 */
11466
11467 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
11468 {
11469 /*
11470 * Bad URI...
11471 */
11472
11473 send_ipp_status(con, IPP_NOT_FOUND,
11474 _("The printer or class was not found."));
11475 return;
11476 }
11477
11478 /*
11479 * Check policy...
11480 */
11481
11482 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11483 {
11484 send_http_error(con, status, printer);
11485 return;
11486 }
11487
11488 /*
11489 * Return a list of attributes that can be set via Set-Printer-Attributes.
11490 */
11491
11492 if ((attr = ippFindAttribute(con->request, "printer-location",
11493 IPP_TAG_TEXT)) != NULL)
11494 {
11495 cupsdSetString(&printer->location, attr->values[0].string.text);
11496 changed = 1;
11497 }
11498
11499 if ((attr = ippFindAttribute(con->request, "printer-info",
11500 IPP_TAG_TEXT)) != NULL)
11501 {
11502 cupsdSetString(&printer->info, attr->values[0].string.text);
11503 changed = 1;
11504 }
11505
11506 /*
11507 * Update the printer attributes and return...
11508 */
11509
11510 if (changed)
11511 {
11512 cupsdSetPrinterAttrs(printer);
11513 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
11514
11515 cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG, printer, NULL,
11516 "Printer \"%s\" description or location changed by \"%s\".",
11517 printer->name, get_username(con));
11518
11519 cupsdLogMessage(CUPSD_LOG_INFO,
11520 "Printer \"%s\" description or location changed by \"%s\".",
11521 printer->name, get_username(con));
11522 }
11523
11524 con->response->request.status.status_code = IPP_OK;
11525}
11526
11527
b423cd4c 11528/*
11529 * 'set_printer_defaults()' - Set printer default options from a request.
11530 */
11531
11532static void
11533set_printer_defaults(
11534 cupsd_client_t *con, /* I - Client connection */
11535 cupsd_printer_t *printer) /* I - Printer */
11536{
11537 int i; /* Looping var */
11538 ipp_attribute_t *attr; /* Current attribute */
11539 int namelen; /* Length of attribute name */
11540 char name[256], /* New attribute name */
11541 value[256]; /* String version of integer attrs */
11542
11543
11544 for (attr = con->request->attrs; attr; attr = attr->next)
11545 {
11546 /*
11547 * Skip non-printer attributes...
11548 */
11549
11550 if (attr->group_tag != IPP_TAG_PRINTER || !attr->name)
11551 continue;
11552
11553 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_defaults: %s", attr->name);
11554
11555 if (!strcmp(attr->name, "job-sheets-default"))
11556 {
11557 /*
11558 * Only allow keywords and names...
11559 */
11560
11561 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
11562 continue;
11563
11564 /*
11565 * Only allow job-sheets-default to be set when running without a
11566 * system high classification level...
11567 */
11568
11569 if (Classification)
11570 continue;
11571
11572 cupsdSetString(&printer->job_sheets[0], attr->values[0].string.text);
11573
11574 if (attr->num_values > 1)
11575 cupsdSetString(&printer->job_sheets[1], attr->values[1].string.text);
11576 else
11577 cupsdSetString(&printer->job_sheets[1], "none");
11578 }
11579 else if (!strcmp(attr->name, "requesting-user-name-allowed"))
11580 {
10d09e33 11581 cupsdFreeStrings(&(printer->users));
b423cd4c 11582
11583 printer->deny_users = 0;
11584
11585 if (attr->value_tag == IPP_TAG_NAME &&
11586 (attr->num_values > 1 ||
11587 strcmp(attr->values[0].string.text, "all")))
11588 {
11589 for (i = 0; i < attr->num_values; i ++)
10d09e33 11590 cupsdAddString(&(printer->users), attr->values[i].string.text);
b423cd4c 11591 }
11592 }
11593 else if (!strcmp(attr->name, "requesting-user-name-denied"))
11594 {
10d09e33 11595 cupsdFreeStrings(&(printer->users));
b423cd4c 11596
11597 printer->deny_users = 1;
11598
11599 if (attr->value_tag == IPP_TAG_NAME &&
11600 (attr->num_values > 1 ||
11601 strcmp(attr->values[0].string.text, "none")))
11602 {
11603 for (i = 0; i < attr->num_values; i ++)
10d09e33 11604 cupsdAddString(&(printer->users), attr->values[i].string.text);
b423cd4c 11605 }
11606 }
11607 else if (!strcmp(attr->name, "job-quota-period"))
11608 {
11609 if (attr->value_tag != IPP_TAG_INTEGER)
11610 continue;
11611
11612 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-quota-period to %d...",
11613 attr->values[0].integer);
11614 cupsdFreeQuotas(printer);
11615
11616 printer->quota_period = attr->values[0].integer;
11617 }
11618 else if (!strcmp(attr->name, "job-k-limit"))
11619 {
11620 if (attr->value_tag != IPP_TAG_INTEGER)
11621 continue;
11622
11623 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-k-limit to %d...",
11624 attr->values[0].integer);
11625 cupsdFreeQuotas(printer);
11626
11627 printer->k_limit = attr->values[0].integer;
11628 }
11629 else if (!strcmp(attr->name, "job-page-limit"))
11630 {
11631 if (attr->value_tag != IPP_TAG_INTEGER)
11632 continue;
11633
11634 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-page-limit to %d...",
11635 attr->values[0].integer);
11636 cupsdFreeQuotas(printer);
11637
11638 printer->page_limit = attr->values[0].integer;
11639 }
11640 else if (!strcmp(attr->name, "printer-op-policy"))
11641 {
11642 cupsd_policy_t *p; /* Policy */
11643
11644
11645 if (attr->value_tag != IPP_TAG_NAME)
11646 continue;
11647
11648 if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
11649 {
11650 cupsdLogMessage(CUPSD_LOG_DEBUG,
11651 "Setting printer-op-policy to \"%s\"...",
11652 attr->values[0].string.text);
11653 cupsdSetString(&printer->op_policy, attr->values[0].string.text);
11654 printer->op_policy_ptr = p;
11655 }
11656 else
11657 {
11658 send_ipp_status(con, IPP_NOT_POSSIBLE,
11659 _("Unknown printer-op-policy \"%s\"."),
11660 attr->values[0].string.text);
11661 return;
11662 }
11663 }
11664 else if (!strcmp(attr->name, "printer-error-policy"))
11665 {
11666 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
11667 continue;
11668
f11a948a
MS
11669 if (strcmp(attr->values[0].string.text, "retry-current-job") &&
11670 ((printer->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_CLASS)) ||
11671 (strcmp(attr->values[0].string.text, "abort-job") &&
11672 strcmp(attr->values[0].string.text, "retry-job") &&
11673 strcmp(attr->values[0].string.text, "stop-printer"))))
b423cd4c 11674 {
11675 send_ipp_status(con, IPP_NOT_POSSIBLE,
11676 _("Unknown printer-error-policy \"%s\"."),
11677 attr->values[0].string.text);
11678 return;
11679 }
11680
11681 cupsdLogMessage(CUPSD_LOG_DEBUG,
11682 "Setting printer-error-policy to \"%s\"...",
11683 attr->values[0].string.text);
11684 cupsdSetString(&printer->error_policy, attr->values[0].string.text);
11685 }
b423cd4c 11686
11687 /*
11688 * Skip any other non-default attributes...
11689 */
11690
11691 namelen = strlen(attr->name);
11692 if (namelen < 9 || strcmp(attr->name + namelen - 8, "-default") ||
11693 namelen > (sizeof(name) - 1) || attr->num_values != 1)
11694 continue;
11695
11696 /*
11697 * OK, anything else must be a user-defined default...
11698 */
11699
11700 strlcpy(name, attr->name, sizeof(name));
11701 name[namelen - 8] = '\0'; /* Strip "-default" */
11702
11703 switch (attr->value_tag)
11704 {
11705 case IPP_TAG_DELETEATTR :
11706 printer->num_options = cupsRemoveOption(name,
11707 printer->num_options,
11708 &(printer->options));
11709 cupsdLogMessage(CUPSD_LOG_DEBUG,
11710 "Deleting %s", attr->name);
11711 break;
11712
11713 case IPP_TAG_NAME :
11714 case IPP_TAG_KEYWORD :
11715 case IPP_TAG_URI :
11716 printer->num_options = cupsAddOption(name,
11717 attr->values[0].string.text,
11718 printer->num_options,
11719 &(printer->options));
11720 cupsdLogMessage(CUPSD_LOG_DEBUG,
11721 "Setting %s to \"%s\"...", attr->name,
11722 attr->values[0].string.text);
11723 break;
11724
11725 case IPP_TAG_BOOLEAN :
11726 printer->num_options = cupsAddOption(name,
11727 attr->values[0].boolean ?
11728 "true" : "false",
11729 printer->num_options,
11730 &(printer->options));
11731 cupsdLogMessage(CUPSD_LOG_DEBUG,
11732 "Setting %s to %s...", attr->name,
11733 attr->values[0].boolean ? "true" : "false");
11734 break;
11735
11736 case IPP_TAG_INTEGER :
11737 case IPP_TAG_ENUM :
11738 sprintf(value, "%d", attr->values[0].integer);
11739 printer->num_options = cupsAddOption(name, value,
11740 printer->num_options,
11741 &(printer->options));
11742 cupsdLogMessage(CUPSD_LOG_DEBUG,
11743 "Setting %s to %s...", attr->name, value);
11744 break;
11745
11746 case IPP_TAG_RANGE :
11747 sprintf(value, "%d-%d", attr->values[0].range.lower,
11748 attr->values[0].range.upper);
11749 printer->num_options = cupsAddOption(name, value,
11750 printer->num_options,
11751 &(printer->options));
11752 cupsdLogMessage(CUPSD_LOG_DEBUG,
11753 "Setting %s to %s...", attr->name, value);
11754 break;
11755
11756 case IPP_TAG_RESOLUTION :
11757 sprintf(value, "%dx%d%s", attr->values[0].resolution.xres,
11758 attr->values[0].resolution.yres,
11759 attr->values[0].resolution.units == IPP_RES_PER_INCH ?
11760 "dpi" : "dpc");
11761 printer->num_options = cupsAddOption(name, value,
11762 printer->num_options,
11763 &(printer->options));
11764 cupsdLogMessage(CUPSD_LOG_DEBUG,
11765 "Setting %s to %s...", attr->name, value);
11766 break;
11767
11768 default :
11769 /* Do nothing for other values */
11770 break;
11771 }
11772 }
11773}
11774
11775
ef416fc2 11776/*
11777 * 'start_printer()' - Start a printer.
11778 */
11779
11780static void
11781start_printer(cupsd_client_t *con, /* I - Client connection */
11782 ipp_attribute_t *uri) /* I - Printer URI */
11783{
10d09e33 11784 int i; /* Temporary variable */
ef416fc2 11785 http_status_t status; /* Policy status */
bc44d920 11786 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 11787 cupsd_printer_t *printer; /* Printer data */
11788
11789
11790 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", con,
11791 con->http.fd, uri->values[0].string.text);
11792
11793 /*
11794 * Is the destination valid?
11795 */
11796
f7deaa1a 11797 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 11798 {
11799 /*
11800 * Bad URI...
11801 */
11802
11803 send_ipp_status(con, IPP_NOT_FOUND,
11804 _("The printer or class was not found."));
11805 return;
11806 }
11807
11808 /*
11809 * Check policy...
11810 */
11811
11812 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11813 {
f899b121 11814 send_http_error(con, status, printer);
ef416fc2 11815 return;
11816 }
11817
11818 /*
11819 * Start the printer...
11820 */
11821
11822 printer->state_message[0] = '\0';
11823
11824 cupsdStartPrinter(printer, 1);
11825
11826 if (dtype & CUPS_PRINTER_CLASS)
f7deaa1a 11827 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".",
11828 printer->name, get_username(con));
e00b005a 11829 else
f7deaa1a 11830 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".",
11831 printer->name, get_username(con));
ef416fc2 11832
11833 cupsdCheckJobs();
11834
10d09e33
MS
11835 /*
11836 * Check quotas...
11837 */
11838
11839 if ((i = check_quotas(con, printer)) < 0)
11840 {
11841 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
11842 return;
11843 }
11844 else if (i == 0)
11845 {
11846 send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Not allowed to print."));
11847 return;
11848 }
11849
ef416fc2 11850 /*
11851 * Everything was ok, so return OK status...
11852 */
11853
11854 con->response->request.status.status_code = IPP_OK;
11855}
11856
11857
11858/*
11859 * 'stop_printer()' - Stop a printer.
11860 */
11861
11862static void
11863stop_printer(cupsd_client_t *con, /* I - Client connection */
11864 ipp_attribute_t *uri) /* I - Printer URI */
11865{
11866 http_status_t status; /* Policy status */
bc44d920 11867 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 11868 cupsd_printer_t *printer; /* Printer data */
11869 ipp_attribute_t *attr; /* printer-state-message attribute */
11870
11871
11872 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", con,
11873 con->http.fd, uri->values[0].string.text);
11874
11875 /*
11876 * Is the destination valid?
11877 */
11878
f7deaa1a 11879 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 11880 {
11881 /*
11882 * Bad URI...
11883 */
11884
11885 send_ipp_status(con, IPP_NOT_FOUND,
11886 _("The printer or class was not found."));
11887 return;
11888 }
11889
11890 /*
11891 * Check policy...
11892 */
11893
11894 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11895 {
f899b121 11896 send_http_error(con, status, printer);
ef416fc2 11897 return;
11898 }
11899
11900 /*
11901 * Stop the printer...
11902 */
11903
11904 if ((attr = ippFindAttribute(con->request, "printer-state-message",
11905 IPP_TAG_TEXT)) == NULL)
11906 strcpy(printer->state_message, "Paused");
11907 else
11908 {
11909 strlcpy(printer->state_message, attr->values[0].string.text,
11910 sizeof(printer->state_message));
11911 }
11912
11913 cupsdStopPrinter(printer, 1);
11914
11915 if (dtype & CUPS_PRINTER_CLASS)
f7deaa1a 11916 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".",
11917 printer->name, get_username(con));
ef416fc2 11918 else
f7deaa1a 11919 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".",
11920 printer->name, get_username(con));
ef416fc2 11921
11922 /*
11923 * Everything was ok, so return OK status...
11924 */
11925
11926 con->response->request.status.status_code = IPP_OK;
11927}
11928
11929
89d46774 11930/*
11931 * 'url_encode_attr()' - URL-encode a string attribute.
11932 */
11933
11934static void
11935url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */
11936 char *buffer,/* I - String buffer */
11937 int bufsize)/* I - Size of buffer */
11938{
11939 int i; /* Looping var */
11940 char *bufptr, /* Pointer into buffer */
b94498cf 11941 *bufend; /* End of buffer */
89d46774 11942
11943
11944 strlcpy(buffer, attr->name, bufsize);
11945 bufptr = buffer + strlen(buffer);
11946 bufend = buffer + bufsize - 1;
11947
11948 for (i = 0; i < attr->num_values; i ++)
11949 {
11950 if (bufptr >= bufend)
11951 break;
11952
11953 if (i)
11954 *bufptr++ = ',';
11955 else
11956 *bufptr++ = '=';
11957
11958 if (bufptr >= bufend)
11959 break;
11960
11961 *bufptr++ = '\'';
11962
b94498cf 11963 bufptr = url_encode_string(attr->values[i].string.text,
11964 bufptr, bufend - bufptr + 1);
89d46774 11965
11966 if (bufptr >= bufend)
11967 break;
11968
11969 *bufptr++ = '\'';
11970 }
11971
11972 *bufptr = '\0';
11973}
11974
11975
b94498cf 11976/*
11977 * 'url_encode_string()' - URL-encode a string.
11978 */
11979
11980static char * /* O - End of string */
11981url_encode_string(const char *s, /* I - String */
11982 char *buffer, /* I - String buffer */
11983 int bufsize) /* I - Size of buffer */
11984{
11985 char *bufptr, /* Pointer into buffer */
11986 *bufend; /* End of buffer */
11987 static const char *hex = "0123456789ABCDEF";
11988 /* Hex digits */
11989
11990
11991 bufptr = buffer;
11992 bufend = buffer + bufsize - 1;
11993
11994 while (*s && bufptr < bufend)
11995 {
f701418f 11996 if (*s == ' ' || *s == '%' || *s == '+')
b94498cf 11997 {
11998 if (bufptr >= (bufend - 2))
11999 break;
12000
12001 *bufptr++ = '%';
12002 *bufptr++ = hex[(*s >> 4) & 15];
12003 *bufptr++ = hex[*s & 15];
12004
12005 s ++;
12006 }
12007 else if (*s == '\'' || *s == '\\')
12008 {
12009 if (bufptr >= (bufend - 1))
12010 break;
12011
12012 *bufptr++ = '\\';
12013 *bufptr++ = *s++;
12014 }
12015 else
12016 *bufptr++ = *s++;
12017 }
12018
12019 *bufptr = '\0';
12020
12021 return (bufptr);
12022}
12023
12024
ef416fc2 12025/*
12026 * 'user_allowed()' - See if a user is allowed to print to a queue.
12027 */
12028
12029static int /* O - 0 if not allowed, 1 if allowed */
12030user_allowed(cupsd_printer_t *p, /* I - Printer or class */
12031 const char *username) /* I - Username */
12032{
ef416fc2 12033 struct passwd *pw; /* User password data */
5bd77a73 12034 char baseuser[256], /* Base username */
10d09e33
MS
12035 *baseptr, /* Pointer to "@" in base username */
12036 *name; /* Current user name */
ef416fc2 12037
12038
10d09e33 12039 if (cupsArrayCount(p->users) == 0)
ef416fc2 12040 return (1);
12041
12042 if (!strcmp(username, "root"))
12043 return (1);
12044
5bd77a73
MS
12045 if (strchr(username, '@'))
12046 {
12047 /*
12048 * Strip @REALM for username check...
12049 */
12050
12051 strlcpy(baseuser, username, sizeof(baseuser));
12052
12053 if ((baseptr = strchr(baseuser, '@')) != NULL)
12054 *baseptr = '\0';
12055
12056 username = baseuser;
12057 }
12058
ef416fc2 12059 pw = getpwnam(username);
12060 endpwent();
12061
10d09e33
MS
12062 for (name = (char *)cupsArrayFirst(p->users);
12063 name;
12064 name = (char *)cupsArrayNext(p->users))
ef416fc2 12065 {
10d09e33 12066 if (name[0] == '@')
ef416fc2 12067 {
12068 /*
12069 * Check group membership...
12070 */
12071
10d09e33 12072 if (cupsdCheckGroup(username, pw, name + 1))
ef416fc2 12073 break;
12074 }
10d09e33 12075 else if (name[0] == '#')
8922323b
MS
12076 {
12077 /*
12078 * Check UUID...
12079 */
12080
10d09e33 12081 if (cupsdCheckGroup(username, pw, name))
8922323b
MS
12082 break;
12083 }
10d09e33 12084 else if (!strcasecmp(username, name))
ef416fc2 12085 break;
12086 }
12087
10d09e33 12088 return ((name != NULL) != p->deny_users);
ef416fc2 12089}
12090
12091
12092/*
12093 * 'validate_job()' - Validate printer options and destination.
12094 */
12095
12096static void
12097validate_job(cupsd_client_t *con, /* I - Client connection */
12098 ipp_attribute_t *uri) /* I - Printer URI */
12099{
12100 http_status_t status; /* Policy status */
c7017ecc
MS
12101 ipp_attribute_t *attr, /* Current attribute */
12102 *auth_info; /* auth-info attribute */
ef416fc2 12103 ipp_attribute_t *format; /* Document-format attribute */
bc44d920 12104 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 12105 char super[MIME_MAX_SUPER],
12106 /* Supertype of file */
12107 type[MIME_MAX_TYPE];
12108 /* Subtype of file */
12109 cupsd_printer_t *printer; /* Printer */
12110
12111
12112 cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", con,
12113 con->http.fd, uri->values[0].string.text);
12114
12115 /*
12116 * OK, see if the client is sending the document compressed - CUPS
12117 * doesn't support compression yet...
12118 */
12119
fa73b229 12120 if ((attr = ippFindAttribute(con->request, "compression",
c7017ecc 12121 IPP_TAG_KEYWORD)) != NULL)
ef416fc2 12122 {
c7017ecc
MS
12123 if (strcmp(attr->values[0].string.text, "none")
12124#ifdef HAVE_LIBZ
12125 && strcmp(attr->values[0].string.text, "gzip")
12126#endif /* HAVE_LIBZ */
12127 )
12128 {
12129 send_ipp_status(con, IPP_ATTRIBUTES,
12130 _("Unsupported compression \"%s\""),
12131 attr->values[0].string.text);
12132 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
12133 "compression", NULL, attr->values[0].string.text);
12134 return;
12135 }
ef416fc2 12136 }
12137
12138 /*
12139 * Is it a format we support?
12140 */
12141
12142 if ((format = ippFindAttribute(con->request, "document-format",
12143 IPP_TAG_MIMETYPE)) != NULL)
12144 {
bc44d920 12145 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
12146 super, type) != 2)
ef416fc2 12147 {
4d301e69 12148 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\""),
ef416fc2 12149 format->values[0].string.text);
12150 return;
12151 }
12152
fa73b229 12153 if ((strcmp(super, "application") || strcmp(type, "octet-stream")) &&
12154 !mimeType(MimeDatabase, super, type))
ef416fc2 12155 {
12156 cupsdLogMessage(CUPSD_LOG_INFO,
12157 "Hint: Do you have the raw file printing rules enabled?");
12158 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
c7017ecc 12159 _("Unsupported document-format \"%s\""),
ef416fc2 12160 format->values[0].string.text);
12161 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
12162 "document-format", NULL, format->values[0].string.text);
12163 return;
12164 }
12165 }
12166
12167 /*
12168 * Is the destination valid?
12169 */
12170
f7deaa1a 12171 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 12172 {
12173 /*
12174 * Bad URI...
12175 */
12176
12177 send_ipp_status(con, IPP_NOT_FOUND,
12178 _("The printer or class was not found."));
12179 return;
12180 }
12181
12182 /*
12183 * Check policy...
12184 */
12185
c7017ecc
MS
12186 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
12187
ef416fc2 12188 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
12189 {
f899b121 12190 send_http_error(con, status, printer);
ef416fc2 12191 return;
12192 }
c7017ecc
MS
12193 else if (printer->num_auth_info_required == 1 &&
12194 !strcmp(printer->auth_info_required[0], "negotiate") &&
12195 !con->username[0])
12196 {
12197 send_http_error(con, HTTP_UNAUTHORIZED, printer);
12198 return;
12199 }
12200#ifdef HAVE_SSL
12201 else if (auth_info && !con->http.tls &&
12202 !httpAddrLocalhost(con->http.hostaddr))
12203 {
12204 /*
12205 * Require encryption of auth-info over non-local connections...
12206 */
12207
12208 send_http_error(con, HTTP_UPGRADE_REQUIRED, printer);
12209 return;
12210 }
12211#endif /* HAVE_SSL */
ef416fc2 12212
12213 /*
12214 * Everything was ok, so return OK status...
12215 */
12216
12217 con->response->request.status.status_code = IPP_OK;
12218}
12219
12220
12221/*
12222 * 'validate_name()' - Make sure the printer name only contains valid chars.
12223 */
12224
bc44d920 12225static int /* O - 0 if name is no good, 1 if good */
ef416fc2 12226validate_name(const char *name) /* I - Name to check */
12227{
12228 const char *ptr; /* Pointer into name */
12229
12230
12231 /*
12232 * Scan the whole name...
12233 */
12234
12235 for (ptr = name; *ptr; ptr ++)
f7deaa1a 12236 if ((*ptr > 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
ef416fc2 12237 return (0);
12238
12239 /*
12240 * All the characters are good; validate the length, too...
12241 */
12242
12243 return ((ptr - name) < 128);
12244}
12245
12246
12247/*
12248 * 'validate_user()' - Validate the user for the request.
12249 */
12250
12251static int /* O - 1 if permitted, 0 otherwise */
12252validate_user(cupsd_job_t *job, /* I - Job */
12253 cupsd_client_t *con, /* I - Client connection */
12254 const char *owner, /* I - Owner of job/resource */
12255 char *username, /* O - Authenticated username */
12256 int userlen) /* I - Length of username */
12257{
ef416fc2 12258 cupsd_printer_t *printer; /* Printer for job */
12259
12260
12261 cupsdLogMessage(CUPSD_LOG_DEBUG2,
12262 "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, "
12263 "userlen=%d)",
91c84a35
MS
12264 job->id, con ? con->http.fd : 0,
12265 owner ? owner : "(null)", username, userlen);
ef416fc2 12266
12267 /*
12268 * Validate input...
12269 */
12270
12271 if (!con || !owner || !username || userlen <= 0)
12272 return (0);
12273
12274 /*
12275 * Get the best authenticated username that is available.
12276 */
12277
e00b005a 12278 strlcpy(username, get_username(con), userlen);
ef416fc2 12279
12280 /*
12281 * Check the username against the owner...
12282 */
12283
80ca4592 12284 printer = cupsdFindDest(job->dest);
ef416fc2 12285
12286 return (cupsdCheckPolicy(printer ? printer->op_policy_ptr : DefaultPolicyPtr,
12287 con, owner) == HTTP_OK);
12288}
12289
12290
12291/*
b19ccc9e 12292 * End of "$Id: ipp.c 7944 2008-09-16 22:32:42Z mike $".
ef416fc2 12293 */