]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/ipp.c
Merge changes from CUPS 1.4svn-r7994.
[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
2761 apple_unregister_profiles(printer);
2762 apple_register_profiles(printer);
2763#endif /* __APPLE__ */
b423cd4c 2764 }
2765 else
2766 {
2767 /*
2768 * This must be an interface script, so remove any old PPD file that
2769 * may be lying around...
2770 */
ef416fc2 2771
b423cd4c 2772 unlink(dstfile);
2773 }
2774 }
2775 }
2776 else if ((attr = ippFindAttribute(con->request, "ppd-name",
2777 IPP_TAG_NAME)) != NULL)
ef416fc2 2778 {
b423cd4c 2779 need_restart_job = 1;
ef416fc2 2780
b423cd4c 2781 if (!strcmp(attr->values[0].string.text, "raw"))
ef416fc2 2782 {
b423cd4c 2783 /*
2784 * Raw driver, remove any existing PPD or interface script files.
2785 */
ef416fc2 2786
b423cd4c 2787 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
2788 printer->name);
2789 unlink(dstfile);
ef416fc2 2790
b423cd4c 2791 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
2792 printer->name);
2793 unlink(dstfile);
2794 }
2795 else
ef416fc2 2796 {
b423cd4c 2797 /*
2798 * PPD model file...
2799 */
2800
2801 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
2802 printer->name);
2803 unlink(dstfile);
2804
2805 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
2806 printer->name);
2807
2808 if (copy_model(con, attr->values[0].string.text, dstfile))
2809 {
2810 send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to copy PPD file!"));
2811 return;
2812 }
568fa3fa
MS
2813
2814 cupsdLogMessage(CUPSD_LOG_DEBUG,
2815 "Copied PPD file successfully!");
2816 chmod(dstfile, 0644);
2817
2818#ifdef __APPLE__
2819 /*
2820 * (Re)register color profiles...
2821 */
2822
2823 apple_unregister_profiles(printer);
2824 apple_register_profiles(printer);
2825#endif /* __APPLE__ */
ef416fc2 2826 }
2827 }
2828
09a101d6 2829 /*
2830 * If we set the device URI but not the port monitor, check which port
2831 * monitor to use by default...
2832 */
2833
2834 if (set_device_uri && !set_port_monitor)
2835 {
2836 ppd_file_t *ppd; /* PPD file */
2837 ppd_attr_t *ppdattr; /* cupsPortMonitor attribute */
2838
2839
2840 httpSeparateURI(HTTP_URI_CODING_ALL, printer->device_uri, scheme,
2841 sizeof(scheme), username, sizeof(username), host,
2842 sizeof(host), &port, resource, sizeof(resource));
2843
2844 snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot,
2845 printer->name);
2846 if ((ppd = ppdOpenFile(srcfile)) != NULL)
2847 {
2848 for (ppdattr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
2849 ppdattr;
2850 ppdattr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL))
2851 if (!strcmp(scheme, ppdattr->spec))
2852 {
2853 cupsdLogMessage(CUPSD_LOG_INFO,
2854 "Setting %s port-monitor to \"%s\" (was \"%s\".)",
2855 printer->name, ppdattr->value,
bc44d920 2856 printer->port_monitor ? printer->port_monitor
2857 : "none");
09a101d6 2858
2859 if (strcmp(ppdattr->value, "none"))
2860 cupsdSetString(&printer->port_monitor, ppdattr->value);
2861 else
2862 cupsdClearString(&printer->port_monitor);
2863
2864 break;
2865 }
2866
2867 ppdClose(ppd);
2868 }
2869 }
2870
ef416fc2 2871 /*
b423cd4c 2872 * Update the printer attributes and return...
ef416fc2 2873 */
2874
b423cd4c 2875 cupsdSetPrinterAttrs(printer);
3dfe78b3 2876 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 2877
b423cd4c 2878 if (need_restart_job && printer->job)
ef416fc2 2879 {
b423cd4c 2880 cupsd_job_t *job;
ef416fc2 2881
b423cd4c 2882 /*
2883 * Stop the current job and then restart it below...
2884 */
ef416fc2 2885
b423cd4c 2886 job = (cupsd_job_t *)printer->job;
ef416fc2 2887
b423cd4c 2888 cupsdStopJob(job, 1);
2889
2890 job->state->values[0].integer = IPP_JOB_PENDING;
2891 job->state_value = IPP_JOB_PENDING;
ef416fc2 2892 }
2893
b423cd4c 2894 if (need_restart_job)
2895 cupsdCheckJobs();
ef416fc2 2896
3dfe78b3 2897 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
b423cd4c 2898
2899 if (modify)
ef416fc2 2900 {
b423cd4c 2901 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, printer, NULL,
2902 "Printer \"%s\" modified by \"%s\".", printer->name,
2903 get_username(con));
ef416fc2 2904
b423cd4c 2905 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" modified by \"%s\".",
2906 printer->name, get_username(con));
ef416fc2 2907 }
b423cd4c 2908 else
2909 {
2910 cupsdAddPrinterHistory(printer);
ef416fc2 2911
b423cd4c 2912 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, printer, NULL,
2913 "New printer \"%s\" added by \"%s\".", printer->name,
2914 get_username(con));
ef416fc2 2915
b423cd4c 2916 cupsdLogMessage(CUPSD_LOG_INFO, "New printer \"%s\" added by \"%s\".",
2917 printer->name, get_username(con));
2918 }
2919
2920 con->response->request.status.status_code = IPP_OK;
ef416fc2 2921}
2922
2923
2924/*
b423cd4c 2925 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
2926 * based upon the printer state...
ef416fc2 2927 */
2928
b423cd4c 2929static void
2930add_printer_state_reasons(
2931 cupsd_client_t *con, /* I - Client connection */
2932 cupsd_printer_t *p) /* I - Printer info */
ef416fc2 2933{
b423cd4c 2934 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2935 "add_printer_state_reasons(%p[%d], %p[%s])",
2936 con, con->http.fd, p, p->name);
ef416fc2 2937
b423cd4c 2938 if (p->num_reasons == 0)
2939 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2940 "printer-state-reasons", NULL,
2941 p->state == IPP_PRINTER_STOPPED ? "paused" : "none");
2942 else
2943 ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2944 "printer-state-reasons", p->num_reasons, NULL,
2945 (const char * const *)p->reasons);
2946}
ef416fc2 2947
ef416fc2 2948
b423cd4c 2949/*
2950 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
2951 * the specified printer or class.
2952 */
ef416fc2 2953
b423cd4c 2954static void
2955add_queued_job_count(
2956 cupsd_client_t *con, /* I - Client connection */
2957 cupsd_printer_t *p) /* I - Printer or class */
2958{
2959 int count; /* Number of jobs on destination */
ef416fc2 2960
ef416fc2 2961
b423cd4c 2962 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])",
2963 con, con->http.fd, p, p->name);
ef416fc2 2964
b423cd4c 2965 count = cupsdGetPrinterJobCount(p->name);
ef416fc2 2966
b423cd4c 2967 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2968 "queued-job-count", count);
2969}
ef416fc2 2970
ef416fc2 2971
568fa3fa 2972#ifdef __APPLE__
568fa3fa
MS
2973/*
2974 * 'apple_init_profile()' - Initialize a color profile.
2975 */
2976
2977static void
2978apple_init_profile(
a603edef
MS
2979 ppd_file_t *ppd, /* I - PPD file */
2980 cups_array_t *languages, /* I - Languages in the PPD file */
568fa3fa
MS
2981 CMDeviceProfileInfo *profile, /* I - Profile record */
2982 unsigned id, /* I - Profile ID */
2983 const char *name, /* I - Profile name */
a603edef 2984 const char *text, /* I - Profile UI text */
568fa3fa
MS
2985 const char *iccfile) /* I - ICC filename */
2986{
2987 char url[1024]; /* URL for profile filename */
2988 CFMutableDictionaryRef dict; /* Dictionary for name */
a603edef
MS
2989 char *language; /* Current language */
2990 ppd_attr_t *attr; /* Profile attribute */
db0bd74a
MS
2991 CFStringRef cflang, /* Language string */
2992 cftext; /* Localized text */
568fa3fa
MS
2993
2994
a603edef
MS
2995 /*
2996 * Build the profile name dictionary...
2997 */
568fa3fa
MS
2998
2999 dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3000 &kCFTypeDictionaryKeyCallBacks,
3001 &kCFTypeDictionaryValueCallBacks);
3002
db0bd74a
MS
3003 cftext = CFStringCreateWithCString(kCFAllocatorDefault, text,
3004 kCFStringEncodingUTF8);
a603edef 3005
db0bd74a
MS
3006 if (cftext)
3007 {
3008 CFDictionarySetValue(dict, CFSTR("en"), cftext);
3009 CFRelease(cftext);
3010 }
3011
3012 if (languages)
a603edef
MS
3013 {
3014 /*
3015 * Find localized names for the color profiles...
3016 */
3017
3018 cupsArraySave(ppd->sorted_attrs);
3019
3020 for (language = (char *)cupsArrayFirst(languages);
3021 language;
3022 language = (char *)cupsArrayNext(languages))
3023 {
db0bd74a 3024 if (iccfile)
c9fc04c6
MS
3025 {
3026 if ((attr = _ppdLocalizedAttr(ppd, "cupsICCProfile", name,
3027 language)) == NULL)
3028 attr = _ppdLocalizedAttr(ppd, "APTiogaProfile", name, language);
3029 }
db0bd74a
MS
3030 else
3031 attr = _ppdLocalizedAttr(ppd, "ColorModel", name, language);
3032
3033 if (attr && attr->text[0])
3034 {
3035 cflang = CFStringCreateWithCString(kCFAllocatorDefault, language,
3036 kCFStringEncodingUTF8);
3037 cftext = CFStringCreateWithCString(kCFAllocatorDefault, attr->text,
3038 kCFStringEncodingUTF8);
3039
3040 if (cflang && cftext)
3041 CFDictionarySetValue(dict, cflang, cftext);
3042
3043 if (cflang)
3044 CFRelease(cflang);
3045
3046 if (cftext)
3047 CFRelease(cftext);
3048 }
a603edef
MS
3049 }
3050
3051 cupsArrayRestore(ppd->sorted_attrs);
3052 }
3053
3054 /*
3055 * Fill in the profile data...
3056 */
3057
3058 if (iccfile)
3059 httpAssembleURI(HTTP_URI_CODING_ALL, url, sizeof(url), "file", NULL, "", 0,
3060 iccfile);
3061
568fa3fa
MS
3062 profile->dataVersion = cmDeviceProfileInfoVersion1;
3063 profile->profileID = id;
3064 profile->profileLoc.locType = iccfile ? cmPathBasedProfile : cmNoProfileBase;
3065 profile->profileName = dict;
3066
3067 if (iccfile)
3068 strlcpy(profile->profileLoc.u.pathLoc.path, iccfile,
3069 sizeof(profile->profileLoc.u.pathLoc.path));
568fa3fa
MS
3070}
3071
3072
3073/*
3074 * 'apple_register_profiles()' - Register color profiles for a printer.
3075 */
3076
3077static void
3078apple_register_profiles(
3079 cupsd_printer_t *p) /* I - Printer */
3080{
3081 int i; /* Looping var */
3082 char ppdfile[1024], /* PPD filename */
db0bd74a
MS
3083 iccfile[1024], /* ICC filename */
3084 selector[PPD_MAX_NAME];
3085 /* Profile selection string */
568fa3fa 3086 ppd_file_t *ppd; /* PPD file */
01ce6322
MS
3087 ppd_attr_t *attr, /* Profile attributes */
3088 *profileid_attr,/* cupsProfileID attribute */
3089 *q1_attr, /* ColorModel (or other) qualifier */
3090 *q2_attr, /* MediaType (or other) qualifier */
3091 *q3_attr; /* Resolution (or other) qualifier */
3092 char q_keyword[PPD_MAX_NAME];
3093 /* Qualifier keyword */
3094 const char *q1_choice, /* ColorModel (or other) choice */
3095 *q2_choice, /* MediaType (or other) choice */
3096 *q3_choice; /* Resolution (or other) choice */
c9fc04c6 3097 const char *profile_key; /* Profile keyword */
568fa3fa 3098 ppd_option_t *cm_option; /* Color model option */
01ce6322 3099 ppd_choice_t *cm_choice; /* Color model choice */
568fa3fa
MS
3100 int num_profiles; /* Number of profiles */
3101 CMError error; /* Last error */
db0bd74a
MS
3102 unsigned device_id, /* Printer device ID */
3103 profile_id, /* Profile ID */
3104 default_profile_id = 0;
3105 /* Default profile ID */
3106 CFMutableDictionaryRef device_name; /* Printer device name dictionary */
3107 CFStringRef printer_name; /* Printer name string */
568fa3fa
MS
3108 CMDeviceScope scope = /* Scope of the registration */
3109 {
3110 kCFPreferencesAnyUser,
3111 kCFPreferencesCurrentHost
3112 };
3113 CMDeviceProfileArrayPtr profiles; /* Profiles */
3114 CMDeviceProfileInfo *profile; /* Current profile */
a603edef 3115 cups_array_t *languages; /* Languages array */
568fa3fa
MS
3116
3117
64a69576
MS
3118 /*
3119 * Make sure ColorSync is available...
3120 */
3121
3122 if (CMRegisterColorDevice == NULL)
3123 return;
3124
568fa3fa
MS
3125 /*
3126 * Try opening the PPD file for this printer...
3127 */
3128
3129 snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
3130 if ((ppd = ppdOpenFile(ppdfile)) == NULL)
3131 return;
3132
3133 /*
3134 * See if we have any profiles...
3135 */
3136
c9fc04c6
MS
3137 if ((attr = ppdFindAttr(ppd, "APTiogaProfile", NULL)) != NULL)
3138 profile_key = "APTiogaProfile";
3139 else
3140 {
3141 attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
3142 profile_key = "cupsICCProfile";
3143 }
3144
3145 for (num_profiles = 0; attr; attr = ppdFindNextAttr(ppd, profile_key, NULL))
568fa3fa
MS
3146 if (attr->spec[0] && attr->value && attr->value[0])
3147 {
3148 if (attr->value[0] != '/')
3149 snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
3150 attr->value);
3151 else
3152 strlcpy(iccfile, attr->value, sizeof(iccfile));
3153
3154 if (access(iccfile, 0))
3155 continue;
3156
3157 num_profiles ++;
3158 }
3159
c9fc04c6 3160
568fa3fa
MS
3161 /*
3162 * If we have profiles, add them...
3163 */
3164
3165 if (num_profiles > 0)
3166 {
01ce6322
MS
3167 if (profile_key[0] == 'A')
3168 {
3169 /*
3170 * For Tioga PPDs, get the default profile using the DefaultAPTiogaProfile
3171 * attribute...
3172 */
db0bd74a 3173
01ce6322
MS
3174 if ((attr = ppdFindAttr(ppd, "DefaultAPTiogaProfile", NULL)) != NULL &&
3175 attr->value)
3176 default_profile_id = atoi(attr->value);
db0bd74a 3177
01ce6322
MS
3178 q1_choice = q2_choice = q3_choice = NULL;
3179 }
db0bd74a 3180 else
01ce6322
MS
3181 {
3182 /*
3183 * For CUPS PPDs, figure out the default profile selector values...
3184 */
db0bd74a 3185
01ce6322
MS
3186 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL &&
3187 attr->value && attr->value[0])
3188 {
3189 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
3190 q1_attr = ppdFindAttr(ppd, q_keyword, NULL);
3191 }
3192 else if ((q1_attr = ppdFindAttr(ppd, "DefaultColorModel", NULL)) == NULL)
3193 q1_attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
3194
3195 if (q1_attr && q1_attr->value && q1_attr->value[0])
3196 q1_choice = q1_attr->value;
3197 else
3198 q1_choice = "";
3199
3200 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL &&
3201 attr->value && attr->value[0])
3202 {
3203 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
3204 q2_attr = ppdFindAttr(ppd, q_keyword, NULL);
3205 }
3206 else
3207 q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL);
3208
3209 if (q2_attr && q2_attr->value && q2_attr->value[0])
3210 q2_choice = q2_attr->value;
3211 else
3212 q2_choice = NULL;
3213
3214 if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL &&
3215 attr->value && attr->value[0])
3216 {
3217 snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
3218 q3_attr = ppdFindAttr(ppd, q_keyword, NULL);
3219 }
3220 else
3221 q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL);
3222
3223 if (q3_attr && q3_attr->value && q3_attr->value[0])
3224 q3_choice = q3_attr->value;
3225 else
3226 q3_choice = NULL;
3227 }
db0bd74a 3228
568fa3fa
MS
3229 /*
3230 * Build the array of profiles...
3231 *
3232 * Note: This calloc actually requests slightly more memory than needed.
3233 */
3234
3235 if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
3236 {
3237 cupsdLogMessage(CUPSD_LOG_ERROR,
3238 "Unable to allocate memory for %d profiles!",
3239 num_profiles);
3240 ppdClose(ppd);
3241 return;
3242 }
3243
3244 profiles->profileCount = num_profiles;
a603edef 3245 languages = _ppdGetLanguages(ppd);
568fa3fa
MS
3246
3247 for (profile = profiles->profiles,
c9fc04c6 3248 attr = ppdFindAttr(ppd, profile_key, NULL);
568fa3fa 3249 attr;
c9fc04c6 3250 attr = ppdFindNextAttr(ppd, profile_key, NULL))
568fa3fa
MS
3251 if (attr->spec[0] && attr->value && attr->value[0])
3252 {
3253 /*
3254 * Add this profile...
3255 */
3256
3257 if (attr->value[0] != '/')
3258 snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
3259 attr->value);
3260 else
3261 strlcpy(iccfile, attr->value, sizeof(iccfile));
3262
3263 if (access(iccfile, 0))
3264 continue;
3265
c9fc04c6
MS
3266 if (profile_key[0] == 'c')
3267 {
3268 cupsArraySave(ppd->sorted_attrs);
db0bd74a 3269
c9fc04c6
MS
3270 if ((profileid_attr = ppdFindAttr(ppd, "cupsProfileID",
3271 attr->spec)) != NULL &&
3272 profileid_attr->value && isdigit(profileid_attr->value[0] & 255))
3273 profile_id = (unsigned)strtoul(profileid_attr->value, NULL, 10);
3274 else
3275 profile_id = _ppdHashName(attr->spec);
db0bd74a 3276
c9fc04c6
MS
3277 cupsArrayRestore(ppd->sorted_attrs);
3278 }
3279 else
3280 profile_id = atoi(attr->spec);
db0bd74a
MS
3281
3282 apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
3283 attr->text[0] ? attr->text : attr->spec, iccfile);
568fa3fa
MS
3284
3285 profile ++;
db0bd74a
MS
3286
3287 /*
3288 * See if this is the default profile...
3289 */
3290
01ce6322 3291 if (!default_profile_id)
db0bd74a
MS
3292 {
3293 if (q2_choice)
3294 {
3295 if (q3_choice)
3296 {
3297 snprintf(selector, sizeof(selector), "%s.%s.%s",
01ce6322 3298 q1_choice, q2_choice, q3_choice);
db0bd74a
MS
3299 if (!strcmp(selector, attr->spec))
3300 default_profile_id = profile_id;
3301 }
3302
3303 if (!default_profile_id)
3304 {
01ce6322
MS
3305 snprintf(selector, sizeof(selector), "%s.%s.", q1_choice,
3306 q2_choice);
db0bd74a
MS
3307 if (!strcmp(selector, attr->spec))
3308 default_profile_id = profile_id;
3309 }
3310 }
3311
3312 if (!default_profile_id && q3_choice)
3313 {
01ce6322
MS
3314 snprintf(selector, sizeof(selector), "%s..%s", q1_choice,
3315 q3_choice);
db0bd74a
MS
3316 if (!strcmp(selector, attr->spec))
3317 default_profile_id = profile_id;
3318 }
3319
3320 if (!default_profile_id)
3321 {
01ce6322 3322 snprintf(selector, sizeof(selector), "%s..", q1_choice);
db0bd74a
MS
3323 if (!strcmp(selector, attr->spec))
3324 default_profile_id = profile_id;
3325 }
3326 }
568fa3fa 3327 }
a603edef
MS
3328
3329 _ppdFreeLanguages(languages);
568fa3fa
MS
3330 }
3331 else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL)
3332 {
3333 /*
db0bd74a 3334 * Extract profiles from ColorModel option...
568fa3fa
MS
3335 */
3336
db0bd74a 3337 const char *profile_name; /* Name of generic profile */
568fa3fa 3338
db0bd74a
MS
3339
3340 num_profiles = cm_option->num_choices;
568fa3fa
MS
3341
3342 if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
3343 {
3344 cupsdLogMessage(CUPSD_LOG_ERROR,
3345 "Unable to allocate memory for %d profiles!",
3346 num_profiles);
3347 ppdClose(ppd);
3348 return;
3349 }
3350
3351 profiles->profileCount = num_profiles;
3352
3353 for (profile = profiles->profiles, i = cm_option->num_choices,
3354 cm_choice = cm_option->choices;
3355 i > 0;
db0bd74a
MS
3356 i --, cm_choice ++, profile ++)
3357 {
3358 if (!strcmp(cm_choice->choice, "Gray") ||
3359 !strcmp(cm_choice->choice, "Black"))
3360 profile_name = "Gray";
3361 else if (!strcmp(cm_choice->choice, "RGB") ||
3362 !strcmp(cm_choice->choice, "CMY"))
3363 profile_name = "RGB";
3364 else if (!strcmp(cm_choice->choice, "CMYK") ||
3365 !strcmp(cm_choice->choice, "KCMY"))
3366 profile_name = "CMYK";
3367 else
3368 profile_name = "DeviceN";
3369
3370 snprintf(selector, sizeof(selector), "%s..", profile_name);
3371 profile_id = _ppdHashName(selector);
3372
3373 apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
3374 cm_choice->text, NULL);
3375
3376 if (cm_choice->marked)
3377 default_profile_id = profile_id;
3378 }
568fa3fa
MS
3379 }
3380 else
3381 {
3382 /*
3383 * Use the default colorspace...
3384 */
3385
db0bd74a 3386 num_profiles = 1 + ppd->colorspace != PPD_CS_GRAY;
568fa3fa 3387
db0bd74a 3388 if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
568fa3fa
MS
3389 {
3390 cupsdLogMessage(CUPSD_LOG_ERROR,
3391 "Unable to allocate memory for %d profiles!",
3392 num_profiles);
3393 ppdClose(ppd);
3394 return;
3395 }
3396
db0bd74a
MS
3397 profiles->profileCount = num_profiles;
3398
3399 apple_init_profile(ppd, NULL, profiles->profiles, _ppdHashName("Gray.."),
3400 "Gray", "Gray", NULL);
568fa3fa
MS
3401
3402 switch (ppd->colorspace)
3403 {
568fa3fa 3404 case PPD_CS_RGB :
db0bd74a
MS
3405 case PPD_CS_CMY :
3406 apple_init_profile(ppd, NULL, profiles->profiles + 1,
a603edef 3407 _ppdHashName("RGB.."), "RGB", "RGB", NULL);
568fa3fa 3408 break;
db0bd74a 3409 case PPD_CS_RGBK :
568fa3fa 3410 case PPD_CS_CMYK :
db0bd74a 3411 apple_init_profile(ppd, NULL, profiles->profiles + 1,
a603edef 3412 _ppdHashName("CMYK.."), "CMYK", "CMYK", NULL);
568fa3fa
MS
3413 break;
3414
db0bd74a
MS
3415 case PPD_CS_N :
3416 apple_init_profile(ppd, NULL, profiles->profiles + 1,
3417 _ppdHashName("DeviceN.."), "DeviceN", "DeviceN",
3418 NULL);
3419 break;
3420
568fa3fa 3421 default :
568fa3fa
MS
3422 break;
3423 }
3424 }
3425
3426 if (num_profiles > 0)
3427 {
db0bd74a
MS
3428 /*
3429 * Make sure we have a default profile ID...
3430 */
3431
3432 if (!default_profile_id)
3433 default_profile_id = profiles->profiles[num_profiles - 1].profileID;
3434
568fa3fa
MS
3435 /*
3436 * Get the device ID hash and pathelogical name dictionary.
3437 */
3438
3439 cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\"",
3440 p->name);
3441
db0bd74a
MS
3442 device_id = _ppdHashName(p->name);
3443 device_name = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3444 &kCFTypeDictionaryKeyCallBacks,
3445 &kCFTypeDictionaryValueCallBacks);
9a4f8274
MS
3446 printer_name = CFStringCreateWithCString(kCFAllocatorDefault,
3447 p->name, kCFStringEncodingUTF8);
568fa3fa 3448
db0bd74a
MS
3449 if (device_name && printer_name)
3450 {
3451 CFDictionarySetValue(device_name, CFSTR("en"), printer_name);
568fa3fa 3452
db0bd74a
MS
3453 /*
3454 * Register the device with ColorSync...
3455 */
568fa3fa 3456
db0bd74a
MS
3457 error = CMRegisterColorDevice(cmPrinterDeviceClass, device_id,
3458 device_name, &scope);
568fa3fa 3459
db0bd74a
MS
3460 /*
3461 * Register the profiles...
3462 */
568fa3fa 3463
db0bd74a
MS
3464 if (error == noErr)
3465 error = CMSetDeviceFactoryProfiles(cmPrinterDeviceClass, device_id,
3466 default_profile_id, profiles);
3467 }
3468 else
3469 error = 1000;
568fa3fa
MS
3470
3471 /*
3472 * Clean up...
3473 */
3474
3475 if (error != noErr)
3476 cupsdLogMessage(CUPSD_LOG_ERROR,
3477 "Unable to register ICC color profiles for \"%s\" - %d",
3478 p->name, (int)error);
3479
3480 for (profile = profiles->profiles;
3481 num_profiles > 0;
3482 profile ++, num_profiles --)
3483 CFRelease(profile->profileName);
3484
3485 free(profiles);
db0bd74a
MS
3486
3487 if (printer_name)
3488 CFRelease(printer_name);
3489
3490 if (device_name)
3491 CFRelease(device_name);
568fa3fa
MS
3492 }
3493
3494 ppdClose(ppd);
3495}
3496
3497
3498/*
3499 * 'apple_unregister_profiles()' - Remove color profiles for the specified
3500 * printer.
3501 */
3502
3503static void
3504apple_unregister_profiles(
3505 cupsd_printer_t *p) /* I - Printer */
3506{
64a69576
MS
3507 /*
3508 * Make sure ColorSync is available...
3509 */
3510
3511 if (CMUnregisterColorDevice != NULL)
3512 CMUnregisterColorDevice(cmPrinterDeviceClass, _ppdHashName(p->name));
568fa3fa
MS
3513}
3514#endif /* __APPLE__ */
3515
b423cd4c 3516/*
3517 * 'apply_printer_defaults()' - Apply printer default options to a job.
3518 */
ef416fc2 3519
b423cd4c 3520static void
3521apply_printer_defaults(
3522 cupsd_printer_t *printer, /* I - Printer */
3523 cupsd_job_t *job) /* I - Job */
3524{
3525 int i, /* Looping var */
3526 num_options; /* Number of default options */
3527 cups_option_t *options, /* Default options */
f7deaa1a 3528 *option; /* Current option */
ef416fc2 3529
ef416fc2 3530
b423cd4c 3531 /*
3532 * Collect all of the default options and add the missing ones to the
3533 * job object...
3534 */
ef416fc2 3535
1f0275e3
MS
3536 for (i = printer->num_options, num_options = 0, options = NULL,
3537 option = printer->options;
b423cd4c 3538 i > 0;
3539 i --, option ++)
3540 if (!ippFindAttribute(job->attrs, option->name, IPP_TAG_ZERO))
3541 {
3542 num_options = cupsAddOption(option->name, option->value, num_options,
3543 &options);
3544 }
ef416fc2 3545
b423cd4c 3546 /*
3547 * Encode these options as attributes in the job object...
3548 */
ef416fc2 3549
b423cd4c 3550 cupsEncodeOptions2(job->attrs, num_options, options, IPP_TAG_JOB);
3551 cupsFreeOptions(num_options, options);
3552}
3553
3554
3555/*
3556 * 'authenticate_job()' - Set job authentication info.
3557 */
3558
3559static void
3560authenticate_job(cupsd_client_t *con, /* I - Client connection */
3561 ipp_attribute_t *uri) /* I - Job URI */
3562{
f7deaa1a 3563 ipp_attribute_t *attr, /* job-id attribute */
3564 *auth_info; /* auth-info attribute */
b423cd4c 3565 int jobid; /* Job ID */
3566 cupsd_job_t *job; /* Current job */
3567 char method[HTTP_MAX_URI],
3568 /* Method portion of URI */
3569 username[HTTP_MAX_URI],
3570 /* Username portion of URI */
3571 host[HTTP_MAX_URI],
3572 /* Host portion of URI */
3573 resource[HTTP_MAX_URI];
3574 /* Resource portion of URI */
3575 int port; /* Port portion of URI */
ef416fc2 3576
ef416fc2 3577
b423cd4c 3578 cupsdLogMessage(CUPSD_LOG_DEBUG2, "authenticate_job(%p[%d], %s)",
3579 con, con->http.fd, uri->values[0].string.text);
ef416fc2 3580
b423cd4c 3581 /*
3582 * Start with "everything is OK" status...
3583 */
ef416fc2 3584
b423cd4c 3585 con->response->request.status.status_code = IPP_OK;
ef416fc2 3586
b423cd4c 3587 /*
3588 * See if we have a job URI or a printer URI...
3589 */
ef416fc2 3590
b423cd4c 3591 if (!strcmp(uri->name, "printer-uri"))
3592 {
3593 /*
3594 * Got a printer URI; see if we also have a job-id attribute...
3595 */
ef416fc2 3596
b423cd4c 3597 if ((attr = ippFindAttribute(con->request, "job-id",
3598 IPP_TAG_INTEGER)) == NULL)
3599 {
3600 send_ipp_status(con, IPP_BAD_REQUEST,
3601 _("Got a printer-uri attribute but no job-id!"));
3602 return;
3603 }
3604
3605 jobid = attr->values[0].integer;
3606 }
3607 else
3608 {
3609 /*
3610 * Got a job URI; parse it to get the job ID...
3611 */
ef416fc2 3612
b423cd4c 3613 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
3614 sizeof(method), username, sizeof(username), host,
3615 sizeof(host), &port, resource, sizeof(resource));
f7deaa1a 3616
b423cd4c 3617 if (strncmp(resource, "/jobs/", 6))
3618 {
3619 /*
3620 * Not a valid URI!
3621 */
ef416fc2 3622
b423cd4c 3623 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri attribute \"%s\"!"),
3624 uri->values[0].string.text);
3625 return;
3626 }
ef416fc2 3627
b423cd4c 3628 jobid = atoi(resource + 6);
3629 }
ef416fc2 3630
b423cd4c 3631 /*
3632 * See if the job exists...
3633 */
ef416fc2 3634
b423cd4c 3635 if ((job = cupsdFindJob(jobid)) == NULL)
3636 {
3637 /*
3638 * Nope - return a "not found" error...
3639 */
ef416fc2 3640
b423cd4c 3641 send_ipp_status(con, IPP_NOT_FOUND,
3642 _("Job #%d does not exist!"), jobid);
ef416fc2 3643 return;
b423cd4c 3644 }
ef416fc2 3645
b423cd4c 3646 /*
3647 * See if the job has been completed...
3648 */
3649
3650 if (job->state_value != IPP_JOB_HELD)
ef416fc2 3651 {
3652 /*
b423cd4c 3653 * Return a "not-possible" error...
ef416fc2 3654 */
3655
b423cd4c 3656 send_ipp_status(con, IPP_NOT_POSSIBLE,
3657 _("Job #%d is not held for authentication!"),
3658 jobid);
3659 return;
3660 }
ef416fc2 3661
b423cd4c 3662 /*
3663 * See if we have already authenticated...
3664 */
3665
f7deaa1a 3666 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
3667
3668 if (!con->username[0] && !auth_info)
b423cd4c 3669 {
db1f069b
MS
3670 cupsd_printer_t *printer; /* Job destination */
3671
3672
3673 /*
3674 * No auth data. If we need to authenticate via Kerberos, send a
3675 * HTTP auth challenge, otherwise just return an IPP error...
3676 */
3677
3678 printer = cupsdFindDest(job->dest);
3679
3680 if (printer && printer->num_auth_info_required > 0 &&
3681 !strcmp(printer->auth_info_required[0], "negotiate"))
3682 send_http_error(con, HTTP_UNAUTHORIZED, printer);
3683 else
3684 send_ipp_status(con, IPP_NOT_AUTHORIZED,
3685 _("No authentication information provided!"));
b423cd4c 3686 return;
3687 }
3688
3689 /*
3690 * See if the job is owned by the requesting user...
3691 */
3692
3693 if (!validate_user(job, con, job->username, username, sizeof(username)))
3694 {
2fb76298 3695 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
b423cd4c 3696 return;
3697 }
3698
3699 /*
3700 * Save the authentication information for this job...
3701 */
3702
f7deaa1a 3703 save_auth_info(con, job, auth_info);
b423cd4c 3704
3705 /*
3706 * Reset the job-hold-until value to "no-hold"...
3707 */
3708
3709 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
3710 IPP_TAG_KEYWORD)) == NULL)
3711 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
3712
3713 if (attr)
3714 {
3715 attr->value_tag = IPP_TAG_KEYWORD;
3716 cupsdSetString(&(attr->values[0].string.text), "no-hold");
ef416fc2 3717 }
b423cd4c 3718
3719 /*
3720 * Release the job and return...
3721 */
3722
3723 cupsdReleaseJob(job);
3724
75bd9771 3725 cupsdLogJob(job, CUPSD_LOG_INFO, "Authenticated by \"%s\".", con->username);
ef416fc2 3726}
3727
3728
3729/*
b423cd4c 3730 * 'cancel_all_jobs()' - Cancel all print jobs.
ef416fc2 3731 */
3732
b423cd4c 3733static void
3734cancel_all_jobs(cupsd_client_t *con, /* I - Client connection */
3735 ipp_attribute_t *uri) /* I - Job or Printer URI */
ef416fc2 3736{
b423cd4c 3737 http_status_t status; /* Policy status */
b423cd4c 3738 cups_ptype_t dtype; /* Destination type */
f7deaa1a 3739 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
b423cd4c 3740 userpass[HTTP_MAX_URI], /* Username portion of URI */
f7deaa1a 3741 hostname[HTTP_MAX_URI], /* Host portion of URI */
b423cd4c 3742 resource[HTTP_MAX_URI]; /* Resource portion of URI */
3743 int port; /* Port portion of URI */
3744 ipp_attribute_t *attr; /* Attribute in request */
3745 const char *username; /* Username */
3746 int purge; /* Purge? */
3747 cupsd_printer_t *printer; /* Printer */
ef416fc2 3748
3749
b423cd4c 3750 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_all_jobs(%p[%d], %s)", con,
3751 con->http.fd, uri->values[0].string.text);
ef416fc2 3752
3753 /*
b423cd4c 3754 * See if we have a printer URI...
ef416fc2 3755 */
3756
b423cd4c 3757 if (strcmp(uri->name, "printer-uri"))
3758 {
3759 send_ipp_status(con, IPP_BAD_REQUEST,
3760 _("The printer-uri attribute is required!"));
3761 return;
3762 }
ef416fc2 3763
3764 /*
b423cd4c 3765 * Get the username (if any) for the jobs we want to cancel (only if
3766 * "my-jobs" is specified...
ef416fc2 3767 */
3768
b423cd4c 3769 if ((attr = ippFindAttribute(con->request, "my-jobs",
3770 IPP_TAG_BOOLEAN)) != NULL &&
3771 attr->values[0].boolean)
ef416fc2 3772 {
b423cd4c 3773 if ((attr = ippFindAttribute(con->request, "requesting-user-name",
3774 IPP_TAG_NAME)) != NULL)
3775 username = attr->values[0].string.text;
3776 else
3777 {
3778 send_ipp_status(con, IPP_BAD_REQUEST,
3779 _("Missing requesting-user-name attribute!"));
3780 return;
3781 }
ef416fc2 3782 }
b423cd4c 3783 else
3784 username = NULL;
ef416fc2 3785
b423cd4c 3786 /*
3787 * Look for the "purge-jobs" attribute...
3788 */
3789
3790 if ((attr = ippFindAttribute(con->request, "purge-jobs",
3791 IPP_TAG_BOOLEAN)) != NULL)
3792 purge = attr->values[0].boolean;
3793 else
3794 purge = 1;
ef416fc2 3795
3796 /*
b423cd4c 3797 * And if the destination is valid...
ef416fc2 3798 */
3799
f7deaa1a 3800 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 3801 {
3802 /*
b423cd4c 3803 * Bad URI?
ef416fc2 3804 */
3805
f7deaa1a 3806 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text,
3807 scheme, sizeof(scheme), userpass, sizeof(userpass),
3808 hostname, sizeof(hostname), &port,
3809 resource, sizeof(resource));
3810
b423cd4c 3811 if ((!strncmp(resource, "/printers/", 10) && resource[10]) ||
3812 (!strncmp(resource, "/classes/", 9) && resource[9]))
3813 {
3814 send_ipp_status(con, IPP_NOT_FOUND,
3815 _("The printer or class was not found."));
3816 return;
3817 }
ef416fc2 3818
b423cd4c 3819 /*
3820 * Check policy...
3821 */
3822
3823 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
3824 {
f899b121 3825 send_http_error(con, status, NULL);
b423cd4c 3826 return;
3827 }
ef416fc2 3828
ef416fc2 3829 /*
b423cd4c 3830 * Cancel all jobs on all printers...
ef416fc2 3831 */
3832
b423cd4c 3833 cupsdCancelJobs(NULL, username, purge);
ef416fc2 3834
b423cd4c 3835 cupsdLogMessage(CUPSD_LOG_INFO, "All jobs were %s by \"%s\".",
d09495fa 3836 purge ? "purged" : "canceled", get_username(con));
ef416fc2 3837 }
b423cd4c 3838 else
ef416fc2 3839 {
3840 /*
b423cd4c 3841 * Check policy...
ef416fc2 3842 */
3843
f7deaa1a 3844 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
3845 NULL)) != HTTP_OK)
b423cd4c 3846 {
f899b121 3847 send_http_error(con, status, printer);
b423cd4c 3848 return;
3849 }
ef416fc2 3850
b423cd4c 3851 /*
3852 * Cancel all of the jobs on the named printer...
3853 */
3854
f7deaa1a 3855 cupsdCancelJobs(printer->name, username, purge);
b423cd4c 3856
3857 cupsdLogMessage(CUPSD_LOG_INFO, "All jobs on \"%s\" were %s by \"%s\".",
f7deaa1a 3858 printer->name, purge ? "purged" : "canceled",
3859 get_username(con));
ef416fc2 3860 }
3861
b423cd4c 3862 con->response->request.status.status_code = IPP_OK;
3863}
3864
3865
3866/*
3867 * 'cancel_job()' - Cancel a print job.
3868 */
3869
3870static void
3871cancel_job(cupsd_client_t *con, /* I - Client connection */
3872 ipp_attribute_t *uri) /* I - Job or Printer URI */
3873{
3874 ipp_attribute_t *attr; /* Current attribute */
3875 int jobid; /* Job ID */
f7deaa1a 3876 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
b423cd4c 3877 username[HTTP_MAX_URI], /* Username portion of URI */
3878 host[HTTP_MAX_URI], /* Host portion of URI */
3879 resource[HTTP_MAX_URI]; /* Resource portion of URI */
3880 int port; /* Port portion of URI */
3881 cupsd_job_t *job; /* Job information */
bc44d920 3882 cups_ptype_t dtype; /* Destination type (printer/class) */
b423cd4c 3883 cupsd_printer_t *printer; /* Printer data */
0a682745 3884 int purge; /* Purge the job? */
b423cd4c 3885
3886
3887 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_job(%p[%d], %s)", con,
3888 con->http.fd, uri->values[0].string.text);
3889
ef416fc2 3890 /*
b423cd4c 3891 * See if we have a job URI or a printer URI...
ef416fc2 3892 */
3893
b423cd4c 3894 if (!strcmp(uri->name, "printer-uri"))
3895 {
3896 /*
3897 * Got a printer URI; see if we also have a job-id attribute...
3898 */
3899
3900 if ((attr = ippFindAttribute(con->request, "job-id",
3901 IPP_TAG_INTEGER)) == NULL)
3902 {
3903 send_ipp_status(con, IPP_BAD_REQUEST,
3904 _("Got a printer-uri attribute but no job-id!"));
3905 return;
3906 }
3907
3908 if ((jobid = attr->values[0].integer) == 0)
ef416fc2 3909 {
3910 /*
b423cd4c 3911 * Find the current job on the specified printer...
ef416fc2 3912 */
3913
f7deaa1a 3914 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 3915 {
3916 /*
b423cd4c 3917 * Bad URI...
ef416fc2 3918 */
3919
b423cd4c 3920 send_ipp_status(con, IPP_NOT_FOUND,
3921 _("The printer or class was not found."));
3922 return;
ef416fc2 3923 }
3924
3925 /*
b423cd4c 3926 * See if the printer is currently printing a job...
ef416fc2 3927 */
3928
b423cd4c 3929 if (printer->job)
3930 jobid = ((cupsd_job_t *)printer->job)->id;
ef416fc2 3931 else
ef416fc2 3932 {
3933 /*
b423cd4c 3934 * No, see if there are any pending jobs...
ef416fc2 3935 */
f7deaa1a 3936
b423cd4c 3937 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
3938 job;
3939 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
3940 if (job->state_value <= IPP_JOB_PROCESSING &&
f7deaa1a 3941 !strcasecmp(job->dest, printer->name))
b423cd4c 3942 break;
ef416fc2 3943
b423cd4c 3944 if (job)
3945 jobid = job->id;
3946 else
ef416fc2 3947 {
355e94dc
MS
3948 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
3949 job;
3950 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
3951 if (job->state_value == IPP_JOB_STOPPED &&
3952 !strcasecmp(job->dest, printer->name))
3953 break;
3954
3955 if (job)
3956 jobid = job->id;
3957 else
3958 {
3959 send_ipp_status(con, IPP_NOT_POSSIBLE, _("No active jobs on %s!"),
3960 printer->name);
3961 return;
3962 }
b423cd4c 3963 }
3964 }
3965 }
3966 }
3967 else
3968 {
3969 /*
3970 * Got a job URI; parse it to get the job ID...
3971 */
ef416fc2 3972
f7deaa1a 3973 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
3974 sizeof(scheme), username, sizeof(username), host,
b423cd4c 3975 sizeof(host), &port, resource, sizeof(resource));
f7deaa1a 3976
b423cd4c 3977 if (strncmp(resource, "/jobs/", 6))
3978 {
3979 /*
3980 * Not a valid URI!
3981 */
ef416fc2 3982
b423cd4c 3983 send_ipp_status(con, IPP_BAD_REQUEST,
3984 _("Bad job-uri attribute \"%s\"!"),
3985 uri->values[0].string.text);
3986 return;
3987 }
3988
3989 jobid = atoi(resource + 6);
3990 }
3991
0a682745
MS
3992 /*
3993 * Look for the "purge-job" attribute...
3994 */
3995
3996 if ((attr = ippFindAttribute(con->request, "purge-job",
3997 IPP_TAG_BOOLEAN)) != NULL)
3998 purge = attr->values[0].boolean;
3999 else
4000 purge = 0;
4001
b423cd4c 4002 /*
4003 * See if the job exists...
4004 */
4005
4006 if ((job = cupsdFindJob(jobid)) == NULL)
4007 {
4008 /*
4009 * Nope - return a "not found" error...
4010 */
4011
4012 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
4013 return;
4014 }
4015
4016 /*
4017 * See if the job is owned by the requesting user...
4018 */
4019
4020 if (!validate_user(job, con, job->username, username, sizeof(username)))
4021 {
2fb76298 4022 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
b423cd4c 4023 return;
4024 }
4025
4026 /*
d09495fa 4027 * See if the job is already completed, canceled, or aborted; if so,
b423cd4c 4028 * we can't cancel...
4029 */
4030
0a682745 4031 if (job->state_value >= IPP_JOB_CANCELED && !purge)
b423cd4c 4032 {
e1d6a774 4033 switch (job->state_value)
4034 {
d09495fa 4035 case IPP_JOB_CANCELED :
e1d6a774 4036 send_ipp_status(con, IPP_NOT_POSSIBLE,
d09495fa 4037 _("Job #%d is already canceled - can\'t cancel."),
e1d6a774 4038 jobid);
4039 break;
4040
4041 case IPP_JOB_ABORTED :
4042 send_ipp_status(con, IPP_NOT_POSSIBLE,
4043 _("Job #%d is already aborted - can\'t cancel."),
4044 jobid);
4045 break;
4046
4047 default :
4048 send_ipp_status(con, IPP_NOT_POSSIBLE,
4049 _("Job #%d is already completed - can\'t cancel."),
4050 jobid);
4051 break;
4052 }
4053
b423cd4c 4054 return;
4055 }
ef416fc2 4056
b423cd4c 4057 /*
4058 * Cancel the job and return...
4059 */
ef416fc2 4060
0a682745 4061 cupsdCancelJob(job, purge, IPP_JOB_CANCELED);
b423cd4c 4062 cupsdCheckJobs();
ef416fc2 4063
0a682745
MS
4064 if (purge)
4065 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Purged by \"%s\".", jobid,
75bd9771 4066 username);
0a682745
MS
4067 else
4068 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Canceled by \"%s\".", jobid,
75bd9771 4069 username);
ef416fc2 4070
b423cd4c 4071 con->response->request.status.status_code = IPP_OK;
4072}
ef416fc2 4073
ef416fc2 4074
b423cd4c 4075/*
4076 * 'cancel_subscription()' - Cancel a subscription.
4077 */
ef416fc2 4078
b423cd4c 4079static void
4080cancel_subscription(
4081 cupsd_client_t *con, /* I - Client connection */
4082 int sub_id) /* I - Subscription ID */
4083{
4084 http_status_t status; /* Policy status */
4085 cupsd_subscription_t *sub; /* Subscription */
ef416fc2 4086
ef416fc2 4087
b423cd4c 4088 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4089 "cancel_subscription(con=%p[%d], sub_id=%d)",
4090 con, con->http.fd, sub_id);
ef416fc2 4091
b423cd4c 4092 /*
4093 * Is the subscription ID valid?
4094 */
ef416fc2 4095
b423cd4c 4096 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
4097 {
4098 /*
4099 * Bad subscription ID...
4100 */
ef416fc2 4101
b423cd4c 4102 send_ipp_status(con, IPP_NOT_FOUND,
4103 _("notify-subscription-id %d no good!"), sub_id);
4104 return;
4105 }
ef416fc2 4106
b423cd4c 4107 /*
4108 * Check policy...
4109 */
ef416fc2 4110
b423cd4c 4111 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
4112 DefaultPolicyPtr,
4113 con, sub->owner)) != HTTP_OK)
4114 {
f899b121 4115 send_http_error(con, status, sub->dest);
b423cd4c 4116 return;
4117 }
ef416fc2 4118
b423cd4c 4119 /*
4120 * Cancel the subscription...
4121 */
ef416fc2 4122
b423cd4c 4123 cupsdDeleteSubscription(sub, 1);
ef416fc2 4124
b423cd4c 4125 con->response->request.status.status_code = IPP_OK;
ef416fc2 4126}
4127
4128
3dfe78b3
MS
4129/*
4130 * 'check_rss_recipient()' - Check that we do not have a duplicate RSS feed URI.
4131 */
4132
4133static int /* O - 1 if OK, 0 if not */
4134check_rss_recipient(
4135 const char *recipient) /* I - Recipient URI */
4136{
4137 cupsd_subscription_t *sub; /* Current subscription */
4138
4139
4140 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
4141 sub;
4142 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
4143 if (sub->recipient)
4144 {
4145 /*
4146 * Compare the URIs up to the first ?...
4147 */
4148
4149 const char *r1, *r2;
4150
4151 for (r1 = recipient, r2 = sub->recipient;
4152 *r1 == *r2 && *r1 && *r1 != '?' && *r2 && *r2 != '?';
4153 r1 ++, r2 ++);
4154
4155 if (*r1 == *r2)
4156 return (0);
4157 }
4158
4159 return (1);
4160}
4161
4162
ef416fc2 4163/*
b423cd4c 4164 * 'check_quotas()' - Check quotas for a printer and user.
ef416fc2 4165 */
4166
b423cd4c 4167static int /* O - 1 if OK, 0 if not */
4168check_quotas(cupsd_client_t *con, /* I - Client connection */
4169 cupsd_printer_t *p) /* I - Printer or class */
ef416fc2 4170{
b423cd4c 4171 int i; /* Looping var */
4172 char username[33]; /* Username */
4173 cupsd_quota_t *q; /* Quota data */
7594b224 4174#ifdef HAVE_MBR_UID_TO_UUID
4175 /*
4176 * Use Apple membership APIs which require that all names represent
4177 * valid user account or group records accessible by the server.
4178 */
4179
4180 uuid_t usr_uuid; /* UUID for job requesting user */
4181 uuid_t usr2_uuid; /* UUID for ACL user name entry */
4182 uuid_t grp_uuid; /* UUID for ACL group name entry */
4183 int mbr_err; /* Error from membership function */
4184 int is_member; /* Is this user a member? */
4185#else
4186 /*
4187 * Use standard POSIX APIs for checking users and groups...
4188 */
4189
b423cd4c 4190 struct passwd *pw; /* User password data */
7594b224 4191#endif /* HAVE_MBR_UID_TO_UUID */
ef416fc2 4192
4193
b423cd4c 4194 cupsdLogMessage(CUPSD_LOG_DEBUG2, "check_quotas(%p[%d], %p[%s])",
4195 con, con->http.fd, p, p->name);
ef416fc2 4196
b423cd4c 4197 /*
4198 * Figure out who is printing...
4199 */
4200
4201 strlcpy(username, get_username(con), sizeof(username));
4202
4203 /*
4204 * Check global active job limits for printers and users...
4205 */
4206
4207 if (MaxJobsPerPrinter)
ef416fc2 4208 {
b423cd4c 4209 /*
4210 * Check if there are too many pending jobs on this printer...
4211 */
4212
4213 if (cupsdGetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
4214 {
4215 cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for printer \"%s\"...",
4216 p->name);
4217 return (0);
4218 }
4219 }
4220
4221 if (MaxJobsPerUser)
4222 {
4223 /*
4224 * Check if there are too many pending jobs for this user...
4225 */
4226
4227 if (cupsdGetUserJobCount(username) >= MaxJobsPerUser)
4228 {
4229 cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for user \"%s\"...",
4230 username);
4231 return (0);
4232 }
ef416fc2 4233 }
4234
4235 /*
b423cd4c 4236 * Check against users...
ef416fc2 4237 */
4238
b423cd4c 4239 if (p->num_users == 0 && p->k_limit == 0 && p->page_limit == 0)
4240 return (1);
4241
4242 if (p->num_users)
4243 {
7594b224 4244#ifdef HAVE_MBR_UID_TO_UUID
4245 /*
4246 * Get UUID for job requesting user...
4247 */
4248
4249 if (mbr_user_name_to_uuid((char *)username, usr_uuid))
4250 {
4251 /*
4252 * Unknown user...
4253 */
4254
4255 cupsdLogMessage(CUPSD_LOG_DEBUG,
4256 "check_quotas: UUID lookup failed for user \"%s\"",
4257 username);
4258 cupsdLogMessage(CUPSD_LOG_INFO,
4259 "Denying user \"%s\" access to printer \"%s\" "
4260 "(unknown user)...",
4261 username, p->name);
4262 return (0);
4263 }
4264#else
4265 /*
4266 * Get UID and GID of requesting user...
4267 */
4268
b423cd4c 4269 pw = getpwnam(username);
4270 endpwent();
7594b224 4271#endif /* HAVE_MBR_UID_TO_UUID */
b423cd4c 4272
4273 for (i = 0; i < p->num_users; i ++)
4274 if (p->users[i][0] == '@')
4275 {
4276 /*
4277 * Check group membership...
4278 */
4279
7594b224 4280#ifdef HAVE_MBR_UID_TO_UUID
c9fc04c6
MS
4281 if (p->users[i][1] == '#')
4282 {
4283 if (uuid_parse((char *)p->users[i] + 2, grp_uuid))
4284 uuid_clear(grp_uuid);
4285 }
4286 else if ((mbr_err = mbr_group_name_to_uuid((char *)p->users[i] + 1,
4287 grp_uuid)) != 0)
7594b224 4288 {
4289 /*
4290 * Invalid ACL entries are ignored for matching; just record a
4291 * warning in the log...
4292 */
4293
4294 cupsdLogMessage(CUPSD_LOG_DEBUG,
4295 "check_quotas: UUID lookup failed for ACL entry "
4296 "\"%s\" (err=%d)", p->users[i], mbr_err);
4297 cupsdLogMessage(CUPSD_LOG_WARN,
4298 "Access control entry \"%s\" not a valid group name; "
4299 "entry ignored", p->users[i]);
4300 }
7594b224 4301
c9fc04c6
MS
4302 if ((mbr_err = mbr_check_membership(usr_uuid, grp_uuid,
4303 &is_member)) != 0)
4304 {
4305 /*
4306 * At this point, there should be no errors, but check anyways...
7594b224 4307 */
4308
c9fc04c6
MS
4309 cupsdLogMessage(CUPSD_LOG_DEBUG,
4310 "check_quotas: group \"%s\" membership check "
4311 "failed (err=%d)", p->users[i] + 1, mbr_err);
4312 is_member = 0;
7594b224 4313 }
c9fc04c6
MS
4314
4315 /*
4316 * Stop if we found a match...
4317 */
4318
4319 if (is_member)
4320 break;
4321
7594b224 4322#else
b423cd4c 4323 if (cupsdCheckGroup(username, pw, p->users[i] + 1))
4324 break;
7594b224 4325#endif /* HAVE_MBR_UID_TO_UUID */
4326 }
4327#ifdef HAVE_MBR_UID_TO_UUID
4328 else
4329 {
c9fc04c6
MS
4330 if (p->users[i][0] == '#')
4331 {
4332 if (uuid_parse((char *)p->users[i] + 1, usr2_uuid))
4333 uuid_clear(usr2_uuid);
4334 }
4335 else if ((mbr_err = mbr_user_name_to_uuid((char *)p->users[i],
4336 usr2_uuid)) != 0)
7594b224 4337 {
4338 /*
4339 * Invalid ACL entries are ignored for matching; just record a
4340 * warning in the log...
4341 */
4342
4343 cupsdLogMessage(CUPSD_LOG_DEBUG,
4344 "check_quotas: UUID lookup failed for ACL entry "
4345 "\"%s\" (err=%d)", p->users[i], mbr_err);
4346 cupsdLogMessage(CUPSD_LOG_WARN,
4347 "Access control entry \"%s\" not a valid user name; "
4348 "entry ignored", p->users[i]);
4349 }
7594b224 4350
c9fc04c6
MS
4351 if ((mbr_err = mbr_check_membership(usr_uuid, usr2_uuid,
4352 &is_member)) != 0)
4353 {
4354 cupsdLogMessage(CUPSD_LOG_DEBUG,
4355 "check_quotas: User \"%s\" identity check failed "
4356 "(err=%d)", p->users[i], mbr_err);
4357 is_member = 0;
7594b224 4358 }
c9fc04c6
MS
4359
4360 if (is_member)
4361 break;
b423cd4c 4362 }
7594b224 4363#else
b423cd4c 4364 else if (!strcasecmp(username, p->users[i]))
4365 break;
7594b224 4366#endif /* HAVE_MBR_UID_TO_UUID */
b423cd4c 4367
4368 if ((i < p->num_users) == p->deny_users)
ef416fc2 4369 {
b423cd4c 4370 cupsdLogMessage(CUPSD_LOG_INFO,
4371 "Denying user \"%s\" access to printer \"%s\"...",
4372 username, p->name);
4373 return (0);
ef416fc2 4374 }
b423cd4c 4375 }
ef416fc2 4376
4377 /*
b423cd4c 4378 * Check quotas...
ef416fc2 4379 */
4380
7594b224 4381#ifdef __APPLE__
3d8365b8 4382 if (AppleQuotas && (q = cupsdFindQuota(p, username)) != NULL)
7594b224 4383 {
4384 /*
4385 * TODO: Define these special page count values as constants!
4386 */
4387
4388 if (q->page_count == -4) /* special case: unlimited user */
4389 {
4390 cupsdLogMessage(CUPSD_LOG_INFO,
4391 "User \"%s\" request approved for printer %s (%s): "
4392 "unlimited quota.",
4393 username, p->name, p->info);
4394 q->page_count = 0; /* allow user to print */
4395 return (1);
4396 }
4397 else if (q->page_count == -3) /* quota exceeded */
4398 {
4399 cupsdLogMessage(CUPSD_LOG_INFO,
4400 "User \"%s\" request denied for printer %s (%s): "
4401 "quota limit exceeded.",
4402 username, p->name, p->info);
4403 q->page_count = 2; /* force quota exceeded failure */
bc44d920 4404 return (-1);
7594b224 4405 }
4406 else if (q->page_count == -2) /* quota disabled for user */
4407 {
4408 cupsdLogMessage(CUPSD_LOG_INFO,
4409 "User \"%s\" request denied for printer %s (%s): "
4410 "printing disabled for user.",
4411 username, p->name, p->info);
4412 q->page_count = 2; /* force quota exceeded failure */
bc44d920 4413 return (-1);
7594b224 4414 }
4415 else if (q->page_count == -1) /* quota access error */
4416 {
4417 cupsdLogMessage(CUPSD_LOG_INFO,
4418 "User \"%s\" request denied for printer %s (%s): "
4419 "unable to determine quota limit.",
4420 username, p->name, p->info);
4421 q->page_count = 2; /* force quota exceeded failure */
bc44d920 4422 return (-1);
7594b224 4423 }
4424 else if (q->page_count < 0) /* user not found or other error */
4425 {
4426 cupsdLogMessage(CUPSD_LOG_INFO,
4427 "User \"%s\" request denied for printer %s (%s): "
4428 "user disabled / missing quota.",
4429 username, p->name, p->info);
4430 q->page_count = 2; /* force quota exceeded failure */
bc44d920 4431 return (-1);
7594b224 4432 }
4433 else /* page within user limits */
4434 {
4435 q->page_count = 0; /* allow user to print */
4436 return (1);
4437 }
4438 }
4439 else
4440#endif /* __APPLE__ */
b423cd4c 4441 if (p->k_limit || p->page_limit)
4442 {
4443 if ((q = cupsdUpdateQuota(p, username, 0, 0)) == NULL)
4444 {
4445 cupsdLogMessage(CUPSD_LOG_ERROR,
4446 "Unable to allocate quota data for user \"%s\"!",
4447 username);
bc44d920 4448 return (-1);
b423cd4c 4449 }
ef416fc2 4450
b423cd4c 4451 if ((q->k_count >= p->k_limit && p->k_limit) ||
4452 (q->page_count >= p->page_limit && p->page_limit))
4453 {
4454 cupsdLogMessage(CUPSD_LOG_INFO, "User \"%s\" is over the quota limit...",
4455 username);
bc44d920 4456 return (-1);
b423cd4c 4457 }
4458 }
4459
4460 /*
4461 * If we have gotten this far, we're done!
4462 */
4463
4464 return (1);
ef416fc2 4465}
4466
4467
4468/*
b423cd4c 4469 * 'copy_attribute()' - Copy a single attribute.
ef416fc2 4470 */
4471
b423cd4c 4472static ipp_attribute_t * /* O - New attribute */
4473copy_attribute(
4474 ipp_t *to, /* O - Destination request/response */
4475 ipp_attribute_t *attr, /* I - Attribute to copy */
4476 int quickcopy) /* I - Do a quick copy? */
ef416fc2 4477{
b423cd4c 4478 int i; /* Looping var */
4479 ipp_attribute_t *toattr; /* Destination attribute */
ef416fc2 4480
4481
4482 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b423cd4c 4483 "copy_attribute(%p, %p[%s,%x,%x])", to, attr,
4484 attr->name ? attr->name : "(null)", attr->group_tag,
4485 attr->value_tag);
ef416fc2 4486
b423cd4c 4487 switch (attr->value_tag & ~IPP_TAG_COPY)
4488 {
4489 case IPP_TAG_ZERO :
4490 toattr = ippAddSeparator(to);
4491 break;
ef416fc2 4492
b423cd4c 4493 case IPP_TAG_INTEGER :
4494 case IPP_TAG_ENUM :
4495 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
4496 attr->name, attr->num_values, NULL);
ef416fc2 4497
b423cd4c 4498 for (i = 0; i < attr->num_values; i ++)
4499 toattr->values[i].integer = attr->values[i].integer;
4500 break;
ef416fc2 4501
b423cd4c 4502 case IPP_TAG_BOOLEAN :
4503 toattr = ippAddBooleans(to, attr->group_tag, attr->name,
4504 attr->num_values, NULL);
ef416fc2 4505
b423cd4c 4506 for (i = 0; i < attr->num_values; i ++)
4507 toattr->values[i].boolean = attr->values[i].boolean;
4508 break;
ef416fc2 4509
b423cd4c 4510 case IPP_TAG_STRING :
4511 case IPP_TAG_TEXT :
4512 case IPP_TAG_NAME :
4513 case IPP_TAG_KEYWORD :
4514 case IPP_TAG_URI :
4515 case IPP_TAG_URISCHEME :
4516 case IPP_TAG_CHARSET :
4517 case IPP_TAG_LANGUAGE :
4518 case IPP_TAG_MIMETYPE :
4519 toattr = ippAddStrings(to, attr->group_tag,
4520 (ipp_tag_t)(attr->value_tag | quickcopy),
4521 attr->name, attr->num_values, NULL, NULL);
ef416fc2 4522
b423cd4c 4523 if (quickcopy)
4524 {
4525 for (i = 0; i < attr->num_values; i ++)
4526 toattr->values[i].string.text = attr->values[i].string.text;
4527 }
4528 else
4529 {
4530 for (i = 0; i < attr->num_values; i ++)
bc44d920 4531 toattr->values[i].string.text =
4532 _cupsStrAlloc(attr->values[i].string.text);
b423cd4c 4533 }
4534 break;
ef416fc2 4535
b423cd4c 4536 case IPP_TAG_DATE :
4537 toattr = ippAddDate(to, attr->group_tag, attr->name,
4538 attr->values[0].date);
4539 break;
ef416fc2 4540
b423cd4c 4541 case IPP_TAG_RESOLUTION :
4542 toattr = ippAddResolutions(to, attr->group_tag, attr->name,
4543 attr->num_values, IPP_RES_PER_INCH,
4544 NULL, NULL);
ef416fc2 4545
b423cd4c 4546 for (i = 0; i < attr->num_values; i ++)
4547 {
4548 toattr->values[i].resolution.xres = attr->values[i].resolution.xres;
4549 toattr->values[i].resolution.yres = attr->values[i].resolution.yres;
4550 toattr->values[i].resolution.units = attr->values[i].resolution.units;
4551 }
4552 break;
ef416fc2 4553
b423cd4c 4554 case IPP_TAG_RANGE :
4555 toattr = ippAddRanges(to, attr->group_tag, attr->name,
4556 attr->num_values, NULL, NULL);
ef416fc2 4557
b423cd4c 4558 for (i = 0; i < attr->num_values; i ++)
4559 {
4560 toattr->values[i].range.lower = attr->values[i].range.lower;
4561 toattr->values[i].range.upper = attr->values[i].range.upper;
4562 }
4563 break;
ef416fc2 4564
b423cd4c 4565 case IPP_TAG_TEXTLANG :
4566 case IPP_TAG_NAMELANG :
4567 toattr = ippAddStrings(to, attr->group_tag,
4568 (ipp_tag_t)(attr->value_tag | quickcopy),
4569 attr->name, attr->num_values, NULL, NULL);
ef416fc2 4570
b423cd4c 4571 if (quickcopy)
4572 {
4573 for (i = 0; i < attr->num_values; i ++)
4574 {
4575 toattr->values[i].string.charset = attr->values[i].string.charset;
4576 toattr->values[i].string.text = attr->values[i].string.text;
4577 }
4578 }
4579 else
4580 {
4581 for (i = 0; i < attr->num_values; i ++)
4582 {
4583 if (!i)
4584 toattr->values[i].string.charset =
757d2cad 4585 _cupsStrAlloc(attr->values[i].string.charset);
b423cd4c 4586 else
4587 toattr->values[i].string.charset =
4588 toattr->values[0].string.charset;
ef416fc2 4589
bc44d920 4590 toattr->values[i].string.text =
4591 _cupsStrAlloc(attr->values[i].string.text);
b423cd4c 4592 }
4593 }
4594 break;
ef416fc2 4595
b423cd4c 4596 case IPP_TAG_BEGIN_COLLECTION :
4597 toattr = ippAddCollections(to, attr->group_tag, attr->name,
4598 attr->num_values, NULL);
ef416fc2 4599
b423cd4c 4600 for (i = 0; i < attr->num_values; i ++)
4601 {
4602 toattr->values[i].collection = ippNew();
4603 copy_attrs(toattr->values[i].collection, attr->values[i].collection,
4604 NULL, IPP_TAG_ZERO, 0);
4605 }
ef416fc2 4606 break;
ef416fc2 4607
b423cd4c 4608 default :
4609 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
4610 attr->name, attr->num_values, NULL);
ef416fc2 4611
b423cd4c 4612 for (i = 0; i < attr->num_values; i ++)
4613 {
4614 toattr->values[i].unknown.length = attr->values[i].unknown.length;
ef416fc2 4615
b423cd4c 4616 if (toattr->values[i].unknown.length > 0)
4617 {
4618 if ((toattr->values[i].unknown.data =
4619 malloc(toattr->values[i].unknown.length)) == NULL)
4620 toattr->values[i].unknown.length = 0;
4621 else
4622 memcpy(toattr->values[i].unknown.data,
4623 attr->values[i].unknown.data,
4624 toattr->values[i].unknown.length);
4625 }
4626 }
4627 break; /* anti-compiler-warning-code */
4628 }
ef416fc2 4629
b423cd4c 4630 return (toattr);
4631}
4632
4633
4634/*
4635 * 'copy_attrs()' - Copy attributes from one request to another.
4636 */
4637
4638static void
4639copy_attrs(ipp_t *to, /* I - Destination request */
4640 ipp_t *from, /* I - Source request */
4641 cups_array_t *ra, /* I - Requested attributes */
4642 ipp_tag_t group, /* I - Group to copy */
4643 int quickcopy) /* I - Do a quick copy? */
4644{
4645 ipp_attribute_t *fromattr; /* Source attribute */
ef416fc2 4646
ef416fc2 4647
b423cd4c 4648 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4649 "copy_attrs(to=%p, from=%p, ra=%p, group=%x, quickcopy=%d)",
4650 to, from, ra, group, quickcopy);
ef416fc2 4651
b423cd4c 4652 if (!to || !from)
4653 return;
e00b005a 4654
b423cd4c 4655 for (fromattr = from->attrs; fromattr; fromattr = fromattr->next)
ef416fc2 4656 {
4657 /*
b423cd4c 4658 * Filter attributes as needed...
ef416fc2 4659 */
4660
2abf387c 4661 if ((group != IPP_TAG_ZERO && fromattr->group_tag != group &&
4662 fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
b423cd4c 4663 continue;
4664
4665 if (!ra || cupsArrayFind(ra, fromattr->name))
4666 copy_attribute(to, fromattr, quickcopy);
ef416fc2 4667 }
b423cd4c 4668}
ef416fc2 4669
ef416fc2 4670
b423cd4c 4671/*
4672 * 'copy_banner()' - Copy a banner file to the requests directory for the
4673 * specified job.
4674 */
ef416fc2 4675
b423cd4c 4676static int /* O - Size of banner file in kbytes */
4677copy_banner(cupsd_client_t *con, /* I - Client connection */
4678 cupsd_job_t *job, /* I - Job information */
4679 const char *name) /* I - Name of banner */
4680{
4681 int i; /* Looping var */
4682 int kbytes; /* Size of banner file in kbytes */
4683 char filename[1024]; /* Job filename */
4684 cupsd_banner_t *banner; /* Pointer to banner */
4685 cups_file_t *in; /* Input file */
4686 cups_file_t *out; /* Output file */
4687 int ch; /* Character from file */
4688 char attrname[255], /* Name of attribute */
4689 *s; /* Pointer into name */
4690 ipp_attribute_t *attr; /* Attribute */
ef416fc2 4691
ef416fc2 4692
3dfe78b3
MS
4693 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4694 "copy_banner(con=%p[%d], job=%p[%d], name=\"%s\")",
09a101d6 4695 con, con ? con->http.fd : -1, job, job->id,
4696 name ? name : "(null)");
ef416fc2 4697
b423cd4c 4698 /*
4699 * Find the banner; return if not found or "none"...
4700 */
ef416fc2 4701
b423cd4c 4702 if (!name || !strcmp(name, "none") ||
4703 (banner = cupsdFindBanner(name)) == NULL)
4704 return (0);
ef416fc2 4705
b423cd4c 4706 /*
4707 * Open the banner and job files...
4708 */
ef416fc2 4709
b423cd4c 4710 if (add_file(con, job, banner->filetype, 0))
91c84a35 4711 return (-1);
ef416fc2 4712
b423cd4c 4713 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
4714 job->num_files);
4715 if ((out = cupsFileOpen(filename, "w")) == NULL)
4716 {
4717 cupsdLogMessage(CUPSD_LOG_ERROR,
3dfe78b3 4718 "Unable to create banner job file %s - %s",
b423cd4c 4719 filename, strerror(errno));
4720 job->num_files --;
4721 return (0);
4722 }
4723
4724 fchmod(cupsFileNumber(out), 0640);
4725 fchown(cupsFileNumber(out), RunUser, Group);
ef416fc2 4726
4727 /*
b423cd4c 4728 * Try the localized banner file under the subdirectory...
ef416fc2 4729 */
4730
80ca4592 4731 strlcpy(attrname, job->attrs->attrs->next->values[0].string.text,
b423cd4c 4732 sizeof(attrname));
4733 if (strlen(attrname) > 2 && attrname[2] == '-')
ef416fc2 4734 {
4735 /*
b423cd4c 4736 * Convert ll-cc to ll_CC...
ef416fc2 4737 */
4738
b423cd4c 4739 attrname[2] = '_';
4740 attrname[3] = toupper(attrname[3] & 255);
4741 attrname[4] = toupper(attrname[4] & 255);
4742 }
ef416fc2 4743
b423cd4c 4744 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
4745 attrname, name);
ef416fc2 4746
b423cd4c 4747 if (access(filename, 0) && strlen(attrname) > 2)
ef416fc2 4748 {
4749 /*
b423cd4c 4750 * Wasn't able to find "ll_CC" locale file; try the non-national
4751 * localization banner directory.
ef416fc2 4752 */
4753
b423cd4c 4754 attrname[2] = '\0';
ef416fc2 4755
b423cd4c 4756 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
4757 attrname, name);
ef416fc2 4758 }
b423cd4c 4759
4760 if (access(filename, 0))
ef416fc2 4761 {
4762 /*
b423cd4c 4763 * Use the non-localized banner file.
ef416fc2 4764 */
4765
b423cd4c 4766 snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
4767 }
ef416fc2 4768
b423cd4c 4769 if ((in = cupsFileOpen(filename, "r")) == NULL)
4770 {
4771 cupsFileClose(out);
4772 unlink(filename);
4773 cupsdLogMessage(CUPSD_LOG_ERROR,
3dfe78b3 4774 "Unable to open banner template file %s - %s",
b423cd4c 4775 filename, strerror(errno));
4776 job->num_files --;
4777 return (0);
ef416fc2 4778 }
4779
4780 /*
b423cd4c 4781 * Parse the file to the end...
ef416fc2 4782 */
4783
b423cd4c 4784 while ((ch = cupsFileGetChar(in)) != EOF)
4785 if (ch == '{')
4786 {
4787 /*
4788 * Get an attribute name...
4789 */
ef416fc2 4790
b423cd4c 4791 for (s = attrname; (ch = cupsFileGetChar(in)) != EOF;)
4792 if (!isalpha(ch & 255) && ch != '-' && ch != '?')
4793 break;
4794 else if (s < (attrname + sizeof(attrname) - 1))
4795 *s++ = ch;
4796 else
4797 break;
ef416fc2 4798
b423cd4c 4799 *s = '\0';
4800
4801 if (ch != '}')
4802 {
4803 /*
4804 * Ignore { followed by stuff that is not an attribute name...
4805 */
4806
4807 cupsFilePrintf(out, "{%s%c", attrname, ch);
4808 continue;
4809 }
ef416fc2 4810
ef416fc2 4811 /*
b423cd4c 4812 * See if it is defined...
ef416fc2 4813 */
4814
b423cd4c 4815 if (attrname[0] == '?')
4816 s = attrname + 1;
4817 else
4818 s = attrname;
ef416fc2 4819
b423cd4c 4820 if (!strcmp(s, "printer-name"))
4821 {
4822 cupsFilePuts(out, job->dest);
4823 continue;
ef416fc2 4824 }
b423cd4c 4825 else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
4826 {
4827 /*
4828 * See if we have a leading question mark...
4829 */
ef416fc2 4830
b423cd4c 4831 if (attrname[0] != '?')
4832 {
4833 /*
4834 * Nope, write to file as-is; probably a PostScript procedure...
4835 */
ef416fc2 4836
b423cd4c 4837 cupsFilePrintf(out, "{%s}", attrname);
4838 }
ef416fc2 4839
b423cd4c 4840 continue;
4841 }
ef416fc2 4842
b423cd4c 4843 /*
4844 * Output value(s)...
4845 */
ef416fc2 4846
b423cd4c 4847 for (i = 0; i < attr->num_values; i ++)
4848 {
4849 if (i)
4850 cupsFilePutChar(out, ',');
ef416fc2 4851
b423cd4c 4852 switch (attr->value_tag)
4853 {
4854 case IPP_TAG_INTEGER :
4855 case IPP_TAG_ENUM :
4856 if (!strncmp(s, "time-at-", 8))
4857 cupsFilePuts(out, cupsdGetDateTime(attr->values[i].integer));
4858 else
4859 cupsFilePrintf(out, "%d", attr->values[i].integer);
4860 break;
ef416fc2 4861
b423cd4c 4862 case IPP_TAG_BOOLEAN :
4863 cupsFilePrintf(out, "%d", attr->values[i].boolean);
4864 break;
ef416fc2 4865
b423cd4c 4866 case IPP_TAG_NOVALUE :
4867 cupsFilePuts(out, "novalue");
4868 break;
ef416fc2 4869
b423cd4c 4870 case IPP_TAG_RANGE :
4871 cupsFilePrintf(out, "%d-%d", attr->values[i].range.lower,
4872 attr->values[i].range.upper);
4873 break;
fa73b229 4874
b423cd4c 4875 case IPP_TAG_RESOLUTION :
4876 cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
4877 attr->values[i].resolution.yres,
4878 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
4879 "dpi" : "dpc");
4880 break;
fa73b229 4881
b423cd4c 4882 case IPP_TAG_URI :
4883 case IPP_TAG_STRING :
4884 case IPP_TAG_TEXT :
4885 case IPP_TAG_NAME :
4886 case IPP_TAG_KEYWORD :
4887 case IPP_TAG_CHARSET :
4888 case IPP_TAG_LANGUAGE :
4889 if (!strcasecmp(banner->filetype->type, "postscript"))
4890 {
4891 /*
4892 * Need to quote strings for PS banners...
4893 */
fa73b229 4894
b423cd4c 4895 const char *p;
fa73b229 4896
b423cd4c 4897 for (p = attr->values[i].string.text; *p; p ++)
4898 {
4899 if (*p == '(' || *p == ')' || *p == '\\')
4900 {
4901 cupsFilePutChar(out, '\\');
4902 cupsFilePutChar(out, *p);
4903 }
4904 else if (*p < 32 || *p > 126)
4905 cupsFilePrintf(out, "\\%03o", *p & 255);
4906 else
4907 cupsFilePutChar(out, *p);
4908 }
4909 }
4910 else
4911 cupsFilePuts(out, attr->values[i].string.text);
4912 break;
fa73b229 4913
b423cd4c 4914 default :
4915 break; /* anti-compiler-warning-code */
4916 }
4917 }
4918 }
4919 else if (ch == '\\') /* Quoted char */
4920 {
4921 ch = cupsFileGetChar(in);
fa73b229 4922
b423cd4c 4923 if (ch != '{') /* Only do special handling for \{ */
4924 cupsFilePutChar(out, '\\');
fa73b229 4925
b423cd4c 4926 cupsFilePutChar(out, ch);
4927 }
4928 else
4929 cupsFilePutChar(out, ch);
fa73b229 4930
b423cd4c 4931 cupsFileClose(in);
fa73b229 4932
b423cd4c 4933 kbytes = (cupsFileTell(out) + 1023) / 1024;
fa73b229 4934
b423cd4c 4935 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
4936 IPP_TAG_INTEGER)) != NULL)
4937 attr->values[0].integer += kbytes;
4938
4939 cupsFileClose(out);
4940
4941 return (kbytes);
fa73b229 4942}
4943
4944
4945/*
b423cd4c 4946 * 'copy_file()' - Copy a PPD file or interface script...
fa73b229 4947 */
4948
b423cd4c 4949static int /* O - 0 = success, -1 = error */
4950copy_file(const char *from, /* I - Source file */
4951 const char *to) /* I - Destination file */
fa73b229 4952{
b423cd4c 4953 cups_file_t *src, /* Source file */
4954 *dst; /* Destination file */
4955 int bytes; /* Bytes to read/write */
4956 char buffer[2048]; /* Copy buffer */
fa73b229 4957
4958
b423cd4c 4959 cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_file(\"%s\", \"%s\")", from, to);
4960
fa73b229 4961 /*
b423cd4c 4962 * Open the source and destination file for a copy...
fa73b229 4963 */
4964
b423cd4c 4965 if ((src = cupsFileOpen(from, "rb")) == NULL)
4966 return (-1);
fa73b229 4967
b423cd4c 4968 if ((dst = cupsFileOpen(to, "wb")) == NULL)
4969 {
4970 cupsFileClose(src);
4971 return (-1);
4972 }
fa73b229 4973
b423cd4c 4974 /*
4975 * Copy the source file to the destination...
4976 */
fa73b229 4977
b423cd4c 4978 while ((bytes = cupsFileRead(src, buffer, sizeof(buffer))) > 0)
4979 if (cupsFileWrite(dst, buffer, bytes) < bytes)
4980 {
4981 cupsFileClose(src);
4982 cupsFileClose(dst);
4983 return (-1);
4984 }
fa73b229 4985
b423cd4c 4986 /*
4987 * Close both files and return...
4988 */
fa73b229 4989
b423cd4c 4990 cupsFileClose(src);
fa73b229 4991
b423cd4c 4992 return (cupsFileClose(dst));
4993}
fa73b229 4994
fa73b229 4995
b423cd4c 4996/*
4997 * 'copy_model()' - Copy a PPD model file, substituting default values
4998 * as needed...
4999 */
e00b005a 5000
b423cd4c 5001static int /* O - 0 = success, -1 = error */
5002copy_model(cupsd_client_t *con, /* I - Client connection */
5003 const char *from, /* I - Source file */
5004 const char *to) /* I - Destination file */
5005{
f7deaa1a 5006 fd_set input; /* select() input set */
b423cd4c 5007 struct timeval timeout; /* select() timeout */
bc44d920 5008 int maxfd; /* Max file descriptor for select() */
b423cd4c 5009 char tempfile[1024]; /* Temporary PPD file */
5010 int tempfd; /* Temporary PPD file descriptor */
5011 int temppid; /* Process ID of cups-driverd */
5012 int temppipe[2]; /* Temporary pipes */
5013 char *argv[4], /* Command-line arguments */
5014 *envp[MAX_ENV]; /* Environment */
5015 cups_file_t *src, /* Source file */
5016 *dst; /* Destination file */
2abf387c 5017 ppd_file_t *ppd; /* PPD file */
b423cd4c 5018 int bytes, /* Bytes from pipe */
5019 total; /* Total bytes from pipe */
2abf387c 5020 char buffer[2048]; /* Copy buffer */
b423cd4c 5021 int i; /* Looping var */
5022 char option[PPD_MAX_NAME], /* Option name */
5023 choice[PPD_MAX_NAME]; /* Choice name */
5024 int num_defaults; /* Number of default options */
2abf387c 5025 cups_option_t *defaults; /* Default options */
b423cd4c 5026 char cups_protocol[PPD_MAX_LINE];
5027 /* cupsProtocol attribute */
e00b005a 5028
e00b005a 5029
b423cd4c 5030 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5031 "copy_model(con=%p, from=\"%s\", to=\"%s\")",
5032 con, from, to);
e00b005a 5033
b423cd4c 5034 /*
5035 * Run cups-driverd to get the PPD file...
5036 */
e00b005a 5037
b423cd4c 5038 argv[0] = "cups-driverd";
5039 argv[1] = "cat";
5040 argv[2] = (char *)from;
5041 argv[3] = NULL;
e00b005a 5042
b423cd4c 5043 cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
e00b005a 5044
b423cd4c 5045 snprintf(buffer, sizeof(buffer), "%s/daemon/cups-driverd", ServerBin);
5046 snprintf(tempfile, sizeof(tempfile), "%s/%d.ppd", TempDir, con->http.fd);
5047 tempfd = open(tempfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
5048 if (tempfd < 0)
5049 return (-1);
5050
5051 cupsdOpenPipe(temppipe);
5052
b423cd4c 5053 cupsdLogMessage(CUPSD_LOG_DEBUG,
5054 "copy_model: Running \"cups-driverd cat %s\"...", from);
fa73b229 5055
b423cd4c 5056 if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1],
a4924f6c 5057 -1, -1, 0, DefaultProfile, &temppid))
fa73b229 5058 {
b423cd4c 5059 close(tempfd);
5060 unlink(tempfile);
a4924f6c 5061
b423cd4c 5062 return (-1);
fa73b229 5063 }
5064
b423cd4c 5065 close(temppipe[1]);
fa73b229 5066
b423cd4c 5067 /*
5068 * Wait up to 30 seconds for the PPD file to be copied...
5069 */
fa73b229 5070
b423cd4c 5071 total = 0;
fa73b229 5072
b423cd4c 5073 if (temppipe[0] > CGIPipes[0])
5074 maxfd = temppipe[0] + 1;
5075 else
5076 maxfd = CGIPipes[0] + 1;
ef416fc2 5077
b423cd4c 5078 for (;;)
5079 {
5080 /*
5081 * See if we have data ready...
5082 */
ef416fc2 5083
f7deaa1a 5084 FD_ZERO(&input);
5085 FD_SET(temppipe[0], &input);
5086 FD_SET(CGIPipes[0], &input);
ef416fc2 5087
b423cd4c 5088 timeout.tv_sec = 30;
5089 timeout.tv_usec = 0;
5090
f7deaa1a 5091 if ((i = select(maxfd, &input, NULL, NULL, &timeout)) < 0)
b423cd4c 5092 {
5093 if (errno == EINTR)
5094 continue;
5095 else
5096 break;
5097 }
5098 else if (i == 0)
ef416fc2 5099 {
5100 /*
b423cd4c 5101 * We have timed out...
ef416fc2 5102 */
5103
b423cd4c 5104 break;
ef416fc2 5105 }
b423cd4c 5106
f7deaa1a 5107 if (FD_ISSET(temppipe[0], &input))
ef416fc2 5108 {
5109 /*
b423cd4c 5110 * Read the PPD file from the pipe, and write it to the PPD file.
ef416fc2 5111 */
5112
b423cd4c 5113 if ((bytes = read(temppipe[0], buffer, sizeof(buffer))) > 0)
5114 {
5115 if (write(tempfd, buffer, bytes) < bytes)
5116 break;
ef416fc2 5117
b423cd4c 5118 total += bytes;
5119 }
5120 else
5121 break;
ef416fc2 5122 }
b423cd4c 5123
f7deaa1a 5124 if (FD_ISSET(CGIPipes[0], &input))
b423cd4c 5125 cupsdUpdateCGI();
ef416fc2 5126 }
5127
b423cd4c 5128 close(temppipe[0]);
5129 close(tempfd);
ef416fc2 5130
b423cd4c 5131 if (!total)
ef416fc2 5132 {
b423cd4c 5133 /*
5134 * No data from cups-deviced...
5135 */
5136
5137 cupsdLogMessage(CUPSD_LOG_ERROR, "copy_model: empty PPD file!");
5138 unlink(tempfile);
5139 return (-1);
ef416fc2 5140 }
5141
b423cd4c 5142 /*
5143 * Read the source file and see what page sizes are supported...
5144 */
fa73b229 5145
2abf387c 5146 if ((ppd = ppdOpenFile(tempfile)) == NULL)
b423cd4c 5147 {
5148 unlink(tempfile);
5149 return (-1);
5150 }
fa73b229 5151
ef416fc2 5152 /*
b423cd4c 5153 * Open the destination (if possible) and set the default options...
ef416fc2 5154 */
5155
b423cd4c 5156 num_defaults = 0;
5157 defaults = NULL;
5158 cups_protocol[0] = '\0';
ef416fc2 5159
b423cd4c 5160 if ((dst = cupsFileOpen(to, "rb")) != NULL)
ef416fc2 5161 {
5162 /*
b423cd4c 5163 * Read all of the default lines from the old PPD...
ef416fc2 5164 */
5165
b423cd4c 5166 while (cupsFileGets(dst, buffer, sizeof(buffer)))
5167 if (!strncmp(buffer, "*Default", 8))
5168 {
5169 /*
5170 * Add the default option...
5171 */
ef416fc2 5172
b423cd4c 5173 if (!ppd_parse_line(buffer, option, sizeof(option),
5174 choice, sizeof(choice)))
2abf387c 5175 {
5176 ppd_option_t *ppdo; /* PPD option */
5177
5178
5179 /*
5180 * Only add the default if the default hasn't already been
5181 * set and the choice exists in the new PPD...
5182 */
5183
5184 if (!cupsGetOption(option, num_defaults, defaults) &&
5185 (ppdo = ppdFindOption(ppd, option)) != NULL &&
5186 ppdFindChoice(ppdo, choice))
5187 num_defaults = cupsAddOption(option, choice, num_defaults,
b423cd4c 5188 &defaults);
2abf387c 5189 }
b423cd4c 5190 }
5191 else if (!strncmp(buffer, "*cupsProtocol:", 14))
5192 strlcpy(cups_protocol, buffer, sizeof(cups_protocol));
ef416fc2 5193
b423cd4c 5194 cupsFileClose(dst);
ef416fc2 5195 }
c5571a1d 5196 else if (ppdPageSize(ppd, DefaultPaperSize))
ef416fc2 5197 {
b423cd4c 5198 /*
5199 * Add the default media sizes...
b423cd4c 5200 */
5201
c5571a1d
MS
5202 num_defaults = cupsAddOption("PageSize", DefaultPaperSize,
5203 num_defaults, &defaults);
5204 num_defaults = cupsAddOption("PageRegion", DefaultPaperSize,
5205 num_defaults, &defaults);
5206 num_defaults = cupsAddOption("PaperDimension", DefaultPaperSize,
5207 num_defaults, &defaults);
5208 num_defaults = cupsAddOption("ImageableArea", DefaultPaperSize,
5209 num_defaults, &defaults);
ef416fc2 5210 }
5211
2abf387c 5212 ppdClose(ppd);
5213
5214 /*
5215 * Open the source file for a copy...
5216 */
5217
5218 if ((src = cupsFileOpen(tempfile, "rb")) == NULL)
5219 {
5220 cupsFreeOptions(num_defaults, defaults);
5221 unlink(tempfile);
5222 return (-1);
5223 }
5224
ef416fc2 5225 /*
b423cd4c 5226 * Open the destination file for a copy...
ef416fc2 5227 */
5228
b423cd4c 5229 if ((dst = cupsFileOpen(to, "wb")) == NULL)
ef416fc2 5230 {
2abf387c 5231 cupsFreeOptions(num_defaults, defaults);
b423cd4c 5232 cupsFileClose(src);
5233 unlink(tempfile);
5234 return (-1);
ef416fc2 5235 }
5236
5237 /*
b423cd4c 5238 * Copy the source file to the destination...
ef416fc2 5239 */
5240
b423cd4c 5241 while (cupsFileGets(src, buffer, sizeof(buffer)))
ef416fc2 5242 {
b423cd4c 5243 if (!strncmp(buffer, "*Default", 8))
ef416fc2 5244 {
b423cd4c 5245 /*
5246 * Check for an previous default option choice...
5247 */
ef416fc2 5248
b423cd4c 5249 if (!ppd_parse_line(buffer, option, sizeof(option),
5250 choice, sizeof(choice)))
ef416fc2 5251 {
2abf387c 5252 const char *val; /* Default option value */
ef416fc2 5253
2abf387c 5254
5255 if ((val = cupsGetOption(option, num_defaults, defaults)) != NULL)
5256 {
5257 /*
5258 * Substitute the previous choice...
5259 */
5260
5261 snprintf(buffer, sizeof(buffer), "*Default%s: %s", option, val);
5262 }
b423cd4c 5263 }
ef416fc2 5264 }
b423cd4c 5265
5266 cupsFilePrintf(dst, "%s\n", buffer);
ef416fc2 5267 }
5268
b423cd4c 5269 if (cups_protocol[0])
5270 cupsFilePrintf(dst, "%s\n", cups_protocol);
5271
2abf387c 5272 cupsFreeOptions(num_defaults, defaults);
b423cd4c 5273
ef416fc2 5274 /*
b423cd4c 5275 * Close both files and return...
ef416fc2 5276 */
5277
b423cd4c 5278 cupsFileClose(src);
5279
5280 unlink(tempfile);
5281
5282 return (cupsFileClose(dst));
5283}
5284
5285
5286/*
5287 * 'copy_job_attrs()' - Copy job attributes.
5288 */
ef416fc2 5289
b423cd4c 5290static void
5291copy_job_attrs(cupsd_client_t *con, /* I - Client connection */
5292 cupsd_job_t *job, /* I - Job */
5293 cups_array_t *ra) /* I - Requested attributes array */
5294{
5295 char job_uri[HTTP_MAX_URI]; /* Job URI */
ef416fc2 5296
ef416fc2 5297
5298 /*
b423cd4c 5299 * Send the requested attributes for each job...
ef416fc2 5300 */
5301
b423cd4c 5302 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
5303 con->servername, con->serverport, "/jobs/%d",
5304 job->id);
ef416fc2 5305
2e4ff8af
MS
5306 if (!ra || cupsArrayFind(ra, "document-count"))
5307 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
5308 "document-count", job->num_files);
5309
9a4f8274
MS
5310 if (!ra || cupsArrayFind(ra, "job-media-progress"))
5311 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
5312 "job-media-progress", job->progress);
5313
b423cd4c 5314 if (!ra || cupsArrayFind(ra, "job-more-info"))
5315 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
5316 "job-more-info", NULL, job_uri);
ef416fc2 5317
b423cd4c 5318 if (job->state_value > IPP_JOB_PROCESSING &&
5319 (!ra || cupsArrayFind(ra, "job-preserved")))
5320 ippAddBoolean(con->response, IPP_TAG_JOB, "job-preserved",
5321 job->num_files > 0);
ef416fc2 5322
b423cd4c 5323 if (!ra || cupsArrayFind(ra, "job-printer-up-time"))
5324 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
5325 "job-printer-up-time", time(NULL));
ef416fc2 5326
b423cd4c 5327 if (!ra || cupsArrayFind(ra, "job-state-reasons"))
5328 add_job_state_reasons(con, job);
bd7854cb 5329
b423cd4c 5330 if (!ra || cupsArrayFind(ra, "job-uri"))
5331 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
5332 "job-uri", NULL, job_uri);
ef416fc2 5333
b423cd4c 5334 copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0);
5335}
ef416fc2 5336
ef416fc2 5337
b423cd4c 5338/*
5339 * 'copy_printer_attrs()' - Copy printer attributes.
5340 */
ef416fc2 5341
b423cd4c 5342static void
5343copy_printer_attrs(
5344 cupsd_client_t *con, /* I - Client connection */
5345 cupsd_printer_t *printer, /* I - Printer */
5346 cups_array_t *ra) /* I - Requested attributes array */
5347{
5348 char printer_uri[HTTP_MAX_URI];
5349 /* Printer URI */
5350 time_t curtime; /* Current time */
5351 int i; /* Looping var */
5352 ipp_attribute_t *history; /* History collection */
ef416fc2 5353
ef416fc2 5354
b423cd4c 5355 /*
5356 * Copy the printer attributes to the response using requested-attributes
5357 * and document-format attributes that may be provided by the client.
5358 */
ef416fc2 5359
b423cd4c 5360 curtime = time(NULL);
ef416fc2 5361
b423cd4c 5362#ifdef __APPLE__
5363 if ((!ra || cupsArrayFind(ra, "com.apple.print.recoverable-message")) &&
5364 printer->recoverable)
5365 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
5366 "com.apple.print.recoverable-message", NULL,
5367 printer->recoverable);
5368#endif /* __APPLE__ */
ef416fc2 5369
5a738aea
MS
5370 if (!ra || cupsArrayFind(ra, "marker-change-time"))
5371 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5372 "marker-change-time", printer->marker_time);
5373
dd1abb6b
MS
5374 if (printer->num_printers > 0 &&
5375 (!ra || cupsArrayFind(ra, "member-uris")))
5376 {
5377 ipp_attribute_t *member_uris; /* member-uris attribute */
5378 cupsd_printer_t *p2; /* Printer in class */
5379 ipp_attribute_t *p2_uri; /* printer-uri-supported for class printer */
5380
5381
5382 if ((member_uris = ippAddStrings(con->response, IPP_TAG_PRINTER,
5383 IPP_TAG_URI, "member-uris",
5384 printer->num_printers, NULL,
5385 NULL)) != NULL)
5386 {
5387 for (i = 0; i < printer->num_printers; i ++)
5388 {
5389 p2 = printer->printers[i];
5390
5391 if ((p2_uri = ippFindAttribute(p2->attrs, "printer-uri-supported",
5392 IPP_TAG_URI)) != NULL)
5393 member_uris->values[i].string.text =
5394 _cupsStrAlloc(p2_uri->values[0].string.text);
5395 else
5396 {
5397 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri,
5398 sizeof(printer_uri), "ipp", NULL, con->servername,
5399 con->serverport,
5400 (p2->type & CUPS_PRINTER_CLASS) ?
5401 "/classes/%s" : "/printers/%s", p2->name);
5402 member_uris->values[i].string.text = _cupsStrAlloc(printer_uri);
5403 }
5404 }
5405 }
5406 }
5407
323c5de1 5408 if (printer->alert && (!ra || cupsArrayFind(ra, "printer-alert")))
5409 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_STRING,
5410 "printer-alert", NULL, printer->alert);
5411
5412 if (printer->alert_description &&
5413 (!ra || cupsArrayFind(ra, "printer-alert-description")))
5414 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
5415 "printer-alert-description", NULL,
5416 printer->alert_description);
5417
b423cd4c 5418 if (!ra || cupsArrayFind(ra, "printer-current-time"))
5419 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
5420 ippTimeToDate(curtime));
ef416fc2 5421
d9bca400
MS
5422#ifdef HAVE_DNSSD
5423 if (!ra || cupsArrayFind(ra, "printer-dns-sd-name"))
5424 {
5425 if (printer->reg_name)
5426 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
5427 "printer-dns-sd-name", NULL, printer->reg_name);
5428 else
5429 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
5430 "printer-dns-sd-name", 0);
5431 }
5432#endif /* HAVE_DNSSD */
5433
b423cd4c 5434 if (!ra || cupsArrayFind(ra, "printer-error-policy"))
5435 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
5436 "printer-error-policy", NULL, printer->error_policy);
ef416fc2 5437
b423cd4c 5438 if (!ra || cupsArrayFind(ra, "printer-is-accepting-jobs"))
5439 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
5440 printer->accepting);
ef416fc2 5441
b423cd4c 5442 if (!ra || cupsArrayFind(ra, "printer-is-shared"))
5443 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-shared",
5444 printer->shared);
ef416fc2 5445
b423cd4c 5446 if (!ra || cupsArrayFind(ra, "printer-op-policy"))
5447 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
5448 "printer-op-policy", NULL, printer->op_policy);
5449
5450 if (!ra || cupsArrayFind(ra, "printer-state"))
5451 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
5452 printer->state);
5453
5454 if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
5455 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5456 "printer-state-change-time", printer->state_time);
f7deaa1a 5457
b423cd4c 5458 if (MaxPrinterHistory > 0 && printer->num_history > 0 &&
5459 cupsArrayFind(ra, "printer-state-history"))
ef416fc2 5460 {
5461 /*
b423cd4c 5462 * Printer history is only sent if specifically requested, so that
5463 * older CUPS/IPP clients won't barf on the collection attributes.
ef416fc2 5464 */
5465
b423cd4c 5466 history = ippAddCollections(con->response, IPP_TAG_PRINTER,
5467 "printer-state-history",
5468 printer->num_history, NULL);
5469
5470 for (i = 0; i < printer->num_history; i ++)
5471 copy_attrs(history->values[i].collection = ippNew(), printer->history[i],
5472 NULL, IPP_TAG_ZERO, 0);
ef416fc2 5473 }
5474
b423cd4c 5475 if (!ra || cupsArrayFind(ra, "printer-state-message"))
5476 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
5477 "printer-state-message", NULL, printer->state_message);
ef416fc2 5478
b423cd4c 5479 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
5480 add_printer_state_reasons(con, printer);
ef416fc2 5481
b423cd4c 5482 if (!ra || cupsArrayFind(ra, "printer-type"))
5483 {
5484 int type; /* printer-type value */
ef416fc2 5485
ef416fc2 5486
ef416fc2 5487 /*
b423cd4c 5488 * Add the CUPS-specific printer-type attribute...
ef416fc2 5489 */
5490
09a101d6 5491 type = printer->type;
b423cd4c 5492
5493 if (printer == DefaultPrinter)
5494 type |= CUPS_PRINTER_DEFAULT;
5495
5496 if (!printer->accepting)
5497 type |= CUPS_PRINTER_REJECTING;
5498
5499 if (!printer->shared)
5500 type |= CUPS_PRINTER_NOT_SHARED;
5501
5502 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-type",
5503 type);
ef416fc2 5504 }
ef416fc2 5505
b423cd4c 5506 if (!ra || cupsArrayFind(ra, "printer-up-time"))
5507 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5508 "printer-up-time", curtime);
ef416fc2 5509
b423cd4c 5510 if ((!ra || cupsArrayFind(ra, "printer-uri-supported")) &&
5511 !ippFindAttribute(printer->attrs, "printer-uri-supported",
5512 IPP_TAG_URI))
ef416fc2 5513 {
b423cd4c 5514 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
5515 "ipp", NULL, con->servername, con->serverport,
8ca02f3c 5516 (printer->type & CUPS_PRINTER_CLASS) ?
5517 "/classes/%s" : "/printers/%s", printer->name);
b423cd4c 5518 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
5519 "printer-uri-supported", NULL, printer_uri);
5520 cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-uri-supported=\"%s\"",
5521 printer_uri);
5522 }
ef416fc2 5523
b423cd4c 5524 if (!ra || cupsArrayFind(ra, "queued-job-count"))
5525 add_queued_job_count(con, printer);
ef416fc2 5526
b423cd4c 5527 copy_attrs(con->response, printer->attrs, ra, IPP_TAG_ZERO, 0);
5528 copy_attrs(con->response, CommonData, ra, IPP_TAG_ZERO, IPP_TAG_COPY);
5529}
ef416fc2 5530
ef416fc2 5531
b423cd4c 5532/*
5533 * 'copy_subscription_attrs()' - Copy subscription attributes.
5534 */
ef416fc2 5535
b423cd4c 5536static void
5537copy_subscription_attrs(
5538 cupsd_client_t *con, /* I - Client connection */
5539 cupsd_subscription_t *sub, /* I - Subscription */
5540 cups_array_t *ra) /* I - Requested attributes array */
5541{
5542 ipp_attribute_t *attr; /* Current attribute */
5543 char printer_uri[HTTP_MAX_URI];
5544 /* Printer URI */
5545 int count; /* Number of events */
5546 unsigned mask; /* Current event mask */
5547 const char *name; /* Current event name */
ef416fc2 5548
ef416fc2 5549
b423cd4c 5550 /*
5551 * Copy the subscription attributes to the response using the
5552 * requested-attributes attribute that may be provided by the client.
5553 */
ef416fc2 5554
b423cd4c 5555 if (!ra || cupsArrayFind(ra, "notify-events"))
5556 {
5557 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
5558 {
5559 /*
5560 * Simple event list...
5561 */
ef416fc2 5562
b423cd4c 5563 ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
d09495fa 5564 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
b423cd4c 5565 "notify-events", NULL, name);
5566 }
5567 else
5568 {
5569 /*
5570 * Complex event list...
5571 */
ef416fc2 5572
b423cd4c 5573 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
5574 if (sub->mask & mask)
5575 count ++;
ef416fc2 5576
b423cd4c 5577 attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
d09495fa 5578 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
b423cd4c 5579 "notify-events", count, NULL, NULL);
ef416fc2 5580
b423cd4c 5581 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
5582 if (sub->mask & mask)
5583 {
5584 attr->values[count].string.text =
5585 (char *)cupsdEventName((cupsd_eventmask_t)mask);
ef416fc2 5586
b423cd4c 5587 count ++;
5588 }
ef416fc2 5589 }
b423cd4c 5590 }
ef416fc2 5591
b423cd4c 5592 if (sub->job && (!ra || cupsArrayFind(ra, "notify-job-id")))
5593 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5594 "notify-job-id", sub->job->id);
ef416fc2 5595
b423cd4c 5596 if (!sub->job && (!ra || cupsArrayFind(ra, "notify-lease-duration")))
5597 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5598 "notify-lease-duration", sub->lease);
ef416fc2 5599
b423cd4c 5600 if (sub->dest && (!ra || cupsArrayFind(ra, "notify-printer-uri")))
5601 {
5602 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
5603 "ipp", NULL, con->servername, con->serverport,
5604 "/printers/%s", sub->dest->name);
5605 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5606 "notify-printer-uri", NULL, printer_uri);
ef416fc2 5607 }
ef416fc2 5608
b423cd4c 5609 if (sub->recipient && (!ra || cupsArrayFind(ra, "notify-recipient-uri")))
5610 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5611 "notify-recipient-uri", NULL, sub->recipient);
5612 else if (!ra || cupsArrayFind(ra, "notify-pull-method"))
5613 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
5614 "notify-pull-method", NULL, "ippget");
ef416fc2 5615
b423cd4c 5616 if (!ra || cupsArrayFind(ra, "notify-subscriber-user-name"))
5617 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
5618 "notify-subscriber-user-name", NULL, sub->owner);
ef416fc2 5619
b423cd4c 5620 if (!ra || cupsArrayFind(ra, "notify-subscription-id"))
5621 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5622 "notify-subscription-id", sub->id);
ef416fc2 5623
b423cd4c 5624 if (!ra || cupsArrayFind(ra, "notify-time-interval"))
5625 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5626 "notify-time-interval", sub->interval);
ef416fc2 5627
b423cd4c 5628 if (sub->user_data_len > 0 && (!ra || cupsArrayFind(ra, "notify-user-data")))
5629 ippAddOctetString(con->response, IPP_TAG_SUBSCRIPTION, "notify-user-data",
5630 sub->user_data, sub->user_data_len);
5631}
ef416fc2 5632
ef416fc2 5633
b423cd4c 5634/*
5635 * 'create_job()' - Print a file to a printer or class.
5636 */
5637
5638static void
5639create_job(cupsd_client_t *con, /* I - Client connection */
5640 ipp_attribute_t *uri) /* I - Printer URI */
5641{
f7deaa1a 5642 cupsd_printer_t *printer; /* Printer */
5643 cupsd_job_t *job; /* New job */
b423cd4c 5644
ef416fc2 5645
b423cd4c 5646 cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con,
5647 con->http.fd, uri->values[0].string.text);
ef416fc2 5648
f7deaa1a 5649 /*
5650 * Is the destination valid?
5651 */
5652
5653 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
5654 {
5655 /*
5656 * Bad URI...
5657 */
5658
5659 send_ipp_status(con, IPP_NOT_FOUND,
5660 _("The printer or class was not found."));
5661 return;
5662 }
5663
ef416fc2 5664 /*
b423cd4c 5665 * Create the job object...
ef416fc2 5666 */
5667
f7deaa1a 5668 if ((job = add_job(con, printer, NULL)) == NULL)
b423cd4c 5669 return;
ef416fc2 5670
09a101d6 5671 job->pending_timeout = 1;
5672
ef416fc2 5673 /*
5674 * Save and log the job...
5675 */
f7deaa1a 5676
75bd9771
MS
5677 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
5678 job->dest, job->username);
ef416fc2 5679}
5680
5681
5682/*
5683 * 'create_requested_array()' - Create an array for the requested-attributes.
5684 */
5685
5686static cups_array_t * /* O - Array of attributes or NULL */
5687create_requested_array(ipp_t *request) /* I - IPP request */
5688{
5689 int i; /* Looping var */
5690 ipp_attribute_t *requested; /* requested-attributes attribute */
5691 cups_array_t *ra; /* Requested attributes array */
5692 char *value; /* Current value */
5693
5694
5695 /*
5696 * Get the requested-attributes attribute, and return NULL if we don't
5697 * have one...
5698 */
5699
5700 if ((requested = ippFindAttribute(request, "requested-attributes",
5701 IPP_TAG_KEYWORD)) == NULL)
5702 return (NULL);
5703
5704 /*
5705 * If the attribute contains a single "all" keyword, return NULL...
5706 */
5707
5708 if (requested->num_values == 1 &&
5709 !strcmp(requested->values[0].string.text, "all"))
5710 return (NULL);
5711
5712 /*
5713 * Create an array using "strcmp" as the comparison function...
5714 */
5715
5716 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
5717
5718 for (i = 0; i < requested->num_values; i ++)
5719 {
5720 value = requested->values[i].string.text;
5721
5722 if (!strcmp(value, "job-template"))
5723 {
5724 cupsArrayAdd(ra, "copies");
5725 cupsArrayAdd(ra, "copies-default");
5726 cupsArrayAdd(ra, "copies-supported");
5727 cupsArrayAdd(ra, "finishings");
5728 cupsArrayAdd(ra, "finishings-default");
5729 cupsArrayAdd(ra, "finishings-supported");
5730 cupsArrayAdd(ra, "job-hold-until");
5731 cupsArrayAdd(ra, "job-hold-until-default");
5732 cupsArrayAdd(ra, "job-hold-until-supported");
5733 cupsArrayAdd(ra, "job-priority");
5734 cupsArrayAdd(ra, "job-priority-default");
5735 cupsArrayAdd(ra, "job-priority-supported");
5736 cupsArrayAdd(ra, "job-sheets");
5737 cupsArrayAdd(ra, "job-sheets-default");
5738 cupsArrayAdd(ra, "job-sheets-supported");
5739 cupsArrayAdd(ra, "media");
5740 cupsArrayAdd(ra, "media-default");
5741 cupsArrayAdd(ra, "media-supported");
5742 cupsArrayAdd(ra, "multiple-document-handling");
5743 cupsArrayAdd(ra, "multiple-document-handling-default");
5744 cupsArrayAdd(ra, "multiple-document-handling-supported");
5745 cupsArrayAdd(ra, "number-up");
5746 cupsArrayAdd(ra, "number-up-default");
5747 cupsArrayAdd(ra, "number-up-supported");
5748 cupsArrayAdd(ra, "orientation-requested");
5749 cupsArrayAdd(ra, "orientation-requested-default");
5750 cupsArrayAdd(ra, "orientation-requested-supported");
5751 cupsArrayAdd(ra, "page-ranges");
5752 cupsArrayAdd(ra, "page-ranges-supported");
5753 cupsArrayAdd(ra, "printer-resolution");
5754 cupsArrayAdd(ra, "printer-resolution-default");
5755 cupsArrayAdd(ra, "printer-resolution-supported");
5756 cupsArrayAdd(ra, "print-quality");
5757 cupsArrayAdd(ra, "print-quality-default");
5758 cupsArrayAdd(ra, "print-quality-supported");
5759 cupsArrayAdd(ra, "sides");
5760 cupsArrayAdd(ra, "sides-default");
5761 cupsArrayAdd(ra, "sides-supported");
5762 }
5763 else if (!strcmp(value, "job-description"))
5764 {
5765 cupsArrayAdd(ra, "date-time-at-completed");
5766 cupsArrayAdd(ra, "date-time-at-creation");
5767 cupsArrayAdd(ra, "date-time-at-processing");
5768 cupsArrayAdd(ra, "job-detailed-status-message");
5769 cupsArrayAdd(ra, "job-document-access-errors");
5770 cupsArrayAdd(ra, "job-id");
5771 cupsArrayAdd(ra, "job-impressions");
5772 cupsArrayAdd(ra, "job-impressions-completed");
5773 cupsArrayAdd(ra, "job-k-octets");
5774 cupsArrayAdd(ra, "job-k-octets-processed");
9a4f8274 5775 cupsArrayAdd(ra, "job-media-progress");
ef416fc2 5776 cupsArrayAdd(ra, "job-media-sheets");
5777 cupsArrayAdd(ra, "job-media-sheets-completed");
5778 cupsArrayAdd(ra, "job-message-from-operator");
5779 cupsArrayAdd(ra, "job-more-info");
5780 cupsArrayAdd(ra, "job-name");
5781 cupsArrayAdd(ra, "job-originating-user-name");
5782 cupsArrayAdd(ra, "job-printer-up-time");
5783 cupsArrayAdd(ra, "job-printer-uri");
5784 cupsArrayAdd(ra, "job-state");
5785 cupsArrayAdd(ra, "job-state-message");
5786 cupsArrayAdd(ra, "job-state-reasons");
5787 cupsArrayAdd(ra, "job-uri");
5788 cupsArrayAdd(ra, "number-of-documents");
5789 cupsArrayAdd(ra, "number-of-intervening-jobs");
5790 cupsArrayAdd(ra, "output-device-assigned");
5791 cupsArrayAdd(ra, "time-at-completed");
5792 cupsArrayAdd(ra, "time-at-creation");
5793 cupsArrayAdd(ra, "time-at-processing");
5794 }
5795 else if (!strcmp(value, "printer-description"))
5796 {
5797 cupsArrayAdd(ra, "charset-configured");
5798 cupsArrayAdd(ra, "charset-supported");
5799 cupsArrayAdd(ra, "color-supported");
5800 cupsArrayAdd(ra, "compression-supported");
5801 cupsArrayAdd(ra, "document-format-default");
5802 cupsArrayAdd(ra, "document-format-supported");
5803 cupsArrayAdd(ra, "generated-natural-language-supported");
5804 cupsArrayAdd(ra, "ipp-versions-supported");
5805 cupsArrayAdd(ra, "job-impressions-supported");
5806 cupsArrayAdd(ra, "job-k-octets-supported");
5807 cupsArrayAdd(ra, "job-media-sheets-supported");
1f6f3dbc 5808 cupsArrayAdd(ra, "job-settable-attributes-supported");
ef416fc2 5809 cupsArrayAdd(ra, "multiple-document-jobs-supported");
5810 cupsArrayAdd(ra, "multiple-operation-time-out");
5811 cupsArrayAdd(ra, "natural-language-configured");
5812 cupsArrayAdd(ra, "notify-attributes-supported");
5813 cupsArrayAdd(ra, "notify-lease-duration-default");
5814 cupsArrayAdd(ra, "notify-lease-duration-supported");
5815 cupsArrayAdd(ra, "notify-max-events-supported");
f301802f 5816 cupsArrayAdd(ra, "notify-events-default");
5817 cupsArrayAdd(ra, "notify-events-supported");
ef416fc2 5818 cupsArrayAdd(ra, "notify-pull-method-supported");
5819 cupsArrayAdd(ra, "notify-schemes-supported");
5820 cupsArrayAdd(ra, "operations-supported");
5821 cupsArrayAdd(ra, "pages-per-minute");
5822 cupsArrayAdd(ra, "pages-per-minute-color");
5823 cupsArrayAdd(ra, "pdl-override-supported");
323c5de1 5824 cupsArrayAdd(ra, "printer-alert");
5825 cupsArrayAdd(ra, "printer-alert-description");
01ce6322 5826 cupsArrayAdd(ra, "printer-commands");
ef416fc2 5827 cupsArrayAdd(ra, "printer-current-time");
5828 cupsArrayAdd(ra, "printer-driver-installer");
01ce6322 5829 cupsArrayAdd(ra, "printer-dns-sd-name");
ef416fc2 5830 cupsArrayAdd(ra, "printer-info");
5831 cupsArrayAdd(ra, "printer-is-accepting-jobs");
5832 cupsArrayAdd(ra, "printer-location");
5833 cupsArrayAdd(ra, "printer-make-and-model");
5834 cupsArrayAdd(ra, "printer-message-from-operator");
5835 cupsArrayAdd(ra, "printer-more-info");
5836 cupsArrayAdd(ra, "printer-more-info-manufacturer");
5837 cupsArrayAdd(ra, "printer-name");
5838 cupsArrayAdd(ra, "printer-state");
5839 cupsArrayAdd(ra, "printer-state-message");
5840 cupsArrayAdd(ra, "printer-state-reasons");
1f6f3dbc 5841 cupsArrayAdd(ra, "printer-settable-attributes-supported");
01ce6322 5842 cupsArrayAdd(ra, "printer-type");
ef416fc2 5843 cupsArrayAdd(ra, "printer-up-time");
5844 cupsArrayAdd(ra, "printer-uri-supported");
5845 cupsArrayAdd(ra, "queued-job-count");
5846 cupsArrayAdd(ra, "reference-uri-schemes-supported");
5847 cupsArrayAdd(ra, "uri-authentication-supported");
5848 cupsArrayAdd(ra, "uri-security-supported");
5849 }
f7deaa1a 5850 else if (!strcmp(value, "printer-defaults"))
5851 {
5852 char *name; /* Option name */
5853
5854
5855 for (name = (char *)cupsArrayFirst(CommonDefaults);
5856 name;
5857 name = (char *)cupsArrayNext(CommonDefaults))
5858 cupsArrayAdd(ra, name);
5859 }
ef416fc2 5860 else if (!strcmp(value, "subscription-template"))
5861 {
5862 cupsArrayAdd(ra, "notify-attributes");
5863 cupsArrayAdd(ra, "notify-charset");
5864 cupsArrayAdd(ra, "notify-events");
5865 cupsArrayAdd(ra, "notify-lease-duration");
5866 cupsArrayAdd(ra, "notify-natural-language");
5867 cupsArrayAdd(ra, "notify-pull-method");
5868 cupsArrayAdd(ra, "notify-recipient-uri");
5869 cupsArrayAdd(ra, "notify-time-interval");
5870 cupsArrayAdd(ra, "notify-user-data");
5871 }
5872 else
5873 cupsArrayAdd(ra, value);
5874 }
5875
5876 return (ra);
5877}
5878
5879
5880/*
5881 * 'create_subscription()' - Create a notification subscription.
5882 */
5883
5884static void
5885create_subscription(
5886 cupsd_client_t *con, /* I - Client connection */
5887 ipp_attribute_t *uri) /* I - Printer URI */
5888{
5889 http_status_t status; /* Policy status */
5890 int i; /* Looping var */
5891 ipp_attribute_t *attr; /* Current attribute */
bc44d920 5892 cups_ptype_t dtype; /* Destination type (printer/class) */
ed486911 5893 char scheme[HTTP_MAX_URI],
5894 /* Scheme portion of URI */
ef416fc2 5895 userpass[HTTP_MAX_URI],
5896 /* Username portion of URI */
5897 host[HTTP_MAX_URI],
5898 /* Host portion of URI */
5899 resource[HTTP_MAX_URI];
5900 /* Resource portion of URI */
5901 int port; /* Port portion of URI */
5902 cupsd_printer_t *printer; /* Printer/class */
5903 cupsd_job_t *job; /* Job */
5904 int jobid; /* Job ID */
5905 cupsd_subscription_t *sub; /* Subscription object */
bc44d920 5906 const char *username, /* requesting-user-name or
5907 authenticated username */
ef416fc2 5908 *recipient, /* notify-recipient-uri */
5909 *pullmethod; /* notify-pull-method */
5910 ipp_attribute_t *user_data; /* notify-user-data */
5911 int interval, /* notify-time-interval */
5912 lease; /* notify-lease-duration */
5913 unsigned mask; /* notify-events */
f7deaa1a 5914 ipp_attribute_t *notify_events,/* notify-events(-default) */
5915 *notify_lease; /* notify-lease-duration(-default) */
ef416fc2 5916
5917
bd7854cb 5918#ifdef DEBUG
5919 for (attr = con->request->attrs; attr; attr = attr->next)
5920 {
5921 if (attr->group_tag != IPP_TAG_ZERO)
3dfe78b3 5922 cupsdLogMessage(CUPSD_LOG_DEBUG2, "g%04x v%04x %s", attr->group_tag,
bd7854cb 5923 attr->value_tag, attr->name);
5924 else
3dfe78b3 5925 cupsdLogMessage(CUPSD_LOG_DEBUG2, "----SEP----");
bd7854cb 5926 }
5927#endif /* DEBUG */
5928
ef416fc2 5929 /*
5930 * Is the destination valid?
5931 */
5932
fa73b229 5933 cupsdLogMessage(CUPSD_LOG_DEBUG,
5934 "cupsdCreateSubscription(con=%p(%d), uri=\"%s\")",
5935 con, con->http.fd, uri->values[0].string.text);
5936
ed486911 5937 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
5938 sizeof(scheme), userpass, sizeof(userpass), host,
a4d04587 5939 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 5940
5941 if (!strcmp(resource, "/"))
5942 {
ef416fc2 5943 dtype = (cups_ptype_t)0;
5944 printer = NULL;
5945 }
5946 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
5947 {
ef416fc2 5948 dtype = (cups_ptype_t)0;
5949 printer = NULL;
5950 }
5951 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
5952 {
ef416fc2 5953 dtype = CUPS_PRINTER_CLASS;
5954 printer = NULL;
5955 }
f7deaa1a 5956 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 5957 {
5958 /*
5959 * Bad URI...
5960 */
5961
5962 send_ipp_status(con, IPP_NOT_FOUND,
5963 _("The printer or class was not found."));
5964 return;
5965 }
5966
5967 /*
5968 * Check policy...
5969 */
5970
5971 if (printer)
5972 {
f7deaa1a 5973 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
5974 NULL)) != HTTP_OK)
ef416fc2 5975 {
f899b121 5976 send_http_error(con, status, printer);
ef416fc2 5977 return;
5978 }
5979 }
5980 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5981 {
f899b121 5982 send_http_error(con, status, NULL);
ef416fc2 5983 return;
5984 }
5985
5986 /*
5987 * Get the user that is requesting the subscription...
5988 */
5989
e00b005a 5990 username = get_username(con);
ef416fc2 5991
5992 /*
5993 * Find the first subscription group attribute; return if we have
5994 * none...
5995 */
5996
5997 for (attr = con->request->attrs; attr; attr = attr->next)
5998 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
5999 break;
6000
6001 if (!attr)
6002 {
6003 send_ipp_status(con, IPP_BAD_REQUEST,
6004 _("No subscription attributes in request!"));
6005 return;
6006 }
6007
6008 /*
6009 * Process the subscription attributes in the request...
6010 */
6011
fa73b229 6012 con->response->request.status.status_code = IPP_BAD_REQUEST;
6013
ef416fc2 6014 while (attr)
6015 {
6016 recipient = NULL;
6017 pullmethod = NULL;
6018 user_data = NULL;
6019 interval = 0;
6020 lease = DefaultLeaseDuration;
6021 jobid = 0;
6022 mask = CUPSD_EVENT_NONE;
6023
f7deaa1a 6024 if (printer)
6025 {
6026 notify_events = ippFindAttribute(printer->attrs, "notify-events-default",
6027 IPP_TAG_KEYWORD);
6028 notify_lease = ippFindAttribute(printer->attrs,
6029 "notify-lease-duration-default",
6030 IPP_TAG_INTEGER);
6031
6032 if (notify_lease)
6033 lease = notify_lease->values[0].integer;
6034 }
6035 else
6036 {
6037 notify_events = NULL;
6038 notify_lease = NULL;
6039 }
6040
ef416fc2 6041 while (attr && attr->group_tag != IPP_TAG_ZERO)
6042 {
7594b224 6043 if (!strcmp(attr->name, "notify-recipient-uri") &&
ef416fc2 6044 attr->value_tag == IPP_TAG_URI)
ed486911 6045 {
6046 /*
6047 * Validate the recipient scheme against the ServerBin/notifier
6048 * directory...
6049 */
6050
6051 char notifier[1024]; /* Notifier filename */
6052
6053
ef416fc2 6054 recipient = attr->values[0].string.text;
ed486911 6055
8ca02f3c 6056 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
6057 scheme, sizeof(scheme), userpass, sizeof(userpass),
6058 host, sizeof(host), &port,
6059 resource, sizeof(resource)) < HTTP_URI_OK)
ed486911 6060 {
6061 send_ipp_status(con, IPP_NOT_POSSIBLE,
7594b224 6062 _("Bad notify-recipient-uri URI \"%s\"!"), recipient);
ed486911 6063 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6064 "notify-status-code", IPP_URI_SCHEME);
6065 return;
6066 }
6067
6068 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
6069 scheme);
6070 if (access(notifier, X_OK))
6071 {
6072 send_ipp_status(con, IPP_NOT_POSSIBLE,
bc44d920 6073 _("notify-recipient-uri URI \"%s\" uses unknown "
6074 "scheme!"), recipient);
ed486911 6075 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6076 "notify-status-code", IPP_URI_SCHEME);
6077 return;
6078 }
3dfe78b3
MS
6079
6080 if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
6081 {
6082 send_ipp_status(con, IPP_NOT_POSSIBLE,
6083 _("notify-recipient-uri URI \"%s\" is already used!"),
6084 recipient);
6085 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6086 "notify-status-code", IPP_ATTRIBUTES);
6087 return;
6088 }
ed486911 6089 }
ef416fc2 6090 else if (!strcmp(attr->name, "notify-pull-method") &&
6091 attr->value_tag == IPP_TAG_KEYWORD)
ed486911 6092 {
ef416fc2 6093 pullmethod = attr->values[0].string.text;
ed486911 6094
6095 if (strcmp(pullmethod, "ippget"))
6096 {
6097 send_ipp_status(con, IPP_NOT_POSSIBLE,
6098 _("Bad notify-pull-method \"%s\"!"), pullmethod);
6099 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6100 "notify-status-code", IPP_ATTRIBUTES);
6101 return;
6102 }
6103 }
ef416fc2 6104 else if (!strcmp(attr->name, "notify-charset") &&
6105 attr->value_tag == IPP_TAG_CHARSET &&
6106 strcmp(attr->values[0].string.text, "us-ascii") &&
6107 strcmp(attr->values[0].string.text, "utf-8"))
6108 {
6109 send_ipp_status(con, IPP_CHARSET,
6110 _("Character set \"%s\" not supported!"),
6111 attr->values[0].string.text);
6112 return;
6113 }
6114 else if (!strcmp(attr->name, "notify-natural-language") &&
6115 (attr->value_tag != IPP_TAG_LANGUAGE ||
6116 strcmp(attr->values[0].string.text, DefaultLanguage)))
6117 {
6118 send_ipp_status(con, IPP_CHARSET,
6119 _("Language \"%s\" not supported!"),
6120 attr->values[0].string.text);
6121 return;
6122 }
6123 else if (!strcmp(attr->name, "notify-user-data") &&
6124 attr->value_tag == IPP_TAG_STRING)
6125 {
6126 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
6127 {
6128 send_ipp_status(con, IPP_REQUEST_VALUE,
6129 _("The notify-user-data value is too large "
6130 "(%d > 63 octets)!"),
6131 attr->values[0].unknown.length);
6132 return;
6133 }
6134
6135 user_data = attr;
6136 }
6137 else if (!strcmp(attr->name, "notify-events") &&
6138 attr->value_tag == IPP_TAG_KEYWORD)
f7deaa1a 6139 notify_events = attr;
ef416fc2 6140 else if (!strcmp(attr->name, "notify-lease-duration") &&
6141 attr->value_tag == IPP_TAG_INTEGER)
6142 lease = attr->values[0].integer;
6143 else if (!strcmp(attr->name, "notify-time-interval") &&
6144 attr->value_tag == IPP_TAG_INTEGER)
6145 interval = attr->values[0].integer;
6146 else if (!strcmp(attr->name, "notify-job-id") &&
6147 attr->value_tag == IPP_TAG_INTEGER)
6148 jobid = attr->values[0].integer;
6149
6150 attr = attr->next;
6151 }
6152
f7deaa1a 6153 if (notify_events)
6154 {
6155 for (i = 0; i < notify_events->num_values; i ++)
6156 mask |= cupsdEventValue(notify_events->values[i].string.text);
6157 }
6158
bd7854cb 6159 if (recipient)
6160 cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
6161 if (pullmethod)
6162 cupsdLogMessage(CUPSD_LOG_DEBUG, "pullmethod=\"%s\"", pullmethod);
6163 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-lease-duration=%d", lease);
6164 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-time-interval=%d", interval);
fa73b229 6165
ef416fc2 6166 if (!recipient && !pullmethod)
6167 break;
6168
6169 if (mask == CUPSD_EVENT_NONE)
6170 {
6171 if (jobid)
6172 mask = CUPSD_EVENT_JOB_COMPLETED;
6173 else if (printer)
6174 mask = CUPSD_EVENT_PRINTER_STATE_CHANGED;
6175 else
6176 {
6177 send_ipp_status(con, IPP_BAD_REQUEST,
6178 _("notify-events not specified!"));
6179 return;
6180 }
6181 }
6182
bd7854cb 6183 if (MaxLeaseDuration && (lease == 0 || lease > MaxLeaseDuration))
ef416fc2 6184 {
6185 cupsdLogMessage(CUPSD_LOG_INFO,
6186 "create_subscription: Limiting notify-lease-duration to "
6187 "%d seconds.",
6188 MaxLeaseDuration);
6189 lease = MaxLeaseDuration;
6190 }
6191
6192 if (jobid)
6193 {
6194 if ((job = cupsdFindJob(jobid)) == NULL)
6195 {
6196 send_ipp_status(con, IPP_NOT_FOUND, _("Job %d not found!"), jobid);
6197 return;
6198 }
6199 }
6200 else
6201 job = NULL;
6202
6203 sub = cupsdAddSubscription(mask, printer, job, recipient, 0);
6204
fa73b229 6205 if (job)
6206 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d",
6207 sub->id, job->id);
6208 else if (printer)
6209 cupsdLogMessage(CUPSD_LOG_DEBUG,
6210 "Added subscription %d for printer \"%s\"",
6211 sub->id, printer->name);
6212 else
6213 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for server",
6214 sub->id);
6215
ef416fc2 6216 sub->interval = interval;
6217 sub->lease = lease;
bd7854cb 6218 sub->expire = lease ? time(NULL) + lease : 0;
ef416fc2 6219
6220 cupsdSetString(&sub->owner, username);
6221
6222 if (user_data)
6223 {
6224 sub->user_data_len = user_data->values[0].unknown.length;
6225 memcpy(sub->user_data, user_data->values[0].unknown.data,
6226 sub->user_data_len);
6227 }
6228
6229 ippAddSeparator(con->response);
6230 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
6231 "notify-subscription-id", sub->id);
6232
fa73b229 6233 con->response->request.status.status_code = IPP_OK;
6234
ef416fc2 6235 if (attr)
6236 attr = attr->next;
6237 }
6238
3dfe78b3 6239 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
ef416fc2 6240}
6241
6242
6243/*
6244 * 'delete_printer()' - Remove a printer or class from the system.
6245 */
6246
6247static void
6248delete_printer(cupsd_client_t *con, /* I - Client connection */
6249 ipp_attribute_t *uri) /* I - URI of printer or class */
6250{
6251 http_status_t status; /* Policy status */
bc44d920 6252 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 6253 cupsd_printer_t *printer; /* Printer/class */
6254 char filename[1024]; /* Script/PPD filename */
6255
6256
6257 cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", con,
6258 con->http.fd, uri->values[0].string.text);
6259
6260 /*
6261 * Do we have a valid URI?
6262 */
6263
f7deaa1a 6264 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 6265 {
6266 /*
6267 * Bad URI...
6268 */
6269
6270 send_ipp_status(con, IPP_NOT_FOUND,
6271 _("The printer or class was not found."));
6272 return;
6273 }
6274
6275 /*
6276 * Check policy...
6277 */
6278
6279 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6280 {
f899b121 6281 send_http_error(con, status, NULL);
ef416fc2 6282 return;
6283 }
6284
6285 /*
6286 * Remove old jobs...
6287 */
6288
f7deaa1a 6289 cupsdCancelJobs(printer->name, NULL, 1);
ef416fc2 6290
6291 /*
6292 * Remove old subscriptions and send a "deleted printer" event...
6293 */
6294
6295 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
6296 "%s \"%s\" deleted by \"%s\".",
6297 (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
f7deaa1a 6298 printer->name, get_username(con));
ef416fc2 6299
6300 cupsdExpireSubscriptions(printer, NULL);
f7deaa1a 6301
ef416fc2 6302 /*
6303 * Remove any old PPD or script files...
6304 */
6305
f7deaa1a 6306 snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot,
6307 printer->name);
ef416fc2 6308 unlink(filename);
6309
f7deaa1a 6310 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
6311 printer->name);
ef416fc2 6312 unlink(filename);
6313
568fa3fa
MS
6314#ifdef __APPLE__
6315 /*
6316 * Unregister color profiles...
6317 */
6318
6319 apple_unregister_profiles(printer);
6320#endif /* __APPLE__ */
6321
ef416fc2 6322 if (dtype & CUPS_PRINTER_CLASS)
6323 {
f7deaa1a 6324 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".",
6325 printer->name, get_username(con));
ef416fc2 6326
6327 cupsdDeletePrinter(printer, 0);
3dfe78b3 6328 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
ef416fc2 6329 }
6330 else
6331 {
f7deaa1a 6332 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".",
6333 printer->name, get_username(con));
ef416fc2 6334
6335 cupsdDeletePrinter(printer, 0);
3dfe78b3 6336 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 6337 }
6338
3dfe78b3 6339 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
ef416fc2 6340
6341 /*
6342 * Return with no errors...
6343 */
6344
6345 con->response->request.status.status_code = IPP_OK;
6346}
6347
6348
6349/*
6350 * 'get_default()' - Get the default destination.
6351 */
6352
6353static void
6354get_default(cupsd_client_t *con) /* I - Client connection */
6355{
fa73b229 6356 http_status_t status; /* Policy status */
6357 cups_array_t *ra; /* Requested attributes array */
ef416fc2 6358
6359
6360 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->http.fd);
6361
6362 /*
6363 * Check policy...
6364 */
6365
6366 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6367 {
f899b121 6368 send_http_error(con, status, NULL);
ef416fc2 6369 return;
6370 }
6371
fa73b229 6372 if (DefaultPrinter)
ef416fc2 6373 {
fa73b229 6374 ra = create_requested_array(con->request);
ef416fc2 6375
fa73b229 6376 copy_printer_attrs(con, DefaultPrinter, ra);
ef416fc2 6377
fa73b229 6378 cupsArrayDelete(ra);
ef416fc2 6379
fa73b229 6380 con->response->request.status.status_code = IPP_OK;
ef416fc2 6381 }
6382 else
6383 send_ipp_status(con, IPP_NOT_FOUND, _("No default printer"));
6384}
6385
6386
6387/*
6388 * 'get_devices()' - Get the list of available devices on the local system.
6389 */
6390
6391static void
6392get_devices(cupsd_client_t *con) /* I - Client connection */
6393{
6394 http_status_t status; /* Policy status */
ae71f5de
MS
6395 ipp_attribute_t *limit, /* limit attribute */
6396 *timeout, /* timeout attribute */
6397 *requested, /* requested-attributes attribute */
6398 *exclude; /* exclude-schemes attribute */
ef416fc2 6399 char command[1024], /* cups-deviced command */
6400 options[1024], /* Options to pass to command */
ae71f5de 6401 requested_str[256],
89d46774 6402 /* String for requested attributes */
ae71f5de
MS
6403 exclude_str[512];
6404 /* String for excluded attributes */
ef416fc2 6405
6406
6407 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->http.fd);
6408
6409 /*
6410 * Check policy...
6411 */
6412
6413 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6414 {
f899b121 6415 send_http_error(con, status, NULL);
ef416fc2 6416 return;
6417 }
6418
6419 /*
6420 * Run cups-deviced command with the given options...
6421 */
6422
ae71f5de
MS
6423 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
6424 timeout = ippFindAttribute(con->request, "timeout", IPP_TAG_INTEGER);
ef416fc2 6425 requested = ippFindAttribute(con->request, "requested-attributes",
6426 IPP_TAG_KEYWORD);
ae71f5de 6427 exclude = ippFindAttribute(con->request, "exclude-schemes", IPP_TAG_NAME);
ef416fc2 6428
6429 if (requested)
89d46774 6430 url_encode_attr(requested, requested_str, sizeof(requested_str));
ef416fc2 6431 else
89d46774 6432 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
ef416fc2 6433
ae71f5de
MS
6434 if (exclude)
6435 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
6436 else
6437 exclude_str[0] = '\0';
6438
ef416fc2 6439 snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
6440 snprintf(options, sizeof(options),
ae71f5de 6441 "%d+%d+%d+%d+%s%s%s",
ef416fc2 6442 con->request->request.op.request_id,
ae71f5de
MS
6443 limit ? limit->values[0].integer : 0,
6444 timeout ? timeout->values[0].integer : 10,
6445 (int)User,
6446 requested_str,
6447 exclude_str[0] ? "%20" : "", exclude_str);
ef416fc2 6448
6449 if (cupsdSendCommand(con, command, options, 1))
6450 {
6451 /*
6452 * Command started successfully, don't send an IPP response here...
6453 */
6454
6455 ippDelete(con->response);
6456 con->response = NULL;
6457 }
6458 else
6459 {
6460 /*
6461 * Command failed, return "internal error" so the user knows something
6462 * went wrong...
6463 */
6464
6465 send_ipp_status(con, IPP_INTERNAL_ERROR,
6466 _("cups-deviced failed to execute."));
6467 }
6468}
6469
6470
2e4ff8af
MS
6471/*
6472 * 'get_document()' - Get a copy of a job file.
6473 */
6474
6475static void
6476get_document(cupsd_client_t *con, /* I - Client connection */
6477 ipp_attribute_t *uri) /* I - Job URI */
6478{
6479 http_status_t status; /* Policy status */
6480 ipp_attribute_t *attr; /* Current attribute */
6481 int jobid; /* Job ID */
6482 int docnum; /* Document number */
6483 cupsd_job_t *job; /* Current job */
6484 char method[HTTP_MAX_URI], /* Method portion of URI */
6485 username[HTTP_MAX_URI], /* Username portion of URI */
6486 host[HTTP_MAX_URI], /* Host portion of URI */
6487 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6488 int port; /* Port portion of URI */
6489 char filename[1024], /* Filename for document */
6490 format[1024]; /* Format for document */
6491
6492
6493 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", con,
6494 con->http.fd, uri->values[0].string.text);
6495
6496 /*
6497 * See if we have a job URI or a printer URI...
6498 */
6499
6500 if (!strcmp(uri->name, "printer-uri"))
6501 {
6502 /*
6503 * Got a printer URI; see if we also have a job-id attribute...
6504 */
6505
6506 if ((attr = ippFindAttribute(con->request, "job-id",
6507 IPP_TAG_INTEGER)) == NULL)
6508 {
6509 send_ipp_status(con, IPP_BAD_REQUEST,
6510 _("Got a printer-uri attribute but no job-id!"));
6511 return;
6512 }
6513
6514 jobid = attr->values[0].integer;
6515 }
6516 else
6517 {
6518 /*
6519 * Got a job URI; parse it to get the job ID...
6520 */
6521
6522 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
6523 sizeof(method), username, sizeof(username), host,
6524 sizeof(host), &port, resource, sizeof(resource));
6525
6526 if (strncmp(resource, "/jobs/", 6))
6527 {
6528 /*
6529 * Not a valid URI!
6530 */
6531
6532 send_ipp_status(con, IPP_BAD_REQUEST,
6533 _("Bad job-uri attribute \"%s\"!"),
6534 uri->values[0].string.text);
6535 return;
6536 }
6537
6538 jobid = atoi(resource + 6);
6539 }
6540
6541 /*
6542 * See if the job exists...
6543 */
6544
6545 if ((job = cupsdFindJob(jobid)) == NULL)
6546 {
6547 /*
6548 * Nope - return a "not found" error...
6549 */
6550
6551 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
6552 return;
6553 }
6554
6555 /*
6556 * Check policy...
6557 */
6558
6559 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6560 {
6561 send_http_error(con, status, NULL);
6562 return;
6563 }
6564
6565 /*
6566 * Get the document number...
6567 */
6568
6569 if ((attr = ippFindAttribute(con->request, "document-number",
6570 IPP_TAG_INTEGER)) == NULL)
6571 {
6572 send_ipp_status(con, IPP_BAD_REQUEST,
6573 _("Missing document-number attribute!"));
6574 return;
6575 }
6576
6577 if ((docnum = attr->values[0].integer) < 1 || docnum > job->num_files ||
6578 attr->num_values > 1)
6579 {
6580 send_ipp_status(con, IPP_NOT_FOUND, _("Document %d not found in job %d."),
6581 docnum, jobid);
6582 return;
6583 }
6584
6585 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, jobid,
6586 docnum);
6587 if ((con->file = open(filename, O_RDONLY)) == -1)
6588 {
6589 cupsdLogMessage(CUPSD_LOG_ERROR,
6590 "Unable to open document %d in job %d - %s", docnum, jobid,
6591 strerror(errno));
6592 send_ipp_status(con, IPP_NOT_FOUND,
6593 _("Unable to open document %d in job %d!"), docnum, jobid);
6594 return;
6595 }
6596
6597 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
6598
6599 cupsdLoadJob(job);
6600
6601 snprintf(format, sizeof(format), "%s/%s", job->filetypes[docnum - 1]->super,
6602 job->filetypes[docnum - 1]->type);
6603
6604 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
6605 NULL, format);
6606 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "document-number",
6607 docnum);
6608 if ((attr = ippFindAttribute(job->attrs, "document-name",
6609 IPP_TAG_NAME)) != NULL)
6610 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "document-name",
6611 NULL, attr->values[0].string.text);
6612}
6613
6614
ef416fc2 6615/*
fa73b229 6616 * 'get_job_attrs()' - Get job attributes.
ef416fc2 6617 */
6618
6619static void
fa73b229 6620get_job_attrs(cupsd_client_t *con, /* I - Client connection */
6621 ipp_attribute_t *uri) /* I - Job URI */
ef416fc2 6622{
6623 http_status_t status; /* Policy status */
fa73b229 6624 ipp_attribute_t *attr; /* Current attribute */
6625 int jobid; /* Job ID */
6626 cupsd_job_t *job; /* Current job */
ef416fc2 6627 char method[HTTP_MAX_URI], /* Method portion of URI */
6628 username[HTTP_MAX_URI], /* Username portion of URI */
6629 host[HTTP_MAX_URI], /* Host portion of URI */
6630 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6631 int port; /* Port portion of URI */
fa73b229 6632 cups_array_t *ra; /* Requested attributes array */
ef416fc2 6633
6634
fa73b229 6635 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", con,
6636 con->http.fd, uri->values[0].string.text);
ef416fc2 6637
6638 /*
fa73b229 6639 * See if we have a job URI or a printer URI...
6640 */
6641
6642 if (!strcmp(uri->name, "printer-uri"))
6643 {
6644 /*
6645 * Got a printer URI; see if we also have a job-id attribute...
6646 */
6647
6648 if ((attr = ippFindAttribute(con->request, "job-id",
6649 IPP_TAG_INTEGER)) == NULL)
6650 {
6651 send_ipp_status(con, IPP_BAD_REQUEST,
6652 _("Got a printer-uri attribute but no job-id!"));
6653 return;
6654 }
6655
6656 jobid = attr->values[0].integer;
6657 }
6658 else
6659 {
6660 /*
6661 * Got a job URI; parse it to get the job ID...
6662 */
6663
a4d04587 6664 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
6665 sizeof(method), username, sizeof(username), host,
6666 sizeof(host), &port, resource, sizeof(resource));
fa73b229 6667
6668 if (strncmp(resource, "/jobs/", 6))
6669 {
6670 /*
6671 * Not a valid URI!
6672 */
6673
6674 send_ipp_status(con, IPP_BAD_REQUEST,
6675 _("Bad job-uri attribute \"%s\"!"),
6676 uri->values[0].string.text);
6677 return;
6678 }
6679
6680 jobid = atoi(resource + 6);
6681 }
6682
6683 /*
6684 * See if the job exists...
6685 */
6686
6687 if ((job = cupsdFindJob(jobid)) == NULL)
6688 {
6689 /*
6690 * Nope - return a "not found" error...
6691 */
6692
6693 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
6694 return;
6695 }
6696
6697 /*
6698 * Check policy...
6699 */
6700
6701 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6702 {
f899b121 6703 send_http_error(con, status, NULL);
fa73b229 6704 return;
6705 }
6706
6707 /*
6708 * Copy attributes...
6709 */
6710
bd7854cb 6711 cupsdLoadJob(job);
6712
fa73b229 6713 ra = create_requested_array(con->request);
6714 copy_job_attrs(con, job, ra);
6715 cupsArrayDelete(ra);
6716
6717 con->response->request.status.status_code = IPP_OK;
6718}
6719
6720
6721/*
6722 * 'get_jobs()' - Get a list of jobs for the specified printer.
6723 */
6724
6725static void
6726get_jobs(cupsd_client_t *con, /* I - Client connection */
6727 ipp_attribute_t *uri) /* I - Printer URI */
6728{
6729 http_status_t status; /* Policy status */
6730 ipp_attribute_t *attr; /* Current attribute */
6731 const char *dest; /* Destination */
bc44d920 6732 cups_ptype_t dtype; /* Destination type (printer/class) */
fa73b229 6733 cups_ptype_t dmask; /* Destination type mask */
f7deaa1a 6734 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
fa73b229 6735 username[HTTP_MAX_URI], /* Username portion of URI */
6736 host[HTTP_MAX_URI], /* Host portion of URI */
6737 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6738 int port; /* Port portion of URI */
6739 int completed; /* Completed jobs? */
6740 int first_job_id; /* First job ID */
6741 int limit; /* Maximum number of jobs to return */
6742 int count; /* Number of jobs that match */
6743 cupsd_job_t *job; /* Current job pointer */
6744 cupsd_printer_t *printer; /* Printer */
6745 cups_array_t *list; /* Which job list... */
6746 cups_array_t *ra; /* Requested attributes array */
6747
6748
6749 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", con, con->http.fd,
6750 uri->values[0].string.text);
6751
6752 /*
6753 * Is the destination valid?
ef416fc2 6754 */
6755
f7deaa1a 6756 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6757 sizeof(scheme), username, sizeof(username), host,
a4d04587 6758 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 6759
fa73b229 6760 if (!strcmp(resource, "/") ||
6761 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6))
ef416fc2 6762 {
6763 dest = NULL;
6764 dtype = (cups_ptype_t)0;
6765 dmask = (cups_ptype_t)0;
6766 printer = NULL;
6767 }
fa73b229 6768 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
ef416fc2 6769 {
6770 dest = NULL;
6771 dtype = (cups_ptype_t)0;
6772 dmask = CUPS_PRINTER_CLASS;
6773 printer = NULL;
6774 }
fa73b229 6775 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
ef416fc2 6776 {
6777 dest = NULL;
6778 dtype = CUPS_PRINTER_CLASS;
6779 dmask = CUPS_PRINTER_CLASS;
6780 printer = NULL;
6781 }
f7deaa1a 6782 else if ((dest = cupsdValidateDest(uri->values[0].string.text, &dtype,
6783 &printer)) == NULL)
ef416fc2 6784 {
6785 /*
6786 * Bad URI...
6787 */
6788
6789 send_ipp_status(con, IPP_NOT_FOUND,
6790 _("The printer or class was not found."));
6791 return;
6792 }
6793 else
b86bc4cf 6794 {
6795 dtype &= CUPS_PRINTER_CLASS;
ef416fc2 6796 dmask = CUPS_PRINTER_CLASS;
b86bc4cf 6797 }
ef416fc2 6798
6799 /*
6800 * Check policy...
6801 */
6802
6803 if (printer)
6804 {
f7deaa1a 6805 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
6806 NULL)) != HTTP_OK)
ef416fc2 6807 {
f899b121 6808 send_http_error(con, status, printer);
ef416fc2 6809 return;
6810 }
6811 }
6812 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6813 {
f899b121 6814 send_http_error(con, status, NULL);
ef416fc2 6815 return;
6816 }
6817
6818 /*
6819 * See if the "which-jobs" attribute have been specified...
6820 */
6821
fa73b229 6822 if ((attr = ippFindAttribute(con->request, "which-jobs",
6823 IPP_TAG_KEYWORD)) != NULL &&
ef416fc2 6824 !strcmp(attr->values[0].string.text, "completed"))
6825 {
6826 completed = 1;
6827 list = Jobs;
6828 }
6829 else if (attr && !strcmp(attr->values[0].string.text, "all"))
6830 {
6831 completed = 0;
6832 list = Jobs;
6833 }
3dfe78b3
MS
6834 else if (attr && !strcmp(attr->values[0].string.text, "printing"))
6835 {
6836 completed = 0;
6837 list = PrintingJobs;
6838 }
ef416fc2 6839 else
6840 {
6841 completed = 0;
6842 list = ActiveJobs;
6843 }
6844
6845 /*
6846 * See if they want to limit the number of jobs reported...
6847 */
6848
fa73b229 6849 if ((attr = ippFindAttribute(con->request, "limit",
6850 IPP_TAG_INTEGER)) != NULL)
ef416fc2 6851 limit = attr->values[0].integer;
6852 else
3dfe78b3 6853 limit = 0;
ef416fc2 6854
6855 if ((attr = ippFindAttribute(con->request, "first-job-id",
6856 IPP_TAG_INTEGER)) != NULL)
6857 first_job_id = attr->values[0].integer;
6858 else
6859 first_job_id = 1;
6860
6861 /*
6862 * See if we only want to see jobs for a specific user...
6863 */
6864
fa73b229 6865 if ((attr = ippFindAttribute(con->request, "my-jobs",
6866 IPP_TAG_BOOLEAN)) != NULL &&
ef416fc2 6867 attr->values[0].boolean)
e00b005a 6868 strlcpy(username, get_username(con), sizeof(username));
ef416fc2 6869 else
6870 username[0] = '\0';
6871
fa73b229 6872 ra = create_requested_array(con->request);
ef416fc2 6873
6874 /*
6875 * OK, build a list of jobs for this printer...
6876 */
6877
6878 for (count = 0, job = (cupsd_job_t *)cupsArrayFirst(list);
3dfe78b3 6879 (limit <= 0 || count < limit) && job;
ef416fc2 6880 job = (cupsd_job_t *)cupsArrayNext(list))
6881 {
6882 /*
6883 * Filter out jobs that don't match...
6884 */
6885
6886 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: job->id = %d", job->id);
6887
0a682745
MS
6888 if (!job->dest || !job->username)
6889 cupsdLoadJob(job);
6890
6891 if (!job->dest || !job->username)
6892 continue;
6893
fa73b229 6894 if ((dest && strcmp(job->dest, dest)) &&
6895 (!job->printer || !dest || strcmp(job->printer->name, dest)))
ef416fc2 6896 continue;
6897 if ((job->dtype & dmask) != dtype &&
fa73b229 6898 (!job->printer || (job->printer->type & dmask) != dtype))
ef416fc2 6899 continue;
bd7854cb 6900 if (completed && job->state_value <= IPP_JOB_STOPPED)
ef416fc2 6901 continue;
6902
6903 if (job->id < first_job_id)
6904 continue;
6905
bd7854cb 6906 cupsdLoadJob(job);
6907
6908 if (!job->attrs)
6909 continue;
6910
7594b224 6911 if (username[0] && strcasecmp(username, job->username))
6912 continue;
6913
fa73b229 6914 if (count > 0)
6915 ippAddSeparator(con->response);
6916
ef416fc2 6917 count ++;
6918
6919 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count = %d", count);
6920
fa73b229 6921 copy_job_attrs(con, job, ra);
ef416fc2 6922 }
6923
fa73b229 6924 cupsArrayDelete(ra);
ef416fc2 6925
fa73b229 6926 con->response->request.status.status_code = IPP_OK;
ef416fc2 6927}
6928
6929
6930/*
6931 * 'get_notifications()' - Get events for a subscription.
6932 */
6933
6934static void
bd7854cb 6935get_notifications(cupsd_client_t *con) /* I - Client connection */
ef416fc2 6936{
bd7854cb 6937 int i, j; /* Looping vars */
6938 http_status_t status; /* Policy status */
6939 cupsd_subscription_t *sub; /* Subscription */
6940 ipp_attribute_t *ids, /* notify-subscription-ids */
6941 *sequences; /* notify-sequence-numbers */
6942 int min_seq; /* Minimum sequence number */
6943 int interval; /* Poll interval */
6944
6945
f7deaa1a 6946 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_notifications(con=%p[%d])",
bd7854cb 6947 con, con->http.fd);
6948
6949 /*
6950 * Get subscription attributes...
6951 */
6952
6953 ids = ippFindAttribute(con->request, "notify-subscription-ids",
6954 IPP_TAG_INTEGER);
6955 sequences = ippFindAttribute(con->request, "notify-sequence-numbers",
6956 IPP_TAG_INTEGER);
6957
6958 if (!ids)
6959 {
6960 send_ipp_status(con, IPP_BAD_REQUEST,
6961 _("Missing notify-subscription-ids attribute!"));
6962 return;
6963 }
6964
6965 /*
6966 * Are the subscription IDs valid?
6967 */
6968
6969 for (i = 0, interval = 60; i < ids->num_values; i ++)
6970 {
6971 if ((sub = cupsdFindSubscription(ids->values[i].integer)) == NULL)
6972 {
6973 /*
6974 * Bad subscription ID...
6975 */
6976
6977 send_ipp_status(con, IPP_NOT_FOUND,
6978 _("notify-subscription-id %d no good!"),
6979 ids->values[i].integer);
6980 return;
6981 }
6982
6983 /*
6984 * Check policy...
6985 */
6986
6987 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
6988 DefaultPolicyPtr,
6989 con, sub->owner)) != HTTP_OK)
6990 {
f899b121 6991 send_http_error(con, status, sub->dest);
bd7854cb 6992 return;
6993 }
6994
6995 /*
6996 * Check the subscription type and update the interval accordingly.
6997 */
6998
6999 if (sub->job && sub->job->state_value == IPP_JOB_PROCESSING &&
7000 interval > 10)
7001 interval = 10;
7002 else if (sub->job && sub->job->state_value >= IPP_JOB_STOPPED)
7003 interval = 0;
7004 else if (sub->dest && sub->dest->state == IPP_PRINTER_PROCESSING &&
7005 interval > 30)
7006 interval = 30;
7007 }
7008
7009 /*
7010 * Tell the client to poll again in N seconds...
7011 */
7012
7013 if (interval > 0)
7014 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
7015 "notify-get-interval", interval);
7016
7017 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
7018 "printer-up-time", time(NULL));
7019
7020 /*
7021 * Copy the subscription event attributes to the response.
7022 */
7023
7024 con->response->request.status.status_code =
7025 interval ? IPP_OK : IPP_OK_EVENTS_COMPLETE;
7026
7027 for (i = 0; i < ids->num_values; i ++)
7028 {
7029 /*
7030 * Get the subscription and sequence number...
7031 */
7032
7033 sub = cupsdFindSubscription(ids->values[i].integer);
7034
7035 if (sequences && i < sequences->num_values)
7036 min_seq = sequences->values[i].integer;
7037 else
7038 min_seq = 1;
7039
7040 /*
7041 * If we don't have any new events, nothing to do here...
7042 */
7043
7044 if (min_seq > (sub->first_event_id + sub->num_events))
7045 continue;
7046
7047 /*
7048 * Otherwise copy all of the new events...
7049 */
7050
7051 if (sub->first_event_id > min_seq)
7052 j = 0;
7053 else
7054 j = min_seq - sub->first_event_id;
7055
b423cd4c 7056 for (; j < sub->num_events; j ++)
7057 {
7058 ippAddSeparator(con->response);
ef416fc2 7059
b423cd4c 7060 copy_attrs(con->response, sub->events[j]->attrs, NULL,
7061 IPP_TAG_EVENT_NOTIFICATION, 0);
7062 }
7063 }
7064}
ef416fc2 7065
ef416fc2 7066
b94498cf 7067/*
7068 * 'get_ppd()' - Get a named PPD from the local system.
7069 */
7070
7071static void
7072get_ppd(cupsd_client_t *con, /* I - Client connection */
7073 ipp_attribute_t *uri) /* I - Printer URI or PPD name */
7074{
7075 http_status_t status; /* Policy status */
7076 cupsd_printer_t *dest; /* Destination */
7077 cups_ptype_t dtype; /* Destination type */
7078
7079
7080 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppd(%p[%d], %p[%s=%s])", con,
7081 con->http.fd, uri, uri->name, uri->values[0].string.text);
7082
7083 if (!strcmp(uri->name, "ppd-name"))
7084 {
7085 /*
7086 * Return a PPD file from cups-driverd...
7087 */
7088
7089 char command[1024], /* cups-driverd command */
7090 options[1024], /* Options to pass to command */
7091 ppd_name[1024]; /* ppd-name */
7092
7093
7094 /*
7095 * Check policy...
7096 */
7097
7098 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7099 {
7100 send_http_error(con, status, NULL);
7101 return;
7102 }
7103
7104 /*
7105 * Run cups-driverd command with the given options...
7106 */
7107
7108 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
7109 url_encode_string(uri->values[0].string.text, ppd_name, sizeof(ppd_name));
7110 snprintf(options, sizeof(options), "get+%d+%s",
7111 con->request->request.op.request_id, ppd_name);
7112
7113 if (cupsdSendCommand(con, command, options, 0))
7114 {
7115 /*
7116 * Command started successfully, don't send an IPP response here...
7117 */
7118
7119 ippDelete(con->response);
7120 con->response = NULL;
7121 }
7122 else
7123 {
7124 /*
7125 * Command failed, return "internal error" so the user knows something
7126 * went wrong...
7127 */
7128
7129 send_ipp_status(con, IPP_INTERNAL_ERROR,
7130 _("cups-driverd failed to execute."));
7131 }
7132 }
7133 else if (!strcmp(uri->name, "printer-uri") &&
7134 cupsdValidateDest(uri->values[0].string.text, &dtype, &dest))
7135 {
7136 int i; /* Looping var */
7137 char filename[1024]; /* PPD filename */
7138
7139
7140 /*
7141 * Check policy...
7142 */
7143
7144 if ((status = cupsdCheckPolicy(dest->op_policy_ptr, con, NULL)) != HTTP_OK)
7145 {
2fb76298 7146 send_http_error(con, status, dest);
b94498cf 7147 return;
7148 }
7149
7150 /*
7151 * See if we need the PPD for a class or remote printer...
7152 */
7153
bc44d920 7154 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
7155 dest->name);
7156
7157 if ((dtype & CUPS_PRINTER_REMOTE) && access(filename, 0))
b94498cf 7158 {
3d8365b8 7159 con->response->request.status.status_code = CUPS_SEE_OTHER;
b94498cf 7160 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
7161 "printer-uri", NULL, dest->uri);
7162 return;
7163 }
7164 else if (dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
7165 {
7166 for (i = 0; i < dest->num_printers; i ++)
7167 if (!(dest->printers[i]->type &
bc44d920 7168 (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)))
7169 {
7170 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
7171 dest->printers[i]->name);
7172
7173 if (!access(filename, 0))
7174 break;
7175 }
b94498cf 7176
7177 if (i < dest->num_printers)
7178 dest = dest->printers[i];
7179 else
7180 {
3d8365b8 7181 con->response->request.status.status_code = CUPS_SEE_OTHER;
b94498cf 7182 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
7183 "printer-uri", NULL, dest->printers[0]->uri);
7184 return;
7185 }
7186 }
7187
7188 /*
7189 * Found the printer with the PPD file, now see if there is one...
7190 */
7191
b94498cf 7192 if ((con->file = open(filename, O_RDONLY)) < 0)
7193 {
7194 send_ipp_status(con, IPP_NOT_FOUND,
7195 _("The PPD file \"%s\" could not be opened: %s"),
3d8365b8 7196 uri->values[0].string.text, strerror(errno));
b94498cf 7197 return;
7198 }
7199
7200 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
7201
7202 con->pipe_pid = 0;
7203
3d8365b8 7204 con->response->request.status.status_code = IPP_OK;
b94498cf 7205 }
7206 else
7207 send_ipp_status(con, IPP_NOT_FOUND,
7208 _("The PPD file \"%s\" could not be found."),
7209 uri->values[0].string.text);
7210}
7211
7212
b423cd4c 7213/*
7214 * 'get_ppds()' - Get the list of PPD files on the local system.
7215 */
ef416fc2 7216
b423cd4c 7217static void
7218get_ppds(cupsd_client_t *con) /* I - Client connection */
7219{
7220 http_status_t status; /* Policy status */
b423cd4c 7221 ipp_attribute_t *limit, /* Limit attribute */
b94498cf 7222 *device, /* ppd-device-id attribute */
7223 *language, /* ppd-natural-language attribute */
b423cd4c 7224 *make, /* ppd-make attribute */
b94498cf 7225 *model, /* ppd-make-and-model attribute */
3d8365b8 7226 *model_number, /* ppd-model-number attribute */
b94498cf 7227 *product, /* ppd-product attribute */
7228 *psversion, /* ppd-psverion attribute */
3d8365b8 7229 *type, /* ppd-type attribute */
b423cd4c 7230 *requested; /* requested-attributes attribute */
b94498cf 7231 char command[1024], /* cups-driverd command */
b423cd4c 7232 options[1024], /* Options to pass to command */
b94498cf 7233 device_str[256],/* Escaped ppd-device-id string */
7234 language_str[256],
bc44d920 7235 /* Escaped ppd-natural-language */
b94498cf 7236 make_str[256], /* Escaped ppd-make string */
7237 model_str[256], /* Escaped ppd-make-and-model string */
3d8365b8 7238 model_number_str[256],
7239 /* ppd-model-number string */
b94498cf 7240 product_str[256],
7241 /* Escaped ppd-product string */
7242 psversion_str[256],
7243 /* Escaped ppd-psversion string */
3d8365b8 7244 type_str[256], /* Escaped ppd-type string */
b94498cf 7245 requested_str[256];
89d46774 7246 /* String for requested attributes */
ef416fc2 7247
ef416fc2 7248
b423cd4c 7249 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->http.fd);
ef416fc2 7250
7251 /*
b423cd4c 7252 * Check policy...
ef416fc2 7253 */
7254
b423cd4c 7255 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
ef416fc2 7256 {
f899b121 7257 send_http_error(con, status, NULL);
b423cd4c 7258 return;
ef416fc2 7259 }
ef416fc2 7260
b423cd4c 7261 /*
7262 * Run cups-driverd command with the given options...
7263 */
7264
3d8365b8 7265 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
7266 device = ippFindAttribute(con->request, "ppd-device-id", IPP_TAG_TEXT);
7267 language = ippFindAttribute(con->request, "ppd-natural-language",
7268 IPP_TAG_LANGUAGE);
7269 make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
7270 model = ippFindAttribute(con->request, "ppd-make-and-model",
7271 IPP_TAG_TEXT);
7272 model_number = ippFindAttribute(con->request, "ppd-model-number",
7273 IPP_TAG_INTEGER);
7274 product = ippFindAttribute(con->request, "ppd-product", IPP_TAG_TEXT);
7275 psversion = ippFindAttribute(con->request, "ppd-psversion", IPP_TAG_TEXT);
7276 type = ippFindAttribute(con->request, "ppd-type", IPP_TAG_KEYWORD);
7277 requested = ippFindAttribute(con->request, "requested-attributes",
7278 IPP_TAG_KEYWORD);
b423cd4c 7279
7280 if (requested)
89d46774 7281 url_encode_attr(requested, requested_str, sizeof(requested_str));
7282 else
7283 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
ef416fc2 7284
b94498cf 7285 if (device)
7286 url_encode_attr(device, device_str, sizeof(device_str));
7287 else
7288 device_str[0] = '\0';
7289
7290 if (language)
7291 url_encode_attr(language, language_str, sizeof(language_str));
7292 else
7293 language_str[0] = '\0';
7294
89d46774 7295 if (make)
7296 url_encode_attr(make, make_str, sizeof(make_str));
b423cd4c 7297 else
89d46774 7298 make_str[0] = '\0';
fa73b229 7299
b94498cf 7300 if (model)
7301 url_encode_attr(model, model_str, sizeof(model_str));
7302 else
7303 model_str[0] = '\0';
7304
3d8365b8 7305 if (model_number)
7306 snprintf(model_number_str, sizeof(model_number_str), "ppd-model-number=%d",
7307 model_number->values[0].integer);
7308 else
7309 model_number_str[0] = '\0';
7310
b94498cf 7311 if (product)
7312 url_encode_attr(product, product_str, sizeof(product_str));
7313 else
7314 product_str[0] = '\0';
7315
7316 if (psversion)
7317 url_encode_attr(psversion, psversion_str, sizeof(psversion_str));
7318 else
7319 psversion_str[0] = '\0';
7320
3d8365b8 7321 if (type)
7322 url_encode_attr(type, type_str, sizeof(type_str));
7323 else
7324 type_str[0] = '\0';
7325
b423cd4c 7326 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
3d8365b8 7327 snprintf(options, sizeof(options),
7328 "list+%d+%d+%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
b423cd4c 7329 con->request->request.op.request_id,
7330 limit ? limit->values[0].integer : 0,
b94498cf 7331 requested_str,
7332 device ? "%20" : "", device_str,
7333 language ? "%20" : "", language_str,
7334 make ? "%20" : "", make_str,
7335 model ? "%20" : "", model_str,
3d8365b8 7336 model_number ? "%20" : "", model_number_str,
b94498cf 7337 product ? "%20" : "", product_str,
3d8365b8 7338 psversion ? "%20" : "", psversion_str,
7339 type ? "%20" : "", type_str);
ef416fc2 7340
b423cd4c 7341 if (cupsdSendCommand(con, command, options, 0))
7342 {
7343 /*
7344 * Command started successfully, don't send an IPP response here...
7345 */
7346
7347 ippDelete(con->response);
7348 con->response = NULL;
7349 }
7350 else
7351 {
7352 /*
7353 * Command failed, return "internal error" so the user knows something
7354 * went wrong...
7355 */
7356
7357 send_ipp_status(con, IPP_INTERNAL_ERROR,
7358 _("cups-driverd failed to execute."));
7359 }
ef416fc2 7360}
7361
7362
7363/*
b423cd4c 7364 * 'get_printer_attrs()' - Get printer attributes.
ef416fc2 7365 */
7366
7367static void
b423cd4c 7368get_printer_attrs(cupsd_client_t *con, /* I - Client connection */
7369 ipp_attribute_t *uri) /* I - Printer URI */
ef416fc2 7370{
7371 http_status_t status; /* Policy status */
bc44d920 7372 cups_ptype_t dtype; /* Destination type (printer/class) */
b423cd4c 7373 cupsd_printer_t *printer; /* Printer/class */
ef416fc2 7374 cups_array_t *ra; /* Requested attributes array */
7375
7376
b423cd4c 7377 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", con,
7378 con->http.fd, uri->values[0].string.text);
ef416fc2 7379
7380 /*
b423cd4c 7381 * Is the destination valid?
ef416fc2 7382 */
7383
f7deaa1a 7384 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 7385 {
7386 /*
b423cd4c 7387 * Bad URI...
ef416fc2 7388 */
7389
7390 send_ipp_status(con, IPP_NOT_FOUND,
b423cd4c 7391 _("The printer or class was not found."));
ef416fc2 7392 return;
7393 }
7394
7395 /*
7396 * Check policy...
7397 */
7398
b423cd4c 7399 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
ef416fc2 7400 {
f899b121 7401 send_http_error(con, status, printer);
ef416fc2 7402 return;
7403 }
7404
7405 /*
b423cd4c 7406 * Send the attributes...
ef416fc2 7407 */
7408
7409 ra = create_requested_array(con->request);
7410
b423cd4c 7411 copy_printer_attrs(con, printer, ra);
ef416fc2 7412
7413 cupsArrayDelete(ra);
7414
7415 con->response->request.status.status_code = IPP_OK;
7416}
7417
7418
7419/*
b423cd4c 7420 * 'get_printers()' - Get a list of printers or classes.
ef416fc2 7421 */
7422
7423static void
b423cd4c 7424get_printers(cupsd_client_t *con, /* I - Client connection */
7425 int type) /* I - 0 or CUPS_PRINTER_CLASS */
ef416fc2 7426{
b423cd4c 7427 http_status_t status; /* Policy status */
7428 ipp_attribute_t *attr; /* Current attribute */
bc44d920 7429 int limit; /* Max number of printers to return */
b423cd4c 7430 int count; /* Number of printers that match */
7431 cupsd_printer_t *printer; /* Current printer pointer */
7432 int printer_type, /* printer-type attribute */
7433 printer_mask; /* printer-type-mask attribute */
7434 char *location; /* Location string */
7435 const char *username; /* Current user */
7436 char *first_printer_name; /* first-printer-name attribute */
7437 cups_array_t *ra; /* Requested attributes array */
ef416fc2 7438
ef416fc2 7439
b423cd4c 7440 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con,
7441 con->http.fd, type);
ef416fc2 7442
7443 /*
7444 * Check policy...
7445 */
7446
b423cd4c 7447 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
ef416fc2 7448 {
f899b121 7449 send_http_error(con, status, NULL);
ef416fc2 7450 return;
7451 }
7452
7453 /*
b423cd4c 7454 * Check for printers...
ef416fc2 7455 */
7456
b423cd4c 7457 if (!Printers || !cupsArrayCount(Printers))
7458 {
7459 send_ipp_status(con, IPP_NOT_FOUND, _("No destinations added."));
7460 return;
7461 }
7462
7463 /*
7464 * See if they want to limit the number of printers reported...
7465 */
ef416fc2 7466
fa73b229 7467 if ((attr = ippFindAttribute(con->request, "limit",
7468 IPP_TAG_INTEGER)) != NULL)
ef416fc2 7469 limit = attr->values[0].integer;
7470 else
b423cd4c 7471 limit = 10000000;
7472
7473 if ((attr = ippFindAttribute(con->request, "first-printer-name",
7474 IPP_TAG_NAME)) != NULL)
7475 first_printer_name = attr->values[0].string.text;
7476 else
7477 first_printer_name = NULL;
ef416fc2 7478
7479 /*
b423cd4c 7480 * Support filtering...
ef416fc2 7481 */
7482
b423cd4c 7483 if ((attr = ippFindAttribute(con->request, "printer-type",
7484 IPP_TAG_ENUM)) != NULL)
7485 printer_type = attr->values[0].integer;
ef416fc2 7486 else
b423cd4c 7487 printer_type = 0;
ef416fc2 7488
b423cd4c 7489 if ((attr = ippFindAttribute(con->request, "printer-type-mask",
7490 IPP_TAG_ENUM)) != NULL)
7491 printer_mask = attr->values[0].integer;
ef416fc2 7492 else
b423cd4c 7493 printer_mask = 0;
e00b005a 7494
b423cd4c 7495 if ((attr = ippFindAttribute(con->request, "printer-location",
7496 IPP_TAG_TEXT)) != NULL)
7497 location = attr->values[0].string.text;
7498 else
7499 location = NULL;
e00b005a 7500
7501 if (con->username[0])
b423cd4c 7502 username = con->username;
e00b005a 7503 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7504 IPP_TAG_NAME)) != NULL)
b423cd4c 7505 username = attr->values[0].string.text;
e00b005a 7506 else
b423cd4c 7507 username = NULL;
ef416fc2 7508
b423cd4c 7509 ra = create_requested_array(con->request);
ef416fc2 7510
7511 /*
b423cd4c 7512 * OK, build a list of printers for this printer...
ef416fc2 7513 */
7514
b423cd4c 7515 if (first_printer_name)
ef416fc2 7516 {
b423cd4c 7517 if ((printer = cupsdFindDest(first_printer_name)) == NULL)
7518 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
ef416fc2 7519 }
7520 else
b423cd4c 7521 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
ef416fc2 7522
b423cd4c 7523 for (count = 0;
7524 count < limit && printer;
7525 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
7526 {
7527 if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
7528 (printer->type & printer_mask) == printer_type &&
b19ccc9e
MS
7529 (!location ||
7530 (printer->location && !strcasecmp(printer->location, location))))
ef416fc2 7531 {
7532 /*
b423cd4c 7533 * If HideImplicitMembers is enabled, see if this printer or class
7534 * is a member of an implicit class...
ef416fc2 7535 */
7536
b423cd4c 7537 if (ImplicitClasses && HideImplicitMembers &&
7538 printer->in_implicit_class)
7539 continue;
ef416fc2 7540
b423cd4c 7541 /*
7542 * If a username is specified, see if it is allowed or denied
7543 * access...
7544 */
ef416fc2 7545
dd1abb6b
MS
7546 if (!(printer->type & CUPS_PRINTER_AUTHENTICATED) &&
7547 printer->num_users && username && !user_allowed(printer, username))
b423cd4c 7548 continue;
ef416fc2 7549
b423cd4c 7550 /*
7551 * Add the group separator as needed...
7552 */
ef416fc2 7553
b423cd4c 7554 if (count > 0)
7555 ippAddSeparator(con->response);
ef416fc2 7556
b423cd4c 7557 count ++;
ef416fc2 7558
b423cd4c 7559 /*
7560 * Send the attributes...
7561 */
ef416fc2 7562
b423cd4c 7563 copy_printer_attrs(con, printer, ra);
7564 }
ef416fc2 7565 }
7566
b423cd4c 7567 cupsArrayDelete(ra);
ef416fc2 7568
7569 con->response->request.status.status_code = IPP_OK;
7570}
7571
7572
7573/*
b423cd4c 7574 * 'get_subscription_attrs()' - Get subscription attributes.
ef416fc2 7575 */
7576
7577static void
b423cd4c 7578get_subscription_attrs(
7579 cupsd_client_t *con, /* I - Client connection */
7580 int sub_id) /* I - Subscription ID */
ef416fc2 7581{
b423cd4c 7582 http_status_t status; /* Policy status */
7583 cupsd_subscription_t *sub; /* Subscription */
7584 cups_array_t *ra; /* Requested attributes array */
ef416fc2 7585
7586
b423cd4c 7587 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7588 "get_subscription_attrs(con=%p[%d], sub_id=%d)",
7589 con, con->http.fd, sub_id);
ef416fc2 7590
fa73b229 7591 /*
b423cd4c 7592 * Is the subscription ID valid?
fa73b229 7593 */
7594
b423cd4c 7595 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
fa73b229 7596 {
7597 /*
b423cd4c 7598 * Bad subscription ID...
fa73b229 7599 */
7600
7601 send_ipp_status(con, IPP_NOT_FOUND,
b423cd4c 7602 _("notify-subscription-id %d no good!"), sub_id);
fa73b229 7603 return;
7604 }
7605
7606 /*
7607 * Check policy...
7608 */
7609
b423cd4c 7610 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
7611 DefaultPolicyPtr,
7612 con, sub->owner)) != HTTP_OK)
fa73b229 7613 {
f899b121 7614 send_http_error(con, status, sub->dest);
fa73b229 7615 return;
7616 }
7617
ef416fc2 7618 /*
b423cd4c 7619 * Copy the subscription attributes to the response using the
7620 * requested-attributes attribute that may be provided by the client.
ef416fc2 7621 */
7622
b423cd4c 7623 ra = create_requested_array(con->request);
fa73b229 7624
b423cd4c 7625 copy_subscription_attrs(con, sub, ra);
ef416fc2 7626
b423cd4c 7627 cupsArrayDelete(ra);
fa73b229 7628
b423cd4c 7629 con->response->request.status.status_code = IPP_OK;
7630}
fa73b229 7631
fa73b229 7632
b423cd4c 7633/*
7634 * 'get_subscriptions()' - Get subscriptions.
7635 */
ef416fc2 7636
b423cd4c 7637static void
7638get_subscriptions(cupsd_client_t *con, /* I - Client connection */
7639 ipp_attribute_t *uri) /* I - Printer/job URI */
7640{
7641 http_status_t status; /* Policy status */
7642 int count; /* Number of subscriptions */
7643 int limit; /* Limit */
7644 cupsd_subscription_t *sub; /* Subscription */
7645 cups_array_t *ra; /* Requested attributes array */
7646 ipp_attribute_t *attr; /* Attribute */
bc44d920 7647 cups_ptype_t dtype; /* Destination type (printer/class) */
f7deaa1a 7648 char scheme[HTTP_MAX_URI],
7649 /* Scheme portion of URI */
b423cd4c 7650 username[HTTP_MAX_URI],
7651 /* Username portion of URI */
7652 host[HTTP_MAX_URI],
7653 /* Host portion of URI */
7654 resource[HTTP_MAX_URI];
7655 /* Resource portion of URI */
7656 int port; /* Port portion of URI */
7657 cupsd_job_t *job; /* Job pointer */
7658 cupsd_printer_t *printer; /* Printer */
fa73b229 7659
fa73b229 7660
b423cd4c 7661 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7662 "get_subscriptions(con=%p[%d], uri=%s)",
7663 con, con->http.fd, uri->values[0].string.text);
7664
7665 /*
7666 * Is the destination valid?
7667 */
7668
f7deaa1a 7669 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7670 sizeof(scheme), username, sizeof(username), host,
b423cd4c 7671 sizeof(host), &port, resource, sizeof(resource));
7672
7673 if (!strcmp(resource, "/") ||
7674 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6) ||
7675 (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) ||
7676 (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9))
7677 {
7678 printer = NULL;
7679 job = NULL;
ef416fc2 7680 }
b423cd4c 7681 else if (!strncmp(resource, "/jobs/", 6) && resource[6])
ef416fc2 7682 {
b423cd4c 7683 printer = NULL;
7684 job = cupsdFindJob(atoi(resource + 6));
ef416fc2 7685
b423cd4c 7686 if (!job)
ef416fc2 7687 {
b423cd4c 7688 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%s does not exist!"),
7689 resource + 6);
ef416fc2 7690 return;
7691 }
b423cd4c 7692 }
f7deaa1a 7693 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
b423cd4c 7694 {
fa73b229 7695 /*
b423cd4c 7696 * Bad URI...
fa73b229 7697 */
7698
b423cd4c 7699 send_ipp_status(con, IPP_NOT_FOUND,
7700 _("The printer or class was not found."));
7701 return;
7702 }
7703 else if ((attr = ippFindAttribute(con->request, "notify-job-id",
7704 IPP_TAG_INTEGER)) != NULL)
7705 {
7706 job = cupsdFindJob(attr->values[0].integer);
ef416fc2 7707
b423cd4c 7708 if (!job)
fa73b229 7709 {
b423cd4c 7710 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"),
7711 attr->values[0].integer);
fa73b229 7712 return;
7713 }
ef416fc2 7714 }
b423cd4c 7715 else
7716 job = NULL;
ef416fc2 7717
7718 /*
b423cd4c 7719 * Check policy...
ef416fc2 7720 */
7721
b423cd4c 7722 if ((status = cupsdCheckPolicy(printer ? printer->op_policy_ptr :
7723 DefaultPolicyPtr,
7724 con, NULL)) != HTTP_OK)
ef416fc2 7725 {
f899b121 7726 send_http_error(con, status, printer);
b423cd4c 7727 return;
7728 }
ef416fc2 7729
b423cd4c 7730 /*
7731 * Copy the subscription attributes to the response using the
7732 * requested-attributes attribute that may be provided by the client.
7733 */
ef416fc2 7734
b423cd4c 7735 ra = create_requested_array(con->request);
ef416fc2 7736
b423cd4c 7737 if ((attr = ippFindAttribute(con->request, "limit",
7738 IPP_TAG_INTEGER)) != NULL)
7739 limit = attr->values[0].integer;
7740 else
7741 limit = 0;
ef416fc2 7742
b423cd4c 7743 /*
7744 * See if we only want to see subscriptions for a specific user...
7745 */
ef416fc2 7746
b423cd4c 7747 if ((attr = ippFindAttribute(con->request, "my-subscriptions",
7748 IPP_TAG_BOOLEAN)) != NULL &&
7749 attr->values[0].boolean)
7750 strlcpy(username, get_username(con), sizeof(username));
fa73b229 7751 else
b423cd4c 7752 username[0] = '\0';
ef416fc2 7753
b423cd4c 7754 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0;
7755 sub;
7756 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
7757 if ((!printer || sub->dest == printer) && (!job || sub->job == job) &&
7758 (!username[0] || !strcasecmp(username, sub->owner)))
fa73b229 7759 {
b423cd4c 7760 ippAddSeparator(con->response);
7761 copy_subscription_attrs(con, sub, ra);
ef416fc2 7762
b423cd4c 7763 count ++;
7764 if (limit && count >= limit)
7765 break;
7766 }
ef416fc2 7767
b423cd4c 7768 cupsArrayDelete(ra);
fa73b229 7769
b423cd4c 7770 if (count)
7771 con->response->request.status.status_code = IPP_OK;
7772 else
7773 send_ipp_status(con, IPP_NOT_FOUND, _("No subscriptions found."));
7774}
ef416fc2 7775
ef416fc2 7776
b423cd4c 7777/*
7778 * 'get_username()' - Get the username associated with a request.
7779 */
ef416fc2 7780
b423cd4c 7781static const char * /* O - Username */
7782get_username(cupsd_client_t *con) /* I - Connection */
7783{
7784 ipp_attribute_t *attr; /* Attribute */
ef416fc2 7785
ef416fc2 7786
b423cd4c 7787 if (con->username[0])
7788 return (con->username);
7789 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7790 IPP_TAG_NAME)) != NULL)
7791 return (attr->values[0].string.text);
7792 else
7793 return ("anonymous");
ef416fc2 7794}
7795
7796
7797/*
b423cd4c 7798 * 'hold_job()' - Hold a print job.
ef416fc2 7799 */
7800
b423cd4c 7801static void
7802hold_job(cupsd_client_t *con, /* I - Client connection */
7803 ipp_attribute_t *uri) /* I - Job or Printer URI */
ef416fc2 7804{
b423cd4c 7805 ipp_attribute_t *attr, /* Current job-hold-until */
7806 *newattr; /* New job-hold-until */
7807 int jobid; /* Job ID */
7808 char method[HTTP_MAX_URI], /* Method portion of URI */
7809 username[HTTP_MAX_URI], /* Username portion of URI */
7810 host[HTTP_MAX_URI], /* Host portion of URI */
7811 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7812 int port; /* Port portion of URI */
7813 cupsd_job_t *job; /* Job information */
7814
ef416fc2 7815
b423cd4c 7816 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", con, con->http.fd,
7817 uri->values[0].string.text);
ef416fc2 7818
7819 /*
b423cd4c 7820 * See if we have a job URI or a printer URI...
ef416fc2 7821 */
7822
b423cd4c 7823 if (!strcmp(uri->name, "printer-uri"))
7824 {
7825 /*
7826 * Got a printer URI; see if we also have a job-id attribute...
7827 */
ef416fc2 7828
b423cd4c 7829 if ((attr = ippFindAttribute(con->request, "job-id",
7830 IPP_TAG_INTEGER)) == NULL)
7831 {
7832 send_ipp_status(con, IPP_BAD_REQUEST,
7833 _("Got a printer-uri attribute but no job-id!"));
7834 return;
7835 }
ef416fc2 7836
b423cd4c 7837 jobid = attr->values[0].integer;
7838 }
ef416fc2 7839 else
ef416fc2 7840 {
b423cd4c 7841 /*
7842 * Got a job URI; parse it to get the job ID...
7843 */
ef416fc2 7844
b423cd4c 7845 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
7846 sizeof(method), username, sizeof(username), host,
7847 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 7848
b423cd4c 7849 if (strncmp(resource, "/jobs/", 6))
7850 {
7851 /*
7852 * Not a valid URI!
7853 */
ef416fc2 7854
b423cd4c 7855 send_ipp_status(con, IPP_BAD_REQUEST,
7856 _("Bad job-uri attribute \"%s\"!"),
7857 uri->values[0].string.text);
7858 return;
7859 }
ef416fc2 7860
b423cd4c 7861 jobid = atoi(resource + 6);
7862 }
ef416fc2 7863
ef416fc2 7864 /*
b423cd4c 7865 * See if the job exists...
ef416fc2 7866 */
7867
b423cd4c 7868 if ((job = cupsdFindJob(jobid)) == NULL)
7869 {
7870 /*
7871 * Nope - return a "not found" error...
7872 */
7873
7874 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
7875 return;
7876 }
ef416fc2 7877
7878 /*
b423cd4c 7879 * See if the job is owned by the requesting user...
ef416fc2 7880 */
7881
b423cd4c 7882 if (!validate_user(job, con, job->username, username, sizeof(username)))
7883 {
2fb76298 7884 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
b423cd4c 7885 return;
7886 }
ef416fc2 7887
7888 /*
b423cd4c 7889 * Hold the job and return...
ef416fc2 7890 */
7891
b423cd4c 7892 cupsdHoldJob(job);
ef416fc2 7893
01ce6322 7894 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
d09495fa 7895 "Job held by user.");
7896
b423cd4c 7897 if ((newattr = ippFindAttribute(con->request, "job-hold-until",
7898 IPP_TAG_KEYWORD)) == NULL)
7899 newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
ef416fc2 7900
b423cd4c 7901 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
7902 IPP_TAG_KEYWORD)) == NULL)
7903 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
ef416fc2 7904
b423cd4c 7905 if (attr)
7906 {
7907 /*
7908 * Free the old hold value and copy the new one over...
7909 */
ef416fc2 7910
757d2cad 7911 _cupsStrFree(attr->values[0].string.text);
ef416fc2 7912
b423cd4c 7913 if (newattr)
ef416fc2 7914 {
b423cd4c 7915 attr->value_tag = newattr->value_tag;
7916 attr->values[0].string.text =
757d2cad 7917 _cupsStrAlloc(newattr->values[0].string.text);
b423cd4c 7918 }
7919 else
7920 {
7921 attr->value_tag = IPP_TAG_KEYWORD;
757d2cad 7922 attr->values[0].string.text = _cupsStrAlloc("indefinite");
ef416fc2 7923 }
7924
b423cd4c 7925 /*
7926 * Hold job until specified time...
7927 */
ef416fc2 7928
b423cd4c 7929 cupsdSetJobHoldUntil(job, attr->values[0].string.text);
d09495fa 7930
01ce6322 7931 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
d09495fa 7932 "Job job-hold-until value changed by user.");
b423cd4c 7933 }
ef416fc2 7934
75bd9771 7935 cupsdLogJob(job, CUPSD_LOG_INFO, "Held by \"%s\".", username);
b423cd4c 7936
7937 con->response->request.status.status_code = IPP_OK;
ef416fc2 7938}
7939
7940
7941/*
b423cd4c 7942 * 'move_job()' - Move a job to a new destination.
ef416fc2 7943 */
7944
7945static void
b423cd4c 7946move_job(cupsd_client_t *con, /* I - Client connection */
7947 ipp_attribute_t *uri) /* I - Job URI */
ef416fc2 7948{
7949 http_status_t status; /* Policy status */
7950 ipp_attribute_t *attr; /* Current attribute */
b423cd4c 7951 int jobid; /* Job ID */
ef416fc2 7952 cupsd_job_t *job; /* Current job */
d09495fa 7953 const char *src; /* Source printer/class */
b423cd4c 7954 cups_ptype_t stype, /* Source type (printer or class) */
bc44d920 7955 dtype; /* Destination type (printer/class) */
f7deaa1a 7956 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
ef416fc2 7957 username[HTTP_MAX_URI], /* Username portion of URI */
7958 host[HTTP_MAX_URI], /* Host portion of URI */
b423cd4c 7959 resource[HTTP_MAX_URI]; /* Resource portion of URI */
ef416fc2 7960 int port; /* Port portion of URI */
b423cd4c 7961 cupsd_printer_t *sprinter, /* Source printer */
7962 *dprinter; /* Destination printer */
ef416fc2 7963
7964
b423cd4c 7965 cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", con, con->http.fd,
ef416fc2 7966 uri->values[0].string.text);
7967
7968 /*
b423cd4c 7969 * Get the new printer or class...
ef416fc2 7970 */
7971
b423cd4c 7972 if ((attr = ippFindAttribute(con->request, "job-printer-uri",
7973 IPP_TAG_URI)) == NULL)
ef416fc2 7974 {
b423cd4c 7975 /*
7976 * Need job-printer-uri...
7977 */
ef416fc2 7978
b423cd4c 7979 send_ipp_status(con, IPP_BAD_REQUEST,
7980 _("job-printer-uri attribute missing!"));
7981 return;
ef416fc2 7982 }
f7faf1f5 7983
f7deaa1a 7984 if (!cupsdValidateDest(attr->values[0].string.text, &dtype, &dprinter))
ef416fc2 7985 {
b423cd4c 7986 /*
7987 * Bad URI...
7988 */
ef416fc2 7989
b423cd4c 7990 send_ipp_status(con, IPP_NOT_FOUND,
7991 _("The printer or class was not found."));
7992 return;
ef416fc2 7993 }
7994
ef416fc2 7995 /*
b423cd4c 7996 * See if we have a job URI or a printer URI...
ef416fc2 7997 */
7998
f7deaa1a 7999 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8000 sizeof(scheme), username, sizeof(username), host,
b423cd4c 8001 sizeof(host), &port, resource, sizeof(resource));
8002
8003 if (!strcmp(uri->name, "printer-uri"))
ef416fc2 8004 {
8005 /*
b423cd4c 8006 * Got a printer URI; see if we also have a job-id attribute...
ef416fc2 8007 */
8008
b423cd4c 8009 if ((attr = ippFindAttribute(con->request, "job-id",
8010 IPP_TAG_INTEGER)) == NULL)
ef416fc2 8011 {
b423cd4c 8012 /*
8013 * Move all jobs...
8014 */
8015
f7deaa1a 8016 if ((src = cupsdValidateDest(uri->values[0].string.text, &stype,
8017 &sprinter)) == NULL)
b423cd4c 8018 {
8019 /*
8020 * Bad URI...
8021 */
8022
8023 send_ipp_status(con, IPP_NOT_FOUND,
8024 _("The printer or class was not found."));
8025 return;
8026 }
8027
8028 job = NULL;
8029 }
8030 else
8031 {
8032 /*
8033 * Otherwise, just move a single job...
8034 */
8035
8036 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
8037 {
8038 /*
8039 * Nope - return a "not found" error...
8040 */
8041
8042 send_ipp_status(con, IPP_NOT_FOUND,
8043 _("Job #%d does not exist!"), attr->values[0].integer);
8044 return;
8045 }
8046 else
8047 {
8048 /*
8049 * Job found, initialize source pointers...
8050 */
8051
8052 src = NULL;
8053 sprinter = NULL;
8054 }
ef416fc2 8055 }
8056 }
8057 else
8058 {
8059 /*
b423cd4c 8060 * Got a job URI; parse it to get the job ID...
ef416fc2 8061 */
8062
b423cd4c 8063 if (strncmp(resource, "/jobs/", 6))
8064 {
8065 /*
8066 * Not a valid URI!
8067 */
8068
8069 send_ipp_status(con, IPP_BAD_REQUEST,
8070 _("Bad job-uri attribute \"%s\"!"),
8071 uri->values[0].string.text);
8072 return;
8073 }
ef416fc2 8074
ef416fc2 8075 /*
b423cd4c 8076 * See if the job exists...
ef416fc2 8077 */
8078
b423cd4c 8079 jobid = atoi(resource + 6);
ef416fc2 8080
b423cd4c 8081 if ((job = cupsdFindJob(jobid)) == NULL)
ef416fc2 8082 {
8083 /*
b423cd4c 8084 * Nope - return a "not found" error...
ef416fc2 8085 */
8086
b423cd4c 8087 send_ipp_status(con, IPP_NOT_FOUND,
8088 _("Job #%d does not exist!"), jobid);
8089 return;
ef416fc2 8090 }
8091 else
b423cd4c 8092 {
8093 /*
8094 * Job found, initialize source pointers...
8095 */
ef416fc2 8096
b423cd4c 8097 src = NULL;
8098 sprinter = NULL;
8099 }
ef416fc2 8100 }
8101
ac884b6a
MS
8102 /*
8103 * Check the policy of the destination printer...
8104 */
8105
8106 if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con,
8107 job ? job->username : NULL)) != HTTP_OK)
8108 {
8109 send_http_error(con, status, dprinter);
8110 return;
8111 }
8112
ef416fc2 8113 /*
b423cd4c 8114 * Now move the job or jobs...
ef416fc2 8115 */
8116
b423cd4c 8117 if (job)
8118 {
8119 /*
8120 * See if the job has been completed...
8121 */
8122
8123 if (job->state_value > IPP_JOB_STOPPED)
8124 {
8125 /*
8126 * Return a "not-possible" error...
8127 */
8128
8129 send_ipp_status(con, IPP_NOT_POSSIBLE,
8130 _("Job #%d is finished and cannot be altered!"),
8131 job->id);
8132 return;
8133 }
ef416fc2 8134
b423cd4c 8135 /*
8136 * See if the job is owned by the requesting user...
8137 */
ef416fc2 8138
b423cd4c 8139 if (!validate_user(job, con, job->username, username, sizeof(username)))
8140 {
2fb76298 8141 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
b423cd4c 8142 return;
8143 }
ef416fc2 8144
ef416fc2 8145 /*
b423cd4c 8146 * Move the job to a different printer or class...
ef416fc2 8147 */
8148
e53920b9 8149 cupsdMoveJob(job, dprinter);
ef416fc2 8150 }
b423cd4c 8151 else
8152 {
8153 /*
8154 * Got the source printer, now look through the jobs...
8155 */
ef416fc2 8156
b423cd4c 8157 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
8158 job;
8159 job = (cupsd_job_t *)cupsArrayNext(Jobs))
8160 {
8161 /*
8162 * See if the job is pointing at the source printer or has not been
8163 * completed...
8164 */
ef416fc2 8165
b423cd4c 8166 if (strcasecmp(job->dest, src) ||
8167 job->state_value > IPP_JOB_STOPPED)
8168 continue;
ef416fc2 8169
b423cd4c 8170 /*
8171 * See if the job can be moved by the requesting user...
8172 */
ef416fc2 8173
b423cd4c 8174 if (!validate_user(job, con, job->username, username, sizeof(username)))
8175 continue;
ef416fc2 8176
b423cd4c 8177 /*
8178 * Move the job to a different printer or class...
8179 */
ef416fc2 8180
e53920b9 8181 cupsdMoveJob(job, dprinter);
b423cd4c 8182 }
ef416fc2 8183 }
8184
8185 /*
b423cd4c 8186 * Start jobs if possible...
ef416fc2 8187 */
8188
b423cd4c 8189 cupsdCheckJobs();
ef416fc2 8190
8191 /*
b423cd4c 8192 * Return with "everything is OK" status...
ef416fc2 8193 */
8194
b423cd4c 8195 con->response->request.status.status_code = IPP_OK;
8196}
ef416fc2 8197
ef416fc2 8198
b423cd4c 8199/*
8200 * 'ppd_parse_line()' - Parse a PPD default line.
8201 */
ef416fc2 8202
b423cd4c 8203static int /* O - 0 on success, -1 on failure */
8204ppd_parse_line(const char *line, /* I - Line */
8205 char *option, /* O - Option name */
8206 int olen, /* I - Size of option name */
8207 char *choice, /* O - Choice name */
8208 int clen) /* I - Size of choice name */
8209{
8210 /*
8211 * Verify this is a default option line...
8212 */
ef416fc2 8213
b423cd4c 8214 if (strncmp(line, "*Default", 8))
8215 return (-1);
ef416fc2 8216
b423cd4c 8217 /*
8218 * Read the option name...
8219 */
8220
8221 for (line += 8, olen --; isalnum(*line & 255); line ++)
8222 if (olen > 0)
8223 {
8224 *option++ = *line;
8225 olen --;
ef416fc2 8226 }
8227
b423cd4c 8228 *option = '\0';
ef416fc2 8229
b423cd4c 8230 /*
8231 * Skip everything else up to the colon (:)...
8232 */
ef416fc2 8233
b423cd4c 8234 while (*line && *line != ':')
8235 line ++;
ef416fc2 8236
b423cd4c 8237 if (!*line)
8238 return (-1);
ef416fc2 8239
b423cd4c 8240 line ++;
ef416fc2 8241
b423cd4c 8242 /*
8243 * Now grab the option choice, skipping leading whitespace...
8244 */
ef416fc2 8245
b423cd4c 8246 while (isspace(*line & 255))
8247 line ++;
ef416fc2 8248
b423cd4c 8249 for (clen --; isalnum(*line & 255); line ++)
8250 if (clen > 0)
8251 {
8252 *choice++ = *line;
8253 clen --;
8254 }
ef416fc2 8255
b423cd4c 8256 *choice = '\0';
ef416fc2 8257
b423cd4c 8258 /*
8259 * Return with no errors...
8260 */
ef416fc2 8261
b423cd4c 8262 return (0);
8263}
ef416fc2 8264
ef416fc2 8265
b423cd4c 8266/*
8267 * 'print_job()' - Print a file to a printer or class.
8268 */
ef416fc2 8269
b423cd4c 8270static void
8271print_job(cupsd_client_t *con, /* I - Client connection */
8272 ipp_attribute_t *uri) /* I - Printer URI */
8273{
8274 ipp_attribute_t *attr; /* Current attribute */
8275 ipp_attribute_t *format; /* Document-format attribute */
f7deaa1a 8276 const char *default_format; /* document-format-default value */
b423cd4c 8277 cupsd_job_t *job; /* New job */
8278 char filename[1024]; /* Job filename */
8279 mime_type_t *filetype; /* Type of file */
8280 char super[MIME_MAX_SUPER], /* Supertype of file */
8281 type[MIME_MAX_TYPE], /* Subtype of file */
8282 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
8283 /* Textual name of mime type */
8284 cupsd_printer_t *printer; /* Printer data */
8285 struct stat fileinfo; /* File information */
8286 int kbytes; /* Size of file */
8287 int compression; /* Document compression */
ef416fc2 8288
ef416fc2 8289
b423cd4c 8290 cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", con, con->http.fd,
8291 uri->values[0].string.text);
ef416fc2 8292
b423cd4c 8293 /*
8294 * Validate print file attributes, for now just document-format and
8295 * compression (CUPS only supports "none" and "gzip")...
8296 */
ef416fc2 8297
b423cd4c 8298 compression = CUPS_FILE_NONE;
ef416fc2 8299
b423cd4c 8300 if ((attr = ippFindAttribute(con->request, "compression",
8301 IPP_TAG_KEYWORD)) != NULL)
8302 {
8303 if (strcmp(attr->values[0].string.text, "none")
8304#ifdef HAVE_LIBZ
8305 && strcmp(attr->values[0].string.text, "gzip")
8306#endif /* HAVE_LIBZ */
8307 )
8308 {
8309 send_ipp_status(con, IPP_ATTRIBUTES,
8310 _("Unsupported compression \"%s\"!"),
8311 attr->values[0].string.text);
8312 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
8313 "compression", NULL, attr->values[0].string.text);
8314 return;
8315 }
ef416fc2 8316
b423cd4c 8317#ifdef HAVE_LIBZ
8318 if (!strcmp(attr->values[0].string.text, "gzip"))
8319 compression = CUPS_FILE_GZIP;
8320#endif /* HAVE_LIBZ */
8321 }
ef416fc2 8322
b423cd4c 8323 /*
8324 * Do we have a file to print?
8325 */
ef416fc2 8326
b423cd4c 8327 if (!con->filename)
8328 {
8329 send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?!"));
8330 return;
8331 }
ef416fc2 8332
f7deaa1a 8333 /*
8334 * Is the destination valid?
8335 */
8336
8337 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
8338 {
8339 /*
8340 * Bad URI...
8341 */
8342
8343 send_ipp_status(con, IPP_NOT_FOUND,
8344 _("The printer or class was not found."));
8345 return;
8346 }
8347
b423cd4c 8348 /*
8349 * Is it a format we support?
8350 */
ef416fc2 8351
b423cd4c 8352 if ((format = ippFindAttribute(con->request, "document-format",
8353 IPP_TAG_MIMETYPE)) != NULL)
8354 {
8355 /*
8356 * Grab format from client...
8357 */
8358
f7deaa1a 8359 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super,
8360 type) != 2)
b423cd4c 8361 {
8362 send_ipp_status(con, IPP_BAD_REQUEST,
8363 _("Could not scan type \"%s\"!"),
8364 format->values[0].string.text);
8365 return;
ef416fc2 8366 }
b423cd4c 8367 }
f7deaa1a 8368 else if ((default_format = cupsGetOption("document-format",
8369 printer->num_options,
8370 printer->options)) != NULL)
8371 {
8372 /*
8373 * Use default document format...
8374 */
8375
8376 if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
8377 {
8378 send_ipp_status(con, IPP_BAD_REQUEST,
8379 _("Could not scan type \"%s\"!"),
8380 default_format);
8381 return;
8382 }
8383 }
b423cd4c 8384 else
8385 {
8386 /*
f7deaa1a 8387 * Auto-type it!
b423cd4c 8388 */
8389
8390 strcpy(super, "application");
8391 strcpy(type, "octet-stream");
8392 }
ef416fc2 8393
b423cd4c 8394 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
8395 {
ef416fc2 8396 /*
b423cd4c 8397 * Auto-type the file...
ef416fc2 8398 */
8399
b423cd4c 8400 ipp_attribute_t *doc_name; /* document-name attribute */
8401
8402
a0f6818e 8403 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job ???] Auto-typing file...");
b423cd4c 8404
8405 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
8406 filetype = mimeFileType(MimeDatabase, con->filename,
8407 doc_name ? doc_name->values[0].string.text : NULL,
8408 &compression);
8409
f7deaa1a 8410 if (!filetype)
8411 filetype = mimeType(MimeDatabase, super, type);
a0f6818e
MS
8412
8413 cupsdLogMessage(CUPSD_LOG_INFO, "[Job ???] Request file type is %s/%s.",
8414 filetype->super, filetype->type);
f7deaa1a 8415 }
8416 else
8417 filetype = mimeType(MimeDatabase, super, type);
ef416fc2 8418
f7deaa1a 8419 if (filetype &&
8420 (!format ||
8421 (!strcmp(super, "application") && !strcmp(type, "octet-stream"))))
8422 {
8423 /*
8424 * Replace the document-format attribute value with the auto-typed or
8425 * default one.
8426 */
b423cd4c 8427
f7deaa1a 8428 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
8429 filetype->type);
ed486911 8430
f7deaa1a 8431 if (format)
8432 {
8433 _cupsStrFree(format->values[0].string.text);
8434
8435 format->values[0].string.text = _cupsStrAlloc(mimetype);
ef416fc2 8436 }
b423cd4c 8437 else
f7deaa1a 8438 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
8439 "document-format", NULL, mimetype);
ef416fc2 8440 }
f7deaa1a 8441 else if (!filetype)
b423cd4c 8442 {
8443 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
8444 _("Unsupported format \'%s/%s\'!"), super, type);
8445 cupsdLogMessage(CUPSD_LOG_INFO,
8446 "Hint: Do you have the raw file printing rules enabled?");
8447
8448 if (format)
8449 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
8450 "document-format", NULL, format->values[0].string.text);
8451
8452 return;
8453 }
8454
b423cd4c 8455 /*
8456 * Read any embedded job ticket info from PS files...
8457 */
8458
8459 if (!strcasecmp(filetype->super, "application") &&
c5571a1d
MS
8460 (!strcasecmp(filetype->type, "postscript") ||
8461 !strcasecmp(filetype->type, "pdf")))
8462 read_job_ticket(con);
b423cd4c 8463
8464 /*
8465 * Create the job object...
8466 */
8467
f7deaa1a 8468 if ((job = add_job(con, printer, filetype)) == NULL)
b423cd4c 8469 return;
8470
8471 /*
8472 * Update quota data...
8473 */
8474
8475 if (stat(con->filename, &fileinfo))
8476 kbytes = 0;
8477 else
8478 kbytes = (fileinfo.st_size + 1023) / 1024;
8479
8480 cupsdUpdateQuota(printer, job->username, 0, kbytes);
8481
8482 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
8483 IPP_TAG_INTEGER)) != NULL)
8484 attr->values[0].integer += kbytes;
8485
ef416fc2 8486 /*
8487 * Add the job file...
8488 */
8489
8490 if (add_file(con, job, filetype, compression))
8491 return;
8492
8493 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
8494 job->num_files);
8495 rename(con->filename, filename);
8496 cupsdClearString(&con->filename);
8497
8498 /*
8499 * See if we need to add the ending sheet...
8500 */
8501
91c84a35
MS
8502 if (cupsdTimeoutJob(job))
8503 return;
ef416fc2 8504
ef416fc2 8505 /*
8506 * Log and save the job...
8507 */
8508
75bd9771
MS
8509 cupsdLogJob(job, CUPSD_LOG_INFO,
8510 "File of type %s/%s queued by \"%s\".",
8511 filetype->super, filetype->type, job->username);
8512 cupsdLogJob(job, CUPSD_LOG_DEBUG, "hold_until=%d", (int)job->hold_until);
b19ccc9e
MS
8513 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
8514 job->dest, job->username);
ef416fc2 8515
ef416fc2 8516 /*
8517 * Start the job if possible...
8518 */
8519
8520 cupsdCheckJobs();
8521}
8522
8523
8524/*
c5571a1d 8525 * 'read_job_ticket()' - Read a job ticket embedded in a print file.
ef416fc2 8526 *
c5571a1d 8527 * This function only gets called when printing a single PDF or PostScript
ef416fc2 8528 * file using the Print-Job operation. It doesn't work for Create-Job +
8529 * Send-File, since the job attributes need to be set at job creation
c5571a1d
MS
8530 * time for banners to work. The embedded job ticket stuff is here
8531 * primarily to allow the Windows printer driver for CUPS to pass in JCL
ef416fc2 8532 * options and IPP attributes which otherwise would be lost.
8533 *
c5571a1d 8534 * The format of a job ticket is simple:
ef416fc2 8535 *
8536 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
8537 *
8538 * %cupsJobTicket: attr1=value1
8539 * %cupsJobTicket: attr2=value2
8540 * ...
8541 * %cupsJobTicket: attrN=valueN
8542 *
8543 * Job ticket lines must appear immediately after the first line that
c5571a1d
MS
8544 * specifies PostScript (%!PS-Adobe-3.0) or PDF (%PDF) format, and CUPS
8545 * stops looking for job ticket info when it finds a line that does not begin
ef416fc2 8546 * with "%cupsJobTicket:".
8547 *
8548 * The maximum length of a job ticket line, including the prefix, is
8549 * 255 characters to conform with the Adobe DSC.
8550 *
8551 * Read-only attributes are rejected with a notice to the error log in
8552 * case a malicious user tries anything. Since the job ticket is read
8553 * prior to attribute validation in print_job(), job ticket attributes
8554 * will go through the same validation as IPP attributes...
8555 */
8556
8557static void
c5571a1d 8558read_job_ticket(cupsd_client_t *con) /* I - Client connection */
ef416fc2 8559{
8560 cups_file_t *fp; /* File to read from */
8561 char line[256]; /* Line data */
8562 int num_options; /* Number of options */
8563 cups_option_t *options; /* Options */
8564 ipp_t *ticket; /* New attributes */
8565 ipp_attribute_t *attr, /* Current attribute */
8566 *attr2, /* Job attribute */
8567 *prev2; /* Previous job attribute */
8568
8569
8570 /*
8571 * First open the print file...
8572 */
8573
8574 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
8575 {
8576 cupsdLogMessage(CUPSD_LOG_ERROR,
c5571a1d 8577 "Unable to open print file for job ticket - %s",
ef416fc2 8578 strerror(errno));
8579 return;
8580 }
8581
8582 /*
8583 * Skip the first line...
8584 */
8585
8586 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
8587 {
8588 cupsdLogMessage(CUPSD_LOG_ERROR,
c5571a1d 8589 "Unable to read from print file for job ticket - %s",
ef416fc2 8590 strerror(errno));
8591 cupsFileClose(fp);
8592 return;
8593 }
8594
c5571a1d 8595 if (strncmp(line, "%!PS-Adobe-", 11) && strncmp(line, "%PDF-", 5))
ef416fc2 8596 {
8597 /*
8598 * Not a DSC-compliant file, so no job ticket info will be available...
8599 */
8600
8601 cupsFileClose(fp);
8602 return;
8603 }
8604
8605 /*
8606 * Read job ticket info from the file...
8607 */
8608
8609 num_options = 0;
8610 options = NULL;
8611
fa73b229 8612 while (cupsFileGets(fp, line, sizeof(line)))
ef416fc2 8613 {
8614 /*
8615 * Stop at the first non-ticket line...
8616 */
8617
fa73b229 8618 if (strncmp(line, "%cupsJobTicket:", 15))
ef416fc2 8619 break;
8620
8621 /*
8622 * Add the options to the option array...
8623 */
8624
8625 num_options = cupsParseOptions(line + 15, num_options, &options);
8626 }
8627
8628 /*
8629 * Done with the file; see if we have any options...
8630 */
8631
8632 cupsFileClose(fp);
8633
8634 if (num_options == 0)
8635 return;
8636
8637 /*
8638 * OK, convert the options to an attribute list, and apply them to
8639 * the request...
8640 */
8641
8642 ticket = ippNew();
8643 cupsEncodeOptions(ticket, num_options, options);
8644
8645 /*
8646 * See what the user wants to change.
8647 */
8648
fa73b229 8649 for (attr = ticket->attrs; attr; attr = attr->next)
ef416fc2 8650 {
8651 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
8652 continue;
8653
8654 if (!strcmp(attr->name, "job-originating-host-name") ||
8655 !strcmp(attr->name, "job-originating-user-name") ||
8656 !strcmp(attr->name, "job-media-sheets-completed") ||
8657 !strcmp(attr->name, "job-k-octets") ||
8658 !strcmp(attr->name, "job-id") ||
8659 !strncmp(attr->name, "job-state", 9) ||
8660 !strncmp(attr->name, "time-at-", 8))
8661 continue; /* Read-only attrs */
8662
fa73b229 8663 if ((attr2 = ippFindAttribute(con->request, attr->name,
8664 IPP_TAG_ZERO)) != NULL)
ef416fc2 8665 {
8666 /*
8667 * Some other value; first free the old value...
8668 */
8669
8670 if (con->request->attrs == attr2)
8671 {
8672 con->request->attrs = attr2->next;
8673 prev2 = NULL;
8674 }
8675 else
8676 {
fa73b229 8677 for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
ef416fc2 8678 if (prev2->next == attr2)
8679 {
8680 prev2->next = attr2->next;
8681 break;
8682 }
8683 }
8684
8685 if (con->request->last == attr2)
8686 con->request->last = prev2;
8687
757d2cad 8688 _ippFreeAttr(attr2);
ef416fc2 8689 }
8690
8691 /*
8692 * Add new option by copying it...
8693 */
8694
8695 copy_attribute(con->request, attr, 0);
8696 }
8697
8698 /*
8699 * Then free the attribute list and option array...
8700 */
8701
8702 ippDelete(ticket);
8703 cupsFreeOptions(num_options, options);
8704}
8705
8706
8707/*
8708 * 'reject_jobs()' - Reject print jobs to a printer.
8709 */
8710
8711static void
8712reject_jobs(cupsd_client_t *con, /* I - Client connection */
8713 ipp_attribute_t *uri) /* I - Printer or class URI */
8714{
8715 http_status_t status; /* Policy status */
bc44d920 8716 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 8717 cupsd_printer_t *printer; /* Printer data */
8718 ipp_attribute_t *attr; /* printer-state-message text */
8719
8720
8721 cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", con,
8722 con->http.fd, uri->values[0].string.text);
8723
8724 /*
8725 * Is the destination valid?
8726 */
8727
f7deaa1a 8728 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 8729 {
8730 /*
8731 * Bad URI...
8732 */
8733
8734 send_ipp_status(con, IPP_NOT_FOUND,
8735 _("The printer or class was not found."));
8736 return;
8737 }
8738
8739 /*
8740 * Check policy...
8741 */
8742
8743 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8744 {
f899b121 8745 send_http_error(con, status, printer);
ef416fc2 8746 return;
8747 }
8748
8749 /*
8750 * Reject jobs sent to the printer...
8751 */
8752
8753 printer->accepting = 0;
8754
8755 if ((attr = ippFindAttribute(con->request, "printer-state-message",
8756 IPP_TAG_TEXT)) == NULL)
8757 strcpy(printer->state_message, "Rejecting Jobs");
8758 else
8759 strlcpy(printer->state_message, attr->values[0].string.text,
8760 sizeof(printer->state_message));
8761
8762 cupsdAddPrinterHistory(printer);
8763
8764 if (dtype & CUPS_PRINTER_CLASS)
8765 {
3dfe78b3 8766 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
ef416fc2 8767
8768 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
f7deaa1a 8769 printer->name, get_username(con));
ef416fc2 8770 }
8771 else
8772 {
3dfe78b3 8773 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 8774
8775 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
f7deaa1a 8776 printer->name, get_username(con));
ef416fc2 8777 }
8778
8779 /*
8780 * Everything was ok, so return OK status...
8781 */
8782
8783 con->response->request.status.status_code = IPP_OK;
8784}
8785
8786
8787/*
8788 * 'release_job()' - Release a held print job.
8789 */
8790
8791static void
8792release_job(cupsd_client_t *con, /* I - Client connection */
8793 ipp_attribute_t *uri) /* I - Job or Printer URI */
8794{
8795 ipp_attribute_t *attr; /* Current attribute */
8796 int jobid; /* Job ID */
8797 char method[HTTP_MAX_URI], /* Method portion of URI */
8798 username[HTTP_MAX_URI], /* Username portion of URI */
8799 host[HTTP_MAX_URI], /* Host portion of URI */
8800 resource[HTTP_MAX_URI]; /* Resource portion of URI */
8801 int port; /* Port portion of URI */
8802 cupsd_job_t *job; /* Job information */
8803
8804
8805 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", con,
8806 con->http.fd, uri->values[0].string.text);
8807
8808 /*
8809 * See if we have a job URI or a printer URI...
8810 */
8811
8812 if (!strcmp(uri->name, "printer-uri"))
8813 {
8814 /*
8815 * Got a printer URI; see if we also have a job-id attribute...
8816 */
8817
fa73b229 8818 if ((attr = ippFindAttribute(con->request, "job-id",
8819 IPP_TAG_INTEGER)) == NULL)
ef416fc2 8820 {
8821 send_ipp_status(con, IPP_BAD_REQUEST,
8822 _("Got a printer-uri attribute but no job-id!"));
8823 return;
8824 }
8825
8826 jobid = attr->values[0].integer;
8827 }
8828 else
8829 {
8830 /*
8831 * Got a job URI; parse it to get the job ID...
8832 */
8833
a4d04587 8834 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
8835 sizeof(method), username, sizeof(username), host,
8836 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 8837
8838 if (strncmp(resource, "/jobs/", 6))
8839 {
8840 /*
8841 * Not a valid URI!
8842 */
8843
8844 send_ipp_status(con, IPP_BAD_REQUEST,
8845 _("Bad job-uri attribute \"%s\"!"),
8846 uri->values[0].string.text);
8847 return;
8848 }
8849
8850 jobid = atoi(resource + 6);
8851 }
8852
8853 /*
8854 * See if the job exists...
8855 */
8856
8857 if ((job = cupsdFindJob(jobid)) == NULL)
8858 {
8859 /*
8860 * Nope - return a "not found" error...
8861 */
8862
8863 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
8864 return;
8865 }
8866
8867 /*
8868 * See if job is "held"...
8869 */
8870
bd7854cb 8871 if (job->state_value != IPP_JOB_HELD)
ef416fc2 8872 {
8873 /*
8874 * Nope - return a "not possible" error...
8875 */
8876
8877 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held!"), jobid);
8878 return;
8879 }
8880
8881 /*
8882 * See if the job is owned by the requesting user...
8883 */
8884
8885 if (!validate_user(job, con, job->username, username, sizeof(username)))
8886 {
2fb76298 8887 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
ef416fc2 8888 return;
8889 }
8890
8891 /*
8892 * Reset the job-hold-until value to "no-hold"...
8893 */
8894
fa73b229 8895 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
8896 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 8897 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
8898
fa73b229 8899 if (attr)
ef416fc2 8900 {
757d2cad 8901 _cupsStrFree(attr->values[0].string.text);
4400e98d 8902
ef416fc2 8903 attr->value_tag = IPP_TAG_KEYWORD;
757d2cad 8904 attr->values[0].string.text = _cupsStrAlloc("no-hold");
d09495fa 8905
01ce6322 8906 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
d09495fa 8907 "Job job-hold-until value changed by user.");
ef416fc2 8908 }
8909
8910 /*
8911 * Release the job and return...
8912 */
8913
8914 cupsdReleaseJob(job);
8915
01ce6322 8916 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
d09495fa 8917 "Job released by user.");
8918
75bd9771 8919 cupsdLogJob(job, CUPSD_LOG_INFO, "Released by \"%s\".", username);
ef416fc2 8920
8921 con->response->request.status.status_code = IPP_OK;
005dd1eb
MS
8922
8923 cupsdCheckJobs();
ef416fc2 8924}
8925
8926
8927/*
8928 * 'renew_subscription()' - Renew an existing subscription...
8929 */
8930
8931static void
8932renew_subscription(
8933 cupsd_client_t *con, /* I - Client connection */
8934 int sub_id) /* I - Subscription ID */
8935{
bd7854cb 8936 http_status_t status; /* Policy status */
8937 cupsd_subscription_t *sub; /* Subscription */
8938 ipp_attribute_t *lease; /* notify-lease-duration */
8939
8940
8941 cupsdLogMessage(CUPSD_LOG_DEBUG2,
8942 "renew_subscription(con=%p[%d], sub_id=%d)",
8943 con, con->http.fd, sub_id);
8944
8945 /*
8946 * Is the subscription ID valid?
8947 */
8948
8949 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
8950 {
8951 /*
8952 * Bad subscription ID...
8953 */
8954
8955 send_ipp_status(con, IPP_NOT_FOUND,
8956 _("notify-subscription-id %d no good!"), sub_id);
8957 return;
8958 }
8959
8960 if (sub->job)
8961 {
8962 /*
8963 * Job subscriptions cannot be renewed...
8964 */
8965
8966 send_ipp_status(con, IPP_NOT_POSSIBLE,
8967 _("Job subscriptions cannot be renewed!"));
8968 return;
8969 }
8970
8971 /*
8972 * Check policy...
8973 */
8974
8975 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
8976 DefaultPolicyPtr,
8977 con, sub->owner)) != HTTP_OK)
8978 {
f899b121 8979 send_http_error(con, status, sub->dest);
bd7854cb 8980 return;
8981 }
8982
8983 /*
8984 * Renew the subscription...
8985 */
8986
8987 lease = ippFindAttribute(con->request, "notify-lease-duration",
8988 IPP_TAG_INTEGER);
8989
8990 sub->lease = lease ? lease->values[0].integer : DefaultLeaseDuration;
8991
8992 if (MaxLeaseDuration && (sub->lease == 0 || sub->lease > MaxLeaseDuration))
8993 {
8994 cupsdLogMessage(CUPSD_LOG_INFO,
8995 "renew_subscription: Limiting notify-lease-duration to "
8996 "%d seconds.",
8997 MaxLeaseDuration);
8998 sub->lease = MaxLeaseDuration;
8999 }
9000
9001 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
9002
3dfe78b3 9003 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
bd7854cb 9004
9005 con->response->request.status.status_code = IPP_OK;
b94498cf 9006
9007 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
9008 "notify-lease-duration", sub->lease);
ef416fc2 9009}
9010
9011
9012/*
9013 * 'restart_job()' - Restart an old print job.
9014 */
9015
9016static void
9017restart_job(cupsd_client_t *con, /* I - Client connection */
9018 ipp_attribute_t *uri) /* I - Job or Printer URI */
9019{
9020 ipp_attribute_t *attr; /* Current attribute */
9021 int jobid; /* Job ID */
9022 char method[HTTP_MAX_URI], /* Method portion of URI */
9023 username[HTTP_MAX_URI], /* Username portion of URI */
9024 host[HTTP_MAX_URI], /* Host portion of URI */
9025 resource[HTTP_MAX_URI]; /* Resource portion of URI */
9026 int port; /* Port portion of URI */
9027 cupsd_job_t *job; /* Job information */
9028
9029
9030 cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con,
9031 con->http.fd, uri->values[0].string.text);
9032
9033 /*
9034 * See if we have a job URI or a printer URI...
9035 */
9036
9037 if (!strcmp(uri->name, "printer-uri"))
9038 {
9039 /*
9040 * Got a printer URI; see if we also have a job-id attribute...
9041 */
9042
fa73b229 9043 if ((attr = ippFindAttribute(con->request, "job-id",
9044 IPP_TAG_INTEGER)) == NULL)
ef416fc2 9045 {
9046 send_ipp_status(con, IPP_BAD_REQUEST,
9047 _("Got a printer-uri attribute but no job-id!"));
9048 return;
9049 }
9050
9051 jobid = attr->values[0].integer;
9052 }
9053 else
9054 {
9055 /*
9056 * Got a job URI; parse it to get the job ID...
9057 */
9058
a4d04587 9059 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
9060 sizeof(method), username, sizeof(username), host,
9061 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 9062
fa73b229 9063 if (strncmp(resource, "/jobs/", 6))
ef416fc2 9064 {
9065 /*
9066 * Not a valid URI!
9067 */
9068
9069 send_ipp_status(con, IPP_BAD_REQUEST,
9070 _("Bad job-uri attribute \"%s\"!"),
9071 uri->values[0].string.text);
9072 return;
9073 }
9074
9075 jobid = atoi(resource + 6);
9076 }
9077
9078 /*
9079 * See if the job exists...
9080 */
9081
9082 if ((job = cupsdFindJob(jobid)) == NULL)
9083 {
9084 /*
9085 * Nope - return a "not found" error...
9086 */
9087
9088 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
9089 return;
9090 }
9091
9092 /*
9093 * See if job is in any of the "completed" states...
9094 */
9095
bd7854cb 9096 if (job->state_value <= IPP_JOB_PROCESSING)
ef416fc2 9097 {
9098 /*
9099 * Nope - return a "not possible" error...
9100 */
9101
9102 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete!"),
9103 jobid);
9104 return;
9105 }
9106
9107 /*
9108 * See if we have retained the job files...
9109 */
9110
bd7854cb 9111 cupsdLoadJob(job);
9112
07725fee 9113 if (!job->attrs || job->num_files == 0)
ef416fc2 9114 {
9115 /*
9116 * Nope - return a "not possible" error...
9117 */
9118
9119 send_ipp_status(con, IPP_NOT_POSSIBLE,
9120 _("Job #%d cannot be restarted - no files!"), jobid);
9121 return;
9122 }
9123
9124 /*
9125 * See if the job is owned by the requesting user...
9126 */
9127
9128 if (!validate_user(job, con, job->username, username, sizeof(username)))
9129 {
2fb76298 9130 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
ef416fc2 9131 return;
9132 }
9133
9134 /*
9135 * Restart the job and return...
9136 */
9137
9138 cupsdRestartJob(job);
9139
75bd9771 9140 cupsdLogJob(job, CUPSD_LOG_INFO, "Restarted by \"%s\".", username);
ef416fc2 9141
9142 con->response->request.status.status_code = IPP_OK;
9143}
9144
9145
9146/*
9147 * 'save_auth_info()' - Save authentication information for a job.
9148 */
9149
9150static void
f7deaa1a 9151save_auth_info(
9152 cupsd_client_t *con, /* I - Client connection */
9153 cupsd_job_t *job, /* I - Job */
9154 ipp_attribute_t *auth_info) /* I - auth-info attribute, if any */
ef416fc2 9155{
09a101d6 9156 int i; /* Looping var */
9157 char filename[1024]; /* Job authentication filename */
9158 cups_file_t *fp; /* Job authentication file */
9159 char line[2048]; /* Line for file */
9160 cupsd_printer_t *dest; /* Destination printer/class */
ef416fc2 9161
9162
9163 /*
9164 * This function saves the in-memory authentication information for
9165 * a job so that it can be used to authenticate with a remote host.
9166 * The information is stored in a file that is readable only by the
f7deaa1a 9167 * root user. The fields are Base-64 encoded, each on a separate line,
9168 * followed by random number (up to 1024) of newlines to limit the
9169 * amount of information that is exposed.
ef416fc2 9170 *
9171 * Because of the potential for exposing of authentication information,
9172 * this functionality is only enabled when running cupsd as root.
9173 *
9174 * This caching only works for the Basic and BasicDigest authentication
9175 * types. Digest authentication cannot be cached this way, and in
9176 * the future Kerberos authentication may make all of this obsolete.
9177 *
9178 * Authentication information is saved whenever an authenticated
9179 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
9180 * performed.
9181 *
9182 * This information is deleted after a job is completed or canceled,
9183 * so reprints may require subsequent re-authentication.
9184 */
9185
9186 if (RunUser)
9187 return;
9188
09a101d6 9189 if ((dest = cupsdFindDest(job->dest)) == NULL)
9190 return;
9191
ef416fc2 9192 /*
9193 * Create the authentication file and change permissions...
9194 */
9195
9196 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
9197 if ((fp = cupsFileOpen(filename, "w")) == NULL)
9198 {
9199 cupsdLogMessage(CUPSD_LOG_ERROR,
9200 "Unable to save authentication info to \"%s\" - %s",
9201 filename, strerror(errno));
9202 return;
9203 }
9204
9205 fchown(cupsFileNumber(fp), 0, 0);
9206 fchmod(cupsFileNumber(fp), 0400);
9207
09a101d6 9208 if (auth_info && auth_info->num_values == dest->num_auth_info_required)
f7deaa1a 9209 {
9210 /*
09a101d6 9211 * Write 1 to 3 auth values...
f7deaa1a 9212 */
ef416fc2 9213
09a101d6 9214 cupsdClearString(&job->auth_username);
9215 cupsdClearString(&job->auth_domain);
9216 cupsdClearString(&job->auth_password);
9217
f7deaa1a 9218 for (i = 0; i < auth_info->num_values; i ++)
9219 {
9220 httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text,
9221 strlen(auth_info->values[i].string.text));
9222 cupsFilePrintf(fp, "%s\n", line);
09a101d6 9223
9224 if (!strcmp(dest->auth_info_required[i], "username"))
839a51c8 9225 cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s",
09a101d6 9226 auth_info->values[i].string.text);
9227 else if (!strcmp(dest->auth_info_required[i], "domain"))
839a51c8 9228 cupsdSetStringf(&job->auth_domain, "AUTH_DOMAIN=%s",
09a101d6 9229 auth_info->values[i].string.text);
9230 else if (!strcmp(dest->auth_info_required[i], "password"))
839a51c8 9231 cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s",
09a101d6 9232 auth_info->values[i].string.text);
f7deaa1a 9233 }
9234 }
09a101d6 9235 else if (con->username[0])
f7deaa1a 9236 {
9237 /*
9238 * Write the authenticated username...
9239 */
ef416fc2 9240
f7deaa1a 9241 httpEncode64_2(line, sizeof(line), con->username, strlen(con->username));
9242 cupsFilePrintf(fp, "%s\n", line);
ef416fc2 9243
839a51c8 9244 cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s", con->username);
09a101d6 9245 cupsdClearString(&job->auth_domain);
9246
f7deaa1a 9247 /*
9248 * Write the authenticated password...
9249 */
9250
9251 httpEncode64_2(line, sizeof(line), con->password, strlen(con->password));
9252 cupsFilePrintf(fp, "%s\n", line);
09a101d6 9253
839a51c8 9254 cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s", con->password);
f7deaa1a 9255 }
ef416fc2 9256
9257 /*
9258 * Write a random number of newlines to the end of the file...
9259 */
9260
9261 for (i = (rand() % 1024); i >= 0; i --)
9262 cupsFilePutChar(fp, '\n');
9263
9264 /*
9265 * Close the file and return...
9266 */
9267
9268 cupsFileClose(fp);
f7deaa1a 9269
9270#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
09a101d6 9271 if (con->gss_have_creds)
9272 save_krb5_creds(con, job);
9273 else if (job->ccname)
9274 cupsdClearString(&(job->ccname));
f7deaa1a 9275#endif /* HAVE_GSSAPI && HAVE_KRB5_H */
9276}
9277
9278
9279#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
9280/*
9281 * 'save_krb5_creds()' - Save Kerberos credentials for the job.
9282 */
9283
9284static void
9285save_krb5_creds(cupsd_client_t *con, /* I - Client connection */
9286 cupsd_job_t *job) /* I - Job */
9287{
7ff4fea9
MS
9288# if !defined(HAVE_KRB5_CC_NEW_UNIQUE) && !defined(HAVE_HEIMDAL)
9289 cupsdLogMessage(CUPSD_LOG_INFO,
9290 "Sorry, your version of Kerberos does not support delegated "
9291 "credentials!");
9292 return;
9293
9294# else
db1f069b
MS
9295 krb5_error_code error; /* Kerberos error code */
9296 OM_uint32 major_status, /* Major status code */
9297 minor_status; /* Minor status code */
db1f069b 9298 krb5_principal principal; /* Kerberos principal */
f7deaa1a 9299
9300
7ff4fea9 9301# ifdef __APPLE__
f7deaa1a 9302 /*
c24d2134
MS
9303 * If the weak-linked GSSAPI/Kerberos library is not present, don't try
9304 * to use it...
f7deaa1a 9305 */
9306
c24d2134 9307 if (krb5_init_context == NULL)
f7deaa1a 9308 return;
7ff4fea9 9309# endif /* __APPLE__ */
f7deaa1a 9310
0af14961
MS
9311 if (!KerberosInitialized)
9312 {
9313 /*
9314 * Setup a Kerberos context for the scheduler to use...
9315 */
9316
9317 KerberosInitialized = 1;
9318
9319 if (krb5_init_context(&KerberosContext))
9320 {
9321 KerberosContext = NULL;
9322
9323 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize Kerberos context");
9324 return;
9325 }
9326 }
9327
cc0d019f
MS
9328 /*
9329 * We MUST create a file-based cache because memory-based caches are
9330 * only valid for the current process/address space.
7ff4fea9
MS
9331 *
9332 * Due to various bugs/features in different versions of Kerberos, we
9333 * need either the krb5_cc_new_unique() function or Heimdal's version
9334 * of krb5_cc_gen_new() to create a new FILE: credential cache that
9335 * can be passed to the backend. These functions create a temporary
9336 * file (typically in /tmp) containing the cached credentials, which
9337 * are removed when we have successfully printed a job.
9338 */
9339
9340# ifdef HAVE_KRB5_CC_NEW_UNIQUE
9341 if ((error = krb5_cc_new_unique(KerberosContext, "FILE", NULL,
9342 &(job->ccache))) != 0)
9343# else /* HAVE_HEIMDAL */
0a682745 9344 if ((error = krb5_cc_gen_new(KerberosContext, &krb5_fcc_ops,
7ff4fea9
MS
9345 &(job->ccache))) != 0)
9346# endif /* HAVE_KRB5_CC_NEW_UNIQUE */
9347 {
db1f069b
MS
9348 cupsdLogMessage(CUPSD_LOG_ERROR,
9349 "Unable to create new credentials cache (%d/%s)",
7ff4fea9 9350 error, strerror(errno));
76cd9e37 9351 job->ccache = NULL;
f7deaa1a 9352 return;
9353 }
9354
db1f069b
MS
9355 if ((error = krb5_parse_name(KerberosContext, con->username, &principal)) != 0)
9356 {
9357 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to parse kerberos username (%d/%s)",
9358 error, strerror(errno));
9359 krb5_cc_destroy(KerberosContext, job->ccache);
9360 job->ccache = NULL;
9361 return;
9362 }
9363
9364 if ((error = krb5_cc_initialize(KerberosContext, job->ccache, principal)))
9365 {
9366 cupsdLogMessage(CUPSD_LOG_ERROR,
9367 "Unable to initialize credentials cache (%d/%s)", error,
9368 strerror(errno));
9369 krb5_cc_destroy(KerberosContext, job->ccache);
9370 krb5_free_principal(KerberosContext, principal);
9371 job->ccache = NULL;
9372 return;
9373 }
9374
9375 krb5_free_principal(KerberosContext, principal);
9376
7ff4fea9
MS
9377 /*
9378 * Copy the user's credentials to the new cache file...
9379 */
9380
f7deaa1a 9381 major_status = gss_krb5_copy_ccache(&minor_status, con->gss_delegated_cred,
c24d2134 9382 job->ccache);
f7deaa1a 9383
9384 if (GSS_ERROR(major_status))
9385 {
9386 cupsdLogGSSMessage(CUPSD_LOG_ERROR, major_status, minor_status,
9387 "Unable to import client credentials cache");
c24d2134 9388 krb5_cc_destroy(KerberosContext, job->ccache);
76cd9e37 9389 job->ccache = NULL;
f7deaa1a 9390 return;
9391 }
9392
7ff4fea9
MS
9393 /*
9394 * Add the KRB5CCNAME environment variable to the job so that the
9395 * backend can use the credentials when printing.
9396 */
9397
9398 cupsdSetStringf(&(job->ccname), "KRB5CCNAME=FILE:%s",
c24d2134 9399 krb5_cc_get_name(KerberosContext, job->ccache));
cc0d019f 9400
75bd9771 9401 cupsdLogJob(job, CUPSD_LOG_DEBUG2, "save_krb5_creds: %s", job->ccname);
7ff4fea9 9402# endif /* HAVE_KRB5_CC_NEW_UNIQUE || HAVE_HEIMDAL */
ef416fc2 9403}
f7deaa1a 9404#endif /* HAVE_GSSAPI && HAVE_KRB5_H */
ef416fc2 9405
9406
9407/*
9408 * 'send_document()' - Send a file to a printer or class.
9409 */
9410
9411static void
9412send_document(cupsd_client_t *con, /* I - Client connection */
9413 ipp_attribute_t *uri) /* I - Printer URI */
9414{
9415 ipp_attribute_t *attr; /* Current attribute */
a0f6818e
MS
9416 ipp_attribute_t *format; /* Request's document-format attribute */
9417 ipp_attribute_t *jformat; /* Job's document-format attribute */
f7deaa1a 9418 const char *default_format;/* document-format-default value */
ef416fc2 9419 int jobid; /* Job ID number */
9420 cupsd_job_t *job; /* Current job */
9421 char job_uri[HTTP_MAX_URI],
9422 /* Job URI */
9423 method[HTTP_MAX_URI],
9424 /* Method portion of URI */
9425 username[HTTP_MAX_URI],
9426 /* Username portion of URI */
9427 host[HTTP_MAX_URI],
9428 /* Host portion of URI */
9429 resource[HTTP_MAX_URI];
9430 /* Resource portion of URI */
9431 int port; /* Port portion of URI */
9432 mime_type_t *filetype; /* Type of file */
9433 char super[MIME_MAX_SUPER],
9434 /* Supertype of file */
9435 type[MIME_MAX_TYPE],
9436 /* Subtype of file */
9437 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
9438 /* Textual name of mime type */
9439 char filename[1024]; /* Job filename */
9440 cupsd_printer_t *printer; /* Current printer */
9441 struct stat fileinfo; /* File information */
9442 int kbytes; /* Size of file */
9443 int compression; /* Type of compression */
75bd9771 9444 int start_job; /* Start the job? */
ef416fc2 9445
9446
9447 cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con,
9448 con->http.fd, uri->values[0].string.text);
9449
9450 /*
9451 * See if we have a job URI or a printer URI...
9452 */
9453
9454 if (!strcmp(uri->name, "printer-uri"))
9455 {
9456 /*
9457 * Got a printer URI; see if we also have a job-id attribute...
9458 */
9459
fa73b229 9460 if ((attr = ippFindAttribute(con->request, "job-id",
9461 IPP_TAG_INTEGER)) == NULL)
ef416fc2 9462 {
9463 send_ipp_status(con, IPP_BAD_REQUEST,
9464 _("Got a printer-uri attribute but no job-id!"));
9465 return;
9466 }
9467
9468 jobid = attr->values[0].integer;
9469 }
9470 else
9471 {
9472 /*
9473 * Got a job URI; parse it to get the job ID...
9474 */
9475
a4d04587 9476 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
9477 sizeof(method), username, sizeof(username), host,
9478 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 9479
9480 if (strncmp(resource, "/jobs/", 6))
9481 {
9482 /*
9483 * Not a valid URI!
9484 */
9485
9486 send_ipp_status(con, IPP_BAD_REQUEST,
9487 _("Bad job-uri attribute \"%s\"!"),
9488 uri->values[0].string.text);
9489 return;
9490 }
9491
9492 jobid = atoi(resource + 6);
9493 }
9494
9495 /*
9496 * See if the job exists...
9497 */
9498
9499 if ((job = cupsdFindJob(jobid)) == NULL)
9500 {
9501 /*
9502 * Nope - return a "not found" error...
9503 */
9504
9505 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
9506 return;
9507 }
9508
f301802f 9509 printer = cupsdFindDest(job->dest);
9510
ef416fc2 9511 /*
9512 * See if the job is owned by the requesting user...
9513 */
9514
9515 if (!validate_user(job, con, job->username, username, sizeof(username)))
9516 {
2fb76298 9517 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
ef416fc2 9518 return;
9519 }
9520
9521 /*
9522 * OK, see if the client is sending the document compressed - CUPS
9523 * only supports "none" and "gzip".
9524 */
9525
9526 compression = CUPS_FILE_NONE;
9527
fa73b229 9528 if ((attr = ippFindAttribute(con->request, "compression",
9529 IPP_TAG_KEYWORD)) != NULL)
ef416fc2 9530 {
9531 if (strcmp(attr->values[0].string.text, "none")
9532#ifdef HAVE_LIBZ
9533 && strcmp(attr->values[0].string.text, "gzip")
9534#endif /* HAVE_LIBZ */
9535 )
9536 {
9537 send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\"!"),
9538 attr->values[0].string.text);
9539 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
9540 "compression", NULL, attr->values[0].string.text);
9541 return;
9542 }
9543
9544#ifdef HAVE_LIBZ
9545 if (!strcmp(attr->values[0].string.text, "gzip"))
9546 compression = CUPS_FILE_GZIP;
9547#endif /* HAVE_LIBZ */
9548 }
9549
9550 /*
9551 * Do we have a file to print?
9552 */
9553
9554 if (!con->filename)
9555 {
9556 send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?!"));
9557 return;
9558 }
9559
9560 /*
9561 * Is it a format we support?
9562 */
9563
9564 if ((format = ippFindAttribute(con->request, "document-format",
9565 IPP_TAG_MIMETYPE)) != NULL)
9566 {
9567 /*
9568 * Grab format from client...
9569 */
9570
bc44d920 9571 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
9572 super, type) != 2)
ef416fc2 9573 {
9574 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"!"),
9575 format->values[0].string.text);
9576 return;
9577 }
9578 }
f7deaa1a 9579 else if ((default_format = cupsGetOption("document-format",
9580 printer->num_options,
9581 printer->options)) != NULL)
9582 {
9583 /*
9584 * Use default document format...
9585 */
9586
9587 if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
9588 {
9589 send_ipp_status(con, IPP_BAD_REQUEST,
9590 _("Could not scan type \"%s\"!"),
9591 default_format);
9592 return;
9593 }
9594 }
ef416fc2 9595 else
9596 {
9597 /*
9598 * No document format attribute? Auto-type it!
9599 */
9600
9601 strcpy(super, "application");
9602 strcpy(type, "octet-stream");
9603 }
9604
fa73b229 9605 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
ef416fc2 9606 {
9607 /*
9608 * Auto-type the file...
9609 */
9610
bd7854cb 9611 ipp_attribute_t *doc_name; /* document-name attribute */
9612
9613
75bd9771 9614 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Auto-typing file...");
ef416fc2 9615
bd7854cb 9616 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
9617 filetype = mimeFileType(MimeDatabase, con->filename,
9618 doc_name ? doc_name->values[0].string.text : NULL,
9619 &compression);
ef416fc2 9620
f7deaa1a 9621 if (!filetype)
f7faf1f5 9622 filetype = mimeType(MimeDatabase, super, type);
a0f6818e 9623
75bd9771
MS
9624 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Request file type is %s/%s.",
9625 filetype->super, filetype->type);
ed486911 9626 }
f7faf1f5 9627 else
9628 filetype = mimeType(MimeDatabase, super, type);
9629
49d87452 9630 if (filetype)
f7deaa1a 9631 {
9632 /*
9633 * Replace the document-format attribute value with the auto-typed or
9634 * default one.
9635 */
9636
9637 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9638 filetype->type);
9639
49d87452
MS
9640 if ((jformat = ippFindAttribute(job->attrs, "document-format",
9641 IPP_TAG_MIMETYPE)) != NULL)
f7deaa1a 9642 {
a0f6818e 9643 _cupsStrFree(jformat->values[0].string.text);
f7deaa1a 9644
a0f6818e 9645 jformat->values[0].string.text = _cupsStrAlloc(mimetype);
f7deaa1a 9646 }
9647 else
a0f6818e 9648 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
f7deaa1a 9649 "document-format", NULL, mimetype);
9650 }
9651 else if (!filetype)
ef416fc2 9652 {
9653 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
9654 _("Unsupported format \'%s/%s\'!"), super, type);
9655 cupsdLogMessage(CUPSD_LOG_INFO,
9656 "Hint: Do you have the raw file printing rules enabled?");
9657
9658 if (format)
9659 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9660 "document-format", NULL, format->values[0].string.text);
9661
9662 return;
9663 }
9664
80ca4592 9665 if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
9666 {
9667 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9668 filetype->type);
9669
9670 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
9671 _("Unsupported format \'%s\'!"), mimetype);
9672
9673 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9674 "document-format", NULL, mimetype);
9675
9676 return;
9677 }
9678
ef416fc2 9679 /*
9680 * Add the file to the job...
9681 */
9682
bd7854cb 9683 cupsdLoadJob(job);
9684
ef416fc2 9685 if (add_file(con, job, filetype, compression))
9686 return;
9687
ef416fc2 9688 if (stat(con->filename, &fileinfo))
9689 kbytes = 0;
9690 else
9691 kbytes = (fileinfo.st_size + 1023) / 1024;
9692
9693 cupsdUpdateQuota(printer, job->username, 0, kbytes);
9694
fa73b229 9695 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
9696 IPP_TAG_INTEGER)) != NULL)
ef416fc2 9697 attr->values[0].integer += kbytes;
9698
9699 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
9700 job->num_files);
9701 rename(con->filename, filename);
9702
9703 cupsdClearString(&con->filename);
9704
75bd9771
MS
9705 cupsdLogJob(job, CUPSD_LOG_INFO, "File of type %s/%s queued by \"%s\".",
9706 filetype->super, filetype->type, job->username);
ef416fc2 9707
9708 /*
9709 * Start the job if this is the last document...
9710 */
9711
fa73b229 9712 if ((attr = ippFindAttribute(con->request, "last-document",
9713 IPP_TAG_BOOLEAN)) != NULL &&
ef416fc2 9714 attr->values[0].boolean)
9715 {
9716 /*
9717 * See if we need to add the ending sheet...
9718 */
9719
91c84a35
MS
9720 if (cupsdTimeoutJob(job))
9721 return;
ef416fc2 9722
bd7854cb 9723 if (job->state_value == IPP_JOB_STOPPED)
9724 {
ef416fc2 9725 job->state->values[0].integer = IPP_JOB_PENDING;
bd7854cb 9726 job->state_value = IPP_JOB_PENDING;
9727 }
9728 else if (job->state_value == IPP_JOB_HELD)
ef416fc2 9729 {
fa73b229 9730 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9731 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 9732 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9733
fa73b229 9734 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
bd7854cb 9735 {
ef416fc2 9736 job->state->values[0].integer = IPP_JOB_PENDING;
bd7854cb 9737 job->state_value = IPP_JOB_PENDING;
9738 }
ef416fc2 9739 }
9740
3dfe78b3
MS
9741 job->dirty = 1;
9742 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 9743
75bd9771 9744 start_job = 1;
ef416fc2 9745 }
9746 else
9747 {
fa73b229 9748 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9749 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 9750 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9751
fa73b229 9752 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
ef416fc2 9753 {
9754 job->state->values[0].integer = IPP_JOB_HELD;
bd7854cb 9755 job->state_value = IPP_JOB_HELD;
ef416fc2 9756 job->hold_until = time(NULL) + 60;
3dfe78b3
MS
9757 job->dirty = 1;
9758
9759 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 9760 }
75bd9771
MS
9761
9762 start_job = 0;
ef416fc2 9763 }
9764
9765 /*
9766 * Fill in the response info...
9767 */
9768
9769 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
9770 LocalPort, jobid);
9771
9772 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
9773 job_uri);
9774
9775 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
9776
9777 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
75bd9771 9778 job->state_value);
ef416fc2 9779 add_job_state_reasons(con, job);
9780
9781 con->response->request.status.status_code = IPP_OK;
75bd9771
MS
9782
9783 /*
9784 * Start the job if necessary...
9785 */
9786
9787 if (start_job)
9788 cupsdCheckJobs();
ef416fc2 9789}
9790
9791
9792/*
9793 * 'send_http_error()' - Send a HTTP error back to the IPP client.
9794 */
9795
9796static void
f899b121 9797send_http_error(
9798 cupsd_client_t *con, /* I - Client connection */
9799 http_status_t status, /* I - HTTP status code */
9800 cupsd_printer_t *printer) /* I - Printer, if any */
ef416fc2 9801{
9f5eb9be
MS
9802 ipp_attribute_t *uri; /* Request URI, if any */
9803
9804
9805 if ((uri = ippFindAttribute(con->request, "printer-uri",
9806 IPP_TAG_URI)) == NULL)
9807 uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI);
9808
9809 cupsdLogMessage(status == HTTP_FORBIDDEN ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
9810 "Returning HTTP %s for %s (%s) from %s",
9811 httpStatus(status),
9812 ippOpString(con->request->request.op.operation_id),
9813 uri ? uri->values[0].string.text : "no URI",
9814 con->http.hostname);
ef416fc2 9815
5f64df29
MS
9816 if (printer)
9817 {
db0bd74a 9818 int auth_type; /* Type of authentication required */
2fb76298
MS
9819
9820
5f64df29
MS
9821 auth_type = CUPSD_AUTH_NONE;
9822
9823 if (status == HTTP_UNAUTHORIZED &&
9824 printer->num_auth_info_required > 0 &&
9825 !strcmp(printer->auth_info_required[0], "negotiate") &&
9826 con->request &&
9827 (con->request->request.op.operation_id == IPP_PRINT_JOB ||
9828 con->request->request.op.operation_id == IPP_CREATE_JOB ||
9829 con->request->request.op.operation_id == CUPS_AUTHENTICATE_JOB))
9830 {
9831 /*
9832 * Creating and authenticating jobs requires Kerberos...
9833 */
9834
9835 auth_type = CUPSD_AUTH_NEGOTIATE;
9836 }
db0bd74a 9837 else
5f64df29
MS
9838 {
9839 /*
9840 * Use policy/location-defined authentication requirements...
9841 */
9842
9843 char resource[HTTP_MAX_URI]; /* Resource portion of URI */
9844 cupsd_location_t *auth; /* Pointer to authentication element */
9845
9846
9847 if (printer->type & CUPS_PRINTER_CLASS)
9848 snprintf(resource, sizeof(resource), "/classes/%s", printer->name);
9849 else
9850 snprintf(resource, sizeof(resource), "/printers/%s", printer->name);
9851
9852 if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
9853 auth->type == CUPSD_AUTH_NONE)
9854 auth = cupsdFindPolicyOp(printer->op_policy_ptr,
9855 con->request ?
9856 con->request->request.op.operation_id :
9857 IPP_PRINT_JOB);
9858
9859 if (auth)
9860 {
9861 if (auth->type == CUPSD_AUTH_DEFAULT)
9862 auth_type = DefaultAuthType;
9863 else
9864 auth_type = auth->type;
9865 }
9866 }
2fb76298 9867
db0bd74a 9868 cupsdSendError(con, status, auth_type);
2fb76298 9869 }
f899b121 9870 else
5bd77a73 9871 cupsdSendError(con, status, CUPSD_AUTH_NONE);
ef416fc2 9872
9873 ippDelete(con->response);
9874 con->response = NULL;
9875
9876 return;
9877}
9878
9879
9880/*
9881 * 'send_ipp_status()' - Send a status back to the IPP client.
9882 */
9883
9884static void
9885send_ipp_status(cupsd_client_t *con, /* I - Client connection */
1f0275e3
MS
9886 ipp_status_t status, /* I - IPP status code */
9887 const char *message,/* I - Status message */
9888 ...) /* I - Additional args as needed */
ef416fc2 9889{
9890 va_list ap; /* Pointer to additional args */
9891 char formatted[1024]; /* Formatted errror message */
9892
9893
3d8365b8 9894 va_start(ap, message);
9895 vsnprintf(formatted, sizeof(formatted),
9896 _cupsLangString(con->language, message), ap);
9897 va_end(ap);
ef416fc2 9898
3d8365b8 9899 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
9900 ippOpString(con->request->request.op.operation_id),
9901 ippErrorString(status), formatted);
ef416fc2 9902
9903 con->response->request.status.status_code = status;
9904
fa73b229 9905 if (ippFindAttribute(con->response, "attributes-charset",
9906 IPP_TAG_ZERO) == NULL)
ef416fc2 9907 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
9908 "attributes-charset", NULL, DefaultCharset);
9909
9910 if (ippFindAttribute(con->response, "attributes-natural-language",
9911 IPP_TAG_ZERO) == NULL)
9912 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
9913 "attributes-natural-language", NULL, DefaultLanguage);
9914
3d8365b8 9915 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
9916 "status-message", NULL, formatted);
ef416fc2 9917}
9918
9919
9920/*
9921 * 'set_default()' - Set the default destination...
9922 */
9923
9924static void
9925set_default(cupsd_client_t *con, /* I - Client connection */
9926 ipp_attribute_t *uri) /* I - Printer URI */
9927{
9928 http_status_t status; /* Policy status */
bc44d920 9929 cups_ptype_t dtype; /* Destination type (printer/class) */
dd1abb6b
MS
9930 cupsd_printer_t *printer, /* Printer */
9931 *oldprinter; /* Old default printer */
ef416fc2 9932
9933
9934 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con,
9935 con->http.fd, uri->values[0].string.text);
9936
9937 /*
9938 * Is the destination valid?
9939 */
9940
f7deaa1a 9941 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 9942 {
9943 /*
9944 * Bad URI...
9945 */
9946
9947 send_ipp_status(con, IPP_NOT_FOUND,
9948 _("The printer or class was not found."));
9949 return;
9950 }
9951
9952 /*
9953 * Check policy...
9954 */
9955
9956 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
9957 {
f899b121 9958 send_http_error(con, status, NULL);
ef416fc2 9959 return;
9960 }
9961
9962 /*
9963 * Set it as the default...
9964 */
9965
dd1abb6b 9966 oldprinter = DefaultPrinter;
ef416fc2 9967 DefaultPrinter = printer;
9968
dd1abb6b
MS
9969 if (oldprinter)
9970 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, oldprinter, NULL,
9971 "%s is no longer the default printer.", oldprinter->name);
9972
9973 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
9974 "%s is now the default printer.", printer->name);
9975
3dfe78b3
MS
9976 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS | CUPSD_DIRTY_CLASSES |
9977 CUPSD_DIRTY_REMOTE | CUPSD_DIRTY_PRINTCAP);
ef416fc2 9978
9979 cupsdLogMessage(CUPSD_LOG_INFO,
f7deaa1a 9980 "Default destination set to \"%s\" by \"%s\".",
9981 printer->name, get_username(con));
ef416fc2 9982
9983 /*
9984 * Everything was ok, so return OK status...
9985 */
9986
9987 con->response->request.status.status_code = IPP_OK;
9988}
9989
9990
9991/*
9992 * 'set_job_attrs()' - Set job attributes.
9993 */
9994
9995static void
9996set_job_attrs(cupsd_client_t *con, /* I - Client connection */
9997 ipp_attribute_t *uri) /* I - Job URI */
9998{
9999 ipp_attribute_t *attr, /* Current attribute */
10000 *attr2; /* Job attribute */
10001 int jobid; /* Job ID */
10002 cupsd_job_t *job; /* Current job */
10003 char method[HTTP_MAX_URI],
10004 /* Method portion of URI */
10005 username[HTTP_MAX_URI],
10006 /* Username portion of URI */
10007 host[HTTP_MAX_URI],
10008 /* Host portion of URI */
10009 resource[HTTP_MAX_URI];
10010 /* Resource portion of URI */
10011 int port; /* Port portion of URI */
d09495fa 10012 int event; /* Events? */
005dd1eb 10013 int check_jobs; /* Check jobs? */
ef416fc2 10014
10015
10016 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
10017 con->http.fd, uri->values[0].string.text);
10018
10019 /*
10020 * Start with "everything is OK" status...
10021 */
10022
10023 con->response->request.status.status_code = IPP_OK;
10024
10025 /*
10026 * See if we have a job URI or a printer URI...
10027 */
10028
fa73b229 10029 if (!strcmp(uri->name, "printer-uri"))
ef416fc2 10030 {
10031 /*
10032 * Got a printer URI; see if we also have a job-id attribute...
10033 */
10034
fa73b229 10035 if ((attr = ippFindAttribute(con->request, "job-id",
10036 IPP_TAG_INTEGER)) == NULL)
ef416fc2 10037 {
10038 send_ipp_status(con, IPP_BAD_REQUEST,
10039 _("Got a printer-uri attribute but no job-id!"));
10040 return;
10041 }
10042
10043 jobid = attr->values[0].integer;
10044 }
10045 else
10046 {
10047 /*
10048 * Got a job URI; parse it to get the job ID...
10049 */
10050
a4d04587 10051 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
10052 sizeof(method), username, sizeof(username), host,
10053 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 10054
fa73b229 10055 if (strncmp(resource, "/jobs/", 6))
ef416fc2 10056 {
10057 /*
10058 * Not a valid URI!
10059 */
10060
10061 send_ipp_status(con, IPP_BAD_REQUEST,
10062 _("Bad job-uri attribute \"%s\"!"),
10063 uri->values[0].string.text);
10064 return;
10065 }
10066
10067 jobid = atoi(resource + 6);
10068 }
10069
10070 /*
10071 * See if the job exists...
10072 */
10073
10074 if ((job = cupsdFindJob(jobid)) == NULL)
10075 {
10076 /*
10077 * Nope - return a "not found" error...
10078 */
10079
10080 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
10081 return;
10082 }
10083
10084 /*
10085 * See if the job has been completed...
10086 */
10087
bd7854cb 10088 if (job->state_value > IPP_JOB_STOPPED)
ef416fc2 10089 {
10090 /*
10091 * Return a "not-possible" error...
10092 */
10093
10094 send_ipp_status(con, IPP_NOT_POSSIBLE,
10095 _("Job #%d is finished and cannot be altered!"), jobid);
10096 return;
10097 }
10098
10099 /*
10100 * See if the job is owned by the requesting user...
10101 */
10102
10103 if (!validate_user(job, con, job->username, username, sizeof(username)))
10104 {
2fb76298 10105 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
ef416fc2 10106 return;
10107 }
10108
10109 /*
10110 * See what the user wants to change.
10111 */
10112
bd7854cb 10113 cupsdLoadJob(job);
10114
005dd1eb
MS
10115 check_jobs = 0;
10116 event = 0;
d09495fa 10117
fa73b229 10118 for (attr = con->request->attrs; attr; attr = attr->next)
ef416fc2 10119 {
10120 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
10121 continue;
10122
10123 if (!strcmp(attr->name, "attributes-charset") ||
10124 !strcmp(attr->name, "attributes-natural-language") ||
10125 !strcmp(attr->name, "document-compression") ||
10126 !strcmp(attr->name, "document-format") ||
10127 !strcmp(attr->name, "job-detailed-status-messages") ||
10128 !strcmp(attr->name, "job-document-access-errors") ||
10129 !strcmp(attr->name, "job-id") ||
5d6412a9 10130 !strcmp(attr->name, "job-impressions-completed") ||
ef416fc2 10131 !strcmp(attr->name, "job-k-octets") ||
10132 !strcmp(attr->name, "job-originating-host-name") ||
10133 !strcmp(attr->name, "job-originating-user-name") ||
10134 !strcmp(attr->name, "job-printer-up-time") ||
10135 !strcmp(attr->name, "job-printer-uri") ||
10136 !strcmp(attr->name, "job-sheets") ||
10137 !strcmp(attr->name, "job-state-message") ||
10138 !strcmp(attr->name, "job-state-reasons") ||
10139 !strcmp(attr->name, "job-uri") ||
10140 !strcmp(attr->name, "number-of-documents") ||
10141 !strcmp(attr->name, "number-of-intervening-jobs") ||
10142 !strcmp(attr->name, "output-device-assigned") ||
10143 !strncmp(attr->name, "date-time-at-", 13) ||
ef416fc2 10144 !strncmp(attr->name, "job-k-octets", 12) ||
10145 !strncmp(attr->name, "job-media-sheets", 16) ||
10146 !strncmp(attr->name, "time-at-", 8))
10147 {
10148 /*
10149 * Read-only attrs!
10150 */
10151
10152 send_ipp_status(con, IPP_ATTRIBUTES_NOT_SETTABLE,
10153 _("%s cannot be changed."), attr->name);
10154
10155 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
10156 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
10157
10158 continue;
10159 }
10160
10161 if (!strcmp(attr->name, "job-priority"))
10162 {
10163 /*
10164 * Change the job priority...
10165 */
10166
10167 if (attr->value_tag != IPP_TAG_INTEGER)
10168 {
10169 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value!"));
10170
10171 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
10172 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
10173 }
bd7854cb 10174 else if (job->state_value >= IPP_JOB_PROCESSING)
ef416fc2 10175 {
10176 send_ipp_status(con, IPP_NOT_POSSIBLE,
10177 _("Job is completed and cannot be changed."));
10178 return;
10179 }
10180 else if (con->response->request.status.status_code == IPP_OK)
d09495fa 10181 {
005dd1eb
MS
10182 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-priority to %d",
10183 attr->values[0].integer);
ef416fc2 10184 cupsdSetJobPriority(job, attr->values[0].integer);
005dd1eb
MS
10185
10186 check_jobs = 1;
10187 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED |
10188 CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED;
d09495fa 10189 }
ef416fc2 10190 }
10191 else if (!strcmp(attr->name, "job-state"))
10192 {
10193 /*
10194 * Change the job state...
10195 */
10196
10197 if (attr->value_tag != IPP_TAG_ENUM)
10198 {
10199 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value!"));
10200
10201 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
10202 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
10203 }
10204 else
10205 {
10206 switch (attr->values[0].integer)
10207 {
10208 case IPP_JOB_PENDING :
10209 case IPP_JOB_HELD :
bd7854cb 10210 if (job->state_value > IPP_JOB_HELD)
ef416fc2 10211 {
10212 send_ipp_status(con, IPP_NOT_POSSIBLE,
10213 _("Job state cannot be changed."));
10214 return;
10215 }
10216 else if (con->response->request.status.status_code == IPP_OK)
bd7854cb 10217 {
005dd1eb
MS
10218 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
10219 attr->values[0].integer);
10220
ef416fc2 10221 job->state->values[0].integer = attr->values[0].integer;
bc44d920 10222 job->state_value = (ipp_jstate_t)attr->values[0].integer;
d09495fa 10223
10224 event |= CUPSD_EVENT_JOB_STATE;
005dd1eb 10225 check_jobs = 1;
bd7854cb 10226 }
ef416fc2 10227 break;
10228
10229 case IPP_JOB_PROCESSING :
10230 case IPP_JOB_STOPPED :
bd7854cb 10231 if (job->state_value != attr->values[0].integer)
ef416fc2 10232 {
10233 send_ipp_status(con, IPP_NOT_POSSIBLE,
10234 _("Job state cannot be changed."));
10235 return;
10236 }
10237 break;
10238
d09495fa 10239 case IPP_JOB_CANCELED :
ef416fc2 10240 case IPP_JOB_ABORTED :
10241 case IPP_JOB_COMPLETED :
bd7854cb 10242 if (job->state_value > IPP_JOB_PROCESSING)
ef416fc2 10243 {
10244 send_ipp_status(con, IPP_NOT_POSSIBLE,
10245 _("Job state cannot be changed."));
10246 return;
10247 }
10248 else if (con->response->request.status.status_code == IPP_OK)
005dd1eb
MS
10249 {
10250 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
10251 attr->values[0].integer);
07725fee 10252 cupsdCancelJob(job, 0, (ipp_jstate_t)attr->values[0].integer);
005dd1eb
MS
10253
10254 check_jobs = 1;
10255 }
ef416fc2 10256 break;
10257 }
10258 }
10259 }
10260 else if (con->response->request.status.status_code != IPP_OK)
10261 continue;
fa73b229 10262 else if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10263 IPP_TAG_ZERO)) != NULL)
ef416fc2 10264 {
10265 /*
10266 * Some other value; first free the old value...
10267 */
10268
10269 if (job->attrs->prev)
10270 job->attrs->prev->next = attr2->next;
10271 else
10272 job->attrs->attrs = attr2->next;
10273
10274 if (job->attrs->last == attr2)
10275 job->attrs->last = job->attrs->prev;
10276
757d2cad 10277 _ippFreeAttr(attr2);
ef416fc2 10278
10279 /*
10280 * Then copy the attribute...
10281 */
10282
10283 copy_attribute(job->attrs, attr, 0);
10284
10285 /*
10286 * See if the job-name or job-hold-until is being changed.
10287 */
10288
10289 if (!strcmp(attr->name, "job-hold-until"))
10290 {
005dd1eb
MS
10291 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-hold-until to %s",
10292 attr->values[0].string.text);
ef416fc2 10293 cupsdSetJobHoldUntil(job, attr->values[0].string.text);
10294
10295 if (!strcmp(attr->values[0].string.text, "no-hold"))
10296 cupsdReleaseJob(job);
10297 else
10298 cupsdHoldJob(job);
d09495fa 10299
005dd1eb
MS
10300 check_jobs = 1;
10301 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
ef416fc2 10302 }
10303 }
10304 else if (attr->value_tag == IPP_TAG_DELETEATTR)
10305 {
10306 /*
10307 * Delete the attribute...
10308 */
10309
10310 if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10311 IPP_TAG_ZERO)) != NULL)
10312 {
10313 if (job->attrs->prev)
10314 job->attrs->prev->next = attr2->next;
10315 else
10316 job->attrs->attrs = attr2->next;
10317
10318 if (attr2 == job->attrs->last)
10319 job->attrs->last = job->attrs->prev;
10320
757d2cad 10321 _ippFreeAttr(attr2);
d09495fa 10322
10323 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
ef416fc2 10324 }
10325 }
10326 else
10327 {
10328 /*
10329 * Add new option by copying it...
10330 */
10331
10332 copy_attribute(job->attrs, attr, 0);
d09495fa 10333
10334 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
ef416fc2 10335 }
10336 }
10337
10338 /*
10339 * Save the job...
10340 */
10341
3dfe78b3
MS
10342 job->dirty = 1;
10343 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 10344
d09495fa 10345 /*
10346 * Send events as needed...
10347 */
10348
d9bca400 10349 if (event & CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED)
01ce6322
MS
10350 cupsdAddEvent(CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED,
10351 cupsdFindDest(job->dest), job,
d9bca400
MS
10352 "Job priority changed by user.");
10353
d09495fa 10354 if (event & CUPSD_EVENT_JOB_STATE)
01ce6322 10355 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
d09495fa 10356 job->state_value == IPP_JOB_HELD ?
10357 "Job held by user." : "Job restarted by user.");
10358
10359 if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
01ce6322 10360 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
d09495fa 10361 "Job options changed by user.");
10362
ef416fc2 10363 /*
10364 * Start jobs if possible...
10365 */
10366
005dd1eb
MS
10367 if (check_jobs)
10368 cupsdCheckJobs();
ef416fc2 10369}
10370
10371
b423cd4c 10372/*
10373 * 'set_printer_defaults()' - Set printer default options from a request.
10374 */
10375
10376static void
10377set_printer_defaults(
10378 cupsd_client_t *con, /* I - Client connection */
10379 cupsd_printer_t *printer) /* I - Printer */
10380{
10381 int i; /* Looping var */
10382 ipp_attribute_t *attr; /* Current attribute */
10383 int namelen; /* Length of attribute name */
10384 char name[256], /* New attribute name */
10385 value[256]; /* String version of integer attrs */
10386
10387
10388 for (attr = con->request->attrs; attr; attr = attr->next)
10389 {
10390 /*
10391 * Skip non-printer attributes...
10392 */
10393
10394 if (attr->group_tag != IPP_TAG_PRINTER || !attr->name)
10395 continue;
10396
10397 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_defaults: %s", attr->name);
10398
10399 if (!strcmp(attr->name, "job-sheets-default"))
10400 {
10401 /*
10402 * Only allow keywords and names...
10403 */
10404
10405 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10406 continue;
10407
10408 /*
10409 * Only allow job-sheets-default to be set when running without a
10410 * system high classification level...
10411 */
10412
10413 if (Classification)
10414 continue;
10415
10416 cupsdSetString(&printer->job_sheets[0], attr->values[0].string.text);
10417
10418 if (attr->num_values > 1)
10419 cupsdSetString(&printer->job_sheets[1], attr->values[1].string.text);
10420 else
10421 cupsdSetString(&printer->job_sheets[1], "none");
10422 }
10423 else if (!strcmp(attr->name, "requesting-user-name-allowed"))
10424 {
10425 cupsdFreePrinterUsers(printer);
10426
10427 printer->deny_users = 0;
10428
10429 if (attr->value_tag == IPP_TAG_NAME &&
10430 (attr->num_values > 1 ||
10431 strcmp(attr->values[0].string.text, "all")))
10432 {
10433 for (i = 0; i < attr->num_values; i ++)
10434 cupsdAddPrinterUser(printer, attr->values[i].string.text);
10435 }
10436 }
10437 else if (!strcmp(attr->name, "requesting-user-name-denied"))
10438 {
10439 cupsdFreePrinterUsers(printer);
10440
10441 printer->deny_users = 1;
10442
10443 if (attr->value_tag == IPP_TAG_NAME &&
10444 (attr->num_values > 1 ||
10445 strcmp(attr->values[0].string.text, "none")))
10446 {
10447 for (i = 0; i < attr->num_values; i ++)
10448 cupsdAddPrinterUser(printer, attr->values[i].string.text);
10449 }
10450 }
10451 else if (!strcmp(attr->name, "job-quota-period"))
10452 {
10453 if (attr->value_tag != IPP_TAG_INTEGER)
10454 continue;
10455
10456 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-quota-period to %d...",
10457 attr->values[0].integer);
10458 cupsdFreeQuotas(printer);
10459
10460 printer->quota_period = attr->values[0].integer;
10461 }
10462 else if (!strcmp(attr->name, "job-k-limit"))
10463 {
10464 if (attr->value_tag != IPP_TAG_INTEGER)
10465 continue;
10466
10467 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-k-limit to %d...",
10468 attr->values[0].integer);
10469 cupsdFreeQuotas(printer);
10470
10471 printer->k_limit = attr->values[0].integer;
10472 }
10473 else if (!strcmp(attr->name, "job-page-limit"))
10474 {
10475 if (attr->value_tag != IPP_TAG_INTEGER)
10476 continue;
10477
10478 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-page-limit to %d...",
10479 attr->values[0].integer);
10480 cupsdFreeQuotas(printer);
10481
10482 printer->page_limit = attr->values[0].integer;
10483 }
10484 else if (!strcmp(attr->name, "printer-op-policy"))
10485 {
10486 cupsd_policy_t *p; /* Policy */
10487
10488
10489 if (attr->value_tag != IPP_TAG_NAME)
10490 continue;
10491
10492 if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
10493 {
10494 cupsdLogMessage(CUPSD_LOG_DEBUG,
10495 "Setting printer-op-policy to \"%s\"...",
10496 attr->values[0].string.text);
10497 cupsdSetString(&printer->op_policy, attr->values[0].string.text);
10498 printer->op_policy_ptr = p;
10499 }
10500 else
10501 {
10502 send_ipp_status(con, IPP_NOT_POSSIBLE,
10503 _("Unknown printer-op-policy \"%s\"."),
10504 attr->values[0].string.text);
10505 return;
10506 }
10507 }
10508 else if (!strcmp(attr->name, "printer-error-policy"))
10509 {
10510 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10511 continue;
10512
10513 if (strcmp(attr->values[0].string.text, "abort-job") &&
ee571f26 10514 strcmp(attr->values[0].string.text, "retry-current-job") &&
b423cd4c 10515 strcmp(attr->values[0].string.text, "retry-job") &&
10516 strcmp(attr->values[0].string.text, "stop-printer"))
10517 {
10518 send_ipp_status(con, IPP_NOT_POSSIBLE,
10519 _("Unknown printer-error-policy \"%s\"."),
10520 attr->values[0].string.text);
10521 return;
10522 }
10523
10524 cupsdLogMessage(CUPSD_LOG_DEBUG,
10525 "Setting printer-error-policy to \"%s\"...",
10526 attr->values[0].string.text);
10527 cupsdSetString(&printer->error_policy, attr->values[0].string.text);
10528 }
b423cd4c 10529
10530 /*
10531 * Skip any other non-default attributes...
10532 */
10533
10534 namelen = strlen(attr->name);
10535 if (namelen < 9 || strcmp(attr->name + namelen - 8, "-default") ||
10536 namelen > (sizeof(name) - 1) || attr->num_values != 1)
10537 continue;
10538
10539 /*
10540 * OK, anything else must be a user-defined default...
10541 */
10542
10543 strlcpy(name, attr->name, sizeof(name));
10544 name[namelen - 8] = '\0'; /* Strip "-default" */
10545
10546 switch (attr->value_tag)
10547 {
10548 case IPP_TAG_DELETEATTR :
10549 printer->num_options = cupsRemoveOption(name,
10550 printer->num_options,
10551 &(printer->options));
10552 cupsdLogMessage(CUPSD_LOG_DEBUG,
10553 "Deleting %s", attr->name);
10554 break;
10555
10556 case IPP_TAG_NAME :
10557 case IPP_TAG_KEYWORD :
10558 case IPP_TAG_URI :
10559 printer->num_options = cupsAddOption(name,
10560 attr->values[0].string.text,
10561 printer->num_options,
10562 &(printer->options));
10563 cupsdLogMessage(CUPSD_LOG_DEBUG,
10564 "Setting %s to \"%s\"...", attr->name,
10565 attr->values[0].string.text);
10566 break;
10567
10568 case IPP_TAG_BOOLEAN :
10569 printer->num_options = cupsAddOption(name,
10570 attr->values[0].boolean ?
10571 "true" : "false",
10572 printer->num_options,
10573 &(printer->options));
10574 cupsdLogMessage(CUPSD_LOG_DEBUG,
10575 "Setting %s to %s...", attr->name,
10576 attr->values[0].boolean ? "true" : "false");
10577 break;
10578
10579 case IPP_TAG_INTEGER :
10580 case IPP_TAG_ENUM :
10581 sprintf(value, "%d", attr->values[0].integer);
10582 printer->num_options = cupsAddOption(name, value,
10583 printer->num_options,
10584 &(printer->options));
10585 cupsdLogMessage(CUPSD_LOG_DEBUG,
10586 "Setting %s to %s...", attr->name, value);
10587 break;
10588
10589 case IPP_TAG_RANGE :
10590 sprintf(value, "%d-%d", attr->values[0].range.lower,
10591 attr->values[0].range.upper);
10592 printer->num_options = cupsAddOption(name, value,
10593 printer->num_options,
10594 &(printer->options));
10595 cupsdLogMessage(CUPSD_LOG_DEBUG,
10596 "Setting %s to %s...", attr->name, value);
10597 break;
10598
10599 case IPP_TAG_RESOLUTION :
10600 sprintf(value, "%dx%d%s", attr->values[0].resolution.xres,
10601 attr->values[0].resolution.yres,
10602 attr->values[0].resolution.units == IPP_RES_PER_INCH ?
10603 "dpi" : "dpc");
10604 printer->num_options = cupsAddOption(name, value,
10605 printer->num_options,
10606 &(printer->options));
10607 cupsdLogMessage(CUPSD_LOG_DEBUG,
10608 "Setting %s to %s...", attr->name, value);
10609 break;
10610
10611 default :
10612 /* Do nothing for other values */
10613 break;
10614 }
10615 }
10616}
10617
10618
ef416fc2 10619/*
10620 * 'start_printer()' - Start a printer.
10621 */
10622
10623static void
10624start_printer(cupsd_client_t *con, /* I - Client connection */
10625 ipp_attribute_t *uri) /* I - Printer URI */
10626{
10627 http_status_t status; /* Policy status */
bc44d920 10628 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 10629 cupsd_printer_t *printer; /* Printer data */
10630
10631
10632 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", con,
10633 con->http.fd, uri->values[0].string.text);
10634
10635 /*
10636 * Is the destination valid?
10637 */
10638
f7deaa1a 10639 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 10640 {
10641 /*
10642 * Bad URI...
10643 */
10644
10645 send_ipp_status(con, IPP_NOT_FOUND,
10646 _("The printer or class was not found."));
10647 return;
10648 }
10649
10650 /*
10651 * Check policy...
10652 */
10653
10654 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10655 {
f899b121 10656 send_http_error(con, status, printer);
ef416fc2 10657 return;
10658 }
10659
10660 /*
10661 * Start the printer...
10662 */
10663
10664 printer->state_message[0] = '\0';
10665
10666 cupsdStartPrinter(printer, 1);
10667
10668 if (dtype & CUPS_PRINTER_CLASS)
f7deaa1a 10669 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".",
10670 printer->name, get_username(con));
e00b005a 10671 else
f7deaa1a 10672 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".",
10673 printer->name, get_username(con));
ef416fc2 10674
10675 cupsdCheckJobs();
10676
10677 /*
10678 * Everything was ok, so return OK status...
10679 */
10680
10681 con->response->request.status.status_code = IPP_OK;
10682}
10683
10684
10685/*
10686 * 'stop_printer()' - Stop a printer.
10687 */
10688
10689static void
10690stop_printer(cupsd_client_t *con, /* I - Client connection */
10691 ipp_attribute_t *uri) /* I - Printer URI */
10692{
10693 http_status_t status; /* Policy status */
bc44d920 10694 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 10695 cupsd_printer_t *printer; /* Printer data */
10696 ipp_attribute_t *attr; /* printer-state-message attribute */
10697
10698
10699 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", con,
10700 con->http.fd, uri->values[0].string.text);
10701
10702 /*
10703 * Is the destination valid?
10704 */
10705
f7deaa1a 10706 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 10707 {
10708 /*
10709 * Bad URI...
10710 */
10711
10712 send_ipp_status(con, IPP_NOT_FOUND,
10713 _("The printer or class was not found."));
10714 return;
10715 }
10716
10717 /*
10718 * Check policy...
10719 */
10720
10721 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10722 {
f899b121 10723 send_http_error(con, status, printer);
ef416fc2 10724 return;
10725 }
10726
10727 /*
10728 * Stop the printer...
10729 */
10730
10731 if ((attr = ippFindAttribute(con->request, "printer-state-message",
10732 IPP_TAG_TEXT)) == NULL)
10733 strcpy(printer->state_message, "Paused");
10734 else
10735 {
10736 strlcpy(printer->state_message, attr->values[0].string.text,
10737 sizeof(printer->state_message));
10738 }
10739
10740 cupsdStopPrinter(printer, 1);
10741
10742 if (dtype & CUPS_PRINTER_CLASS)
f7deaa1a 10743 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".",
10744 printer->name, get_username(con));
ef416fc2 10745 else
f7deaa1a 10746 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".",
10747 printer->name, get_username(con));
ef416fc2 10748
10749 /*
10750 * Everything was ok, so return OK status...
10751 */
10752
10753 con->response->request.status.status_code = IPP_OK;
10754}
10755
10756
89d46774 10757/*
10758 * 'url_encode_attr()' - URL-encode a string attribute.
10759 */
10760
10761static void
10762url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */
10763 char *buffer,/* I - String buffer */
10764 int bufsize)/* I - Size of buffer */
10765{
10766 int i; /* Looping var */
10767 char *bufptr, /* Pointer into buffer */
b94498cf 10768 *bufend; /* End of buffer */
89d46774 10769
10770
10771 strlcpy(buffer, attr->name, bufsize);
10772 bufptr = buffer + strlen(buffer);
10773 bufend = buffer + bufsize - 1;
10774
10775 for (i = 0; i < attr->num_values; i ++)
10776 {
10777 if (bufptr >= bufend)
10778 break;
10779
10780 if (i)
10781 *bufptr++ = ',';
10782 else
10783 *bufptr++ = '=';
10784
10785 if (bufptr >= bufend)
10786 break;
10787
10788 *bufptr++ = '\'';
10789
b94498cf 10790 bufptr = url_encode_string(attr->values[i].string.text,
10791 bufptr, bufend - bufptr + 1);
89d46774 10792
10793 if (bufptr >= bufend)
10794 break;
10795
10796 *bufptr++ = '\'';
10797 }
10798
10799 *bufptr = '\0';
10800}
10801
10802
b94498cf 10803/*
10804 * 'url_encode_string()' - URL-encode a string.
10805 */
10806
10807static char * /* O - End of string */
10808url_encode_string(const char *s, /* I - String */
10809 char *buffer, /* I - String buffer */
10810 int bufsize) /* I - Size of buffer */
10811{
10812 char *bufptr, /* Pointer into buffer */
10813 *bufend; /* End of buffer */
10814 static const char *hex = "0123456789ABCDEF";
10815 /* Hex digits */
10816
10817
10818 bufptr = buffer;
10819 bufend = buffer + bufsize - 1;
10820
10821 while (*s && bufptr < bufend)
10822 {
10823 if (*s == ' ' || *s == '%')
10824 {
10825 if (bufptr >= (bufend - 2))
10826 break;
10827
10828 *bufptr++ = '%';
10829 *bufptr++ = hex[(*s >> 4) & 15];
10830 *bufptr++ = hex[*s & 15];
10831
10832 s ++;
10833 }
10834 else if (*s == '\'' || *s == '\\')
10835 {
10836 if (bufptr >= (bufend - 1))
10837 break;
10838
10839 *bufptr++ = '\\';
10840 *bufptr++ = *s++;
10841 }
10842 else
10843 *bufptr++ = *s++;
10844 }
10845
10846 *bufptr = '\0';
10847
10848 return (bufptr);
10849}
10850
10851
ef416fc2 10852/*
10853 * 'user_allowed()' - See if a user is allowed to print to a queue.
10854 */
10855
10856static int /* O - 0 if not allowed, 1 if allowed */
10857user_allowed(cupsd_printer_t *p, /* I - Printer or class */
10858 const char *username) /* I - Username */
10859{
10860 int i; /* Looping var */
10861 struct passwd *pw; /* User password data */
5bd77a73
MS
10862 char baseuser[256], /* Base username */
10863 *baseptr; /* Pointer to "@" in base username */
ef416fc2 10864
10865
10866 if (p->num_users == 0)
10867 return (1);
10868
10869 if (!strcmp(username, "root"))
10870 return (1);
10871
5bd77a73
MS
10872 if (strchr(username, '@'))
10873 {
10874 /*
10875 * Strip @REALM for username check...
10876 */
10877
10878 strlcpy(baseuser, username, sizeof(baseuser));
10879
10880 if ((baseptr = strchr(baseuser, '@')) != NULL)
10881 *baseptr = '\0';
10882
10883 username = baseuser;
10884 }
10885
ef416fc2 10886 pw = getpwnam(username);
10887 endpwent();
10888
10889 for (i = 0; i < p->num_users; i ++)
10890 {
8922323b 10891 if (p->users[i][0] == '@')
ef416fc2 10892 {
10893 /*
10894 * Check group membership...
10895 */
10896
10897 if (cupsdCheckGroup(username, pw, p->users[i] + 1))
10898 break;
10899 }
8922323b
MS
10900 else if (p->users[i][0] == '#')
10901 {
10902 /*
10903 * Check UUID...
10904 */
10905
10906 if (cupsdCheckGroup(username, pw, p->users[i]))
10907 break;
10908 }
ef416fc2 10909 else if (!strcasecmp(username, p->users[i]))
10910 break;
10911 }
10912
10913 return ((i < p->num_users) != p->deny_users);
10914}
10915
10916
10917/*
10918 * 'validate_job()' - Validate printer options and destination.
10919 */
10920
10921static void
10922validate_job(cupsd_client_t *con, /* I - Client connection */
10923 ipp_attribute_t *uri) /* I - Printer URI */
10924{
10925 http_status_t status; /* Policy status */
10926 ipp_attribute_t *attr; /* Current attribute */
10927 ipp_attribute_t *format; /* Document-format attribute */
bc44d920 10928 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 10929 char super[MIME_MAX_SUPER],
10930 /* Supertype of file */
10931 type[MIME_MAX_TYPE];
10932 /* Subtype of file */
10933 cupsd_printer_t *printer; /* Printer */
10934
10935
10936 cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", con,
10937 con->http.fd, uri->values[0].string.text);
10938
10939 /*
10940 * OK, see if the client is sending the document compressed - CUPS
10941 * doesn't support compression yet...
10942 */
10943
fa73b229 10944 if ((attr = ippFindAttribute(con->request, "compression",
10945 IPP_TAG_KEYWORD)) != NULL &&
10946 !strcmp(attr->values[0].string.text, "none"))
ef416fc2 10947 {
10948 send_ipp_status(con, IPP_ATTRIBUTES,
10949 _("Unsupported compression attribute %s!"),
10950 attr->values[0].string.text);
10951 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
10952 "compression", NULL, attr->values[0].string.text);
10953 return;
10954 }
10955
10956 /*
10957 * Is it a format we support?
10958 */
10959
10960 if ((format = ippFindAttribute(con->request, "document-format",
10961 IPP_TAG_MIMETYPE)) != NULL)
10962 {
bc44d920 10963 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
10964 super, type) != 2)
ef416fc2 10965 {
10966 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"!"),
10967 format->values[0].string.text);
10968 return;
10969 }
10970
fa73b229 10971 if ((strcmp(super, "application") || strcmp(type, "octet-stream")) &&
10972 !mimeType(MimeDatabase, super, type))
ef416fc2 10973 {
10974 cupsdLogMessage(CUPSD_LOG_INFO,
10975 "Hint: Do you have the raw file printing rules enabled?");
10976 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
10977 _("Unsupported format \"%s\"!"),
10978 format->values[0].string.text);
10979 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
10980 "document-format", NULL, format->values[0].string.text);
10981 return;
10982 }
10983 }
10984
10985 /*
10986 * Is the destination valid?
10987 */
10988
f7deaa1a 10989 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 10990 {
10991 /*
10992 * Bad URI...
10993 */
10994
10995 send_ipp_status(con, IPP_NOT_FOUND,
10996 _("The printer or class was not found."));
10997 return;
10998 }
10999
11000 /*
11001 * Check policy...
11002 */
11003
11004 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11005 {
f899b121 11006 send_http_error(con, status, printer);
ef416fc2 11007 return;
11008 }
11009
11010 /*
11011 * Everything was ok, so return OK status...
11012 */
11013
11014 con->response->request.status.status_code = IPP_OK;
11015}
11016
11017
11018/*
11019 * 'validate_name()' - Make sure the printer name only contains valid chars.
11020 */
11021
bc44d920 11022static int /* O - 0 if name is no good, 1 if good */
ef416fc2 11023validate_name(const char *name) /* I - Name to check */
11024{
11025 const char *ptr; /* Pointer into name */
11026
11027
11028 /*
11029 * Scan the whole name...
11030 */
11031
11032 for (ptr = name; *ptr; ptr ++)
f7deaa1a 11033 if ((*ptr > 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
ef416fc2 11034 return (0);
11035
11036 /*
11037 * All the characters are good; validate the length, too...
11038 */
11039
11040 return ((ptr - name) < 128);
11041}
11042
11043
11044/*
11045 * 'validate_user()' - Validate the user for the request.
11046 */
11047
11048static int /* O - 1 if permitted, 0 otherwise */
11049validate_user(cupsd_job_t *job, /* I - Job */
11050 cupsd_client_t *con, /* I - Client connection */
11051 const char *owner, /* I - Owner of job/resource */
11052 char *username, /* O - Authenticated username */
11053 int userlen) /* I - Length of username */
11054{
ef416fc2 11055 cupsd_printer_t *printer; /* Printer for job */
11056
11057
11058 cupsdLogMessage(CUPSD_LOG_DEBUG2,
11059 "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, "
11060 "userlen=%d)",
91c84a35
MS
11061 job->id, con ? con->http.fd : 0,
11062 owner ? owner : "(null)", username, userlen);
ef416fc2 11063
11064 /*
11065 * Validate input...
11066 */
11067
11068 if (!con || !owner || !username || userlen <= 0)
11069 return (0);
11070
11071 /*
11072 * Get the best authenticated username that is available.
11073 */
11074
e00b005a 11075 strlcpy(username, get_username(con), userlen);
ef416fc2 11076
11077 /*
11078 * Check the username against the owner...
11079 */
11080
80ca4592 11081 printer = cupsdFindDest(job->dest);
ef416fc2 11082
11083 return (cupsdCheckPolicy(printer ? printer->op_policy_ptr : DefaultPolicyPtr,
11084 con, owner) == HTTP_OK);
11085}
11086
11087
11088/*
b19ccc9e 11089 * End of "$Id: ipp.c 7944 2008-09-16 22:32:42Z mike $".
ef416fc2 11090 */