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