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