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