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