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