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