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