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