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