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