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