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