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