]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/ipp.c
Merge changes from CUPS 1.5svn-r8916.
[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
b423cd4c 5433#ifdef __APPLE__
5434 if ((!ra || cupsArrayFind(ra, "com.apple.print.recoverable-message")) &&
5435 printer->recoverable)
5436 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
5437 "com.apple.print.recoverable-message", NULL,
5438 printer->recoverable);
5439#endif /* __APPLE__ */
ef416fc2 5440
5a738aea
MS
5441 if (!ra || cupsArrayFind(ra, "marker-change-time"))
5442 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5443 "marker-change-time", printer->marker_time);
5444
dd1abb6b
MS
5445 if (printer->num_printers > 0 &&
5446 (!ra || cupsArrayFind(ra, "member-uris")))
5447 {
5448 ipp_attribute_t *member_uris; /* member-uris attribute */
5449 cupsd_printer_t *p2; /* Printer in class */
5450 ipp_attribute_t *p2_uri; /* printer-uri-supported for class printer */
5451
5452
5453 if ((member_uris = ippAddStrings(con->response, IPP_TAG_PRINTER,
5454 IPP_TAG_URI, "member-uris",
5455 printer->num_printers, NULL,
5456 NULL)) != NULL)
5457 {
5458 for (i = 0; i < printer->num_printers; i ++)
5459 {
5460 p2 = printer->printers[i];
5461
5462 if ((p2_uri = ippFindAttribute(p2->attrs, "printer-uri-supported",
5463 IPP_TAG_URI)) != NULL)
5464 member_uris->values[i].string.text =
426c6a59 5465 _cupsStrRetain(p2_uri->values[0].string.text);
dd1abb6b
MS
5466 else
5467 {
5468 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri,
5469 sizeof(printer_uri), "ipp", NULL, con->servername,
5470 con->serverport,
5471 (p2->type & CUPS_PRINTER_CLASS) ?
5472 "/classes/%s" : "/printers/%s", p2->name);
5473 member_uris->values[i].string.text = _cupsStrAlloc(printer_uri);
5474 }
5475 }
5476 }
5477 }
5478
323c5de1 5479 if (printer->alert && (!ra || cupsArrayFind(ra, "printer-alert")))
5480 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_STRING,
5481 "printer-alert", NULL, printer->alert);
5482
5483 if (printer->alert_description &&
5484 (!ra || cupsArrayFind(ra, "printer-alert-description")))
5485 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
5486 "printer-alert-description", NULL,
5487 printer->alert_description);
5488
b423cd4c 5489 if (!ra || cupsArrayFind(ra, "printer-current-time"))
5490 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
5491 ippTimeToDate(curtime));
ef416fc2 5492
d9bca400
MS
5493#ifdef HAVE_DNSSD
5494 if (!ra || cupsArrayFind(ra, "printer-dns-sd-name"))
5495 {
5496 if (printer->reg_name)
5497 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
5498 "printer-dns-sd-name", NULL, printer->reg_name);
5499 else
5500 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
5501 "printer-dns-sd-name", 0);
5502 }
5503#endif /* HAVE_DNSSD */
5504
b423cd4c 5505 if (!ra || cupsArrayFind(ra, "printer-error-policy"))
5506 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
5507 "printer-error-policy", NULL, printer->error_policy);
ef416fc2 5508
f11a948a
MS
5509 if (!ra || cupsArrayFind(ra, "printer-error-policy-supported"))
5510 {
5511 static const char * const errors[] =/* printer-error-policy-supported values */
5512 {
5513 "abort-job",
5514 "retry-current-job",
5515 "retry-job",
5516 "stop-printer"
5517 };
5518
5519 if (printer->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_CLASS))
5520 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
5521 "printer-error-policy-supported", NULL, "retry-current-job");
5522 else
5523 ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
5524 "printer-error-policy-supported",
5525 sizeof(errors) / sizeof(errors[0]), NULL, errors);
5526 }
5527
b423cd4c 5528 if (!ra || cupsArrayFind(ra, "printer-is-accepting-jobs"))
5529 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
5530 printer->accepting);
ef416fc2 5531
b423cd4c 5532 if (!ra || cupsArrayFind(ra, "printer-is-shared"))
5533 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-shared",
5534 printer->shared);
ef416fc2 5535
58dc1933
MS
5536 if ((!ra || cupsArrayFind(ra, "printer-more-info")) &&
5537 !(printer->type & CUPS_PRINTER_DISCOVERED))
5538 {
5539 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
5540 "http", NULL, con->servername, con->serverport,
5541 (printer->type & CUPS_PRINTER_CLASS) ?
5542 "/classes/%s" : "/printers/%s", printer->name);
5543 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
5544 "printer-more-info", NULL, printer_uri);
5545 }
5546
b423cd4c 5547 if (!ra || cupsArrayFind(ra, "printer-op-policy"))
5548 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
5549 "printer-op-policy", NULL, printer->op_policy);
5550
5551 if (!ra || cupsArrayFind(ra, "printer-state"))
5552 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
5553 printer->state);
5554
5555 if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
5556 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5557 "printer-state-change-time", printer->state_time);
f7deaa1a 5558
b423cd4c 5559 if (MaxPrinterHistory > 0 && printer->num_history > 0 &&
5560 cupsArrayFind(ra, "printer-state-history"))
ef416fc2 5561 {
5562 /*
b423cd4c 5563 * Printer history is only sent if specifically requested, so that
5564 * older CUPS/IPP clients won't barf on the collection attributes.
ef416fc2 5565 */
5566
b423cd4c 5567 history = ippAddCollections(con->response, IPP_TAG_PRINTER,
5568 "printer-state-history",
5569 printer->num_history, NULL);
5570
5571 for (i = 0; i < printer->num_history; i ++)
5572 copy_attrs(history->values[i].collection = ippNew(), printer->history[i],
5573 NULL, IPP_TAG_ZERO, 0);
ef416fc2 5574 }
5575
b423cd4c 5576 if (!ra || cupsArrayFind(ra, "printer-state-message"))
5577 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
5578 "printer-state-message", NULL, printer->state_message);
ef416fc2 5579
b423cd4c 5580 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
5581 add_printer_state_reasons(con, printer);
ef416fc2 5582
b423cd4c 5583 if (!ra || cupsArrayFind(ra, "printer-type"))
5584 {
5585 int type; /* printer-type value */
ef416fc2 5586
ef416fc2 5587
ef416fc2 5588 /*
b423cd4c 5589 * Add the CUPS-specific printer-type attribute...
ef416fc2 5590 */
5591
09a101d6 5592 type = printer->type;
b423cd4c 5593
5594 if (printer == DefaultPrinter)
5595 type |= CUPS_PRINTER_DEFAULT;
5596
5597 if (!printer->accepting)
5598 type |= CUPS_PRINTER_REJECTING;
5599
5600 if (!printer->shared)
5601 type |= CUPS_PRINTER_NOT_SHARED;
5602
5603 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-type",
5604 type);
ef416fc2 5605 }
ef416fc2 5606
b423cd4c 5607 if (!ra || cupsArrayFind(ra, "printer-up-time"))
5608 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5609 "printer-up-time", curtime);
ef416fc2 5610
b423cd4c 5611 if ((!ra || cupsArrayFind(ra, "printer-uri-supported")) &&
58dc1933 5612 !(printer->type & CUPS_PRINTER_DISCOVERED))
ef416fc2 5613 {
b423cd4c 5614 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
5615 "ipp", NULL, con->servername, con->serverport,
8ca02f3c 5616 (printer->type & CUPS_PRINTER_CLASS) ?
5617 "/classes/%s" : "/printers/%s", printer->name);
b423cd4c 5618 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
5619 "printer-uri-supported", NULL, printer_uri);
5620 cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-uri-supported=\"%s\"",
5621 printer_uri);
5622 }
ef416fc2 5623
b423cd4c 5624 if (!ra || cupsArrayFind(ra, "queued-job-count"))
5625 add_queued_job_count(con, printer);
ef416fc2 5626
b423cd4c 5627 copy_attrs(con->response, printer->attrs, ra, IPP_TAG_ZERO, 0);
61cf44e2
MS
5628 if (printer->ppd_attrs)
5629 copy_attrs(con->response, printer->ppd_attrs, ra, IPP_TAG_ZERO, 0);
b423cd4c 5630 copy_attrs(con->response, CommonData, ra, IPP_TAG_ZERO, IPP_TAG_COPY);
5631}
ef416fc2 5632
ef416fc2 5633
b423cd4c 5634/*
5635 * 'copy_subscription_attrs()' - Copy subscription attributes.
5636 */
ef416fc2 5637
b423cd4c 5638static void
5639copy_subscription_attrs(
5640 cupsd_client_t *con, /* I - Client connection */
5641 cupsd_subscription_t *sub, /* I - Subscription */
5642 cups_array_t *ra) /* I - Requested attributes array */
5643{
5644 ipp_attribute_t *attr; /* Current attribute */
5645 char printer_uri[HTTP_MAX_URI];
5646 /* Printer URI */
5647 int count; /* Number of events */
5648 unsigned mask; /* Current event mask */
5649 const char *name; /* Current event name */
ef416fc2 5650
ef416fc2 5651
b423cd4c 5652 /*
5653 * Copy the subscription attributes to the response using the
5654 * requested-attributes attribute that may be provided by the client.
5655 */
ef416fc2 5656
b423cd4c 5657 if (!ra || cupsArrayFind(ra, "notify-events"))
5658 {
5659 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
5660 {
5661 /*
5662 * Simple event list...
5663 */
ef416fc2 5664
b423cd4c 5665 ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
d09495fa 5666 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
b423cd4c 5667 "notify-events", NULL, name);
5668 }
5669 else
5670 {
5671 /*
5672 * Complex event list...
5673 */
ef416fc2 5674
b423cd4c 5675 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
5676 if (sub->mask & mask)
5677 count ++;
ef416fc2 5678
b423cd4c 5679 attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
d09495fa 5680 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
b423cd4c 5681 "notify-events", count, NULL, NULL);
ef416fc2 5682
b423cd4c 5683 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
5684 if (sub->mask & mask)
5685 {
5686 attr->values[count].string.text =
5687 (char *)cupsdEventName((cupsd_eventmask_t)mask);
ef416fc2 5688
b423cd4c 5689 count ++;
5690 }
ef416fc2 5691 }
b423cd4c 5692 }
ef416fc2 5693
b423cd4c 5694 if (sub->job && (!ra || cupsArrayFind(ra, "notify-job-id")))
5695 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5696 "notify-job-id", sub->job->id);
ef416fc2 5697
b423cd4c 5698 if (!sub->job && (!ra || cupsArrayFind(ra, "notify-lease-duration")))
5699 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5700 "notify-lease-duration", sub->lease);
ef416fc2 5701
b423cd4c 5702 if (sub->dest && (!ra || cupsArrayFind(ra, "notify-printer-uri")))
5703 {
5704 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
5705 "ipp", NULL, con->servername, con->serverport,
5706 "/printers/%s", sub->dest->name);
5707 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5708 "notify-printer-uri", NULL, printer_uri);
ef416fc2 5709 }
ef416fc2 5710
b423cd4c 5711 if (sub->recipient && (!ra || cupsArrayFind(ra, "notify-recipient-uri")))
5712 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5713 "notify-recipient-uri", NULL, sub->recipient);
5714 else if (!ra || cupsArrayFind(ra, "notify-pull-method"))
5715 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
5716 "notify-pull-method", NULL, "ippget");
ef416fc2 5717
b423cd4c 5718 if (!ra || cupsArrayFind(ra, "notify-subscriber-user-name"))
5719 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
5720 "notify-subscriber-user-name", NULL, sub->owner);
ef416fc2 5721
b423cd4c 5722 if (!ra || cupsArrayFind(ra, "notify-subscription-id"))
5723 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5724 "notify-subscription-id", sub->id);
ef416fc2 5725
b423cd4c 5726 if (!ra || cupsArrayFind(ra, "notify-time-interval"))
5727 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5728 "notify-time-interval", sub->interval);
ef416fc2 5729
b423cd4c 5730 if (sub->user_data_len > 0 && (!ra || cupsArrayFind(ra, "notify-user-data")))
5731 ippAddOctetString(con->response, IPP_TAG_SUBSCRIPTION, "notify-user-data",
5732 sub->user_data, sub->user_data_len);
5733}
ef416fc2 5734
ef416fc2 5735
b423cd4c 5736/*
5737 * 'create_job()' - Print a file to a printer or class.
5738 */
5739
5740static void
5741create_job(cupsd_client_t *con, /* I - Client connection */
5742 ipp_attribute_t *uri) /* I - Printer URI */
5743{
f7deaa1a 5744 cupsd_printer_t *printer; /* Printer */
5745 cupsd_job_t *job; /* New job */
b423cd4c 5746
ef416fc2 5747
b423cd4c 5748 cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con,
5749 con->http.fd, uri->values[0].string.text);
ef416fc2 5750
f7deaa1a 5751 /*
5752 * Is the destination valid?
5753 */
5754
5755 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
5756 {
5757 /*
5758 * Bad URI...
5759 */
5760
5761 send_ipp_status(con, IPP_NOT_FOUND,
5762 _("The printer or class was not found."));
5763 return;
5764 }
5765
ef416fc2 5766 /*
b423cd4c 5767 * Create the job object...
ef416fc2 5768 */
5769
f7deaa1a 5770 if ((job = add_job(con, printer, NULL)) == NULL)
b423cd4c 5771 return;
ef416fc2 5772
09a101d6 5773 job->pending_timeout = 1;
5774
ef416fc2 5775 /*
5776 * Save and log the job...
5777 */
f7deaa1a 5778
75bd9771
MS
5779 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
5780 job->dest, job->username);
ef416fc2 5781}
5782
5783
5784/*
5785 * 'create_requested_array()' - Create an array for the requested-attributes.
5786 */
5787
5788static cups_array_t * /* O - Array of attributes or NULL */
5789create_requested_array(ipp_t *request) /* I - IPP request */
5790{
5791 int i; /* Looping var */
5792 ipp_attribute_t *requested; /* requested-attributes attribute */
5793 cups_array_t *ra; /* Requested attributes array */
5794 char *value; /* Current value */
5795
5796
5797 /*
5798 * Get the requested-attributes attribute, and return NULL if we don't
5799 * have one...
5800 */
5801
5802 if ((requested = ippFindAttribute(request, "requested-attributes",
5803 IPP_TAG_KEYWORD)) == NULL)
5804 return (NULL);
5805
5806 /*
5807 * If the attribute contains a single "all" keyword, return NULL...
5808 */
5809
5810 if (requested->num_values == 1 &&
5811 !strcmp(requested->values[0].string.text, "all"))
5812 return (NULL);
5813
5814 /*
5815 * Create an array using "strcmp" as the comparison function...
5816 */
5817
5818 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
5819
5820 for (i = 0; i < requested->num_values; i ++)
5821 {
5822 value = requested->values[i].string.text;
5823
5824 if (!strcmp(value, "job-template"))
5825 {
5826 cupsArrayAdd(ra, "copies");
5827 cupsArrayAdd(ra, "copies-default");
5828 cupsArrayAdd(ra, "copies-supported");
5829 cupsArrayAdd(ra, "finishings");
5830 cupsArrayAdd(ra, "finishings-default");
5831 cupsArrayAdd(ra, "finishings-supported");
5832 cupsArrayAdd(ra, "job-hold-until");
5833 cupsArrayAdd(ra, "job-hold-until-default");
5834 cupsArrayAdd(ra, "job-hold-until-supported");
5835 cupsArrayAdd(ra, "job-priority");
5836 cupsArrayAdd(ra, "job-priority-default");
5837 cupsArrayAdd(ra, "job-priority-supported");
5838 cupsArrayAdd(ra, "job-sheets");
5839 cupsArrayAdd(ra, "job-sheets-default");
5840 cupsArrayAdd(ra, "job-sheets-supported");
5841 cupsArrayAdd(ra, "media");
5842 cupsArrayAdd(ra, "media-default");
5843 cupsArrayAdd(ra, "media-supported");
5844 cupsArrayAdd(ra, "multiple-document-handling");
5845 cupsArrayAdd(ra, "multiple-document-handling-default");
5846 cupsArrayAdd(ra, "multiple-document-handling-supported");
5847 cupsArrayAdd(ra, "number-up");
5848 cupsArrayAdd(ra, "number-up-default");
5849 cupsArrayAdd(ra, "number-up-supported");
5850 cupsArrayAdd(ra, "orientation-requested");
5851 cupsArrayAdd(ra, "orientation-requested-default");
5852 cupsArrayAdd(ra, "orientation-requested-supported");
5853 cupsArrayAdd(ra, "page-ranges");
5854 cupsArrayAdd(ra, "page-ranges-supported");
5855 cupsArrayAdd(ra, "printer-resolution");
5856 cupsArrayAdd(ra, "printer-resolution-default");
5857 cupsArrayAdd(ra, "printer-resolution-supported");
5858 cupsArrayAdd(ra, "print-quality");
5859 cupsArrayAdd(ra, "print-quality-default");
5860 cupsArrayAdd(ra, "print-quality-supported");
5861 cupsArrayAdd(ra, "sides");
5862 cupsArrayAdd(ra, "sides-default");
5863 cupsArrayAdd(ra, "sides-supported");
5864 }
5865 else if (!strcmp(value, "job-description"))
5866 {
5867 cupsArrayAdd(ra, "date-time-at-completed");
5868 cupsArrayAdd(ra, "date-time-at-creation");
5869 cupsArrayAdd(ra, "date-time-at-processing");
5870 cupsArrayAdd(ra, "job-detailed-status-message");
5871 cupsArrayAdd(ra, "job-document-access-errors");
5872 cupsArrayAdd(ra, "job-id");
5873 cupsArrayAdd(ra, "job-impressions");
5874 cupsArrayAdd(ra, "job-impressions-completed");
5875 cupsArrayAdd(ra, "job-k-octets");
5876 cupsArrayAdd(ra, "job-k-octets-processed");
9a4f8274 5877 cupsArrayAdd(ra, "job-media-progress");
ef416fc2 5878 cupsArrayAdd(ra, "job-media-sheets");
5879 cupsArrayAdd(ra, "job-media-sheets-completed");
5880 cupsArrayAdd(ra, "job-message-from-operator");
5881 cupsArrayAdd(ra, "job-more-info");
5882 cupsArrayAdd(ra, "job-name");
5883 cupsArrayAdd(ra, "job-originating-user-name");
5884 cupsArrayAdd(ra, "job-printer-up-time");
5885 cupsArrayAdd(ra, "job-printer-uri");
5886 cupsArrayAdd(ra, "job-state");
5887 cupsArrayAdd(ra, "job-state-message");
5888 cupsArrayAdd(ra, "job-state-reasons");
5889 cupsArrayAdd(ra, "job-uri");
5890 cupsArrayAdd(ra, "number-of-documents");
5891 cupsArrayAdd(ra, "number-of-intervening-jobs");
5892 cupsArrayAdd(ra, "output-device-assigned");
5893 cupsArrayAdd(ra, "time-at-completed");
5894 cupsArrayAdd(ra, "time-at-creation");
5895 cupsArrayAdd(ra, "time-at-processing");
5896 }
5897 else if (!strcmp(value, "printer-description"))
5898 {
5899 cupsArrayAdd(ra, "charset-configured");
5900 cupsArrayAdd(ra, "charset-supported");
5901 cupsArrayAdd(ra, "color-supported");
5902 cupsArrayAdd(ra, "compression-supported");
5903 cupsArrayAdd(ra, "document-format-default");
5904 cupsArrayAdd(ra, "document-format-supported");
5905 cupsArrayAdd(ra, "generated-natural-language-supported");
5906 cupsArrayAdd(ra, "ipp-versions-supported");
5907 cupsArrayAdd(ra, "job-impressions-supported");
5908 cupsArrayAdd(ra, "job-k-octets-supported");
5909 cupsArrayAdd(ra, "job-media-sheets-supported");
1f6f3dbc 5910 cupsArrayAdd(ra, "job-settable-attributes-supported");
ef416fc2 5911 cupsArrayAdd(ra, "multiple-document-jobs-supported");
5912 cupsArrayAdd(ra, "multiple-operation-time-out");
5913 cupsArrayAdd(ra, "natural-language-configured");
5914 cupsArrayAdd(ra, "notify-attributes-supported");
5915 cupsArrayAdd(ra, "notify-lease-duration-default");
5916 cupsArrayAdd(ra, "notify-lease-duration-supported");
5917 cupsArrayAdd(ra, "notify-max-events-supported");
f301802f 5918 cupsArrayAdd(ra, "notify-events-default");
5919 cupsArrayAdd(ra, "notify-events-supported");
ef416fc2 5920 cupsArrayAdd(ra, "notify-pull-method-supported");
5921 cupsArrayAdd(ra, "notify-schemes-supported");
5922 cupsArrayAdd(ra, "operations-supported");
5923 cupsArrayAdd(ra, "pages-per-minute");
5924 cupsArrayAdd(ra, "pages-per-minute-color");
5925 cupsArrayAdd(ra, "pdl-override-supported");
323c5de1 5926 cupsArrayAdd(ra, "printer-alert");
5927 cupsArrayAdd(ra, "printer-alert-description");
01ce6322 5928 cupsArrayAdd(ra, "printer-commands");
ef416fc2 5929 cupsArrayAdd(ra, "printer-current-time");
5930 cupsArrayAdd(ra, "printer-driver-installer");
01ce6322 5931 cupsArrayAdd(ra, "printer-dns-sd-name");
ef416fc2 5932 cupsArrayAdd(ra, "printer-info");
5933 cupsArrayAdd(ra, "printer-is-accepting-jobs");
5934 cupsArrayAdd(ra, "printer-location");
5935 cupsArrayAdd(ra, "printer-make-and-model");
5936 cupsArrayAdd(ra, "printer-message-from-operator");
5937 cupsArrayAdd(ra, "printer-more-info");
5938 cupsArrayAdd(ra, "printer-more-info-manufacturer");
5939 cupsArrayAdd(ra, "printer-name");
5940 cupsArrayAdd(ra, "printer-state");
5941 cupsArrayAdd(ra, "printer-state-message");
5942 cupsArrayAdd(ra, "printer-state-reasons");
1f6f3dbc 5943 cupsArrayAdd(ra, "printer-settable-attributes-supported");
01ce6322 5944 cupsArrayAdd(ra, "printer-type");
ef416fc2 5945 cupsArrayAdd(ra, "printer-up-time");
5946 cupsArrayAdd(ra, "printer-uri-supported");
5947 cupsArrayAdd(ra, "queued-job-count");
5948 cupsArrayAdd(ra, "reference-uri-schemes-supported");
5949 cupsArrayAdd(ra, "uri-authentication-supported");
5950 cupsArrayAdd(ra, "uri-security-supported");
5951 }
f7deaa1a 5952 else if (!strcmp(value, "printer-defaults"))
5953 {
5954 char *name; /* Option name */
5955
5956
5957 for (name = (char *)cupsArrayFirst(CommonDefaults);
5958 name;
5959 name = (char *)cupsArrayNext(CommonDefaults))
5960 cupsArrayAdd(ra, name);
5961 }
ef416fc2 5962 else if (!strcmp(value, "subscription-template"))
5963 {
5964 cupsArrayAdd(ra, "notify-attributes");
5965 cupsArrayAdd(ra, "notify-charset");
5966 cupsArrayAdd(ra, "notify-events");
5967 cupsArrayAdd(ra, "notify-lease-duration");
5968 cupsArrayAdd(ra, "notify-natural-language");
5969 cupsArrayAdd(ra, "notify-pull-method");
5970 cupsArrayAdd(ra, "notify-recipient-uri");
5971 cupsArrayAdd(ra, "notify-time-interval");
5972 cupsArrayAdd(ra, "notify-user-data");
5973 }
5974 else
5975 cupsArrayAdd(ra, value);
5976 }
5977
5978 return (ra);
5979}
5980
5981
5982/*
5983 * 'create_subscription()' - Create a notification subscription.
5984 */
5985
5986static void
5987create_subscription(
5988 cupsd_client_t *con, /* I - Client connection */
5989 ipp_attribute_t *uri) /* I - Printer URI */
5990{
5991 http_status_t status; /* Policy status */
5992 int i; /* Looping var */
5993 ipp_attribute_t *attr; /* Current attribute */
bc44d920 5994 cups_ptype_t dtype; /* Destination type (printer/class) */
ed486911 5995 char scheme[HTTP_MAX_URI],
5996 /* Scheme portion of URI */
ef416fc2 5997 userpass[HTTP_MAX_URI],
5998 /* Username portion of URI */
5999 host[HTTP_MAX_URI],
6000 /* Host portion of URI */
6001 resource[HTTP_MAX_URI];
6002 /* Resource portion of URI */
6003 int port; /* Port portion of URI */
6004 cupsd_printer_t *printer; /* Printer/class */
6005 cupsd_job_t *job; /* Job */
6006 int jobid; /* Job ID */
6007 cupsd_subscription_t *sub; /* Subscription object */
bc44d920 6008 const char *username, /* requesting-user-name or
6009 authenticated username */
ef416fc2 6010 *recipient, /* notify-recipient-uri */
6011 *pullmethod; /* notify-pull-method */
6012 ipp_attribute_t *user_data; /* notify-user-data */
6013 int interval, /* notify-time-interval */
6014 lease; /* notify-lease-duration */
6015 unsigned mask; /* notify-events */
f7deaa1a 6016 ipp_attribute_t *notify_events,/* notify-events(-default) */
6017 *notify_lease; /* notify-lease-duration(-default) */
ef416fc2 6018
6019
bd7854cb 6020#ifdef DEBUG
6021 for (attr = con->request->attrs; attr; attr = attr->next)
6022 {
6023 if (attr->group_tag != IPP_TAG_ZERO)
3dfe78b3 6024 cupsdLogMessage(CUPSD_LOG_DEBUG2, "g%04x v%04x %s", attr->group_tag,
bd7854cb 6025 attr->value_tag, attr->name);
6026 else
3dfe78b3 6027 cupsdLogMessage(CUPSD_LOG_DEBUG2, "----SEP----");
bd7854cb 6028 }
6029#endif /* DEBUG */
6030
ef416fc2 6031 /*
6032 * Is the destination valid?
6033 */
6034
fa73b229 6035 cupsdLogMessage(CUPSD_LOG_DEBUG,
6036 "cupsdCreateSubscription(con=%p(%d), uri=\"%s\")",
6037 con, con->http.fd, uri->values[0].string.text);
6038
ed486911 6039 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6040 sizeof(scheme), userpass, sizeof(userpass), host,
a4d04587 6041 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 6042
6043 if (!strcmp(resource, "/"))
6044 {
ef416fc2 6045 dtype = (cups_ptype_t)0;
6046 printer = NULL;
6047 }
6048 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
6049 {
ef416fc2 6050 dtype = (cups_ptype_t)0;
6051 printer = NULL;
6052 }
6053 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
6054 {
ef416fc2 6055 dtype = CUPS_PRINTER_CLASS;
6056 printer = NULL;
6057 }
f7deaa1a 6058 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 6059 {
6060 /*
6061 * Bad URI...
6062 */
6063
6064 send_ipp_status(con, IPP_NOT_FOUND,
6065 _("The printer or class was not found."));
6066 return;
6067 }
6068
6069 /*
6070 * Check policy...
6071 */
6072
6073 if (printer)
6074 {
f7deaa1a 6075 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
6076 NULL)) != HTTP_OK)
ef416fc2 6077 {
f899b121 6078 send_http_error(con, status, printer);
ef416fc2 6079 return;
6080 }
6081 }
6082 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6083 {
f899b121 6084 send_http_error(con, status, NULL);
ef416fc2 6085 return;
6086 }
6087
6088 /*
6089 * Get the user that is requesting the subscription...
6090 */
6091
e00b005a 6092 username = get_username(con);
ef416fc2 6093
6094 /*
6095 * Find the first subscription group attribute; return if we have
6096 * none...
6097 */
6098
6099 for (attr = con->request->attrs; attr; attr = attr->next)
6100 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
6101 break;
6102
6103 if (!attr)
6104 {
6105 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 6106 _("No subscription attributes in request"));
ef416fc2 6107 return;
6108 }
6109
6110 /*
6111 * Process the subscription attributes in the request...
6112 */
6113
fa73b229 6114 con->response->request.status.status_code = IPP_BAD_REQUEST;
6115
ef416fc2 6116 while (attr)
6117 {
6118 recipient = NULL;
6119 pullmethod = NULL;
6120 user_data = NULL;
6121 interval = 0;
6122 lease = DefaultLeaseDuration;
6123 jobid = 0;
6124 mask = CUPSD_EVENT_NONE;
6125
f7deaa1a 6126 if (printer)
6127 {
6128 notify_events = ippFindAttribute(printer->attrs, "notify-events-default",
6129 IPP_TAG_KEYWORD);
6130 notify_lease = ippFindAttribute(printer->attrs,
6131 "notify-lease-duration-default",
6132 IPP_TAG_INTEGER);
6133
6134 if (notify_lease)
6135 lease = notify_lease->values[0].integer;
6136 }
6137 else
6138 {
6139 notify_events = NULL;
6140 notify_lease = NULL;
6141 }
6142
ef416fc2 6143 while (attr && attr->group_tag != IPP_TAG_ZERO)
6144 {
7594b224 6145 if (!strcmp(attr->name, "notify-recipient-uri") &&
ef416fc2 6146 attr->value_tag == IPP_TAG_URI)
ed486911 6147 {
6148 /*
6149 * Validate the recipient scheme against the ServerBin/notifier
6150 * directory...
6151 */
6152
6153 char notifier[1024]; /* Notifier filename */
6154
6155
ef416fc2 6156 recipient = attr->values[0].string.text;
ed486911 6157
8ca02f3c 6158 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
6159 scheme, sizeof(scheme), userpass, sizeof(userpass),
6160 host, sizeof(host), &port,
6161 resource, sizeof(resource)) < HTTP_URI_OK)
ed486911 6162 {
6163 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 6164 _("Bad notify-recipient-uri URI \"%s\""), recipient);
ed486911 6165 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6166 "notify-status-code", IPP_URI_SCHEME);
6167 return;
6168 }
6169
6170 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
6171 scheme);
6172 if (access(notifier, X_OK))
6173 {
6174 send_ipp_status(con, IPP_NOT_POSSIBLE,
bc44d920 6175 _("notify-recipient-uri URI \"%s\" uses unknown "
4d301e69 6176 "scheme"), recipient);
ed486911 6177 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6178 "notify-status-code", IPP_URI_SCHEME);
6179 return;
6180 }
3dfe78b3
MS
6181
6182 if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
6183 {
6184 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 6185 _("notify-recipient-uri URI \"%s\" is already used"),
3dfe78b3
MS
6186 recipient);
6187 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6188 "notify-status-code", IPP_ATTRIBUTES);
6189 return;
6190 }
ed486911 6191 }
ef416fc2 6192 else if (!strcmp(attr->name, "notify-pull-method") &&
6193 attr->value_tag == IPP_TAG_KEYWORD)
ed486911 6194 {
ef416fc2 6195 pullmethod = attr->values[0].string.text;
ed486911 6196
6197 if (strcmp(pullmethod, "ippget"))
6198 {
6199 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 6200 _("Bad notify-pull-method \"%s\""), pullmethod);
ed486911 6201 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6202 "notify-status-code", IPP_ATTRIBUTES);
6203 return;
6204 }
6205 }
ef416fc2 6206 else if (!strcmp(attr->name, "notify-charset") &&
6207 attr->value_tag == IPP_TAG_CHARSET &&
6208 strcmp(attr->values[0].string.text, "us-ascii") &&
6209 strcmp(attr->values[0].string.text, "utf-8"))
6210 {
6211 send_ipp_status(con, IPP_CHARSET,
4d301e69 6212 _("Character set \"%s\" not supported"),
ef416fc2 6213 attr->values[0].string.text);
6214 return;
6215 }
6216 else if (!strcmp(attr->name, "notify-natural-language") &&
6217 (attr->value_tag != IPP_TAG_LANGUAGE ||
6218 strcmp(attr->values[0].string.text, DefaultLanguage)))
6219 {
6220 send_ipp_status(con, IPP_CHARSET,
4d301e69 6221 _("Language \"%s\" not supported"),
ef416fc2 6222 attr->values[0].string.text);
6223 return;
6224 }
6225 else if (!strcmp(attr->name, "notify-user-data") &&
6226 attr->value_tag == IPP_TAG_STRING)
6227 {
6228 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
6229 {
6230 send_ipp_status(con, IPP_REQUEST_VALUE,
6231 _("The notify-user-data value is too large "
4d301e69 6232 "(%d > 63 octets)"),
ef416fc2 6233 attr->values[0].unknown.length);
6234 return;
6235 }
6236
6237 user_data = attr;
6238 }
6239 else if (!strcmp(attr->name, "notify-events") &&
6240 attr->value_tag == IPP_TAG_KEYWORD)
f7deaa1a 6241 notify_events = attr;
ef416fc2 6242 else if (!strcmp(attr->name, "notify-lease-duration") &&
6243 attr->value_tag == IPP_TAG_INTEGER)
6244 lease = attr->values[0].integer;
6245 else if (!strcmp(attr->name, "notify-time-interval") &&
6246 attr->value_tag == IPP_TAG_INTEGER)
6247 interval = attr->values[0].integer;
6248 else if (!strcmp(attr->name, "notify-job-id") &&
6249 attr->value_tag == IPP_TAG_INTEGER)
6250 jobid = attr->values[0].integer;
6251
6252 attr = attr->next;
6253 }
6254
f7deaa1a 6255 if (notify_events)
6256 {
6257 for (i = 0; i < notify_events->num_values; i ++)
6258 mask |= cupsdEventValue(notify_events->values[i].string.text);
6259 }
6260
bd7854cb 6261 if (recipient)
6262 cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
6263 if (pullmethod)
6264 cupsdLogMessage(CUPSD_LOG_DEBUG, "pullmethod=\"%s\"", pullmethod);
6265 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-lease-duration=%d", lease);
6266 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-time-interval=%d", interval);
fa73b229 6267
ef416fc2 6268 if (!recipient && !pullmethod)
6269 break;
6270
6271 if (mask == CUPSD_EVENT_NONE)
6272 {
6273 if (jobid)
6274 mask = CUPSD_EVENT_JOB_COMPLETED;
6275 else if (printer)
6276 mask = CUPSD_EVENT_PRINTER_STATE_CHANGED;
6277 else
6278 {
6279 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 6280 _("notify-events not specified"));
ef416fc2 6281 return;
6282 }
6283 }
6284
bd7854cb 6285 if (MaxLeaseDuration && (lease == 0 || lease > MaxLeaseDuration))
ef416fc2 6286 {
6287 cupsdLogMessage(CUPSD_LOG_INFO,
6288 "create_subscription: Limiting notify-lease-duration to "
6289 "%d seconds.",
6290 MaxLeaseDuration);
6291 lease = MaxLeaseDuration;
6292 }
6293
6294 if (jobid)
6295 {
6296 if ((job = cupsdFindJob(jobid)) == NULL)
6297 {
4d301e69 6298 send_ipp_status(con, IPP_NOT_FOUND, _("Job %d not found"), jobid);
ef416fc2 6299 return;
6300 }
6301 }
6302 else
6303 job = NULL;
6304
52f6f666
MS
6305 if ((sub = cupsdAddSubscription(mask, printer, job, recipient, 0)) == NULL)
6306 {
6307 send_ipp_status(con, IPP_TOO_MANY_SUBSCRIPTIONS,
6308 _("There are too many subscriptions."));
6309 return;
6310 }
ef416fc2 6311
fa73b229 6312 if (job)
6313 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d",
6314 sub->id, job->id);
6315 else if (printer)
6316 cupsdLogMessage(CUPSD_LOG_DEBUG,
6317 "Added subscription %d for printer \"%s\"",
6318 sub->id, printer->name);
6319 else
6320 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for server",
6321 sub->id);
6322
ef416fc2 6323 sub->interval = interval;
6324 sub->lease = lease;
bd7854cb 6325 sub->expire = lease ? time(NULL) + lease : 0;
ef416fc2 6326
6327 cupsdSetString(&sub->owner, username);
6328
6329 if (user_data)
6330 {
6331 sub->user_data_len = user_data->values[0].unknown.length;
6332 memcpy(sub->user_data, user_data->values[0].unknown.data,
6333 sub->user_data_len);
6334 }
6335
6336 ippAddSeparator(con->response);
6337 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
6338 "notify-subscription-id", sub->id);
6339
fa73b229 6340 con->response->request.status.status_code = IPP_OK;
6341
ef416fc2 6342 if (attr)
6343 attr = attr->next;
6344 }
6345
3dfe78b3 6346 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
ef416fc2 6347}
6348
6349
6350/*
6351 * 'delete_printer()' - Remove a printer or class from the system.
6352 */
6353
6354static void
6355delete_printer(cupsd_client_t *con, /* I - Client connection */
6356 ipp_attribute_t *uri) /* I - URI of printer or class */
6357{
6358 http_status_t status; /* Policy status */
bc44d920 6359 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 6360 cupsd_printer_t *printer; /* Printer/class */
6361 char filename[1024]; /* Script/PPD filename */
6362
6363
6364 cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", con,
6365 con->http.fd, uri->values[0].string.text);
6366
6367 /*
6368 * Do we have a valid URI?
6369 */
6370
f7deaa1a 6371 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 6372 {
6373 /*
6374 * Bad URI...
6375 */
6376
6377 send_ipp_status(con, IPP_NOT_FOUND,
6378 _("The printer or class was not found."));
6379 return;
6380 }
6381
6382 /*
6383 * Check policy...
6384 */
6385
6386 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6387 {
f899b121 6388 send_http_error(con, status, NULL);
ef416fc2 6389 return;
6390 }
6391
6392 /*
6393 * Remove old jobs...
6394 */
6395
f7deaa1a 6396 cupsdCancelJobs(printer->name, NULL, 1);
ef416fc2 6397
6398 /*
6399 * Remove old subscriptions and send a "deleted printer" event...
6400 */
6401
6402 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
6403 "%s \"%s\" deleted by \"%s\".",
6404 (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
f7deaa1a 6405 printer->name, get_username(con));
ef416fc2 6406
6407 cupsdExpireSubscriptions(printer, NULL);
f7deaa1a 6408
ef416fc2 6409 /*
6410 * Remove any old PPD or script files...
6411 */
6412
f7deaa1a 6413 snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot,
6414 printer->name);
ef416fc2 6415 unlink(filename);
6416
f7deaa1a 6417 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
6418 printer->name);
ef416fc2 6419 unlink(filename);
6420
e4572d57 6421 snprintf(filename, sizeof(filename), "%s/%s.ipp", CacheDir, printer->name);
61cf44e2
MS
6422 unlink(filename);
6423
568fa3fa
MS
6424#ifdef __APPLE__
6425 /*
6426 * Unregister color profiles...
6427 */
6428
6429 apple_unregister_profiles(printer);
6430#endif /* __APPLE__ */
6431
ef416fc2 6432 if (dtype & CUPS_PRINTER_CLASS)
6433 {
f7deaa1a 6434 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".",
6435 printer->name, get_username(con));
ef416fc2 6436
6437 cupsdDeletePrinter(printer, 0);
3dfe78b3 6438 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
ef416fc2 6439 }
6440 else
6441 {
f7deaa1a 6442 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".",
6443 printer->name, get_username(con));
ef416fc2 6444
6445 cupsdDeletePrinter(printer, 0);
3dfe78b3 6446 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 6447 }
6448
3dfe78b3 6449 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
ef416fc2 6450
6451 /*
6452 * Return with no errors...
6453 */
6454
6455 con->response->request.status.status_code = IPP_OK;
6456}
6457
6458
6459/*
6460 * 'get_default()' - Get the default destination.
6461 */
6462
6463static void
6464get_default(cupsd_client_t *con) /* I - Client connection */
6465{
fa73b229 6466 http_status_t status; /* Policy status */
6467 cups_array_t *ra; /* Requested attributes array */
ef416fc2 6468
6469
6470 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->http.fd);
6471
6472 /*
6473 * Check policy...
6474 */
6475
6476 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6477 {
f899b121 6478 send_http_error(con, status, NULL);
ef416fc2 6479 return;
6480 }
6481
fa73b229 6482 if (DefaultPrinter)
ef416fc2 6483 {
fa73b229 6484 ra = create_requested_array(con->request);
ef416fc2 6485
fa73b229 6486 copy_printer_attrs(con, DefaultPrinter, ra);
ef416fc2 6487
fa73b229 6488 cupsArrayDelete(ra);
ef416fc2 6489
fa73b229 6490 con->response->request.status.status_code = IPP_OK;
ef416fc2 6491 }
6492 else
6493 send_ipp_status(con, IPP_NOT_FOUND, _("No default printer"));
6494}
6495
6496
6497/*
6498 * 'get_devices()' - Get the list of available devices on the local system.
6499 */
6500
6501static void
6502get_devices(cupsd_client_t *con) /* I - Client connection */
6503{
6504 http_status_t status; /* Policy status */
ae71f5de
MS
6505 ipp_attribute_t *limit, /* limit attribute */
6506 *timeout, /* timeout attribute */
6507 *requested, /* requested-attributes attribute */
ed6e7faf
MS
6508 *exclude, /* exclude-schemes attribute */
6509 *include; /* include-schemes attribute */
ef416fc2 6510 char command[1024], /* cups-deviced command */
ed6e7faf 6511 options[2048], /* Options to pass to command */
ae71f5de 6512 requested_str[256],
89d46774 6513 /* String for requested attributes */
ed6e7faf
MS
6514 exclude_str[512],
6515 /* String for excluded schemes */
6516 include_str[512];
6517 /* String for included schemes */
ef416fc2 6518
6519
6520 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->http.fd);
6521
6522 /*
6523 * Check policy...
6524 */
6525
6526 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6527 {
f899b121 6528 send_http_error(con, status, NULL);
ef416fc2 6529 return;
6530 }
6531
6532 /*
6533 * Run cups-deviced command with the given options...
6534 */
6535
ae71f5de
MS
6536 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
6537 timeout = ippFindAttribute(con->request, "timeout", IPP_TAG_INTEGER);
ef416fc2 6538 requested = ippFindAttribute(con->request, "requested-attributes",
6539 IPP_TAG_KEYWORD);
ae71f5de 6540 exclude = ippFindAttribute(con->request, "exclude-schemes", IPP_TAG_NAME);
ed6e7faf 6541 include = ippFindAttribute(con->request, "include-schemes", IPP_TAG_NAME);
ef416fc2 6542
6543 if (requested)
89d46774 6544 url_encode_attr(requested, requested_str, sizeof(requested_str));
ef416fc2 6545 else
89d46774 6546 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
ef416fc2 6547
ae71f5de
MS
6548 if (exclude)
6549 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
6550 else
6551 exclude_str[0] = '\0';
6552
ed6e7faf
MS
6553 if (include)
6554 url_encode_attr(include, include_str, sizeof(include_str));
6555 else
6556 include_str[0] = '\0';
6557
ef416fc2 6558 snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
6559 snprintf(options, sizeof(options),
ed6e7faf 6560 "%d+%d+%d+%d+%s%s%s%s%s",
ef416fc2 6561 con->request->request.op.request_id,
ae71f5de 6562 limit ? limit->values[0].integer : 0,
7a0cbd5e 6563 timeout ? timeout->values[0].integer : 15,
ae71f5de
MS
6564 (int)User,
6565 requested_str,
ed6e7faf
MS
6566 exclude_str[0] ? "%20" : "", exclude_str,
6567 include_str[0] ? "%20" : "", include_str);
ef416fc2 6568
6569 if (cupsdSendCommand(con, command, options, 1))
6570 {
6571 /*
6572 * Command started successfully, don't send an IPP response here...
6573 */
6574
6575 ippDelete(con->response);
6576 con->response = NULL;
6577 }
6578 else
6579 {
6580 /*
6581 * Command failed, return "internal error" so the user knows something
6582 * went wrong...
6583 */
6584
6585 send_ipp_status(con, IPP_INTERNAL_ERROR,
6586 _("cups-deviced failed to execute."));
6587 }
6588}
6589
6590
2e4ff8af
MS
6591/*
6592 * 'get_document()' - Get a copy of a job file.
6593 */
6594
6595static void
6596get_document(cupsd_client_t *con, /* I - Client connection */
6597 ipp_attribute_t *uri) /* I - Job URI */
6598{
6599 http_status_t status; /* Policy status */
6600 ipp_attribute_t *attr; /* Current attribute */
6601 int jobid; /* Job ID */
6602 int docnum; /* Document number */
6603 cupsd_job_t *job; /* Current job */
61cf44e2 6604 char scheme[HTTP_MAX_URI], /* Method portion of URI */
2e4ff8af
MS
6605 username[HTTP_MAX_URI], /* Username portion of URI */
6606 host[HTTP_MAX_URI], /* Host portion of URI */
6607 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6608 int port; /* Port portion of URI */
6609 char filename[1024], /* Filename for document */
6610 format[1024]; /* Format for document */
6611
6612
6613 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", con,
6614 con->http.fd, uri->values[0].string.text);
6615
6616 /*
6617 * See if we have a job URI or a printer URI...
6618 */
6619
6620 if (!strcmp(uri->name, "printer-uri"))
6621 {
6622 /*
6623 * Got a printer URI; see if we also have a job-id attribute...
6624 */
6625
6626 if ((attr = ippFindAttribute(con->request, "job-id",
6627 IPP_TAG_INTEGER)) == NULL)
6628 {
6629 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 6630 _("Got a printer-uri attribute but no job-id"));
2e4ff8af
MS
6631 return;
6632 }
6633
6634 jobid = attr->values[0].integer;
6635 }
6636 else
6637 {
6638 /*
6639 * Got a job URI; parse it to get the job ID...
6640 */
6641
61cf44e2
MS
6642 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6643 sizeof(scheme), username, sizeof(username), host,
2e4ff8af
MS
6644 sizeof(host), &port, resource, sizeof(resource));
6645
6646 if (strncmp(resource, "/jobs/", 6))
6647 {
6648 /*
6649 * Not a valid URI!
6650 */
6651
6652 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 6653 _("Bad job-uri attribute \"%s\""),
2e4ff8af
MS
6654 uri->values[0].string.text);
6655 return;
6656 }
6657
6658 jobid = atoi(resource + 6);
6659 }
6660
6661 /*
6662 * See if the job exists...
6663 */
6664
6665 if ((job = cupsdFindJob(jobid)) == NULL)
6666 {
6667 /*
6668 * Nope - return a "not found" error...
6669 */
6670
4d301e69 6671 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
2e4ff8af
MS
6672 return;
6673 }
6674
6675 /*
6676 * Check policy...
6677 */
6678
6679 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6680 {
6681 send_http_error(con, status, NULL);
6682 return;
6683 }
6684
6685 /*
6686 * Get the document number...
6687 */
6688
6689 if ((attr = ippFindAttribute(con->request, "document-number",
6690 IPP_TAG_INTEGER)) == NULL)
6691 {
6692 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 6693 _("Missing document-number attribute"));
2e4ff8af
MS
6694 return;
6695 }
6696
6697 if ((docnum = attr->values[0].integer) < 1 || docnum > job->num_files ||
6698 attr->num_values > 1)
6699 {
6700 send_ipp_status(con, IPP_NOT_FOUND, _("Document %d not found in job %d."),
6701 docnum, jobid);
6702 return;
6703 }
6704
6705 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, jobid,
6706 docnum);
6707 if ((con->file = open(filename, O_RDONLY)) == -1)
6708 {
6709 cupsdLogMessage(CUPSD_LOG_ERROR,
6710 "Unable to open document %d in job %d - %s", docnum, jobid,
6711 strerror(errno));
6712 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 6713 _("Unable to open document %d in job %d"), docnum, jobid);
2e4ff8af
MS
6714 return;
6715 }
6716
6717 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
6718
6719 cupsdLoadJob(job);
6720
6721 snprintf(format, sizeof(format), "%s/%s", job->filetypes[docnum - 1]->super,
6722 job->filetypes[docnum - 1]->type);
6723
6724 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
6725 NULL, format);
6726 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "document-number",
6727 docnum);
6728 if ((attr = ippFindAttribute(job->attrs, "document-name",
6729 IPP_TAG_NAME)) != NULL)
6730 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "document-name",
6731 NULL, attr->values[0].string.text);
6732}
6733
6734
ef416fc2 6735/*
fa73b229 6736 * 'get_job_attrs()' - Get job attributes.
ef416fc2 6737 */
6738
6739static void
fa73b229 6740get_job_attrs(cupsd_client_t *con, /* I - Client connection */
6741 ipp_attribute_t *uri) /* I - Job URI */
ef416fc2 6742{
6743 http_status_t status; /* Policy status */
fa73b229 6744 ipp_attribute_t *attr; /* Current attribute */
6745 int jobid; /* Job ID */
6746 cupsd_job_t *job; /* Current job */
ef55b745 6747 cupsd_printer_t *printer; /* Current printer */
61cf44e2 6748 char scheme[HTTP_MAX_URI], /* Method portion of URI */
ef416fc2 6749 username[HTTP_MAX_URI], /* Username portion of URI */
6750 host[HTTP_MAX_URI], /* Host portion of URI */
6751 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6752 int port; /* Port portion of URI */
fa73b229 6753 cups_array_t *ra; /* Requested attributes array */
ef416fc2 6754
6755
fa73b229 6756 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", con,
6757 con->http.fd, uri->values[0].string.text);
ef416fc2 6758
6759 /*
fa73b229 6760 * See if we have a job URI or a printer URI...
6761 */
6762
6763 if (!strcmp(uri->name, "printer-uri"))
6764 {
6765 /*
6766 * Got a printer URI; see if we also have a job-id attribute...
6767 */
6768
6769 if ((attr = ippFindAttribute(con->request, "job-id",
6770 IPP_TAG_INTEGER)) == NULL)
6771 {
6772 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 6773 _("Got a printer-uri attribute but no job-id"));
fa73b229 6774 return;
6775 }
6776
6777 jobid = attr->values[0].integer;
6778 }
6779 else
6780 {
6781 /*
6782 * Got a job URI; parse it to get the job ID...
6783 */
6784
61cf44e2
MS
6785 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6786 sizeof(scheme), username, sizeof(username), host,
a4d04587 6787 sizeof(host), &port, resource, sizeof(resource));
fa73b229 6788
6789 if (strncmp(resource, "/jobs/", 6))
6790 {
6791 /*
6792 * Not a valid URI!
6793 */
6794
6795 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 6796 _("Bad job-uri attribute \"%s\""),
fa73b229 6797 uri->values[0].string.text);
6798 return;
6799 }
6800
6801 jobid = atoi(resource + 6);
6802 }
6803
6804 /*
6805 * See if the job exists...
6806 */
6807
6808 if ((job = cupsdFindJob(jobid)) == NULL)
6809 {
6810 /*
6811 * Nope - return a "not found" error...
6812 */
6813
4d301e69 6814 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
fa73b229 6815 return;
6816 }
6817
6818 /*
6819 * Check policy...
6820 */
6821
ef55b745
MS
6822 if ((printer = job->printer) == NULL)
6823 printer = cupsdFindDest(job->dest);
6824
6825 if (printer)
6826 {
6827 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
6828 NULL)) != HTTP_OK)
6829 {
6830 send_http_error(con, status, printer);
6831 return;
6832 }
6833 }
6834 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
fa73b229 6835 {
f899b121 6836 send_http_error(con, status, NULL);
fa73b229 6837 return;
6838 }
6839
6840 /*
6841 * Copy attributes...
6842 */
6843
bd7854cb 6844 cupsdLoadJob(job);
6845
fa73b229 6846 ra = create_requested_array(con->request);
6847 copy_job_attrs(con, job, ra);
6848 cupsArrayDelete(ra);
6849
6850 con->response->request.status.status_code = IPP_OK;
6851}
6852
6853
6854/*
6855 * 'get_jobs()' - Get a list of jobs for the specified printer.
6856 */
6857
6858static void
6859get_jobs(cupsd_client_t *con, /* I - Client connection */
6860 ipp_attribute_t *uri) /* I - Printer URI */
6861{
6862 http_status_t status; /* Policy status */
6863 ipp_attribute_t *attr; /* Current attribute */
6864 const char *dest; /* Destination */
bc44d920 6865 cups_ptype_t dtype; /* Destination type (printer/class) */
fa73b229 6866 cups_ptype_t dmask; /* Destination type mask */
f7deaa1a 6867 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
fa73b229 6868 username[HTTP_MAX_URI], /* Username portion of URI */
6869 host[HTTP_MAX_URI], /* Host portion of URI */
6870 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6871 int port; /* Port portion of URI */
6872 int completed; /* Completed jobs? */
6873 int first_job_id; /* First job ID */
6874 int limit; /* Maximum number of jobs to return */
6875 int count; /* Number of jobs that match */
6876 cupsd_job_t *job; /* Current job pointer */
6877 cupsd_printer_t *printer; /* Printer */
6878 cups_array_t *list; /* Which job list... */
6879 cups_array_t *ra; /* Requested attributes array */
6880
6881
6882 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", con, con->http.fd,
6883 uri->values[0].string.text);
6884
6885 /*
6886 * Is the destination valid?
ef416fc2 6887 */
6888
4b3f67ff
MS
6889 if (strcmp(uri->name, "printer-uri"))
6890 {
4d301e69 6891 send_ipp_status(con, IPP_BAD_REQUEST, _("No printer-uri in request"));
4b3f67ff
MS
6892 return;
6893 }
6894
f7deaa1a 6895 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6896 sizeof(scheme), username, sizeof(username), host,
a4d04587 6897 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 6898
b9faaae1 6899 if (!strcmp(resource, "/") || !strcmp(resource, "/jobs"))
ef416fc2 6900 {
6901 dest = NULL;
6902 dtype = (cups_ptype_t)0;
6903 dmask = (cups_ptype_t)0;
6904 printer = NULL;
6905 }
fa73b229 6906 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
ef416fc2 6907 {
6908 dest = NULL;
6909 dtype = (cups_ptype_t)0;
6910 dmask = CUPS_PRINTER_CLASS;
6911 printer = NULL;
6912 }
fa73b229 6913 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
ef416fc2 6914 {
6915 dest = NULL;
6916 dtype = CUPS_PRINTER_CLASS;
6917 dmask = CUPS_PRINTER_CLASS;
6918 printer = NULL;
6919 }
f7deaa1a 6920 else if ((dest = cupsdValidateDest(uri->values[0].string.text, &dtype,
6921 &printer)) == NULL)
ef416fc2 6922 {
6923 /*
6924 * Bad URI...
6925 */
6926
6927 send_ipp_status(con, IPP_NOT_FOUND,
6928 _("The printer or class was not found."));
6929 return;
6930 }
6931 else
b86bc4cf 6932 {
6933 dtype &= CUPS_PRINTER_CLASS;
ef416fc2 6934 dmask = CUPS_PRINTER_CLASS;
b86bc4cf 6935 }
ef416fc2 6936
6937 /*
6938 * Check policy...
6939 */
6940
6941 if (printer)
6942 {
f7deaa1a 6943 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
6944 NULL)) != HTTP_OK)
ef416fc2 6945 {
f899b121 6946 send_http_error(con, status, printer);
ef416fc2 6947 return;
6948 }
6949 }
6950 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6951 {
f899b121 6952 send_http_error(con, status, NULL);
ef416fc2 6953 return;
6954 }
6955
6956 /*
6957 * See if the "which-jobs" attribute have been specified...
6958 */
6959
fa73b229 6960 if ((attr = ippFindAttribute(con->request, "which-jobs",
6961 IPP_TAG_KEYWORD)) != NULL &&
ef416fc2 6962 !strcmp(attr->values[0].string.text, "completed"))
6963 {
6964 completed = 1;
6965 list = Jobs;
6966 }
6967 else if (attr && !strcmp(attr->values[0].string.text, "all"))
6968 {
6969 completed = 0;
6970 list = Jobs;
6971 }
d1c13e16 6972 else if (attr && !strcmp(attr->values[0].string.text, "processing"))
3dfe78b3
MS
6973 {
6974 completed = 0;
6975 list = PrintingJobs;
6976 }
ef416fc2 6977 else
6978 {
6979 completed = 0;
6980 list = ActiveJobs;
6981 }
6982
6983 /*
6984 * See if they want to limit the number of jobs reported...
6985 */
6986
fa73b229 6987 if ((attr = ippFindAttribute(con->request, "limit",
6988 IPP_TAG_INTEGER)) != NULL)
ef416fc2 6989 limit = attr->values[0].integer;
6990 else
3dfe78b3 6991 limit = 0;
ef416fc2 6992
6993 if ((attr = ippFindAttribute(con->request, "first-job-id",
6994 IPP_TAG_INTEGER)) != NULL)
6995 first_job_id = attr->values[0].integer;
6996 else
6997 first_job_id = 1;
6998
6999 /*
7000 * See if we only want to see jobs for a specific user...
7001 */
7002
fa73b229 7003 if ((attr = ippFindAttribute(con->request, "my-jobs",
7004 IPP_TAG_BOOLEAN)) != NULL &&
ef416fc2 7005 attr->values[0].boolean)
e00b005a 7006 strlcpy(username, get_username(con), sizeof(username));
ef416fc2 7007 else
7008 username[0] = '\0';
7009
fa73b229 7010 ra = create_requested_array(con->request);
ef416fc2 7011
7012 /*
7013 * OK, build a list of jobs for this printer...
7014 */
7015
7016 for (count = 0, job = (cupsd_job_t *)cupsArrayFirst(list);
3dfe78b3 7017 (limit <= 0 || count < limit) && job;
ef416fc2 7018 job = (cupsd_job_t *)cupsArrayNext(list))
7019 {
7020 /*
7021 * Filter out jobs that don't match...
7022 */
7023
d1c13e16
MS
7024 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7025 "get_jobs: job->id=%d, dest=\"%s\", username=\"%s\", "
7026 "state_value=%d, attrs=%p", job->id, job->dest,
7027 job->username, job->state_value, job->attrs);
ef416fc2 7028
0a682745
MS
7029 if (!job->dest || !job->username)
7030 cupsdLoadJob(job);
7031
7032 if (!job->dest || !job->username)
7033 continue;
7034
fa73b229 7035 if ((dest && strcmp(job->dest, dest)) &&
7036 (!job->printer || !dest || strcmp(job->printer->name, dest)))
ef416fc2 7037 continue;
7038 if ((job->dtype & dmask) != dtype &&
fa73b229 7039 (!job->printer || (job->printer->type & dmask) != dtype))
ef416fc2 7040 continue;
bd7854cb 7041 if (completed && job->state_value <= IPP_JOB_STOPPED)
ef416fc2 7042 continue;
7043
7044 if (job->id < first_job_id)
7045 continue;
7046
bd7854cb 7047 cupsdLoadJob(job);
7048
7049 if (!job->attrs)
d1c13e16 7050 {
4d301e69 7051 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d",
d1c13e16 7052 job->id);
bd7854cb 7053 continue;
d1c13e16 7054 }
bd7854cb 7055
7594b224 7056 if (username[0] && strcasecmp(username, job->username))
7057 continue;
7058
fa73b229 7059 if (count > 0)
7060 ippAddSeparator(con->response);
7061
ef416fc2 7062 count ++;
7063
fa73b229 7064 copy_job_attrs(con, job, ra);
ef416fc2 7065 }
7066
d1c13e16
MS
7067 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count=%d", count);
7068
fa73b229 7069 cupsArrayDelete(ra);
ef416fc2 7070
fa73b229 7071 con->response->request.status.status_code = IPP_OK;
ef416fc2 7072}
7073
7074
7075/*
7076 * 'get_notifications()' - Get events for a subscription.
7077 */
7078
7079static void
bd7854cb 7080get_notifications(cupsd_client_t *con) /* I - Client connection */
ef416fc2 7081{
bd7854cb 7082 int i, j; /* Looping vars */
7083 http_status_t status; /* Policy status */
7084 cupsd_subscription_t *sub; /* Subscription */
7085 ipp_attribute_t *ids, /* notify-subscription-ids */
7086 *sequences; /* notify-sequence-numbers */
7087 int min_seq; /* Minimum sequence number */
7088 int interval; /* Poll interval */
7089
7090
f7deaa1a 7091 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_notifications(con=%p[%d])",
bd7854cb 7092 con, con->http.fd);
7093
7094 /*
7095 * Get subscription attributes...
7096 */
7097
7098 ids = ippFindAttribute(con->request, "notify-subscription-ids",
7099 IPP_TAG_INTEGER);
7100 sequences = ippFindAttribute(con->request, "notify-sequence-numbers",
7101 IPP_TAG_INTEGER);
7102
7103 if (!ids)
7104 {
7105 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 7106 _("Missing notify-subscription-ids attribute"));
bd7854cb 7107 return;
7108 }
7109
7110 /*
7111 * Are the subscription IDs valid?
7112 */
7113
7114 for (i = 0, interval = 60; i < ids->num_values; i ++)
7115 {
7116 if ((sub = cupsdFindSubscription(ids->values[i].integer)) == NULL)
7117 {
7118 /*
7119 * Bad subscription ID...
7120 */
7121
7122 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 7123 _("notify-subscription-id %d no good"),
bd7854cb 7124 ids->values[i].integer);
7125 return;
7126 }
7127
7128 /*
7129 * Check policy...
7130 */
7131
7132 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
7133 DefaultPolicyPtr,
7134 con, sub->owner)) != HTTP_OK)
7135 {
f899b121 7136 send_http_error(con, status, sub->dest);
bd7854cb 7137 return;
7138 }
7139
7140 /*
7141 * Check the subscription type and update the interval accordingly.
7142 */
7143
7144 if (sub->job && sub->job->state_value == IPP_JOB_PROCESSING &&
7145 interval > 10)
7146 interval = 10;
7147 else if (sub->job && sub->job->state_value >= IPP_JOB_STOPPED)
7148 interval = 0;
7149 else if (sub->dest && sub->dest->state == IPP_PRINTER_PROCESSING &&
7150 interval > 30)
7151 interval = 30;
7152 }
7153
7154 /*
7155 * Tell the client to poll again in N seconds...
7156 */
7157
7158 if (interval > 0)
7159 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
7160 "notify-get-interval", interval);
7161
7162 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
7163 "printer-up-time", time(NULL));
7164
7165 /*
7166 * Copy the subscription event attributes to the response.
7167 */
7168
7169 con->response->request.status.status_code =
7170 interval ? IPP_OK : IPP_OK_EVENTS_COMPLETE;
7171
7172 for (i = 0; i < ids->num_values; i ++)
7173 {
7174 /*
7175 * Get the subscription and sequence number...
7176 */
7177
7178 sub = cupsdFindSubscription(ids->values[i].integer);
7179
7180 if (sequences && i < sequences->num_values)
7181 min_seq = sequences->values[i].integer;
7182 else
7183 min_seq = 1;
7184
7185 /*
7186 * If we don't have any new events, nothing to do here...
7187 */
7188
7189 if (min_seq > (sub->first_event_id + sub->num_events))
7190 continue;
7191
7192 /*
7193 * Otherwise copy all of the new events...
7194 */
7195
7196 if (sub->first_event_id > min_seq)
7197 j = 0;
7198 else
7199 j = min_seq - sub->first_event_id;
7200
b423cd4c 7201 for (; j < sub->num_events; j ++)
7202 {
7203 ippAddSeparator(con->response);
ef416fc2 7204
b423cd4c 7205 copy_attrs(con->response, sub->events[j]->attrs, NULL,
7206 IPP_TAG_EVENT_NOTIFICATION, 0);
7207 }
7208 }
7209}
ef416fc2 7210
ef416fc2 7211
b94498cf 7212/*
7213 * 'get_ppd()' - Get a named PPD from the local system.
7214 */
7215
7216static void
7217get_ppd(cupsd_client_t *con, /* I - Client connection */
7218 ipp_attribute_t *uri) /* I - Printer URI or PPD name */
7219{
7220 http_status_t status; /* Policy status */
7221 cupsd_printer_t *dest; /* Destination */
7222 cups_ptype_t dtype; /* Destination type */
7223
7224
7225 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppd(%p[%d], %p[%s=%s])", con,
7226 con->http.fd, uri, uri->name, uri->values[0].string.text);
7227
7228 if (!strcmp(uri->name, "ppd-name"))
7229 {
7230 /*
7231 * Return a PPD file from cups-driverd...
7232 */
7233
7234 char command[1024], /* cups-driverd command */
7235 options[1024], /* Options to pass to command */
7236 ppd_name[1024]; /* ppd-name */
7237
7238
7239 /*
7240 * Check policy...
7241 */
7242
7243 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7244 {
7245 send_http_error(con, status, NULL);
7246 return;
7247 }
7248
7249 /*
7250 * Run cups-driverd command with the given options...
7251 */
7252
7253 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
7254 url_encode_string(uri->values[0].string.text, ppd_name, sizeof(ppd_name));
7255 snprintf(options, sizeof(options), "get+%d+%s",
7256 con->request->request.op.request_id, ppd_name);
7257
7258 if (cupsdSendCommand(con, command, options, 0))
7259 {
7260 /*
7261 * Command started successfully, don't send an IPP response here...
7262 */
7263
7264 ippDelete(con->response);
7265 con->response = NULL;
7266 }
7267 else
7268 {
7269 /*
7270 * Command failed, return "internal error" so the user knows something
7271 * went wrong...
7272 */
7273
7274 send_ipp_status(con, IPP_INTERNAL_ERROR,
7275 _("cups-driverd failed to execute."));
7276 }
7277 }
7278 else if (!strcmp(uri->name, "printer-uri") &&
7279 cupsdValidateDest(uri->values[0].string.text, &dtype, &dest))
7280 {
7281 int i; /* Looping var */
7282 char filename[1024]; /* PPD filename */
7283
7284
7285 /*
7286 * Check policy...
7287 */
7288
7289 if ((status = cupsdCheckPolicy(dest->op_policy_ptr, con, NULL)) != HTTP_OK)
7290 {
2fb76298 7291 send_http_error(con, status, dest);
b94498cf 7292 return;
7293 }
7294
7295 /*
7296 * See if we need the PPD for a class or remote printer...
7297 */
7298
bc44d920 7299 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
7300 dest->name);
7301
7302 if ((dtype & CUPS_PRINTER_REMOTE) && access(filename, 0))
b94498cf 7303 {
3d8365b8 7304 con->response->request.status.status_code = CUPS_SEE_OTHER;
b94498cf 7305 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
7306 "printer-uri", NULL, dest->uri);
7307 return;
7308 }
7309 else if (dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
7310 {
7311 for (i = 0; i < dest->num_printers; i ++)
7312 if (!(dest->printers[i]->type &
bc44d920 7313 (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)))
7314 {
7315 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
7316 dest->printers[i]->name);
7317
7318 if (!access(filename, 0))
7319 break;
7320 }
b94498cf 7321
7322 if (i < dest->num_printers)
7323 dest = dest->printers[i];
7324 else
7325 {
3d8365b8 7326 con->response->request.status.status_code = CUPS_SEE_OTHER;
b94498cf 7327 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
7328 "printer-uri", NULL, dest->printers[0]->uri);
7329 return;
7330 }
7331 }
7332
7333 /*
7334 * Found the printer with the PPD file, now see if there is one...
7335 */
7336
b94498cf 7337 if ((con->file = open(filename, O_RDONLY)) < 0)
7338 {
7339 send_ipp_status(con, IPP_NOT_FOUND,
7340 _("The PPD file \"%s\" could not be opened: %s"),
3d8365b8 7341 uri->values[0].string.text, strerror(errno));
b94498cf 7342 return;
7343 }
7344
7345 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
7346
7347 con->pipe_pid = 0;
7348
3d8365b8 7349 con->response->request.status.status_code = IPP_OK;
b94498cf 7350 }
7351 else
7352 send_ipp_status(con, IPP_NOT_FOUND,
7353 _("The PPD file \"%s\" could not be found."),
7354 uri->values[0].string.text);
7355}
7356
7357
b423cd4c 7358/*
7359 * 'get_ppds()' - Get the list of PPD files on the local system.
7360 */
ef416fc2 7361
b423cd4c 7362static void
7363get_ppds(cupsd_client_t *con) /* I - Client connection */
7364{
7365 http_status_t status; /* Policy status */
b423cd4c 7366 ipp_attribute_t *limit, /* Limit attribute */
b94498cf 7367 *device, /* ppd-device-id attribute */
7368 *language, /* ppd-natural-language attribute */
b423cd4c 7369 *make, /* ppd-make attribute */
b94498cf 7370 *model, /* ppd-make-and-model attribute */
3d8365b8 7371 *model_number, /* ppd-model-number attribute */
b94498cf 7372 *product, /* ppd-product attribute */
7373 *psversion, /* ppd-psverion attribute */
3d8365b8 7374 *type, /* ppd-type attribute */
ed6e7faf
MS
7375 *requested, /* requested-attributes attribute */
7376 *exclude, /* exclude-schemes attribute */
7377 *include; /* include-schemes attribute */
b94498cf 7378 char command[1024], /* cups-driverd command */
ed6e7faf 7379 options[4096], /* Options to pass to command */
b94498cf 7380 device_str[256],/* Escaped ppd-device-id string */
7381 language_str[256],
bc44d920 7382 /* Escaped ppd-natural-language */
b94498cf 7383 make_str[256], /* Escaped ppd-make string */
7384 model_str[256], /* Escaped ppd-make-and-model string */
3d8365b8 7385 model_number_str[256],
7386 /* ppd-model-number string */
b94498cf 7387 product_str[256],
7388 /* Escaped ppd-product string */
7389 psversion_str[256],
7390 /* Escaped ppd-psversion string */
3d8365b8 7391 type_str[256], /* Escaped ppd-type string */
ed6e7faf 7392 requested_str[256],
89d46774 7393 /* String for requested attributes */
ed6e7faf
MS
7394 exclude_str[512],
7395 /* String for excluded schemes */
7396 include_str[512];
7397 /* String for included schemes */
ef416fc2 7398
ef416fc2 7399
b423cd4c 7400 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->http.fd);
ef416fc2 7401
7402 /*
b423cd4c 7403 * Check policy...
ef416fc2 7404 */
7405
b423cd4c 7406 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
ef416fc2 7407 {
f899b121 7408 send_http_error(con, status, NULL);
b423cd4c 7409 return;
ef416fc2 7410 }
ef416fc2 7411
b423cd4c 7412 /*
7413 * Run cups-driverd command with the given options...
7414 */
7415
3d8365b8 7416 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
7417 device = ippFindAttribute(con->request, "ppd-device-id", IPP_TAG_TEXT);
7418 language = ippFindAttribute(con->request, "ppd-natural-language",
7419 IPP_TAG_LANGUAGE);
7420 make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
7421 model = ippFindAttribute(con->request, "ppd-make-and-model",
7422 IPP_TAG_TEXT);
7423 model_number = ippFindAttribute(con->request, "ppd-model-number",
7424 IPP_TAG_INTEGER);
7425 product = ippFindAttribute(con->request, "ppd-product", IPP_TAG_TEXT);
7426 psversion = ippFindAttribute(con->request, "ppd-psversion", IPP_TAG_TEXT);
7427 type = ippFindAttribute(con->request, "ppd-type", IPP_TAG_KEYWORD);
7428 requested = ippFindAttribute(con->request, "requested-attributes",
7429 IPP_TAG_KEYWORD);
8b450588
MS
7430 exclude = ippFindAttribute(con->request, "exclude-schemes",
7431 IPP_TAG_NAME);
7432 include = ippFindAttribute(con->request, "include-schemes",
7433 IPP_TAG_NAME);
b423cd4c 7434
7435 if (requested)
89d46774 7436 url_encode_attr(requested, requested_str, sizeof(requested_str));
7437 else
7438 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
ef416fc2 7439
b94498cf 7440 if (device)
7441 url_encode_attr(device, device_str, sizeof(device_str));
7442 else
7443 device_str[0] = '\0';
7444
7445 if (language)
7446 url_encode_attr(language, language_str, sizeof(language_str));
7447 else
7448 language_str[0] = '\0';
7449
89d46774 7450 if (make)
7451 url_encode_attr(make, make_str, sizeof(make_str));
b423cd4c 7452 else
89d46774 7453 make_str[0] = '\0';
fa73b229 7454
b94498cf 7455 if (model)
7456 url_encode_attr(model, model_str, sizeof(model_str));
7457 else
7458 model_str[0] = '\0';
7459
3d8365b8 7460 if (model_number)
7461 snprintf(model_number_str, sizeof(model_number_str), "ppd-model-number=%d",
7462 model_number->values[0].integer);
7463 else
7464 model_number_str[0] = '\0';
7465
b94498cf 7466 if (product)
7467 url_encode_attr(product, product_str, sizeof(product_str));
7468 else
7469 product_str[0] = '\0';
7470
7471 if (psversion)
7472 url_encode_attr(psversion, psversion_str, sizeof(psversion_str));
7473 else
7474 psversion_str[0] = '\0';
7475
3d8365b8 7476 if (type)
7477 url_encode_attr(type, type_str, sizeof(type_str));
7478 else
7479 type_str[0] = '\0';
7480
ed6e7faf
MS
7481 if (exclude)
7482 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
7483 else
7484 exclude_str[0] = '\0';
7485
7486 if (include)
7487 url_encode_attr(include, include_str, sizeof(include_str));
7488 else
7489 include_str[0] = '\0';
7490
b423cd4c 7491 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
3d8365b8 7492 snprintf(options, sizeof(options),
ed6e7faf 7493 "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 7494 con->request->request.op.request_id,
7495 limit ? limit->values[0].integer : 0,
b94498cf 7496 requested_str,
7497 device ? "%20" : "", device_str,
7498 language ? "%20" : "", language_str,
7499 make ? "%20" : "", make_str,
7500 model ? "%20" : "", model_str,
3d8365b8 7501 model_number ? "%20" : "", model_number_str,
b94498cf 7502 product ? "%20" : "", product_str,
3d8365b8 7503 psversion ? "%20" : "", psversion_str,
ed6e7faf
MS
7504 type ? "%20" : "", type_str,
7505 exclude_str[0] ? "%20" : "", exclude_str,
7506 include_str[0] ? "%20" : "", include_str);
ef416fc2 7507
b423cd4c 7508 if (cupsdSendCommand(con, command, options, 0))
7509 {
7510 /*
7511 * Command started successfully, don't send an IPP response here...
7512 */
7513
7514 ippDelete(con->response);
7515 con->response = NULL;
7516 }
7517 else
7518 {
7519 /*
7520 * Command failed, return "internal error" so the user knows something
7521 * went wrong...
7522 */
7523
7524 send_ipp_status(con, IPP_INTERNAL_ERROR,
7525 _("cups-driverd failed to execute."));
7526 }
ef416fc2 7527}
7528
7529
7530/*
b423cd4c 7531 * 'get_printer_attrs()' - Get printer attributes.
ef416fc2 7532 */
7533
7534static void
b423cd4c 7535get_printer_attrs(cupsd_client_t *con, /* I - Client connection */
7536 ipp_attribute_t *uri) /* I - Printer URI */
ef416fc2 7537{
7538 http_status_t status; /* Policy status */
bc44d920 7539 cups_ptype_t dtype; /* Destination type (printer/class) */
b423cd4c 7540 cupsd_printer_t *printer; /* Printer/class */
ef416fc2 7541 cups_array_t *ra; /* Requested attributes array */
7542
7543
b423cd4c 7544 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", con,
7545 con->http.fd, uri->values[0].string.text);
ef416fc2 7546
7547 /*
b423cd4c 7548 * Is the destination valid?
ef416fc2 7549 */
7550
f7deaa1a 7551 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 7552 {
7553 /*
b423cd4c 7554 * Bad URI...
ef416fc2 7555 */
7556
7557 send_ipp_status(con, IPP_NOT_FOUND,
b423cd4c 7558 _("The printer or class was not found."));
ef416fc2 7559 return;
7560 }
7561
7562 /*
7563 * Check policy...
7564 */
7565
b423cd4c 7566 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
ef416fc2 7567 {
f899b121 7568 send_http_error(con, status, printer);
ef416fc2 7569 return;
7570 }
7571
7572 /*
b423cd4c 7573 * Send the attributes...
ef416fc2 7574 */
7575
7576 ra = create_requested_array(con->request);
7577
b423cd4c 7578 copy_printer_attrs(con, printer, ra);
ef416fc2 7579
7580 cupsArrayDelete(ra);
7581
7582 con->response->request.status.status_code = IPP_OK;
7583}
7584
7585
c168a833
MS
7586/*
7587 * 'get_printer_supported()' - Get printer supported values.
7588 */
7589
7590static void
7591get_printer_supported(
7592 cupsd_client_t *con, /* I - Client connection */
7593 ipp_attribute_t *uri) /* I - Printer URI */
7594{
7595 http_status_t status; /* Policy status */
7596 cups_ptype_t dtype; /* Destination type (printer/class) */
7597 cupsd_printer_t *printer; /* Printer/class */
7598
7599
7600 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_supported(%p[%d], %s)", con,
7601 con->http.fd, uri->values[0].string.text);
7602
7603 /*
7604 * Is the destination valid?
7605 */
7606
7607 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7608 {
7609 /*
7610 * Bad URI...
7611 */
7612
7613 send_ipp_status(con, IPP_NOT_FOUND,
7614 _("The printer or class was not found."));
7615 return;
7616 }
7617
7618 /*
7619 * Check policy...
7620 */
7621
7622 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7623 {
7624 send_http_error(con, status, printer);
7625 return;
7626 }
7627
7628 /*
7629 * Return a list of attributes that can be set via Set-Printer-Attributes.
7630 */
7631
7632 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7633 "printer-info", 0);
7634 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7635 "printer-location", 0);
7636
7637 con->response->request.status.status_code = IPP_OK;
7638}
7639
7640
ef416fc2 7641/*
b423cd4c 7642 * 'get_printers()' - Get a list of printers or classes.
ef416fc2 7643 */
7644
7645static void
b423cd4c 7646get_printers(cupsd_client_t *con, /* I - Client connection */
7647 int type) /* I - 0 or CUPS_PRINTER_CLASS */
ef416fc2 7648{
b423cd4c 7649 http_status_t status; /* Policy status */
7650 ipp_attribute_t *attr; /* Current attribute */
bc44d920 7651 int limit; /* Max number of printers to return */
b423cd4c 7652 int count; /* Number of printers that match */
7653 cupsd_printer_t *printer; /* Current printer pointer */
7654 int printer_type, /* printer-type attribute */
7655 printer_mask; /* printer-type-mask attribute */
7656 char *location; /* Location string */
7657 const char *username; /* Current user */
7658 char *first_printer_name; /* first-printer-name attribute */
7659 cups_array_t *ra; /* Requested attributes array */
e07d4801 7660 int local; /* Local connection? */
ef416fc2 7661
ef416fc2 7662
b423cd4c 7663 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con,
7664 con->http.fd, type);
ef416fc2 7665
7666 /*
7667 * Check policy...
7668 */
7669
b423cd4c 7670 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
ef416fc2 7671 {
f899b121 7672 send_http_error(con, status, NULL);
ef416fc2 7673 return;
7674 }
7675
7676 /*
b423cd4c 7677 * Check for printers...
ef416fc2 7678 */
7679
b423cd4c 7680 if (!Printers || !cupsArrayCount(Printers))
7681 {
7682 send_ipp_status(con, IPP_NOT_FOUND, _("No destinations added."));
7683 return;
7684 }
7685
7686 /*
7687 * See if they want to limit the number of printers reported...
7688 */
ef416fc2 7689
fa73b229 7690 if ((attr = ippFindAttribute(con->request, "limit",
7691 IPP_TAG_INTEGER)) != NULL)
ef416fc2 7692 limit = attr->values[0].integer;
7693 else
b423cd4c 7694 limit = 10000000;
7695
7696 if ((attr = ippFindAttribute(con->request, "first-printer-name",
7697 IPP_TAG_NAME)) != NULL)
7698 first_printer_name = attr->values[0].string.text;
7699 else
7700 first_printer_name = NULL;
ef416fc2 7701
7702 /*
b423cd4c 7703 * Support filtering...
ef416fc2 7704 */
7705
b423cd4c 7706 if ((attr = ippFindAttribute(con->request, "printer-type",
7707 IPP_TAG_ENUM)) != NULL)
7708 printer_type = attr->values[0].integer;
ef416fc2 7709 else
b423cd4c 7710 printer_type = 0;
ef416fc2 7711
b423cd4c 7712 if ((attr = ippFindAttribute(con->request, "printer-type-mask",
7713 IPP_TAG_ENUM)) != NULL)
7714 printer_mask = attr->values[0].integer;
ef416fc2 7715 else
b423cd4c 7716 printer_mask = 0;
e00b005a 7717
e07d4801
MS
7718 local = httpAddrLocalhost(&(con->clientaddr));
7719
b423cd4c 7720 if ((attr = ippFindAttribute(con->request, "printer-location",
7721 IPP_TAG_TEXT)) != NULL)
7722 location = attr->values[0].string.text;
7723 else
7724 location = NULL;
e00b005a 7725
7726 if (con->username[0])
b423cd4c 7727 username = con->username;
e00b005a 7728 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7729 IPP_TAG_NAME)) != NULL)
b423cd4c 7730 username = attr->values[0].string.text;
e00b005a 7731 else
b423cd4c 7732 username = NULL;
ef416fc2 7733
b423cd4c 7734 ra = create_requested_array(con->request);
ef416fc2 7735
7736 /*
b423cd4c 7737 * OK, build a list of printers for this printer...
ef416fc2 7738 */
7739
b423cd4c 7740 if (first_printer_name)
ef416fc2 7741 {
b423cd4c 7742 if ((printer = cupsdFindDest(first_printer_name)) == NULL)
7743 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
ef416fc2 7744 }
7745 else
b423cd4c 7746 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
ef416fc2 7747
b423cd4c 7748 for (count = 0;
7749 count < limit && printer;
7750 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
7751 {
e07d4801
MS
7752 if (!local && !printer->shared)
7753 continue;
7754
b423cd4c 7755 if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
7756 (printer->type & printer_mask) == printer_type &&
b19ccc9e
MS
7757 (!location ||
7758 (printer->location && !strcasecmp(printer->location, location))))
ef416fc2 7759 {
7760 /*
b423cd4c 7761 * If HideImplicitMembers is enabled, see if this printer or class
7762 * is a member of an implicit class...
ef416fc2 7763 */
7764
b423cd4c 7765 if (ImplicitClasses && HideImplicitMembers &&
7766 printer->in_implicit_class)
7767 continue;
ef416fc2 7768
b423cd4c 7769 /*
7770 * If a username is specified, see if it is allowed or denied
7771 * access...
7772 */
ef416fc2 7773
536bc2c6 7774 if (printer->num_users && username && !user_allowed(printer, username))
b423cd4c 7775 continue;
ef416fc2 7776
b423cd4c 7777 /*
7778 * Add the group separator as needed...
7779 */
ef416fc2 7780
b423cd4c 7781 if (count > 0)
7782 ippAddSeparator(con->response);
ef416fc2 7783
b423cd4c 7784 count ++;
ef416fc2 7785
b423cd4c 7786 /*
7787 * Send the attributes...
7788 */
ef416fc2 7789
b423cd4c 7790 copy_printer_attrs(con, printer, ra);
7791 }
ef416fc2 7792 }
7793
b423cd4c 7794 cupsArrayDelete(ra);
ef416fc2 7795
7796 con->response->request.status.status_code = IPP_OK;
7797}
7798
7799
7800/*
b423cd4c 7801 * 'get_subscription_attrs()' - Get subscription attributes.
ef416fc2 7802 */
7803
7804static void
b423cd4c 7805get_subscription_attrs(
7806 cupsd_client_t *con, /* I - Client connection */
7807 int sub_id) /* I - Subscription ID */
ef416fc2 7808{
b423cd4c 7809 http_status_t status; /* Policy status */
7810 cupsd_subscription_t *sub; /* Subscription */
7811 cups_array_t *ra; /* Requested attributes array */
ef416fc2 7812
7813
b423cd4c 7814 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7815 "get_subscription_attrs(con=%p[%d], sub_id=%d)",
7816 con, con->http.fd, sub_id);
ef416fc2 7817
fa73b229 7818 /*
b423cd4c 7819 * Is the subscription ID valid?
fa73b229 7820 */
7821
b423cd4c 7822 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
fa73b229 7823 {
7824 /*
b423cd4c 7825 * Bad subscription ID...
fa73b229 7826 */
7827
7828 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 7829 _("notify-subscription-id %d no good"), sub_id);
fa73b229 7830 return;
7831 }
7832
7833 /*
7834 * Check policy...
7835 */
7836
b423cd4c 7837 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
7838 DefaultPolicyPtr,
7839 con, sub->owner)) != HTTP_OK)
fa73b229 7840 {
f899b121 7841 send_http_error(con, status, sub->dest);
fa73b229 7842 return;
7843 }
7844
ef416fc2 7845 /*
b423cd4c 7846 * Copy the subscription attributes to the response using the
7847 * requested-attributes attribute that may be provided by the client.
ef416fc2 7848 */
7849
b423cd4c 7850 ra = create_requested_array(con->request);
fa73b229 7851
b423cd4c 7852 copy_subscription_attrs(con, sub, ra);
ef416fc2 7853
b423cd4c 7854 cupsArrayDelete(ra);
fa73b229 7855
b423cd4c 7856 con->response->request.status.status_code = IPP_OK;
7857}
fa73b229 7858
fa73b229 7859
b423cd4c 7860/*
7861 * 'get_subscriptions()' - Get subscriptions.
7862 */
ef416fc2 7863
b423cd4c 7864static void
7865get_subscriptions(cupsd_client_t *con, /* I - Client connection */
7866 ipp_attribute_t *uri) /* I - Printer/job URI */
7867{
7868 http_status_t status; /* Policy status */
7869 int count; /* Number of subscriptions */
7870 int limit; /* Limit */
7871 cupsd_subscription_t *sub; /* Subscription */
7872 cups_array_t *ra; /* Requested attributes array */
7873 ipp_attribute_t *attr; /* Attribute */
bc44d920 7874 cups_ptype_t dtype; /* Destination type (printer/class) */
f7deaa1a 7875 char scheme[HTTP_MAX_URI],
7876 /* Scheme portion of URI */
b423cd4c 7877 username[HTTP_MAX_URI],
7878 /* Username portion of URI */
7879 host[HTTP_MAX_URI],
7880 /* Host portion of URI */
7881 resource[HTTP_MAX_URI];
7882 /* Resource portion of URI */
7883 int port; /* Port portion of URI */
7884 cupsd_job_t *job; /* Job pointer */
7885 cupsd_printer_t *printer; /* Printer */
fa73b229 7886
fa73b229 7887
b423cd4c 7888 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7889 "get_subscriptions(con=%p[%d], uri=%s)",
7890 con, con->http.fd, uri->values[0].string.text);
7891
7892 /*
7893 * Is the destination valid?
7894 */
7895
f7deaa1a 7896 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7897 sizeof(scheme), username, sizeof(username), host,
b423cd4c 7898 sizeof(host), &port, resource, sizeof(resource));
7899
7900 if (!strcmp(resource, "/") ||
7901 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6) ||
7902 (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) ||
7903 (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9))
7904 {
7905 printer = NULL;
7906 job = NULL;
ef416fc2 7907 }
b423cd4c 7908 else if (!strncmp(resource, "/jobs/", 6) && resource[6])
ef416fc2 7909 {
b423cd4c 7910 printer = NULL;
7911 job = cupsdFindJob(atoi(resource + 6));
ef416fc2 7912
b423cd4c 7913 if (!job)
ef416fc2 7914 {
4d301e69 7915 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%s does not exist"),
b423cd4c 7916 resource + 6);
ef416fc2 7917 return;
7918 }
b423cd4c 7919 }
f7deaa1a 7920 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
b423cd4c 7921 {
fa73b229 7922 /*
b423cd4c 7923 * Bad URI...
fa73b229 7924 */
7925
b423cd4c 7926 send_ipp_status(con, IPP_NOT_FOUND,
7927 _("The printer or class was not found."));
7928 return;
7929 }
7930 else if ((attr = ippFindAttribute(con->request, "notify-job-id",
7931 IPP_TAG_INTEGER)) != NULL)
7932 {
7933 job = cupsdFindJob(attr->values[0].integer);
ef416fc2 7934
b423cd4c 7935 if (!job)
fa73b229 7936 {
4d301e69 7937 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"),
b423cd4c 7938 attr->values[0].integer);
fa73b229 7939 return;
7940 }
ef416fc2 7941 }
b423cd4c 7942 else
7943 job = NULL;
ef416fc2 7944
7945 /*
b423cd4c 7946 * Check policy...
ef416fc2 7947 */
7948
b423cd4c 7949 if ((status = cupsdCheckPolicy(printer ? printer->op_policy_ptr :
7950 DefaultPolicyPtr,
7951 con, NULL)) != HTTP_OK)
ef416fc2 7952 {
f899b121 7953 send_http_error(con, status, printer);
b423cd4c 7954 return;
7955 }
ef416fc2 7956
b423cd4c 7957 /*
7958 * Copy the subscription attributes to the response using the
7959 * requested-attributes attribute that may be provided by the client.
7960 */
ef416fc2 7961
b423cd4c 7962 ra = create_requested_array(con->request);
ef416fc2 7963
b423cd4c 7964 if ((attr = ippFindAttribute(con->request, "limit",
7965 IPP_TAG_INTEGER)) != NULL)
7966 limit = attr->values[0].integer;
7967 else
7968 limit = 0;
ef416fc2 7969
b423cd4c 7970 /*
7971 * See if we only want to see subscriptions for a specific user...
7972 */
ef416fc2 7973
b423cd4c 7974 if ((attr = ippFindAttribute(con->request, "my-subscriptions",
7975 IPP_TAG_BOOLEAN)) != NULL &&
7976 attr->values[0].boolean)
7977 strlcpy(username, get_username(con), sizeof(username));
fa73b229 7978 else
b423cd4c 7979 username[0] = '\0';
ef416fc2 7980
b423cd4c 7981 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0;
7982 sub;
7983 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
7984 if ((!printer || sub->dest == printer) && (!job || sub->job == job) &&
7985 (!username[0] || !strcasecmp(username, sub->owner)))
fa73b229 7986 {
b423cd4c 7987 ippAddSeparator(con->response);
7988 copy_subscription_attrs(con, sub, ra);
ef416fc2 7989
b423cd4c 7990 count ++;
7991 if (limit && count >= limit)
7992 break;
7993 }
ef416fc2 7994
b423cd4c 7995 cupsArrayDelete(ra);
fa73b229 7996
b423cd4c 7997 if (count)
7998 con->response->request.status.status_code = IPP_OK;
7999 else
8000 send_ipp_status(con, IPP_NOT_FOUND, _("No subscriptions found."));
8001}
ef416fc2 8002
ef416fc2 8003
b423cd4c 8004/*
8005 * 'get_username()' - Get the username associated with a request.
8006 */
ef416fc2 8007
b423cd4c 8008static const char * /* O - Username */
8009get_username(cupsd_client_t *con) /* I - Connection */
8010{
8011 ipp_attribute_t *attr; /* Attribute */
ef416fc2 8012
ef416fc2 8013
b423cd4c 8014 if (con->username[0])
8015 return (con->username);
8016 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
8017 IPP_TAG_NAME)) != NULL)
8018 return (attr->values[0].string.text);
8019 else
8020 return ("anonymous");
ef416fc2 8021}
8022
8023
8024/*
b423cd4c 8025 * 'hold_job()' - Hold a print job.
ef416fc2 8026 */
8027
b423cd4c 8028static void
8029hold_job(cupsd_client_t *con, /* I - Client connection */
8030 ipp_attribute_t *uri) /* I - Job or Printer URI */
ef416fc2 8031{
b9faaae1
MS
8032 ipp_attribute_t *attr; /* Current job-hold-until */
8033 const char *when; /* New value */
b423cd4c 8034 int jobid; /* Job ID */
61cf44e2 8035 char scheme[HTTP_MAX_URI], /* Method portion of URI */
b423cd4c 8036 username[HTTP_MAX_URI], /* Username portion of URI */
8037 host[HTTP_MAX_URI], /* Host portion of URI */
8038 resource[HTTP_MAX_URI]; /* Resource portion of URI */
8039 int port; /* Port portion of URI */
8040 cupsd_job_t *job; /* Job information */
8041
ef416fc2 8042
b423cd4c 8043 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", con, con->http.fd,
8044 uri->values[0].string.text);
ef416fc2 8045
8046 /*
b423cd4c 8047 * See if we have a job URI or a printer URI...
ef416fc2 8048 */
8049
b423cd4c 8050 if (!strcmp(uri->name, "printer-uri"))
8051 {
8052 /*
8053 * Got a printer URI; see if we also have a job-id attribute...
8054 */
ef416fc2 8055
b423cd4c 8056 if ((attr = ippFindAttribute(con->request, "job-id",
8057 IPP_TAG_INTEGER)) == NULL)
8058 {
8059 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 8060 _("Got a printer-uri attribute but no job-id"));
b423cd4c 8061 return;
8062 }
ef416fc2 8063
b423cd4c 8064 jobid = attr->values[0].integer;
8065 }
ef416fc2 8066 else
ef416fc2 8067 {
b423cd4c 8068 /*
8069 * Got a job URI; parse it to get the job ID...
8070 */
ef416fc2 8071
61cf44e2
MS
8072 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8073 sizeof(scheme), username, sizeof(username), host,
b423cd4c 8074 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 8075
b423cd4c 8076 if (strncmp(resource, "/jobs/", 6))
8077 {
8078 /*
8079 * Not a valid URI!
8080 */
ef416fc2 8081
b423cd4c 8082 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 8083 _("Bad job-uri attribute \"%s\""),
b423cd4c 8084 uri->values[0].string.text);
8085 return;
8086 }
ef416fc2 8087
b423cd4c 8088 jobid = atoi(resource + 6);
8089 }
ef416fc2 8090
ef416fc2 8091 /*
b423cd4c 8092 * See if the job exists...
ef416fc2 8093 */
8094
b423cd4c 8095 if ((job = cupsdFindJob(jobid)) == NULL)
8096 {
8097 /*
8098 * Nope - return a "not found" error...
8099 */
8100
4d301e69 8101 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
b423cd4c 8102 return;
8103 }
ef416fc2 8104
8105 /*
b423cd4c 8106 * See if the job is owned by the requesting user...
ef416fc2 8107 */
8108
b423cd4c 8109 if (!validate_user(job, con, job->username, username, sizeof(username)))
8110 {
b0f6947b
MS
8111 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
8112 cupsdFindDest(job->dest));
b423cd4c 8113 return;
8114 }
ef416fc2 8115
8116 /*
b423cd4c 8117 * Hold the job and return...
ef416fc2 8118 */
8119
b9faaae1
MS
8120 if ((attr = ippFindAttribute(con->request, "job-hold-until",
8121 IPP_TAG_KEYWORD)) == NULL)
8122 attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
ef416fc2 8123
b423cd4c 8124 if (attr)
8125 {
b9faaae1 8126 when = attr->values[0].string.text;
d09495fa 8127
01ce6322 8128 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
b9faaae1 8129 "Job job-hold-until value changed by user.");
b423cd4c 8130 }
b9faaae1
MS
8131 else
8132 when = "indefinite";
ef416fc2 8133
b9faaae1
MS
8134 cupsdSetJobHoldUntil(job, when, 1);
8135 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT, "Job held by \"%s\".",
8136 username);
b423cd4c 8137
8138 con->response->request.status.status_code = IPP_OK;
ef416fc2 8139}
8140
8141
61cf44e2
MS
8142/*
8143 * 'hold_new_jobs()' - Hold pending/new jobs on a printer or class.
8144 */
8145
8146static void
8147hold_new_jobs(cupsd_client_t *con, /* I - Connection */
8148 ipp_attribute_t *uri) /* I - Printer URI */
8149{
8150 http_status_t status; /* Policy status */
8151 cups_ptype_t dtype; /* Destination type (printer/class) */
8152 cupsd_printer_t *printer; /* Printer data */
8153
8154
8155 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_new_jobs(%p[%d], %s)", con,
8156 con->http.fd, uri->values[0].string.text);
8157
8158 /*
8159 * Is the destination valid?
8160 */
8161
8162 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8163 {
8164 /*
8165 * Bad URI...
8166 */
8167
8168 send_ipp_status(con, IPP_NOT_FOUND,
8169 _("The printer or class was not found."));
8170 return;
8171 }
8172
8173 /*
8174 * Check policy...
8175 */
8176
8177 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8178 {
8179 send_http_error(con, status, printer);
8180 return;
8181 }
8182
8183 /*
8184 * Hold pending/new jobs sent to the printer...
8185 */
8186
8187 printer->holding_new_jobs = 1;
8188
8189 cupsdSetPrinterReasons(printer, "+hold-new-jobs");
8190 cupsdAddPrinterHistory(printer);
8191
8192 if (dtype & CUPS_PRINTER_CLASS)
8193 cupsdLogMessage(CUPSD_LOG_INFO,
8194 "Class \"%s\" now holding pending/new jobs (\"%s\").",
8195 printer->name, get_username(con));
8196 else
8197 cupsdLogMessage(CUPSD_LOG_INFO,
8198 "Printer \"%s\" now holding pending/new jobs (\"%s\").",
8199 printer->name, get_username(con));
8200
8201 /*
8202 * Everything was ok, so return OK status...
8203 */
8204
8205 con->response->request.status.status_code = IPP_OK;
8206}
8207
8208
ef416fc2 8209/*
b423cd4c 8210 * 'move_job()' - Move a job to a new destination.
ef416fc2 8211 */
8212
8213static void
b423cd4c 8214move_job(cupsd_client_t *con, /* I - Client connection */
8215 ipp_attribute_t *uri) /* I - Job URI */
ef416fc2 8216{
8217 http_status_t status; /* Policy status */
8218 ipp_attribute_t *attr; /* Current attribute */
b423cd4c 8219 int jobid; /* Job ID */
ef416fc2 8220 cupsd_job_t *job; /* Current job */
d09495fa 8221 const char *src; /* Source printer/class */
b423cd4c 8222 cups_ptype_t stype, /* Source type (printer or class) */
bc44d920 8223 dtype; /* Destination type (printer/class) */
f7deaa1a 8224 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
ef416fc2 8225 username[HTTP_MAX_URI], /* Username portion of URI */
8226 host[HTTP_MAX_URI], /* Host portion of URI */
b423cd4c 8227 resource[HTTP_MAX_URI]; /* Resource portion of URI */
ef416fc2 8228 int port; /* Port portion of URI */
b423cd4c 8229 cupsd_printer_t *sprinter, /* Source printer */
8230 *dprinter; /* Destination printer */
ef416fc2 8231
8232
b423cd4c 8233 cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", con, con->http.fd,
ef416fc2 8234 uri->values[0].string.text);
8235
8236 /*
b423cd4c 8237 * Get the new printer or class...
ef416fc2 8238 */
8239
b423cd4c 8240 if ((attr = ippFindAttribute(con->request, "job-printer-uri",
8241 IPP_TAG_URI)) == NULL)
ef416fc2 8242 {
b423cd4c 8243 /*
8244 * Need job-printer-uri...
8245 */
ef416fc2 8246
b423cd4c 8247 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 8248 _("job-printer-uri attribute missing"));
b423cd4c 8249 return;
ef416fc2 8250 }
f7faf1f5 8251
f7deaa1a 8252 if (!cupsdValidateDest(attr->values[0].string.text, &dtype, &dprinter))
ef416fc2 8253 {
b423cd4c 8254 /*
8255 * Bad URI...
8256 */
ef416fc2 8257
b423cd4c 8258 send_ipp_status(con, IPP_NOT_FOUND,
8259 _("The printer or class was not found."));
8260 return;
ef416fc2 8261 }
8262
ef416fc2 8263 /*
b423cd4c 8264 * See if we have a job URI or a printer URI...
ef416fc2 8265 */
8266
f7deaa1a 8267 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8268 sizeof(scheme), username, sizeof(username), host,
b423cd4c 8269 sizeof(host), &port, resource, sizeof(resource));
8270
8271 if (!strcmp(uri->name, "printer-uri"))
ef416fc2 8272 {
8273 /*
b423cd4c 8274 * Got a printer URI; see if we also have a job-id attribute...
ef416fc2 8275 */
8276
b423cd4c 8277 if ((attr = ippFindAttribute(con->request, "job-id",
8278 IPP_TAG_INTEGER)) == NULL)
ef416fc2 8279 {
b423cd4c 8280 /*
8281 * Move all jobs...
8282 */
8283
f7deaa1a 8284 if ((src = cupsdValidateDest(uri->values[0].string.text, &stype,
8285 &sprinter)) == NULL)
b423cd4c 8286 {
8287 /*
8288 * Bad URI...
8289 */
8290
8291 send_ipp_status(con, IPP_NOT_FOUND,
8292 _("The printer or class was not found."));
8293 return;
8294 }
8295
8296 job = NULL;
8297 }
8298 else
8299 {
8300 /*
8301 * Otherwise, just move a single job...
8302 */
8303
8304 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
8305 {
8306 /*
8307 * Nope - return a "not found" error...
8308 */
8309
8310 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 8311 _("Job #%d does not exist"), attr->values[0].integer);
b423cd4c 8312 return;
8313 }
8314 else
8315 {
8316 /*
8317 * Job found, initialize source pointers...
8318 */
8319
8320 src = NULL;
8321 sprinter = NULL;
8322 }
ef416fc2 8323 }
8324 }
8325 else
8326 {
8327 /*
b423cd4c 8328 * Got a job URI; parse it to get the job ID...
ef416fc2 8329 */
8330
b423cd4c 8331 if (strncmp(resource, "/jobs/", 6))
8332 {
8333 /*
8334 * Not a valid URI!
8335 */
8336
8337 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 8338 _("Bad job-uri attribute \"%s\""),
b423cd4c 8339 uri->values[0].string.text);
8340 return;
8341 }
ef416fc2 8342
ef416fc2 8343 /*
b423cd4c 8344 * See if the job exists...
ef416fc2 8345 */
8346
b423cd4c 8347 jobid = atoi(resource + 6);
ef416fc2 8348
b423cd4c 8349 if ((job = cupsdFindJob(jobid)) == NULL)
ef416fc2 8350 {
8351 /*
b423cd4c 8352 * Nope - return a "not found" error...
ef416fc2 8353 */
8354
b423cd4c 8355 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 8356 _("Job #%d does not exist"), jobid);
b423cd4c 8357 return;
ef416fc2 8358 }
8359 else
b423cd4c 8360 {
8361 /*
8362 * Job found, initialize source pointers...
8363 */
ef416fc2 8364
b423cd4c 8365 src = NULL;
8366 sprinter = NULL;
8367 }
ef416fc2 8368 }
8369
ac884b6a
MS
8370 /*
8371 * Check the policy of the destination printer...
8372 */
8373
8374 if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con,
8375 job ? job->username : NULL)) != HTTP_OK)
8376 {
8377 send_http_error(con, status, dprinter);
8378 return;
8379 }
8380
ef416fc2 8381 /*
b423cd4c 8382 * Now move the job or jobs...
ef416fc2 8383 */
8384
b423cd4c 8385 if (job)
8386 {
8387 /*
8388 * See if the job has been completed...
8389 */
8390
8391 if (job->state_value > IPP_JOB_STOPPED)
8392 {
8393 /*
8394 * Return a "not-possible" error...
8395 */
8396
8397 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 8398 _("Job #%d is finished and cannot be altered"),
b423cd4c 8399 job->id);
8400 return;
8401 }
ef416fc2 8402
b423cd4c 8403 /*
8404 * See if the job is owned by the requesting user...
8405 */
ef416fc2 8406
b423cd4c 8407 if (!validate_user(job, con, job->username, username, sizeof(username)))
8408 {
b0f6947b
MS
8409 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
8410 cupsdFindDest(job->dest));
b423cd4c 8411 return;
8412 }
ef416fc2 8413
ef416fc2 8414 /*
b423cd4c 8415 * Move the job to a different printer or class...
ef416fc2 8416 */
8417
e53920b9 8418 cupsdMoveJob(job, dprinter);
ef416fc2 8419 }
b423cd4c 8420 else
8421 {
8422 /*
8423 * Got the source printer, now look through the jobs...
8424 */
ef416fc2 8425
b423cd4c 8426 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
8427 job;
8428 job = (cupsd_job_t *)cupsArrayNext(Jobs))
8429 {
8430 /*
8431 * See if the job is pointing at the source printer or has not been
8432 * completed...
8433 */
ef416fc2 8434
b423cd4c 8435 if (strcasecmp(job->dest, src) ||
8436 job->state_value > IPP_JOB_STOPPED)
8437 continue;
ef416fc2 8438
b423cd4c 8439 /*
8440 * See if the job can be moved by the requesting user...
8441 */
ef416fc2 8442
b423cd4c 8443 if (!validate_user(job, con, job->username, username, sizeof(username)))
8444 continue;
ef416fc2 8445
b423cd4c 8446 /*
8447 * Move the job to a different printer or class...
8448 */
ef416fc2 8449
e53920b9 8450 cupsdMoveJob(job, dprinter);
b423cd4c 8451 }
ef416fc2 8452 }
8453
8454 /*
b423cd4c 8455 * Start jobs if possible...
ef416fc2 8456 */
8457
b423cd4c 8458 cupsdCheckJobs();
ef416fc2 8459
8460 /*
b423cd4c 8461 * Return with "everything is OK" status...
ef416fc2 8462 */
8463
b423cd4c 8464 con->response->request.status.status_code = IPP_OK;
8465}
ef416fc2 8466
ef416fc2 8467
b423cd4c 8468/*
8469 * 'ppd_parse_line()' - Parse a PPD default line.
8470 */
ef416fc2 8471
b423cd4c 8472static int /* O - 0 on success, -1 on failure */
8473ppd_parse_line(const char *line, /* I - Line */
8474 char *option, /* O - Option name */
8475 int olen, /* I - Size of option name */
8476 char *choice, /* O - Choice name */
8477 int clen) /* I - Size of choice name */
8478{
8479 /*
8480 * Verify this is a default option line...
8481 */
ef416fc2 8482
b423cd4c 8483 if (strncmp(line, "*Default", 8))
8484 return (-1);
ef416fc2 8485
b423cd4c 8486 /*
8487 * Read the option name...
8488 */
8489
18ecb428
MS
8490 for (line += 8, olen --;
8491 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
8492 line ++)
b423cd4c 8493 if (olen > 0)
8494 {
8495 *option++ = *line;
8496 olen --;
ef416fc2 8497 }
8498
b423cd4c 8499 *option = '\0';
ef416fc2 8500
b423cd4c 8501 /*
8502 * Skip everything else up to the colon (:)...
8503 */
ef416fc2 8504
b423cd4c 8505 while (*line && *line != ':')
8506 line ++;
ef416fc2 8507
b423cd4c 8508 if (!*line)
8509 return (-1);
ef416fc2 8510
b423cd4c 8511 line ++;
ef416fc2 8512
b423cd4c 8513 /*
8514 * Now grab the option choice, skipping leading whitespace...
8515 */
ef416fc2 8516
b423cd4c 8517 while (isspace(*line & 255))
8518 line ++;
ef416fc2 8519
18ecb428
MS
8520 for (clen --;
8521 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
8522 line ++)
b423cd4c 8523 if (clen > 0)
8524 {
8525 *choice++ = *line;
8526 clen --;
8527 }
ef416fc2 8528
b423cd4c 8529 *choice = '\0';
ef416fc2 8530
b423cd4c 8531 /*
8532 * Return with no errors...
8533 */
ef416fc2 8534
b423cd4c 8535 return (0);
8536}
ef416fc2 8537
ef416fc2 8538
b423cd4c 8539/*
8540 * 'print_job()' - Print a file to a printer or class.
8541 */
ef416fc2 8542
b423cd4c 8543static void
8544print_job(cupsd_client_t *con, /* I - Client connection */
8545 ipp_attribute_t *uri) /* I - Printer URI */
8546{
8547 ipp_attribute_t *attr; /* Current attribute */
8548 ipp_attribute_t *format; /* Document-format attribute */
f7deaa1a 8549 const char *default_format; /* document-format-default value */
b423cd4c 8550 cupsd_job_t *job; /* New job */
8551 char filename[1024]; /* Job filename */
8552 mime_type_t *filetype; /* Type of file */
8553 char super[MIME_MAX_SUPER], /* Supertype of file */
8554 type[MIME_MAX_TYPE], /* Subtype of file */
8555 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
8556 /* Textual name of mime type */
8557 cupsd_printer_t *printer; /* Printer data */
8558 struct stat fileinfo; /* File information */
8559 int kbytes; /* Size of file */
8560 int compression; /* Document compression */
ef416fc2 8561
ef416fc2 8562
b423cd4c 8563 cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", con, con->http.fd,
8564 uri->values[0].string.text);
ef416fc2 8565
b423cd4c 8566 /*
8567 * Validate print file attributes, for now just document-format and
8568 * compression (CUPS only supports "none" and "gzip")...
8569 */
ef416fc2 8570
b423cd4c 8571 compression = CUPS_FILE_NONE;
ef416fc2 8572
b423cd4c 8573 if ((attr = ippFindAttribute(con->request, "compression",
8574 IPP_TAG_KEYWORD)) != NULL)
8575 {
8576 if (strcmp(attr->values[0].string.text, "none")
8577#ifdef HAVE_LIBZ
8578 && strcmp(attr->values[0].string.text, "gzip")
8579#endif /* HAVE_LIBZ */
8580 )
8581 {
8582 send_ipp_status(con, IPP_ATTRIBUTES,
4d301e69 8583 _("Unsupported compression \"%s\""),
b423cd4c 8584 attr->values[0].string.text);
8585 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
8586 "compression", NULL, attr->values[0].string.text);
8587 return;
8588 }
ef416fc2 8589
b423cd4c 8590#ifdef HAVE_LIBZ
8591 if (!strcmp(attr->values[0].string.text, "gzip"))
8592 compression = CUPS_FILE_GZIP;
8593#endif /* HAVE_LIBZ */
8594 }
ef416fc2 8595
b423cd4c 8596 /*
8597 * Do we have a file to print?
8598 */
ef416fc2 8599
b423cd4c 8600 if (!con->filename)
8601 {
4d301e69 8602 send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?"));
b423cd4c 8603 return;
8604 }
ef416fc2 8605
f7deaa1a 8606 /*
8607 * Is the destination valid?
8608 */
8609
8610 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
8611 {
8612 /*
8613 * Bad URI...
8614 */
8615
8616 send_ipp_status(con, IPP_NOT_FOUND,
8617 _("The printer or class was not found."));
8618 return;
8619 }
8620
b423cd4c 8621 /*
8622 * Is it a format we support?
8623 */
ef416fc2 8624
b423cd4c 8625 if ((format = ippFindAttribute(con->request, "document-format",
8626 IPP_TAG_MIMETYPE)) != NULL)
8627 {
8628 /*
8629 * Grab format from client...
8630 */
8631
f7deaa1a 8632 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super,
8633 type) != 2)
b423cd4c 8634 {
8635 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 8636 _("Could not scan type \"%s\""),
b423cd4c 8637 format->values[0].string.text);
8638 return;
ef416fc2 8639 }
b423cd4c 8640 }
f7deaa1a 8641 else if ((default_format = cupsGetOption("document-format",
8642 printer->num_options,
8643 printer->options)) != NULL)
8644 {
8645 /*
8646 * Use default document format...
8647 */
8648
8649 if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
8650 {
8651 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 8652 _("Could not scan type \"%s\""),
f7deaa1a 8653 default_format);
8654 return;
8655 }
8656 }
b423cd4c 8657 else
8658 {
8659 /*
f7deaa1a 8660 * Auto-type it!
b423cd4c 8661 */
8662
8663 strcpy(super, "application");
8664 strcpy(type, "octet-stream");
8665 }
ef416fc2 8666
b423cd4c 8667 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
8668 {
ef416fc2 8669 /*
b423cd4c 8670 * Auto-type the file...
ef416fc2 8671 */
8672
b423cd4c 8673 ipp_attribute_t *doc_name; /* document-name attribute */
8674
8675
a0f6818e 8676 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job ???] Auto-typing file...");
b423cd4c 8677
8678 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
8679 filetype = mimeFileType(MimeDatabase, con->filename,
8680 doc_name ? doc_name->values[0].string.text : NULL,
8681 &compression);
8682
f7deaa1a 8683 if (!filetype)
8684 filetype = mimeType(MimeDatabase, super, type);
a0f6818e
MS
8685
8686 cupsdLogMessage(CUPSD_LOG_INFO, "[Job ???] Request file type is %s/%s.",
8687 filetype->super, filetype->type);
f7deaa1a 8688 }
8689 else
8690 filetype = mimeType(MimeDatabase, super, type);
ef416fc2 8691
f7deaa1a 8692 if (filetype &&
8693 (!format ||
8694 (!strcmp(super, "application") && !strcmp(type, "octet-stream"))))
8695 {
8696 /*
8697 * Replace the document-format attribute value with the auto-typed or
8698 * default one.
8699 */
b423cd4c 8700
f7deaa1a 8701 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
8702 filetype->type);
ed486911 8703
f7deaa1a 8704 if (format)
8705 {
8706 _cupsStrFree(format->values[0].string.text);
8707
8708 format->values[0].string.text = _cupsStrAlloc(mimetype);
ef416fc2 8709 }
b423cd4c 8710 else
f7deaa1a 8711 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
8712 "document-format", NULL, mimetype);
ef416fc2 8713 }
f7deaa1a 8714 else if (!filetype)
b423cd4c 8715 {
8716 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
4d301e69 8717 _("Unsupported format \'%s/%s\'"), super, type);
b423cd4c 8718 cupsdLogMessage(CUPSD_LOG_INFO,
8719 "Hint: Do you have the raw file printing rules enabled?");
8720
8721 if (format)
8722 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
8723 "document-format", NULL, format->values[0].string.text);
8724
8725 return;
8726 }
8727
b423cd4c 8728 /*
8729 * Read any embedded job ticket info from PS files...
8730 */
8731
8732 if (!strcasecmp(filetype->super, "application") &&
c5571a1d
MS
8733 (!strcasecmp(filetype->type, "postscript") ||
8734 !strcasecmp(filetype->type, "pdf")))
8735 read_job_ticket(con);
b423cd4c 8736
8737 /*
8738 * Create the job object...
8739 */
8740
f7deaa1a 8741 if ((job = add_job(con, printer, filetype)) == NULL)
b423cd4c 8742 return;
8743
8744 /*
8745 * Update quota data...
8746 */
8747
8748 if (stat(con->filename, &fileinfo))
8749 kbytes = 0;
8750 else
8751 kbytes = (fileinfo.st_size + 1023) / 1024;
8752
8753 cupsdUpdateQuota(printer, job->username, 0, kbytes);
8754
8755 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
8756 IPP_TAG_INTEGER)) != NULL)
8757 attr->values[0].integer += kbytes;
8758
ef416fc2 8759 /*
8760 * Add the job file...
8761 */
8762
8763 if (add_file(con, job, filetype, compression))
8764 return;
8765
8766 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
8767 job->num_files);
8768 rename(con->filename, filename);
8769 cupsdClearString(&con->filename);
8770
8771 /*
8772 * See if we need to add the ending sheet...
8773 */
8774
91c84a35
MS
8775 if (cupsdTimeoutJob(job))
8776 return;
ef416fc2 8777
ef416fc2 8778 /*
8779 * Log and save the job...
8780 */
8781
75bd9771
MS
8782 cupsdLogJob(job, CUPSD_LOG_INFO,
8783 "File of type %s/%s queued by \"%s\".",
8784 filetype->super, filetype->type, job->username);
8785 cupsdLogJob(job, CUPSD_LOG_DEBUG, "hold_until=%d", (int)job->hold_until);
b19ccc9e
MS
8786 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
8787 job->dest, job->username);
ef416fc2 8788
ef416fc2 8789 /*
8790 * Start the job if possible...
8791 */
8792
8793 cupsdCheckJobs();
8794}
8795
8796
8797/*
c5571a1d 8798 * 'read_job_ticket()' - Read a job ticket embedded in a print file.
ef416fc2 8799 *
c5571a1d 8800 * This function only gets called when printing a single PDF or PostScript
ef416fc2 8801 * file using the Print-Job operation. It doesn't work for Create-Job +
8802 * Send-File, since the job attributes need to be set at job creation
c5571a1d
MS
8803 * time for banners to work. The embedded job ticket stuff is here
8804 * primarily to allow the Windows printer driver for CUPS to pass in JCL
ef416fc2 8805 * options and IPP attributes which otherwise would be lost.
8806 *
c5571a1d 8807 * The format of a job ticket is simple:
ef416fc2 8808 *
8809 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
8810 *
8811 * %cupsJobTicket: attr1=value1
8812 * %cupsJobTicket: attr2=value2
8813 * ...
8814 * %cupsJobTicket: attrN=valueN
8815 *
8816 * Job ticket lines must appear immediately after the first line that
c5571a1d
MS
8817 * specifies PostScript (%!PS-Adobe-3.0) or PDF (%PDF) format, and CUPS
8818 * stops looking for job ticket info when it finds a line that does not begin
ef416fc2 8819 * with "%cupsJobTicket:".
8820 *
8821 * The maximum length of a job ticket line, including the prefix, is
8822 * 255 characters to conform with the Adobe DSC.
8823 *
8824 * Read-only attributes are rejected with a notice to the error log in
8825 * case a malicious user tries anything. Since the job ticket is read
8826 * prior to attribute validation in print_job(), job ticket attributes
8827 * will go through the same validation as IPP attributes...
8828 */
8829
8830static void
c5571a1d 8831read_job_ticket(cupsd_client_t *con) /* I - Client connection */
ef416fc2 8832{
8833 cups_file_t *fp; /* File to read from */
8834 char line[256]; /* Line data */
8835 int num_options; /* Number of options */
8836 cups_option_t *options; /* Options */
8837 ipp_t *ticket; /* New attributes */
8838 ipp_attribute_t *attr, /* Current attribute */
8839 *attr2, /* Job attribute */
8840 *prev2; /* Previous job attribute */
8841
8842
8843 /*
8844 * First open the print file...
8845 */
8846
8847 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
8848 {
8849 cupsdLogMessage(CUPSD_LOG_ERROR,
c5571a1d 8850 "Unable to open print file for job ticket - %s",
ef416fc2 8851 strerror(errno));
8852 return;
8853 }
8854
8855 /*
8856 * Skip the first line...
8857 */
8858
8859 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
8860 {
8861 cupsdLogMessage(CUPSD_LOG_ERROR,
c5571a1d 8862 "Unable to read from print file for job ticket - %s",
ef416fc2 8863 strerror(errno));
8864 cupsFileClose(fp);
8865 return;
8866 }
8867
c5571a1d 8868 if (strncmp(line, "%!PS-Adobe-", 11) && strncmp(line, "%PDF-", 5))
ef416fc2 8869 {
8870 /*
8871 * Not a DSC-compliant file, so no job ticket info will be available...
8872 */
8873
8874 cupsFileClose(fp);
8875 return;
8876 }
8877
8878 /*
8879 * Read job ticket info from the file...
8880 */
8881
8882 num_options = 0;
8883 options = NULL;
8884
fa73b229 8885 while (cupsFileGets(fp, line, sizeof(line)))
ef416fc2 8886 {
8887 /*
8888 * Stop at the first non-ticket line...
8889 */
8890
fa73b229 8891 if (strncmp(line, "%cupsJobTicket:", 15))
ef416fc2 8892 break;
8893
8894 /*
8895 * Add the options to the option array...
8896 */
8897
8898 num_options = cupsParseOptions(line + 15, num_options, &options);
8899 }
8900
8901 /*
8902 * Done with the file; see if we have any options...
8903 */
8904
8905 cupsFileClose(fp);
8906
8907 if (num_options == 0)
8908 return;
8909
8910 /*
8911 * OK, convert the options to an attribute list, and apply them to
8912 * the request...
8913 */
8914
8915 ticket = ippNew();
8916 cupsEncodeOptions(ticket, num_options, options);
8917
8918 /*
8919 * See what the user wants to change.
8920 */
8921
fa73b229 8922 for (attr = ticket->attrs; attr; attr = attr->next)
ef416fc2 8923 {
8924 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
8925 continue;
8926
8927 if (!strcmp(attr->name, "job-originating-host-name") ||
8928 !strcmp(attr->name, "job-originating-user-name") ||
8929 !strcmp(attr->name, "job-media-sheets-completed") ||
8930 !strcmp(attr->name, "job-k-octets") ||
8931 !strcmp(attr->name, "job-id") ||
8932 !strncmp(attr->name, "job-state", 9) ||
8933 !strncmp(attr->name, "time-at-", 8))
8934 continue; /* Read-only attrs */
8935
fa73b229 8936 if ((attr2 = ippFindAttribute(con->request, attr->name,
8937 IPP_TAG_ZERO)) != NULL)
ef416fc2 8938 {
8939 /*
8940 * Some other value; first free the old value...
8941 */
8942
8943 if (con->request->attrs == attr2)
8944 {
8945 con->request->attrs = attr2->next;
8946 prev2 = NULL;
8947 }
8948 else
8949 {
fa73b229 8950 for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
ef416fc2 8951 if (prev2->next == attr2)
8952 {
8953 prev2->next = attr2->next;
8954 break;
8955 }
8956 }
8957
8958 if (con->request->last == attr2)
8959 con->request->last = prev2;
8960
757d2cad 8961 _ippFreeAttr(attr2);
ef416fc2 8962 }
8963
8964 /*
8965 * Add new option by copying it...
8966 */
8967
8968 copy_attribute(con->request, attr, 0);
8969 }
8970
8971 /*
8972 * Then free the attribute list and option array...
8973 */
8974
8975 ippDelete(ticket);
8976 cupsFreeOptions(num_options, options);
8977}
8978
8979
8980/*
8981 * 'reject_jobs()' - Reject print jobs to a printer.
8982 */
8983
8984static void
8985reject_jobs(cupsd_client_t *con, /* I - Client connection */
8986 ipp_attribute_t *uri) /* I - Printer or class URI */
8987{
8988 http_status_t status; /* Policy status */
bc44d920 8989 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 8990 cupsd_printer_t *printer; /* Printer data */
8991 ipp_attribute_t *attr; /* printer-state-message text */
8992
8993
8994 cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", con,
8995 con->http.fd, uri->values[0].string.text);
8996
8997 /*
8998 * Is the destination valid?
8999 */
9000
f7deaa1a 9001 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 9002 {
9003 /*
9004 * Bad URI...
9005 */
9006
9007 send_ipp_status(con, IPP_NOT_FOUND,
9008 _("The printer or class was not found."));
9009 return;
9010 }
9011
9012 /*
9013 * Check policy...
9014 */
9015
9016 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
9017 {
f899b121 9018 send_http_error(con, status, printer);
ef416fc2 9019 return;
9020 }
9021
9022 /*
9023 * Reject jobs sent to the printer...
9024 */
9025
9026 printer->accepting = 0;
9027
9028 if ((attr = ippFindAttribute(con->request, "printer-state-message",
9029 IPP_TAG_TEXT)) == NULL)
9030 strcpy(printer->state_message, "Rejecting Jobs");
9031 else
9032 strlcpy(printer->state_message, attr->values[0].string.text,
9033 sizeof(printer->state_message));
9034
9035 cupsdAddPrinterHistory(printer);
9036
9037 if (dtype & CUPS_PRINTER_CLASS)
9038 {
3dfe78b3 9039 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
ef416fc2 9040
9041 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
f7deaa1a 9042 printer->name, get_username(con));
ef416fc2 9043 }
9044 else
9045 {
3dfe78b3 9046 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 9047
9048 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
f7deaa1a 9049 printer->name, get_username(con));
ef416fc2 9050 }
9051
9052 /*
9053 * Everything was ok, so return OK status...
9054 */
9055
9056 con->response->request.status.status_code = IPP_OK;
9057}
9058
9059
61cf44e2
MS
9060/*
9061 * 'release_held_new_jobs()' - Release pending/new jobs on a printer or class.
9062 */
9063
9064static void
9065release_held_new_jobs(
9066 cupsd_client_t *con, /* I - Connection */
9067 ipp_attribute_t *uri) /* I - Printer URI */
9068{
9069 http_status_t status; /* Policy status */
9070 cups_ptype_t dtype; /* Destination type (printer/class) */
9071 cupsd_printer_t *printer; /* Printer data */
9072
9073
9074 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_held_new_jobs(%p[%d], %s)", con,
9075 con->http.fd, uri->values[0].string.text);
9076
9077 /*
9078 * Is the destination valid?
9079 */
9080
9081 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
9082 {
9083 /*
9084 * Bad URI...
9085 */
9086
9087 send_ipp_status(con, IPP_NOT_FOUND,
9088 _("The printer or class was not found."));
9089 return;
9090 }
9091
9092 /*
9093 * Check policy...
9094 */
9095
9096 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
9097 {
9098 send_http_error(con, status, printer);
9099 return;
9100 }
9101
9102 /*
9103 * Hold pending/new jobs sent to the printer...
9104 */
9105
9106 printer->holding_new_jobs = 0;
9107
9108 cupsdSetPrinterReasons(printer, "-hold-new-jobs");
9109 cupsdAddPrinterHistory(printer);
9110
9111 if (dtype & CUPS_PRINTER_CLASS)
9112 cupsdLogMessage(CUPSD_LOG_INFO,
9113 "Class \"%s\" now printing pending/new jobs (\"%s\").",
9114 printer->name, get_username(con));
9115 else
9116 cupsdLogMessage(CUPSD_LOG_INFO,
9117 "Printer \"%s\" now printing pending/new jobs (\"%s\").",
9118 printer->name, get_username(con));
9119
9120 /*
9121 * Everything was ok, so return OK status...
9122 */
9123
9124 con->response->request.status.status_code = IPP_OK;
9125}
9126
9127
ef416fc2 9128/*
9129 * 'release_job()' - Release a held print job.
9130 */
9131
9132static void
9133release_job(cupsd_client_t *con, /* I - Client connection */
9134 ipp_attribute_t *uri) /* I - Job or Printer URI */
9135{
9136 ipp_attribute_t *attr; /* Current attribute */
9137 int jobid; /* Job ID */
61cf44e2 9138 char scheme[HTTP_MAX_URI], /* Method portion of URI */
ef416fc2 9139 username[HTTP_MAX_URI], /* Username portion of URI */
9140 host[HTTP_MAX_URI], /* Host portion of URI */
9141 resource[HTTP_MAX_URI]; /* Resource portion of URI */
9142 int port; /* Port portion of URI */
9143 cupsd_job_t *job; /* Job information */
9144
9145
9146 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", con,
9147 con->http.fd, uri->values[0].string.text);
9148
9149 /*
9150 * See if we have a job URI or a printer URI...
9151 */
9152
9153 if (!strcmp(uri->name, "printer-uri"))
9154 {
9155 /*
9156 * Got a printer URI; see if we also have a job-id attribute...
9157 */
9158
fa73b229 9159 if ((attr = ippFindAttribute(con->request, "job-id",
9160 IPP_TAG_INTEGER)) == NULL)
ef416fc2 9161 {
9162 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 9163 _("Got a printer-uri attribute but no job-id"));
ef416fc2 9164 return;
9165 }
9166
9167 jobid = attr->values[0].integer;
9168 }
9169 else
9170 {
9171 /*
9172 * Got a job URI; parse it to get the job ID...
9173 */
9174
61cf44e2
MS
9175 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9176 sizeof(scheme), username, sizeof(username), host,
a4d04587 9177 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 9178
9179 if (strncmp(resource, "/jobs/", 6))
9180 {
9181 /*
9182 * Not a valid URI!
9183 */
9184
9185 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 9186 _("Bad job-uri attribute \"%s\""),
ef416fc2 9187 uri->values[0].string.text);
9188 return;
9189 }
9190
9191 jobid = atoi(resource + 6);
9192 }
9193
9194 /*
9195 * See if the job exists...
9196 */
9197
9198 if ((job = cupsdFindJob(jobid)) == NULL)
9199 {
9200 /*
9201 * Nope - return a "not found" error...
9202 */
9203
4d301e69 9204 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
ef416fc2 9205 return;
9206 }
9207
9208 /*
9209 * See if job is "held"...
9210 */
9211
bd7854cb 9212 if (job->state_value != IPP_JOB_HELD)
ef416fc2 9213 {
9214 /*
9215 * Nope - return a "not possible" error...
9216 */
9217
4d301e69 9218 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held"), jobid);
ef416fc2 9219 return;
9220 }
9221
9222 /*
9223 * See if the job is owned by the requesting user...
9224 */
9225
9226 if (!validate_user(job, con, job->username, username, sizeof(username)))
9227 {
b0f6947b
MS
9228 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9229 cupsdFindDest(job->dest));
ef416fc2 9230 return;
9231 }
9232
9233 /*
9234 * Reset the job-hold-until value to "no-hold"...
9235 */
9236
fa73b229 9237 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9238 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 9239 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9240
fa73b229 9241 if (attr)
ef416fc2 9242 {
757d2cad 9243 _cupsStrFree(attr->values[0].string.text);
4400e98d 9244
ef416fc2 9245 attr->value_tag = IPP_TAG_KEYWORD;
757d2cad 9246 attr->values[0].string.text = _cupsStrAlloc("no-hold");
d09495fa 9247
01ce6322 9248 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
d09495fa 9249 "Job job-hold-until value changed by user.");
ef416fc2 9250 }
9251
9252 /*
9253 * Release the job and return...
9254 */
9255
9256 cupsdReleaseJob(job);
9257
01ce6322 9258 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
d09495fa 9259 "Job released by user.");
9260
75bd9771 9261 cupsdLogJob(job, CUPSD_LOG_INFO, "Released by \"%s\".", username);
ef416fc2 9262
9263 con->response->request.status.status_code = IPP_OK;
005dd1eb
MS
9264
9265 cupsdCheckJobs();
ef416fc2 9266}
9267
9268
9269/*
9270 * 'renew_subscription()' - Renew an existing subscription...
9271 */
9272
9273static void
9274renew_subscription(
9275 cupsd_client_t *con, /* I - Client connection */
9276 int sub_id) /* I - Subscription ID */
9277{
bd7854cb 9278 http_status_t status; /* Policy status */
9279 cupsd_subscription_t *sub; /* Subscription */
9280 ipp_attribute_t *lease; /* notify-lease-duration */
9281
9282
9283 cupsdLogMessage(CUPSD_LOG_DEBUG2,
9284 "renew_subscription(con=%p[%d], sub_id=%d)",
9285 con, con->http.fd, sub_id);
9286
9287 /*
9288 * Is the subscription ID valid?
9289 */
9290
9291 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
9292 {
9293 /*
9294 * Bad subscription ID...
9295 */
9296
9297 send_ipp_status(con, IPP_NOT_FOUND,
4d301e69 9298 _("notify-subscription-id %d no good"), sub_id);
bd7854cb 9299 return;
9300 }
9301
9302 if (sub->job)
9303 {
9304 /*
9305 * Job subscriptions cannot be renewed...
9306 */
9307
9308 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 9309 _("Job subscriptions cannot be renewed"));
bd7854cb 9310 return;
9311 }
9312
9313 /*
9314 * Check policy...
9315 */
9316
9317 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
9318 DefaultPolicyPtr,
9319 con, sub->owner)) != HTTP_OK)
9320 {
f899b121 9321 send_http_error(con, status, sub->dest);
bd7854cb 9322 return;
9323 }
9324
9325 /*
9326 * Renew the subscription...
9327 */
9328
9329 lease = ippFindAttribute(con->request, "notify-lease-duration",
9330 IPP_TAG_INTEGER);
9331
9332 sub->lease = lease ? lease->values[0].integer : DefaultLeaseDuration;
9333
9334 if (MaxLeaseDuration && (sub->lease == 0 || sub->lease > MaxLeaseDuration))
9335 {
9336 cupsdLogMessage(CUPSD_LOG_INFO,
9337 "renew_subscription: Limiting notify-lease-duration to "
9338 "%d seconds.",
9339 MaxLeaseDuration);
9340 sub->lease = MaxLeaseDuration;
9341 }
9342
9343 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
9344
3dfe78b3 9345 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
bd7854cb 9346
9347 con->response->request.status.status_code = IPP_OK;
b94498cf 9348
9349 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
9350 "notify-lease-duration", sub->lease);
ef416fc2 9351}
9352
9353
9354/*
9355 * 'restart_job()' - Restart an old print job.
9356 */
9357
9358static void
9359restart_job(cupsd_client_t *con, /* I - Client connection */
9360 ipp_attribute_t *uri) /* I - Job or Printer URI */
9361{
9362 ipp_attribute_t *attr; /* Current attribute */
9363 int jobid; /* Job ID */
238c3832 9364 cupsd_job_t *job; /* Job information */
61cf44e2 9365 char scheme[HTTP_MAX_URI], /* Method portion of URI */
ef416fc2 9366 username[HTTP_MAX_URI], /* Username portion of URI */
9367 host[HTTP_MAX_URI], /* Host portion of URI */
9368 resource[HTTP_MAX_URI]; /* Resource portion of URI */
9369 int port; /* Port portion of URI */
ef416fc2 9370
9371
9372 cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con,
9373 con->http.fd, uri->values[0].string.text);
9374
9375 /*
9376 * See if we have a job URI or a printer URI...
9377 */
9378
9379 if (!strcmp(uri->name, "printer-uri"))
9380 {
9381 /*
9382 * Got a printer URI; see if we also have a job-id attribute...
9383 */
9384
fa73b229 9385 if ((attr = ippFindAttribute(con->request, "job-id",
9386 IPP_TAG_INTEGER)) == NULL)
ef416fc2 9387 {
9388 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 9389 _("Got a printer-uri attribute but no job-id"));
ef416fc2 9390 return;
9391 }
9392
9393 jobid = attr->values[0].integer;
9394 }
9395 else
9396 {
9397 /*
9398 * Got a job URI; parse it to get the job ID...
9399 */
9400
61cf44e2
MS
9401 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9402 sizeof(scheme), username, sizeof(username), host,
a4d04587 9403 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 9404
fa73b229 9405 if (strncmp(resource, "/jobs/", 6))
ef416fc2 9406 {
9407 /*
9408 * Not a valid URI!
9409 */
9410
9411 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 9412 _("Bad job-uri attribute \"%s\""),
ef416fc2 9413 uri->values[0].string.text);
9414 return;
9415 }
9416
9417 jobid = atoi(resource + 6);
9418 }
9419
9420 /*
9421 * See if the job exists...
9422 */
9423
9424 if ((job = cupsdFindJob(jobid)) == NULL)
9425 {
9426 /*
9427 * Nope - return a "not found" error...
9428 */
9429
4d301e69 9430 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
ef416fc2 9431 return;
9432 }
9433
9434 /*
9435 * See if job is in any of the "completed" states...
9436 */
9437
bd7854cb 9438 if (job->state_value <= IPP_JOB_PROCESSING)
ef416fc2 9439 {
9440 /*
9441 * Nope - return a "not possible" error...
9442 */
9443
4d301e69 9444 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete"),
ef416fc2 9445 jobid);
9446 return;
9447 }
9448
9449 /*
9450 * See if we have retained the job files...
9451 */
9452
bd7854cb 9453 cupsdLoadJob(job);
9454
07725fee 9455 if (!job->attrs || job->num_files == 0)
ef416fc2 9456 {
9457 /*
9458 * Nope - return a "not possible" error...
9459 */
9460
9461 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 9462 _("Job #%d cannot be restarted - no files"), jobid);
ef416fc2 9463 return;
9464 }
9465
9466 /*
9467 * See if the job is owned by the requesting user...
9468 */
9469
9470 if (!validate_user(job, con, job->username, username, sizeof(username)))
9471 {
b0f6947b
MS
9472 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9473 cupsdFindDest(job->dest));
ef416fc2 9474 return;
9475 }
9476
9477 /*
238c3832 9478 * See if the job-hold-until attribute is specified...
ef416fc2 9479 */
9480
238c3832
MS
9481 if ((attr = ippFindAttribute(con->request, "job-hold-until",
9482 IPP_TAG_KEYWORD)) == NULL)
9483 attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
9484
9485 if (attr && strcmp(attr->values[0].string.text, "no-hold"))
9486 {
9487 /*
9488 * Return the job to a held state...
9489 */
9490
9491 cupsdLogJob(job, CUPSD_LOG_DEBUG,
9492 "Restarted by \"%s\" with job-hold-until=%s.",
9493 username, attr->values[0].string.text);
9494 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
9495
9496 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE,
9497 NULL, job, "Job restarted by user with job-hold-until=%s",
9498 attr->values[0].string.text);
9499 }
9500 else
9501 {
9502 /*
9503 * Restart the job...
9504 */
9505
9506 cupsdRestartJob(job);
f11a948a 9507 cupsdCheckJobs();
238c3832 9508 }
ef416fc2 9509
75bd9771 9510 cupsdLogJob(job, CUPSD_LOG_INFO, "Restarted by \"%s\".", username);
ef416fc2 9511
9512 con->response->request.status.status_code = IPP_OK;
9513}
9514
9515
9516/*
9517 * 'save_auth_info()' - Save authentication information for a job.
9518 */
9519
9520static void
f7deaa1a 9521save_auth_info(
9522 cupsd_client_t *con, /* I - Client connection */
9523 cupsd_job_t *job, /* I - Job */
9524 ipp_attribute_t *auth_info) /* I - auth-info attribute, if any */
ef416fc2 9525{
09a101d6 9526 int i; /* Looping var */
9527 char filename[1024]; /* Job authentication filename */
9528 cups_file_t *fp; /* Job authentication file */
9529 char line[2048]; /* Line for file */
9530 cupsd_printer_t *dest; /* Destination printer/class */
ef416fc2 9531
9532
9533 /*
9534 * This function saves the in-memory authentication information for
9535 * a job so that it can be used to authenticate with a remote host.
9536 * The information is stored in a file that is readable only by the
f7deaa1a 9537 * root user. The fields are Base-64 encoded, each on a separate line,
9538 * followed by random number (up to 1024) of newlines to limit the
9539 * amount of information that is exposed.
ef416fc2 9540 *
9541 * Because of the potential for exposing of authentication information,
9542 * this functionality is only enabled when running cupsd as root.
9543 *
9544 * This caching only works for the Basic and BasicDigest authentication
9545 * types. Digest authentication cannot be cached this way, and in
9546 * the future Kerberos authentication may make all of this obsolete.
9547 *
9548 * Authentication information is saved whenever an authenticated
9549 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
9550 * performed.
9551 *
9552 * This information is deleted after a job is completed or canceled,
9553 * so reprints may require subsequent re-authentication.
9554 */
9555
9556 if (RunUser)
9557 return;
9558
09a101d6 9559 if ((dest = cupsdFindDest(job->dest)) == NULL)
9560 return;
9561
ef416fc2 9562 /*
9563 * Create the authentication file and change permissions...
9564 */
9565
9566 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
9567 if ((fp = cupsFileOpen(filename, "w")) == NULL)
9568 {
9569 cupsdLogMessage(CUPSD_LOG_ERROR,
9570 "Unable to save authentication info to \"%s\" - %s",
9571 filename, strerror(errno));
9572 return;
9573 }
9574
9575 fchown(cupsFileNumber(fp), 0, 0);
9576 fchmod(cupsFileNumber(fp), 0400);
9577
09a101d6 9578 if (auth_info && auth_info->num_values == dest->num_auth_info_required)
f7deaa1a 9579 {
9580 /*
09a101d6 9581 * Write 1 to 3 auth values...
f7deaa1a 9582 */
ef416fc2 9583
09a101d6 9584 cupsdClearString(&job->auth_username);
9585 cupsdClearString(&job->auth_domain);
9586 cupsdClearString(&job->auth_password);
9587
f7deaa1a 9588 for (i = 0; i < auth_info->num_values; i ++)
9589 {
9590 httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text,
9591 strlen(auth_info->values[i].string.text));
9592 cupsFilePrintf(fp, "%s\n", line);
09a101d6 9593
9594 if (!strcmp(dest->auth_info_required[i], "username"))
839a51c8 9595 cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s",
09a101d6 9596 auth_info->values[i].string.text);
9597 else if (!strcmp(dest->auth_info_required[i], "domain"))
839a51c8 9598 cupsdSetStringf(&job->auth_domain, "AUTH_DOMAIN=%s",
09a101d6 9599 auth_info->values[i].string.text);
9600 else if (!strcmp(dest->auth_info_required[i], "password"))
839a51c8 9601 cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s",
09a101d6 9602 auth_info->values[i].string.text);
f7deaa1a 9603 }
9604 }
09a101d6 9605 else if (con->username[0])
f7deaa1a 9606 {
9607 /*
9608 * Write the authenticated username...
9609 */
ef416fc2 9610
f7deaa1a 9611 httpEncode64_2(line, sizeof(line), con->username, strlen(con->username));
9612 cupsFilePrintf(fp, "%s\n", line);
ef416fc2 9613
839a51c8 9614 cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s", con->username);
09a101d6 9615 cupsdClearString(&job->auth_domain);
9616
f7deaa1a 9617 /*
9618 * Write the authenticated password...
9619 */
9620
9621 httpEncode64_2(line, sizeof(line), con->password, strlen(con->password));
9622 cupsFilePrintf(fp, "%s\n", line);
09a101d6 9623
839a51c8 9624 cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s", con->password);
f7deaa1a 9625 }
ef416fc2 9626
9627 /*
9628 * Write a random number of newlines to the end of the file...
9629 */
9630
9631 for (i = (rand() % 1024); i >= 0; i --)
9632 cupsFilePutChar(fp, '\n');
9633
9634 /*
9635 * Close the file and return...
9636 */
9637
9638 cupsFileClose(fp);
f7deaa1a 9639
9640#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
68b10830 9641# ifdef HAVE_KRB5_IPC_CLIENT_SET_TARGET_UID
97c9a8d7
MS
9642 if (con->have_gss &&
9643 (con->http.hostaddr->addr.sa_family == AF_LOCAL || con->gss_creds))
68b10830 9644# else
97c9a8d7 9645 if (con->have_gss && con->gss_creds)
68b10830 9646# endif /* HAVE_KRB5_IPC_CLIENT_SET_TARGET_UID */
09a101d6 9647 save_krb5_creds(con, job);
9648 else if (job->ccname)
9649 cupsdClearString(&(job->ccname));
f7deaa1a 9650#endif /* HAVE_GSSAPI && HAVE_KRB5_H */
9651}
9652
9653
9654#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
9655/*
9656 * 'save_krb5_creds()' - Save Kerberos credentials for the job.
9657 */
9658
9659static void
9660save_krb5_creds(cupsd_client_t *con, /* I - Client connection */
9661 cupsd_job_t *job) /* I - Job */
9662{
f7deaa1a 9663 /*
e07d4801 9664 * Get the credentials...
f7deaa1a 9665 */
9666
e07d4801 9667 job->ccache = cupsdCopyKrb5Creds(con);
f7deaa1a 9668
7ff4fea9
MS
9669 /*
9670 * Add the KRB5CCNAME environment variable to the job so that the
9671 * backend can use the credentials when printing.
9672 */
9673
e07d4801
MS
9674 if (job->ccache)
9675 {
9676 cupsdSetStringf(&(job->ccname), "KRB5CCNAME=FILE:%s",
9677 krb5_cc_get_name(KerberosContext, job->ccache));
cc0d019f 9678
e07d4801
MS
9679 cupsdLogJob(job, CUPSD_LOG_DEBUG2, "save_krb5_creds: %s", job->ccname);
9680 }
9681 else
9682 cupsdClearString(&(job->ccname));
ef416fc2 9683}
f7deaa1a 9684#endif /* HAVE_GSSAPI && HAVE_KRB5_H */
ef416fc2 9685
9686
9687/*
9688 * 'send_document()' - Send a file to a printer or class.
9689 */
9690
9691static void
9692send_document(cupsd_client_t *con, /* I - Client connection */
9693 ipp_attribute_t *uri) /* I - Printer URI */
9694{
9695 ipp_attribute_t *attr; /* Current attribute */
a0f6818e
MS
9696 ipp_attribute_t *format; /* Request's document-format attribute */
9697 ipp_attribute_t *jformat; /* Job's document-format attribute */
f7deaa1a 9698 const char *default_format;/* document-format-default value */
ef416fc2 9699 int jobid; /* Job ID number */
9700 cupsd_job_t *job; /* Current job */
9701 char job_uri[HTTP_MAX_URI],
9702 /* Job URI */
61cf44e2 9703 scheme[HTTP_MAX_URI],
ef416fc2 9704 /* Method portion of URI */
9705 username[HTTP_MAX_URI],
9706 /* Username portion of URI */
9707 host[HTTP_MAX_URI],
9708 /* Host portion of URI */
9709 resource[HTTP_MAX_URI];
9710 /* Resource portion of URI */
9711 int port; /* Port portion of URI */
9712 mime_type_t *filetype; /* Type of file */
9713 char super[MIME_MAX_SUPER],
9714 /* Supertype of file */
9715 type[MIME_MAX_TYPE],
9716 /* Subtype of file */
9717 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
9718 /* Textual name of mime type */
9719 char filename[1024]; /* Job filename */
9720 cupsd_printer_t *printer; /* Current printer */
9721 struct stat fileinfo; /* File information */
9722 int kbytes; /* Size of file */
9723 int compression; /* Type of compression */
75bd9771 9724 int start_job; /* Start the job? */
ef416fc2 9725
9726
9727 cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con,
9728 con->http.fd, uri->values[0].string.text);
9729
9730 /*
9731 * See if we have a job URI or a printer URI...
9732 */
9733
9734 if (!strcmp(uri->name, "printer-uri"))
9735 {
9736 /*
9737 * Got a printer URI; see if we also have a job-id attribute...
9738 */
9739
fa73b229 9740 if ((attr = ippFindAttribute(con->request, "job-id",
9741 IPP_TAG_INTEGER)) == NULL)
ef416fc2 9742 {
9743 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 9744 _("Got a printer-uri attribute but no job-id"));
ef416fc2 9745 return;
9746 }
9747
9748 jobid = attr->values[0].integer;
9749 }
9750 else
9751 {
9752 /*
9753 * Got a job URI; parse it to get the job ID...
9754 */
9755
61cf44e2
MS
9756 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9757 sizeof(scheme), username, sizeof(username), host,
a4d04587 9758 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 9759
9760 if (strncmp(resource, "/jobs/", 6))
9761 {
9762 /*
9763 * Not a valid URI!
9764 */
9765
9766 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 9767 _("Bad job-uri attribute \"%s\""),
ef416fc2 9768 uri->values[0].string.text);
9769 return;
9770 }
9771
9772 jobid = atoi(resource + 6);
9773 }
9774
9775 /*
9776 * See if the job exists...
9777 */
9778
9779 if ((job = cupsdFindJob(jobid)) == NULL)
9780 {
9781 /*
9782 * Nope - return a "not found" error...
9783 */
9784
4d301e69 9785 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
ef416fc2 9786 return;
9787 }
9788
f301802f 9789 printer = cupsdFindDest(job->dest);
9790
ef416fc2 9791 /*
9792 * See if the job is owned by the requesting user...
9793 */
9794
9795 if (!validate_user(job, con, job->username, username, sizeof(username)))
9796 {
b0f6947b
MS
9797 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9798 cupsdFindDest(job->dest));
ef416fc2 9799 return;
9800 }
9801
9802 /*
9803 * OK, see if the client is sending the document compressed - CUPS
9804 * only supports "none" and "gzip".
9805 */
9806
9807 compression = CUPS_FILE_NONE;
9808
fa73b229 9809 if ((attr = ippFindAttribute(con->request, "compression",
9810 IPP_TAG_KEYWORD)) != NULL)
ef416fc2 9811 {
9812 if (strcmp(attr->values[0].string.text, "none")
9813#ifdef HAVE_LIBZ
9814 && strcmp(attr->values[0].string.text, "gzip")
9815#endif /* HAVE_LIBZ */
9816 )
9817 {
4d301e69 9818 send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\""),
ef416fc2 9819 attr->values[0].string.text);
9820 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
9821 "compression", NULL, attr->values[0].string.text);
9822 return;
9823 }
9824
9825#ifdef HAVE_LIBZ
9826 if (!strcmp(attr->values[0].string.text, "gzip"))
9827 compression = CUPS_FILE_GZIP;
9828#endif /* HAVE_LIBZ */
9829 }
9830
9831 /*
9832 * Do we have a file to print?
9833 */
9834
9835 if (!con->filename)
9836 {
4d301e69 9837 send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?"));
ef416fc2 9838 return;
9839 }
9840
9841 /*
9842 * Is it a format we support?
9843 */
9844
9845 if ((format = ippFindAttribute(con->request, "document-format",
9846 IPP_TAG_MIMETYPE)) != NULL)
9847 {
9848 /*
9849 * Grab format from client...
9850 */
9851
bc44d920 9852 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
9853 super, type) != 2)
ef416fc2 9854 {
4d301e69 9855 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\""),
ef416fc2 9856 format->values[0].string.text);
9857 return;
9858 }
9859 }
f7deaa1a 9860 else if ((default_format = cupsGetOption("document-format",
9861 printer->num_options,
9862 printer->options)) != NULL)
9863 {
9864 /*
9865 * Use default document format...
9866 */
9867
9868 if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
9869 {
9870 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 9871 _("Could not scan type \"%s\""),
f7deaa1a 9872 default_format);
9873 return;
9874 }
9875 }
ef416fc2 9876 else
9877 {
9878 /*
9879 * No document format attribute? Auto-type it!
9880 */
9881
9882 strcpy(super, "application");
9883 strcpy(type, "octet-stream");
9884 }
9885
fa73b229 9886 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
ef416fc2 9887 {
9888 /*
9889 * Auto-type the file...
9890 */
9891
bd7854cb 9892 ipp_attribute_t *doc_name; /* document-name attribute */
9893
9894
75bd9771 9895 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Auto-typing file...");
ef416fc2 9896
bd7854cb 9897 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
9898 filetype = mimeFileType(MimeDatabase, con->filename,
9899 doc_name ? doc_name->values[0].string.text : NULL,
9900 &compression);
ef416fc2 9901
f7deaa1a 9902 if (!filetype)
f7faf1f5 9903 filetype = mimeType(MimeDatabase, super, type);
a0f6818e 9904
75bd9771
MS
9905 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Request file type is %s/%s.",
9906 filetype->super, filetype->type);
ed486911 9907 }
f7faf1f5 9908 else
9909 filetype = mimeType(MimeDatabase, super, type);
9910
49d87452 9911 if (filetype)
f7deaa1a 9912 {
9913 /*
9914 * Replace the document-format attribute value with the auto-typed or
9915 * default one.
9916 */
9917
9918 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9919 filetype->type);
9920
49d87452
MS
9921 if ((jformat = ippFindAttribute(job->attrs, "document-format",
9922 IPP_TAG_MIMETYPE)) != NULL)
f7deaa1a 9923 {
a0f6818e 9924 _cupsStrFree(jformat->values[0].string.text);
f7deaa1a 9925
a0f6818e 9926 jformat->values[0].string.text = _cupsStrAlloc(mimetype);
f7deaa1a 9927 }
9928 else
a0f6818e 9929 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
f7deaa1a 9930 "document-format", NULL, mimetype);
9931 }
9932 else if (!filetype)
ef416fc2 9933 {
9934 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
4d301e69 9935 _("Unsupported format \'%s/%s\'"), super, type);
ef416fc2 9936 cupsdLogMessage(CUPSD_LOG_INFO,
9937 "Hint: Do you have the raw file printing rules enabled?");
9938
9939 if (format)
9940 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9941 "document-format", NULL, format->values[0].string.text);
9942
9943 return;
9944 }
9945
80ca4592 9946 if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
9947 {
9948 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9949 filetype->type);
9950
9951 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
4d301e69 9952 _("Unsupported format \'%s\'"), mimetype);
80ca4592 9953
9954 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9955 "document-format", NULL, mimetype);
9956
9957 return;
9958 }
9959
ef416fc2 9960 /*
9961 * Add the file to the job...
9962 */
9963
bd7854cb 9964 cupsdLoadJob(job);
9965
ef416fc2 9966 if (add_file(con, job, filetype, compression))
9967 return;
9968
ef416fc2 9969 if (stat(con->filename, &fileinfo))
9970 kbytes = 0;
9971 else
9972 kbytes = (fileinfo.st_size + 1023) / 1024;
9973
9974 cupsdUpdateQuota(printer, job->username, 0, kbytes);
9975
fa73b229 9976 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
9977 IPP_TAG_INTEGER)) != NULL)
ef416fc2 9978 attr->values[0].integer += kbytes;
9979
9980 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
9981 job->num_files);
9982 rename(con->filename, filename);
9983
9984 cupsdClearString(&con->filename);
9985
75bd9771
MS
9986 cupsdLogJob(job, CUPSD_LOG_INFO, "File of type %s/%s queued by \"%s\".",
9987 filetype->super, filetype->type, job->username);
ef416fc2 9988
9989 /*
9990 * Start the job if this is the last document...
9991 */
9992
fa73b229 9993 if ((attr = ippFindAttribute(con->request, "last-document",
9994 IPP_TAG_BOOLEAN)) != NULL &&
ef416fc2 9995 attr->values[0].boolean)
9996 {
9997 /*
9998 * See if we need to add the ending sheet...
9999 */
10000
91c84a35
MS
10001 if (cupsdTimeoutJob(job))
10002 return;
ef416fc2 10003
bd7854cb 10004 if (job->state_value == IPP_JOB_STOPPED)
10005 {
ef416fc2 10006 job->state->values[0].integer = IPP_JOB_PENDING;
bd7854cb 10007 job->state_value = IPP_JOB_PENDING;
10008 }
10009 else if (job->state_value == IPP_JOB_HELD)
ef416fc2 10010 {
fa73b229 10011 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
10012 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 10013 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
10014
fa73b229 10015 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
bd7854cb 10016 {
ef416fc2 10017 job->state->values[0].integer = IPP_JOB_PENDING;
bd7854cb 10018 job->state_value = IPP_JOB_PENDING;
10019 }
ef416fc2 10020 }
10021
3dfe78b3
MS
10022 job->dirty = 1;
10023 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 10024
75bd9771 10025 start_job = 1;
ef416fc2 10026 }
10027 else
10028 {
fa73b229 10029 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
10030 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 10031 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
10032
fa73b229 10033 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
ef416fc2 10034 {
10035 job->state->values[0].integer = IPP_JOB_HELD;
bd7854cb 10036 job->state_value = IPP_JOB_HELD;
dfd5680b 10037 job->hold_until = time(NULL) + MultipleOperationTimeout;
3dfe78b3
MS
10038 job->dirty = 1;
10039
10040 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 10041 }
75bd9771
MS
10042
10043 start_job = 0;
ef416fc2 10044 }
10045
10046 /*
10047 * Fill in the response info...
10048 */
10049
10050 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
10051 LocalPort, jobid);
10052
10053 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
10054 job_uri);
10055
10056 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
10057
10058 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
75bd9771 10059 job->state_value);
ef416fc2 10060 add_job_state_reasons(con, job);
10061
10062 con->response->request.status.status_code = IPP_OK;
75bd9771
MS
10063
10064 /*
10065 * Start the job if necessary...
10066 */
10067
10068 if (start_job)
10069 cupsdCheckJobs();
ef416fc2 10070}
10071
10072
10073/*
10074 * 'send_http_error()' - Send a HTTP error back to the IPP client.
10075 */
10076
10077static void
f899b121 10078send_http_error(
10079 cupsd_client_t *con, /* I - Client connection */
10080 http_status_t status, /* I - HTTP status code */
10081 cupsd_printer_t *printer) /* I - Printer, if any */
ef416fc2 10082{
9f5eb9be
MS
10083 ipp_attribute_t *uri; /* Request URI, if any */
10084
10085
10086 if ((uri = ippFindAttribute(con->request, "printer-uri",
10087 IPP_TAG_URI)) == NULL)
10088 uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI);
10089
10090 cupsdLogMessage(status == HTTP_FORBIDDEN ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
10091 "Returning HTTP %s for %s (%s) from %s",
10092 httpStatus(status),
10093 ippOpString(con->request->request.op.operation_id),
10094 uri ? uri->values[0].string.text : "no URI",
10095 con->http.hostname);
ef416fc2 10096
5f64df29
MS
10097 if (printer)
10098 {
db0bd74a 10099 int auth_type; /* Type of authentication required */
2fb76298
MS
10100
10101
5f64df29
MS
10102 auth_type = CUPSD_AUTH_NONE;
10103
10104 if (status == HTTP_UNAUTHORIZED &&
10105 printer->num_auth_info_required > 0 &&
10106 !strcmp(printer->auth_info_required[0], "negotiate") &&
10107 con->request &&
10108 (con->request->request.op.operation_id == IPP_PRINT_JOB ||
10109 con->request->request.op.operation_id == IPP_CREATE_JOB ||
10110 con->request->request.op.operation_id == CUPS_AUTHENTICATE_JOB))
10111 {
10112 /*
10113 * Creating and authenticating jobs requires Kerberos...
10114 */
10115
10116 auth_type = CUPSD_AUTH_NEGOTIATE;
10117 }
db0bd74a 10118 else
5f64df29
MS
10119 {
10120 /*
10121 * Use policy/location-defined authentication requirements...
10122 */
10123
10124 char resource[HTTP_MAX_URI]; /* Resource portion of URI */
10125 cupsd_location_t *auth; /* Pointer to authentication element */
10126
10127
10128 if (printer->type & CUPS_PRINTER_CLASS)
10129 snprintf(resource, sizeof(resource), "/classes/%s", printer->name);
10130 else
10131 snprintf(resource, sizeof(resource), "/printers/%s", printer->name);
10132
10133 if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
10134 auth->type == CUPSD_AUTH_NONE)
10135 auth = cupsdFindPolicyOp(printer->op_policy_ptr,
10136 con->request ?
10137 con->request->request.op.operation_id :
10138 IPP_PRINT_JOB);
10139
10140 if (auth)
10141 {
10142 if (auth->type == CUPSD_AUTH_DEFAULT)
10143 auth_type = DefaultAuthType;
10144 else
10145 auth_type = auth->type;
10146 }
10147 }
2fb76298 10148
db0bd74a 10149 cupsdSendError(con, status, auth_type);
2fb76298 10150 }
f899b121 10151 else
5bd77a73 10152 cupsdSendError(con, status, CUPSD_AUTH_NONE);
ef416fc2 10153
10154 ippDelete(con->response);
10155 con->response = NULL;
10156
10157 return;
10158}
10159
10160
10161/*
10162 * 'send_ipp_status()' - Send a status back to the IPP client.
10163 */
10164
10165static void
10166send_ipp_status(cupsd_client_t *con, /* I - Client connection */
1f0275e3
MS
10167 ipp_status_t status, /* I - IPP status code */
10168 const char *message,/* I - Status message */
10169 ...) /* I - Additional args as needed */
ef416fc2 10170{
10171 va_list ap; /* Pointer to additional args */
10172 char formatted[1024]; /* Formatted errror message */
10173
10174
3d8365b8 10175 va_start(ap, message);
10176 vsnprintf(formatted, sizeof(formatted),
10177 _cupsLangString(con->language, message), ap);
10178 va_end(ap);
ef416fc2 10179
3d8365b8 10180 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
10181 ippOpString(con->request->request.op.operation_id),
10182 ippErrorString(status), formatted);
ef416fc2 10183
10184 con->response->request.status.status_code = status;
10185
fa73b229 10186 if (ippFindAttribute(con->response, "attributes-charset",
10187 IPP_TAG_ZERO) == NULL)
ef416fc2 10188 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
10189 "attributes-charset", NULL, DefaultCharset);
10190
10191 if (ippFindAttribute(con->response, "attributes-natural-language",
10192 IPP_TAG_ZERO) == NULL)
10193 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
10194 "attributes-natural-language", NULL, DefaultLanguage);
10195
3d8365b8 10196 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
10197 "status-message", NULL, formatted);
ef416fc2 10198}
10199
10200
10201/*
10202 * 'set_default()' - Set the default destination...
10203 */
10204
10205static void
10206set_default(cupsd_client_t *con, /* I - Client connection */
10207 ipp_attribute_t *uri) /* I - Printer URI */
10208{
10209 http_status_t status; /* Policy status */
bc44d920 10210 cups_ptype_t dtype; /* Destination type (printer/class) */
dd1abb6b
MS
10211 cupsd_printer_t *printer, /* Printer */
10212 *oldprinter; /* Old default printer */
ef416fc2 10213
10214
10215 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con,
10216 con->http.fd, uri->values[0].string.text);
10217
10218 /*
10219 * Is the destination valid?
10220 */
10221
f7deaa1a 10222 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 10223 {
10224 /*
10225 * Bad URI...
10226 */
10227
10228 send_ipp_status(con, IPP_NOT_FOUND,
10229 _("The printer or class was not found."));
10230 return;
10231 }
10232
10233 /*
10234 * Check policy...
10235 */
10236
10237 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
10238 {
f899b121 10239 send_http_error(con, status, NULL);
ef416fc2 10240 return;
10241 }
10242
10243 /*
10244 * Set it as the default...
10245 */
10246
dd1abb6b 10247 oldprinter = DefaultPrinter;
ef416fc2 10248 DefaultPrinter = printer;
10249
dd1abb6b
MS
10250 if (oldprinter)
10251 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, oldprinter, NULL,
10252 "%s is no longer the default printer.", oldprinter->name);
10253
10254 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
10255 "%s is now the default printer.", printer->name);
10256
3dfe78b3
MS
10257 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS | CUPSD_DIRTY_CLASSES |
10258 CUPSD_DIRTY_REMOTE | CUPSD_DIRTY_PRINTCAP);
ef416fc2 10259
10260 cupsdLogMessage(CUPSD_LOG_INFO,
f7deaa1a 10261 "Default destination set to \"%s\" by \"%s\".",
10262 printer->name, get_username(con));
ef416fc2 10263
10264 /*
10265 * Everything was ok, so return OK status...
10266 */
10267
10268 con->response->request.status.status_code = IPP_OK;
10269}
10270
10271
10272/*
10273 * 'set_job_attrs()' - Set job attributes.
10274 */
10275
10276static void
10277set_job_attrs(cupsd_client_t *con, /* I - Client connection */
10278 ipp_attribute_t *uri) /* I - Job URI */
10279{
10280 ipp_attribute_t *attr, /* Current attribute */
10281 *attr2; /* Job attribute */
10282 int jobid; /* Job ID */
10283 cupsd_job_t *job; /* Current job */
61cf44e2 10284 char scheme[HTTP_MAX_URI],
ef416fc2 10285 /* Method portion of URI */
10286 username[HTTP_MAX_URI],
10287 /* Username portion of URI */
10288 host[HTTP_MAX_URI],
10289 /* Host portion of URI */
10290 resource[HTTP_MAX_URI];
10291 /* Resource portion of URI */
10292 int port; /* Port portion of URI */
d09495fa 10293 int event; /* Events? */
005dd1eb 10294 int check_jobs; /* Check jobs? */
ef416fc2 10295
10296
10297 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
10298 con->http.fd, uri->values[0].string.text);
10299
10300 /*
10301 * Start with "everything is OK" status...
10302 */
10303
10304 con->response->request.status.status_code = IPP_OK;
10305
10306 /*
10307 * See if we have a job URI or a printer URI...
10308 */
10309
fa73b229 10310 if (!strcmp(uri->name, "printer-uri"))
ef416fc2 10311 {
10312 /*
10313 * Got a printer URI; see if we also have a job-id attribute...
10314 */
10315
fa73b229 10316 if ((attr = ippFindAttribute(con->request, "job-id",
10317 IPP_TAG_INTEGER)) == NULL)
ef416fc2 10318 {
10319 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 10320 _("Got a printer-uri attribute but no job-id"));
ef416fc2 10321 return;
10322 }
10323
10324 jobid = attr->values[0].integer;
10325 }
10326 else
10327 {
10328 /*
10329 * Got a job URI; parse it to get the job ID...
10330 */
10331
61cf44e2
MS
10332 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
10333 sizeof(scheme), username, sizeof(username), host,
a4d04587 10334 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 10335
fa73b229 10336 if (strncmp(resource, "/jobs/", 6))
ef416fc2 10337 {
10338 /*
10339 * Not a valid URI!
10340 */
10341
10342 send_ipp_status(con, IPP_BAD_REQUEST,
4d301e69 10343 _("Bad job-uri attribute \"%s\""),
ef416fc2 10344 uri->values[0].string.text);
10345 return;
10346 }
10347
10348 jobid = atoi(resource + 6);
10349 }
10350
10351 /*
10352 * See if the job exists...
10353 */
10354
10355 if ((job = cupsdFindJob(jobid)) == NULL)
10356 {
10357 /*
10358 * Nope - return a "not found" error...
10359 */
10360
4d301e69 10361 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
ef416fc2 10362 return;
10363 }
10364
10365 /*
10366 * See if the job has been completed...
10367 */
10368
bd7854cb 10369 if (job->state_value > IPP_JOB_STOPPED)
ef416fc2 10370 {
10371 /*
10372 * Return a "not-possible" error...
10373 */
10374
10375 send_ipp_status(con, IPP_NOT_POSSIBLE,
4d301e69 10376 _("Job #%d is finished and cannot be altered"), jobid);
ef416fc2 10377 return;
10378 }
10379
10380 /*
10381 * See if the job is owned by the requesting user...
10382 */
10383
10384 if (!validate_user(job, con, job->username, username, sizeof(username)))
10385 {
b0f6947b
MS
10386 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
10387 cupsdFindDest(job->dest));
ef416fc2 10388 return;
10389 }
10390
10391 /*
10392 * See what the user wants to change.
10393 */
10394
bd7854cb 10395 cupsdLoadJob(job);
10396
005dd1eb
MS
10397 check_jobs = 0;
10398 event = 0;
d09495fa 10399
fa73b229 10400 for (attr = con->request->attrs; attr; attr = attr->next)
ef416fc2 10401 {
10402 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
10403 continue;
10404
10405 if (!strcmp(attr->name, "attributes-charset") ||
10406 !strcmp(attr->name, "attributes-natural-language") ||
10407 !strcmp(attr->name, "document-compression") ||
10408 !strcmp(attr->name, "document-format") ||
10409 !strcmp(attr->name, "job-detailed-status-messages") ||
10410 !strcmp(attr->name, "job-document-access-errors") ||
10411 !strcmp(attr->name, "job-id") ||
5d6412a9 10412 !strcmp(attr->name, "job-impressions-completed") ||
ef416fc2 10413 !strcmp(attr->name, "job-k-octets") ||
10414 !strcmp(attr->name, "job-originating-host-name") ||
10415 !strcmp(attr->name, "job-originating-user-name") ||
10416 !strcmp(attr->name, "job-printer-up-time") ||
10417 !strcmp(attr->name, "job-printer-uri") ||
10418 !strcmp(attr->name, "job-sheets") ||
10419 !strcmp(attr->name, "job-state-message") ||
10420 !strcmp(attr->name, "job-state-reasons") ||
10421 !strcmp(attr->name, "job-uri") ||
10422 !strcmp(attr->name, "number-of-documents") ||
10423 !strcmp(attr->name, "number-of-intervening-jobs") ||
10424 !strcmp(attr->name, "output-device-assigned") ||
10425 !strncmp(attr->name, "date-time-at-", 13) ||
ef416fc2 10426 !strncmp(attr->name, "job-k-octets", 12) ||
10427 !strncmp(attr->name, "job-media-sheets", 16) ||
10428 !strncmp(attr->name, "time-at-", 8))
10429 {
10430 /*
10431 * Read-only attrs!
10432 */
10433
10434 send_ipp_status(con, IPP_ATTRIBUTES_NOT_SETTABLE,
10435 _("%s cannot be changed."), attr->name);
10436
10437 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
10438 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
10439
10440 continue;
10441 }
10442
10443 if (!strcmp(attr->name, "job-priority"))
10444 {
10445 /*
10446 * Change the job priority...
10447 */
10448
10449 if (attr->value_tag != IPP_TAG_INTEGER)
10450 {
4d301e69 10451 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value"));
ef416fc2 10452
10453 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
10454 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
10455 }
bd7854cb 10456 else if (job->state_value >= IPP_JOB_PROCESSING)
ef416fc2 10457 {
10458 send_ipp_status(con, IPP_NOT_POSSIBLE,
10459 _("Job is completed and cannot be changed."));
10460 return;
10461 }
10462 else if (con->response->request.status.status_code == IPP_OK)
d09495fa 10463 {
005dd1eb
MS
10464 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-priority to %d",
10465 attr->values[0].integer);
ef416fc2 10466 cupsdSetJobPriority(job, attr->values[0].integer);
005dd1eb
MS
10467
10468 check_jobs = 1;
10469 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED |
10470 CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED;
d09495fa 10471 }
ef416fc2 10472 }
10473 else if (!strcmp(attr->name, "job-state"))
10474 {
10475 /*
10476 * Change the job state...
10477 */
10478
10479 if (attr->value_tag != IPP_TAG_ENUM)
10480 {
4d301e69 10481 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value"));
ef416fc2 10482
10483 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
10484 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
10485 }
10486 else
10487 {
10488 switch (attr->values[0].integer)
10489 {
10490 case IPP_JOB_PENDING :
10491 case IPP_JOB_HELD :
bd7854cb 10492 if (job->state_value > IPP_JOB_HELD)
ef416fc2 10493 {
10494 send_ipp_status(con, IPP_NOT_POSSIBLE,
10495 _("Job state cannot be changed."));
10496 return;
10497 }
10498 else if (con->response->request.status.status_code == IPP_OK)
bd7854cb 10499 {
005dd1eb
MS
10500 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
10501 attr->values[0].integer);
b9faaae1
MS
10502 cupsdSetJobState(job, attr->values[0].integer,
10503 CUPSD_JOB_DEFAULT,
10504 "Job state changed by \"%s\"", username);
005dd1eb 10505 check_jobs = 1;
bd7854cb 10506 }
ef416fc2 10507 break;
10508
10509 case IPP_JOB_PROCESSING :
10510 case IPP_JOB_STOPPED :
bd7854cb 10511 if (job->state_value != attr->values[0].integer)
ef416fc2 10512 {
10513 send_ipp_status(con, IPP_NOT_POSSIBLE,
10514 _("Job state cannot be changed."));
10515 return;
10516 }
10517 break;
10518
d09495fa 10519 case IPP_JOB_CANCELED :
ef416fc2 10520 case IPP_JOB_ABORTED :
10521 case IPP_JOB_COMPLETED :
bd7854cb 10522 if (job->state_value > IPP_JOB_PROCESSING)
ef416fc2 10523 {
10524 send_ipp_status(con, IPP_NOT_POSSIBLE,
10525 _("Job state cannot be changed."));
10526 return;
10527 }
10528 else if (con->response->request.status.status_code == IPP_OK)
005dd1eb
MS
10529 {
10530 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
10531 attr->values[0].integer);
b9faaae1
MS
10532 cupsdSetJobState(job, (ipp_jstate_t)attr->values[0].integer,
10533 CUPSD_JOB_DEFAULT,
10534 "Job state changed by \"%s\"", username);
005dd1eb
MS
10535 check_jobs = 1;
10536 }
ef416fc2 10537 break;
10538 }
10539 }
10540 }
10541 else if (con->response->request.status.status_code != IPP_OK)
10542 continue;
fa73b229 10543 else if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10544 IPP_TAG_ZERO)) != NULL)
ef416fc2 10545 {
10546 /*
10547 * Some other value; first free the old value...
10548 */
10549
10550 if (job->attrs->prev)
10551 job->attrs->prev->next = attr2->next;
10552 else
10553 job->attrs->attrs = attr2->next;
10554
10555 if (job->attrs->last == attr2)
10556 job->attrs->last = job->attrs->prev;
10557
757d2cad 10558 _ippFreeAttr(attr2);
ef416fc2 10559
10560 /*
10561 * Then copy the attribute...
10562 */
10563
10564 copy_attribute(job->attrs, attr, 0);
10565
10566 /*
10567 * See if the job-name or job-hold-until is being changed.
10568 */
10569
10570 if (!strcmp(attr->name, "job-hold-until"))
10571 {
005dd1eb
MS
10572 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-hold-until to %s",
10573 attr->values[0].string.text);
b9faaae1 10574 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
ef416fc2 10575
10576 if (!strcmp(attr->values[0].string.text, "no-hold"))
b9faaae1 10577 {
ef416fc2 10578 cupsdReleaseJob(job);
b9faaae1
MS
10579 check_jobs = 1;
10580 }
ef416fc2 10581 else
b9faaae1
MS
10582 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT,
10583 "Job held by \"%s\".", username);
d09495fa 10584
b9faaae1 10585 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
ef416fc2 10586 }
10587 }
10588 else if (attr->value_tag == IPP_TAG_DELETEATTR)
10589 {
10590 /*
10591 * Delete the attribute...
10592 */
10593
10594 if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10595 IPP_TAG_ZERO)) != NULL)
10596 {
10597 if (job->attrs->prev)
10598 job->attrs->prev->next = attr2->next;
10599 else
10600 job->attrs->attrs = attr2->next;
10601
10602 if (attr2 == job->attrs->last)
10603 job->attrs->last = job->attrs->prev;
10604
757d2cad 10605 _ippFreeAttr(attr2);
d09495fa 10606
10607 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
ef416fc2 10608 }
10609 }
10610 else
10611 {
10612 /*
10613 * Add new option by copying it...
10614 */
10615
10616 copy_attribute(job->attrs, attr, 0);
d09495fa 10617
10618 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
ef416fc2 10619 }
10620 }
10621
10622 /*
10623 * Save the job...
10624 */
10625
3dfe78b3
MS
10626 job->dirty = 1;
10627 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 10628
d09495fa 10629 /*
10630 * Send events as needed...
10631 */
10632
d9bca400 10633 if (event & CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED)
01ce6322
MS
10634 cupsdAddEvent(CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED,
10635 cupsdFindDest(job->dest), job,
d9bca400
MS
10636 "Job priority changed by user.");
10637
d09495fa 10638 if (event & CUPSD_EVENT_JOB_STATE)
01ce6322 10639 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
d09495fa 10640 job->state_value == IPP_JOB_HELD ?
10641 "Job held by user." : "Job restarted by user.");
10642
10643 if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
01ce6322 10644 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
d09495fa 10645 "Job options changed by user.");
10646
ef416fc2 10647 /*
10648 * Start jobs if possible...
10649 */
10650
005dd1eb
MS
10651 if (check_jobs)
10652 cupsdCheckJobs();
ef416fc2 10653}
10654
10655
c168a833
MS
10656/*
10657 * 'set_printer_attrs()' - Set printer attributes.
10658 */
10659
10660static void
10661set_printer_attrs(cupsd_client_t *con, /* I - Client connection */
10662 ipp_attribute_t *uri) /* I - Printer */
10663{
10664 http_status_t status; /* Policy status */
10665 cups_ptype_t dtype; /* Destination type (printer/class) */
10666 cupsd_printer_t *printer; /* Printer/class */
10667 ipp_attribute_t *attr; /* Printer attribute */
bf3816c7 10668 int changed = 0; /* Was anything changed? */
c168a833
MS
10669
10670
10671 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_attrs(%p[%d], %s)", con,
10672 con->http.fd, uri->values[0].string.text);
10673
10674 /*
10675 * Is the destination valid?
10676 */
10677
10678 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10679 {
10680 /*
10681 * Bad URI...
10682 */
10683
10684 send_ipp_status(con, IPP_NOT_FOUND,
10685 _("The printer or class was not found."));
10686 return;
10687 }
10688
10689 /*
10690 * Check policy...
10691 */
10692
10693 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10694 {
10695 send_http_error(con, status, printer);
10696 return;
10697 }
10698
10699 /*
10700 * Return a list of attributes that can be set via Set-Printer-Attributes.
10701 */
10702
10703 if ((attr = ippFindAttribute(con->request, "printer-location",
10704 IPP_TAG_TEXT)) != NULL)
10705 {
10706 cupsdSetString(&printer->location, attr->values[0].string.text);
10707 changed = 1;
10708 }
10709
10710 if ((attr = ippFindAttribute(con->request, "printer-info",
10711 IPP_TAG_TEXT)) != NULL)
10712 {
10713 cupsdSetString(&printer->info, attr->values[0].string.text);
10714 changed = 1;
10715 }
10716
10717 /*
10718 * Update the printer attributes and return...
10719 */
10720
10721 if (changed)
10722 {
10723 cupsdSetPrinterAttrs(printer);
10724 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
10725
10726 cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG, printer, NULL,
10727 "Printer \"%s\" description or location changed by \"%s\".",
10728 printer->name, get_username(con));
10729
10730 cupsdLogMessage(CUPSD_LOG_INFO,
10731 "Printer \"%s\" description or location changed by \"%s\".",
10732 printer->name, get_username(con));
10733 }
10734
10735 con->response->request.status.status_code = IPP_OK;
10736}
10737
10738
b423cd4c 10739/*
10740 * 'set_printer_defaults()' - Set printer default options from a request.
10741 */
10742
10743static void
10744set_printer_defaults(
10745 cupsd_client_t *con, /* I - Client connection */
10746 cupsd_printer_t *printer) /* I - Printer */
10747{
10748 int i; /* Looping var */
10749 ipp_attribute_t *attr; /* Current attribute */
10750 int namelen; /* Length of attribute name */
10751 char name[256], /* New attribute name */
10752 value[256]; /* String version of integer attrs */
10753
10754
10755 for (attr = con->request->attrs; attr; attr = attr->next)
10756 {
10757 /*
10758 * Skip non-printer attributes...
10759 */
10760
10761 if (attr->group_tag != IPP_TAG_PRINTER || !attr->name)
10762 continue;
10763
10764 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_defaults: %s", attr->name);
10765
10766 if (!strcmp(attr->name, "job-sheets-default"))
10767 {
10768 /*
10769 * Only allow keywords and names...
10770 */
10771
10772 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10773 continue;
10774
10775 /*
10776 * Only allow job-sheets-default to be set when running without a
10777 * system high classification level...
10778 */
10779
10780 if (Classification)
10781 continue;
10782
10783 cupsdSetString(&printer->job_sheets[0], attr->values[0].string.text);
10784
10785 if (attr->num_values > 1)
10786 cupsdSetString(&printer->job_sheets[1], attr->values[1].string.text);
10787 else
10788 cupsdSetString(&printer->job_sheets[1], "none");
10789 }
10790 else if (!strcmp(attr->name, "requesting-user-name-allowed"))
10791 {
10792 cupsdFreePrinterUsers(printer);
10793
10794 printer->deny_users = 0;
10795
10796 if (attr->value_tag == IPP_TAG_NAME &&
10797 (attr->num_values > 1 ||
10798 strcmp(attr->values[0].string.text, "all")))
10799 {
10800 for (i = 0; i < attr->num_values; i ++)
10801 cupsdAddPrinterUser(printer, attr->values[i].string.text);
10802 }
10803 }
10804 else if (!strcmp(attr->name, "requesting-user-name-denied"))
10805 {
10806 cupsdFreePrinterUsers(printer);
10807
10808 printer->deny_users = 1;
10809
10810 if (attr->value_tag == IPP_TAG_NAME &&
10811 (attr->num_values > 1 ||
10812 strcmp(attr->values[0].string.text, "none")))
10813 {
10814 for (i = 0; i < attr->num_values; i ++)
10815 cupsdAddPrinterUser(printer, attr->values[i].string.text);
10816 }
10817 }
10818 else if (!strcmp(attr->name, "job-quota-period"))
10819 {
10820 if (attr->value_tag != IPP_TAG_INTEGER)
10821 continue;
10822
10823 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-quota-period to %d...",
10824 attr->values[0].integer);
10825 cupsdFreeQuotas(printer);
10826
10827 printer->quota_period = attr->values[0].integer;
10828 }
10829 else if (!strcmp(attr->name, "job-k-limit"))
10830 {
10831 if (attr->value_tag != IPP_TAG_INTEGER)
10832 continue;
10833
10834 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-k-limit to %d...",
10835 attr->values[0].integer);
10836 cupsdFreeQuotas(printer);
10837
10838 printer->k_limit = attr->values[0].integer;
10839 }
10840 else if (!strcmp(attr->name, "job-page-limit"))
10841 {
10842 if (attr->value_tag != IPP_TAG_INTEGER)
10843 continue;
10844
10845 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-page-limit to %d...",
10846 attr->values[0].integer);
10847 cupsdFreeQuotas(printer);
10848
10849 printer->page_limit = attr->values[0].integer;
10850 }
10851 else if (!strcmp(attr->name, "printer-op-policy"))
10852 {
10853 cupsd_policy_t *p; /* Policy */
10854
10855
10856 if (attr->value_tag != IPP_TAG_NAME)
10857 continue;
10858
10859 if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
10860 {
10861 cupsdLogMessage(CUPSD_LOG_DEBUG,
10862 "Setting printer-op-policy to \"%s\"...",
10863 attr->values[0].string.text);
10864 cupsdSetString(&printer->op_policy, attr->values[0].string.text);
10865 printer->op_policy_ptr = p;
10866 }
10867 else
10868 {
10869 send_ipp_status(con, IPP_NOT_POSSIBLE,
10870 _("Unknown printer-op-policy \"%s\"."),
10871 attr->values[0].string.text);
10872 return;
10873 }
10874 }
10875 else if (!strcmp(attr->name, "printer-error-policy"))
10876 {
10877 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10878 continue;
10879
f11a948a
MS
10880 if (strcmp(attr->values[0].string.text, "retry-current-job") &&
10881 ((printer->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_CLASS)) ||
10882 (strcmp(attr->values[0].string.text, "abort-job") &&
10883 strcmp(attr->values[0].string.text, "retry-job") &&
10884 strcmp(attr->values[0].string.text, "stop-printer"))))
b423cd4c 10885 {
10886 send_ipp_status(con, IPP_NOT_POSSIBLE,
10887 _("Unknown printer-error-policy \"%s\"."),
10888 attr->values[0].string.text);
10889 return;
10890 }
10891
10892 cupsdLogMessage(CUPSD_LOG_DEBUG,
10893 "Setting printer-error-policy to \"%s\"...",
10894 attr->values[0].string.text);
10895 cupsdSetString(&printer->error_policy, attr->values[0].string.text);
10896 }
b423cd4c 10897
10898 /*
10899 * Skip any other non-default attributes...
10900 */
10901
10902 namelen = strlen(attr->name);
10903 if (namelen < 9 || strcmp(attr->name + namelen - 8, "-default") ||
10904 namelen > (sizeof(name) - 1) || attr->num_values != 1)
10905 continue;
10906
10907 /*
10908 * OK, anything else must be a user-defined default...
10909 */
10910
10911 strlcpy(name, attr->name, sizeof(name));
10912 name[namelen - 8] = '\0'; /* Strip "-default" */
10913
10914 switch (attr->value_tag)
10915 {
10916 case IPP_TAG_DELETEATTR :
10917 printer->num_options = cupsRemoveOption(name,
10918 printer->num_options,
10919 &(printer->options));
10920 cupsdLogMessage(CUPSD_LOG_DEBUG,
10921 "Deleting %s", attr->name);
10922 break;
10923
10924 case IPP_TAG_NAME :
10925 case IPP_TAG_KEYWORD :
10926 case IPP_TAG_URI :
10927 printer->num_options = cupsAddOption(name,
10928 attr->values[0].string.text,
10929 printer->num_options,
10930 &(printer->options));
10931 cupsdLogMessage(CUPSD_LOG_DEBUG,
10932 "Setting %s to \"%s\"...", attr->name,
10933 attr->values[0].string.text);
10934 break;
10935
10936 case IPP_TAG_BOOLEAN :
10937 printer->num_options = cupsAddOption(name,
10938 attr->values[0].boolean ?
10939 "true" : "false",
10940 printer->num_options,
10941 &(printer->options));
10942 cupsdLogMessage(CUPSD_LOG_DEBUG,
10943 "Setting %s to %s...", attr->name,
10944 attr->values[0].boolean ? "true" : "false");
10945 break;
10946
10947 case IPP_TAG_INTEGER :
10948 case IPP_TAG_ENUM :
10949 sprintf(value, "%d", attr->values[0].integer);
10950 printer->num_options = cupsAddOption(name, value,
10951 printer->num_options,
10952 &(printer->options));
10953 cupsdLogMessage(CUPSD_LOG_DEBUG,
10954 "Setting %s to %s...", attr->name, value);
10955 break;
10956
10957 case IPP_TAG_RANGE :
10958 sprintf(value, "%d-%d", attr->values[0].range.lower,
10959 attr->values[0].range.upper);
10960 printer->num_options = cupsAddOption(name, value,
10961 printer->num_options,
10962 &(printer->options));
10963 cupsdLogMessage(CUPSD_LOG_DEBUG,
10964 "Setting %s to %s...", attr->name, value);
10965 break;
10966
10967 case IPP_TAG_RESOLUTION :
10968 sprintf(value, "%dx%d%s", attr->values[0].resolution.xres,
10969 attr->values[0].resolution.yres,
10970 attr->values[0].resolution.units == IPP_RES_PER_INCH ?
10971 "dpi" : "dpc");
10972 printer->num_options = cupsAddOption(name, value,
10973 printer->num_options,
10974 &(printer->options));
10975 cupsdLogMessage(CUPSD_LOG_DEBUG,
10976 "Setting %s to %s...", attr->name, value);
10977 break;
10978
10979 default :
10980 /* Do nothing for other values */
10981 break;
10982 }
10983 }
10984}
10985
10986
ef416fc2 10987/*
10988 * 'start_printer()' - Start a printer.
10989 */
10990
10991static void
10992start_printer(cupsd_client_t *con, /* I - Client connection */
10993 ipp_attribute_t *uri) /* I - Printer URI */
10994{
10995 http_status_t status; /* Policy status */
bc44d920 10996 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 10997 cupsd_printer_t *printer; /* Printer data */
10998
10999
11000 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", con,
11001 con->http.fd, uri->values[0].string.text);
11002
11003 /*
11004 * Is the destination valid?
11005 */
11006
f7deaa1a 11007 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 11008 {
11009 /*
11010 * Bad URI...
11011 */
11012
11013 send_ipp_status(con, IPP_NOT_FOUND,
11014 _("The printer or class was not found."));
11015 return;
11016 }
11017
11018 /*
11019 * Check policy...
11020 */
11021
11022 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11023 {
f899b121 11024 send_http_error(con, status, printer);
ef416fc2 11025 return;
11026 }
11027
11028 /*
11029 * Start the printer...
11030 */
11031
11032 printer->state_message[0] = '\0';
11033
11034 cupsdStartPrinter(printer, 1);
11035
11036 if (dtype & CUPS_PRINTER_CLASS)
f7deaa1a 11037 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".",
11038 printer->name, get_username(con));
e00b005a 11039 else
f7deaa1a 11040 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".",
11041 printer->name, get_username(con));
ef416fc2 11042
11043 cupsdCheckJobs();
11044
11045 /*
11046 * Everything was ok, so return OK status...
11047 */
11048
11049 con->response->request.status.status_code = IPP_OK;
11050}
11051
11052
11053/*
11054 * 'stop_printer()' - Stop a printer.
11055 */
11056
11057static void
11058stop_printer(cupsd_client_t *con, /* I - Client connection */
11059 ipp_attribute_t *uri) /* I - Printer URI */
11060{
11061 http_status_t status; /* Policy status */
bc44d920 11062 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 11063 cupsd_printer_t *printer; /* Printer data */
11064 ipp_attribute_t *attr; /* printer-state-message attribute */
11065
11066
11067 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", con,
11068 con->http.fd, uri->values[0].string.text);
11069
11070 /*
11071 * Is the destination valid?
11072 */
11073
f7deaa1a 11074 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 11075 {
11076 /*
11077 * Bad URI...
11078 */
11079
11080 send_ipp_status(con, IPP_NOT_FOUND,
11081 _("The printer or class was not found."));
11082 return;
11083 }
11084
11085 /*
11086 * Check policy...
11087 */
11088
11089 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11090 {
f899b121 11091 send_http_error(con, status, printer);
ef416fc2 11092 return;
11093 }
11094
11095 /*
11096 * Stop the printer...
11097 */
11098
11099 if ((attr = ippFindAttribute(con->request, "printer-state-message",
11100 IPP_TAG_TEXT)) == NULL)
11101 strcpy(printer->state_message, "Paused");
11102 else
11103 {
11104 strlcpy(printer->state_message, attr->values[0].string.text,
11105 sizeof(printer->state_message));
11106 }
11107
11108 cupsdStopPrinter(printer, 1);
11109
11110 if (dtype & CUPS_PRINTER_CLASS)
f7deaa1a 11111 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".",
11112 printer->name, get_username(con));
ef416fc2 11113 else
f7deaa1a 11114 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".",
11115 printer->name, get_username(con));
ef416fc2 11116
11117 /*
11118 * Everything was ok, so return OK status...
11119 */
11120
11121 con->response->request.status.status_code = IPP_OK;
11122}
11123
11124
89d46774 11125/*
11126 * 'url_encode_attr()' - URL-encode a string attribute.
11127 */
11128
11129static void
11130url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */
11131 char *buffer,/* I - String buffer */
11132 int bufsize)/* I - Size of buffer */
11133{
11134 int i; /* Looping var */
11135 char *bufptr, /* Pointer into buffer */
b94498cf 11136 *bufend; /* End of buffer */
89d46774 11137
11138
11139 strlcpy(buffer, attr->name, bufsize);
11140 bufptr = buffer + strlen(buffer);
11141 bufend = buffer + bufsize - 1;
11142
11143 for (i = 0; i < attr->num_values; i ++)
11144 {
11145 if (bufptr >= bufend)
11146 break;
11147
11148 if (i)
11149 *bufptr++ = ',';
11150 else
11151 *bufptr++ = '=';
11152
11153 if (bufptr >= bufend)
11154 break;
11155
11156 *bufptr++ = '\'';
11157
b94498cf 11158 bufptr = url_encode_string(attr->values[i].string.text,
11159 bufptr, bufend - bufptr + 1);
89d46774 11160
11161 if (bufptr >= bufend)
11162 break;
11163
11164 *bufptr++ = '\'';
11165 }
11166
11167 *bufptr = '\0';
11168}
11169
11170
b94498cf 11171/*
11172 * 'url_encode_string()' - URL-encode a string.
11173 */
11174
11175static char * /* O - End of string */
11176url_encode_string(const char *s, /* I - String */
11177 char *buffer, /* I - String buffer */
11178 int bufsize) /* I - Size of buffer */
11179{
11180 char *bufptr, /* Pointer into buffer */
11181 *bufend; /* End of buffer */
11182 static const char *hex = "0123456789ABCDEF";
11183 /* Hex digits */
11184
11185
11186 bufptr = buffer;
11187 bufend = buffer + bufsize - 1;
11188
11189 while (*s && bufptr < bufend)
11190 {
f701418f 11191 if (*s == ' ' || *s == '%' || *s == '+')
b94498cf 11192 {
11193 if (bufptr >= (bufend - 2))
11194 break;
11195
11196 *bufptr++ = '%';
11197 *bufptr++ = hex[(*s >> 4) & 15];
11198 *bufptr++ = hex[*s & 15];
11199
11200 s ++;
11201 }
11202 else if (*s == '\'' || *s == '\\')
11203 {
11204 if (bufptr >= (bufend - 1))
11205 break;
11206
11207 *bufptr++ = '\\';
11208 *bufptr++ = *s++;
11209 }
11210 else
11211 *bufptr++ = *s++;
11212 }
11213
11214 *bufptr = '\0';
11215
11216 return (bufptr);
11217}
11218
11219
ef416fc2 11220/*
11221 * 'user_allowed()' - See if a user is allowed to print to a queue.
11222 */
11223
11224static int /* O - 0 if not allowed, 1 if allowed */
11225user_allowed(cupsd_printer_t *p, /* I - Printer or class */
11226 const char *username) /* I - Username */
11227{
11228 int i; /* Looping var */
11229 struct passwd *pw; /* User password data */
5bd77a73
MS
11230 char baseuser[256], /* Base username */
11231 *baseptr; /* Pointer to "@" in base username */
ef416fc2 11232
11233
11234 if (p->num_users == 0)
11235 return (1);
11236
11237 if (!strcmp(username, "root"))
11238 return (1);
11239
5bd77a73
MS
11240 if (strchr(username, '@'))
11241 {
11242 /*
11243 * Strip @REALM for username check...
11244 */
11245
11246 strlcpy(baseuser, username, sizeof(baseuser));
11247
11248 if ((baseptr = strchr(baseuser, '@')) != NULL)
11249 *baseptr = '\0';
11250
11251 username = baseuser;
11252 }
11253
ef416fc2 11254 pw = getpwnam(username);
11255 endpwent();
11256
11257 for (i = 0; i < p->num_users; i ++)
11258 {
8922323b 11259 if (p->users[i][0] == '@')
ef416fc2 11260 {
11261 /*
11262 * Check group membership...
11263 */
11264
11265 if (cupsdCheckGroup(username, pw, p->users[i] + 1))
11266 break;
11267 }
8922323b
MS
11268 else if (p->users[i][0] == '#')
11269 {
11270 /*
11271 * Check UUID...
11272 */
11273
11274 if (cupsdCheckGroup(username, pw, p->users[i]))
11275 break;
11276 }
ef416fc2 11277 else if (!strcasecmp(username, p->users[i]))
11278 break;
11279 }
11280
11281 return ((i < p->num_users) != p->deny_users);
11282}
11283
11284
11285/*
11286 * 'validate_job()' - Validate printer options and destination.
11287 */
11288
11289static void
11290validate_job(cupsd_client_t *con, /* I - Client connection */
11291 ipp_attribute_t *uri) /* I - Printer URI */
11292{
11293 http_status_t status; /* Policy status */
11294 ipp_attribute_t *attr; /* Current attribute */
11295 ipp_attribute_t *format; /* Document-format attribute */
bc44d920 11296 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 11297 char super[MIME_MAX_SUPER],
11298 /* Supertype of file */
11299 type[MIME_MAX_TYPE];
11300 /* Subtype of file */
11301 cupsd_printer_t *printer; /* Printer */
11302
11303
11304 cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", con,
11305 con->http.fd, uri->values[0].string.text);
11306
11307 /*
11308 * OK, see if the client is sending the document compressed - CUPS
11309 * doesn't support compression yet...
11310 */
11311
fa73b229 11312 if ((attr = ippFindAttribute(con->request, "compression",
11313 IPP_TAG_KEYWORD)) != NULL &&
11314 !strcmp(attr->values[0].string.text, "none"))
ef416fc2 11315 {
11316 send_ipp_status(con, IPP_ATTRIBUTES,
4d301e69 11317 _("Unsupported compression attribute %s"),
ef416fc2 11318 attr->values[0].string.text);
11319 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
11320 "compression", NULL, attr->values[0].string.text);
11321 return;
11322 }
11323
11324 /*
11325 * Is it a format we support?
11326 */
11327
11328 if ((format = ippFindAttribute(con->request, "document-format",
11329 IPP_TAG_MIMETYPE)) != NULL)
11330 {
bc44d920 11331 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
11332 super, type) != 2)
ef416fc2 11333 {
4d301e69 11334 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\""),
ef416fc2 11335 format->values[0].string.text);
11336 return;
11337 }
11338
fa73b229 11339 if ((strcmp(super, "application") || strcmp(type, "octet-stream")) &&
11340 !mimeType(MimeDatabase, super, type))
ef416fc2 11341 {
11342 cupsdLogMessage(CUPSD_LOG_INFO,
11343 "Hint: Do you have the raw file printing rules enabled?");
11344 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
4d301e69 11345 _("Unsupported format \"%s\""),
ef416fc2 11346 format->values[0].string.text);
11347 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
11348 "document-format", NULL, format->values[0].string.text);
11349 return;
11350 }
11351 }
11352
11353 /*
11354 * Is the destination valid?
11355 */
11356
f7deaa1a 11357 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 11358 {
11359 /*
11360 * Bad URI...
11361 */
11362
11363 send_ipp_status(con, IPP_NOT_FOUND,
11364 _("The printer or class was not found."));
11365 return;
11366 }
11367
11368 /*
11369 * Check policy...
11370 */
11371
11372 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11373 {
f899b121 11374 send_http_error(con, status, printer);
ef416fc2 11375 return;
11376 }
11377
11378 /*
11379 * Everything was ok, so return OK status...
11380 */
11381
11382 con->response->request.status.status_code = IPP_OK;
11383}
11384
11385
11386/*
11387 * 'validate_name()' - Make sure the printer name only contains valid chars.
11388 */
11389
bc44d920 11390static int /* O - 0 if name is no good, 1 if good */
ef416fc2 11391validate_name(const char *name) /* I - Name to check */
11392{
11393 const char *ptr; /* Pointer into name */
11394
11395
11396 /*
11397 * Scan the whole name...
11398 */
11399
11400 for (ptr = name; *ptr; ptr ++)
f7deaa1a 11401 if ((*ptr > 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
ef416fc2 11402 return (0);
11403
11404 /*
11405 * All the characters are good; validate the length, too...
11406 */
11407
11408 return ((ptr - name) < 128);
11409}
11410
11411
11412/*
11413 * 'validate_user()' - Validate the user for the request.
11414 */
11415
11416static int /* O - 1 if permitted, 0 otherwise */
11417validate_user(cupsd_job_t *job, /* I - Job */
11418 cupsd_client_t *con, /* I - Client connection */
11419 const char *owner, /* I - Owner of job/resource */
11420 char *username, /* O - Authenticated username */
11421 int userlen) /* I - Length of username */
11422{
ef416fc2 11423 cupsd_printer_t *printer; /* Printer for job */
11424
11425
11426 cupsdLogMessage(CUPSD_LOG_DEBUG2,
11427 "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, "
11428 "userlen=%d)",
91c84a35
MS
11429 job->id, con ? con->http.fd : 0,
11430 owner ? owner : "(null)", username, userlen);
ef416fc2 11431
11432 /*
11433 * Validate input...
11434 */
11435
11436 if (!con || !owner || !username || userlen <= 0)
11437 return (0);
11438
11439 /*
11440 * Get the best authenticated username that is available.
11441 */
11442
e00b005a 11443 strlcpy(username, get_username(con), userlen);
ef416fc2 11444
11445 /*
11446 * Check the username against the owner...
11447 */
11448
80ca4592 11449 printer = cupsdFindDest(job->dest);
ef416fc2 11450
11451 return (cupsdCheckPolicy(printer ? printer->op_policy_ptr : DefaultPolicyPtr,
11452 con, owner) == HTTP_OK);
11453}
11454
11455
11456/*
b19ccc9e 11457 * End of "$Id: ipp.c 7944 2008-09-16 22:32:42Z mike $".
ef416fc2 11458 */