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