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