]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/ipp.c
c84e34f7e35aaad878bc0ed13ba1c640eec2d41f
[thirdparty/cups.git] / scheduler / ipp.c
1 /*
2 * "$Id: ipp.c 12778 2015-07-07 17:28:51Z 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 cupsdCheckJobs();
3289 }
3290
3291
3292 /*
3293 * 'cancel_job()' - Cancel a print job.
3294 */
3295
3296 static void
3297 cancel_job(cupsd_client_t *con, /* I - Client connection */
3298 ipp_attribute_t *uri) /* I - Job or Printer URI */
3299 {
3300 ipp_attribute_t *attr; /* Current attribute */
3301 int jobid; /* Job ID */
3302 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
3303 username[HTTP_MAX_URI], /* Username portion of URI */
3304 host[HTTP_MAX_URI], /* Host portion of URI */
3305 resource[HTTP_MAX_URI]; /* Resource portion of URI */
3306 int port; /* Port portion of URI */
3307 cupsd_job_t *job; /* Job information */
3308 cups_ptype_t dtype; /* Destination type (printer/class) */
3309 cupsd_printer_t *printer; /* Printer data */
3310 cupsd_jobaction_t purge; /* Purge the job? */
3311
3312
3313 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_job(%p[%d], %s)", con,
3314 con->number, uri->values[0].string.text);
3315
3316 /*
3317 * See if we have a job URI or a printer URI...
3318 */
3319
3320 if (!strcmp(uri->name, "printer-uri"))
3321 {
3322 /*
3323 * Got a printer URI; see if we also have a job-id attribute...
3324 */
3325
3326 if ((attr = ippFindAttribute(con->request, "job-id",
3327 IPP_TAG_INTEGER)) == NULL)
3328 {
3329 send_ipp_status(con, IPP_BAD_REQUEST,
3330 _("Got a printer-uri attribute but no job-id."));
3331 return;
3332 }
3333
3334 if ((jobid = attr->values[0].integer) == 0)
3335 {
3336 /*
3337 * Find the current job on the specified printer...
3338 */
3339
3340 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
3341 {
3342 /*
3343 * Bad URI...
3344 */
3345
3346 send_ipp_status(con, IPP_NOT_FOUND,
3347 _("The printer or class does not exist."));
3348 return;
3349 }
3350
3351 /*
3352 * See if there are any pending jobs...
3353 */
3354
3355 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
3356 job;
3357 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
3358 if (job->state_value <= IPP_JOB_PROCESSING &&
3359 !_cups_strcasecmp(job->dest, printer->name))
3360 break;
3361
3362 if (job)
3363 jobid = job->id;
3364 else
3365 {
3366 /*
3367 * No, try stopped jobs...
3368 */
3369
3370 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
3371 job;
3372 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
3373 if (job->state_value == IPP_JOB_STOPPED &&
3374 !_cups_strcasecmp(job->dest, printer->name))
3375 break;
3376
3377 if (job)
3378 jobid = job->id;
3379 else
3380 {
3381 send_ipp_status(con, IPP_NOT_POSSIBLE, _("No active jobs on %s."),
3382 printer->name);
3383 return;
3384 }
3385 }
3386 }
3387 }
3388 else
3389 {
3390 /*
3391 * Got a job URI; parse it to get the job ID...
3392 */
3393
3394 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
3395 sizeof(scheme), username, sizeof(username), host,
3396 sizeof(host), &port, resource, sizeof(resource));
3397
3398 if (strncmp(resource, "/jobs/", 6))
3399 {
3400 /*
3401 * Not a valid URI!
3402 */
3403
3404 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
3405 uri->values[0].string.text);
3406 return;
3407 }
3408
3409 jobid = atoi(resource + 6);
3410 }
3411
3412 /*
3413 * Look for the "purge-job" attribute...
3414 */
3415
3416 if ((attr = ippFindAttribute(con->request, "purge-job",
3417 IPP_TAG_BOOLEAN)) != NULL)
3418 purge = attr->values[0].boolean ? CUPSD_JOB_PURGE : CUPSD_JOB_DEFAULT;
3419 else
3420 purge = CUPSD_JOB_DEFAULT;
3421
3422 /*
3423 * See if the job exists...
3424 */
3425
3426 if ((job = cupsdFindJob(jobid)) == NULL)
3427 {
3428 /*
3429 * Nope - return a "not found" error...
3430 */
3431
3432 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
3433 return;
3434 }
3435
3436 /*
3437 * See if the job is owned by the requesting user...
3438 */
3439
3440 if (!validate_user(job, con, job->username, username, sizeof(username)))
3441 {
3442 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
3443 cupsdFindDest(job->dest));
3444 return;
3445 }
3446
3447 /*
3448 * See if the job is already completed, canceled, or aborted; if so,
3449 * we can't cancel...
3450 */
3451
3452 if (job->state_value >= IPP_JOB_CANCELED && purge != CUPSD_JOB_PURGE)
3453 {
3454 switch (job->state_value)
3455 {
3456 case IPP_JOB_CANCELED :
3457 send_ipp_status(con, IPP_NOT_POSSIBLE,
3458 _("Job #%d is already canceled - can\'t cancel."),
3459 jobid);
3460 break;
3461
3462 case IPP_JOB_ABORTED :
3463 send_ipp_status(con, IPP_NOT_POSSIBLE,
3464 _("Job #%d is already aborted - can\'t cancel."),
3465 jobid);
3466 break;
3467
3468 default :
3469 send_ipp_status(con, IPP_NOT_POSSIBLE,
3470 _("Job #%d is already completed - can\'t cancel."),
3471 jobid);
3472 break;
3473 }
3474
3475 return;
3476 }
3477
3478 /*
3479 * Cancel the job and return...
3480 */
3481
3482 cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
3483 purge == CUPSD_JOB_PURGE ? "Job purged by \"%s\"" :
3484 "Job canceled by \"%s\"",
3485 username);
3486 cupsdCheckJobs();
3487
3488 if (purge == CUPSD_JOB_PURGE)
3489 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Purged by \"%s\".", jobid,
3490 username);
3491 else
3492 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Canceled by \"%s\".", jobid,
3493 username);
3494
3495 con->response->request.status.status_code = IPP_OK;
3496 }
3497
3498
3499 /*
3500 * 'cancel_subscription()' - Cancel a subscription.
3501 */
3502
3503 static void
3504 cancel_subscription(
3505 cupsd_client_t *con, /* I - Client connection */
3506 int sub_id) /* I - Subscription ID */
3507 {
3508 http_status_t status; /* Policy status */
3509 cupsd_subscription_t *sub; /* Subscription */
3510
3511
3512 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3513 "cancel_subscription(con=%p[%d], sub_id=%d)",
3514 con, con->number, sub_id);
3515
3516 /*
3517 * Is the subscription ID valid?
3518 */
3519
3520 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
3521 {
3522 /*
3523 * Bad subscription ID...
3524 */
3525
3526 send_ipp_status(con, IPP_NOT_FOUND,
3527 _("Subscription #%d does not exist."), sub_id);
3528 return;
3529 }
3530
3531 /*
3532 * Check policy...
3533 */
3534
3535 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
3536 DefaultPolicyPtr,
3537 con, sub->owner)) != HTTP_OK)
3538 {
3539 send_http_error(con, status, sub->dest);
3540 return;
3541 }
3542
3543 /*
3544 * Cancel the subscription...
3545 */
3546
3547 cupsdDeleteSubscription(sub, 1);
3548
3549 con->response->request.status.status_code = IPP_OK;
3550 }
3551
3552
3553 /*
3554 * 'check_rss_recipient()' - Check that we do not have a duplicate RSS feed URI.
3555 */
3556
3557 static int /* O - 1 if OK, 0 if not */
3558 check_rss_recipient(
3559 const char *recipient) /* I - Recipient URI */
3560 {
3561 cupsd_subscription_t *sub; /* Current subscription */
3562
3563
3564 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
3565 sub;
3566 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
3567 if (sub->recipient)
3568 {
3569 /*
3570 * Compare the URIs up to the first ?...
3571 */
3572
3573 const char *r1, *r2;
3574
3575 for (r1 = recipient, r2 = sub->recipient;
3576 *r1 == *r2 && *r1 && *r1 != '?' && *r2 && *r2 != '?';
3577 r1 ++, r2 ++);
3578
3579 if (*r1 == *r2)
3580 return (0);
3581 }
3582
3583 return (1);
3584 }
3585
3586
3587 /*
3588 * 'check_quotas()' - Check quotas for a printer and user.
3589 */
3590
3591 static int /* O - 1 if OK, 0 if forbidden,
3592 -1 if limit reached */
3593 check_quotas(cupsd_client_t *con, /* I - Client connection */
3594 cupsd_printer_t *p) /* I - Printer or class */
3595 {
3596 char username[33], /* Username */
3597 *name; /* Current user name */
3598 cupsd_quota_t *q; /* Quota data */
3599 #ifdef HAVE_MBR_UID_TO_UUID
3600 /*
3601 * Use Apple membership APIs which require that all names represent
3602 * valid user account or group records accessible by the server.
3603 */
3604
3605 uuid_t usr_uuid; /* UUID for job requesting user */
3606 uuid_t usr2_uuid; /* UUID for ACL user name entry */
3607 uuid_t grp_uuid; /* UUID for ACL group name entry */
3608 int mbr_err; /* Error from membership function */
3609 int is_member; /* Is this user a member? */
3610 #else
3611 /*
3612 * Use standard POSIX APIs for checking users and groups...
3613 */
3614
3615 struct passwd *pw; /* User password data */
3616 #endif /* HAVE_MBR_UID_TO_UUID */
3617
3618
3619 cupsdLogMessage(CUPSD_LOG_DEBUG2, "check_quotas(%p[%d], %p[%s])",
3620 con, con->number, p, p->name);
3621
3622 /*
3623 * Figure out who is printing...
3624 */
3625
3626 strlcpy(username, get_username(con), sizeof(username));
3627
3628 if ((name = strchr(username, '@')) != NULL)
3629 *name = '\0'; /* Strip @REALM */
3630
3631 /*
3632 * Check global active job limits for printers and users...
3633 */
3634
3635 if (MaxJobsPerPrinter)
3636 {
3637 /*
3638 * Check if there are too many pending jobs on this printer...
3639 */
3640
3641 if (cupsdGetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
3642 {
3643 cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for printer \"%s\"...",
3644 p->name);
3645 return (-1);
3646 }
3647 }
3648
3649 if (MaxJobsPerUser)
3650 {
3651 /*
3652 * Check if there are too many pending jobs for this user...
3653 */
3654
3655 if (cupsdGetUserJobCount(username) >= MaxJobsPerUser)
3656 {
3657 cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for user \"%s\"...",
3658 username);
3659 return (-1);
3660 }
3661 }
3662
3663 /*
3664 * Check against users...
3665 */
3666
3667 if (cupsArrayCount(p->users) == 0 && p->k_limit == 0 && p->page_limit == 0)
3668 return (1);
3669
3670 if (cupsArrayCount(p->users))
3671 {
3672 #ifdef HAVE_MBR_UID_TO_UUID
3673 /*
3674 * Get UUID for job requesting user...
3675 */
3676
3677 if (mbr_user_name_to_uuid((char *)username, usr_uuid))
3678 {
3679 /*
3680 * Unknown user...
3681 */
3682
3683 cupsdLogMessage(CUPSD_LOG_DEBUG,
3684 "check_quotas: UUID lookup failed for user \"%s\"",
3685 username);
3686 cupsdLogMessage(CUPSD_LOG_INFO,
3687 "Denying user \"%s\" access to printer \"%s\" "
3688 "(unknown user)...",
3689 username, p->name);
3690 return (0);
3691 }
3692 #else
3693 /*
3694 * Get UID and GID of requesting user...
3695 */
3696
3697 pw = getpwnam(username);
3698 endpwent();
3699 #endif /* HAVE_MBR_UID_TO_UUID */
3700
3701 for (name = (char *)cupsArrayFirst(p->users);
3702 name;
3703 name = (char *)cupsArrayNext(p->users))
3704 if (name[0] == '@')
3705 {
3706 /*
3707 * Check group membership...
3708 */
3709
3710 #ifdef HAVE_MBR_UID_TO_UUID
3711 if (name[1] == '#')
3712 {
3713 if (uuid_parse(name + 2, grp_uuid))
3714 uuid_clear(grp_uuid);
3715 }
3716 else if ((mbr_err = mbr_group_name_to_uuid(name + 1, grp_uuid)) != 0)
3717 {
3718 /*
3719 * Invalid ACL entries are ignored for matching; just record a
3720 * warning in the log...
3721 */
3722
3723 cupsdLogMessage(CUPSD_LOG_DEBUG,
3724 "check_quotas: UUID lookup failed for ACL entry "
3725 "\"%s\" (err=%d)", name, mbr_err);
3726 cupsdLogMessage(CUPSD_LOG_WARN,
3727 "Access control entry \"%s\" not a valid group name; "
3728 "entry ignored", name);
3729 }
3730
3731 if ((mbr_err = mbr_check_membership(usr_uuid, grp_uuid,
3732 &is_member)) != 0)
3733 {
3734 /*
3735 * At this point, there should be no errors, but check anyways...
3736 */
3737
3738 cupsdLogMessage(CUPSD_LOG_DEBUG,
3739 "check_quotas: group \"%s\" membership check "
3740 "failed (err=%d)", name + 1, mbr_err);
3741 is_member = 0;
3742 }
3743
3744 /*
3745 * Stop if we found a match...
3746 */
3747
3748 if (is_member)
3749 break;
3750
3751 #else
3752 if (cupsdCheckGroup(username, pw, name + 1))
3753 break;
3754 #endif /* HAVE_MBR_UID_TO_UUID */
3755 }
3756 #ifdef HAVE_MBR_UID_TO_UUID
3757 else
3758 {
3759 if (name[0] == '#')
3760 {
3761 if (uuid_parse(name + 1, usr2_uuid))
3762 uuid_clear(usr2_uuid);
3763 }
3764 else if ((mbr_err = mbr_user_name_to_uuid(name, usr2_uuid)) != 0)
3765 {
3766 /*
3767 * Invalid ACL entries are ignored for matching; just record a
3768 * warning in the log...
3769 */
3770
3771 cupsdLogMessage(CUPSD_LOG_DEBUG,
3772 "check_quotas: UUID lookup failed for ACL entry "
3773 "\"%s\" (err=%d)", name, mbr_err);
3774 cupsdLogMessage(CUPSD_LOG_WARN,
3775 "Access control entry \"%s\" not a valid user name; "
3776 "entry ignored", name);
3777 }
3778
3779 if (!uuid_compare(usr_uuid, usr2_uuid))
3780 break;
3781 }
3782 #else
3783 else if (!_cups_strcasecmp(username, name))
3784 break;
3785 #endif /* HAVE_MBR_UID_TO_UUID */
3786
3787 if ((name != NULL) == p->deny_users)
3788 {
3789 cupsdLogMessage(CUPSD_LOG_INFO,
3790 "Denying user \"%s\" access to printer \"%s\"...",
3791 username, p->name);
3792 return (0);
3793 }
3794 }
3795
3796 /*
3797 * Check quotas...
3798 */
3799
3800 if (p->k_limit || p->page_limit)
3801 {
3802 if ((q = cupsdUpdateQuota(p, username, 0, 0)) == NULL)
3803 {
3804 cupsdLogMessage(CUPSD_LOG_ERROR,
3805 "Unable to allocate quota data for user \"%s\"",
3806 username);
3807 return (-1);
3808 }
3809
3810 if ((q->k_count >= p->k_limit && p->k_limit) ||
3811 (q->page_count >= p->page_limit && p->page_limit))
3812 {
3813 cupsdLogMessage(CUPSD_LOG_INFO, "User \"%s\" is over the quota limit...",
3814 username);
3815 return (-1);
3816 }
3817 }
3818
3819 /*
3820 * If we have gotten this far, we're done!
3821 */
3822
3823 return (1);
3824 }
3825
3826
3827 /*
3828 * 'close_job()' - Close a multi-file job.
3829 */
3830
3831 static void
3832 close_job(cupsd_client_t *con, /* I - Client connection */
3833 ipp_attribute_t *uri) /* I - Printer URI */
3834 {
3835 cupsd_job_t *job; /* Job */
3836 ipp_attribute_t *attr; /* Attribute */
3837 char job_uri[HTTP_MAX_URI],
3838 /* Job URI */
3839 username[256]; /* User name */
3840
3841
3842 cupsdLogMessage(CUPSD_LOG_DEBUG2, "close_job(%p[%d], %s)", con,
3843 con->number, uri->values[0].string.text);
3844
3845 /*
3846 * See if we have a job URI or a printer URI...
3847 */
3848
3849 if (strcmp(uri->name, "printer-uri"))
3850 {
3851 /*
3852 * job-uri is not supported by Close-Job!
3853 */
3854
3855 send_ipp_status(con, IPP_BAD_REQUEST,
3856 _("Close-Job doesn't support the job-uri attribute."));
3857 return;
3858 }
3859
3860 /*
3861 * Got a printer URI; see if we also have a job-id attribute...
3862 */
3863
3864 if ((attr = ippFindAttribute(con->request, "job-id",
3865 IPP_TAG_INTEGER)) == NULL)
3866 {
3867 send_ipp_status(con, IPP_BAD_REQUEST,
3868 _("Got a printer-uri attribute but no job-id."));
3869 return;
3870 }
3871
3872 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
3873 {
3874 /*
3875 * Nope - return a "not found" error...
3876 */
3877
3878 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
3879 attr->values[0].integer);
3880 return;
3881 }
3882
3883 /*
3884 * See if the job is owned by the requesting user...
3885 */
3886
3887 if (!validate_user(job, con, job->username, username, sizeof(username)))
3888 {
3889 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
3890 cupsdFindDest(job->dest));
3891 return;
3892 }
3893
3894 /*
3895 * Add any ending sheet...
3896 */
3897
3898 if (cupsdTimeoutJob(job))
3899 return;
3900
3901 if (job->state_value == IPP_JOB_STOPPED)
3902 {
3903 job->state->values[0].integer = IPP_JOB_PENDING;
3904 job->state_value = IPP_JOB_PENDING;
3905 }
3906 else if (job->state_value == IPP_JOB_HELD)
3907 {
3908 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
3909 IPP_TAG_KEYWORD)) == NULL)
3910 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
3911
3912 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
3913 {
3914 job->state->values[0].integer = IPP_JOB_PENDING;
3915 job->state_value = IPP_JOB_PENDING;
3916 }
3917 }
3918
3919 job->dirty = 1;
3920 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
3921
3922 /*
3923 * Fill in the response info...
3924 */
3925
3926 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
3927 con->clientname, con->clientport, "/jobs/%d", job->id);
3928 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
3929 job_uri);
3930
3931 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
3932
3933 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
3934 job->state_value);
3935
3936 con->response->request.status.status_code = IPP_OK;
3937
3938 /*
3939 * Start the job if necessary...
3940 */
3941
3942 cupsdCheckJobs();
3943 }
3944
3945
3946 /*
3947 * 'copy_attrs()' - Copy attributes from one request to another.
3948 */
3949
3950 static void
3951 copy_attrs(ipp_t *to, /* I - Destination request */
3952 ipp_t *from, /* I - Source request */
3953 cups_array_t *ra, /* I - Requested attributes */
3954 ipp_tag_t group, /* I - Group to copy */
3955 int quickcopy, /* I - Do a quick copy? */
3956 cups_array_t *exclude) /* I - Attributes to exclude? */
3957 {
3958 ipp_attribute_t *fromattr; /* Source attribute */
3959
3960
3961 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3962 "copy_attrs(to=%p, from=%p, ra=%p, group=%x, quickcopy=%d)",
3963 to, from, ra, group, quickcopy);
3964
3965 if (!to || !from)
3966 return;
3967
3968 for (fromattr = from->attrs; fromattr; fromattr = fromattr->next)
3969 {
3970 /*
3971 * Filter attributes as needed...
3972 */
3973
3974 if ((group != IPP_TAG_ZERO && fromattr->group_tag != group &&
3975 fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
3976 continue;
3977
3978 if (!strcmp(fromattr->name, "document-password") ||
3979 !strcmp(fromattr->name, "job-authorization-uri") ||
3980 !strcmp(fromattr->name, "job-password") ||
3981 !strcmp(fromattr->name, "job-password-encryption") ||
3982 !strcmp(fromattr->name, "job-printer-uri"))
3983 continue;
3984
3985 if (exclude &&
3986 (cupsArrayFind(exclude, fromattr->name) ||
3987 cupsArrayFind(exclude, "all")))
3988 {
3989 /*
3990 * We need to exclude this attribute for security reasons; we require the
3991 * job-id attribute regardless of the security settings for IPP
3992 * conformance.
3993 *
3994 * The job-printer-uri attribute is handled by copy_job_attrs().
3995 *
3996 * Subscription attribute security is handled by copy_subscription_attrs().
3997 */
3998
3999 if (strcmp(fromattr->name, "job-id"))
4000 continue;
4001 }
4002
4003 if (!ra || cupsArrayFind(ra, fromattr->name))
4004 {
4005 /*
4006 * Don't send collection attributes by default to IPP/1.x clients
4007 * since many do not support collections. Also don't send
4008 * media-col-database unless specifically requested by the client.
4009 */
4010
4011 if (fromattr->value_tag == IPP_TAG_BEGIN_COLLECTION &&
4012 !ra &&
4013 (to->request.status.version[0] == 1 ||
4014 !strcmp(fromattr->name, "media-col-database")))
4015 continue;
4016
4017 ippCopyAttribute(to, fromattr, quickcopy);
4018 }
4019 }
4020 }
4021
4022
4023 /*
4024 * 'copy_banner()' - Copy a banner file to the requests directory for the
4025 * specified job.
4026 */
4027
4028 static int /* O - Size of banner file in kbytes */
4029 copy_banner(cupsd_client_t *con, /* I - Client connection */
4030 cupsd_job_t *job, /* I - Job information */
4031 const char *name) /* I - Name of banner */
4032 {
4033 int i; /* Looping var */
4034 int kbytes; /* Size of banner file in kbytes */
4035 char filename[1024]; /* Job filename */
4036 cupsd_banner_t *banner; /* Pointer to banner */
4037 cups_file_t *in; /* Input file */
4038 cups_file_t *out; /* Output file */
4039 int ch; /* Character from file */
4040 char attrname[255], /* Name of attribute */
4041 *s; /* Pointer into name */
4042 ipp_attribute_t *attr; /* Attribute */
4043
4044
4045 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4046 "copy_banner(con=%p[%d], job=%p[%d], name=\"%s\")",
4047 con, con ? con->number : -1, job, job->id,
4048 name ? name : "(null)");
4049
4050 /*
4051 * Find the banner; return if not found or "none"...
4052 */
4053
4054 if (!name || !strcmp(name, "none") ||
4055 (banner = cupsdFindBanner(name)) == NULL)
4056 return (0);
4057
4058 /*
4059 * Open the banner and job files...
4060 */
4061
4062 if (add_file(con, job, banner->filetype, 0))
4063 return (-1);
4064
4065 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
4066 job->num_files);
4067 if ((out = cupsFileOpen(filename, "w")) == NULL)
4068 {
4069 cupsdLogMessage(CUPSD_LOG_ERROR,
4070 "Unable to create banner job file %s - %s",
4071 filename, strerror(errno));
4072 job->num_files --;
4073 return (0);
4074 }
4075
4076 fchmod(cupsFileNumber(out), 0640);
4077 fchown(cupsFileNumber(out), RunUser, Group);
4078
4079 /*
4080 * Try the localized banner file under the subdirectory...
4081 */
4082
4083 strlcpy(attrname, job->attrs->attrs->next->values[0].string.text,
4084 sizeof(attrname));
4085 if (strlen(attrname) > 2 && attrname[2] == '-')
4086 {
4087 /*
4088 * Convert ll-cc to ll_CC...
4089 */
4090
4091 attrname[2] = '_';
4092 attrname[3] = (char)toupper(attrname[3] & 255);
4093 attrname[4] = (char)toupper(attrname[4] & 255);
4094 }
4095
4096 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
4097 attrname, name);
4098
4099 if (access(filename, 0) && strlen(attrname) > 2)
4100 {
4101 /*
4102 * Wasn't able to find "ll_CC" locale file; try the non-national
4103 * localization banner directory.
4104 */
4105
4106 attrname[2] = '\0';
4107
4108 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
4109 attrname, name);
4110 }
4111
4112 if (access(filename, 0))
4113 {
4114 /*
4115 * Use the non-localized banner file.
4116 */
4117
4118 snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
4119 }
4120
4121 if ((in = cupsFileOpen(filename, "r")) == NULL)
4122 {
4123 cupsFileClose(out);
4124 unlink(filename);
4125 cupsdLogMessage(CUPSD_LOG_ERROR,
4126 "Unable to open banner template file %s - %s",
4127 filename, strerror(errno));
4128 job->num_files --;
4129 return (0);
4130 }
4131
4132 /*
4133 * Parse the file to the end...
4134 */
4135
4136 while ((ch = cupsFileGetChar(in)) != EOF)
4137 if (ch == '{')
4138 {
4139 /*
4140 * Get an attribute name...
4141 */
4142
4143 for (s = attrname; (ch = cupsFileGetChar(in)) != EOF;)
4144 if (!isalpha(ch & 255) && ch != '-' && ch != '?')
4145 break;
4146 else if (s < (attrname + sizeof(attrname) - 1))
4147 *s++ = (char)ch;
4148 else
4149 break;
4150
4151 *s = '\0';
4152
4153 if (ch != '}')
4154 {
4155 /*
4156 * Ignore { followed by stuff that is not an attribute name...
4157 */
4158
4159 cupsFilePrintf(out, "{%s%c", attrname, ch);
4160 continue;
4161 }
4162
4163 /*
4164 * See if it is defined...
4165 */
4166
4167 if (attrname[0] == '?')
4168 s = attrname + 1;
4169 else
4170 s = attrname;
4171
4172 if (!strcmp(s, "printer-name"))
4173 {
4174 cupsFilePuts(out, job->dest);
4175 continue;
4176 }
4177 else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
4178 {
4179 /*
4180 * See if we have a leading question mark...
4181 */
4182
4183 if (attrname[0] != '?')
4184 {
4185 /*
4186 * Nope, write to file as-is; probably a PostScript procedure...
4187 */
4188
4189 cupsFilePrintf(out, "{%s}", attrname);
4190 }
4191
4192 continue;
4193 }
4194
4195 /*
4196 * Output value(s)...
4197 */
4198
4199 for (i = 0; i < attr->num_values; i ++)
4200 {
4201 if (i)
4202 cupsFilePutChar(out, ',');
4203
4204 switch (attr->value_tag)
4205 {
4206 case IPP_TAG_INTEGER :
4207 case IPP_TAG_ENUM :
4208 if (!strncmp(s, "time-at-", 8))
4209 {
4210 struct timeval tv; /* Time value */
4211
4212 tv.tv_sec = attr->values[i].integer;
4213 tv.tv_usec = 0;
4214
4215 cupsFilePuts(out, cupsdGetDateTime(&tv, CUPSD_TIME_STANDARD));
4216 }
4217 else
4218 cupsFilePrintf(out, "%d", attr->values[i].integer);
4219 break;
4220
4221 case IPP_TAG_BOOLEAN :
4222 cupsFilePrintf(out, "%d", attr->values[i].boolean);
4223 break;
4224
4225 case IPP_TAG_NOVALUE :
4226 cupsFilePuts(out, "novalue");
4227 break;
4228
4229 case IPP_TAG_RANGE :
4230 cupsFilePrintf(out, "%d-%d", attr->values[i].range.lower,
4231 attr->values[i].range.upper);
4232 break;
4233
4234 case IPP_TAG_RESOLUTION :
4235 cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
4236 attr->values[i].resolution.yres,
4237 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
4238 "dpi" : "dpcm");
4239 break;
4240
4241 case IPP_TAG_URI :
4242 case IPP_TAG_STRING :
4243 case IPP_TAG_TEXT :
4244 case IPP_TAG_NAME :
4245 case IPP_TAG_KEYWORD :
4246 case IPP_TAG_CHARSET :
4247 case IPP_TAG_LANGUAGE :
4248 if (!_cups_strcasecmp(banner->filetype->type, "postscript"))
4249 {
4250 /*
4251 * Need to quote strings for PS banners...
4252 */
4253
4254 const char *p;
4255
4256 for (p = attr->values[i].string.text; *p; p ++)
4257 {
4258 if (*p == '(' || *p == ')' || *p == '\\')
4259 {
4260 cupsFilePutChar(out, '\\');
4261 cupsFilePutChar(out, *p);
4262 }
4263 else if (*p < 32 || *p > 126)
4264 cupsFilePrintf(out, "\\%03o", *p & 255);
4265 else
4266 cupsFilePutChar(out, *p);
4267 }
4268 }
4269 else
4270 cupsFilePuts(out, attr->values[i].string.text);
4271 break;
4272
4273 default :
4274 break; /* anti-compiler-warning-code */
4275 }
4276 }
4277 }
4278 else if (ch == '\\') /* Quoted char */
4279 {
4280 ch = cupsFileGetChar(in);
4281
4282 if (ch != '{') /* Only do special handling for \{ */
4283 cupsFilePutChar(out, '\\');
4284
4285 cupsFilePutChar(out, ch);
4286 }
4287 else
4288 cupsFilePutChar(out, ch);
4289
4290 cupsFileClose(in);
4291
4292 kbytes = (cupsFileTell(out) + 1023) / 1024;
4293
4294 job->koctets += kbytes;
4295
4296 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
4297 attr->values[0].integer += kbytes;
4298
4299 cupsFileClose(out);
4300
4301 return (kbytes);
4302 }
4303
4304
4305 /*
4306 * 'copy_file()' - Copy a PPD file or interface script...
4307 */
4308
4309 static int /* O - 0 = success, -1 = error */
4310 copy_file(const char *from, /* I - Source file */
4311 const char *to) /* I - Destination file */
4312 {
4313 cups_file_t *src, /* Source file */
4314 *dst; /* Destination file */
4315 int bytes; /* Bytes to read/write */
4316 char buffer[2048]; /* Copy buffer */
4317
4318
4319 cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_file(\"%s\", \"%s\")", from, to);
4320
4321 /*
4322 * Open the source and destination file for a copy...
4323 */
4324
4325 if ((src = cupsFileOpen(from, "rb")) == NULL)
4326 return (-1);
4327
4328 if ((dst = cupsFileOpen(to, "wb")) == NULL)
4329 {
4330 cupsFileClose(src);
4331 return (-1);
4332 }
4333
4334 /*
4335 * Copy the source file to the destination...
4336 */
4337
4338 while ((bytes = cupsFileRead(src, buffer, sizeof(buffer))) > 0)
4339 if (cupsFileWrite(dst, buffer, (size_t)bytes) < bytes)
4340 {
4341 cupsFileClose(src);
4342 cupsFileClose(dst);
4343 return (-1);
4344 }
4345
4346 /*
4347 * Close both files and return...
4348 */
4349
4350 cupsFileClose(src);
4351
4352 return (cupsFileClose(dst));
4353 }
4354
4355
4356 /*
4357 * 'copy_model()' - Copy a PPD model file, substituting default values
4358 * as needed...
4359 */
4360
4361 static int /* O - 0 = success, -1 = error */
4362 copy_model(cupsd_client_t *con, /* I - Client connection */
4363 const char *from, /* I - Source file */
4364 const char *to) /* I - Destination file */
4365 {
4366 fd_set input; /* select() input set */
4367 struct timeval timeout; /* select() timeout */
4368 int maxfd; /* Max file descriptor for select() */
4369 char tempfile[1024]; /* Temporary PPD file */
4370 int tempfd; /* Temporary PPD file descriptor */
4371 int temppid; /* Process ID of cups-driverd */
4372 int temppipe[2]; /* Temporary pipes */
4373 char *argv[4], /* Command-line arguments */
4374 *envp[MAX_ENV]; /* Environment */
4375 cups_file_t *src, /* Source file */
4376 *dst; /* Destination file */
4377 ppd_file_t *ppd; /* PPD file */
4378 int bytes, /* Bytes from pipe */
4379 total; /* Total bytes from pipe */
4380 char buffer[2048]; /* Copy buffer */
4381 int i; /* Looping var */
4382 char option[PPD_MAX_NAME], /* Option name */
4383 choice[PPD_MAX_NAME]; /* Choice name */
4384 ppd_size_t *size; /* Default size */
4385 int num_defaults; /* Number of default options */
4386 cups_option_t *defaults; /* Default options */
4387 char cups_protocol[PPD_MAX_LINE];
4388 /* cupsProtocol attribute */
4389
4390
4391 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4392 "copy_model(con=%p, from=\"%s\", to=\"%s\")",
4393 con, from, to);
4394
4395 /*
4396 * Run cups-driverd to get the PPD file...
4397 */
4398
4399 argv[0] = "cups-driverd";
4400 argv[1] = "cat";
4401 argv[2] = (char *)from;
4402 argv[3] = NULL;
4403
4404 cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
4405
4406 snprintf(buffer, sizeof(buffer), "%s/daemon/cups-driverd", ServerBin);
4407 snprintf(tempfile, sizeof(tempfile), "%s/%d.ppd", TempDir, con->number);
4408 tempfd = open(tempfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
4409 if (tempfd < 0 || cupsdOpenPipe(temppipe))
4410 return (-1);
4411
4412 cupsdLogMessage(CUPSD_LOG_DEBUG,
4413 "copy_model: Running \"cups-driverd cat %s\"...", from);
4414
4415 if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1],
4416 -1, -1, 0, DefaultProfile, NULL, &temppid))
4417 {
4418 close(tempfd);
4419 unlink(tempfile);
4420
4421 return (-1);
4422 }
4423
4424 close(temppipe[1]);
4425
4426 /*
4427 * Wait up to 30 seconds for the PPD file to be copied...
4428 */
4429
4430 total = 0;
4431
4432 if (temppipe[0] > CGIPipes[0])
4433 maxfd = temppipe[0] + 1;
4434 else
4435 maxfd = CGIPipes[0] + 1;
4436
4437 for (;;)
4438 {
4439 /*
4440 * See if we have data ready...
4441 */
4442
4443 FD_ZERO(&input);
4444 FD_SET(temppipe[0], &input);
4445 FD_SET(CGIPipes[0], &input);
4446
4447 timeout.tv_sec = 30;
4448 timeout.tv_usec = 0;
4449
4450 if ((i = select(maxfd, &input, NULL, NULL, &timeout)) < 0)
4451 {
4452 if (errno == EINTR)
4453 continue;
4454 else
4455 break;
4456 }
4457 else if (i == 0)
4458 {
4459 /*
4460 * We have timed out...
4461 */
4462
4463 break;
4464 }
4465
4466 if (FD_ISSET(temppipe[0], &input))
4467 {
4468 /*
4469 * Read the PPD file from the pipe, and write it to the PPD file.
4470 */
4471
4472 if ((bytes = read(temppipe[0], buffer, sizeof(buffer))) > 0)
4473 {
4474 if (write(tempfd, buffer, (size_t)bytes) < bytes)
4475 break;
4476
4477 total += bytes;
4478 }
4479 else
4480 break;
4481 }
4482
4483 if (FD_ISSET(CGIPipes[0], &input))
4484 cupsdUpdateCGI();
4485 }
4486
4487 close(temppipe[0]);
4488 close(tempfd);
4489
4490 if (!total)
4491 {
4492 /*
4493 * No data from cups-deviced...
4494 */
4495
4496 cupsdLogMessage(CUPSD_LOG_ERROR, "copy_model: empty PPD file");
4497 unlink(tempfile);
4498 return (-1);
4499 }
4500
4501 /*
4502 * Open the source file for a copy...
4503 */
4504
4505 if ((src = cupsFileOpen(tempfile, "rb")) == NULL)
4506 {
4507 unlink(tempfile);
4508 return (-1);
4509 }
4510
4511 /*
4512 * Read the source file and see what page sizes are supported...
4513 */
4514
4515 if ((ppd = _ppdOpen(src, _PPD_LOCALIZATION_NONE)) == NULL)
4516 {
4517 cupsFileClose(src);
4518 unlink(tempfile);
4519 return (-1);
4520 }
4521
4522 /*
4523 * Open the destination (if possible) and set the default options...
4524 */
4525
4526 num_defaults = 0;
4527 defaults = NULL;
4528 cups_protocol[0] = '\0';
4529
4530 if ((dst = cupsFileOpen(to, "rb")) != NULL)
4531 {
4532 /*
4533 * Read all of the default lines from the old PPD...
4534 */
4535
4536 while (cupsFileGets(dst, buffer, sizeof(buffer)))
4537 if (!strncmp(buffer, "*Default", 8))
4538 {
4539 /*
4540 * Add the default option...
4541 */
4542
4543 if (!ppd_parse_line(buffer, option, sizeof(option),
4544 choice, sizeof(choice)))
4545 {
4546 ppd_option_t *ppdo; /* PPD option */
4547
4548
4549 /*
4550 * Only add the default if the default hasn't already been
4551 * set and the choice exists in the new PPD...
4552 */
4553
4554 if (!cupsGetOption(option, num_defaults, defaults) &&
4555 (ppdo = ppdFindOption(ppd, option)) != NULL &&
4556 ppdFindChoice(ppdo, choice))
4557 num_defaults = cupsAddOption(option, choice, num_defaults,
4558 &defaults);
4559 }
4560 }
4561 else if (!strncmp(buffer, "*cupsProtocol:", 14))
4562 strlcpy(cups_protocol, buffer, sizeof(cups_protocol));
4563
4564 cupsFileClose(dst);
4565 }
4566 else if ((size = ppdPageSize(ppd, DefaultPaperSize)) != NULL)
4567 {
4568 /*
4569 * Add the default media sizes...
4570 */
4571
4572 num_defaults = cupsAddOption("PageSize", size->name,
4573 num_defaults, &defaults);
4574 num_defaults = cupsAddOption("PageRegion", size->name,
4575 num_defaults, &defaults);
4576 num_defaults = cupsAddOption("PaperDimension", size->name,
4577 num_defaults, &defaults);
4578 num_defaults = cupsAddOption("ImageableArea", size->name,
4579 num_defaults, &defaults);
4580 }
4581
4582 ppdClose(ppd);
4583
4584 /*
4585 * Open the destination file for a copy...
4586 */
4587
4588 if ((dst = cupsdCreateConfFile(to, ConfigFilePerm)) == NULL)
4589 {
4590 cupsFreeOptions(num_defaults, defaults);
4591 cupsFileClose(src);
4592 unlink(tempfile);
4593 return (-1);
4594 }
4595
4596 /*
4597 * Copy the source file to the destination...
4598 */
4599
4600 cupsFileRewind(src);
4601
4602 while (cupsFileGets(src, buffer, sizeof(buffer)))
4603 {
4604 if (!strncmp(buffer, "*Default", 8))
4605 {
4606 /*
4607 * Check for an previous default option choice...
4608 */
4609
4610 if (!ppd_parse_line(buffer, option, sizeof(option),
4611 choice, sizeof(choice)))
4612 {
4613 const char *val; /* Default option value */
4614
4615
4616 if ((val = cupsGetOption(option, num_defaults, defaults)) != NULL)
4617 {
4618 /*
4619 * Substitute the previous choice...
4620 */
4621
4622 snprintf(buffer, sizeof(buffer), "*Default%s: %s", option, val);
4623 }
4624 }
4625 }
4626
4627 cupsFilePrintf(dst, "%s\n", buffer);
4628 }
4629
4630 if (cups_protocol[0])
4631 cupsFilePrintf(dst, "%s\n", cups_protocol);
4632
4633 cupsFreeOptions(num_defaults, defaults);
4634
4635 /*
4636 * Close both files and return...
4637 */
4638
4639 cupsFileClose(src);
4640
4641 unlink(tempfile);
4642
4643 return (cupsdCloseCreatedConfFile(dst, to));
4644 }
4645
4646
4647 /*
4648 * 'copy_job_attrs()' - Copy job attributes.
4649 */
4650
4651 static void
4652 copy_job_attrs(cupsd_client_t *con, /* I - Client connection */
4653 cupsd_job_t *job, /* I - Job */
4654 cups_array_t *ra, /* I - Requested attributes array */
4655 cups_array_t *exclude) /* I - Private attributes array */
4656 {
4657 char job_uri[HTTP_MAX_URI]; /* Job URI */
4658
4659
4660 /*
4661 * Send the requested attributes for each job...
4662 */
4663
4664 if (!cupsArrayFind(exclude, "all"))
4665 {
4666 if ((!exclude || !cupsArrayFind(exclude, "number-of-documents")) &&
4667 (!ra || cupsArrayFind(ra, "number-of-documents")))
4668 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
4669 "number-of-documents", job->num_files);
4670
4671 if ((!exclude || !cupsArrayFind(exclude, "job-media-progress")) &&
4672 (!ra || cupsArrayFind(ra, "job-media-progress")))
4673 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
4674 "job-media-progress", job->progress);
4675
4676 if ((!exclude || !cupsArrayFind(exclude, "job-more-info")) &&
4677 (!ra || cupsArrayFind(ra, "job-more-info")))
4678 {
4679 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "http",
4680 NULL, con->clientname, con->clientport, "/jobs/%d",
4681 job->id);
4682 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4683 "job-more-info", NULL, job_uri);
4684 }
4685
4686 if (job->state_value > IPP_JOB_PROCESSING &&
4687 (!exclude || !cupsArrayFind(exclude, "job-preserved")) &&
4688 (!ra || cupsArrayFind(ra, "job-preserved")))
4689 ippAddBoolean(con->response, IPP_TAG_JOB, "job-preserved",
4690 job->num_files > 0);
4691
4692 if ((!exclude || !cupsArrayFind(exclude, "job-printer-up-time")) &&
4693 (!ra || cupsArrayFind(ra, "job-printer-up-time")))
4694 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
4695 "job-printer-up-time", time(NULL));
4696 }
4697
4698 if (!ra || cupsArrayFind(ra, "job-printer-uri"))
4699 {
4700 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
4701 con->clientname, con->clientport,
4702 (job->dtype & CUPS_PRINTER_CLASS) ? "/classes/%s" :
4703 "/printers/%s",
4704 job->dest);
4705 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4706 "job-printer-uri", NULL, job_uri);
4707 }
4708
4709 if (!ra || cupsArrayFind(ra, "job-uri"))
4710 {
4711 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
4712 con->clientname, con->clientport, "/jobs/%d",
4713 job->id);
4714 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4715 "job-uri", NULL, job_uri);
4716 }
4717
4718 if (job->attrs)
4719 {
4720 copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0, exclude);
4721 }
4722 else
4723 {
4724 /*
4725 * Generate attributes from the job structure...
4726 */
4727
4728 if (!ra || cupsArrayFind(ra, "job-id"))
4729 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
4730
4731 if (!ra || cupsArrayFind(ra, "job-k-octets"))
4732 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-k-octets", job->koctets);
4733
4734 if (job->name && (!ra || cupsArrayFind(ra, "job-name")))
4735 ippAddString(con->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_NAME), "job-name", NULL, job->name);
4736
4737 if (job->username && (!ra || cupsArrayFind(ra, "job-originating-user-name")))
4738 ippAddString(con->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_NAME), "job-originating-user-name", NULL, job->username);
4739
4740 if (!ra || cupsArrayFind(ra, "job-state"))
4741 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state_value);
4742
4743 if (!ra || cupsArrayFind(ra, "job-state-reasons"))
4744 {
4745 switch (job->state_value)
4746 {
4747 default : /* Should never get here for processing, pending, held, or stopped jobs since they don't get unloaded... */
4748 break;
4749 case IPP_JSTATE_ABORTED :
4750 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-aborted-by-system");
4751 break;
4752 case IPP_JSTATE_CANCELED :
4753 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-canceled-by-user");
4754 break;
4755 case IPP_JSTATE_COMPLETED :
4756 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-completed-successfully");
4757 break;
4758 }
4759 }
4760
4761 if (job->completed_time && (!ra || cupsArrayFind(ra, "time-at-completed")))
4762 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-completed", (int)job->completed_time);
4763
4764 if (job->completed_time && (!ra || cupsArrayFind(ra, "time-at-creation")))
4765 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", (int)job->creation_time);
4766 }
4767 }
4768
4769
4770 /*
4771 * 'copy_printer_attrs()' - Copy printer attributes.
4772 */
4773
4774 static void
4775 copy_printer_attrs(
4776 cupsd_client_t *con, /* I - Client connection */
4777 cupsd_printer_t *printer, /* I - Printer */
4778 cups_array_t *ra) /* I - Requested attributes array */
4779 {
4780 char printer_uri[HTTP_MAX_URI];
4781 /* Printer URI */
4782 char printer_icons[HTTP_MAX_URI];
4783 /* Printer icons */
4784 time_t curtime; /* Current time */
4785 int i; /* Looping var */
4786
4787
4788 /*
4789 * Copy the printer attributes to the response using requested-attributes
4790 * and document-format attributes that may be provided by the client.
4791 */
4792
4793 curtime = time(NULL);
4794
4795 if (!ra || cupsArrayFind(ra, "marker-change-time"))
4796 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4797 "marker-change-time", printer->marker_time);
4798
4799 if (printer->num_printers > 0 &&
4800 (!ra || cupsArrayFind(ra, "member-uris")))
4801 {
4802 ipp_attribute_t *member_uris; /* member-uris attribute */
4803 cupsd_printer_t *p2; /* Printer in class */
4804 ipp_attribute_t *p2_uri; /* printer-uri-supported for class printer */
4805
4806
4807 if ((member_uris = ippAddStrings(con->response, IPP_TAG_PRINTER,
4808 IPP_TAG_URI, "member-uris",
4809 printer->num_printers, NULL,
4810 NULL)) != NULL)
4811 {
4812 for (i = 0; i < printer->num_printers; i ++)
4813 {
4814 p2 = printer->printers[i];
4815
4816 if ((p2_uri = ippFindAttribute(p2->attrs, "printer-uri-supported",
4817 IPP_TAG_URI)) != NULL)
4818 member_uris->values[i].string.text =
4819 _cupsStrRetain(p2_uri->values[0].string.text);
4820 else
4821 {
4822 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri,
4823 sizeof(printer_uri), "ipp", NULL, con->clientname,
4824 con->clientport,
4825 (p2->type & CUPS_PRINTER_CLASS) ?
4826 "/classes/%s" : "/printers/%s", p2->name);
4827 member_uris->values[i].string.text = _cupsStrAlloc(printer_uri);
4828 }
4829 }
4830 }
4831 }
4832
4833 if (printer->alert && (!ra || cupsArrayFind(ra, "printer-alert")))
4834 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_STRING,
4835 "printer-alert", NULL, printer->alert);
4836
4837 if (printer->alert_description &&
4838 (!ra || cupsArrayFind(ra, "printer-alert-description")))
4839 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
4840 "printer-alert-description", NULL,
4841 printer->alert_description);
4842
4843 if (!ra || cupsArrayFind(ra, "printer-current-time"))
4844 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
4845 ippTimeToDate(curtime));
4846
4847 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
4848 if (!ra || cupsArrayFind(ra, "printer-dns-sd-name"))
4849 {
4850 if (printer->reg_name)
4851 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4852 "printer-dns-sd-name", NULL, printer->reg_name);
4853 else
4854 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
4855 "printer-dns-sd-name", 0);
4856 }
4857 #endif /* HAVE_DNSSD || HAVE_AVAHI */
4858
4859 if (!ra || cupsArrayFind(ra, "printer-error-policy"))
4860 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4861 "printer-error-policy", NULL, printer->error_policy);
4862
4863 if (!ra || cupsArrayFind(ra, "printer-error-policy-supported"))
4864 {
4865 static const char * const errors[] =/* printer-error-policy-supported values */
4866 {
4867 "abort-job",
4868 "retry-current-job",
4869 "retry-job",
4870 "stop-printer"
4871 };
4872
4873 if (printer->type & CUPS_PRINTER_CLASS)
4874 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
4875 "printer-error-policy-supported", NULL, "retry-current-job");
4876 else
4877 ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
4878 "printer-error-policy-supported",
4879 sizeof(errors) / sizeof(errors[0]), NULL, errors);
4880 }
4881
4882 if (!ra || cupsArrayFind(ra, "printer-icons"))
4883 {
4884 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_icons, sizeof(printer_icons),
4885 "http", NULL, con->clientname, con->clientport,
4886 "/icons/%s.png", printer->name);
4887 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons",
4888 NULL, printer_icons);
4889 cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-icons=\"%s\"", printer_icons);
4890 }
4891
4892 if (!ra || cupsArrayFind(ra, "printer-is-accepting-jobs"))
4893 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", (char)printer->accepting);
4894
4895 if (!ra || cupsArrayFind(ra, "printer-is-shared"))
4896 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-shared", (char)printer->shared);
4897
4898 if (!ra || cupsArrayFind(ra, "printer-more-info"))
4899 {
4900 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
4901 "http", NULL, con->clientname, con->clientport,
4902 (printer->type & CUPS_PRINTER_CLASS) ?
4903 "/classes/%s" : "/printers/%s", printer->name);
4904 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
4905 "printer-more-info", NULL, printer_uri);
4906 }
4907
4908 if (!ra || cupsArrayFind(ra, "printer-op-policy"))
4909 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4910 "printer-op-policy", NULL, printer->op_policy);
4911
4912 if (!ra || cupsArrayFind(ra, "printer-state"))
4913 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
4914 printer->state);
4915
4916 if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
4917 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4918 "printer-state-change-time", printer->state_time);
4919
4920 if (!ra || cupsArrayFind(ra, "printer-state-message"))
4921 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
4922 "printer-state-message", NULL, printer->state_message);
4923
4924 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
4925 add_printer_state_reasons(con, printer);
4926
4927 if (!ra || cupsArrayFind(ra, "printer-type"))
4928 {
4929 cups_ptype_t type; /* printer-type value */
4930
4931 /*
4932 * Add the CUPS-specific printer-type attribute...
4933 */
4934
4935 type = printer->type;
4936
4937 if (printer == DefaultPrinter)
4938 type |= CUPS_PRINTER_DEFAULT;
4939
4940 if (!printer->accepting)
4941 type |= CUPS_PRINTER_REJECTING;
4942
4943 if (!printer->shared)
4944 type |= CUPS_PRINTER_NOT_SHARED;
4945
4946 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-type", (int)type);
4947 }
4948
4949 if (!ra || cupsArrayFind(ra, "printer-up-time"))
4950 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4951 "printer-up-time", curtime);
4952
4953 if (!ra || cupsArrayFind(ra, "printer-uri-supported"))
4954 {
4955 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
4956 "ipp", NULL, con->clientname, con->clientport,
4957 (printer->type & CUPS_PRINTER_CLASS) ?
4958 "/classes/%s" : "/printers/%s", printer->name);
4959 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
4960 "printer-uri-supported", NULL, printer_uri);
4961 cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-uri-supported=\"%s\"",
4962 printer_uri);
4963 }
4964
4965 if (!ra || cupsArrayFind(ra, "queued-job-count"))
4966 add_queued_job_count(con, printer);
4967
4968 copy_attrs(con->response, printer->attrs, ra, IPP_TAG_ZERO, 0, NULL);
4969 if (printer->ppd_attrs)
4970 copy_attrs(con->response, printer->ppd_attrs, ra, IPP_TAG_ZERO, 0, NULL);
4971 copy_attrs(con->response, CommonData, ra, IPP_TAG_ZERO, IPP_TAG_COPY, NULL);
4972 }
4973
4974
4975 /*
4976 * 'copy_subscription_attrs()' - Copy subscription attributes.
4977 */
4978
4979 static void
4980 copy_subscription_attrs(
4981 cupsd_client_t *con, /* I - Client connection */
4982 cupsd_subscription_t *sub, /* I - Subscription */
4983 cups_array_t *ra, /* I - Requested attributes array */
4984 cups_array_t *exclude) /* I - Private attributes array */
4985 {
4986 ipp_attribute_t *attr; /* Current attribute */
4987 char printer_uri[HTTP_MAX_URI];
4988 /* Printer URI */
4989 int count; /* Number of events */
4990 unsigned mask; /* Current event mask */
4991 const char *name; /* Current event name */
4992
4993
4994 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4995 "copy_subscription_attrs(con=%p, sub=%p, ra=%p, exclude=%p)",
4996 con, sub, ra, exclude);
4997
4998 /*
4999 * Copy the subscription attributes to the response using the
5000 * requested-attributes attribute that may be provided by the client.
5001 */
5002
5003 if (!exclude || !cupsArrayFind(exclude, "all"))
5004 {
5005 if ((!exclude || !cupsArrayFind(exclude, "notify-events")) &&
5006 (!ra || cupsArrayFind(ra, "notify-events")))
5007 {
5008 cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_subscription_attrs: notify-events");
5009
5010 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
5011 {
5012 /*
5013 * Simple event list...
5014 */
5015
5016 ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
5017 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
5018 "notify-events", NULL, name);
5019 }
5020 else
5021 {
5022 /*
5023 * Complex event list...
5024 */
5025
5026 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
5027 if (sub->mask & mask)
5028 count ++;
5029
5030 attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
5031 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
5032 "notify-events", count, NULL, NULL);
5033
5034 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
5035 if (sub->mask & mask)
5036 {
5037 attr->values[count].string.text =
5038 (char *)cupsdEventName((cupsd_eventmask_t)mask);
5039
5040 count ++;
5041 }
5042 }
5043 }
5044
5045 if ((!exclude || !cupsArrayFind(exclude, "notify-lease-duration")) &&
5046 (!sub->job && (!ra || cupsArrayFind(ra, "notify-lease-duration"))))
5047 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5048 "notify-lease-duration", sub->lease);
5049
5050 if ((!exclude || !cupsArrayFind(exclude, "notify-recipient-uri")) &&
5051 (sub->recipient && (!ra || cupsArrayFind(ra, "notify-recipient-uri"))))
5052 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5053 "notify-recipient-uri", NULL, sub->recipient);
5054 else if ((!exclude || !cupsArrayFind(exclude, "notify-pull-method")) &&
5055 (!ra || cupsArrayFind(ra, "notify-pull-method")))
5056 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
5057 "notify-pull-method", NULL, "ippget");
5058
5059 if ((!exclude || !cupsArrayFind(exclude, "notify-subscriber-user-name")) &&
5060 (!ra || cupsArrayFind(ra, "notify-subscriber-user-name")))
5061 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
5062 "notify-subscriber-user-name", NULL, sub->owner);
5063
5064 if ((!exclude || !cupsArrayFind(exclude, "notify-time-interval")) &&
5065 (!ra || cupsArrayFind(ra, "notify-time-interval")))
5066 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5067 "notify-time-interval", sub->interval);
5068
5069 if (sub->user_data_len > 0 &&
5070 (!exclude || !cupsArrayFind(exclude, "notify-user-data")) &&
5071 (!ra || cupsArrayFind(ra, "notify-user-data")))
5072 ippAddOctetString(con->response, IPP_TAG_SUBSCRIPTION, "notify-user-data",
5073 sub->user_data, sub->user_data_len);
5074 }
5075
5076 if (sub->job && (!ra || cupsArrayFind(ra, "notify-job-id")))
5077 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5078 "notify-job-id", sub->job->id);
5079
5080 if (sub->dest && (!ra || cupsArrayFind(ra, "notify-printer-uri")))
5081 {
5082 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
5083 "ipp", NULL, con->clientname, con->clientport,
5084 "/printers/%s", sub->dest->name);
5085 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5086 "notify-printer-uri", NULL, printer_uri);
5087 }
5088
5089 if (!ra || cupsArrayFind(ra, "notify-subscription-id"))
5090 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5091 "notify-subscription-id", sub->id);
5092 }
5093
5094
5095 /*
5096 * 'create_job()' - Print a file to a printer or class.
5097 */
5098
5099 static void
5100 create_job(cupsd_client_t *con, /* I - Client connection */
5101 ipp_attribute_t *uri) /* I - Printer URI */
5102 {
5103 int i; /* Looping var */
5104 cupsd_printer_t *printer; /* Printer */
5105 cupsd_job_t *job; /* New job */
5106 static const char * const forbidden_attrs[] =
5107 { /* List of forbidden attributes */
5108 "compression",
5109 "document-format",
5110 "document-name",
5111 "document-natural-language"
5112 };
5113
5114
5115 cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con,
5116 con->number, uri->values[0].string.text);
5117
5118 /*
5119 * Is the destination valid?
5120 */
5121
5122 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
5123 {
5124 /*
5125 * Bad URI...
5126 */
5127
5128 send_ipp_status(con, IPP_NOT_FOUND,
5129 _("The printer or class does not exist."));
5130 return;
5131 }
5132
5133 /*
5134 * Check for invalid Create-Job attributes and log a warning or error depending
5135 * on whether cupsd is running in "strict conformance" mode...
5136 */
5137
5138 for (i = 0;
5139 i < (int)(sizeof(forbidden_attrs) / sizeof(forbidden_attrs[0]));
5140 i ++)
5141 if (ippFindAttribute(con->request, forbidden_attrs[i], IPP_TAG_ZERO))
5142 {
5143 if (StrictConformance)
5144 {
5145 send_ipp_status(con, IPP_BAD_REQUEST,
5146 _("The '%s' operation attribute cannot be supplied in a "
5147 "Create-Job request."), forbidden_attrs[i]);
5148 return;
5149 }
5150
5151 cupsdLogMessage(CUPSD_LOG_WARN,
5152 "Unexpected '%s' operation attribute in a Create-Job "
5153 "request.", forbidden_attrs[i]);
5154 }
5155
5156 /*
5157 * Create the job object...
5158 */
5159
5160 if ((job = add_job(con, printer, NULL)) == NULL)
5161 return;
5162
5163 job->pending_timeout = 1;
5164
5165 /*
5166 * Save and log the job...
5167 */
5168
5169 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
5170 job->dest, job->username);
5171 }
5172
5173
5174 /*
5175 * 'create_requested_array()' - Create an array for the requested-attributes.
5176 */
5177
5178 static cups_array_t * /* O - Array of attributes or NULL */
5179 create_requested_array(ipp_t *request) /* I - IPP request */
5180 {
5181 cups_array_t *ra; /* Requested attributes array */
5182
5183
5184 /*
5185 * Create the array for standard attributes...
5186 */
5187
5188 ra = ippCreateRequestedArray(request);
5189
5190 /*
5191 * Add CUPS defaults as needed...
5192 */
5193
5194 if (cupsArrayFind(ra, "printer-defaults"))
5195 {
5196 /*
5197 * Include user-set defaults...
5198 */
5199
5200 char *name; /* Option name */
5201
5202 cupsArrayRemove(ra, "printer-defaults");
5203
5204 for (name = (char *)cupsArrayFirst(CommonDefaults);
5205 name;
5206 name = (char *)cupsArrayNext(CommonDefaults))
5207 if (!cupsArrayFind(ra, name))
5208 cupsArrayAdd(ra, name);
5209 }
5210
5211 return (ra);
5212 }
5213
5214
5215 /*
5216 * 'create_subscriptions()' - Create one or more notification subscriptions.
5217 */
5218
5219 static void
5220 create_subscriptions(
5221 cupsd_client_t *con, /* I - Client connection */
5222 ipp_attribute_t *uri) /* I - Printer URI */
5223 {
5224 http_status_t status; /* Policy status */
5225 int i; /* Looping var */
5226 ipp_attribute_t *attr; /* Current attribute */
5227 cups_ptype_t dtype; /* Destination type (printer/class) */
5228 char scheme[HTTP_MAX_URI],
5229 /* Scheme portion of URI */
5230 userpass[HTTP_MAX_URI],
5231 /* Username portion of URI */
5232 host[HTTP_MAX_URI],
5233 /* Host portion of URI */
5234 resource[HTTP_MAX_URI];
5235 /* Resource portion of URI */
5236 int port; /* Port portion of URI */
5237 cupsd_printer_t *printer; /* Printer/class */
5238 cupsd_job_t *job; /* Job */
5239 int jobid; /* Job ID */
5240 cupsd_subscription_t *sub; /* Subscription object */
5241 const char *username, /* requesting-user-name or
5242 authenticated username */
5243 *recipient, /* notify-recipient-uri */
5244 *pullmethod; /* notify-pull-method */
5245 ipp_attribute_t *user_data; /* notify-user-data */
5246 int interval, /* notify-time-interval */
5247 lease; /* notify-lease-duration */
5248 unsigned mask; /* notify-events */
5249 ipp_attribute_t *notify_events,/* notify-events(-default) */
5250 *notify_lease; /* notify-lease-duration(-default) */
5251
5252
5253 #ifdef DEBUG
5254 for (attr = con->request->attrs; attr; attr = attr->next)
5255 {
5256 if (attr->group_tag != IPP_TAG_ZERO)
5257 cupsdLogMessage(CUPSD_LOG_DEBUG2, "g%04x v%04x %s", attr->group_tag,
5258 attr->value_tag, attr->name);
5259 else
5260 cupsdLogMessage(CUPSD_LOG_DEBUG2, "----SEP----");
5261 }
5262 #endif /* DEBUG */
5263
5264 /*
5265 * Is the destination valid?
5266 */
5267
5268 cupsdLogMessage(CUPSD_LOG_DEBUG, "create_subscriptions(con=%p(%d), uri=\"%s\")", con, con->number, uri->values[0].string.text);
5269
5270 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
5271 sizeof(scheme), userpass, sizeof(userpass), host,
5272 sizeof(host), &port, resource, sizeof(resource));
5273
5274 if (!strcmp(resource, "/"))
5275 {
5276 dtype = (cups_ptype_t)0;
5277 printer = NULL;
5278 }
5279 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
5280 {
5281 dtype = (cups_ptype_t)0;
5282 printer = NULL;
5283 }
5284 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
5285 {
5286 dtype = CUPS_PRINTER_CLASS;
5287 printer = NULL;
5288 }
5289 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
5290 {
5291 /*
5292 * Bad URI...
5293 */
5294
5295 send_ipp_status(con, IPP_NOT_FOUND,
5296 _("The printer or class does not exist."));
5297 return;
5298 }
5299
5300 /*
5301 * Check policy...
5302 */
5303
5304 if (printer)
5305 {
5306 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
5307 NULL)) != HTTP_OK)
5308 {
5309 send_http_error(con, status, printer);
5310 return;
5311 }
5312 }
5313 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5314 {
5315 send_http_error(con, status, NULL);
5316 return;
5317 }
5318
5319 /*
5320 * Get the user that is requesting the subscription...
5321 */
5322
5323 username = get_username(con);
5324
5325 /*
5326 * Find the first subscription group attribute; return if we have
5327 * none...
5328 */
5329
5330 for (attr = con->request->attrs; attr; attr = attr->next)
5331 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
5332 break;
5333
5334 if (!attr)
5335 {
5336 send_ipp_status(con, IPP_BAD_REQUEST,
5337 _("No subscription attributes in request."));
5338 return;
5339 }
5340
5341 /*
5342 * Process the subscription attributes in the request...
5343 */
5344
5345 con->response->request.status.status_code = IPP_BAD_REQUEST;
5346
5347 while (attr)
5348 {
5349 recipient = NULL;
5350 pullmethod = NULL;
5351 user_data = NULL;
5352 interval = 0;
5353 lease = DefaultLeaseDuration;
5354 jobid = 0;
5355 mask = CUPSD_EVENT_NONE;
5356
5357 if (printer)
5358 {
5359 notify_events = ippFindAttribute(printer->attrs, "notify-events-default",
5360 IPP_TAG_KEYWORD);
5361 notify_lease = ippFindAttribute(printer->attrs,
5362 "notify-lease-duration-default",
5363 IPP_TAG_INTEGER);
5364
5365 if (notify_lease)
5366 lease = notify_lease->values[0].integer;
5367 }
5368 else
5369 {
5370 notify_events = NULL;
5371 notify_lease = NULL;
5372 }
5373
5374 while (attr && attr->group_tag != IPP_TAG_ZERO)
5375 {
5376 if (!strcmp(attr->name, "notify-recipient-uri") &&
5377 attr->value_tag == IPP_TAG_URI)
5378 {
5379 /*
5380 * Validate the recipient scheme against the ServerBin/notifier
5381 * directory...
5382 */
5383
5384 char notifier[1024]; /* Notifier filename */
5385
5386
5387 recipient = attr->values[0].string.text;
5388
5389 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
5390 scheme, sizeof(scheme), userpass, sizeof(userpass),
5391 host, sizeof(host), &port,
5392 resource, sizeof(resource)) < HTTP_URI_OK)
5393 {
5394 send_ipp_status(con, IPP_NOT_POSSIBLE,
5395 _("Bad notify-recipient-uri \"%s\"."), recipient);
5396 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5397 "notify-status-code", IPP_URI_SCHEME);
5398 return;
5399 }
5400
5401 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
5402 scheme);
5403 if (access(notifier, X_OK))
5404 {
5405 send_ipp_status(con, IPP_NOT_POSSIBLE,
5406 _("notify-recipient-uri URI \"%s\" uses unknown "
5407 "scheme."), recipient);
5408 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5409 "notify-status-code", IPP_URI_SCHEME);
5410 return;
5411 }
5412
5413 if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
5414 {
5415 send_ipp_status(con, IPP_NOT_POSSIBLE,
5416 _("notify-recipient-uri URI \"%s\" is already used."),
5417 recipient);
5418 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5419 "notify-status-code", IPP_ATTRIBUTES);
5420 return;
5421 }
5422 }
5423 else if (!strcmp(attr->name, "notify-pull-method") &&
5424 attr->value_tag == IPP_TAG_KEYWORD)
5425 {
5426 pullmethod = attr->values[0].string.text;
5427
5428 if (strcmp(pullmethod, "ippget"))
5429 {
5430 send_ipp_status(con, IPP_NOT_POSSIBLE,
5431 _("Bad notify-pull-method \"%s\"."), pullmethod);
5432 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5433 "notify-status-code", IPP_ATTRIBUTES);
5434 return;
5435 }
5436 }
5437 else if (!strcmp(attr->name, "notify-charset") &&
5438 attr->value_tag == IPP_TAG_CHARSET &&
5439 strcmp(attr->values[0].string.text, "us-ascii") &&
5440 strcmp(attr->values[0].string.text, "utf-8"))
5441 {
5442 send_ipp_status(con, IPP_CHARSET,
5443 _("Character set \"%s\" not supported."),
5444 attr->values[0].string.text);
5445 return;
5446 }
5447 else if (!strcmp(attr->name, "notify-natural-language") &&
5448 (attr->value_tag != IPP_TAG_LANGUAGE ||
5449 strcmp(attr->values[0].string.text, DefaultLanguage)))
5450 {
5451 send_ipp_status(con, IPP_CHARSET,
5452 _("Language \"%s\" not supported."),
5453 attr->values[0].string.text);
5454 return;
5455 }
5456 else if (!strcmp(attr->name, "notify-user-data") &&
5457 attr->value_tag == IPP_TAG_STRING)
5458 {
5459 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
5460 {
5461 send_ipp_status(con, IPP_REQUEST_VALUE,
5462 _("The notify-user-data value is too large "
5463 "(%d > 63 octets)."),
5464 attr->values[0].unknown.length);
5465 return;
5466 }
5467
5468 user_data = attr;
5469 }
5470 else if (!strcmp(attr->name, "notify-events") &&
5471 attr->value_tag == IPP_TAG_KEYWORD)
5472 notify_events = attr;
5473 else if (!strcmp(attr->name, "notify-lease-duration") &&
5474 attr->value_tag == IPP_TAG_INTEGER)
5475 lease = attr->values[0].integer;
5476 else if (!strcmp(attr->name, "notify-time-interval") &&
5477 attr->value_tag == IPP_TAG_INTEGER)
5478 interval = attr->values[0].integer;
5479 else if (!strcmp(attr->name, "notify-job-id") &&
5480 attr->value_tag == IPP_TAG_INTEGER)
5481 jobid = attr->values[0].integer;
5482
5483 attr = attr->next;
5484 }
5485
5486 if (notify_events)
5487 {
5488 for (i = 0; i < notify_events->num_values; i ++)
5489 mask |= cupsdEventValue(notify_events->values[i].string.text);
5490 }
5491
5492 if (recipient)
5493 cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
5494 if (pullmethod)
5495 cupsdLogMessage(CUPSD_LOG_DEBUG, "pullmethod=\"%s\"", pullmethod);
5496 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-lease-duration=%d", lease);
5497 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-time-interval=%d", interval);
5498
5499 if (!recipient && !pullmethod)
5500 break;
5501
5502 if (mask == CUPSD_EVENT_NONE)
5503 {
5504 if (jobid)
5505 mask = CUPSD_EVENT_JOB_COMPLETED;
5506 else if (printer)
5507 mask = CUPSD_EVENT_PRINTER_STATE_CHANGED;
5508 else
5509 {
5510 send_ipp_status(con, IPP_BAD_REQUEST,
5511 _("notify-events not specified."));
5512 return;
5513 }
5514 }
5515
5516 if (MaxLeaseDuration && (lease == 0 || lease > MaxLeaseDuration))
5517 {
5518 cupsdLogMessage(CUPSD_LOG_INFO,
5519 "create_subscriptions: Limiting notify-lease-duration to "
5520 "%d seconds.",
5521 MaxLeaseDuration);
5522 lease = MaxLeaseDuration;
5523 }
5524
5525 if (jobid)
5526 {
5527 if ((job = cupsdFindJob(jobid)) == NULL)
5528 {
5529 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
5530 jobid);
5531 return;
5532 }
5533 }
5534 else
5535 job = NULL;
5536
5537 if ((sub = cupsdAddSubscription(mask, printer, job, recipient, 0)) == NULL)
5538 {
5539 send_ipp_status(con, IPP_TOO_MANY_SUBSCRIPTIONS,
5540 _("There are too many subscriptions."));
5541 return;
5542 }
5543
5544 if (job)
5545 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for job %d.",
5546 sub->id, job->id);
5547 else if (printer)
5548 cupsdLogMessage(CUPSD_LOG_DEBUG,
5549 "Added subscription #%d for printer \"%s\".",
5550 sub->id, printer->name);
5551 else
5552 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for server.",
5553 sub->id);
5554
5555 sub->interval = interval;
5556 sub->lease = lease;
5557 sub->expire = lease ? time(NULL) + lease : 0;
5558
5559 cupsdSetString(&sub->owner, username);
5560
5561 if (user_data)
5562 {
5563 sub->user_data_len = user_data->values[0].unknown.length;
5564 memcpy(sub->user_data, user_data->values[0].unknown.data,
5565 (size_t)sub->user_data_len);
5566 }
5567
5568 ippAddSeparator(con->response);
5569 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5570 "notify-subscription-id", sub->id);
5571
5572 con->response->request.status.status_code = IPP_OK;
5573
5574 if (attr)
5575 attr = attr->next;
5576 }
5577
5578 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
5579 }
5580
5581
5582 /*
5583 * 'delete_printer()' - Remove a printer or class from the system.
5584 */
5585
5586 static void
5587 delete_printer(cupsd_client_t *con, /* I - Client connection */
5588 ipp_attribute_t *uri) /* I - URI of printer or class */
5589 {
5590 http_status_t status; /* Policy status */
5591 cups_ptype_t dtype; /* Destination type (printer/class) */
5592 cupsd_printer_t *printer; /* Printer/class */
5593 char filename[1024]; /* Script/PPD filename */
5594
5595
5596 cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", con,
5597 con->number, uri->values[0].string.text);
5598
5599 /*
5600 * Do we have a valid URI?
5601 */
5602
5603 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
5604 {
5605 /*
5606 * Bad URI...
5607 */
5608
5609 send_ipp_status(con, IPP_NOT_FOUND,
5610 _("The printer or class does not exist."));
5611 return;
5612 }
5613
5614 /*
5615 * Check policy...
5616 */
5617
5618 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5619 {
5620 send_http_error(con, status, NULL);
5621 return;
5622 }
5623
5624 /*
5625 * Remove old jobs...
5626 */
5627
5628 cupsdCancelJobs(printer->name, NULL, 1);
5629
5630 /*
5631 * Remove old subscriptions and send a "deleted printer" event...
5632 */
5633
5634 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
5635 "%s \"%s\" deleted by \"%s\".",
5636 (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
5637 printer->name, get_username(con));
5638
5639 cupsdExpireSubscriptions(printer, NULL);
5640
5641 /*
5642 * Remove any old PPD or script files...
5643 */
5644
5645 snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot,
5646 printer->name);
5647 unlink(filename);
5648 snprintf(filename, sizeof(filename), "%s/interfaces/%s.O", ServerRoot,
5649 printer->name);
5650 unlink(filename);
5651
5652 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
5653 printer->name);
5654 unlink(filename);
5655 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd.O", ServerRoot,
5656 printer->name);
5657 unlink(filename);
5658
5659 snprintf(filename, sizeof(filename), "%s/%s.png", CacheDir, printer->name);
5660 unlink(filename);
5661
5662 snprintf(filename, sizeof(filename), "%s/%s.data", CacheDir, printer->name);
5663 unlink(filename);
5664
5665 /*
5666 * Unregister color profiles...
5667 */
5668
5669 cupsdUnregisterColor(printer);
5670
5671 if (dtype & CUPS_PRINTER_CLASS)
5672 {
5673 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".",
5674 printer->name, get_username(con));
5675
5676 cupsdDeletePrinter(printer, 0);
5677 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
5678 }
5679 else
5680 {
5681 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".",
5682 printer->name, get_username(con));
5683
5684 if (cupsdDeletePrinter(printer, 0))
5685 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
5686
5687 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
5688 }
5689
5690 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
5691
5692 /*
5693 * Return with no errors...
5694 */
5695
5696 con->response->request.status.status_code = IPP_OK;
5697 }
5698
5699
5700 /*
5701 * 'get_default()' - Get the default destination.
5702 */
5703
5704 static void
5705 get_default(cupsd_client_t *con) /* I - Client connection */
5706 {
5707 http_status_t status; /* Policy status */
5708 cups_array_t *ra; /* Requested attributes array */
5709
5710
5711 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->number);
5712
5713 /*
5714 * Check policy...
5715 */
5716
5717 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5718 {
5719 send_http_error(con, status, NULL);
5720 return;
5721 }
5722
5723 if (DefaultPrinter)
5724 {
5725 ra = create_requested_array(con->request);
5726
5727 copy_printer_attrs(con, DefaultPrinter, ra);
5728
5729 cupsArrayDelete(ra);
5730
5731 con->response->request.status.status_code = IPP_OK;
5732 }
5733 else
5734 send_ipp_status(con, IPP_NOT_FOUND, _("No default printer."));
5735 }
5736
5737
5738 /*
5739 * 'get_devices()' - Get the list of available devices on the local system.
5740 */
5741
5742 static void
5743 get_devices(cupsd_client_t *con) /* I - Client connection */
5744 {
5745 http_status_t status; /* Policy status */
5746 ipp_attribute_t *limit, /* limit attribute */
5747 *timeout, /* timeout attribute */
5748 *requested, /* requested-attributes attribute */
5749 *exclude, /* exclude-schemes attribute */
5750 *include; /* include-schemes attribute */
5751 char command[1024], /* cups-deviced command */
5752 options[2048], /* Options to pass to command */
5753 requested_str[256],
5754 /* String for requested attributes */
5755 exclude_str[512],
5756 /* String for excluded schemes */
5757 include_str[512];
5758 /* String for included schemes */
5759
5760
5761 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->number);
5762
5763 /*
5764 * Check policy...
5765 */
5766
5767 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5768 {
5769 send_http_error(con, status, NULL);
5770 return;
5771 }
5772
5773 /*
5774 * Run cups-deviced command with the given options...
5775 */
5776
5777 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
5778 timeout = ippFindAttribute(con->request, "timeout", IPP_TAG_INTEGER);
5779 requested = ippFindAttribute(con->request, "requested-attributes",
5780 IPP_TAG_KEYWORD);
5781 exclude = ippFindAttribute(con->request, "exclude-schemes", IPP_TAG_NAME);
5782 include = ippFindAttribute(con->request, "include-schemes", IPP_TAG_NAME);
5783
5784 if (requested)
5785 url_encode_attr(requested, requested_str, sizeof(requested_str));
5786 else
5787 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
5788
5789 if (exclude)
5790 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
5791 else
5792 exclude_str[0] = '\0';
5793
5794 if (include)
5795 url_encode_attr(include, include_str, sizeof(include_str));
5796 else
5797 include_str[0] = '\0';
5798
5799 snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
5800 snprintf(options, sizeof(options),
5801 "%d+%d+%d+%d+%s%s%s%s%s",
5802 con->request->request.op.request_id,
5803 limit ? limit->values[0].integer : 0,
5804 timeout ? timeout->values[0].integer : 15,
5805 (int)User,
5806 requested_str,
5807 exclude_str[0] ? "%20" : "", exclude_str,
5808 include_str[0] ? "%20" : "", include_str);
5809
5810 if (cupsdSendCommand(con, command, options, 1))
5811 {
5812 /*
5813 * Command started successfully, don't send an IPP response here...
5814 */
5815
5816 ippDelete(con->response);
5817 con->response = NULL;
5818 }
5819 else
5820 {
5821 /*
5822 * Command failed, return "internal error" so the user knows something
5823 * went wrong...
5824 */
5825
5826 send_ipp_status(con, IPP_INTERNAL_ERROR,
5827 _("cups-deviced failed to execute."));
5828 }
5829 }
5830
5831
5832 /*
5833 * 'get_document()' - Get a copy of a job file.
5834 */
5835
5836 static void
5837 get_document(cupsd_client_t *con, /* I - Client connection */
5838 ipp_attribute_t *uri) /* I - Job URI */
5839 {
5840 http_status_t status; /* Policy status */
5841 ipp_attribute_t *attr; /* Current attribute */
5842 int jobid; /* Job ID */
5843 int docnum; /* Document number */
5844 cupsd_job_t *job; /* Current job */
5845 char scheme[HTTP_MAX_URI], /* Method portion of URI */
5846 username[HTTP_MAX_URI], /* Username portion of URI */
5847 host[HTTP_MAX_URI], /* Host portion of URI */
5848 resource[HTTP_MAX_URI]; /* Resource portion of URI */
5849 int port; /* Port portion of URI */
5850 char filename[1024], /* Filename for document */
5851 format[1024]; /* Format for document */
5852
5853
5854 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", con,
5855 con->number, uri->values[0].string.text);
5856
5857 /*
5858 * See if we have a job URI or a printer URI...
5859 */
5860
5861 if (!strcmp(uri->name, "printer-uri"))
5862 {
5863 /*
5864 * Got a printer URI; see if we also have a job-id attribute...
5865 */
5866
5867 if ((attr = ippFindAttribute(con->request, "job-id",
5868 IPP_TAG_INTEGER)) == NULL)
5869 {
5870 send_ipp_status(con, IPP_BAD_REQUEST,
5871 _("Got a printer-uri attribute but no job-id."));
5872 return;
5873 }
5874
5875 jobid = attr->values[0].integer;
5876 }
5877 else
5878 {
5879 /*
5880 * Got a job URI; parse it to get the job ID...
5881 */
5882
5883 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
5884 sizeof(scheme), username, sizeof(username), host,
5885 sizeof(host), &port, resource, sizeof(resource));
5886
5887 if (strncmp(resource, "/jobs/", 6))
5888 {
5889 /*
5890 * Not a valid URI!
5891 */
5892
5893 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
5894 uri->values[0].string.text);
5895 return;
5896 }
5897
5898 jobid = atoi(resource + 6);
5899 }
5900
5901 /*
5902 * See if the job exists...
5903 */
5904
5905 if ((job = cupsdFindJob(jobid)) == NULL)
5906 {
5907 /*
5908 * Nope - return a "not found" error...
5909 */
5910
5911 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
5912 return;
5913 }
5914
5915 /*
5916 * Check policy...
5917 */
5918
5919 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con,
5920 job->username)) != HTTP_OK)
5921 {
5922 send_http_error(con, status, NULL);
5923 return;
5924 }
5925
5926 /*
5927 * Get the document number...
5928 */
5929
5930 if ((attr = ippFindAttribute(con->request, "document-number",
5931 IPP_TAG_INTEGER)) == NULL)
5932 {
5933 send_ipp_status(con, IPP_BAD_REQUEST,
5934 _("Missing document-number attribute."));
5935 return;
5936 }
5937
5938 if ((docnum = attr->values[0].integer) < 1 || docnum > job->num_files ||
5939 attr->num_values > 1)
5940 {
5941 send_ipp_status(con, IPP_NOT_FOUND,
5942 _("Document #%d does not exist in job #%d."), docnum,
5943 jobid);
5944 return;
5945 }
5946
5947 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, jobid,
5948 docnum);
5949 if ((con->file = open(filename, O_RDONLY)) == -1)
5950 {
5951 cupsdLogMessage(CUPSD_LOG_ERROR,
5952 "Unable to open document %d in job %d - %s", docnum, jobid,
5953 strerror(errno));
5954 send_ipp_status(con, IPP_NOT_FOUND,
5955 _("Unable to open document #%d in job #%d."), docnum,
5956 jobid);
5957 return;
5958 }
5959
5960 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
5961
5962 cupsdLoadJob(job);
5963
5964 snprintf(format, sizeof(format), "%s/%s", job->filetypes[docnum - 1]->super,
5965 job->filetypes[docnum - 1]->type);
5966
5967 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
5968 NULL, format);
5969 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "document-number",
5970 docnum);
5971 if ((attr = ippFindAttribute(job->attrs, "document-name",
5972 IPP_TAG_NAME)) != NULL)
5973 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "document-name",
5974 NULL, attr->values[0].string.text);
5975 }
5976
5977
5978 /*
5979 * 'get_job_attrs()' - Get job attributes.
5980 */
5981
5982 static void
5983 get_job_attrs(cupsd_client_t *con, /* I - Client connection */
5984 ipp_attribute_t *uri) /* I - Job URI */
5985 {
5986 http_status_t status; /* Policy status */
5987 ipp_attribute_t *attr; /* Current attribute */
5988 int jobid; /* Job ID */
5989 cupsd_job_t *job; /* Current job */
5990 cupsd_printer_t *printer; /* Current printer */
5991 cupsd_policy_t *policy; /* Current security policy */
5992 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
5993 username[HTTP_MAX_URI], /* Username portion of URI */
5994 host[HTTP_MAX_URI], /* Host portion of URI */
5995 resource[HTTP_MAX_URI]; /* Resource portion of URI */
5996 int port; /* Port portion of URI */
5997 cups_array_t *ra, /* Requested attributes array */
5998 *exclude; /* Private attributes array */
5999
6000
6001 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", con,
6002 con->number, uri->values[0].string.text);
6003
6004 /*
6005 * See if we have a job URI or a printer URI...
6006 */
6007
6008 if (!strcmp(uri->name, "printer-uri"))
6009 {
6010 /*
6011 * Got a printer URI; see if we also have a job-id attribute...
6012 */
6013
6014 if ((attr = ippFindAttribute(con->request, "job-id",
6015 IPP_TAG_INTEGER)) == NULL)
6016 {
6017 send_ipp_status(con, IPP_BAD_REQUEST,
6018 _("Got a printer-uri attribute but no job-id."));
6019 return;
6020 }
6021
6022 jobid = attr->values[0].integer;
6023 }
6024 else
6025 {
6026 /*
6027 * Got a job URI; parse it to get the job ID...
6028 */
6029
6030 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6031 sizeof(scheme), username, sizeof(username), host,
6032 sizeof(host), &port, resource, sizeof(resource));
6033
6034 if (strncmp(resource, "/jobs/", 6))
6035 {
6036 /*
6037 * Not a valid URI!
6038 */
6039
6040 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
6041 uri->values[0].string.text);
6042 return;
6043 }
6044
6045 jobid = atoi(resource + 6);
6046 }
6047
6048 /*
6049 * See if the job exists...
6050 */
6051
6052 if ((job = cupsdFindJob(jobid)) == NULL)
6053 {
6054 /*
6055 * Nope - return a "not found" error...
6056 */
6057
6058 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
6059 return;
6060 }
6061
6062 /*
6063 * Check policy...
6064 */
6065
6066 if ((printer = job->printer) == NULL)
6067 printer = cupsdFindDest(job->dest);
6068
6069 if (printer)
6070 policy = printer->op_policy_ptr;
6071 else
6072 policy = DefaultPolicyPtr;
6073
6074 if ((status = cupsdCheckPolicy(policy, con, job->username)) != HTTP_OK)
6075 {
6076 send_http_error(con, status, NULL);
6077 return;
6078 }
6079
6080 exclude = cupsdGetPrivateAttrs(policy, con, printer, job->username);
6081
6082 /*
6083 * Copy attributes...
6084 */
6085
6086 cupsdLoadJob(job);
6087
6088 ra = create_requested_array(con->request);
6089 copy_job_attrs(con, job, ra, exclude);
6090 cupsArrayDelete(ra);
6091
6092 con->response->request.status.status_code = IPP_OK;
6093 }
6094
6095
6096 /*
6097 * 'get_jobs()' - Get a list of jobs for the specified printer.
6098 */
6099
6100 static void
6101 get_jobs(cupsd_client_t *con, /* I - Client connection */
6102 ipp_attribute_t *uri) /* I - Printer URI */
6103 {
6104 http_status_t status; /* Policy status */
6105 ipp_attribute_t *attr; /* Current attribute */
6106 const char *dest; /* Destination */
6107 cups_ptype_t dtype; /* Destination type (printer/class) */
6108 cups_ptype_t dmask; /* Destination type mask */
6109 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
6110 username[HTTP_MAX_URI], /* Username portion of URI */
6111 host[HTTP_MAX_URI], /* Host portion of URI */
6112 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6113 int port; /* Port portion of URI */
6114 int job_comparison; /* Job comparison */
6115 ipp_jstate_t job_state; /* job-state value */
6116 int first_job_id = 1, /* First job ID */
6117 first_index = 1, /* First index */
6118 current_index = 0; /* Current index */
6119 int limit = 0; /* Maximum number of jobs to return */
6120 int count; /* Number of jobs that match */
6121 int need_load_job = 0; /* Do we need to load the job? */
6122 const char *job_attr; /* Job attribute requested */
6123 ipp_attribute_t *job_ids; /* job-ids attribute */
6124 cupsd_job_t *job; /* Current job pointer */
6125 cupsd_printer_t *printer; /* Printer */
6126 cups_array_t *list; /* Which job list... */
6127 int delete_list = 0; /* Delete the list afterwards? */
6128 cups_array_t *ra, /* Requested attributes array */
6129 *exclude; /* Private attributes array */
6130 cupsd_policy_t *policy; /* Current policy */
6131
6132
6133 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", con, con->number,
6134 uri->values[0].string.text);
6135
6136 /*
6137 * Is the destination valid?
6138 */
6139
6140 if (strcmp(uri->name, "printer-uri"))
6141 {
6142 send_ipp_status(con, IPP_BAD_REQUEST, _("No printer-uri in request."));
6143 return;
6144 }
6145
6146 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6147 sizeof(scheme), username, sizeof(username), host,
6148 sizeof(host), &port, resource, sizeof(resource));
6149
6150 if (!strcmp(resource, "/") || !strcmp(resource, "/jobs"))
6151 {
6152 dest = NULL;
6153 dtype = (cups_ptype_t)0;
6154 dmask = (cups_ptype_t)0;
6155 printer = NULL;
6156 }
6157 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
6158 {
6159 dest = NULL;
6160 dtype = (cups_ptype_t)0;
6161 dmask = CUPS_PRINTER_CLASS;
6162 printer = NULL;
6163 }
6164 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
6165 {
6166 dest = NULL;
6167 dtype = CUPS_PRINTER_CLASS;
6168 dmask = CUPS_PRINTER_CLASS;
6169 printer = NULL;
6170 }
6171 else if ((dest = cupsdValidateDest(uri->values[0].string.text, &dtype,
6172 &printer)) == NULL)
6173 {
6174 /*
6175 * Bad URI...
6176 */
6177
6178 send_ipp_status(con, IPP_NOT_FOUND,
6179 _("The printer or class does not exist."));
6180 return;
6181 }
6182 else
6183 {
6184 dtype &= CUPS_PRINTER_CLASS;
6185 dmask = CUPS_PRINTER_CLASS;
6186 }
6187
6188 /*
6189 * Check policy...
6190 */
6191
6192 if (printer)
6193 policy = printer->op_policy_ptr;
6194 else
6195 policy = DefaultPolicyPtr;
6196
6197 if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
6198 {
6199 send_http_error(con, status, NULL);
6200 return;
6201 }
6202
6203 job_ids = ippFindAttribute(con->request, "job-ids", IPP_TAG_INTEGER);
6204
6205 /*
6206 * See if the "which-jobs" attribute have been specified...
6207 */
6208
6209 if ((attr = ippFindAttribute(con->request, "which-jobs",
6210 IPP_TAG_KEYWORD)) != NULL && job_ids)
6211 {
6212 send_ipp_status(con, IPP_CONFLICT,
6213 _("The %s attribute cannot be provided with job-ids."),
6214 "which-jobs");
6215 return;
6216 }
6217 else if (!attr || !strcmp(attr->values[0].string.text, "not-completed"))
6218 {
6219 job_comparison = -1;
6220 job_state = IPP_JOB_STOPPED;
6221 list = ActiveJobs;
6222 }
6223 else if (!strcmp(attr->values[0].string.text, "completed"))
6224 {
6225 job_comparison = 1;
6226 job_state = IPP_JOB_CANCELED;
6227 list = cupsdGetCompletedJobs(printer);
6228 delete_list = 1;
6229 }
6230 else if (!strcmp(attr->values[0].string.text, "aborted"))
6231 {
6232 job_comparison = 0;
6233 job_state = IPP_JOB_ABORTED;
6234 list = cupsdGetCompletedJobs(printer);
6235 delete_list = 1;
6236 }
6237 else if (!strcmp(attr->values[0].string.text, "all"))
6238 {
6239 job_comparison = 1;
6240 job_state = IPP_JOB_PENDING;
6241 list = Jobs;
6242 }
6243 else if (!strcmp(attr->values[0].string.text, "canceled"))
6244 {
6245 job_comparison = 0;
6246 job_state = IPP_JOB_CANCELED;
6247 list = cupsdGetCompletedJobs(printer);
6248 delete_list = 1;
6249 }
6250 else if (!strcmp(attr->values[0].string.text, "pending"))
6251 {
6252 job_comparison = 0;
6253 job_state = IPP_JOB_PENDING;
6254 list = ActiveJobs;
6255 }
6256 else if (!strcmp(attr->values[0].string.text, "pending-held"))
6257 {
6258 job_comparison = 0;
6259 job_state = IPP_JOB_HELD;
6260 list = ActiveJobs;
6261 }
6262 else if (!strcmp(attr->values[0].string.text, "processing"))
6263 {
6264 job_comparison = 0;
6265 job_state = IPP_JOB_PROCESSING;
6266 list = PrintingJobs;
6267 }
6268 else if (!strcmp(attr->values[0].string.text, "processing-stopped"))
6269 {
6270 job_comparison = 0;
6271 job_state = IPP_JOB_STOPPED;
6272 list = ActiveJobs;
6273 }
6274 else
6275 {
6276 send_ipp_status(con, IPP_ATTRIBUTES,
6277 _("The which-jobs value \"%s\" is not supported."),
6278 attr->values[0].string.text);
6279 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
6280 "which-jobs", NULL, attr->values[0].string.text);
6281 return;
6282 }
6283
6284 /*
6285 * See if they want to limit the number of jobs reported...
6286 */
6287
6288 if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
6289 {
6290 if (job_ids)
6291 {
6292 send_ipp_status(con, IPP_CONFLICT,
6293 _("The %s attribute cannot be provided with job-ids."),
6294 "limit");
6295 return;
6296 }
6297
6298 limit = attr->values[0].integer;
6299 }
6300
6301 if ((attr = ippFindAttribute(con->request, "first-index", IPP_TAG_INTEGER)) != NULL)
6302 {
6303 if (job_ids)
6304 {
6305 send_ipp_status(con, IPP_CONFLICT,
6306 _("The %s attribute cannot be provided with job-ids."),
6307 "first-index");
6308 return;
6309 }
6310
6311 first_index = attr->values[0].integer;
6312 }
6313 else if ((attr = ippFindAttribute(con->request, "first-job-id", IPP_TAG_INTEGER)) != NULL)
6314 {
6315 if (job_ids)
6316 {
6317 send_ipp_status(con, IPP_CONFLICT,
6318 _("The %s attribute cannot be provided with job-ids."),
6319 "first-job-id");
6320 return;
6321 }
6322
6323 first_job_id = attr->values[0].integer;
6324 }
6325
6326 /*
6327 * See if we only want to see jobs for a specific user...
6328 */
6329
6330 if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL && job_ids)
6331 {
6332 send_ipp_status(con, IPP_CONFLICT,
6333 _("The %s attribute cannot be provided with job-ids."),
6334 "my-jobs");
6335 return;
6336 }
6337 else if (attr && attr->values[0].boolean)
6338 strlcpy(username, get_username(con), sizeof(username));
6339 else
6340 username[0] = '\0';
6341
6342 ra = create_requested_array(con->request);
6343 for (job_attr = (char *)cupsArrayFirst(ra); job_attr; job_attr = (char *)cupsArrayNext(ra))
6344 if (strcmp(job_attr, "job-id") &&
6345 strcmp(job_attr, "job-k-octets") &&
6346 strcmp(job_attr, "job-media-progress") &&
6347 strcmp(job_attr, "job-more-info") &&
6348 strcmp(job_attr, "job-name") &&
6349 strcmp(job_attr, "job-originating-user-name") &&
6350 strcmp(job_attr, "job-preserved") &&
6351 strcmp(job_attr, "job-printer-up-time") &&
6352 strcmp(job_attr, "job-printer-uri") &&
6353 strcmp(job_attr, "job-state") &&
6354 strcmp(job_attr, "job-state-reasons") &&
6355 strcmp(job_attr, "job-uri") &&
6356 strcmp(job_attr, "time-at-completed") &&
6357 strcmp(job_attr, "time-at-creation") &&
6358 strcmp(job_attr, "number-of-documents"))
6359 {
6360 need_load_job = 1;
6361 break;
6362 }
6363
6364 if (need_load_job && (limit == 0 || limit > 500) && (list == Jobs || delete_list))
6365 {
6366 /*
6367 * Limit expensive Get-Jobs for job history to 500 jobs...
6368 */
6369
6370 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "limit", 500);
6371
6372 if (limit)
6373 ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER, "limit", limit);
6374
6375 limit = 500;
6376
6377 cupsdLogClient(con, CUPSD_LOG_INFO, "Limiting Get-Jobs response to %d jobs.", limit);
6378 }
6379
6380 /*
6381 * OK, build a list of jobs for this printer...
6382 */
6383
6384 if (job_ids)
6385 {
6386 int i; /* Looping var */
6387
6388 for (i = 0; i < job_ids->num_values; i ++)
6389 {
6390 if (!cupsdFindJob(job_ids->values[i].integer))
6391 break;
6392 }
6393
6394 if (i < job_ids->num_values)
6395 {
6396 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
6397 job_ids->values[i].integer);
6398 return;
6399 }
6400
6401 for (i = 0; i < job_ids->num_values; i ++)
6402 {
6403 job = cupsdFindJob(job_ids->values[i].integer);
6404
6405 if (need_load_job && !job->attrs)
6406 {
6407 cupsdLoadJob(job);
6408
6409 if (!job->attrs)
6410 {
6411 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d", job->id);
6412 continue;
6413 }
6414 }
6415
6416 if (i > 0)
6417 ippAddSeparator(con->response);
6418
6419 exclude = cupsdGetPrivateAttrs(job->printer ?
6420 job->printer->op_policy_ptr :
6421 policy, con, job->printer,
6422 job->username);
6423
6424 copy_job_attrs(con, job, ra, exclude);
6425 }
6426 }
6427 else
6428 {
6429 for (count = 0, job = (cupsd_job_t *)cupsArrayFirst(list);
6430 (limit <= 0 || count < limit) && job;
6431 job = (cupsd_job_t *)cupsArrayNext(list))
6432 {
6433 /*
6434 * Filter out jobs that don't match...
6435 */
6436
6437 cupsdLogMessage(CUPSD_LOG_DEBUG2,
6438 "get_jobs: job->id=%d, dest=\"%s\", username=\"%s\", "
6439 "state_value=%d, attrs=%p", job->id, job->dest,
6440 job->username, job->state_value, job->attrs);
6441
6442 if (!job->dest || !job->username)
6443 cupsdLoadJob(job);
6444
6445 if (!job->dest || !job->username)
6446 continue;
6447
6448 if ((dest && strcmp(job->dest, dest)) &&
6449 (!job->printer || !dest || strcmp(job->printer->name, dest)))
6450 continue;
6451 if ((job->dtype & dmask) != dtype &&
6452 (!job->printer || (job->printer->type & dmask) != dtype))
6453 continue;
6454
6455 if ((job_comparison < 0 && job->state_value > job_state) ||
6456 (job_comparison == 0 && job->state_value != job_state) ||
6457 (job_comparison > 0 && job->state_value < job_state))
6458 continue;
6459
6460 if (job->id < first_job_id)
6461 continue;
6462
6463 current_index ++;
6464 if (current_index < first_index)
6465 continue;
6466
6467 if (need_load_job && !job->attrs)
6468 {
6469 cupsdLoadJob(job);
6470
6471 if (!job->attrs)
6472 {
6473 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d", job->id);
6474 continue;
6475 }
6476 }
6477
6478 if (username[0] && _cups_strcasecmp(username, job->username))
6479 continue;
6480
6481 if (count > 0)
6482 ippAddSeparator(con->response);
6483
6484 count ++;
6485
6486 exclude = cupsdGetPrivateAttrs(job->printer ?
6487 job->printer->op_policy_ptr :
6488 policy, con, job->printer,
6489 job->username);
6490
6491 copy_job_attrs(con, job, ra, exclude);
6492 }
6493
6494 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count=%d", count);
6495 }
6496
6497 cupsArrayDelete(ra);
6498
6499 if (delete_list)
6500 cupsArrayDelete(list);
6501
6502 con->response->request.status.status_code = IPP_OK;
6503 }
6504
6505
6506 /*
6507 * 'get_notifications()' - Get events for a subscription.
6508 */
6509
6510 static void
6511 get_notifications(cupsd_client_t *con) /* I - Client connection */
6512 {
6513 int i, j; /* Looping vars */
6514 http_status_t status; /* Policy status */
6515 cupsd_subscription_t *sub; /* Subscription */
6516 ipp_attribute_t *ids, /* notify-subscription-ids */
6517 *sequences; /* notify-sequence-numbers */
6518 int min_seq; /* Minimum sequence number */
6519 int interval; /* Poll interval */
6520
6521
6522 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_notifications(con=%p[%d])",
6523 con, con->number);
6524
6525 /*
6526 * Get subscription attributes...
6527 */
6528
6529 ids = ippFindAttribute(con->request, "notify-subscription-ids",
6530 IPP_TAG_INTEGER);
6531 sequences = ippFindAttribute(con->request, "notify-sequence-numbers",
6532 IPP_TAG_INTEGER);
6533
6534 if (!ids)
6535 {
6536 send_ipp_status(con, IPP_BAD_REQUEST,
6537 _("Missing notify-subscription-ids attribute."));
6538 return;
6539 }
6540
6541 /*
6542 * Are the subscription IDs valid?
6543 */
6544
6545 for (i = 0, interval = 60; i < ids->num_values; i ++)
6546 {
6547 if ((sub = cupsdFindSubscription(ids->values[i].integer)) == NULL)
6548 {
6549 /*
6550 * Bad subscription ID...
6551 */
6552
6553 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
6554 ids->values[i].integer);
6555 return;
6556 }
6557
6558 /*
6559 * Check policy...
6560 */
6561
6562 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
6563 DefaultPolicyPtr,
6564 con, sub->owner)) != HTTP_OK)
6565 {
6566 send_http_error(con, status, sub->dest);
6567 return;
6568 }
6569
6570 /*
6571 * Check the subscription type and update the interval accordingly.
6572 */
6573
6574 if (sub->job && sub->job->state_value == IPP_JOB_PROCESSING &&
6575 interval > 10)
6576 interval = 10;
6577 else if (sub->job && sub->job->state_value >= IPP_JOB_STOPPED)
6578 interval = 0;
6579 else if (sub->dest && sub->dest->state == IPP_PRINTER_PROCESSING &&
6580 interval > 30)
6581 interval = 30;
6582 }
6583
6584 /*
6585 * Tell the client to poll again in N seconds...
6586 */
6587
6588 if (interval > 0)
6589 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
6590 "notify-get-interval", interval);
6591
6592 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
6593 "printer-up-time", time(NULL));
6594
6595 /*
6596 * Copy the subscription event attributes to the response.
6597 */
6598
6599 con->response->request.status.status_code =
6600 interval ? IPP_OK : IPP_OK_EVENTS_COMPLETE;
6601
6602 for (i = 0; i < ids->num_values; i ++)
6603 {
6604 /*
6605 * Get the subscription and sequence number...
6606 */
6607
6608 sub = cupsdFindSubscription(ids->values[i].integer);
6609
6610 if (sequences && i < sequences->num_values)
6611 min_seq = sequences->values[i].integer;
6612 else
6613 min_seq = 1;
6614
6615 /*
6616 * If we don't have any new events, nothing to do here...
6617 */
6618
6619 if (min_seq > (sub->first_event_id + cupsArrayCount(sub->events)))
6620 continue;
6621
6622 /*
6623 * Otherwise copy all of the new events...
6624 */
6625
6626 if (sub->first_event_id > min_seq)
6627 j = 0;
6628 else
6629 j = min_seq - sub->first_event_id;
6630
6631 for (; j < cupsArrayCount(sub->events); j ++)
6632 {
6633 ippAddSeparator(con->response);
6634
6635 copy_attrs(con->response,
6636 ((cupsd_event_t *)cupsArrayIndex(sub->events, j))->attrs, NULL,
6637 IPP_TAG_EVENT_NOTIFICATION, 0, NULL);
6638 }
6639 }
6640 }
6641
6642
6643 /*
6644 * 'get_ppd()' - Get a named PPD from the local system.
6645 */
6646
6647 static void
6648 get_ppd(cupsd_client_t *con, /* I - Client connection */
6649 ipp_attribute_t *uri) /* I - Printer URI or PPD name */
6650 {
6651 http_status_t status; /* Policy status */
6652 cupsd_printer_t *dest; /* Destination */
6653 cups_ptype_t dtype; /* Destination type */
6654
6655
6656 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppd(%p[%d], %p[%s=%s])", con,
6657 con->number, uri, uri->name, uri->values[0].string.text);
6658
6659 if (!strcmp(uri->name, "ppd-name"))
6660 {
6661 /*
6662 * Return a PPD file from cups-driverd...
6663 */
6664
6665 char command[1024], /* cups-driverd command */
6666 options[1024], /* Options to pass to command */
6667 ppd_name[1024]; /* ppd-name */
6668
6669
6670 /*
6671 * Check policy...
6672 */
6673
6674 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6675 {
6676 send_http_error(con, status, NULL);
6677 return;
6678 }
6679
6680 /*
6681 * Run cups-driverd command with the given options...
6682 */
6683
6684 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
6685 url_encode_string(uri->values[0].string.text, ppd_name, sizeof(ppd_name));
6686 snprintf(options, sizeof(options), "get+%d+%s",
6687 con->request->request.op.request_id, ppd_name);
6688
6689 if (cupsdSendCommand(con, command, options, 0))
6690 {
6691 /*
6692 * Command started successfully, don't send an IPP response here...
6693 */
6694
6695 ippDelete(con->response);
6696 con->response = NULL;
6697 }
6698 else
6699 {
6700 /*
6701 * Command failed, return "internal error" so the user knows something
6702 * went wrong...
6703 */
6704
6705 send_ipp_status(con, IPP_INTERNAL_ERROR,
6706 _("cups-driverd failed to execute."));
6707 }
6708 }
6709 else if (!strcmp(uri->name, "printer-uri") &&
6710 cupsdValidateDest(uri->values[0].string.text, &dtype, &dest))
6711 {
6712 int i; /* Looping var */
6713 char filename[1024]; /* PPD filename */
6714
6715
6716 /*
6717 * Check policy...
6718 */
6719
6720 if ((status = cupsdCheckPolicy(dest->op_policy_ptr, con, NULL)) != HTTP_OK)
6721 {
6722 send_http_error(con, status, dest);
6723 return;
6724 }
6725
6726 /*
6727 * See if we need the PPD for a class or remote printer...
6728 */
6729
6730 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
6731 dest->name);
6732
6733 if ((dtype & CUPS_PRINTER_REMOTE) && access(filename, 0))
6734 {
6735 con->response->request.status.status_code = CUPS_SEE_OTHER;
6736 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
6737 "printer-uri", NULL, dest->uri);
6738 return;
6739 }
6740 else if (dtype & CUPS_PRINTER_CLASS)
6741 {
6742 for (i = 0; i < dest->num_printers; i ++)
6743 if (!(dest->printers[i]->type & CUPS_PRINTER_CLASS))
6744 {
6745 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
6746 dest->printers[i]->name);
6747
6748 if (!access(filename, 0))
6749 break;
6750 }
6751
6752 if (i < dest->num_printers)
6753 dest = dest->printers[i];
6754 else
6755 {
6756 con->response->request.status.status_code = CUPS_SEE_OTHER;
6757 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
6758 "printer-uri", NULL, dest->printers[0]->uri);
6759 return;
6760 }
6761 }
6762
6763 /*
6764 * Found the printer with the PPD file, now see if there is one...
6765 */
6766
6767 if ((con->file = open(filename, O_RDONLY)) < 0)
6768 {
6769 send_ipp_status(con, IPP_NOT_FOUND,
6770 _("The PPD file \"%s\" could not be opened: %s"),
6771 uri->values[0].string.text, strerror(errno));
6772 return;
6773 }
6774
6775 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
6776
6777 con->pipe_pid = 0;
6778
6779 con->response->request.status.status_code = IPP_OK;
6780 }
6781 else
6782 send_ipp_status(con, IPP_NOT_FOUND,
6783 _("The PPD file \"%s\" could not be found."),
6784 uri->values[0].string.text);
6785 }
6786
6787
6788 /*
6789 * 'get_ppds()' - Get the list of PPD files on the local system.
6790 */
6791
6792 static void
6793 get_ppds(cupsd_client_t *con) /* I - Client connection */
6794 {
6795 http_status_t status; /* Policy status */
6796 ipp_attribute_t *limit, /* Limit attribute */
6797 *device, /* ppd-device-id attribute */
6798 *language, /* ppd-natural-language attribute */
6799 *make, /* ppd-make attribute */
6800 *model, /* ppd-make-and-model attribute */
6801 *model_number, /* ppd-model-number attribute */
6802 *product, /* ppd-product attribute */
6803 *psversion, /* ppd-psverion attribute */
6804 *type, /* ppd-type attribute */
6805 *requested, /* requested-attributes attribute */
6806 *exclude, /* exclude-schemes attribute */
6807 *include; /* include-schemes attribute */
6808 char command[1024], /* cups-driverd command */
6809 options[4096], /* Options to pass to command */
6810 device_str[256],/* Escaped ppd-device-id string */
6811 language_str[256],
6812 /* Escaped ppd-natural-language */
6813 make_str[256], /* Escaped ppd-make string */
6814 model_str[256], /* Escaped ppd-make-and-model string */
6815 model_number_str[256],
6816 /* ppd-model-number string */
6817 product_str[256],
6818 /* Escaped ppd-product string */
6819 psversion_str[256],
6820 /* Escaped ppd-psversion string */
6821 type_str[256], /* Escaped ppd-type string */
6822 requested_str[256],
6823 /* String for requested attributes */
6824 exclude_str[512],
6825 /* String for excluded schemes */
6826 include_str[512];
6827 /* String for included schemes */
6828
6829
6830 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->number);
6831
6832 /*
6833 * Check policy...
6834 */
6835
6836 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6837 {
6838 send_http_error(con, status, NULL);
6839 return;
6840 }
6841
6842 /*
6843 * Run cups-driverd command with the given options...
6844 */
6845
6846 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
6847 device = ippFindAttribute(con->request, "ppd-device-id", IPP_TAG_TEXT);
6848 language = ippFindAttribute(con->request, "ppd-natural-language",
6849 IPP_TAG_LANGUAGE);
6850 make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
6851 model = ippFindAttribute(con->request, "ppd-make-and-model",
6852 IPP_TAG_TEXT);
6853 model_number = ippFindAttribute(con->request, "ppd-model-number",
6854 IPP_TAG_INTEGER);
6855 product = ippFindAttribute(con->request, "ppd-product", IPP_TAG_TEXT);
6856 psversion = ippFindAttribute(con->request, "ppd-psversion", IPP_TAG_TEXT);
6857 type = ippFindAttribute(con->request, "ppd-type", IPP_TAG_KEYWORD);
6858 requested = ippFindAttribute(con->request, "requested-attributes",
6859 IPP_TAG_KEYWORD);
6860 exclude = ippFindAttribute(con->request, "exclude-schemes",
6861 IPP_TAG_NAME);
6862 include = ippFindAttribute(con->request, "include-schemes",
6863 IPP_TAG_NAME);
6864
6865 if (requested)
6866 url_encode_attr(requested, requested_str, sizeof(requested_str));
6867 else
6868 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
6869
6870 if (device)
6871 url_encode_attr(device, device_str, sizeof(device_str));
6872 else
6873 device_str[0] = '\0';
6874
6875 if (language)
6876 url_encode_attr(language, language_str, sizeof(language_str));
6877 else
6878 language_str[0] = '\0';
6879
6880 if (make)
6881 url_encode_attr(make, make_str, sizeof(make_str));
6882 else
6883 make_str[0] = '\0';
6884
6885 if (model)
6886 url_encode_attr(model, model_str, sizeof(model_str));
6887 else
6888 model_str[0] = '\0';
6889
6890 if (model_number)
6891 snprintf(model_number_str, sizeof(model_number_str), "ppd-model-number=%d",
6892 model_number->values[0].integer);
6893 else
6894 model_number_str[0] = '\0';
6895
6896 if (product)
6897 url_encode_attr(product, product_str, sizeof(product_str));
6898 else
6899 product_str[0] = '\0';
6900
6901 if (psversion)
6902 url_encode_attr(psversion, psversion_str, sizeof(psversion_str));
6903 else
6904 psversion_str[0] = '\0';
6905
6906 if (type)
6907 url_encode_attr(type, type_str, sizeof(type_str));
6908 else
6909 type_str[0] = '\0';
6910
6911 if (exclude)
6912 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
6913 else
6914 exclude_str[0] = '\0';
6915
6916 if (include)
6917 url_encode_attr(include, include_str, sizeof(include_str));
6918 else
6919 include_str[0] = '\0';
6920
6921 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
6922 snprintf(options, sizeof(options),
6923 "list+%d+%d+%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
6924 con->request->request.op.request_id,
6925 limit ? limit->values[0].integer : 0,
6926 requested_str,
6927 device ? "%20" : "", device_str,
6928 language ? "%20" : "", language_str,
6929 make ? "%20" : "", make_str,
6930 model ? "%20" : "", model_str,
6931 model_number ? "%20" : "", model_number_str,
6932 product ? "%20" : "", product_str,
6933 psversion ? "%20" : "", psversion_str,
6934 type ? "%20" : "", type_str,
6935 exclude_str[0] ? "%20" : "", exclude_str,
6936 include_str[0] ? "%20" : "", include_str);
6937
6938 if (cupsdSendCommand(con, command, options, 0))
6939 {
6940 /*
6941 * Command started successfully, don't send an IPP response here...
6942 */
6943
6944 ippDelete(con->response);
6945 con->response = NULL;
6946 }
6947 else
6948 {
6949 /*
6950 * Command failed, return "internal error" so the user knows something
6951 * went wrong...
6952 */
6953
6954 send_ipp_status(con, IPP_INTERNAL_ERROR,
6955 _("cups-driverd failed to execute."));
6956 }
6957 }
6958
6959
6960 /*
6961 * 'get_printer_attrs()' - Get printer attributes.
6962 */
6963
6964 static void
6965 get_printer_attrs(cupsd_client_t *con, /* I - Client connection */
6966 ipp_attribute_t *uri) /* I - Printer URI */
6967 {
6968 http_status_t status; /* Policy status */
6969 cups_ptype_t dtype; /* Destination type (printer/class) */
6970 cupsd_printer_t *printer; /* Printer/class */
6971 cups_array_t *ra; /* Requested attributes array */
6972
6973
6974 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", con,
6975 con->number, uri->values[0].string.text);
6976
6977 /*
6978 * Is the destination valid?
6979 */
6980
6981 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
6982 {
6983 /*
6984 * Bad URI...
6985 */
6986
6987 send_ipp_status(con, IPP_NOT_FOUND,
6988 _("The printer or class does not exist."));
6989 return;
6990 }
6991
6992 /*
6993 * Check policy...
6994 */
6995
6996 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
6997 {
6998 send_http_error(con, status, printer);
6999 return;
7000 }
7001
7002 /*
7003 * Send the attributes...
7004 */
7005
7006 ra = create_requested_array(con->request);
7007
7008 copy_printer_attrs(con, printer, ra);
7009
7010 cupsArrayDelete(ra);
7011
7012 con->response->request.status.status_code = IPP_OK;
7013 }
7014
7015
7016 /*
7017 * 'get_printer_supported()' - Get printer supported values.
7018 */
7019
7020 static void
7021 get_printer_supported(
7022 cupsd_client_t *con, /* I - Client connection */
7023 ipp_attribute_t *uri) /* I - Printer URI */
7024 {
7025 http_status_t status; /* Policy status */
7026 cups_ptype_t dtype; /* Destination type (printer/class) */
7027 cupsd_printer_t *printer; /* Printer/class */
7028
7029
7030 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_supported(%p[%d], %s)", con,
7031 con->number, uri->values[0].string.text);
7032
7033 /*
7034 * Is the destination valid?
7035 */
7036
7037 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7038 {
7039 /*
7040 * Bad URI...
7041 */
7042
7043 send_ipp_status(con, IPP_NOT_FOUND,
7044 _("The printer or class does not exist."));
7045 return;
7046 }
7047
7048 /*
7049 * Check policy...
7050 */
7051
7052 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7053 {
7054 send_http_error(con, status, printer);
7055 return;
7056 }
7057
7058 /*
7059 * Return a list of attributes that can be set via Set-Printer-Attributes.
7060 */
7061
7062 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7063 "printer-info", 0);
7064 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7065 "printer-location", 0);
7066
7067 con->response->request.status.status_code = IPP_OK;
7068 }
7069
7070
7071 /*
7072 * 'get_printers()' - Get a list of printers or classes.
7073 */
7074
7075 static void
7076 get_printers(cupsd_client_t *con, /* I - Client connection */
7077 int type) /* I - 0 or CUPS_PRINTER_CLASS */
7078 {
7079 http_status_t status; /* Policy status */
7080 ipp_attribute_t *attr; /* Current attribute */
7081 int limit; /* Max number of printers to return */
7082 int count; /* Number of printers that match */
7083 cupsd_printer_t *printer; /* Current printer pointer */
7084 cups_ptype_t printer_type, /* printer-type attribute */
7085 printer_mask; /* printer-type-mask attribute */
7086 char *location; /* Location string */
7087 const char *username; /* Current user */
7088 char *first_printer_name; /* first-printer-name attribute */
7089 cups_array_t *ra; /* Requested attributes array */
7090 int local; /* Local connection? */
7091
7092
7093 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con,
7094 con->number, type);
7095
7096 /*
7097 * Check policy...
7098 */
7099
7100 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7101 {
7102 send_http_error(con, status, NULL);
7103 return;
7104 }
7105
7106 /*
7107 * Check for printers...
7108 */
7109
7110 if (!Printers || !cupsArrayCount(Printers))
7111 {
7112 send_ipp_status(con, IPP_NOT_FOUND, _("No destinations added."));
7113 return;
7114 }
7115
7116 /*
7117 * See if they want to limit the number of printers reported...
7118 */
7119
7120 if ((attr = ippFindAttribute(con->request, "limit",
7121 IPP_TAG_INTEGER)) != NULL)
7122 limit = attr->values[0].integer;
7123 else
7124 limit = 10000000;
7125
7126 if ((attr = ippFindAttribute(con->request, "first-printer-name",
7127 IPP_TAG_NAME)) != NULL)
7128 first_printer_name = attr->values[0].string.text;
7129 else
7130 first_printer_name = NULL;
7131
7132 /*
7133 * Support filtering...
7134 */
7135
7136 if ((attr = ippFindAttribute(con->request, "printer-type",
7137 IPP_TAG_ENUM)) != NULL)
7138 printer_type = (cups_ptype_t)attr->values[0].integer;
7139 else
7140 printer_type = (cups_ptype_t)0;
7141
7142 if ((attr = ippFindAttribute(con->request, "printer-type-mask",
7143 IPP_TAG_ENUM)) != NULL)
7144 printer_mask = (cups_ptype_t)attr->values[0].integer;
7145 else
7146 printer_mask = (cups_ptype_t)0;
7147
7148 local = httpAddrLocalhost(&(con->clientaddr));
7149
7150 if ((attr = ippFindAttribute(con->request, "printer-location",
7151 IPP_TAG_TEXT)) != NULL)
7152 location = attr->values[0].string.text;
7153 else
7154 location = NULL;
7155
7156 if (con->username[0])
7157 username = con->username;
7158 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7159 IPP_TAG_NAME)) != NULL)
7160 username = attr->values[0].string.text;
7161 else
7162 username = NULL;
7163
7164 ra = create_requested_array(con->request);
7165
7166 /*
7167 * OK, build a list of printers for this printer...
7168 */
7169
7170 if (first_printer_name)
7171 {
7172 if ((printer = cupsdFindDest(first_printer_name)) == NULL)
7173 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
7174 }
7175 else
7176 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
7177
7178 for (count = 0;
7179 count < limit && printer;
7180 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
7181 {
7182 if (!local && !printer->shared)
7183 continue;
7184
7185 if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
7186 (printer->type & printer_mask) == printer_type &&
7187 (!location ||
7188 (printer->location && !_cups_strcasecmp(printer->location, location))))
7189 {
7190 /*
7191 * If a username is specified, see if it is allowed or denied
7192 * access...
7193 */
7194
7195 if (cupsArrayCount(printer->users) && username &&
7196 !user_allowed(printer, username))
7197 continue;
7198
7199 /*
7200 * Add the group separator as needed...
7201 */
7202
7203 if (count > 0)
7204 ippAddSeparator(con->response);
7205
7206 count ++;
7207
7208 /*
7209 * Send the attributes...
7210 */
7211
7212 copy_printer_attrs(con, printer, ra);
7213 }
7214 }
7215
7216 cupsArrayDelete(ra);
7217
7218 con->response->request.status.status_code = IPP_OK;
7219 }
7220
7221
7222 /*
7223 * 'get_subscription_attrs()' - Get subscription attributes.
7224 */
7225
7226 static void
7227 get_subscription_attrs(
7228 cupsd_client_t *con, /* I - Client connection */
7229 int sub_id) /* I - Subscription ID */
7230 {
7231 http_status_t status; /* Policy status */
7232 cupsd_subscription_t *sub; /* Subscription */
7233 cupsd_policy_t *policy; /* Current security policy */
7234 cups_array_t *ra, /* Requested attributes array */
7235 *exclude; /* Private attributes array */
7236
7237
7238 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7239 "get_subscription_attrs(con=%p[%d], sub_id=%d)",
7240 con, con->number, sub_id);
7241
7242 /*
7243 * Is the subscription ID valid?
7244 */
7245
7246 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
7247 {
7248 /*
7249 * Bad subscription ID...
7250 */
7251
7252 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
7253 sub_id);
7254 return;
7255 }
7256
7257 /*
7258 * Check policy...
7259 */
7260
7261 if (sub->dest)
7262 policy = sub->dest->op_policy_ptr;
7263 else
7264 policy = DefaultPolicyPtr;
7265
7266 if ((status = cupsdCheckPolicy(policy, con, sub->owner)) != HTTP_OK)
7267 {
7268 send_http_error(con, status, sub->dest);
7269 return;
7270 }
7271
7272 exclude = cupsdGetPrivateAttrs(policy, con, sub->dest, sub->owner);
7273
7274 /*
7275 * Copy the subscription attributes to the response using the
7276 * requested-attributes attribute that may be provided by the client.
7277 */
7278
7279 ra = create_requested_array(con->request);
7280
7281 copy_subscription_attrs(con, sub, ra, exclude);
7282
7283 cupsArrayDelete(ra);
7284
7285 con->response->request.status.status_code = IPP_OK;
7286 }
7287
7288
7289 /*
7290 * 'get_subscriptions()' - Get subscriptions.
7291 */
7292
7293 static void
7294 get_subscriptions(cupsd_client_t *con, /* I - Client connection */
7295 ipp_attribute_t *uri) /* I - Printer/job URI */
7296 {
7297 http_status_t status; /* Policy status */
7298 int count; /* Number of subscriptions */
7299 int limit; /* Limit */
7300 cupsd_subscription_t *sub; /* Subscription */
7301 cups_array_t *ra; /* Requested attributes array */
7302 ipp_attribute_t *attr; /* Attribute */
7303 cups_ptype_t dtype; /* Destination type (printer/class) */
7304 char scheme[HTTP_MAX_URI],
7305 /* Scheme portion of URI */
7306 username[HTTP_MAX_URI],
7307 /* Username portion of URI */
7308 host[HTTP_MAX_URI],
7309 /* Host portion of URI */
7310 resource[HTTP_MAX_URI];
7311 /* Resource portion of URI */
7312 int port; /* Port portion of URI */
7313 cupsd_job_t *job; /* Job pointer */
7314 cupsd_printer_t *printer; /* Printer */
7315 cupsd_policy_t *policy; /* Policy */
7316 cups_array_t *exclude; /* Private attributes array */
7317
7318
7319 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7320 "get_subscriptions(con=%p[%d], uri=%s)",
7321 con, con->number, uri->values[0].string.text);
7322
7323 /*
7324 * Is the destination valid?
7325 */
7326
7327 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7328 sizeof(scheme), username, sizeof(username), host,
7329 sizeof(host), &port, resource, sizeof(resource));
7330
7331 if (!strcmp(resource, "/") ||
7332 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6) ||
7333 (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) ||
7334 (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9))
7335 {
7336 printer = NULL;
7337 job = NULL;
7338 }
7339 else if (!strncmp(resource, "/jobs/", 6) && resource[6])
7340 {
7341 printer = NULL;
7342 job = cupsdFindJob(atoi(resource + 6));
7343
7344 if (!job)
7345 {
7346 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
7347 atoi(resource + 6));
7348 return;
7349 }
7350 }
7351 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7352 {
7353 /*
7354 * Bad URI...
7355 */
7356
7357 send_ipp_status(con, IPP_NOT_FOUND,
7358 _("The printer or class does not exist."));
7359 return;
7360 }
7361 else if ((attr = ippFindAttribute(con->request, "notify-job-id",
7362 IPP_TAG_INTEGER)) != NULL)
7363 {
7364 job = cupsdFindJob(attr->values[0].integer);
7365
7366 if (!job)
7367 {
7368 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
7369 attr->values[0].integer);
7370 return;
7371 }
7372 }
7373 else
7374 job = NULL;
7375
7376 /*
7377 * Check policy...
7378 */
7379
7380 if (printer)
7381 policy = printer->op_policy_ptr;
7382 else
7383 policy = DefaultPolicyPtr;
7384
7385 if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
7386 {
7387 send_http_error(con, status, printer);
7388 return;
7389 }
7390
7391 /*
7392 * Copy the subscription attributes to the response using the
7393 * requested-attributes attribute that may be provided by the client.
7394 */
7395
7396 ra = create_requested_array(con->request);
7397
7398 if ((attr = ippFindAttribute(con->request, "limit",
7399 IPP_TAG_INTEGER)) != NULL)
7400 limit = attr->values[0].integer;
7401 else
7402 limit = 0;
7403
7404 /*
7405 * See if we only want to see subscriptions for a specific user...
7406 */
7407
7408 if ((attr = ippFindAttribute(con->request, "my-subscriptions",
7409 IPP_TAG_BOOLEAN)) != NULL &&
7410 attr->values[0].boolean)
7411 strlcpy(username, get_username(con), sizeof(username));
7412 else
7413 username[0] = '\0';
7414
7415 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0;
7416 sub;
7417 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
7418 if ((!printer || sub->dest == printer) && (!job || sub->job == job) &&
7419 (!username[0] || !_cups_strcasecmp(username, sub->owner)))
7420 {
7421 ippAddSeparator(con->response);
7422
7423 exclude = cupsdGetPrivateAttrs(sub->dest ? sub->dest->op_policy_ptr :
7424 policy, con, sub->dest,
7425 sub->owner);
7426
7427 copy_subscription_attrs(con, sub, ra, exclude);
7428
7429 count ++;
7430 if (limit && count >= limit)
7431 break;
7432 }
7433
7434 cupsArrayDelete(ra);
7435
7436 if (count)
7437 con->response->request.status.status_code = IPP_OK;
7438 else
7439 send_ipp_status(con, IPP_NOT_FOUND, _("No subscriptions found."));
7440 }
7441
7442
7443 /*
7444 * 'get_username()' - Get the username associated with a request.
7445 */
7446
7447 static const char * /* O - Username */
7448 get_username(cupsd_client_t *con) /* I - Connection */
7449 {
7450 ipp_attribute_t *attr; /* Attribute */
7451
7452
7453 if (con->username[0])
7454 return (con->username);
7455 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7456 IPP_TAG_NAME)) != NULL)
7457 return (attr->values[0].string.text);
7458 else
7459 return ("anonymous");
7460 }
7461
7462
7463 /*
7464 * 'hold_job()' - Hold a print job.
7465 */
7466
7467 static void
7468 hold_job(cupsd_client_t *con, /* I - Client connection */
7469 ipp_attribute_t *uri) /* I - Job or Printer URI */
7470 {
7471 ipp_attribute_t *attr; /* Current job-hold-until */
7472 const char *when; /* New value */
7473 int jobid; /* Job ID */
7474 char scheme[HTTP_MAX_URI], /* Method portion of URI */
7475 username[HTTP_MAX_URI], /* Username portion of URI */
7476 host[HTTP_MAX_URI], /* Host portion of URI */
7477 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7478 int port; /* Port portion of URI */
7479 cupsd_job_t *job; /* Job information */
7480
7481
7482 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", con, con->number,
7483 uri->values[0].string.text);
7484
7485 /*
7486 * See if we have a job URI or a printer URI...
7487 */
7488
7489 if (!strcmp(uri->name, "printer-uri"))
7490 {
7491 /*
7492 * Got a printer URI; see if we also have a job-id attribute...
7493 */
7494
7495 if ((attr = ippFindAttribute(con->request, "job-id",
7496 IPP_TAG_INTEGER)) == NULL)
7497 {
7498 send_ipp_status(con, IPP_BAD_REQUEST,
7499 _("Got a printer-uri attribute but no job-id."));
7500 return;
7501 }
7502
7503 jobid = attr->values[0].integer;
7504 }
7505 else
7506 {
7507 /*
7508 * Got a job URI; parse it to get the job ID...
7509 */
7510
7511 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7512 sizeof(scheme), username, sizeof(username), host,
7513 sizeof(host), &port, resource, sizeof(resource));
7514
7515 if (strncmp(resource, "/jobs/", 6))
7516 {
7517 /*
7518 * Not a valid URI!
7519 */
7520
7521 send_ipp_status(con, IPP_BAD_REQUEST,
7522 _("Bad job-uri \"%s\"."),
7523 uri->values[0].string.text);
7524 return;
7525 }
7526
7527 jobid = atoi(resource + 6);
7528 }
7529
7530 /*
7531 * See if the job exists...
7532 */
7533
7534 if ((job = cupsdFindJob(jobid)) == NULL)
7535 {
7536 /*
7537 * Nope - return a "not found" error...
7538 */
7539
7540 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
7541 return;
7542 }
7543
7544 /*
7545 * See if the job is owned by the requesting user...
7546 */
7547
7548 if (!validate_user(job, con, job->username, username, sizeof(username)))
7549 {
7550 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
7551 cupsdFindDest(job->dest));
7552 return;
7553 }
7554
7555 /*
7556 * See if the job is in a state that allows holding...
7557 */
7558
7559 if (job->state_value > IPP_JOB_STOPPED)
7560 {
7561 /*
7562 * Return a "not-possible" error...
7563 */
7564
7565 send_ipp_status(con, IPP_NOT_POSSIBLE,
7566 _("Job #%d is finished and cannot be altered."),
7567 job->id);
7568 return;
7569 }
7570
7571 /*
7572 * Hold the job and return...
7573 */
7574
7575 if ((attr = ippFindAttribute(con->request, "job-hold-until",
7576 IPP_TAG_KEYWORD)) == NULL)
7577 attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
7578
7579 if (attr)
7580 {
7581 when = attr->values[0].string.text;
7582
7583 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
7584 "Job job-hold-until value changed by user.");
7585 }
7586 else
7587 when = "indefinite";
7588
7589 cupsdSetJobHoldUntil(job, when, 1);
7590 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT, "Job held by \"%s\".",
7591 username);
7592
7593 con->response->request.status.status_code = IPP_OK;
7594 }
7595
7596
7597 /*
7598 * 'hold_new_jobs()' - Hold pending/new jobs on a printer or class.
7599 */
7600
7601 static void
7602 hold_new_jobs(cupsd_client_t *con, /* I - Connection */
7603 ipp_attribute_t *uri) /* I - Printer URI */
7604 {
7605 http_status_t status; /* Policy status */
7606 cups_ptype_t dtype; /* Destination type (printer/class) */
7607 cupsd_printer_t *printer; /* Printer data */
7608
7609
7610 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_new_jobs(%p[%d], %s)", con,
7611 con->number, uri->values[0].string.text);
7612
7613 /*
7614 * Is the destination valid?
7615 */
7616
7617 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7618 {
7619 /*
7620 * Bad URI...
7621 */
7622
7623 send_ipp_status(con, IPP_NOT_FOUND,
7624 _("The printer or class does not exist."));
7625 return;
7626 }
7627
7628 /*
7629 * Check policy...
7630 */
7631
7632 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7633 {
7634 send_http_error(con, status, printer);
7635 return;
7636 }
7637
7638 /*
7639 * Hold pending/new jobs sent to the printer...
7640 */
7641
7642 printer->holding_new_jobs = 1;
7643
7644 cupsdSetPrinterReasons(printer, "+hold-new-jobs");
7645
7646 if (dtype & CUPS_PRINTER_CLASS)
7647 cupsdLogMessage(CUPSD_LOG_INFO,
7648 "Class \"%s\" now holding pending/new jobs (\"%s\").",
7649 printer->name, get_username(con));
7650 else
7651 cupsdLogMessage(CUPSD_LOG_INFO,
7652 "Printer \"%s\" now holding pending/new jobs (\"%s\").",
7653 printer->name, get_username(con));
7654
7655 /*
7656 * Everything was ok, so return OK status...
7657 */
7658
7659 con->response->request.status.status_code = IPP_OK;
7660 }
7661
7662
7663 /*
7664 * 'move_job()' - Move a job to a new destination.
7665 */
7666
7667 static void
7668 move_job(cupsd_client_t *con, /* I - Client connection */
7669 ipp_attribute_t *uri) /* I - Job URI */
7670 {
7671 http_status_t status; /* Policy status */
7672 ipp_attribute_t *attr; /* Current attribute */
7673 int jobid; /* Job ID */
7674 cupsd_job_t *job; /* Current job */
7675 const char *src; /* Source printer/class */
7676 cups_ptype_t stype, /* Source type (printer or class) */
7677 dtype; /* Destination type (printer/class) */
7678 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
7679 username[HTTP_MAX_URI], /* Username portion of URI */
7680 host[HTTP_MAX_URI], /* Host portion of URI */
7681 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7682 int port; /* Port portion of URI */
7683 cupsd_printer_t *sprinter, /* Source printer */
7684 *dprinter; /* Destination printer */
7685
7686
7687 cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", con, con->number,
7688 uri->values[0].string.text);
7689
7690 /*
7691 * Get the new printer or class...
7692 */
7693
7694 if ((attr = ippFindAttribute(con->request, "job-printer-uri",
7695 IPP_TAG_URI)) == NULL)
7696 {
7697 /*
7698 * Need job-printer-uri...
7699 */
7700
7701 send_ipp_status(con, IPP_BAD_REQUEST,
7702 _("job-printer-uri attribute missing."));
7703 return;
7704 }
7705
7706 if (!cupsdValidateDest(attr->values[0].string.text, &dtype, &dprinter))
7707 {
7708 /*
7709 * Bad URI...
7710 */
7711
7712 send_ipp_status(con, IPP_NOT_FOUND,
7713 _("The printer or class does not exist."));
7714 return;
7715 }
7716
7717 /*
7718 * See if we have a job URI or a printer URI...
7719 */
7720
7721 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7722 sizeof(scheme), username, sizeof(username), host,
7723 sizeof(host), &port, resource, sizeof(resource));
7724
7725 if (!strcmp(uri->name, "printer-uri"))
7726 {
7727 /*
7728 * Got a printer URI; see if we also have a job-id attribute...
7729 */
7730
7731 if ((attr = ippFindAttribute(con->request, "job-id",
7732 IPP_TAG_INTEGER)) == NULL)
7733 {
7734 /*
7735 * Move all jobs...
7736 */
7737
7738 if ((src = cupsdValidateDest(uri->values[0].string.text, &stype,
7739 &sprinter)) == NULL)
7740 {
7741 /*
7742 * Bad URI...
7743 */
7744
7745 send_ipp_status(con, IPP_NOT_FOUND,
7746 _("The printer or class does not exist."));
7747 return;
7748 }
7749
7750 job = NULL;
7751 }
7752 else
7753 {
7754 /*
7755 * Otherwise, just move a single job...
7756 */
7757
7758 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
7759 {
7760 /*
7761 * Nope - return a "not found" error...
7762 */
7763
7764 send_ipp_status(con, IPP_NOT_FOUND,
7765 _("Job #%d does not exist."), attr->values[0].integer);
7766 return;
7767 }
7768 else
7769 {
7770 /*
7771 * Job found, initialize source pointers...
7772 */
7773
7774 src = NULL;
7775 sprinter = NULL;
7776 }
7777 }
7778 }
7779 else
7780 {
7781 /*
7782 * Got a job URI; parse it to get the job ID...
7783 */
7784
7785 if (strncmp(resource, "/jobs/", 6))
7786 {
7787 /*
7788 * Not a valid URI!
7789 */
7790
7791 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
7792 uri->values[0].string.text);
7793 return;
7794 }
7795
7796 /*
7797 * See if the job exists...
7798 */
7799
7800 jobid = atoi(resource + 6);
7801
7802 if ((job = cupsdFindJob(jobid)) == NULL)
7803 {
7804 /*
7805 * Nope - return a "not found" error...
7806 */
7807
7808 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
7809 return;
7810 }
7811 else
7812 {
7813 /*
7814 * Job found, initialize source pointers...
7815 */
7816
7817 src = NULL;
7818 sprinter = NULL;
7819 }
7820 }
7821
7822 /*
7823 * Check the policy of the destination printer...
7824 */
7825
7826 if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con,
7827 job ? job->username : NULL)) != HTTP_OK)
7828 {
7829 send_http_error(con, status, dprinter);
7830 return;
7831 }
7832
7833 /*
7834 * Now move the job or jobs...
7835 */
7836
7837 if (job)
7838 {
7839 /*
7840 * See if the job has been completed...
7841 */
7842
7843 if (job->state_value > IPP_JOB_STOPPED)
7844 {
7845 /*
7846 * Return a "not-possible" error...
7847 */
7848
7849 send_ipp_status(con, IPP_NOT_POSSIBLE,
7850 _("Job #%d is finished and cannot be altered."),
7851 job->id);
7852 return;
7853 }
7854
7855 /*
7856 * See if the job is owned by the requesting user...
7857 */
7858
7859 if (!validate_user(job, con, job->username, username, sizeof(username)))
7860 {
7861 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
7862 cupsdFindDest(job->dest));
7863 return;
7864 }
7865
7866 /*
7867 * Move the job to a different printer or class...
7868 */
7869
7870 cupsdMoveJob(job, dprinter);
7871 }
7872 else
7873 {
7874 /*
7875 * Got the source printer, now look through the jobs...
7876 */
7877
7878 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
7879 job;
7880 job = (cupsd_job_t *)cupsArrayNext(Jobs))
7881 {
7882 /*
7883 * See if the job is pointing at the source printer or has not been
7884 * completed...
7885 */
7886
7887 if (_cups_strcasecmp(job->dest, src) ||
7888 job->state_value > IPP_JOB_STOPPED)
7889 continue;
7890
7891 /*
7892 * See if the job can be moved by the requesting user...
7893 */
7894
7895 if (!validate_user(job, con, job->username, username, sizeof(username)))
7896 continue;
7897
7898 /*
7899 * Move the job to a different printer or class...
7900 */
7901
7902 cupsdMoveJob(job, dprinter);
7903 }
7904 }
7905
7906 /*
7907 * Start jobs if possible...
7908 */
7909
7910 cupsdCheckJobs();
7911
7912 /*
7913 * Return with "everything is OK" status...
7914 */
7915
7916 con->response->request.status.status_code = IPP_OK;
7917 }
7918
7919
7920 /*
7921 * 'ppd_parse_line()' - Parse a PPD default line.
7922 */
7923
7924 static int /* O - 0 on success, -1 on failure */
7925 ppd_parse_line(const char *line, /* I - Line */
7926 char *option, /* O - Option name */
7927 int olen, /* I - Size of option name */
7928 char *choice, /* O - Choice name */
7929 int clen) /* I - Size of choice name */
7930 {
7931 /*
7932 * Verify this is a default option line...
7933 */
7934
7935 if (strncmp(line, "*Default", 8))
7936 return (-1);
7937
7938 /*
7939 * Read the option name...
7940 */
7941
7942 for (line += 8, olen --;
7943 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
7944 line ++)
7945 if (olen > 0)
7946 {
7947 *option++ = *line;
7948 olen --;
7949 }
7950
7951 *option = '\0';
7952
7953 /*
7954 * Skip everything else up to the colon (:)...
7955 */
7956
7957 while (*line && *line != ':')
7958 line ++;
7959
7960 if (!*line)
7961 return (-1);
7962
7963 line ++;
7964
7965 /*
7966 * Now grab the option choice, skipping leading whitespace...
7967 */
7968
7969 while (isspace(*line & 255))
7970 line ++;
7971
7972 for (clen --;
7973 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
7974 line ++)
7975 if (clen > 0)
7976 {
7977 *choice++ = *line;
7978 clen --;
7979 }
7980
7981 *choice = '\0';
7982
7983 /*
7984 * Return with no errors...
7985 */
7986
7987 return (0);
7988 }
7989
7990
7991 /*
7992 * 'print_job()' - Print a file to a printer or class.
7993 */
7994
7995 static void
7996 print_job(cupsd_client_t *con, /* I - Client connection */
7997 ipp_attribute_t *uri) /* I - Printer URI */
7998 {
7999 ipp_attribute_t *attr; /* Current attribute */
8000 ipp_attribute_t *format; /* Document-format attribute */
8001 const char *default_format; /* document-format-default value */
8002 cupsd_job_t *job; /* New job */
8003 char filename[1024]; /* Job filename */
8004 mime_type_t *filetype; /* Type of file */
8005 char super[MIME_MAX_SUPER], /* Supertype of file */
8006 type[MIME_MAX_TYPE], /* Subtype of file */
8007 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
8008 /* Textual name of mime type */
8009 cupsd_printer_t *printer; /* Printer data */
8010 struct stat fileinfo; /* File information */
8011 int kbytes; /* Size of file */
8012 int compression; /* Document compression */
8013
8014
8015 cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", con, con->number,
8016 uri->values[0].string.text);
8017
8018 /*
8019 * Validate print file attributes, for now just document-format and
8020 * compression (CUPS only supports "none" and "gzip")...
8021 */
8022
8023 compression = CUPS_FILE_NONE;
8024
8025 if ((attr = ippFindAttribute(con->request, "compression",
8026 IPP_TAG_KEYWORD)) != NULL)
8027 {
8028 if (strcmp(attr->values[0].string.text, "none")
8029 #ifdef HAVE_LIBZ
8030 && strcmp(attr->values[0].string.text, "gzip")
8031 #endif /* HAVE_LIBZ */
8032 )
8033 {
8034 send_ipp_status(con, IPP_ATTRIBUTES,
8035 _("Unsupported compression \"%s\"."),
8036 attr->values[0].string.text);
8037 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
8038 "compression", NULL, attr->values[0].string.text);
8039 return;
8040 }
8041
8042 #ifdef HAVE_LIBZ
8043 if (!strcmp(attr->values[0].string.text, "gzip"))
8044 compression = CUPS_FILE_GZIP;
8045 #endif /* HAVE_LIBZ */
8046 }
8047
8048 /*
8049 * Do we have a file to print?
8050 */
8051
8052 if (!con->filename)
8053 {
8054 send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
8055 return;
8056 }
8057
8058 /*
8059 * Is the destination valid?
8060 */
8061
8062 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
8063 {
8064 /*
8065 * Bad URI...
8066 */
8067
8068 send_ipp_status(con, IPP_NOT_FOUND,
8069 _("The printer or class does not exist."));
8070 return;
8071 }
8072
8073 /*
8074 * Is it a format we support?
8075 */
8076
8077 if ((format = ippFindAttribute(con->request, "document-format",
8078 IPP_TAG_MIMETYPE)) != NULL)
8079 {
8080 /*
8081 * Grab format from client...
8082 */
8083
8084 if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]", super,
8085 type) != 2)
8086 {
8087 send_ipp_status(con, IPP_BAD_REQUEST,
8088 _("Bad document-format \"%s\"."),
8089 format->values[0].string.text);
8090 return;
8091 }
8092 }
8093 else if ((default_format = cupsGetOption("document-format",
8094 printer->num_options,
8095 printer->options)) != NULL)
8096 {
8097 /*
8098 * Use default document format...
8099 */
8100
8101 if (sscanf(default_format, "%15[^/]/%255[^;]", super, type) != 2)
8102 {
8103 send_ipp_status(con, IPP_BAD_REQUEST,
8104 _("Bad document-format \"%s\"."),
8105 default_format);
8106 return;
8107 }
8108 }
8109 else
8110 {
8111 /*
8112 * Auto-type it!
8113 */
8114
8115 strlcpy(super, "application", sizeof(super));
8116 strlcpy(type, "octet-stream", sizeof(type));
8117 }
8118
8119 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
8120 {
8121 /*
8122 * Auto-type the file...
8123 */
8124
8125 ipp_attribute_t *doc_name; /* document-name attribute */
8126
8127
8128 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job ???] Auto-typing file...");
8129
8130 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
8131 filetype = mimeFileType(MimeDatabase, con->filename,
8132 doc_name ? doc_name->values[0].string.text : NULL,
8133 &compression);
8134
8135 if (!filetype)
8136 filetype = mimeType(MimeDatabase, super, type);
8137
8138 cupsdLogMessage(CUPSD_LOG_INFO, "[Job ???] Request file type is %s/%s.",
8139 filetype->super, filetype->type);
8140 }
8141 else
8142 filetype = mimeType(MimeDatabase, super, type);
8143
8144 if (filetype &&
8145 (!format ||
8146 (!strcmp(super, "application") && !strcmp(type, "octet-stream"))))
8147 {
8148 /*
8149 * Replace the document-format attribute value with the auto-typed or
8150 * default one.
8151 */
8152
8153 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
8154 filetype->type);
8155
8156 if (format)
8157 ippSetString(con->request, &format, 0, mimetype);
8158 else
8159 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
8160 "document-format", NULL, mimetype);
8161 }
8162 else if (!filetype)
8163 {
8164 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
8165 _("Unsupported document-format \"%s\"."),
8166 format ? format->values[0].string.text :
8167 "application/octet-stream");
8168 cupsdLogMessage(CUPSD_LOG_INFO,
8169 "Hint: Do you have the raw file printing rules enabled?");
8170
8171 if (format)
8172 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
8173 "document-format", NULL, format->values[0].string.text);
8174
8175 return;
8176 }
8177
8178 /*
8179 * Read any embedded job ticket info from PS files...
8180 */
8181
8182 if (!_cups_strcasecmp(filetype->super, "application") &&
8183 (!_cups_strcasecmp(filetype->type, "postscript") ||
8184 !_cups_strcasecmp(filetype->type, "pdf")))
8185 read_job_ticket(con);
8186
8187 /*
8188 * Create the job object...
8189 */
8190
8191 if ((job = add_job(con, printer, filetype)) == NULL)
8192 return;
8193
8194 /*
8195 * Update quota data...
8196 */
8197
8198 if (stat(con->filename, &fileinfo))
8199 kbytes = 0;
8200 else
8201 kbytes = (fileinfo.st_size + 1023) / 1024;
8202
8203 cupsdUpdateQuota(printer, job->username, 0, kbytes);
8204
8205 job->koctets += kbytes;
8206
8207 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
8208 attr->values[0].integer += kbytes;
8209
8210 /*
8211 * Add the job file...
8212 */
8213
8214 if (add_file(con, job, filetype, compression))
8215 return;
8216
8217 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
8218 job->num_files);
8219 rename(con->filename, filename);
8220 cupsdClearString(&con->filename);
8221
8222 /*
8223 * See if we need to add the ending sheet...
8224 */
8225
8226 if (cupsdTimeoutJob(job))
8227 return;
8228
8229 /*
8230 * Log and save the job...
8231 */
8232
8233 cupsdLogJob(job, CUPSD_LOG_INFO,
8234 "File of type %s/%s queued by \"%s\".",
8235 filetype->super, filetype->type, job->username);
8236 cupsdLogJob(job, CUPSD_LOG_DEBUG, "hold_until=%d", (int)job->hold_until);
8237 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
8238 job->dest, job->username);
8239
8240 /*
8241 * Start the job if possible...
8242 */
8243
8244 cupsdCheckJobs();
8245 }
8246
8247
8248 /*
8249 * 'read_job_ticket()' - Read a job ticket embedded in a print file.
8250 *
8251 * This function only gets called when printing a single PDF or PostScript
8252 * file using the Print-Job operation. It doesn't work for Create-Job +
8253 * Send-File, since the job attributes need to be set at job creation
8254 * time for banners to work. The embedded job ticket stuff is here
8255 * primarily to allow the Windows printer driver for CUPS to pass in JCL
8256 * options and IPP attributes which otherwise would be lost.
8257 *
8258 * The format of a job ticket is simple:
8259 *
8260 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
8261 *
8262 * %cupsJobTicket: attr1=value1
8263 * %cupsJobTicket: attr2=value2
8264 * ...
8265 * %cupsJobTicket: attrN=valueN
8266 *
8267 * Job ticket lines must appear immediately after the first line that
8268 * specifies PostScript (%!PS-Adobe-3.0) or PDF (%PDF) format, and CUPS
8269 * stops looking for job ticket info when it finds a line that does not begin
8270 * with "%cupsJobTicket:".
8271 *
8272 * The maximum length of a job ticket line, including the prefix, is
8273 * 255 characters to conform with the Adobe DSC.
8274 *
8275 * Read-only attributes are rejected with a notice to the error log in
8276 * case a malicious user tries anything. Since the job ticket is read
8277 * prior to attribute validation in print_job(), job ticket attributes
8278 * will go through the same validation as IPP attributes...
8279 */
8280
8281 static void
8282 read_job_ticket(cupsd_client_t *con) /* I - Client connection */
8283 {
8284 cups_file_t *fp; /* File to read from */
8285 char line[256]; /* Line data */
8286 int num_options; /* Number of options */
8287 cups_option_t *options; /* Options */
8288 ipp_t *ticket; /* New attributes */
8289 ipp_attribute_t *attr, /* Current attribute */
8290 *attr2, /* Job attribute */
8291 *prev2; /* Previous job attribute */
8292
8293
8294 /*
8295 * First open the print file...
8296 */
8297
8298 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
8299 {
8300 cupsdLogMessage(CUPSD_LOG_ERROR,
8301 "Unable to open print file for job ticket - %s",
8302 strerror(errno));
8303 return;
8304 }
8305
8306 /*
8307 * Skip the first line...
8308 */
8309
8310 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
8311 {
8312 cupsdLogMessage(CUPSD_LOG_ERROR,
8313 "Unable to read from print file for job ticket - %s",
8314 strerror(errno));
8315 cupsFileClose(fp);
8316 return;
8317 }
8318
8319 if (strncmp(line, "%!PS-Adobe-", 11) && strncmp(line, "%PDF-", 5))
8320 {
8321 /*
8322 * Not a DSC-compliant file, so no job ticket info will be available...
8323 */
8324
8325 cupsFileClose(fp);
8326 return;
8327 }
8328
8329 /*
8330 * Read job ticket info from the file...
8331 */
8332
8333 num_options = 0;
8334 options = NULL;
8335
8336 while (cupsFileGets(fp, line, sizeof(line)))
8337 {
8338 /*
8339 * Stop at the first non-ticket line...
8340 */
8341
8342 if (strncmp(line, "%cupsJobTicket:", 15))
8343 break;
8344
8345 /*
8346 * Add the options to the option array...
8347 */
8348
8349 num_options = cupsParseOptions(line + 15, num_options, &options);
8350 }
8351
8352 /*
8353 * Done with the file; see if we have any options...
8354 */
8355
8356 cupsFileClose(fp);
8357
8358 if (num_options == 0)
8359 return;
8360
8361 /*
8362 * OK, convert the options to an attribute list, and apply them to
8363 * the request...
8364 */
8365
8366 ticket = ippNew();
8367 cupsEncodeOptions(ticket, num_options, options);
8368
8369 /*
8370 * See what the user wants to change.
8371 */
8372
8373 for (attr = ticket->attrs; attr; attr = attr->next)
8374 {
8375 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
8376 continue;
8377
8378 if (!strcmp(attr->name, "job-originating-host-name") ||
8379 !strcmp(attr->name, "job-originating-user-name") ||
8380 !strcmp(attr->name, "job-media-sheets-completed") ||
8381 !strcmp(attr->name, "job-k-octets") ||
8382 !strcmp(attr->name, "job-id") ||
8383 !strncmp(attr->name, "job-state", 9) ||
8384 !strncmp(attr->name, "time-at-", 8))
8385 continue; /* Read-only attrs */
8386
8387 if ((attr2 = ippFindAttribute(con->request, attr->name,
8388 IPP_TAG_ZERO)) != NULL)
8389 {
8390 /*
8391 * Some other value; first free the old value...
8392 */
8393
8394 if (con->request->attrs == attr2)
8395 {
8396 con->request->attrs = attr2->next;
8397 prev2 = NULL;
8398 }
8399 else
8400 {
8401 for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
8402 if (prev2->next == attr2)
8403 {
8404 prev2->next = attr2->next;
8405 break;
8406 }
8407 }
8408
8409 if (con->request->last == attr2)
8410 con->request->last = prev2;
8411
8412 ippDeleteAttribute(NULL, attr2);
8413 }
8414
8415 /*
8416 * Add new option by copying it...
8417 */
8418
8419 ippCopyAttribute(con->request, attr, 0);
8420 }
8421
8422 /*
8423 * Then free the attribute list and option array...
8424 */
8425
8426 ippDelete(ticket);
8427 cupsFreeOptions(num_options, options);
8428 }
8429
8430
8431 /*
8432 * 'reject_jobs()' - Reject print jobs to a printer.
8433 */
8434
8435 static void
8436 reject_jobs(cupsd_client_t *con, /* I - Client connection */
8437 ipp_attribute_t *uri) /* I - Printer or class URI */
8438 {
8439 http_status_t status; /* Policy status */
8440 cups_ptype_t dtype; /* Destination type (printer/class) */
8441 cupsd_printer_t *printer; /* Printer data */
8442 ipp_attribute_t *attr; /* printer-state-message text */
8443
8444
8445 cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", con,
8446 con->number, uri->values[0].string.text);
8447
8448 /*
8449 * Is the destination valid?
8450 */
8451
8452 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8453 {
8454 /*
8455 * Bad URI...
8456 */
8457
8458 send_ipp_status(con, IPP_NOT_FOUND,
8459 _("The printer or class does not exist."));
8460 return;
8461 }
8462
8463 /*
8464 * Check policy...
8465 */
8466
8467 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8468 {
8469 send_http_error(con, status, printer);
8470 return;
8471 }
8472
8473 /*
8474 * Reject jobs sent to the printer...
8475 */
8476
8477 printer->accepting = 0;
8478
8479 if ((attr = ippFindAttribute(con->request, "printer-state-message",
8480 IPP_TAG_TEXT)) == NULL)
8481 strlcpy(printer->state_message, "Rejecting Jobs",
8482 sizeof(printer->state_message));
8483 else
8484 strlcpy(printer->state_message, attr->values[0].string.text,
8485 sizeof(printer->state_message));
8486
8487 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
8488 "No longer accepting jobs.");
8489
8490 if (dtype & CUPS_PRINTER_CLASS)
8491 {
8492 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
8493
8494 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
8495 printer->name, get_username(con));
8496 }
8497 else
8498 {
8499 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
8500
8501 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
8502 printer->name, get_username(con));
8503 }
8504
8505 /*
8506 * Everything was ok, so return OK status...
8507 */
8508
8509 con->response->request.status.status_code = IPP_OK;
8510 }
8511
8512
8513 /*
8514 * 'release_held_new_jobs()' - Release pending/new jobs on a printer or class.
8515 */
8516
8517 static void
8518 release_held_new_jobs(
8519 cupsd_client_t *con, /* I - Connection */
8520 ipp_attribute_t *uri) /* I - Printer URI */
8521 {
8522 http_status_t status; /* Policy status */
8523 cups_ptype_t dtype; /* Destination type (printer/class) */
8524 cupsd_printer_t *printer; /* Printer data */
8525
8526
8527 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_held_new_jobs(%p[%d], %s)", con,
8528 con->number, uri->values[0].string.text);
8529
8530 /*
8531 * Is the destination valid?
8532 */
8533
8534 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8535 {
8536 /*
8537 * Bad URI...
8538 */
8539
8540 send_ipp_status(con, IPP_NOT_FOUND,
8541 _("The printer or class does not exist."));
8542 return;
8543 }
8544
8545 /*
8546 * Check policy...
8547 */
8548
8549 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8550 {
8551 send_http_error(con, status, printer);
8552 return;
8553 }
8554
8555 /*
8556 * Hold pending/new jobs sent to the printer...
8557 */
8558
8559 printer->holding_new_jobs = 0;
8560
8561 cupsdSetPrinterReasons(printer, "-hold-new-jobs");
8562
8563 if (dtype & CUPS_PRINTER_CLASS)
8564 cupsdLogMessage(CUPSD_LOG_INFO,
8565 "Class \"%s\" now printing pending/new jobs (\"%s\").",
8566 printer->name, get_username(con));
8567 else
8568 cupsdLogMessage(CUPSD_LOG_INFO,
8569 "Printer \"%s\" now printing pending/new jobs (\"%s\").",
8570 printer->name, get_username(con));
8571
8572 /*
8573 * Everything was ok, so return OK status...
8574 */
8575
8576 con->response->request.status.status_code = IPP_OK;
8577 }
8578
8579
8580 /*
8581 * 'release_job()' - Release a held print job.
8582 */
8583
8584 static void
8585 release_job(cupsd_client_t *con, /* I - Client connection */
8586 ipp_attribute_t *uri) /* I - Job or Printer URI */
8587 {
8588 ipp_attribute_t *attr; /* Current attribute */
8589 int jobid; /* Job ID */
8590 char scheme[HTTP_MAX_URI], /* Method portion of URI */
8591 username[HTTP_MAX_URI], /* Username portion of URI */
8592 host[HTTP_MAX_URI], /* Host portion of URI */
8593 resource[HTTP_MAX_URI]; /* Resource portion of URI */
8594 int port; /* Port portion of URI */
8595 cupsd_job_t *job; /* Job information */
8596
8597
8598 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", con,
8599 con->number, uri->values[0].string.text);
8600
8601 /*
8602 * See if we have a job URI or a printer URI...
8603 */
8604
8605 if (!strcmp(uri->name, "printer-uri"))
8606 {
8607 /*
8608 * Got a printer URI; see if we also have a job-id attribute...
8609 */
8610
8611 if ((attr = ippFindAttribute(con->request, "job-id",
8612 IPP_TAG_INTEGER)) == NULL)
8613 {
8614 send_ipp_status(con, IPP_BAD_REQUEST,
8615 _("Got a printer-uri attribute but no job-id."));
8616 return;
8617 }
8618
8619 jobid = attr->values[0].integer;
8620 }
8621 else
8622 {
8623 /*
8624 * Got a job URI; parse it to get the job ID...
8625 */
8626
8627 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8628 sizeof(scheme), username, sizeof(username), host,
8629 sizeof(host), &port, resource, sizeof(resource));
8630
8631 if (strncmp(resource, "/jobs/", 6))
8632 {
8633 /*
8634 * Not a valid URI!
8635 */
8636
8637 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
8638 uri->values[0].string.text);
8639 return;
8640 }
8641
8642 jobid = atoi(resource + 6);
8643 }
8644
8645 /*
8646 * See if the job exists...
8647 */
8648
8649 if ((job = cupsdFindJob(jobid)) == NULL)
8650 {
8651 /*
8652 * Nope - return a "not found" error...
8653 */
8654
8655 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
8656 return;
8657 }
8658
8659 /*
8660 * See if job is "held"...
8661 */
8662
8663 if (job->state_value != IPP_JOB_HELD)
8664 {
8665 /*
8666 * Nope - return a "not possible" error...
8667 */
8668
8669 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held."), jobid);
8670 return;
8671 }
8672
8673 /*
8674 * See if the job is owned by the requesting user...
8675 */
8676
8677 if (!validate_user(job, con, job->username, username, sizeof(username)))
8678 {
8679 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
8680 cupsdFindDest(job->dest));
8681 return;
8682 }
8683
8684 /*
8685 * Reset the job-hold-until value to "no-hold"...
8686 */
8687
8688 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
8689 IPP_TAG_KEYWORD)) == NULL)
8690 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
8691
8692 if (attr)
8693 {
8694 ippSetValueTag(job->attrs, &attr, IPP_TAG_KEYWORD);
8695 ippSetString(job->attrs, &attr, 0, "no-hold");
8696
8697 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
8698 "Job job-hold-until value changed by user.");
8699 ippSetString(job->attrs, &job->reasons, 0, "none");
8700 }
8701
8702 /*
8703 * Release the job and return...
8704 */
8705
8706 cupsdReleaseJob(job);
8707
8708 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
8709 "Job released by user.");
8710
8711 cupsdLogJob(job, CUPSD_LOG_INFO, "Released by \"%s\".", username);
8712
8713 con->response->request.status.status_code = IPP_OK;
8714
8715 cupsdCheckJobs();
8716 }
8717
8718
8719 /*
8720 * 'renew_subscription()' - Renew an existing subscription...
8721 */
8722
8723 static void
8724 renew_subscription(
8725 cupsd_client_t *con, /* I - Client connection */
8726 int sub_id) /* I - Subscription ID */
8727 {
8728 http_status_t status; /* Policy status */
8729 cupsd_subscription_t *sub; /* Subscription */
8730 ipp_attribute_t *lease; /* notify-lease-duration */
8731
8732
8733 cupsdLogMessage(CUPSD_LOG_DEBUG2,
8734 "renew_subscription(con=%p[%d], sub_id=%d)",
8735 con, con->number, sub_id);
8736
8737 /*
8738 * Is the subscription ID valid?
8739 */
8740
8741 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
8742 {
8743 /*
8744 * Bad subscription ID...
8745 */
8746
8747 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
8748 sub_id);
8749 return;
8750 }
8751
8752 if (sub->job)
8753 {
8754 /*
8755 * Job subscriptions cannot be renewed...
8756 */
8757
8758 send_ipp_status(con, IPP_NOT_POSSIBLE,
8759 _("Job subscriptions cannot be renewed."));
8760 return;
8761 }
8762
8763 /*
8764 * Check policy...
8765 */
8766
8767 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
8768 DefaultPolicyPtr,
8769 con, sub->owner)) != HTTP_OK)
8770 {
8771 send_http_error(con, status, sub->dest);
8772 return;
8773 }
8774
8775 /*
8776 * Renew the subscription...
8777 */
8778
8779 lease = ippFindAttribute(con->request, "notify-lease-duration",
8780 IPP_TAG_INTEGER);
8781
8782 sub->lease = lease ? lease->values[0].integer : DefaultLeaseDuration;
8783
8784 if (MaxLeaseDuration && (sub->lease == 0 || sub->lease > MaxLeaseDuration))
8785 {
8786 cupsdLogMessage(CUPSD_LOG_INFO,
8787 "renew_subscription: Limiting notify-lease-duration to "
8788 "%d seconds.",
8789 MaxLeaseDuration);
8790 sub->lease = MaxLeaseDuration;
8791 }
8792
8793 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
8794
8795 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
8796
8797 con->response->request.status.status_code = IPP_OK;
8798
8799 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
8800 "notify-lease-duration", sub->lease);
8801 }
8802
8803
8804 /*
8805 * 'restart_job()' - Restart an old print job.
8806 */
8807
8808 static void
8809 restart_job(cupsd_client_t *con, /* I - Client connection */
8810 ipp_attribute_t *uri) /* I - Job or Printer URI */
8811 {
8812 ipp_attribute_t *attr; /* Current attribute */
8813 int jobid; /* Job ID */
8814 cupsd_job_t *job; /* Job information */
8815 char scheme[HTTP_MAX_URI], /* Method portion of URI */
8816 username[HTTP_MAX_URI], /* Username portion of URI */
8817 host[HTTP_MAX_URI], /* Host portion of URI */
8818 resource[HTTP_MAX_URI]; /* Resource portion of URI */
8819 int port; /* Port portion of URI */
8820
8821
8822 cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con,
8823 con->number, uri->values[0].string.text);
8824
8825 /*
8826 * See if we have a job URI or a printer URI...
8827 */
8828
8829 if (!strcmp(uri->name, "printer-uri"))
8830 {
8831 /*
8832 * Got a printer URI; see if we also have a job-id attribute...
8833 */
8834
8835 if ((attr = ippFindAttribute(con->request, "job-id",
8836 IPP_TAG_INTEGER)) == NULL)
8837 {
8838 send_ipp_status(con, IPP_BAD_REQUEST,
8839 _("Got a printer-uri attribute but no job-id."));
8840 return;
8841 }
8842
8843 jobid = attr->values[0].integer;
8844 }
8845 else
8846 {
8847 /*
8848 * Got a job URI; parse it to get the job ID...
8849 */
8850
8851 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8852 sizeof(scheme), username, sizeof(username), host,
8853 sizeof(host), &port, resource, sizeof(resource));
8854
8855 if (strncmp(resource, "/jobs/", 6))
8856 {
8857 /*
8858 * Not a valid URI!
8859 */
8860
8861 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
8862 uri->values[0].string.text);
8863 return;
8864 }
8865
8866 jobid = atoi(resource + 6);
8867 }
8868
8869 /*
8870 * See if the job exists...
8871 */
8872
8873 if ((job = cupsdFindJob(jobid)) == NULL)
8874 {
8875 /*
8876 * Nope - return a "not found" error...
8877 */
8878
8879 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
8880 return;
8881 }
8882
8883 /*
8884 * See if job is in any of the "completed" states...
8885 */
8886
8887 if (job->state_value <= IPP_JOB_PROCESSING)
8888 {
8889 /*
8890 * Nope - return a "not possible" error...
8891 */
8892
8893 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete."),
8894 jobid);
8895 return;
8896 }
8897
8898 /*
8899 * See if we have retained the job files...
8900 */
8901
8902 cupsdLoadJob(job);
8903
8904 if (!job->attrs || job->num_files == 0)
8905 {
8906 /*
8907 * Nope - return a "not possible" error...
8908 */
8909
8910 send_ipp_status(con, IPP_NOT_POSSIBLE,
8911 _("Job #%d cannot be restarted - no files."), jobid);
8912 return;
8913 }
8914
8915 /*
8916 * See if the job is owned by the requesting user...
8917 */
8918
8919 if (!validate_user(job, con, job->username, username, sizeof(username)))
8920 {
8921 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
8922 cupsdFindDest(job->dest));
8923 return;
8924 }
8925
8926 /*
8927 * See if the job-hold-until attribute is specified...
8928 */
8929
8930 if ((attr = ippFindAttribute(con->request, "job-hold-until",
8931 IPP_TAG_KEYWORD)) == NULL)
8932 attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
8933
8934 if (attr && strcmp(attr->values[0].string.text, "no-hold"))
8935 {
8936 /*
8937 * Return the job to a held state...
8938 */
8939
8940 cupsdLogJob(job, CUPSD_LOG_DEBUG,
8941 "Restarted by \"%s\" with job-hold-until=%s.",
8942 username, attr->values[0].string.text);
8943 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
8944
8945 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE,
8946 NULL, job, "Job restarted by user with job-hold-until=%s",
8947 attr->values[0].string.text);
8948 }
8949 else
8950 {
8951 /*
8952 * Restart the job...
8953 */
8954
8955 cupsdRestartJob(job);
8956 cupsdCheckJobs();
8957 }
8958
8959 cupsdLogJob(job, CUPSD_LOG_INFO, "Restarted by \"%s\".", username);
8960
8961 con->response->request.status.status_code = IPP_OK;
8962 }
8963
8964
8965 /*
8966 * 'save_auth_info()' - Save authentication information for a job.
8967 */
8968
8969 static void
8970 save_auth_info(
8971 cupsd_client_t *con, /* I - Client connection */
8972 cupsd_job_t *job, /* I - Job */
8973 ipp_attribute_t *auth_info) /* I - auth-info attribute, if any */
8974 {
8975 int i; /* Looping var */
8976 char filename[1024]; /* Job authentication filename */
8977 cups_file_t *fp; /* Job authentication file */
8978 char line[65536]; /* Line for file */
8979 cupsd_printer_t *dest; /* Destination printer/class */
8980
8981
8982 /*
8983 * This function saves the in-memory authentication information for
8984 * a job so that it can be used to authenticate with a remote host.
8985 * The information is stored in a file that is readable only by the
8986 * root user. The fields are Base-64 encoded, each on a separate line,
8987 * followed by random number (up to 1024) of newlines to limit the
8988 * amount of information that is exposed.
8989 *
8990 * Because of the potential for exposing of authentication information,
8991 * this functionality is only enabled when running cupsd as root.
8992 *
8993 * This caching only works for the Basic and BasicDigest authentication
8994 * types. Digest authentication cannot be cached this way, and in
8995 * the future Kerberos authentication may make all of this obsolete.
8996 *
8997 * Authentication information is saved whenever an authenticated
8998 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
8999 * performed.
9000 *
9001 * This information is deleted after a job is completed or canceled,
9002 * so reprints may require subsequent re-authentication.
9003 */
9004
9005 if (RunUser)
9006 return;
9007
9008 if ((dest = cupsdFindDest(job->dest)) == NULL)
9009 return;
9010
9011 /*
9012 * Create the authentication file and change permissions...
9013 */
9014
9015 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
9016 if ((fp = cupsFileOpen(filename, "w")) == NULL)
9017 {
9018 cupsdLogMessage(CUPSD_LOG_ERROR,
9019 "Unable to save authentication info to \"%s\" - %s",
9020 filename, strerror(errno));
9021 return;
9022 }
9023
9024 fchown(cupsFileNumber(fp), 0, 0);
9025 fchmod(cupsFileNumber(fp), 0400);
9026
9027 cupsFilePuts(fp, "CUPSD-AUTH-V3\n");
9028
9029 for (i = 0;
9030 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
9031 i ++)
9032 cupsdClearString(job->auth_env + i);
9033
9034 if (auth_info && auth_info->num_values == dest->num_auth_info_required)
9035 {
9036 /*
9037 * Write 1 to 3 auth values...
9038 */
9039
9040 for (i = 0;
9041 i < auth_info->num_values &&
9042 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
9043 i ++)
9044 {
9045 if (strcmp(dest->auth_info_required[i], "negotiate"))
9046 {
9047 httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text, (int)strlen(auth_info->values[i].string.text));
9048 cupsFilePutConf(fp, dest->auth_info_required[i], line);
9049 }
9050 else
9051 cupsFilePutConf(fp, dest->auth_info_required[i],
9052 auth_info->values[i].string.text);
9053
9054 if (!strcmp(dest->auth_info_required[i], "username"))
9055 cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s",
9056 auth_info->values[i].string.text);
9057 else if (!strcmp(dest->auth_info_required[i], "domain"))
9058 cupsdSetStringf(job->auth_env + i, "AUTH_DOMAIN=%s",
9059 auth_info->values[i].string.text);
9060 else if (!strcmp(dest->auth_info_required[i], "password"))
9061 cupsdSetStringf(job->auth_env + i, "AUTH_PASSWORD=%s",
9062 auth_info->values[i].string.text);
9063 else if (!strcmp(dest->auth_info_required[i], "negotiate"))
9064 cupsdSetStringf(job->auth_env + i, "AUTH_NEGOTIATE=%s",
9065 auth_info->values[i].string.text);
9066 else
9067 i --;
9068 }
9069 }
9070 else if (auth_info && auth_info->num_values == 2 &&
9071 dest->num_auth_info_required == 1 &&
9072 !strcmp(dest->auth_info_required[0], "negotiate"))
9073 {
9074 /*
9075 * Allow fallback to username+password for Kerberized queues...
9076 */
9077
9078 httpEncode64_2(line, sizeof(line), auth_info->values[0].string.text, (int)strlen(auth_info->values[0].string.text));
9079 cupsFilePutConf(fp, "username", line);
9080
9081 cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s",
9082 auth_info->values[0].string.text);
9083
9084 httpEncode64_2(line, sizeof(line), auth_info->values[1].string.text, (int)strlen(auth_info->values[1].string.text));
9085 cupsFilePutConf(fp, "password", line);
9086
9087 cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s",
9088 auth_info->values[1].string.text);
9089 }
9090 else if (con->username[0])
9091 {
9092 /*
9093 * Write the authenticated username...
9094 */
9095
9096 httpEncode64_2(line, sizeof(line), con->username, (int)strlen(con->username));
9097 cupsFilePutConf(fp, "username", line);
9098
9099 cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s", con->username);
9100
9101 /*
9102 * Write the authenticated password...
9103 */
9104
9105 httpEncode64_2(line, sizeof(line), con->password, (int)strlen(con->password));
9106 cupsFilePutConf(fp, "password", line);
9107
9108 cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s", con->password);
9109 }
9110
9111 #ifdef HAVE_GSSAPI
9112 if (con->gss_uid > 0)
9113 {
9114 cupsFilePrintf(fp, "uid %d\n", (int)con->gss_uid);
9115 cupsdSetStringf(&job->auth_uid, "AUTH_UID=%d", (int)con->gss_uid);
9116 }
9117 #endif /* HAVE_GSSAPI */
9118
9119 /*
9120 * Write a random number of newlines to the end of the file...
9121 */
9122
9123 for (i = (CUPS_RAND() % 1024); i >= 0; i --)
9124 cupsFilePutChar(fp, '\n');
9125
9126 /*
9127 * Close the file and return...
9128 */
9129
9130 cupsFileClose(fp);
9131 }
9132
9133
9134 /*
9135 * 'send_document()' - Send a file to a printer or class.
9136 */
9137
9138 static void
9139 send_document(cupsd_client_t *con, /* I - Client connection */
9140 ipp_attribute_t *uri) /* I - Printer URI */
9141 {
9142 ipp_attribute_t *attr; /* Current attribute */
9143 ipp_attribute_t *format; /* Request's document-format attribute */
9144 ipp_attribute_t *jformat; /* Job's document-format attribute */
9145 const char *default_format;/* document-format-default value */
9146 int jobid; /* Job ID number */
9147 cupsd_job_t *job; /* Current job */
9148 char job_uri[HTTP_MAX_URI],
9149 /* Job URI */
9150 scheme[HTTP_MAX_URI],
9151 /* Method portion of URI */
9152 username[HTTP_MAX_URI],
9153 /* Username portion of URI */
9154 host[HTTP_MAX_URI],
9155 /* Host portion of URI */
9156 resource[HTTP_MAX_URI];
9157 /* Resource portion of URI */
9158 int port; /* Port portion of URI */
9159 mime_type_t *filetype; /* Type of file */
9160 char super[MIME_MAX_SUPER],
9161 /* Supertype of file */
9162 type[MIME_MAX_TYPE],
9163 /* Subtype of file */
9164 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
9165 /* Textual name of mime type */
9166 char filename[1024]; /* Job filename */
9167 cupsd_printer_t *printer; /* Current printer */
9168 struct stat fileinfo; /* File information */
9169 int kbytes; /* Size of file */
9170 int compression; /* Type of compression */
9171 int start_job; /* Start the job? */
9172
9173
9174 cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con,
9175 con->number, uri->values[0].string.text);
9176
9177 /*
9178 * See if we have a job URI or a printer URI...
9179 */
9180
9181 if (!strcmp(uri->name, "printer-uri"))
9182 {
9183 /*
9184 * Got a printer URI; see if we also have a job-id attribute...
9185 */
9186
9187 if ((attr = ippFindAttribute(con->request, "job-id",
9188 IPP_TAG_INTEGER)) == NULL)
9189 {
9190 send_ipp_status(con, IPP_BAD_REQUEST,
9191 _("Got a printer-uri attribute but no job-id."));
9192 return;
9193 }
9194
9195 jobid = attr->values[0].integer;
9196 }
9197 else
9198 {
9199 /*
9200 * Got a job URI; parse it to get the job ID...
9201 */
9202
9203 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9204 sizeof(scheme), username, sizeof(username), host,
9205 sizeof(host), &port, resource, sizeof(resource));
9206
9207 if (strncmp(resource, "/jobs/", 6))
9208 {
9209 /*
9210 * Not a valid URI!
9211 */
9212
9213 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
9214 uri->values[0].string.text);
9215 return;
9216 }
9217
9218 jobid = atoi(resource + 6);
9219 }
9220
9221 /*
9222 * See if the job exists...
9223 */
9224
9225 if ((job = cupsdFindJob(jobid)) == NULL)
9226 {
9227 /*
9228 * Nope - return a "not found" error...
9229 */
9230
9231 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
9232 return;
9233 }
9234
9235 printer = cupsdFindDest(job->dest);
9236
9237 /*
9238 * See if the job is owned by the requesting user...
9239 */
9240
9241 if (!validate_user(job, con, job->username, username, sizeof(username)))
9242 {
9243 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9244 cupsdFindDest(job->dest));
9245 return;
9246 }
9247
9248 /*
9249 * OK, see if the client is sending the document compressed - CUPS
9250 * only supports "none" and "gzip".
9251 */
9252
9253 compression = CUPS_FILE_NONE;
9254
9255 if ((attr = ippFindAttribute(con->request, "compression",
9256 IPP_TAG_KEYWORD)) != NULL)
9257 {
9258 if (strcmp(attr->values[0].string.text, "none")
9259 #ifdef HAVE_LIBZ
9260 && strcmp(attr->values[0].string.text, "gzip")
9261 #endif /* HAVE_LIBZ */
9262 )
9263 {
9264 send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\"."),
9265 attr->values[0].string.text);
9266 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
9267 "compression", NULL, attr->values[0].string.text);
9268 return;
9269 }
9270
9271 #ifdef HAVE_LIBZ
9272 if (!strcmp(attr->values[0].string.text, "gzip"))
9273 compression = CUPS_FILE_GZIP;
9274 #endif /* HAVE_LIBZ */
9275 }
9276
9277 /*
9278 * Do we have a file to print?
9279 */
9280
9281 if ((attr = ippFindAttribute(con->request, "last-document",
9282 IPP_TAG_BOOLEAN)) == NULL)
9283 {
9284 send_ipp_status(con, IPP_BAD_REQUEST,
9285 _("Missing last-document attribute in request."));
9286 return;
9287 }
9288
9289 if (!con->filename)
9290 {
9291 /*
9292 * Check for an empty request with "last-document" set to true, which is
9293 * used to close an "open" job by RFC 2911, section 3.3.2.
9294 */
9295
9296 if (job->num_files > 0 && attr->values[0].boolean)
9297 goto last_document;
9298
9299 send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
9300 return;
9301 }
9302
9303 /*
9304 * Is it a format we support?
9305 */
9306
9307 if ((format = ippFindAttribute(con->request, "document-format",
9308 IPP_TAG_MIMETYPE)) != NULL)
9309 {
9310 /*
9311 * Grab format from client...
9312 */
9313
9314 if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]",
9315 super, type) != 2)
9316 {
9317 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"."),
9318 format->values[0].string.text);
9319 return;
9320 }
9321 }
9322 else if ((default_format = cupsGetOption("document-format",
9323 printer->num_options,
9324 printer->options)) != NULL)
9325 {
9326 /*
9327 * Use default document format...
9328 */
9329
9330 if (sscanf(default_format, "%15[^/]/%255[^;]", super, type) != 2)
9331 {
9332 send_ipp_status(con, IPP_BAD_REQUEST,
9333 _("Bad document-format-default \"%s\"."), default_format);
9334 return;
9335 }
9336 }
9337 else
9338 {
9339 /*
9340 * No document format attribute? Auto-type it!
9341 */
9342
9343 strlcpy(super, "application", sizeof(super));
9344 strlcpy(type, "octet-stream", sizeof(type));
9345 }
9346
9347 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
9348 {
9349 /*
9350 * Auto-type the file...
9351 */
9352
9353 ipp_attribute_t *doc_name; /* document-name attribute */
9354
9355
9356 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Auto-typing file...");
9357
9358 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
9359 filetype = mimeFileType(MimeDatabase, con->filename,
9360 doc_name ? doc_name->values[0].string.text : NULL,
9361 &compression);
9362
9363 if (!filetype)
9364 filetype = mimeType(MimeDatabase, super, type);
9365
9366 if (filetype)
9367 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Request file type is %s/%s.",
9368 filetype->super, filetype->type);
9369 }
9370 else
9371 filetype = mimeType(MimeDatabase, super, type);
9372
9373 if (filetype)
9374 {
9375 /*
9376 * Replace the document-format attribute value with the auto-typed or
9377 * default one.
9378 */
9379
9380 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9381 filetype->type);
9382
9383 if ((jformat = ippFindAttribute(job->attrs, "document-format",
9384 IPP_TAG_MIMETYPE)) != NULL)
9385 ippSetString(job->attrs, &jformat, 0, mimetype);
9386 else
9387 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
9388 "document-format", NULL, mimetype);
9389 }
9390 else if (!filetype)
9391 {
9392 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
9393 _("Unsupported document-format \"%s/%s\"."), super, type);
9394 cupsdLogMessage(CUPSD_LOG_INFO,
9395 "Hint: Do you have the raw file printing rules enabled?");
9396
9397 if (format)
9398 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9399 "document-format", NULL, format->values[0].string.text);
9400
9401 return;
9402 }
9403
9404 if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
9405 {
9406 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9407 filetype->type);
9408
9409 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
9410 _("Unsupported document-format \"%s\"."), mimetype);
9411
9412 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9413 "document-format", NULL, mimetype);
9414
9415 return;
9416 }
9417
9418 /*
9419 * Add the file to the job...
9420 */
9421
9422 cupsdLoadJob(job);
9423
9424 if (add_file(con, job, filetype, compression))
9425 return;
9426
9427 if (stat(con->filename, &fileinfo))
9428 kbytes = 0;
9429 else
9430 kbytes = (fileinfo.st_size + 1023) / 1024;
9431
9432 cupsdUpdateQuota(printer, job->username, 0, kbytes);
9433
9434 job->koctets += kbytes;
9435
9436 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
9437 attr->values[0].integer += kbytes;
9438
9439 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
9440 job->num_files);
9441 rename(con->filename, filename);
9442
9443 cupsdClearString(&con->filename);
9444
9445 cupsdLogJob(job, CUPSD_LOG_INFO, "File of type %s/%s queued by \"%s\".",
9446 filetype->super, filetype->type, job->username);
9447
9448 /*
9449 * Start the job if this is the last document...
9450 */
9451
9452 last_document:
9453
9454 if ((attr = ippFindAttribute(con->request, "last-document",
9455 IPP_TAG_BOOLEAN)) != NULL &&
9456 attr->values[0].boolean)
9457 {
9458 /*
9459 * See if we need to add the ending sheet...
9460 */
9461
9462 if (cupsdTimeoutJob(job))
9463 return;
9464
9465 if (job->state_value == IPP_JOB_STOPPED)
9466 {
9467 job->state->values[0].integer = IPP_JOB_PENDING;
9468 job->state_value = IPP_JOB_PENDING;
9469
9470 ippSetString(job->attrs, &job->reasons, 0, "none");
9471 }
9472 else if (job->state_value == IPP_JOB_HELD)
9473 {
9474 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9475 IPP_TAG_KEYWORD)) == NULL)
9476 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9477
9478 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
9479 {
9480 job->state->values[0].integer = IPP_JOB_PENDING;
9481 job->state_value = IPP_JOB_PENDING;
9482
9483 ippSetString(job->attrs, &job->reasons, 0, "none");
9484 }
9485 else
9486 ippSetString(job->attrs, &job->reasons, 0, "job-hold-until-specified");
9487 }
9488
9489 job->dirty = 1;
9490 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
9491
9492 start_job = 1;
9493 }
9494 else
9495 {
9496 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9497 IPP_TAG_KEYWORD)) == NULL)
9498 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9499
9500 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
9501 {
9502 job->state->values[0].integer = IPP_JOB_HELD;
9503 job->state_value = IPP_JOB_HELD;
9504 job->hold_until = time(NULL) + MultipleOperationTimeout;
9505
9506 ippSetString(job->attrs, &job->reasons, 0, "job-incoming");
9507
9508 job->dirty = 1;
9509 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
9510 }
9511
9512 start_job = 0;
9513 }
9514
9515 /*
9516 * Fill in the response info...
9517 */
9518
9519 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
9520 con->clientname, con->clientport, "/jobs/%d", jobid);
9521 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
9522 job_uri);
9523
9524 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
9525
9526 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
9527 job->state_value);
9528 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons",
9529 NULL, job->reasons->values[0].string.text);
9530
9531 con->response->request.status.status_code = IPP_OK;
9532
9533 /*
9534 * Start the job if necessary...
9535 */
9536
9537 if (start_job)
9538 cupsdCheckJobs();
9539 }
9540
9541
9542 /*
9543 * 'send_http_error()' - Send a HTTP error back to the IPP client.
9544 */
9545
9546 static void
9547 send_http_error(
9548 cupsd_client_t *con, /* I - Client connection */
9549 http_status_t status, /* I - HTTP status code */
9550 cupsd_printer_t *printer) /* I - Printer, if any */
9551 {
9552 ipp_attribute_t *uri; /* Request URI, if any */
9553
9554
9555 if ((uri = ippFindAttribute(con->request, "printer-uri",
9556 IPP_TAG_URI)) == NULL)
9557 uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI);
9558
9559 cupsdLogMessage(status == HTTP_FORBIDDEN ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
9560 "[Client %d] Returning HTTP %s for %s (%s) from %s",
9561 con->number, httpStatus(status),
9562 con->request ?
9563 ippOpString(con->request->request.op.operation_id) :
9564 "no operation-id",
9565 uri ? uri->values[0].string.text : "no URI",
9566 con->http->hostname);
9567
9568 if (printer)
9569 {
9570 int auth_type; /* Type of authentication required */
9571
9572
9573 auth_type = CUPSD_AUTH_NONE;
9574
9575 if (status == HTTP_UNAUTHORIZED &&
9576 printer->num_auth_info_required > 0 &&
9577 !strcmp(printer->auth_info_required[0], "negotiate") &&
9578 con->request &&
9579 (con->request->request.op.operation_id == IPP_PRINT_JOB ||
9580 con->request->request.op.operation_id == IPP_CREATE_JOB ||
9581 con->request->request.op.operation_id == CUPS_AUTHENTICATE_JOB))
9582 {
9583 /*
9584 * Creating and authenticating jobs requires Kerberos...
9585 */
9586
9587 auth_type = CUPSD_AUTH_NEGOTIATE;
9588 }
9589 else
9590 {
9591 /*
9592 * Use policy/location-defined authentication requirements...
9593 */
9594
9595 char resource[HTTP_MAX_URI]; /* Resource portion of URI */
9596 cupsd_location_t *auth; /* Pointer to authentication element */
9597
9598
9599 if (printer->type & CUPS_PRINTER_CLASS)
9600 snprintf(resource, sizeof(resource), "/classes/%s", printer->name);
9601 else
9602 snprintf(resource, sizeof(resource), "/printers/%s", printer->name);
9603
9604 if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
9605 auth->type == CUPSD_AUTH_NONE)
9606 auth = cupsdFindPolicyOp(printer->op_policy_ptr,
9607 con->request ?
9608 con->request->request.op.operation_id :
9609 IPP_PRINT_JOB);
9610
9611 if (auth)
9612 {
9613 if (auth->type == CUPSD_AUTH_DEFAULT)
9614 auth_type = cupsdDefaultAuthType();
9615 else
9616 auth_type = auth->type;
9617 }
9618 }
9619
9620 cupsdSendError(con, status, auth_type);
9621 }
9622 else
9623 cupsdSendError(con, status, CUPSD_AUTH_NONE);
9624
9625 ippDelete(con->response);
9626 con->response = NULL;
9627
9628 return;
9629 }
9630
9631
9632 /*
9633 * 'send_ipp_status()' - Send a status back to the IPP client.
9634 */
9635
9636 static void
9637 send_ipp_status(cupsd_client_t *con, /* I - Client connection */
9638 ipp_status_t status, /* I - IPP status code */
9639 const char *message,/* I - Status message */
9640 ...) /* I - Additional args as needed */
9641 {
9642 va_list ap; /* Pointer to additional args */
9643 char formatted[1024]; /* Formatted errror message */
9644
9645
9646 va_start(ap, message);
9647 vsnprintf(formatted, sizeof(formatted),
9648 _cupsLangString(con->language, message), ap);
9649 va_end(ap);
9650
9651 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
9652 ippOpString(con->request->request.op.operation_id),
9653 ippErrorString(status), formatted);
9654
9655 con->response->request.status.status_code = status;
9656
9657 if (ippFindAttribute(con->response, "attributes-charset",
9658 IPP_TAG_ZERO) == NULL)
9659 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
9660 "attributes-charset", NULL, "utf-8");
9661
9662 if (ippFindAttribute(con->response, "attributes-natural-language",
9663 IPP_TAG_ZERO) == NULL)
9664 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
9665 "attributes-natural-language", NULL, DefaultLanguage);
9666
9667 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
9668 "status-message", NULL, formatted);
9669 }
9670
9671
9672 /*
9673 * 'set_default()' - Set the default destination...
9674 */
9675
9676 static void
9677 set_default(cupsd_client_t *con, /* I - Client connection */
9678 ipp_attribute_t *uri) /* I - Printer URI */
9679 {
9680 http_status_t status; /* Policy status */
9681 cups_ptype_t dtype; /* Destination type (printer/class) */
9682 cupsd_printer_t *printer, /* Printer */
9683 *oldprinter; /* Old default printer */
9684
9685
9686 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con,
9687 con->number, uri->values[0].string.text);
9688
9689 /*
9690 * Is the destination valid?
9691 */
9692
9693 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
9694 {
9695 /*
9696 * Bad URI...
9697 */
9698
9699 send_ipp_status(con, IPP_NOT_FOUND,
9700 _("The printer or class does not exist."));
9701 return;
9702 }
9703
9704 /*
9705 * Check policy...
9706 */
9707
9708 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
9709 {
9710 send_http_error(con, status, NULL);
9711 return;
9712 }
9713
9714 /*
9715 * Set it as the default...
9716 */
9717
9718 oldprinter = DefaultPrinter;
9719 DefaultPrinter = printer;
9720
9721 if (oldprinter)
9722 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, oldprinter, NULL,
9723 "%s is no longer the default printer.", oldprinter->name);
9724
9725 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
9726 "%s is now the default printer.", printer->name);
9727
9728 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS | CUPSD_DIRTY_CLASSES |
9729 CUPSD_DIRTY_PRINTCAP);
9730
9731 cupsdLogMessage(CUPSD_LOG_INFO,
9732 "Default destination set to \"%s\" by \"%s\".",
9733 printer->name, get_username(con));
9734
9735 /*
9736 * Everything was ok, so return OK status...
9737 */
9738
9739 con->response->request.status.status_code = IPP_OK;
9740 }
9741
9742
9743 /*
9744 * 'set_job_attrs()' - Set job attributes.
9745 */
9746
9747 static void
9748 set_job_attrs(cupsd_client_t *con, /* I - Client connection */
9749 ipp_attribute_t *uri) /* I - Job URI */
9750 {
9751 ipp_attribute_t *attr, /* Current attribute */
9752 *attr2; /* Job attribute */
9753 int jobid; /* Job ID */
9754 cupsd_job_t *job; /* Current job */
9755 char scheme[HTTP_MAX_URI],
9756 /* Method portion of URI */
9757 username[HTTP_MAX_URI],
9758 /* Username portion of URI */
9759 host[HTTP_MAX_URI],
9760 /* Host portion of URI */
9761 resource[HTTP_MAX_URI];
9762 /* Resource portion of URI */
9763 int port; /* Port portion of URI */
9764 int event; /* Events? */
9765 int check_jobs; /* Check jobs? */
9766
9767
9768 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
9769 con->number, uri->values[0].string.text);
9770
9771 /*
9772 * Start with "everything is OK" status...
9773 */
9774
9775 con->response->request.status.status_code = IPP_OK;
9776
9777 /*
9778 * See if we have a job URI or a printer URI...
9779 */
9780
9781 if (!strcmp(uri->name, "printer-uri"))
9782 {
9783 /*
9784 * Got a printer URI; see if we also have a job-id attribute...
9785 */
9786
9787 if ((attr = ippFindAttribute(con->request, "job-id",
9788 IPP_TAG_INTEGER)) == NULL)
9789 {
9790 send_ipp_status(con, IPP_BAD_REQUEST,
9791 _("Got a printer-uri attribute but no job-id."));
9792 return;
9793 }
9794
9795 jobid = attr->values[0].integer;
9796 }
9797 else
9798 {
9799 /*
9800 * Got a job URI; parse it to get the job ID...
9801 */
9802
9803 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9804 sizeof(scheme), username, sizeof(username), host,
9805 sizeof(host), &port, resource, sizeof(resource));
9806
9807 if (strncmp(resource, "/jobs/", 6))
9808 {
9809 /*
9810 * Not a valid URI!
9811 */
9812
9813 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
9814 uri->values[0].string.text);
9815 return;
9816 }
9817
9818 jobid = atoi(resource + 6);
9819 }
9820
9821 /*
9822 * See if the job exists...
9823 */
9824
9825 if ((job = cupsdFindJob(jobid)) == NULL)
9826 {
9827 /*
9828 * Nope - return a "not found" error...
9829 */
9830
9831 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
9832 return;
9833 }
9834
9835 /*
9836 * See if the job has been completed...
9837 */
9838
9839 if (job->state_value > IPP_JOB_STOPPED)
9840 {
9841 /*
9842 * Return a "not-possible" error...
9843 */
9844
9845 send_ipp_status(con, IPP_NOT_POSSIBLE,
9846 _("Job #%d is finished and cannot be altered."), jobid);
9847 return;
9848 }
9849
9850 /*
9851 * See if the job is owned by the requesting user...
9852 */
9853
9854 if (!validate_user(job, con, job->username, username, sizeof(username)))
9855 {
9856 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9857 cupsdFindDest(job->dest));
9858 return;
9859 }
9860
9861 /*
9862 * See what the user wants to change.
9863 */
9864
9865 cupsdLoadJob(job);
9866
9867 check_jobs = 0;
9868 event = 0;
9869
9870 for (attr = con->request->attrs; attr; attr = attr->next)
9871 {
9872 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
9873 continue;
9874
9875 if (!strcmp(attr->name, "attributes-charset") ||
9876 !strcmp(attr->name, "attributes-natural-language") ||
9877 !strcmp(attr->name, "document-compression") ||
9878 !strcmp(attr->name, "document-format") ||
9879 !strcmp(attr->name, "job-detailed-status-messages") ||
9880 !strcmp(attr->name, "job-document-access-errors") ||
9881 !strcmp(attr->name, "job-id") ||
9882 !strcmp(attr->name, "job-impressions-completed") ||
9883 !strcmp(attr->name, "job-k-octets") ||
9884 !strcmp(attr->name, "job-originating-host-name") ||
9885 !strcmp(attr->name, "job-originating-user-name") ||
9886 !strcmp(attr->name, "job-printer-up-time") ||
9887 !strcmp(attr->name, "job-printer-uri") ||
9888 !strcmp(attr->name, "job-sheets") ||
9889 !strcmp(attr->name, "job-state-message") ||
9890 !strcmp(attr->name, "job-state-reasons") ||
9891 !strcmp(attr->name, "job-uri") ||
9892 !strcmp(attr->name, "number-of-documents") ||
9893 !strcmp(attr->name, "number-of-intervening-jobs") ||
9894 !strcmp(attr->name, "output-device-assigned") ||
9895 !strncmp(attr->name, "date-time-at-", 13) ||
9896 !strncmp(attr->name, "job-k-octets", 12) ||
9897 !strncmp(attr->name, "job-media-sheets", 16) ||
9898 !strncmp(attr->name, "time-at-", 8))
9899 {
9900 /*
9901 * Read-only attrs!
9902 */
9903
9904 send_ipp_status(con, IPP_ATTRIBUTES_NOT_SETTABLE,
9905 _("%s cannot be changed."), attr->name);
9906
9907 attr2 = ippCopyAttribute(con->response, attr, 0);
9908 ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
9909 continue;
9910 }
9911
9912 if (!strcmp(attr->name, "job-priority"))
9913 {
9914 /*
9915 * Change the job priority...
9916 */
9917
9918 if (attr->value_tag != IPP_TAG_INTEGER)
9919 {
9920 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value."));
9921
9922 attr2 = ippCopyAttribute(con->response, attr, 0);
9923 ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
9924 }
9925 else if (job->state_value >= IPP_JOB_PROCESSING)
9926 {
9927 send_ipp_status(con, IPP_NOT_POSSIBLE,
9928 _("Job is completed and cannot be changed."));
9929 return;
9930 }
9931 else if (con->response->request.status.status_code == IPP_OK)
9932 {
9933 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-priority to %d",
9934 attr->values[0].integer);
9935 cupsdSetJobPriority(job, attr->values[0].integer);
9936
9937 check_jobs = 1;
9938 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED |
9939 CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED;
9940 }
9941 }
9942 else if (!strcmp(attr->name, "job-state"))
9943 {
9944 /*
9945 * Change the job state...
9946 */
9947
9948 if (attr->value_tag != IPP_TAG_ENUM)
9949 {
9950 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value."));
9951
9952 attr2 = ippCopyAttribute(con->response, attr, 0);
9953 ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
9954 }
9955 else
9956 {
9957 switch (attr->values[0].integer)
9958 {
9959 case IPP_JOB_PENDING :
9960 case IPP_JOB_HELD :
9961 if (job->state_value > IPP_JOB_HELD)
9962 {
9963 send_ipp_status(con, IPP_NOT_POSSIBLE,
9964 _("Job state cannot be changed."));
9965 return;
9966 }
9967 else if (con->response->request.status.status_code == IPP_OK)
9968 {
9969 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
9970 attr->values[0].integer);
9971 cupsdSetJobState(job, (ipp_jstate_t)attr->values[0].integer, CUPSD_JOB_DEFAULT, "Job state changed by \"%s\"", username);
9972 check_jobs = 1;
9973 }
9974 break;
9975
9976 case IPP_JOB_PROCESSING :
9977 case IPP_JOB_STOPPED :
9978 if (job->state_value != attr->values[0].integer)
9979 {
9980 send_ipp_status(con, IPP_NOT_POSSIBLE,
9981 _("Job state cannot be changed."));
9982 return;
9983 }
9984 break;
9985
9986 case IPP_JOB_CANCELED :
9987 case IPP_JOB_ABORTED :
9988 case IPP_JOB_COMPLETED :
9989 if (job->state_value > IPP_JOB_PROCESSING)
9990 {
9991 send_ipp_status(con, IPP_NOT_POSSIBLE,
9992 _("Job state cannot be changed."));
9993 return;
9994 }
9995 else if (con->response->request.status.status_code == IPP_OK)
9996 {
9997 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
9998 attr->values[0].integer);
9999 cupsdSetJobState(job, (ipp_jstate_t)attr->values[0].integer,
10000 CUPSD_JOB_DEFAULT,
10001 "Job state changed by \"%s\"", username);
10002 check_jobs = 1;
10003 }
10004 break;
10005 }
10006 }
10007 }
10008 else if (con->response->request.status.status_code != IPP_OK)
10009 continue;
10010 else if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10011 IPP_TAG_ZERO)) != NULL)
10012 {
10013 /*
10014 * Some other value; first free the old value...
10015 */
10016
10017 if (job->attrs->prev)
10018 job->attrs->prev->next = attr2->next;
10019 else
10020 job->attrs->attrs = attr2->next;
10021
10022 if (job->attrs->last == attr2)
10023 job->attrs->last = job->attrs->prev;
10024
10025 ippDeleteAttribute(NULL, attr2);
10026
10027 /*
10028 * Then copy the attribute...
10029 */
10030
10031 ippCopyAttribute(job->attrs, attr, 0);
10032
10033 /*
10034 * See if the job-name or job-hold-until is being changed.
10035 */
10036
10037 if (!strcmp(attr->name, "job-hold-until"))
10038 {
10039 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-hold-until to %s",
10040 attr->values[0].string.text);
10041 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
10042
10043 if (!strcmp(attr->values[0].string.text, "no-hold"))
10044 {
10045 cupsdReleaseJob(job);
10046 check_jobs = 1;
10047 }
10048 else
10049 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT,
10050 "Job held by \"%s\".", username);
10051
10052 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
10053 }
10054 }
10055 else if (attr->value_tag == IPP_TAG_DELETEATTR)
10056 {
10057 /*
10058 * Delete the attribute...
10059 */
10060
10061 if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10062 IPP_TAG_ZERO)) != NULL)
10063 {
10064 if (job->attrs->prev)
10065 job->attrs->prev->next = attr2->next;
10066 else
10067 job->attrs->attrs = attr2->next;
10068
10069 if (attr2 == job->attrs->last)
10070 job->attrs->last = job->attrs->prev;
10071
10072 ippDeleteAttribute(NULL, attr2);
10073
10074 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
10075 }
10076 }
10077 else
10078 {
10079 /*
10080 * Add new option by copying it...
10081 */
10082
10083 ippCopyAttribute(job->attrs, attr, 0);
10084
10085 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
10086 }
10087 }
10088
10089 /*
10090 * Save the job...
10091 */
10092
10093 job->dirty = 1;
10094 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
10095
10096 /*
10097 * Send events as needed...
10098 */
10099
10100 if (event & CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED)
10101 cupsdAddEvent(CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED,
10102 cupsdFindDest(job->dest), job,
10103 "Job priority changed by user.");
10104
10105 if (event & CUPSD_EVENT_JOB_STATE)
10106 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
10107 job->state_value == IPP_JOB_HELD ?
10108 "Job held by user." : "Job restarted by user.");
10109
10110 if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
10111 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
10112 "Job options changed by user.");
10113
10114 /*
10115 * Start jobs if possible...
10116 */
10117
10118 if (check_jobs)
10119 cupsdCheckJobs();
10120 }
10121
10122
10123 /*
10124 * 'set_printer_attrs()' - Set printer attributes.
10125 */
10126
10127 static void
10128 set_printer_attrs(cupsd_client_t *con, /* I - Client connection */
10129 ipp_attribute_t *uri) /* I - Printer */
10130 {
10131 http_status_t status; /* Policy status */
10132 cups_ptype_t dtype; /* Destination type (printer/class) */
10133 cupsd_printer_t *printer; /* Printer/class */
10134 ipp_attribute_t *attr; /* Printer attribute */
10135 int changed = 0; /* Was anything changed? */
10136
10137
10138 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_attrs(%p[%d], %s)", con,
10139 con->number, uri->values[0].string.text);
10140
10141 /*
10142 * Is the destination valid?
10143 */
10144
10145 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10146 {
10147 /*
10148 * Bad URI...
10149 */
10150
10151 send_ipp_status(con, IPP_NOT_FOUND,
10152 _("The printer or class does not exist."));
10153 return;
10154 }
10155
10156 /*
10157 * Check policy...
10158 */
10159
10160 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10161 {
10162 send_http_error(con, status, printer);
10163 return;
10164 }
10165
10166 /*
10167 * Return a list of attributes that can be set via Set-Printer-Attributes.
10168 */
10169
10170 if ((attr = ippFindAttribute(con->request, "printer-location",
10171 IPP_TAG_TEXT)) != NULL)
10172 {
10173 cupsdSetString(&printer->location, attr->values[0].string.text);
10174 changed = 1;
10175 }
10176
10177 if ((attr = ippFindAttribute(con->request, "printer-info",
10178 IPP_TAG_TEXT)) != NULL)
10179 {
10180 cupsdSetString(&printer->info, attr->values[0].string.text);
10181 changed = 1;
10182 }
10183
10184 /*
10185 * Update the printer attributes and return...
10186 */
10187
10188 if (changed)
10189 {
10190 cupsdSetPrinterAttrs(printer);
10191 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
10192
10193 cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG, printer, NULL,
10194 "Printer \"%s\" description or location changed by \"%s\".",
10195 printer->name, get_username(con));
10196
10197 cupsdLogMessage(CUPSD_LOG_INFO,
10198 "Printer \"%s\" description or location changed by \"%s\".",
10199 printer->name, get_username(con));
10200 }
10201
10202 con->response->request.status.status_code = IPP_OK;
10203 }
10204
10205
10206 /*
10207 * 'set_printer_defaults()' - Set printer default options from a request.
10208 */
10209
10210 static void
10211 set_printer_defaults(
10212 cupsd_client_t *con, /* I - Client connection */
10213 cupsd_printer_t *printer) /* I - Printer */
10214 {
10215 int i; /* Looping var */
10216 ipp_attribute_t *attr; /* Current attribute */
10217 size_t namelen; /* Length of attribute name */
10218 char name[256], /* New attribute name */
10219 value[256]; /* String version of integer attrs */
10220
10221
10222 for (attr = con->request->attrs; attr; attr = attr->next)
10223 {
10224 /*
10225 * Skip non-printer attributes...
10226 */
10227
10228 if (attr->group_tag != IPP_TAG_PRINTER || !attr->name)
10229 continue;
10230
10231 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_defaults: %s", attr->name);
10232
10233 if (!strcmp(attr->name, "job-sheets-default"))
10234 {
10235 /*
10236 * Only allow keywords and names...
10237 */
10238
10239 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10240 continue;
10241
10242 /*
10243 * Only allow job-sheets-default to be set when running without a
10244 * system high classification level...
10245 */
10246
10247 if (Classification)
10248 continue;
10249
10250 cupsdSetString(&printer->job_sheets[0], attr->values[0].string.text);
10251
10252 if (attr->num_values > 1)
10253 cupsdSetString(&printer->job_sheets[1], attr->values[1].string.text);
10254 else
10255 cupsdSetString(&printer->job_sheets[1], "none");
10256 }
10257 else if (!strcmp(attr->name, "requesting-user-name-allowed"))
10258 {
10259 cupsdFreeStrings(&(printer->users));
10260
10261 printer->deny_users = 0;
10262
10263 if (attr->value_tag == IPP_TAG_NAME &&
10264 (attr->num_values > 1 ||
10265 strcmp(attr->values[0].string.text, "all")))
10266 {
10267 for (i = 0; i < attr->num_values; i ++)
10268 cupsdAddString(&(printer->users), attr->values[i].string.text);
10269 }
10270 }
10271 else if (!strcmp(attr->name, "requesting-user-name-denied"))
10272 {
10273 cupsdFreeStrings(&(printer->users));
10274
10275 printer->deny_users = 1;
10276
10277 if (attr->value_tag == IPP_TAG_NAME &&
10278 (attr->num_values > 1 ||
10279 strcmp(attr->values[0].string.text, "none")))
10280 {
10281 for (i = 0; i < attr->num_values; i ++)
10282 cupsdAddString(&(printer->users), attr->values[i].string.text);
10283 }
10284 }
10285 else if (!strcmp(attr->name, "job-quota-period"))
10286 {
10287 if (attr->value_tag != IPP_TAG_INTEGER)
10288 continue;
10289
10290 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-quota-period to %d...",
10291 attr->values[0].integer);
10292 cupsdFreeQuotas(printer);
10293
10294 printer->quota_period = attr->values[0].integer;
10295 }
10296 else if (!strcmp(attr->name, "job-k-limit"))
10297 {
10298 if (attr->value_tag != IPP_TAG_INTEGER)
10299 continue;
10300
10301 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-k-limit to %d...",
10302 attr->values[0].integer);
10303 cupsdFreeQuotas(printer);
10304
10305 printer->k_limit = attr->values[0].integer;
10306 }
10307 else if (!strcmp(attr->name, "job-page-limit"))
10308 {
10309 if (attr->value_tag != IPP_TAG_INTEGER)
10310 continue;
10311
10312 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-page-limit to %d...",
10313 attr->values[0].integer);
10314 cupsdFreeQuotas(printer);
10315
10316 printer->page_limit = attr->values[0].integer;
10317 }
10318 else if (!strcmp(attr->name, "printer-op-policy"))
10319 {
10320 cupsd_policy_t *p; /* Policy */
10321
10322
10323 if (attr->value_tag != IPP_TAG_NAME)
10324 continue;
10325
10326 if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
10327 {
10328 cupsdLogMessage(CUPSD_LOG_DEBUG,
10329 "Setting printer-op-policy to \"%s\"...",
10330 attr->values[0].string.text);
10331 cupsdSetString(&printer->op_policy, attr->values[0].string.text);
10332 printer->op_policy_ptr = p;
10333 }
10334 else
10335 {
10336 send_ipp_status(con, IPP_NOT_POSSIBLE,
10337 _("Unknown printer-op-policy \"%s\"."),
10338 attr->values[0].string.text);
10339 return;
10340 }
10341 }
10342 else if (!strcmp(attr->name, "printer-error-policy"))
10343 {
10344 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10345 continue;
10346
10347 if (strcmp(attr->values[0].string.text, "retry-current-job") &&
10348 ((printer->type & CUPS_PRINTER_CLASS) ||
10349 (strcmp(attr->values[0].string.text, "abort-job") &&
10350 strcmp(attr->values[0].string.text, "retry-job") &&
10351 strcmp(attr->values[0].string.text, "stop-printer"))))
10352 {
10353 send_ipp_status(con, IPP_NOT_POSSIBLE,
10354 _("Unknown printer-error-policy \"%s\"."),
10355 attr->values[0].string.text);
10356 return;
10357 }
10358
10359 cupsdLogMessage(CUPSD_LOG_DEBUG,
10360 "Setting printer-error-policy to \"%s\"...",
10361 attr->values[0].string.text);
10362 cupsdSetString(&printer->error_policy, attr->values[0].string.text);
10363 }
10364
10365 /*
10366 * Skip any other non-default attributes...
10367 */
10368
10369 namelen = strlen(attr->name);
10370 if (namelen < 9 || strcmp(attr->name + namelen - 8, "-default") ||
10371 namelen > (sizeof(name) - 1) || attr->num_values != 1)
10372 continue;
10373
10374 /*
10375 * OK, anything else must be a user-defined default...
10376 */
10377
10378 strlcpy(name, attr->name, sizeof(name));
10379 name[namelen - 8] = '\0'; /* Strip "-default" */
10380
10381 switch (attr->value_tag)
10382 {
10383 case IPP_TAG_DELETEATTR :
10384 printer->num_options = cupsRemoveOption(name,
10385 printer->num_options,
10386 &(printer->options));
10387 cupsdLogMessage(CUPSD_LOG_DEBUG,
10388 "Deleting %s", attr->name);
10389 break;
10390
10391 case IPP_TAG_NAME :
10392 case IPP_TAG_TEXT :
10393 case IPP_TAG_KEYWORD :
10394 case IPP_TAG_URI :
10395 printer->num_options = cupsAddOption(name,
10396 attr->values[0].string.text,
10397 printer->num_options,
10398 &(printer->options));
10399 cupsdLogMessage(CUPSD_LOG_DEBUG,
10400 "Setting %s to \"%s\"...", attr->name,
10401 attr->values[0].string.text);
10402 break;
10403
10404 case IPP_TAG_BOOLEAN :
10405 printer->num_options = cupsAddOption(name,
10406 attr->values[0].boolean ?
10407 "true" : "false",
10408 printer->num_options,
10409 &(printer->options));
10410 cupsdLogMessage(CUPSD_LOG_DEBUG,
10411 "Setting %s to %s...", attr->name,
10412 attr->values[0].boolean ? "true" : "false");
10413 break;
10414
10415 case IPP_TAG_INTEGER :
10416 case IPP_TAG_ENUM :
10417 sprintf(value, "%d", attr->values[0].integer);
10418 printer->num_options = cupsAddOption(name, value,
10419 printer->num_options,
10420 &(printer->options));
10421 cupsdLogMessage(CUPSD_LOG_DEBUG,
10422 "Setting %s to %s...", attr->name, value);
10423 break;
10424
10425 case IPP_TAG_RANGE :
10426 sprintf(value, "%d-%d", attr->values[0].range.lower,
10427 attr->values[0].range.upper);
10428 printer->num_options = cupsAddOption(name, value,
10429 printer->num_options,
10430 &(printer->options));
10431 cupsdLogMessage(CUPSD_LOG_DEBUG,
10432 "Setting %s to %s...", attr->name, value);
10433 break;
10434
10435 case IPP_TAG_RESOLUTION :
10436 sprintf(value, "%dx%d%s", attr->values[0].resolution.xres,
10437 attr->values[0].resolution.yres,
10438 attr->values[0].resolution.units == IPP_RES_PER_INCH ?
10439 "dpi" : "dpcm");
10440 printer->num_options = cupsAddOption(name, value,
10441 printer->num_options,
10442 &(printer->options));
10443 cupsdLogMessage(CUPSD_LOG_DEBUG,
10444 "Setting %s to %s...", attr->name, value);
10445 break;
10446
10447 default :
10448 /* Do nothing for other values */
10449 break;
10450 }
10451 }
10452 }
10453
10454
10455 /*
10456 * 'start_printer()' - Start a printer.
10457 */
10458
10459 static void
10460 start_printer(cupsd_client_t *con, /* I - Client connection */
10461 ipp_attribute_t *uri) /* I - Printer URI */
10462 {
10463 int i; /* Temporary variable */
10464 http_status_t status; /* Policy status */
10465 cups_ptype_t dtype; /* Destination type (printer/class) */
10466 cupsd_printer_t *printer; /* Printer data */
10467
10468
10469 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", con,
10470 con->number, uri->values[0].string.text);
10471
10472 /*
10473 * Is the destination valid?
10474 */
10475
10476 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10477 {
10478 /*
10479 * Bad URI...
10480 */
10481
10482 send_ipp_status(con, IPP_NOT_FOUND,
10483 _("The printer or class does not exist."));
10484 return;
10485 }
10486
10487 /*
10488 * Check policy...
10489 */
10490
10491 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10492 {
10493 send_http_error(con, status, printer);
10494 return;
10495 }
10496
10497 /*
10498 * Start the printer...
10499 */
10500
10501 printer->state_message[0] = '\0';
10502
10503 cupsdStartPrinter(printer, 1);
10504
10505 if (dtype & CUPS_PRINTER_CLASS)
10506 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".",
10507 printer->name, get_username(con));
10508 else
10509 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".",
10510 printer->name, get_username(con));
10511
10512 cupsdCheckJobs();
10513
10514 /*
10515 * Check quotas...
10516 */
10517
10518 if ((i = check_quotas(con, printer)) < 0)
10519 {
10520 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
10521 return;
10522 }
10523 else if (i == 0)
10524 {
10525 send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Not allowed to print."));
10526 return;
10527 }
10528
10529 /*
10530 * Everything was ok, so return OK status...
10531 */
10532
10533 con->response->request.status.status_code = IPP_OK;
10534 }
10535
10536
10537 /*
10538 * 'stop_printer()' - Stop a printer.
10539 */
10540
10541 static void
10542 stop_printer(cupsd_client_t *con, /* I - Client connection */
10543 ipp_attribute_t *uri) /* I - Printer URI */
10544 {
10545 http_status_t status; /* Policy status */
10546 cups_ptype_t dtype; /* Destination type (printer/class) */
10547 cupsd_printer_t *printer; /* Printer data */
10548 ipp_attribute_t *attr; /* printer-state-message attribute */
10549
10550
10551 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", con,
10552 con->number, uri->values[0].string.text);
10553
10554 /*
10555 * Is the destination valid?
10556 */
10557
10558 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10559 {
10560 /*
10561 * Bad URI...
10562 */
10563
10564 send_ipp_status(con, IPP_NOT_FOUND,
10565 _("The printer or class does not exist."));
10566 return;
10567 }
10568
10569 /*
10570 * Check policy...
10571 */
10572
10573 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10574 {
10575 send_http_error(con, status, printer);
10576 return;
10577 }
10578
10579 /*
10580 * Stop the printer...
10581 */
10582
10583 if ((attr = ippFindAttribute(con->request, "printer-state-message",
10584 IPP_TAG_TEXT)) == NULL)
10585 strlcpy(printer->state_message, "Paused", sizeof(printer->state_message));
10586 else
10587 {
10588 strlcpy(printer->state_message, attr->values[0].string.text,
10589 sizeof(printer->state_message));
10590 }
10591
10592 cupsdStopPrinter(printer, 1);
10593
10594 if (dtype & CUPS_PRINTER_CLASS)
10595 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".",
10596 printer->name, get_username(con));
10597 else
10598 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".",
10599 printer->name, get_username(con));
10600
10601 /*
10602 * Everything was ok, so return OK status...
10603 */
10604
10605 con->response->request.status.status_code = IPP_OK;
10606 }
10607
10608
10609 /*
10610 * 'url_encode_attr()' - URL-encode a string attribute.
10611 */
10612
10613 static void
10614 url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */
10615 char *buffer,/* I - String buffer */
10616 size_t bufsize)/* I - Size of buffer */
10617 {
10618 int i; /* Looping var */
10619 char *bufptr, /* Pointer into buffer */
10620 *bufend; /* End of buffer */
10621
10622
10623 strlcpy(buffer, attr->name, bufsize);
10624 bufptr = buffer + strlen(buffer);
10625 bufend = buffer + bufsize - 1;
10626
10627 for (i = 0; i < attr->num_values; i ++)
10628 {
10629 if (bufptr >= bufend)
10630 break;
10631
10632 if (i)
10633 *bufptr++ = ',';
10634 else
10635 *bufptr++ = '=';
10636
10637 if (bufptr >= bufend)
10638 break;
10639
10640 *bufptr++ = '\'';
10641
10642 bufptr = url_encode_string(attr->values[i].string.text, bufptr, (size_t)(bufend - bufptr + 1));
10643
10644 if (bufptr >= bufend)
10645 break;
10646
10647 *bufptr++ = '\'';
10648 }
10649
10650 *bufptr = '\0';
10651 }
10652
10653
10654 /*
10655 * 'url_encode_string()' - URL-encode a string.
10656 */
10657
10658 static char * /* O - End of string */
10659 url_encode_string(const char *s, /* I - String */
10660 char *buffer, /* I - String buffer */
10661 size_t bufsize) /* I - Size of buffer */
10662 {
10663 char *bufptr, /* Pointer into buffer */
10664 *bufend; /* End of buffer */
10665 static const char *hex = "0123456789ABCDEF";
10666 /* Hex digits */
10667
10668
10669 bufptr = buffer;
10670 bufend = buffer + bufsize - 1;
10671
10672 while (*s && bufptr < bufend)
10673 {
10674 if (*s == ' ' || *s == '%' || *s == '+')
10675 {
10676 if (bufptr >= (bufend - 2))
10677 break;
10678
10679 *bufptr++ = '%';
10680 *bufptr++ = hex[(*s >> 4) & 15];
10681 *bufptr++ = hex[*s & 15];
10682
10683 s ++;
10684 }
10685 else if (*s == '\'' || *s == '\\')
10686 {
10687 if (bufptr >= (bufend - 1))
10688 break;
10689
10690 *bufptr++ = '\\';
10691 *bufptr++ = *s++;
10692 }
10693 else
10694 *bufptr++ = *s++;
10695 }
10696
10697 *bufptr = '\0';
10698
10699 return (bufptr);
10700 }
10701
10702
10703 /*
10704 * 'user_allowed()' - See if a user is allowed to print to a queue.
10705 */
10706
10707 static int /* O - 0 if not allowed, 1 if allowed */
10708 user_allowed(cupsd_printer_t *p, /* I - Printer or class */
10709 const char *username) /* I - Username */
10710 {
10711 struct passwd *pw; /* User password data */
10712 char baseuser[256], /* Base username */
10713 *baseptr, /* Pointer to "@" in base username */
10714 *name; /* Current user name */
10715
10716
10717 if (cupsArrayCount(p->users) == 0)
10718 return (1);
10719
10720 if (!strcmp(username, "root"))
10721 return (1);
10722
10723 if (strchr(username, '@'))
10724 {
10725 /*
10726 * Strip @REALM for username check...
10727 */
10728
10729 strlcpy(baseuser, username, sizeof(baseuser));
10730
10731 if ((baseptr = strchr(baseuser, '@')) != NULL)
10732 *baseptr = '\0';
10733
10734 username = baseuser;
10735 }
10736
10737 pw = getpwnam(username);
10738 endpwent();
10739
10740 for (name = (char *)cupsArrayFirst(p->users);
10741 name;
10742 name = (char *)cupsArrayNext(p->users))
10743 {
10744 if (name[0] == '@')
10745 {
10746 /*
10747 * Check group membership...
10748 */
10749
10750 if (cupsdCheckGroup(username, pw, name + 1))
10751 break;
10752 }
10753 else if (name[0] == '#')
10754 {
10755 /*
10756 * Check UUID...
10757 */
10758
10759 if (cupsdCheckGroup(username, pw, name))
10760 break;
10761 }
10762 else if (!_cups_strcasecmp(username, name))
10763 break;
10764 }
10765
10766 return ((name != NULL) != p->deny_users);
10767 }
10768
10769
10770 /*
10771 * 'validate_job()' - Validate printer options and destination.
10772 */
10773
10774 static void
10775 validate_job(cupsd_client_t *con, /* I - Client connection */
10776 ipp_attribute_t *uri) /* I - Printer URI */
10777 {
10778 http_status_t status; /* Policy status */
10779 ipp_attribute_t *attr; /* Current attribute */
10780 #ifdef HAVE_SSL
10781 ipp_attribute_t *auth_info; /* auth-info attribute */
10782 #endif /* HAVE_SSL */
10783 ipp_attribute_t *format, /* Document-format attribute */
10784 *name; /* Job-name attribute */
10785 cups_ptype_t dtype; /* Destination type (printer/class) */
10786 char super[MIME_MAX_SUPER],
10787 /* Supertype of file */
10788 type[MIME_MAX_TYPE];
10789 /* Subtype of file */
10790 cupsd_printer_t *printer; /* Printer */
10791
10792
10793 cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", con,
10794 con->number, uri->values[0].string.text);
10795
10796 /*
10797 * OK, see if the client is sending the document compressed - CUPS
10798 * doesn't support compression yet...
10799 */
10800
10801 if ((attr = ippFindAttribute(con->request, "compression",
10802 IPP_TAG_KEYWORD)) != NULL)
10803 {
10804 if (strcmp(attr->values[0].string.text, "none")
10805 #ifdef HAVE_LIBZ
10806 && strcmp(attr->values[0].string.text, "gzip")
10807 #endif /* HAVE_LIBZ */
10808 )
10809 {
10810 send_ipp_status(con, IPP_ATTRIBUTES,
10811 _("Unsupported 'compression' value \"%s\"."),
10812 attr->values[0].string.text);
10813 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
10814 "compression", NULL, attr->values[0].string.text);
10815 return;
10816 }
10817 }
10818
10819 /*
10820 * Is it a format we support?
10821 */
10822
10823 if ((format = ippFindAttribute(con->request, "document-format",
10824 IPP_TAG_MIMETYPE)) != NULL)
10825 {
10826 if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]",
10827 super, type) != 2)
10828 {
10829 send_ipp_status(con, IPP_BAD_REQUEST,
10830 _("Bad 'document-format' value \"%s\"."),
10831 format->values[0].string.text);
10832 return;
10833 }
10834
10835 if ((strcmp(super, "application") || strcmp(type, "octet-stream")) &&
10836 !mimeType(MimeDatabase, super, type))
10837 {
10838 cupsdLogMessage(CUPSD_LOG_INFO,
10839 "Hint: Do you have the raw file printing rules enabled?");
10840 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
10841 _("Unsupported 'document-format' value \"%s\"."),
10842 format->values[0].string.text);
10843 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
10844 "document-format", NULL, format->values[0].string.text);
10845 return;
10846 }
10847 }
10848
10849 /*
10850 * Is the job-name valid?
10851 */
10852
10853 if ((name = ippFindAttribute(con->request, "job-name", IPP_TAG_ZERO)) != NULL)
10854 {
10855 int bad_name = 0; /* Is the job-name value bad? */
10856
10857 if ((name->value_tag != IPP_TAG_NAME && name->value_tag != IPP_TAG_NAMELANG) ||
10858 name->num_values != 1)
10859 {
10860 bad_name = 1;
10861 }
10862 else
10863 {
10864 /*
10865 * Validate that job-name conforms to RFC 5198 (Network Unicode) and
10866 * IPP Everywhere requirements for "name" values...
10867 */
10868
10869 const unsigned char *nameptr; /* Pointer into "job-name" attribute */
10870
10871 for (nameptr = (unsigned char *)name->values[0].string.text;
10872 *nameptr;
10873 nameptr ++)
10874 {
10875 if (*nameptr < ' ' && *nameptr != '\t')
10876 break;
10877 else if (*nameptr == 0x7f)
10878 break;
10879 else if ((*nameptr & 0xe0) == 0xc0)
10880 {
10881 if ((nameptr[1] & 0xc0) != 0x80)
10882 break;
10883
10884 nameptr ++;
10885 }
10886 else if ((*nameptr & 0xf0) == 0xe0)
10887 {
10888 if ((nameptr[1] & 0xc0) != 0x80 ||
10889 (nameptr[2] & 0xc0) != 0x80)
10890 break;
10891
10892 nameptr += 2;
10893 }
10894 else if ((*nameptr & 0xf8) == 0xf0)
10895 {
10896 if ((nameptr[1] & 0xc0) != 0x80 ||
10897 (nameptr[2] & 0xc0) != 0x80 ||
10898 (nameptr[3] & 0xc0) != 0x80)
10899 break;
10900
10901 nameptr += 3;
10902 }
10903 else if (*nameptr & 0x80)
10904 break;
10905 }
10906
10907 if (*nameptr)
10908 bad_name = 1;
10909 }
10910
10911 if (bad_name)
10912 {
10913 if (StrictConformance)
10914 {
10915 send_ipp_status(con, IPP_ATTRIBUTES,
10916 _("Unsupported 'job-name' value."));
10917 ippCopyAttribute(con->response, name, 0);
10918 return;
10919 }
10920 else
10921 {
10922 cupsdLogMessage(CUPSD_LOG_WARN,
10923 "Unsupported 'job-name' value, deleting from request.");
10924 ippDeleteAttribute(con->request, name);
10925 }
10926 }
10927 }
10928
10929 /*
10930 * Is the destination valid?
10931 */
10932
10933 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10934 {
10935 /*
10936 * Bad URI...
10937 */
10938
10939 send_ipp_status(con, IPP_NOT_FOUND,
10940 _("The printer or class does not exist."));
10941 return;
10942 }
10943
10944 /*
10945 * Check policy...
10946 */
10947
10948 #ifdef HAVE_SSL
10949 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
10950 #endif /* HAVE_SSL */
10951
10952 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10953 {
10954 send_http_error(con, status, printer);
10955 return;
10956 }
10957 else if (printer->num_auth_info_required == 1 &&
10958 !strcmp(printer->auth_info_required[0], "negotiate") &&
10959 !con->username[0])
10960 {
10961 send_http_error(con, HTTP_UNAUTHORIZED, printer);
10962 return;
10963 }
10964 #ifdef HAVE_SSL
10965 else if (auth_info && !con->http->tls &&
10966 !httpAddrLocalhost(con->http->hostaddr))
10967 {
10968 /*
10969 * Require encryption of auth-info over non-local connections...
10970 */
10971
10972 send_http_error(con, HTTP_UPGRADE_REQUIRED, printer);
10973 return;
10974 }
10975 #endif /* HAVE_SSL */
10976
10977 /*
10978 * Everything was ok, so return OK status...
10979 */
10980
10981 con->response->request.status.status_code = IPP_OK;
10982 }
10983
10984
10985 /*
10986 * 'validate_name()' - Make sure the printer name only contains valid chars.
10987 */
10988
10989 static int /* O - 0 if name is no good, 1 if good */
10990 validate_name(const char *name) /* I - Name to check */
10991 {
10992 const char *ptr; /* Pointer into name */
10993
10994
10995 /*
10996 * Scan the whole name...
10997 */
10998
10999 for (ptr = name; *ptr; ptr ++)
11000 if ((*ptr > 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
11001 return (0);
11002
11003 /*
11004 * All the characters are good; validate the length, too...
11005 */
11006
11007 return ((ptr - name) < 128);
11008 }
11009
11010
11011 /*
11012 * 'validate_user()' - Validate the user for the request.
11013 */
11014
11015 static int /* O - 1 if permitted, 0 otherwise */
11016 validate_user(cupsd_job_t *job, /* I - Job */
11017 cupsd_client_t *con, /* I - Client connection */
11018 const char *owner, /* I - Owner of job/resource */
11019 char *username, /* O - Authenticated username */
11020 size_t userlen) /* I - Length of username */
11021 {
11022 cupsd_printer_t *printer; /* Printer for job */
11023
11024
11025 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);
11026
11027 /*
11028 * Validate input...
11029 */
11030
11031 if (!con || !owner || !username || userlen <= 0)
11032 return (0);
11033
11034 /*
11035 * Get the best authenticated username that is available.
11036 */
11037
11038 strlcpy(username, get_username(con), userlen);
11039
11040 /*
11041 * Check the username against the owner...
11042 */
11043
11044 printer = cupsdFindDest(job->dest);
11045
11046 return (cupsdCheckPolicy(printer ? printer->op_policy_ptr : DefaultPolicyPtr,
11047 con, owner) == HTTP_OK);
11048 }
11049
11050
11051 /*
11052 * End of "$Id: ipp.c 12778 2015-07-07 17:28:51Z msweet $".
11053 */