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