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