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