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