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