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