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