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