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