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