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