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