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