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