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