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