]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/ipp.c
Implement CUPS-Create-Local-Printer operation and add test file.
[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 * Create the printer...
5387 */
5388
5389 if ((printer = cupsdAddPrinter(name)) == NULL)
5390 {
5391 send_ipp_status(con, IPP_STATUS_ERROR_INTERNAL, _("Unable to create printer."));
5392 return;
5393 }
5394
5395 cupsdSetDeviceURI(printer, ippGetString(device_uri, 0, NULL));
5396
5397 if (printer_geo_location)
5398 cupsdSetString(&printer->geo_location, ippGetString(printer_geo_location, 0, NULL));
5399 if (printer_info)
5400 cupsdSetString(&printer->info, ippGetString(printer_info, 0, NULL));
5401 if (printer_location)
5402 cupsdSetString(&printer->location, ippGetString(printer_location, 0, NULL));
5403
5404 cupsdSetPrinterAttrs(printer);
5405
5406 /*
5407 * Run a background thread to create the PPD...
5408 */
5409
5410 _cupsThreadCreate((_cups_thread_func_t)create_local_bg_thread, printer);
5411
5412 /*
5413 * Return printer attributes...
5414 */
5415
5416 send_ipp_status(con, IPP_STATUS_OK, _("Local printer created."));
5417
5418 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", (char)printer->accepting);
5419 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", printer->state);
5420 add_printer_state_reasons(con, printer);
5421
5422 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), httpIsEncrypted(con->http) ? "ipps" : "ipp", NULL, con->clientname, con->clientport, "/printers/%s", printer->name);
5423 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri);
5424 }
5425
5426
5427 /*
5428 * 'create_requested_array()' - Create an array for the requested-attributes.
5429 */
5430
5431 static cups_array_t * /* O - Array of attributes or NULL */
5432 create_requested_array(ipp_t *request) /* I - IPP request */
5433 {
5434 cups_array_t *ra; /* Requested attributes array */
5435
5436
5437 /*
5438 * Create the array for standard attributes...
5439 */
5440
5441 ra = ippCreateRequestedArray(request);
5442
5443 /*
5444 * Add CUPS defaults as needed...
5445 */
5446
5447 if (cupsArrayFind(ra, "printer-defaults"))
5448 {
5449 /*
5450 * Include user-set defaults...
5451 */
5452
5453 char *name; /* Option name */
5454
5455 cupsArrayRemove(ra, "printer-defaults");
5456
5457 for (name = (char *)cupsArrayFirst(CommonDefaults);
5458 name;
5459 name = (char *)cupsArrayNext(CommonDefaults))
5460 if (!cupsArrayFind(ra, name))
5461 cupsArrayAdd(ra, name);
5462 }
5463
5464 return (ra);
5465 }
5466
5467
5468 /*
5469 * 'create_subscriptions()' - Create one or more notification subscriptions.
5470 */
5471
5472 static void
5473 create_subscriptions(
5474 cupsd_client_t *con, /* I - Client connection */
5475 ipp_attribute_t *uri) /* I - Printer URI */
5476 {
5477 http_status_t status; /* Policy status */
5478 int i; /* Looping var */
5479 ipp_attribute_t *attr; /* Current attribute */
5480 cups_ptype_t dtype; /* Destination type (printer/class) */
5481 char scheme[HTTP_MAX_URI],
5482 /* Scheme portion of URI */
5483 userpass[HTTP_MAX_URI],
5484 /* Username portion of URI */
5485 host[HTTP_MAX_URI],
5486 /* Host portion of URI */
5487 resource[HTTP_MAX_URI];
5488 /* Resource portion of URI */
5489 int port; /* Port portion of URI */
5490 cupsd_printer_t *printer; /* Printer/class */
5491 cupsd_job_t *job; /* Job */
5492 int jobid; /* Job ID */
5493 cupsd_subscription_t *sub; /* Subscription object */
5494 const char *username, /* requesting-user-name or
5495 authenticated username */
5496 *recipient, /* notify-recipient-uri */
5497 *pullmethod; /* notify-pull-method */
5498 ipp_attribute_t *user_data; /* notify-user-data */
5499 int interval, /* notify-time-interval */
5500 lease; /* notify-lease-duration */
5501 unsigned mask; /* notify-events */
5502 ipp_attribute_t *notify_events,/* notify-events(-default) */
5503 *notify_lease; /* notify-lease-duration(-default) */
5504
5505
5506 #ifdef DEBUG
5507 for (attr = con->request->attrs; attr; attr = attr->next)
5508 {
5509 if (attr->group_tag != IPP_TAG_ZERO)
5510 cupsdLogMessage(CUPSD_LOG_DEBUG2, "g%04x v%04x %s", attr->group_tag,
5511 attr->value_tag, attr->name);
5512 else
5513 cupsdLogMessage(CUPSD_LOG_DEBUG2, "----SEP----");
5514 }
5515 #endif /* DEBUG */
5516
5517 /*
5518 * Is the destination valid?
5519 */
5520
5521 cupsdLogMessage(CUPSD_LOG_DEBUG, "create_subscriptions(con=%p(%d), uri=\"%s\")", con, con->number, uri->values[0].string.text);
5522
5523 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
5524 sizeof(scheme), userpass, sizeof(userpass), host,
5525 sizeof(host), &port, resource, sizeof(resource));
5526
5527 if (!strcmp(resource, "/"))
5528 {
5529 dtype = (cups_ptype_t)0;
5530 printer = NULL;
5531 }
5532 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
5533 {
5534 dtype = (cups_ptype_t)0;
5535 printer = NULL;
5536 }
5537 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
5538 {
5539 dtype = CUPS_PRINTER_CLASS;
5540 printer = NULL;
5541 }
5542 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
5543 {
5544 /*
5545 * Bad URI...
5546 */
5547
5548 send_ipp_status(con, IPP_NOT_FOUND,
5549 _("The printer or class does not exist."));
5550 return;
5551 }
5552
5553 /*
5554 * Check policy...
5555 */
5556
5557 if (printer)
5558 {
5559 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
5560 NULL)) != HTTP_OK)
5561 {
5562 send_http_error(con, status, printer);
5563 return;
5564 }
5565 }
5566 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5567 {
5568 send_http_error(con, status, NULL);
5569 return;
5570 }
5571
5572 /*
5573 * Get the user that is requesting the subscription...
5574 */
5575
5576 username = get_username(con);
5577
5578 /*
5579 * Find the first subscription group attribute; return if we have
5580 * none...
5581 */
5582
5583 for (attr = con->request->attrs; attr; attr = attr->next)
5584 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
5585 break;
5586
5587 if (!attr)
5588 {
5589 send_ipp_status(con, IPP_BAD_REQUEST,
5590 _("No subscription attributes in request."));
5591 return;
5592 }
5593
5594 /*
5595 * Process the subscription attributes in the request...
5596 */
5597
5598 con->response->request.status.status_code = IPP_BAD_REQUEST;
5599
5600 while (attr)
5601 {
5602 recipient = NULL;
5603 pullmethod = NULL;
5604 user_data = NULL;
5605 interval = 0;
5606 lease = DefaultLeaseDuration;
5607 jobid = 0;
5608 mask = CUPSD_EVENT_NONE;
5609
5610 if (printer)
5611 {
5612 notify_events = ippFindAttribute(printer->attrs, "notify-events-default",
5613 IPP_TAG_KEYWORD);
5614 notify_lease = ippFindAttribute(printer->attrs,
5615 "notify-lease-duration-default",
5616 IPP_TAG_INTEGER);
5617
5618 if (notify_lease)
5619 lease = notify_lease->values[0].integer;
5620 }
5621 else
5622 {
5623 notify_events = NULL;
5624 notify_lease = NULL;
5625 }
5626
5627 while (attr && attr->group_tag != IPP_TAG_ZERO)
5628 {
5629 if (!strcmp(attr->name, "notify-recipient-uri") &&
5630 attr->value_tag == IPP_TAG_URI)
5631 {
5632 /*
5633 * Validate the recipient scheme against the ServerBin/notifier
5634 * directory...
5635 */
5636
5637 char notifier[1024]; /* Notifier filename */
5638
5639
5640 recipient = attr->values[0].string.text;
5641
5642 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
5643 scheme, sizeof(scheme), userpass, sizeof(userpass),
5644 host, sizeof(host), &port,
5645 resource, sizeof(resource)) < HTTP_URI_OK)
5646 {
5647 send_ipp_status(con, IPP_NOT_POSSIBLE,
5648 _("Bad notify-recipient-uri \"%s\"."), recipient);
5649 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5650 "notify-status-code", IPP_URI_SCHEME);
5651 return;
5652 }
5653
5654 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
5655 scheme);
5656 if (access(notifier, X_OK))
5657 {
5658 send_ipp_status(con, IPP_NOT_POSSIBLE,
5659 _("notify-recipient-uri URI \"%s\" uses unknown "
5660 "scheme."), recipient);
5661 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5662 "notify-status-code", IPP_URI_SCHEME);
5663 return;
5664 }
5665
5666 if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
5667 {
5668 send_ipp_status(con, IPP_NOT_POSSIBLE,
5669 _("notify-recipient-uri URI \"%s\" is already used."),
5670 recipient);
5671 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5672 "notify-status-code", IPP_ATTRIBUTES);
5673 return;
5674 }
5675 }
5676 else if (!strcmp(attr->name, "notify-pull-method") &&
5677 attr->value_tag == IPP_TAG_KEYWORD)
5678 {
5679 pullmethod = attr->values[0].string.text;
5680
5681 if (strcmp(pullmethod, "ippget"))
5682 {
5683 send_ipp_status(con, IPP_NOT_POSSIBLE,
5684 _("Bad notify-pull-method \"%s\"."), pullmethod);
5685 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5686 "notify-status-code", IPP_ATTRIBUTES);
5687 return;
5688 }
5689 }
5690 else if (!strcmp(attr->name, "notify-charset") &&
5691 attr->value_tag == IPP_TAG_CHARSET &&
5692 strcmp(attr->values[0].string.text, "us-ascii") &&
5693 strcmp(attr->values[0].string.text, "utf-8"))
5694 {
5695 send_ipp_status(con, IPP_CHARSET,
5696 _("Character set \"%s\" not supported."),
5697 attr->values[0].string.text);
5698 return;
5699 }
5700 else if (!strcmp(attr->name, "notify-natural-language") &&
5701 (attr->value_tag != IPP_TAG_LANGUAGE ||
5702 strcmp(attr->values[0].string.text, DefaultLanguage)))
5703 {
5704 send_ipp_status(con, IPP_CHARSET,
5705 _("Language \"%s\" not supported."),
5706 attr->values[0].string.text);
5707 return;
5708 }
5709 else if (!strcmp(attr->name, "notify-user-data") &&
5710 attr->value_tag == IPP_TAG_STRING)
5711 {
5712 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
5713 {
5714 send_ipp_status(con, IPP_REQUEST_VALUE,
5715 _("The notify-user-data value is too large "
5716 "(%d > 63 octets)."),
5717 attr->values[0].unknown.length);
5718 return;
5719 }
5720
5721 user_data = attr;
5722 }
5723 else if (!strcmp(attr->name, "notify-events") &&
5724 attr->value_tag == IPP_TAG_KEYWORD)
5725 notify_events = attr;
5726 else if (!strcmp(attr->name, "notify-lease-duration") &&
5727 attr->value_tag == IPP_TAG_INTEGER)
5728 lease = attr->values[0].integer;
5729 else if (!strcmp(attr->name, "notify-time-interval") &&
5730 attr->value_tag == IPP_TAG_INTEGER)
5731 interval = attr->values[0].integer;
5732 else if (!strcmp(attr->name, "notify-job-id") &&
5733 attr->value_tag == IPP_TAG_INTEGER)
5734 jobid = attr->values[0].integer;
5735
5736 attr = attr->next;
5737 }
5738
5739 if (notify_events)
5740 {
5741 for (i = 0; i < notify_events->num_values; i ++)
5742 mask |= cupsdEventValue(notify_events->values[i].string.text);
5743 }
5744
5745 if (recipient)
5746 cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
5747 if (pullmethod)
5748 cupsdLogMessage(CUPSD_LOG_DEBUG, "pullmethod=\"%s\"", pullmethod);
5749 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-lease-duration=%d", lease);
5750 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-time-interval=%d", interval);
5751
5752 if (!recipient && !pullmethod)
5753 break;
5754
5755 if (mask == CUPSD_EVENT_NONE)
5756 {
5757 if (jobid)
5758 mask = CUPSD_EVENT_JOB_COMPLETED;
5759 else if (printer)
5760 mask = CUPSD_EVENT_PRINTER_STATE_CHANGED;
5761 else
5762 {
5763 send_ipp_status(con, IPP_BAD_REQUEST,
5764 _("notify-events not specified."));
5765 return;
5766 }
5767 }
5768
5769 if (MaxLeaseDuration && (lease == 0 || lease > MaxLeaseDuration))
5770 {
5771 cupsdLogMessage(CUPSD_LOG_INFO,
5772 "create_subscriptions: Limiting notify-lease-duration to "
5773 "%d seconds.",
5774 MaxLeaseDuration);
5775 lease = MaxLeaseDuration;
5776 }
5777
5778 if (jobid)
5779 {
5780 if ((job = cupsdFindJob(jobid)) == NULL)
5781 {
5782 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
5783 jobid);
5784 return;
5785 }
5786 }
5787 else
5788 job = NULL;
5789
5790 if ((sub = cupsdAddSubscription(mask, printer, job, recipient, 0)) == NULL)
5791 {
5792 send_ipp_status(con, IPP_TOO_MANY_SUBSCRIPTIONS,
5793 _("There are too many subscriptions."));
5794 return;
5795 }
5796
5797 if (job)
5798 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for job %d.",
5799 sub->id, job->id);
5800 else if (printer)
5801 cupsdLogMessage(CUPSD_LOG_DEBUG,
5802 "Added subscription #%d for printer \"%s\".",
5803 sub->id, printer->name);
5804 else
5805 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for server.",
5806 sub->id);
5807
5808 sub->interval = interval;
5809 sub->lease = lease;
5810 sub->expire = lease ? time(NULL) + lease : 0;
5811
5812 cupsdSetString(&sub->owner, username);
5813
5814 if (user_data)
5815 {
5816 sub->user_data_len = user_data->values[0].unknown.length;
5817 memcpy(sub->user_data, user_data->values[0].unknown.data,
5818 (size_t)sub->user_data_len);
5819 }
5820
5821 ippAddSeparator(con->response);
5822 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5823 "notify-subscription-id", sub->id);
5824
5825 con->response->request.status.status_code = IPP_OK;
5826
5827 if (attr)
5828 attr = attr->next;
5829 }
5830
5831 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
5832 }
5833
5834
5835 /*
5836 * 'delete_printer()' - Remove a printer or class from the system.
5837 */
5838
5839 static void
5840 delete_printer(cupsd_client_t *con, /* I - Client connection */
5841 ipp_attribute_t *uri) /* I - URI of printer or class */
5842 {
5843 http_status_t status; /* Policy status */
5844 cups_ptype_t dtype; /* Destination type (printer/class) */
5845 cupsd_printer_t *printer; /* Printer/class */
5846 char filename[1024]; /* Script/PPD filename */
5847 int temporary; /* Temporary queue? */
5848
5849
5850 cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", con,
5851 con->number, uri->values[0].string.text);
5852
5853 /*
5854 * Do we have a valid URI?
5855 */
5856
5857 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
5858 {
5859 /*
5860 * Bad URI...
5861 */
5862
5863 send_ipp_status(con, IPP_NOT_FOUND,
5864 _("The printer or class does not exist."));
5865 return;
5866 }
5867
5868 /*
5869 * Check policy...
5870 */
5871
5872 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5873 {
5874 send_http_error(con, status, NULL);
5875 return;
5876 }
5877
5878 /*
5879 * Remove old jobs...
5880 */
5881
5882 cupsdCancelJobs(printer->name, NULL, 1);
5883
5884 /*
5885 * Remove old subscriptions and send a "deleted printer" event...
5886 */
5887
5888 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
5889 "%s \"%s\" deleted by \"%s\".",
5890 (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
5891 printer->name, get_username(con));
5892
5893 cupsdExpireSubscriptions(printer, NULL);
5894
5895 /*
5896 * Remove any old PPD or script files...
5897 */
5898
5899 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
5900 printer->name);
5901 unlink(filename);
5902 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd.O", ServerRoot,
5903 printer->name);
5904 unlink(filename);
5905
5906 snprintf(filename, sizeof(filename), "%s/%s.png", CacheDir, printer->name);
5907 unlink(filename);
5908
5909 snprintf(filename, sizeof(filename), "%s/%s.data", CacheDir, printer->name);
5910 unlink(filename);
5911
5912 /*
5913 * Unregister color profiles...
5914 */
5915
5916 cupsdUnregisterColor(printer);
5917
5918 temporary = printer->temporary;
5919
5920 if (dtype & CUPS_PRINTER_CLASS)
5921 {
5922 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".",
5923 printer->name, get_username(con));
5924
5925 cupsdDeletePrinter(printer, 0);
5926 if (!temporary)
5927 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
5928 }
5929 else
5930 {
5931 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".",
5932 printer->name, get_username(con));
5933
5934 if (cupsdDeletePrinter(printer, 0) && !temporary)
5935 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
5936
5937 if (!temporary)
5938 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
5939 }
5940
5941 if (!temporary)
5942 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
5943
5944 /*
5945 * Return with no errors...
5946 */
5947
5948 con->response->request.status.status_code = IPP_OK;
5949 }
5950
5951
5952 /*
5953 * 'get_default()' - Get the default destination.
5954 */
5955
5956 static void
5957 get_default(cupsd_client_t *con) /* I - Client connection */
5958 {
5959 http_status_t status; /* Policy status */
5960 cups_array_t *ra; /* Requested attributes array */
5961
5962
5963 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->number);
5964
5965 /*
5966 * Check policy...
5967 */
5968
5969 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5970 {
5971 send_http_error(con, status, NULL);
5972 return;
5973 }
5974
5975 if (DefaultPrinter)
5976 {
5977 ra = create_requested_array(con->request);
5978
5979 copy_printer_attrs(con, DefaultPrinter, ra);
5980
5981 cupsArrayDelete(ra);
5982
5983 con->response->request.status.status_code = IPP_OK;
5984 }
5985 else
5986 send_ipp_status(con, IPP_NOT_FOUND, _("No default printer."));
5987 }
5988
5989
5990 /*
5991 * 'get_devices()' - Get the list of available devices on the local system.
5992 */
5993
5994 static void
5995 get_devices(cupsd_client_t *con) /* I - Client connection */
5996 {
5997 http_status_t status; /* Policy status */
5998 ipp_attribute_t *limit, /* limit attribute */
5999 *timeout, /* timeout attribute */
6000 *requested, /* requested-attributes attribute */
6001 *exclude, /* exclude-schemes attribute */
6002 *include; /* include-schemes attribute */
6003 char command[1024], /* cups-deviced command */
6004 options[2048], /* Options to pass to command */
6005 requested_str[256],
6006 /* String for requested attributes */
6007 exclude_str[512],
6008 /* String for excluded schemes */
6009 include_str[512];
6010 /* String for included schemes */
6011
6012
6013 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->number);
6014
6015 /*
6016 * Check policy...
6017 */
6018
6019 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6020 {
6021 send_http_error(con, status, NULL);
6022 return;
6023 }
6024
6025 /*
6026 * Run cups-deviced command with the given options...
6027 */
6028
6029 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
6030 timeout = ippFindAttribute(con->request, "timeout", IPP_TAG_INTEGER);
6031 requested = ippFindAttribute(con->request, "requested-attributes",
6032 IPP_TAG_KEYWORD);
6033 exclude = ippFindAttribute(con->request, "exclude-schemes", IPP_TAG_NAME);
6034 include = ippFindAttribute(con->request, "include-schemes", IPP_TAG_NAME);
6035
6036 if (requested)
6037 url_encode_attr(requested, requested_str, sizeof(requested_str));
6038 else
6039 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
6040
6041 if (exclude)
6042 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
6043 else
6044 exclude_str[0] = '\0';
6045
6046 if (include)
6047 url_encode_attr(include, include_str, sizeof(include_str));
6048 else
6049 include_str[0] = '\0';
6050
6051 snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
6052 snprintf(options, sizeof(options),
6053 "%d+%d+%d+%d+%s%s%s%s%s",
6054 con->request->request.op.request_id,
6055 limit ? limit->values[0].integer : 0,
6056 timeout ? timeout->values[0].integer : 15,
6057 (int)User,
6058 requested_str,
6059 exclude_str[0] ? "%20" : "", exclude_str,
6060 include_str[0] ? "%20" : "", include_str);
6061
6062 if (cupsdSendCommand(con, command, options, 1))
6063 {
6064 /*
6065 * Command started successfully, don't send an IPP response here...
6066 */
6067
6068 ippDelete(con->response);
6069 con->response = NULL;
6070 }
6071 else
6072 {
6073 /*
6074 * Command failed, return "internal error" so the user knows something
6075 * went wrong...
6076 */
6077
6078 send_ipp_status(con, IPP_INTERNAL_ERROR,
6079 _("cups-deviced failed to execute."));
6080 }
6081 }
6082
6083
6084 /*
6085 * 'get_document()' - Get a copy of a job file.
6086 */
6087
6088 static void
6089 get_document(cupsd_client_t *con, /* I - Client connection */
6090 ipp_attribute_t *uri) /* I - Job URI */
6091 {
6092 http_status_t status; /* Policy status */
6093 ipp_attribute_t *attr; /* Current attribute */
6094 int jobid; /* Job ID */
6095 int docnum; /* Document number */
6096 cupsd_job_t *job; /* Current job */
6097 char scheme[HTTP_MAX_URI], /* Method portion of URI */
6098 username[HTTP_MAX_URI], /* Username portion of URI */
6099 host[HTTP_MAX_URI], /* Host portion of URI */
6100 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6101 int port; /* Port portion of URI */
6102 char filename[1024], /* Filename for document */
6103 format[1024]; /* Format for document */
6104
6105
6106 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", con,
6107 con->number, uri->values[0].string.text);
6108
6109 /*
6110 * See if we have a job URI or a printer URI...
6111 */
6112
6113 if (!strcmp(uri->name, "printer-uri"))
6114 {
6115 /*
6116 * Got a printer URI; see if we also have a job-id attribute...
6117 */
6118
6119 if ((attr = ippFindAttribute(con->request, "job-id",
6120 IPP_TAG_INTEGER)) == NULL)
6121 {
6122 send_ipp_status(con, IPP_BAD_REQUEST,
6123 _("Got a printer-uri attribute but no job-id."));
6124 return;
6125 }
6126
6127 jobid = attr->values[0].integer;
6128 }
6129 else
6130 {
6131 /*
6132 * Got a job URI; parse it to get the job ID...
6133 */
6134
6135 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6136 sizeof(scheme), username, sizeof(username), host,
6137 sizeof(host), &port, resource, sizeof(resource));
6138
6139 if (strncmp(resource, "/jobs/", 6))
6140 {
6141 /*
6142 * Not a valid URI!
6143 */
6144
6145 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
6146 uri->values[0].string.text);
6147 return;
6148 }
6149
6150 jobid = atoi(resource + 6);
6151 }
6152
6153 /*
6154 * See if the job exists...
6155 */
6156
6157 if ((job = cupsdFindJob(jobid)) == NULL)
6158 {
6159 /*
6160 * Nope - return a "not found" error...
6161 */
6162
6163 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
6164 return;
6165 }
6166
6167 /*
6168 * Check policy...
6169 */
6170
6171 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con,
6172 job->username)) != HTTP_OK)
6173 {
6174 send_http_error(con, status, NULL);
6175 return;
6176 }
6177
6178 /*
6179 * Get the document number...
6180 */
6181
6182 if ((attr = ippFindAttribute(con->request, "document-number",
6183 IPP_TAG_INTEGER)) == NULL)
6184 {
6185 send_ipp_status(con, IPP_BAD_REQUEST,
6186 _("Missing document-number attribute."));
6187 return;
6188 }
6189
6190 if ((docnum = attr->values[0].integer) < 1 || docnum > job->num_files ||
6191 attr->num_values > 1)
6192 {
6193 send_ipp_status(con, IPP_NOT_FOUND,
6194 _("Document #%d does not exist in job #%d."), docnum,
6195 jobid);
6196 return;
6197 }
6198
6199 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, jobid,
6200 docnum);
6201 if ((con->file = open(filename, O_RDONLY)) == -1)
6202 {
6203 cupsdLogMessage(CUPSD_LOG_ERROR,
6204 "Unable to open document %d in job %d - %s", docnum, jobid,
6205 strerror(errno));
6206 send_ipp_status(con, IPP_NOT_FOUND,
6207 _("Unable to open document #%d in job #%d."), docnum,
6208 jobid);
6209 return;
6210 }
6211
6212 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
6213
6214 cupsdLoadJob(job);
6215
6216 snprintf(format, sizeof(format), "%s/%s", job->filetypes[docnum - 1]->super,
6217 job->filetypes[docnum - 1]->type);
6218
6219 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
6220 NULL, format);
6221 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "document-number",
6222 docnum);
6223 if ((attr = ippFindAttribute(job->attrs, "document-name",
6224 IPP_TAG_NAME)) != NULL)
6225 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "document-name",
6226 NULL, attr->values[0].string.text);
6227 }
6228
6229
6230 /*
6231 * 'get_job_attrs()' - Get job attributes.
6232 */
6233
6234 static void
6235 get_job_attrs(cupsd_client_t *con, /* I - Client connection */
6236 ipp_attribute_t *uri) /* I - Job URI */
6237 {
6238 http_status_t status; /* Policy status */
6239 ipp_attribute_t *attr; /* Current attribute */
6240 int jobid; /* Job ID */
6241 cupsd_job_t *job; /* Current job */
6242 cupsd_printer_t *printer; /* Current printer */
6243 cupsd_policy_t *policy; /* Current security policy */
6244 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
6245 username[HTTP_MAX_URI], /* Username portion of URI */
6246 host[HTTP_MAX_URI], /* Host portion of URI */
6247 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6248 int port; /* Port portion of URI */
6249 cups_array_t *ra, /* Requested attributes array */
6250 *exclude; /* Private attributes array */
6251
6252
6253 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", con,
6254 con->number, uri->values[0].string.text);
6255
6256 /*
6257 * See if we have a job URI or a printer URI...
6258 */
6259
6260 if (!strcmp(uri->name, "printer-uri"))
6261 {
6262 /*
6263 * Got a printer URI; see if we also have a job-id attribute...
6264 */
6265
6266 if ((attr = ippFindAttribute(con->request, "job-id",
6267 IPP_TAG_INTEGER)) == NULL)
6268 {
6269 send_ipp_status(con, IPP_BAD_REQUEST,
6270 _("Got a printer-uri attribute but no job-id."));
6271 return;
6272 }
6273
6274 jobid = attr->values[0].integer;
6275 }
6276 else
6277 {
6278 /*
6279 * Got a job URI; parse it to get the job ID...
6280 */
6281
6282 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6283 sizeof(scheme), username, sizeof(username), host,
6284 sizeof(host), &port, resource, sizeof(resource));
6285
6286 if (strncmp(resource, "/jobs/", 6))
6287 {
6288 /*
6289 * Not a valid URI!
6290 */
6291
6292 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
6293 uri->values[0].string.text);
6294 return;
6295 }
6296
6297 jobid = atoi(resource + 6);
6298 }
6299
6300 /*
6301 * See if the job exists...
6302 */
6303
6304 if ((job = cupsdFindJob(jobid)) == NULL)
6305 {
6306 /*
6307 * Nope - return a "not found" error...
6308 */
6309
6310 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
6311 return;
6312 }
6313
6314 /*
6315 * Check policy...
6316 */
6317
6318 if ((printer = job->printer) == NULL)
6319 printer = cupsdFindDest(job->dest);
6320
6321 if (printer)
6322 policy = printer->op_policy_ptr;
6323 else
6324 policy = DefaultPolicyPtr;
6325
6326 if ((status = cupsdCheckPolicy(policy, con, job->username)) != HTTP_OK)
6327 {
6328 send_http_error(con, status, NULL);
6329 return;
6330 }
6331
6332 exclude = cupsdGetPrivateAttrs(policy, con, printer, job->username);
6333
6334 /*
6335 * Copy attributes...
6336 */
6337
6338 cupsdLoadJob(job);
6339
6340 ra = create_requested_array(con->request);
6341 copy_job_attrs(con, job, ra, exclude);
6342 cupsArrayDelete(ra);
6343
6344 con->response->request.status.status_code = IPP_OK;
6345 }
6346
6347
6348 /*
6349 * 'get_jobs()' - Get a list of jobs for the specified printer.
6350 */
6351
6352 static void
6353 get_jobs(cupsd_client_t *con, /* I - Client connection */
6354 ipp_attribute_t *uri) /* I - Printer URI */
6355 {
6356 http_status_t status; /* Policy status */
6357 ipp_attribute_t *attr; /* Current attribute */
6358 const char *dest; /* Destination */
6359 cups_ptype_t dtype; /* Destination type (printer/class) */
6360 cups_ptype_t dmask; /* Destination type mask */
6361 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
6362 username[HTTP_MAX_URI], /* Username portion of URI */
6363 host[HTTP_MAX_URI], /* Host portion of URI */
6364 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6365 int port; /* Port portion of URI */
6366 int job_comparison; /* Job comparison */
6367 ipp_jstate_t job_state; /* job-state value */
6368 int first_job_id = 1, /* First job ID */
6369 first_index = 1, /* First index */
6370 current_index = 0; /* Current index */
6371 int limit = 0; /* Maximum number of jobs to return */
6372 int count; /* Number of jobs that match */
6373 int need_load_job = 0; /* Do we need to load the job? */
6374 const char *job_attr; /* Job attribute requested */
6375 ipp_attribute_t *job_ids; /* job-ids attribute */
6376 cupsd_job_t *job; /* Current job pointer */
6377 cupsd_printer_t *printer; /* Printer */
6378 cups_array_t *list; /* Which job list... */
6379 int delete_list = 0; /* Delete the list afterwards? */
6380 cups_array_t *ra, /* Requested attributes array */
6381 *exclude; /* Private attributes array */
6382 cupsd_policy_t *policy; /* Current policy */
6383
6384
6385 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", con, con->number,
6386 uri->values[0].string.text);
6387
6388 /*
6389 * Is the destination valid?
6390 */
6391
6392 if (strcmp(uri->name, "printer-uri"))
6393 {
6394 send_ipp_status(con, IPP_BAD_REQUEST, _("No printer-uri in request."));
6395 return;
6396 }
6397
6398 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6399 sizeof(scheme), username, sizeof(username), host,
6400 sizeof(host), &port, resource, sizeof(resource));
6401
6402 if (!strcmp(resource, "/") || !strcmp(resource, "/jobs"))
6403 {
6404 dest = NULL;
6405 dtype = (cups_ptype_t)0;
6406 dmask = (cups_ptype_t)0;
6407 printer = NULL;
6408 }
6409 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
6410 {
6411 dest = NULL;
6412 dtype = (cups_ptype_t)0;
6413 dmask = CUPS_PRINTER_CLASS;
6414 printer = NULL;
6415 }
6416 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
6417 {
6418 dest = NULL;
6419 dtype = CUPS_PRINTER_CLASS;
6420 dmask = CUPS_PRINTER_CLASS;
6421 printer = NULL;
6422 }
6423 else if ((dest = cupsdValidateDest(uri->values[0].string.text, &dtype,
6424 &printer)) == NULL)
6425 {
6426 /*
6427 * Bad URI...
6428 */
6429
6430 send_ipp_status(con, IPP_NOT_FOUND,
6431 _("The printer or class does not exist."));
6432 return;
6433 }
6434 else
6435 {
6436 dtype &= CUPS_PRINTER_CLASS;
6437 dmask = CUPS_PRINTER_CLASS;
6438 }
6439
6440 /*
6441 * Check policy...
6442 */
6443
6444 if (printer)
6445 policy = printer->op_policy_ptr;
6446 else
6447 policy = DefaultPolicyPtr;
6448
6449 if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
6450 {
6451 send_http_error(con, status, NULL);
6452 return;
6453 }
6454
6455 job_ids = ippFindAttribute(con->request, "job-ids", IPP_TAG_INTEGER);
6456
6457 /*
6458 * See if the "which-jobs" attribute have been specified...
6459 */
6460
6461 if ((attr = ippFindAttribute(con->request, "which-jobs",
6462 IPP_TAG_KEYWORD)) != NULL && job_ids)
6463 {
6464 send_ipp_status(con, IPP_CONFLICT,
6465 _("The %s attribute cannot be provided with job-ids."),
6466 "which-jobs");
6467 return;
6468 }
6469 else if (!attr || !strcmp(attr->values[0].string.text, "not-completed"))
6470 {
6471 job_comparison = -1;
6472 job_state = IPP_JOB_STOPPED;
6473 list = ActiveJobs;
6474 }
6475 else if (!strcmp(attr->values[0].string.text, "completed"))
6476 {
6477 job_comparison = 1;
6478 job_state = IPP_JOB_CANCELED;
6479 list = cupsdGetCompletedJobs(printer);
6480 delete_list = 1;
6481 }
6482 else if (!strcmp(attr->values[0].string.text, "aborted"))
6483 {
6484 job_comparison = 0;
6485 job_state = IPP_JOB_ABORTED;
6486 list = cupsdGetCompletedJobs(printer);
6487 delete_list = 1;
6488 }
6489 else if (!strcmp(attr->values[0].string.text, "all"))
6490 {
6491 job_comparison = 1;
6492 job_state = IPP_JOB_PENDING;
6493 list = Jobs;
6494 }
6495 else if (!strcmp(attr->values[0].string.text, "canceled"))
6496 {
6497 job_comparison = 0;
6498 job_state = IPP_JOB_CANCELED;
6499 list = cupsdGetCompletedJobs(printer);
6500 delete_list = 1;
6501 }
6502 else if (!strcmp(attr->values[0].string.text, "pending"))
6503 {
6504 job_comparison = 0;
6505 job_state = IPP_JOB_PENDING;
6506 list = ActiveJobs;
6507 }
6508 else if (!strcmp(attr->values[0].string.text, "pending-held"))
6509 {
6510 job_comparison = 0;
6511 job_state = IPP_JOB_HELD;
6512 list = ActiveJobs;
6513 }
6514 else if (!strcmp(attr->values[0].string.text, "processing"))
6515 {
6516 job_comparison = 0;
6517 job_state = IPP_JOB_PROCESSING;
6518 list = PrintingJobs;
6519 }
6520 else if (!strcmp(attr->values[0].string.text, "processing-stopped"))
6521 {
6522 job_comparison = 0;
6523 job_state = IPP_JOB_STOPPED;
6524 list = ActiveJobs;
6525 }
6526 else
6527 {
6528 send_ipp_status(con, IPP_ATTRIBUTES,
6529 _("The which-jobs value \"%s\" is not supported."),
6530 attr->values[0].string.text);
6531 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
6532 "which-jobs", NULL, attr->values[0].string.text);
6533 return;
6534 }
6535
6536 /*
6537 * See if they want to limit the number of jobs reported...
6538 */
6539
6540 if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
6541 {
6542 if (job_ids)
6543 {
6544 send_ipp_status(con, IPP_CONFLICT,
6545 _("The %s attribute cannot be provided with job-ids."),
6546 "limit");
6547 return;
6548 }
6549
6550 limit = attr->values[0].integer;
6551 }
6552
6553 if ((attr = ippFindAttribute(con->request, "first-index", IPP_TAG_INTEGER)) != NULL)
6554 {
6555 if (job_ids)
6556 {
6557 send_ipp_status(con, IPP_CONFLICT,
6558 _("The %s attribute cannot be provided with job-ids."),
6559 "first-index");
6560 return;
6561 }
6562
6563 first_index = attr->values[0].integer;
6564 }
6565 else if ((attr = ippFindAttribute(con->request, "first-job-id", 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-job-id");
6572 return;
6573 }
6574
6575 first_job_id = attr->values[0].integer;
6576 }
6577
6578 /*
6579 * See if we only want to see jobs for a specific user...
6580 */
6581
6582 if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL && job_ids)
6583 {
6584 send_ipp_status(con, IPP_CONFLICT,
6585 _("The %s attribute cannot be provided with job-ids."),
6586 "my-jobs");
6587 return;
6588 }
6589 else if (attr && attr->values[0].boolean)
6590 strlcpy(username, get_username(con), sizeof(username));
6591 else
6592 username[0] = '\0';
6593
6594 ra = create_requested_array(con->request);
6595 for (job_attr = (char *)cupsArrayFirst(ra); job_attr; job_attr = (char *)cupsArrayNext(ra))
6596 if (strcmp(job_attr, "job-id") &&
6597 strcmp(job_attr, "job-k-octets") &&
6598 strcmp(job_attr, "job-media-progress") &&
6599 strcmp(job_attr, "job-more-info") &&
6600 strcmp(job_attr, "job-name") &&
6601 strcmp(job_attr, "job-originating-user-name") &&
6602 strcmp(job_attr, "job-preserved") &&
6603 strcmp(job_attr, "job-printer-up-time") &&
6604 strcmp(job_attr, "job-printer-uri") &&
6605 strcmp(job_attr, "job-state") &&
6606 strcmp(job_attr, "job-state-reasons") &&
6607 strcmp(job_attr, "job-uri") &&
6608 strcmp(job_attr, "time-at-completed") &&
6609 strcmp(job_attr, "time-at-creation") &&
6610 strcmp(job_attr, "number-of-documents"))
6611 {
6612 need_load_job = 1;
6613 break;
6614 }
6615
6616 if (need_load_job && (limit == 0 || limit > 500) && (list == Jobs || delete_list))
6617 {
6618 /*
6619 * Limit expensive Get-Jobs for job history to 500 jobs...
6620 */
6621
6622 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "limit", 500);
6623
6624 if (limit)
6625 ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER, "limit", limit);
6626
6627 limit = 500;
6628
6629 cupsdLogClient(con, CUPSD_LOG_INFO, "Limiting Get-Jobs response to %d jobs.", limit);
6630 }
6631
6632 /*
6633 * OK, build a list of jobs for this printer...
6634 */
6635
6636 if (job_ids)
6637 {
6638 int i; /* Looping var */
6639
6640 for (i = 0; i < job_ids->num_values; i ++)
6641 {
6642 if (!cupsdFindJob(job_ids->values[i].integer))
6643 break;
6644 }
6645
6646 if (i < job_ids->num_values)
6647 {
6648 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
6649 job_ids->values[i].integer);
6650 return;
6651 }
6652
6653 for (i = 0; i < job_ids->num_values; i ++)
6654 {
6655 job = cupsdFindJob(job_ids->values[i].integer);
6656
6657 if (need_load_job && !job->attrs)
6658 {
6659 cupsdLoadJob(job);
6660
6661 if (!job->attrs)
6662 {
6663 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d", job->id);
6664 continue;
6665 }
6666 }
6667
6668 if (i > 0)
6669 ippAddSeparator(con->response);
6670
6671 exclude = cupsdGetPrivateAttrs(job->printer ?
6672 job->printer->op_policy_ptr :
6673 policy, con, job->printer,
6674 job->username);
6675
6676 copy_job_attrs(con, job, ra, exclude);
6677 }
6678 }
6679 else
6680 {
6681 for (count = 0, job = (cupsd_job_t *)cupsArrayFirst(list);
6682 (limit <= 0 || count < limit) && job;
6683 job = (cupsd_job_t *)cupsArrayNext(list))
6684 {
6685 /*
6686 * Filter out jobs that don't match...
6687 */
6688
6689 cupsdLogMessage(CUPSD_LOG_DEBUG2,
6690 "get_jobs: job->id=%d, dest=\"%s\", username=\"%s\", "
6691 "state_value=%d, attrs=%p", job->id, job->dest,
6692 job->username, job->state_value, job->attrs);
6693
6694 if (!job->dest || !job->username)
6695 cupsdLoadJob(job);
6696
6697 if (!job->dest || !job->username)
6698 continue;
6699
6700 if ((dest && strcmp(job->dest, dest)) &&
6701 (!job->printer || !dest || strcmp(job->printer->name, dest)))
6702 continue;
6703 if ((job->dtype & dmask) != dtype &&
6704 (!job->printer || (job->printer->type & dmask) != dtype))
6705 continue;
6706
6707 if ((job_comparison < 0 && job->state_value > job_state) ||
6708 (job_comparison == 0 && job->state_value != job_state) ||
6709 (job_comparison > 0 && job->state_value < job_state))
6710 continue;
6711
6712 if (job->id < first_job_id)
6713 continue;
6714
6715 current_index ++;
6716 if (current_index < first_index)
6717 continue;
6718
6719 if (need_load_job && !job->attrs)
6720 {
6721 cupsdLoadJob(job);
6722
6723 if (!job->attrs)
6724 {
6725 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d", job->id);
6726 continue;
6727 }
6728 }
6729
6730 if (username[0] && _cups_strcasecmp(username, job->username))
6731 continue;
6732
6733 if (count > 0)
6734 ippAddSeparator(con->response);
6735
6736 count ++;
6737
6738 exclude = cupsdGetPrivateAttrs(job->printer ?
6739 job->printer->op_policy_ptr :
6740 policy, con, job->printer,
6741 job->username);
6742
6743 copy_job_attrs(con, job, ra, exclude);
6744 }
6745
6746 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count=%d", count);
6747 }
6748
6749 cupsArrayDelete(ra);
6750
6751 if (delete_list)
6752 cupsArrayDelete(list);
6753
6754 con->response->request.status.status_code = IPP_OK;
6755 }
6756
6757
6758 /*
6759 * 'get_notifications()' - Get events for a subscription.
6760 */
6761
6762 static void
6763 get_notifications(cupsd_client_t *con) /* I - Client connection */
6764 {
6765 int i, j; /* Looping vars */
6766 http_status_t status; /* Policy status */
6767 cupsd_subscription_t *sub; /* Subscription */
6768 ipp_attribute_t *ids, /* notify-subscription-ids */
6769 *sequences; /* notify-sequence-numbers */
6770 int min_seq; /* Minimum sequence number */
6771 int interval; /* Poll interval */
6772
6773
6774 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_notifications(con=%p[%d])",
6775 con, con->number);
6776
6777 /*
6778 * Get subscription attributes...
6779 */
6780
6781 ids = ippFindAttribute(con->request, "notify-subscription-ids",
6782 IPP_TAG_INTEGER);
6783 sequences = ippFindAttribute(con->request, "notify-sequence-numbers",
6784 IPP_TAG_INTEGER);
6785
6786 if (!ids)
6787 {
6788 send_ipp_status(con, IPP_BAD_REQUEST,
6789 _("Missing notify-subscription-ids attribute."));
6790 return;
6791 }
6792
6793 /*
6794 * Are the subscription IDs valid?
6795 */
6796
6797 for (i = 0, interval = 60; i < ids->num_values; i ++)
6798 {
6799 if ((sub = cupsdFindSubscription(ids->values[i].integer)) == NULL)
6800 {
6801 /*
6802 * Bad subscription ID...
6803 */
6804
6805 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
6806 ids->values[i].integer);
6807 return;
6808 }
6809
6810 /*
6811 * Check policy...
6812 */
6813
6814 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
6815 DefaultPolicyPtr,
6816 con, sub->owner)) != HTTP_OK)
6817 {
6818 send_http_error(con, status, sub->dest);
6819 return;
6820 }
6821
6822 /*
6823 * Check the subscription type and update the interval accordingly.
6824 */
6825
6826 if (sub->job && sub->job->state_value == IPP_JOB_PROCESSING &&
6827 interval > 10)
6828 interval = 10;
6829 else if (sub->job && sub->job->state_value >= IPP_JOB_STOPPED)
6830 interval = 0;
6831 else if (sub->dest && sub->dest->state == IPP_PRINTER_PROCESSING &&
6832 interval > 30)
6833 interval = 30;
6834 }
6835
6836 /*
6837 * Tell the client to poll again in N seconds...
6838 */
6839
6840 if (interval > 0)
6841 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
6842 "notify-get-interval", interval);
6843
6844 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
6845 "printer-up-time", time(NULL));
6846
6847 /*
6848 * Copy the subscription event attributes to the response.
6849 */
6850
6851 con->response->request.status.status_code =
6852 interval ? IPP_OK : IPP_OK_EVENTS_COMPLETE;
6853
6854 for (i = 0; i < ids->num_values; i ++)
6855 {
6856 /*
6857 * Get the subscription and sequence number...
6858 */
6859
6860 sub = cupsdFindSubscription(ids->values[i].integer);
6861
6862 if (sequences && i < sequences->num_values)
6863 min_seq = sequences->values[i].integer;
6864 else
6865 min_seq = 1;
6866
6867 /*
6868 * If we don't have any new events, nothing to do here...
6869 */
6870
6871 if (min_seq > (sub->first_event_id + cupsArrayCount(sub->events)))
6872 continue;
6873
6874 /*
6875 * Otherwise copy all of the new events...
6876 */
6877
6878 if (sub->first_event_id > min_seq)
6879 j = 0;
6880 else
6881 j = min_seq - sub->first_event_id;
6882
6883 for (; j < cupsArrayCount(sub->events); j ++)
6884 {
6885 ippAddSeparator(con->response);
6886
6887 copy_attrs(con->response,
6888 ((cupsd_event_t *)cupsArrayIndex(sub->events, j))->attrs, NULL,
6889 IPP_TAG_EVENT_NOTIFICATION, 0, NULL);
6890 }
6891 }
6892 }
6893
6894
6895 /*
6896 * 'get_ppd()' - Get a named PPD from the local system.
6897 */
6898
6899 static void
6900 get_ppd(cupsd_client_t *con, /* I - Client connection */
6901 ipp_attribute_t *uri) /* I - Printer URI or PPD name */
6902 {
6903 http_status_t status; /* Policy status */
6904 cupsd_printer_t *dest; /* Destination */
6905 cups_ptype_t dtype; /* Destination type */
6906
6907
6908 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppd(%p[%d], %p[%s=%s])", con,
6909 con->number, uri, uri->name, uri->values[0].string.text);
6910
6911 if (!strcmp(ippGetName(uri), "ppd-name"))
6912 {
6913 /*
6914 * Return a PPD file from cups-driverd...
6915 */
6916
6917 const char *ppd_name = ippGetString(uri, 0, NULL);
6918 /* ppd-name value */
6919 char command[1024], /* cups-driverd command */
6920 options[1024], /* Options to pass to command */
6921 oppd_name[1024]; /* Escaped ppd-name */
6922
6923 /*
6924 * Check policy...
6925 */
6926
6927 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6928 {
6929 send_http_error(con, status, NULL);
6930 return;
6931 }
6932
6933 /*
6934 * Check ppd-name value...
6935 */
6936
6937 if (strstr(ppd_name, "../"))
6938 {
6939 send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Invalid ppd-name value."));
6940 return;
6941 }
6942
6943 /*
6944 * Run cups-driverd command with the given options...
6945 */
6946
6947 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
6948 url_encode_string(ppd_name, oppd_name, sizeof(oppd_name));
6949 snprintf(options, sizeof(options), "get+%d+%s", ippGetRequestId(con->request), oppd_name);
6950
6951 if (cupsdSendCommand(con, command, options, 0))
6952 {
6953 /*
6954 * Command started successfully, don't send an IPP response here...
6955 */
6956
6957 ippDelete(con->response);
6958 con->response = NULL;
6959 }
6960 else
6961 {
6962 /*
6963 * Command failed, return "internal error" so the user knows something
6964 * went wrong...
6965 */
6966
6967 send_ipp_status(con, IPP_INTERNAL_ERROR, _("cups-driverd failed to execute."));
6968 }
6969 }
6970 else if (!strcmp(ippGetName(uri), "printer-uri") && cupsdValidateDest(ippGetString(uri, 0, NULL), &dtype, &dest))
6971 {
6972 int i; /* Looping var */
6973 char filename[1024]; /* PPD filename */
6974
6975 /*
6976 * Check policy...
6977 */
6978
6979 if ((status = cupsdCheckPolicy(dest->op_policy_ptr, con, NULL)) != HTTP_OK)
6980 {
6981 send_http_error(con, status, dest);
6982 return;
6983 }
6984
6985 /*
6986 * See if we need the PPD for a class or remote printer...
6987 */
6988
6989 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest->name);
6990
6991 if ((dtype & CUPS_PRINTER_REMOTE) && access(filename, 0))
6992 {
6993 send_ipp_status(con, IPP_STATUS_CUPS_SEE_OTHER, _("See remote printer."));
6994 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, dest->uri);
6995 return;
6996 }
6997 else if (dtype & CUPS_PRINTER_CLASS)
6998 {
6999 for (i = 0; i < dest->num_printers; i ++)
7000 if (!(dest->printers[i]->type & CUPS_PRINTER_CLASS))
7001 {
7002 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest->printers[i]->name);
7003
7004 if (!access(filename, 0))
7005 break;
7006 }
7007
7008 if (i < dest->num_printers)
7009 dest = dest->printers[i];
7010 else
7011 {
7012 send_ipp_status(con, IPP_STATUS_CUPS_SEE_OTHER, _("See remote printer."));
7013 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, dest->printers[0]->uri);
7014 return;
7015 }
7016 }
7017
7018 /*
7019 * Found the printer with the PPD file, now see if there is one...
7020 */
7021
7022 if ((con->file = open(filename, O_RDONLY)) < 0)
7023 {
7024 send_ipp_status(con, IPP_STATUS_ERROR_NOT_FOUND, _("The PPD file \"%s\" could not be opened: %s"), ippGetString(uri, 0, NULL), strerror(errno));
7025 return;
7026 }
7027
7028 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
7029
7030 con->pipe_pid = 0;
7031
7032 ippSetStatusCode(con->response, IPP_STATUS_OK);
7033 }
7034 else
7035 send_ipp_status(con, IPP_STATUS_ERROR_NOT_FOUND, _("The PPD file \"%s\" could not be found."), ippGetString(uri, 0, NULL));
7036 }
7037
7038
7039 /*
7040 * 'get_ppds()' - Get the list of PPD files on the local system.
7041 */
7042
7043 static void
7044 get_ppds(cupsd_client_t *con) /* I - Client connection */
7045 {
7046 http_status_t status; /* Policy status */
7047 ipp_attribute_t *limit, /* Limit attribute */
7048 *device, /* ppd-device-id attribute */
7049 *language, /* ppd-natural-language attribute */
7050 *make, /* ppd-make attribute */
7051 *model, /* ppd-make-and-model attribute */
7052 *model_number, /* ppd-model-number attribute */
7053 *product, /* ppd-product attribute */
7054 *psversion, /* ppd-psverion attribute */
7055 *type, /* ppd-type attribute */
7056 *requested, /* requested-attributes attribute */
7057 *exclude, /* exclude-schemes attribute */
7058 *include; /* include-schemes attribute */
7059 char command[1024], /* cups-driverd command */
7060 options[4096], /* Options to pass to command */
7061 device_str[256],/* Escaped ppd-device-id string */
7062 language_str[256],
7063 /* Escaped ppd-natural-language */
7064 make_str[256], /* Escaped ppd-make string */
7065 model_str[256], /* Escaped ppd-make-and-model string */
7066 model_number_str[256],
7067 /* ppd-model-number string */
7068 product_str[256],
7069 /* Escaped ppd-product string */
7070 psversion_str[256],
7071 /* Escaped ppd-psversion string */
7072 type_str[256], /* Escaped ppd-type string */
7073 requested_str[256],
7074 /* String for requested attributes */
7075 exclude_str[512],
7076 /* String for excluded schemes */
7077 include_str[512];
7078 /* String for included schemes */
7079
7080
7081 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->number);
7082
7083 /*
7084 * Check policy...
7085 */
7086
7087 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7088 {
7089 send_http_error(con, status, NULL);
7090 return;
7091 }
7092
7093 /*
7094 * Run cups-driverd command with the given options...
7095 */
7096
7097 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
7098 device = ippFindAttribute(con->request, "ppd-device-id", IPP_TAG_TEXT);
7099 language = ippFindAttribute(con->request, "ppd-natural-language",
7100 IPP_TAG_LANGUAGE);
7101 make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
7102 model = ippFindAttribute(con->request, "ppd-make-and-model",
7103 IPP_TAG_TEXT);
7104 model_number = ippFindAttribute(con->request, "ppd-model-number",
7105 IPP_TAG_INTEGER);
7106 product = ippFindAttribute(con->request, "ppd-product", IPP_TAG_TEXT);
7107 psversion = ippFindAttribute(con->request, "ppd-psversion", IPP_TAG_TEXT);
7108 type = ippFindAttribute(con->request, "ppd-type", IPP_TAG_KEYWORD);
7109 requested = ippFindAttribute(con->request, "requested-attributes",
7110 IPP_TAG_KEYWORD);
7111 exclude = ippFindAttribute(con->request, "exclude-schemes",
7112 IPP_TAG_NAME);
7113 include = ippFindAttribute(con->request, "include-schemes",
7114 IPP_TAG_NAME);
7115
7116 if (requested)
7117 url_encode_attr(requested, requested_str, sizeof(requested_str));
7118 else
7119 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
7120
7121 if (device)
7122 url_encode_attr(device, device_str, sizeof(device_str));
7123 else
7124 device_str[0] = '\0';
7125
7126 if (language)
7127 url_encode_attr(language, language_str, sizeof(language_str));
7128 else
7129 language_str[0] = '\0';
7130
7131 if (make)
7132 url_encode_attr(make, make_str, sizeof(make_str));
7133 else
7134 make_str[0] = '\0';
7135
7136 if (model)
7137 url_encode_attr(model, model_str, sizeof(model_str));
7138 else
7139 model_str[0] = '\0';
7140
7141 if (model_number)
7142 snprintf(model_number_str, sizeof(model_number_str), "ppd-model-number=%d",
7143 model_number->values[0].integer);
7144 else
7145 model_number_str[0] = '\0';
7146
7147 if (product)
7148 url_encode_attr(product, product_str, sizeof(product_str));
7149 else
7150 product_str[0] = '\0';
7151
7152 if (psversion)
7153 url_encode_attr(psversion, psversion_str, sizeof(psversion_str));
7154 else
7155 psversion_str[0] = '\0';
7156
7157 if (type)
7158 url_encode_attr(type, type_str, sizeof(type_str));
7159 else
7160 type_str[0] = '\0';
7161
7162 if (exclude)
7163 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
7164 else
7165 exclude_str[0] = '\0';
7166
7167 if (include)
7168 url_encode_attr(include, include_str, sizeof(include_str));
7169 else
7170 include_str[0] = '\0';
7171
7172 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
7173 snprintf(options, sizeof(options),
7174 "list+%d+%d+%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
7175 con->request->request.op.request_id,
7176 limit ? limit->values[0].integer : 0,
7177 requested_str,
7178 device ? "%20" : "", device_str,
7179 language ? "%20" : "", language_str,
7180 make ? "%20" : "", make_str,
7181 model ? "%20" : "", model_str,
7182 model_number ? "%20" : "", model_number_str,
7183 product ? "%20" : "", product_str,
7184 psversion ? "%20" : "", psversion_str,
7185 type ? "%20" : "", type_str,
7186 exclude_str[0] ? "%20" : "", exclude_str,
7187 include_str[0] ? "%20" : "", include_str);
7188
7189 if (cupsdSendCommand(con, command, options, 0))
7190 {
7191 /*
7192 * Command started successfully, don't send an IPP response here...
7193 */
7194
7195 ippDelete(con->response);
7196 con->response = NULL;
7197 }
7198 else
7199 {
7200 /*
7201 * Command failed, return "internal error" so the user knows something
7202 * went wrong...
7203 */
7204
7205 send_ipp_status(con, IPP_INTERNAL_ERROR,
7206 _("cups-driverd failed to execute."));
7207 }
7208 }
7209
7210
7211 /*
7212 * 'get_printer_attrs()' - Get printer attributes.
7213 */
7214
7215 static void
7216 get_printer_attrs(cupsd_client_t *con, /* I - Client connection */
7217 ipp_attribute_t *uri) /* I - Printer URI */
7218 {
7219 http_status_t status; /* Policy status */
7220 cups_ptype_t dtype; /* Destination type (printer/class) */
7221 cupsd_printer_t *printer; /* Printer/class */
7222 cups_array_t *ra; /* Requested attributes array */
7223
7224
7225 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", con,
7226 con->number, uri->values[0].string.text);
7227
7228 /*
7229 * Is the destination valid?
7230 */
7231
7232 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7233 {
7234 /*
7235 * Bad URI...
7236 */
7237
7238 send_ipp_status(con, IPP_NOT_FOUND,
7239 _("The printer or class does not exist."));
7240 return;
7241 }
7242
7243 /*
7244 * Check policy...
7245 */
7246
7247 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7248 {
7249 send_http_error(con, status, printer);
7250 return;
7251 }
7252
7253 /*
7254 * Send the attributes...
7255 */
7256
7257 ra = create_requested_array(con->request);
7258
7259 copy_printer_attrs(con, printer, ra);
7260
7261 cupsArrayDelete(ra);
7262
7263 con->response->request.status.status_code = IPP_OK;
7264 }
7265
7266
7267 /*
7268 * 'get_printer_supported()' - Get printer supported values.
7269 */
7270
7271 static void
7272 get_printer_supported(
7273 cupsd_client_t *con, /* I - Client connection */
7274 ipp_attribute_t *uri) /* I - Printer URI */
7275 {
7276 http_status_t status; /* Policy status */
7277 cups_ptype_t dtype; /* Destination type (printer/class) */
7278 cupsd_printer_t *printer; /* Printer/class */
7279
7280
7281 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_supported(%p[%d], %s)", con,
7282 con->number, uri->values[0].string.text);
7283
7284 /*
7285 * Is the destination valid?
7286 */
7287
7288 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7289 {
7290 /*
7291 * Bad URI...
7292 */
7293
7294 send_ipp_status(con, IPP_NOT_FOUND,
7295 _("The printer or class does not exist."));
7296 return;
7297 }
7298
7299 /*
7300 * Check policy...
7301 */
7302
7303 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7304 {
7305 send_http_error(con, status, printer);
7306 return;
7307 }
7308
7309 /*
7310 * Return a list of attributes that can be set via Set-Printer-Attributes.
7311 */
7312
7313 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7314 "printer-geo-location", 0);
7315 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7316 "printer-info", 0);
7317 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7318 "printer-location", 0);
7319 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7320 "printer-organization", 0);
7321 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7322 "printer-organizational-unit", 0);
7323
7324 con->response->request.status.status_code = IPP_OK;
7325 }
7326
7327
7328 /*
7329 * 'get_printers()' - Get a list of printers or classes.
7330 */
7331
7332 static void
7333 get_printers(cupsd_client_t *con, /* I - Client connection */
7334 int type) /* I - 0 or CUPS_PRINTER_CLASS */
7335 {
7336 http_status_t status; /* Policy status */
7337 ipp_attribute_t *attr; /* Current attribute */
7338 int limit; /* Max number of printers to return */
7339 int count; /* Number of printers that match */
7340 cupsd_printer_t *printer; /* Current printer pointer */
7341 cups_ptype_t printer_type, /* printer-type attribute */
7342 printer_mask; /* printer-type-mask attribute */
7343 char *location; /* Location string */
7344 const char *username; /* Current user */
7345 char *first_printer_name; /* first-printer-name attribute */
7346 cups_array_t *ra; /* Requested attributes array */
7347 int local; /* Local connection? */
7348
7349
7350 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con,
7351 con->number, type);
7352
7353 /*
7354 * Check policy...
7355 */
7356
7357 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7358 {
7359 send_http_error(con, status, NULL);
7360 return;
7361 }
7362
7363 /*
7364 * Check for printers...
7365 */
7366
7367 if (!Printers || !cupsArrayCount(Printers))
7368 {
7369 send_ipp_status(con, IPP_NOT_FOUND, _("No destinations added."));
7370 return;
7371 }
7372
7373 /*
7374 * See if they want to limit the number of printers reported...
7375 */
7376
7377 if ((attr = ippFindAttribute(con->request, "limit",
7378 IPP_TAG_INTEGER)) != NULL)
7379 limit = attr->values[0].integer;
7380 else
7381 limit = 10000000;
7382
7383 if ((attr = ippFindAttribute(con->request, "first-printer-name",
7384 IPP_TAG_NAME)) != NULL)
7385 first_printer_name = attr->values[0].string.text;
7386 else
7387 first_printer_name = NULL;
7388
7389 /*
7390 * Support filtering...
7391 */
7392
7393 if ((attr = ippFindAttribute(con->request, "printer-type",
7394 IPP_TAG_ENUM)) != NULL)
7395 printer_type = (cups_ptype_t)attr->values[0].integer;
7396 else
7397 printer_type = (cups_ptype_t)0;
7398
7399 if ((attr = ippFindAttribute(con->request, "printer-type-mask",
7400 IPP_TAG_ENUM)) != NULL)
7401 printer_mask = (cups_ptype_t)attr->values[0].integer;
7402 else
7403 printer_mask = (cups_ptype_t)0;
7404
7405 local = httpAddrLocalhost(&(con->clientaddr));
7406
7407 if ((attr = ippFindAttribute(con->request, "printer-location",
7408 IPP_TAG_TEXT)) != NULL)
7409 location = attr->values[0].string.text;
7410 else
7411 location = NULL;
7412
7413 if (con->username[0])
7414 username = con->username;
7415 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7416 IPP_TAG_NAME)) != NULL)
7417 username = attr->values[0].string.text;
7418 else
7419 username = NULL;
7420
7421 ra = create_requested_array(con->request);
7422
7423 /*
7424 * OK, build a list of printers for this printer...
7425 */
7426
7427 if (first_printer_name)
7428 {
7429 if ((printer = cupsdFindDest(first_printer_name)) == NULL)
7430 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
7431 }
7432 else
7433 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
7434
7435 for (count = 0;
7436 count < limit && printer;
7437 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
7438 {
7439 if (!local && !printer->shared)
7440 continue;
7441
7442 if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
7443 (printer->type & printer_mask) == printer_type &&
7444 (!location ||
7445 (printer->location && !_cups_strcasecmp(printer->location, location))))
7446 {
7447 /*
7448 * If a username is specified, see if it is allowed or denied
7449 * access...
7450 */
7451
7452 if (cupsArrayCount(printer->users) && username &&
7453 !user_allowed(printer, username))
7454 continue;
7455
7456 /*
7457 * Add the group separator as needed...
7458 */
7459
7460 if (count > 0)
7461 ippAddSeparator(con->response);
7462
7463 count ++;
7464
7465 /*
7466 * Send the attributes...
7467 */
7468
7469 copy_printer_attrs(con, printer, ra);
7470 }
7471 }
7472
7473 cupsArrayDelete(ra);
7474
7475 con->response->request.status.status_code = IPP_OK;
7476 }
7477
7478
7479 /*
7480 * 'get_subscription_attrs()' - Get subscription attributes.
7481 */
7482
7483 static void
7484 get_subscription_attrs(
7485 cupsd_client_t *con, /* I - Client connection */
7486 int sub_id) /* I - Subscription ID */
7487 {
7488 http_status_t status; /* Policy status */
7489 cupsd_subscription_t *sub; /* Subscription */
7490 cupsd_policy_t *policy; /* Current security policy */
7491 cups_array_t *ra, /* Requested attributes array */
7492 *exclude; /* Private attributes array */
7493
7494
7495 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7496 "get_subscription_attrs(con=%p[%d], sub_id=%d)",
7497 con, con->number, sub_id);
7498
7499 /*
7500 * Expire subscriptions as needed...
7501 */
7502
7503 cupsdExpireSubscriptions(NULL, NULL);
7504
7505 /*
7506 * Is the subscription ID valid?
7507 */
7508
7509 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
7510 {
7511 /*
7512 * Bad subscription ID...
7513 */
7514
7515 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
7516 sub_id);
7517 return;
7518 }
7519
7520 /*
7521 * Check policy...
7522 */
7523
7524 if (sub->dest)
7525 policy = sub->dest->op_policy_ptr;
7526 else
7527 policy = DefaultPolicyPtr;
7528
7529 if ((status = cupsdCheckPolicy(policy, con, sub->owner)) != HTTP_OK)
7530 {
7531 send_http_error(con, status, sub->dest);
7532 return;
7533 }
7534
7535 exclude = cupsdGetPrivateAttrs(policy, con, sub->dest, sub->owner);
7536
7537 /*
7538 * Copy the subscription attributes to the response using the
7539 * requested-attributes attribute that may be provided by the client.
7540 */
7541
7542 ra = create_requested_array(con->request);
7543
7544 copy_subscription_attrs(con, sub, ra, exclude);
7545
7546 cupsArrayDelete(ra);
7547
7548 con->response->request.status.status_code = IPP_OK;
7549 }
7550
7551
7552 /*
7553 * 'get_subscriptions()' - Get subscriptions.
7554 */
7555
7556 static void
7557 get_subscriptions(cupsd_client_t *con, /* I - Client connection */
7558 ipp_attribute_t *uri) /* I - Printer/job URI */
7559 {
7560 http_status_t status; /* Policy status */
7561 int count; /* Number of subscriptions */
7562 int limit; /* Limit */
7563 cupsd_subscription_t *sub; /* Subscription */
7564 cups_array_t *ra; /* Requested attributes array */
7565 ipp_attribute_t *attr; /* Attribute */
7566 cups_ptype_t dtype; /* Destination type (printer/class) */
7567 char scheme[HTTP_MAX_URI],
7568 /* Scheme portion of URI */
7569 username[HTTP_MAX_URI],
7570 /* Username portion of URI */
7571 host[HTTP_MAX_URI],
7572 /* Host portion of URI */
7573 resource[HTTP_MAX_URI];
7574 /* Resource portion of URI */
7575 int port; /* Port portion of URI */
7576 cupsd_job_t *job; /* Job pointer */
7577 cupsd_printer_t *printer; /* Printer */
7578 cupsd_policy_t *policy; /* Policy */
7579 cups_array_t *exclude; /* Private attributes array */
7580
7581
7582 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7583 "get_subscriptions(con=%p[%d], uri=%s)",
7584 con, con->number, uri->values[0].string.text);
7585
7586 /*
7587 * Is the destination valid?
7588 */
7589
7590 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7591 sizeof(scheme), username, sizeof(username), host,
7592 sizeof(host), &port, resource, sizeof(resource));
7593
7594 if (!strcmp(resource, "/") ||
7595 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6) ||
7596 (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) ||
7597 (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9))
7598 {
7599 printer = NULL;
7600 job = NULL;
7601 }
7602 else if (!strncmp(resource, "/jobs/", 6) && resource[6])
7603 {
7604 printer = NULL;
7605 job = cupsdFindJob(atoi(resource + 6));
7606
7607 if (!job)
7608 {
7609 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
7610 atoi(resource + 6));
7611 return;
7612 }
7613 }
7614 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7615 {
7616 /*
7617 * Bad URI...
7618 */
7619
7620 send_ipp_status(con, IPP_NOT_FOUND,
7621 _("The printer or class does not exist."));
7622 return;
7623 }
7624 else if ((attr = ippFindAttribute(con->request, "notify-job-id",
7625 IPP_TAG_INTEGER)) != NULL)
7626 {
7627 job = cupsdFindJob(attr->values[0].integer);
7628
7629 if (!job)
7630 {
7631 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
7632 attr->values[0].integer);
7633 return;
7634 }
7635 }
7636 else
7637 job = NULL;
7638
7639 /*
7640 * Check policy...
7641 */
7642
7643 if (printer)
7644 policy = printer->op_policy_ptr;
7645 else
7646 policy = DefaultPolicyPtr;
7647
7648 if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
7649 {
7650 send_http_error(con, status, printer);
7651 return;
7652 }
7653
7654 /*
7655 * Expire subscriptions as needed...
7656 */
7657
7658 cupsdExpireSubscriptions(NULL, NULL);
7659
7660 /*
7661 * Copy the subscription attributes to the response using the
7662 * requested-attributes attribute that may be provided by the client.
7663 */
7664
7665 ra = create_requested_array(con->request);
7666
7667 if ((attr = ippFindAttribute(con->request, "limit",
7668 IPP_TAG_INTEGER)) != NULL)
7669 limit = attr->values[0].integer;
7670 else
7671 limit = 0;
7672
7673 /*
7674 * See if we only want to see subscriptions for a specific user...
7675 */
7676
7677 if ((attr = ippFindAttribute(con->request, "my-subscriptions",
7678 IPP_TAG_BOOLEAN)) != NULL &&
7679 attr->values[0].boolean)
7680 strlcpy(username, get_username(con), sizeof(username));
7681 else
7682 username[0] = '\0';
7683
7684 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0;
7685 sub;
7686 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
7687 if ((!printer || sub->dest == printer) && (!job || sub->job == job) &&
7688 (!username[0] || !_cups_strcasecmp(username, sub->owner)))
7689 {
7690 ippAddSeparator(con->response);
7691
7692 exclude = cupsdGetPrivateAttrs(sub->dest ? sub->dest->op_policy_ptr :
7693 policy, con, sub->dest,
7694 sub->owner);
7695
7696 copy_subscription_attrs(con, sub, ra, exclude);
7697
7698 count ++;
7699 if (limit && count >= limit)
7700 break;
7701 }
7702
7703 cupsArrayDelete(ra);
7704
7705 if (count)
7706 con->response->request.status.status_code = IPP_OK;
7707 else
7708 send_ipp_status(con, IPP_NOT_FOUND, _("No subscriptions found."));
7709 }
7710
7711
7712 /*
7713 * 'get_username()' - Get the username associated with a request.
7714 */
7715
7716 static const char * /* O - Username */
7717 get_username(cupsd_client_t *con) /* I - Connection */
7718 {
7719 ipp_attribute_t *attr; /* Attribute */
7720
7721
7722 if (con->username[0])
7723 return (con->username);
7724 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7725 IPP_TAG_NAME)) != NULL)
7726 return (attr->values[0].string.text);
7727 else
7728 return ("anonymous");
7729 }
7730
7731
7732 /*
7733 * 'hold_job()' - Hold a print job.
7734 */
7735
7736 static void
7737 hold_job(cupsd_client_t *con, /* I - Client connection */
7738 ipp_attribute_t *uri) /* I - Job or Printer URI */
7739 {
7740 ipp_attribute_t *attr; /* Current job-hold-until */
7741 const char *when; /* New value */
7742 int jobid; /* Job ID */
7743 char scheme[HTTP_MAX_URI], /* Method portion of URI */
7744 username[HTTP_MAX_URI], /* Username portion of URI */
7745 host[HTTP_MAX_URI], /* Host portion of URI */
7746 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7747 int port; /* Port portion of URI */
7748 cupsd_job_t *job; /* Job information */
7749
7750
7751 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", con, con->number,
7752 uri->values[0].string.text);
7753
7754 /*
7755 * See if we have a job URI or a printer URI...
7756 */
7757
7758 if (!strcmp(uri->name, "printer-uri"))
7759 {
7760 /*
7761 * Got a printer URI; see if we also have a job-id attribute...
7762 */
7763
7764 if ((attr = ippFindAttribute(con->request, "job-id",
7765 IPP_TAG_INTEGER)) == NULL)
7766 {
7767 send_ipp_status(con, IPP_BAD_REQUEST,
7768 _("Got a printer-uri attribute but no job-id."));
7769 return;
7770 }
7771
7772 jobid = attr->values[0].integer;
7773 }
7774 else
7775 {
7776 /*
7777 * Got a job URI; parse it to get the job ID...
7778 */
7779
7780 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7781 sizeof(scheme), username, sizeof(username), host,
7782 sizeof(host), &port, resource, sizeof(resource));
7783
7784 if (strncmp(resource, "/jobs/", 6))
7785 {
7786 /*
7787 * Not a valid URI!
7788 */
7789
7790 send_ipp_status(con, IPP_BAD_REQUEST,
7791 _("Bad job-uri \"%s\"."),
7792 uri->values[0].string.text);
7793 return;
7794 }
7795
7796 jobid = atoi(resource + 6);
7797 }
7798
7799 /*
7800 * See if the job exists...
7801 */
7802
7803 if ((job = cupsdFindJob(jobid)) == NULL)
7804 {
7805 /*
7806 * Nope - return a "not found" error...
7807 */
7808
7809 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
7810 return;
7811 }
7812
7813 /*
7814 * See if the job is owned by the requesting user...
7815 */
7816
7817 if (!validate_user(job, con, job->username, username, sizeof(username)))
7818 {
7819 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
7820 cupsdFindDest(job->dest));
7821 return;
7822 }
7823
7824 /*
7825 * See if the job is in a state that allows holding...
7826 */
7827
7828 if (job->state_value > IPP_JOB_STOPPED)
7829 {
7830 /*
7831 * Return a "not-possible" error...
7832 */
7833
7834 send_ipp_status(con, IPP_NOT_POSSIBLE,
7835 _("Job #%d is finished and cannot be altered."),
7836 job->id);
7837 return;
7838 }
7839
7840 /*
7841 * Hold the job and return...
7842 */
7843
7844 if ((attr = ippFindAttribute(con->request, "job-hold-until",
7845 IPP_TAG_KEYWORD)) == NULL)
7846 attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
7847
7848 if (attr)
7849 {
7850 when = attr->values[0].string.text;
7851
7852 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
7853 "Job job-hold-until value changed by user.");
7854 }
7855 else
7856 when = "indefinite";
7857
7858 cupsdSetJobHoldUntil(job, when, 1);
7859 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT, "Job held by \"%s\".",
7860 username);
7861
7862 con->response->request.status.status_code = IPP_OK;
7863 }
7864
7865
7866 /*
7867 * 'hold_new_jobs()' - Hold pending/new jobs on a printer or class.
7868 */
7869
7870 static void
7871 hold_new_jobs(cupsd_client_t *con, /* I - Connection */
7872 ipp_attribute_t *uri) /* I - Printer URI */
7873 {
7874 http_status_t status; /* Policy status */
7875 cups_ptype_t dtype; /* Destination type (printer/class) */
7876 cupsd_printer_t *printer; /* Printer data */
7877
7878
7879 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_new_jobs(%p[%d], %s)", con,
7880 con->number, uri->values[0].string.text);
7881
7882 /*
7883 * Is the destination valid?
7884 */
7885
7886 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7887 {
7888 /*
7889 * Bad URI...
7890 */
7891
7892 send_ipp_status(con, IPP_NOT_FOUND,
7893 _("The printer or class does not exist."));
7894 return;
7895 }
7896
7897 /*
7898 * Check policy...
7899 */
7900
7901 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7902 {
7903 send_http_error(con, status, printer);
7904 return;
7905 }
7906
7907 /*
7908 * Hold pending/new jobs sent to the printer...
7909 */
7910
7911 printer->holding_new_jobs = 1;
7912
7913 cupsdSetPrinterReasons(printer, "+hold-new-jobs");
7914
7915 if (dtype & CUPS_PRINTER_CLASS)
7916 cupsdLogMessage(CUPSD_LOG_INFO,
7917 "Class \"%s\" now holding pending/new jobs (\"%s\").",
7918 printer->name, get_username(con));
7919 else
7920 cupsdLogMessage(CUPSD_LOG_INFO,
7921 "Printer \"%s\" now holding pending/new jobs (\"%s\").",
7922 printer->name, get_username(con));
7923
7924 /*
7925 * Everything was ok, so return OK status...
7926 */
7927
7928 con->response->request.status.status_code = IPP_OK;
7929 }
7930
7931
7932 /*
7933 * 'move_job()' - Move a job to a new destination.
7934 */
7935
7936 static void
7937 move_job(cupsd_client_t *con, /* I - Client connection */
7938 ipp_attribute_t *uri) /* I - Job URI */
7939 {
7940 http_status_t status; /* Policy status */
7941 ipp_attribute_t *attr; /* Current attribute */
7942 int jobid; /* Job ID */
7943 cupsd_job_t *job; /* Current job */
7944 const char *src; /* Source printer/class */
7945 cups_ptype_t stype, /* Source type (printer or class) */
7946 dtype; /* Destination type (printer/class) */
7947 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
7948 username[HTTP_MAX_URI], /* Username portion of URI */
7949 host[HTTP_MAX_URI], /* Host portion of URI */
7950 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7951 int port; /* Port portion of URI */
7952 cupsd_printer_t *sprinter, /* Source printer */
7953 *dprinter; /* Destination printer */
7954
7955
7956 cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", con, con->number,
7957 uri->values[0].string.text);
7958
7959 /*
7960 * Get the new printer or class...
7961 */
7962
7963 if ((attr = ippFindAttribute(con->request, "job-printer-uri",
7964 IPP_TAG_URI)) == NULL)
7965 {
7966 /*
7967 * Need job-printer-uri...
7968 */
7969
7970 send_ipp_status(con, IPP_BAD_REQUEST,
7971 _("job-printer-uri attribute missing."));
7972 return;
7973 }
7974
7975 if (!cupsdValidateDest(attr->values[0].string.text, &dtype, &dprinter))
7976 {
7977 /*
7978 * Bad URI...
7979 */
7980
7981 send_ipp_status(con, IPP_NOT_FOUND,
7982 _("The printer or class does not exist."));
7983 return;
7984 }
7985
7986 /*
7987 * See if we have a job URI or a printer URI...
7988 */
7989
7990 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7991 sizeof(scheme), username, sizeof(username), host,
7992 sizeof(host), &port, resource, sizeof(resource));
7993
7994 if (!strcmp(uri->name, "printer-uri"))
7995 {
7996 /*
7997 * Got a printer URI; see if we also have a job-id attribute...
7998 */
7999
8000 if ((attr = ippFindAttribute(con->request, "job-id",
8001 IPP_TAG_INTEGER)) == NULL)
8002 {
8003 /*
8004 * Move all jobs...
8005 */
8006
8007 if ((src = cupsdValidateDest(uri->values[0].string.text, &stype,
8008 &sprinter)) == NULL)
8009 {
8010 /*
8011 * Bad URI...
8012 */
8013
8014 send_ipp_status(con, IPP_NOT_FOUND,
8015 _("The printer or class does not exist."));
8016 return;
8017 }
8018
8019 job = NULL;
8020 }
8021 else
8022 {
8023 /*
8024 * Otherwise, just move a single job...
8025 */
8026
8027 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
8028 {
8029 /*
8030 * Nope - return a "not found" error...
8031 */
8032
8033 send_ipp_status(con, IPP_NOT_FOUND,
8034 _("Job #%d does not exist."), attr->values[0].integer);
8035 return;
8036 }
8037 else
8038 {
8039 /*
8040 * Job found, initialize source pointers...
8041 */
8042
8043 src = NULL;
8044 sprinter = NULL;
8045 }
8046 }
8047 }
8048 else
8049 {
8050 /*
8051 * Got a job URI; parse it to get the job ID...
8052 */
8053
8054 if (strncmp(resource, "/jobs/", 6))
8055 {
8056 /*
8057 * Not a valid URI!
8058 */
8059
8060 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
8061 uri->values[0].string.text);
8062 return;
8063 }
8064
8065 /*
8066 * See if the job exists...
8067 */
8068
8069 jobid = atoi(resource + 6);
8070
8071 if ((job = cupsdFindJob(jobid)) == NULL)
8072 {
8073 /*
8074 * Nope - return a "not found" error...
8075 */
8076
8077 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
8078 return;
8079 }
8080 else
8081 {
8082 /*
8083 * Job found, initialize source pointers...
8084 */
8085
8086 src = NULL;
8087 sprinter = NULL;
8088 }
8089 }
8090
8091 /*
8092 * Check the policy of the destination printer...
8093 */
8094
8095 if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con,
8096 job ? job->username : NULL)) != HTTP_OK)
8097 {
8098 send_http_error(con, status, dprinter);
8099 return;
8100 }
8101
8102 /*
8103 * Now move the job or jobs...
8104 */
8105
8106 if (job)
8107 {
8108 /*
8109 * See if the job has been completed...
8110 */
8111
8112 if (job->state_value > IPP_JOB_STOPPED)
8113 {
8114 /*
8115 * Return a "not-possible" error...
8116 */
8117
8118 send_ipp_status(con, IPP_NOT_POSSIBLE,
8119 _("Job #%d is finished and cannot be altered."),
8120 job->id);
8121 return;
8122 }
8123
8124 /*
8125 * See if the job is owned by the requesting user...
8126 */
8127
8128 if (!validate_user(job, con, job->username, username, sizeof(username)))
8129 {
8130 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
8131 cupsdFindDest(job->dest));
8132 return;
8133 }
8134
8135 /*
8136 * Move the job to a different printer or class...
8137 */
8138
8139 cupsdMoveJob(job, dprinter);
8140 }
8141 else
8142 {
8143 /*
8144 * Got the source printer, now look through the jobs...
8145 */
8146
8147 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
8148 job;
8149 job = (cupsd_job_t *)cupsArrayNext(Jobs))
8150 {
8151 /*
8152 * See if the job is pointing at the source printer or has not been
8153 * completed...
8154 */
8155
8156 if (_cups_strcasecmp(job->dest, src) ||
8157 job->state_value > IPP_JOB_STOPPED)
8158 continue;
8159
8160 /*
8161 * See if the job can be moved by the requesting user...
8162 */
8163
8164 if (!validate_user(job, con, job->username, username, sizeof(username)))
8165 continue;
8166
8167 /*
8168 * Move the job to a different printer or class...
8169 */
8170
8171 cupsdMoveJob(job, dprinter);
8172 }
8173 }
8174
8175 /*
8176 * Start jobs if possible...
8177 */
8178
8179 cupsdCheckJobs();
8180
8181 /*
8182 * Return with "everything is OK" status...
8183 */
8184
8185 con->response->request.status.status_code = IPP_OK;
8186 }
8187
8188
8189 /*
8190 * 'ppd_parse_line()' - Parse a PPD default line.
8191 */
8192
8193 static int /* O - 0 on success, -1 on failure */
8194 ppd_parse_line(const char *line, /* I - Line */
8195 char *option, /* O - Option name */
8196 int olen, /* I - Size of option name */
8197 char *choice, /* O - Choice name */
8198 int clen) /* I - Size of choice name */
8199 {
8200 /*
8201 * Verify this is a default option line...
8202 */
8203
8204 if (strncmp(line, "*Default", 8))
8205 return (-1);
8206
8207 /*
8208 * Read the option name...
8209 */
8210
8211 for (line += 8, olen --;
8212 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
8213 line ++)
8214 if (olen > 0)
8215 {
8216 *option++ = *line;
8217 olen --;
8218 }
8219
8220 *option = '\0';
8221
8222 /*
8223 * Skip everything else up to the colon (:)...
8224 */
8225
8226 while (*line && *line != ':')
8227 line ++;
8228
8229 if (!*line)
8230 return (-1);
8231
8232 line ++;
8233
8234 /*
8235 * Now grab the option choice, skipping leading whitespace...
8236 */
8237
8238 while (isspace(*line & 255))
8239 line ++;
8240
8241 for (clen --;
8242 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
8243 line ++)
8244 if (clen > 0)
8245 {
8246 *choice++ = *line;
8247 clen --;
8248 }
8249
8250 *choice = '\0';
8251
8252 /*
8253 * Return with no errors...
8254 */
8255
8256 return (0);
8257 }
8258
8259
8260 /*
8261 * 'print_job()' - Print a file to a printer or class.
8262 */
8263
8264 static void
8265 print_job(cupsd_client_t *con, /* I - Client connection */
8266 ipp_attribute_t *uri) /* I - Printer URI */
8267 {
8268 ipp_attribute_t *attr; /* Current attribute */
8269 ipp_attribute_t *doc_name; /* document-name attribute */
8270 ipp_attribute_t *format; /* Document-format attribute */
8271 const char *default_format; /* document-format-default value */
8272 cupsd_job_t *job; /* New job */
8273 char filename[1024]; /* Job filename */
8274 mime_type_t *filetype; /* Type of file */
8275 char super[MIME_MAX_SUPER], /* Supertype of file */
8276 type[MIME_MAX_TYPE], /* Subtype of file */
8277 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
8278 /* Textual name of mime type */
8279 cupsd_printer_t *printer; /* Printer data */
8280 struct stat fileinfo; /* File information */
8281 int kbytes; /* Size of file */
8282 int compression; /* Document compression */
8283
8284
8285 cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", con, con->number,
8286 uri->values[0].string.text);
8287
8288 /*
8289 * Validate print file attributes, for now just document-format and
8290 * compression (CUPS only supports "none" and "gzip")...
8291 */
8292
8293 compression = CUPS_FILE_NONE;
8294
8295 if ((attr = ippFindAttribute(con->request, "compression",
8296 IPP_TAG_KEYWORD)) != NULL)
8297 {
8298 if (strcmp(attr->values[0].string.text, "none")
8299 #ifdef HAVE_LIBZ
8300 && strcmp(attr->values[0].string.text, "gzip")
8301 #endif /* HAVE_LIBZ */
8302 )
8303 {
8304 send_ipp_status(con, IPP_ATTRIBUTES,
8305 _("Unsupported compression \"%s\"."),
8306 attr->values[0].string.text);
8307 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
8308 "compression", NULL, attr->values[0].string.text);
8309 return;
8310 }
8311
8312 #ifdef HAVE_LIBZ
8313 if (!strcmp(attr->values[0].string.text, "gzip"))
8314 compression = CUPS_FILE_GZIP;
8315 #endif /* HAVE_LIBZ */
8316 }
8317
8318 /*
8319 * Do we have a file to print?
8320 */
8321
8322 if (!con->filename)
8323 {
8324 send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
8325 return;
8326 }
8327
8328 /*
8329 * Is the destination valid?
8330 */
8331
8332 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
8333 {
8334 /*
8335 * Bad URI...
8336 */
8337
8338 send_ipp_status(con, IPP_NOT_FOUND,
8339 _("The printer or class does not exist."));
8340 return;
8341 }
8342
8343 /*
8344 * Is it a format we support?
8345 */
8346
8347 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
8348 if (doc_name)
8349 ippSetName(con->request, &doc_name, "document-name-supplied");
8350
8351 if ((format = ippFindAttribute(con->request, "document-format",
8352 IPP_TAG_MIMETYPE)) != NULL)
8353 {
8354 /*
8355 * Grab format from client...
8356 */
8357
8358 if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]", super,
8359 type) != 2)
8360 {
8361 send_ipp_status(con, IPP_BAD_REQUEST,
8362 _("Bad document-format \"%s\"."),
8363 format->values[0].string.text);
8364 return;
8365 }
8366
8367 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, ippGetString(format, 0, NULL));
8368 }
8369 else if ((default_format = cupsGetOption("document-format",
8370 printer->num_options,
8371 printer->options)) != NULL)
8372 {
8373 /*
8374 * Use default document format...
8375 */
8376
8377 if (sscanf(default_format, "%15[^/]/%255[^;]", super, type) != 2)
8378 {
8379 send_ipp_status(con, IPP_BAD_REQUEST,
8380 _("Bad document-format \"%s\"."),
8381 default_format);
8382 return;
8383 }
8384 }
8385 else
8386 {
8387 /*
8388 * Auto-type it!
8389 */
8390
8391 strlcpy(super, "application", sizeof(super));
8392 strlcpy(type, "octet-stream", sizeof(type));
8393 }
8394
8395 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
8396 {
8397 /*
8398 * Auto-type the file...
8399 */
8400
8401 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job ???] Auto-typing file...");
8402
8403
8404 filetype = mimeFileType(MimeDatabase, con->filename,
8405 doc_name ? doc_name->values[0].string.text : NULL,
8406 &compression);
8407
8408 if (!filetype)
8409 filetype = mimeType(MimeDatabase, super, type);
8410
8411 cupsdLogMessage(CUPSD_LOG_INFO, "[Job ???] Request file type is %s/%s.",
8412 filetype->super, filetype->type);
8413
8414 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super, filetype->type);
8415 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, mimetype);
8416 }
8417 else
8418 filetype = mimeType(MimeDatabase, super, type);
8419
8420 if (filetype &&
8421 (!format ||
8422 (!strcmp(super, "application") && !strcmp(type, "octet-stream"))))
8423 {
8424 /*
8425 * Replace the document-format attribute value with the auto-typed or
8426 * default one.
8427 */
8428
8429 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
8430 filetype->type);
8431
8432 if (format)
8433 ippSetString(con->request, &format, 0, mimetype);
8434 else
8435 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
8436 "document-format", NULL, mimetype);
8437 }
8438 else if (!filetype)
8439 {
8440 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
8441 _("Unsupported document-format \"%s\"."),
8442 format ? format->values[0].string.text :
8443 "application/octet-stream");
8444 cupsdLogMessage(CUPSD_LOG_INFO,
8445 "Hint: Do you have the raw file printing rules enabled?");
8446
8447 if (format)
8448 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
8449 "document-format", NULL, format->values[0].string.text);
8450
8451 return;
8452 }
8453
8454 /*
8455 * Read any embedded job ticket info from PS files...
8456 */
8457
8458 if (!_cups_strcasecmp(filetype->super, "application") &&
8459 (!_cups_strcasecmp(filetype->type, "postscript") ||
8460 !_cups_strcasecmp(filetype->type, "pdf")))
8461 read_job_ticket(con);
8462
8463 /*
8464 * Create the job object...
8465 */
8466
8467 if ((job = add_job(con, printer, filetype)) == NULL)
8468 return;
8469
8470 /*
8471 * Update quota data...
8472 */
8473
8474 if (stat(con->filename, &fileinfo))
8475 kbytes = 0;
8476 else
8477 kbytes = (fileinfo.st_size + 1023) / 1024;
8478
8479 cupsdUpdateQuota(printer, job->username, 0, kbytes);
8480
8481 job->koctets += kbytes;
8482
8483 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
8484 attr->values[0].integer += kbytes;
8485
8486 /*
8487 * Add the job file...
8488 */
8489
8490 if (add_file(con, job, filetype, compression))
8491 return;
8492
8493 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id, job->num_files);
8494 if (rename(con->filename, filename))
8495 {
8496 cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to rename job document file \"%s\": %s", filename, strerror(errno));
8497
8498 send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to rename job document file."));
8499 return;
8500 }
8501
8502 cupsdClearString(&con->filename);
8503
8504 /*
8505 * See if we need to add the ending sheet...
8506 */
8507
8508 if (cupsdTimeoutJob(job))
8509 return;
8510
8511 /*
8512 * Log and save the job...
8513 */
8514
8515 cupsdLogJob(job, CUPSD_LOG_INFO,
8516 "File of type %s/%s queued by \"%s\".",
8517 filetype->super, filetype->type, job->username);
8518 cupsdLogJob(job, CUPSD_LOG_DEBUG, "hold_until=%d", (int)job->hold_until);
8519 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
8520 job->dest, job->username);
8521
8522 /*
8523 * Start the job if possible...
8524 */
8525
8526 cupsdCheckJobs();
8527 }
8528
8529
8530 /*
8531 * 'read_job_ticket()' - Read a job ticket embedded in a print file.
8532 *
8533 * This function only gets called when printing a single PDF or PostScript
8534 * file using the Print-Job operation. It doesn't work for Create-Job +
8535 * Send-File, since the job attributes need to be set at job creation
8536 * time for banners to work. The embedded job ticket stuff is here
8537 * primarily to allow the Windows printer driver for CUPS to pass in JCL
8538 * options and IPP attributes which otherwise would be lost.
8539 *
8540 * The format of a job ticket is simple:
8541 *
8542 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
8543 *
8544 * %cupsJobTicket: attr1=value1
8545 * %cupsJobTicket: attr2=value2
8546 * ...
8547 * %cupsJobTicket: attrN=valueN
8548 *
8549 * Job ticket lines must appear immediately after the first line that
8550 * specifies PostScript (%!PS-Adobe-3.0) or PDF (%PDF) format, and CUPS
8551 * stops looking for job ticket info when it finds a line that does not begin
8552 * with "%cupsJobTicket:".
8553 *
8554 * The maximum length of a job ticket line, including the prefix, is
8555 * 255 characters to conform with the Adobe DSC.
8556 *
8557 * Read-only attributes are rejected with a notice to the error log in
8558 * case a malicious user tries anything. Since the job ticket is read
8559 * prior to attribute validation in print_job(), job ticket attributes
8560 * will go through the same validation as IPP attributes...
8561 */
8562
8563 static void
8564 read_job_ticket(cupsd_client_t *con) /* I - Client connection */
8565 {
8566 cups_file_t *fp; /* File to read from */
8567 char line[256]; /* Line data */
8568 int num_options; /* Number of options */
8569 cups_option_t *options; /* Options */
8570 ipp_t *ticket; /* New attributes */
8571 ipp_attribute_t *attr, /* Current attribute */
8572 *attr2, /* Job attribute */
8573 *prev2; /* Previous job attribute */
8574
8575
8576 /*
8577 * First open the print file...
8578 */
8579
8580 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
8581 {
8582 cupsdLogMessage(CUPSD_LOG_ERROR,
8583 "Unable to open print file for job ticket - %s",
8584 strerror(errno));
8585 return;
8586 }
8587
8588 /*
8589 * Skip the first line...
8590 */
8591
8592 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
8593 {
8594 cupsdLogMessage(CUPSD_LOG_ERROR,
8595 "Unable to read from print file for job ticket - %s",
8596 strerror(errno));
8597 cupsFileClose(fp);
8598 return;
8599 }
8600
8601 if (strncmp(line, "%!PS-Adobe-", 11) && strncmp(line, "%PDF-", 5))
8602 {
8603 /*
8604 * Not a DSC-compliant file, so no job ticket info will be available...
8605 */
8606
8607 cupsFileClose(fp);
8608 return;
8609 }
8610
8611 /*
8612 * Read job ticket info from the file...
8613 */
8614
8615 num_options = 0;
8616 options = NULL;
8617
8618 while (cupsFileGets(fp, line, sizeof(line)))
8619 {
8620 /*
8621 * Stop at the first non-ticket line...
8622 */
8623
8624 if (strncmp(line, "%cupsJobTicket:", 15))
8625 break;
8626
8627 /*
8628 * Add the options to the option array...
8629 */
8630
8631 num_options = cupsParseOptions(line + 15, num_options, &options);
8632 }
8633
8634 /*
8635 * Done with the file; see if we have any options...
8636 */
8637
8638 cupsFileClose(fp);
8639
8640 if (num_options == 0)
8641 return;
8642
8643 /*
8644 * OK, convert the options to an attribute list, and apply them to
8645 * the request...
8646 */
8647
8648 ticket = ippNew();
8649 cupsEncodeOptions(ticket, num_options, options);
8650
8651 /*
8652 * See what the user wants to change.
8653 */
8654
8655 for (attr = ticket->attrs; attr; attr = attr->next)
8656 {
8657 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
8658 continue;
8659
8660 if (!strncmp(attr->name, "date-time-at-", 13) ||
8661 !strcmp(attr->name, "job-impressions-completed") ||
8662 !strcmp(attr->name, "job-media-sheets-completed") ||
8663 !strncmp(attr->name, "job-k-octets", 12) ||
8664 !strcmp(attr->name, "job-id") ||
8665 !strcmp(attr->name, "job-originating-host-name") ||
8666 !strcmp(attr->name, "job-originating-user-name") ||
8667 !strcmp(attr->name, "job-pages-completed") ||
8668 !strcmp(attr->name, "job-printer-uri") ||
8669 !strncmp(attr->name, "job-state", 9) ||
8670 !strcmp(attr->name, "job-uri") ||
8671 !strncmp(attr->name, "time-at-", 8))
8672 continue; /* Read-only attrs */
8673
8674 if ((attr2 = ippFindAttribute(con->request, attr->name,
8675 IPP_TAG_ZERO)) != NULL)
8676 {
8677 /*
8678 * Some other value; first free the old value...
8679 */
8680
8681 if (con->request->attrs == attr2)
8682 {
8683 con->request->attrs = attr2->next;
8684 prev2 = NULL;
8685 }
8686 else
8687 {
8688 for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
8689 if (prev2->next == attr2)
8690 {
8691 prev2->next = attr2->next;
8692 break;
8693 }
8694 }
8695
8696 if (con->request->last == attr2)
8697 con->request->last = prev2;
8698
8699 ippDeleteAttribute(NULL, attr2);
8700 }
8701
8702 /*
8703 * Add new option by copying it...
8704 */
8705
8706 ippCopyAttribute(con->request, attr, 0);
8707 }
8708
8709 /*
8710 * Then free the attribute list and option array...
8711 */
8712
8713 ippDelete(ticket);
8714 cupsFreeOptions(num_options, options);
8715 }
8716
8717
8718 /*
8719 * 'reject_jobs()' - Reject print jobs to a printer.
8720 */
8721
8722 static void
8723 reject_jobs(cupsd_client_t *con, /* I - Client connection */
8724 ipp_attribute_t *uri) /* I - Printer or class URI */
8725 {
8726 http_status_t status; /* Policy status */
8727 cups_ptype_t dtype; /* Destination type (printer/class) */
8728 cupsd_printer_t *printer; /* Printer data */
8729 ipp_attribute_t *attr; /* printer-state-message text */
8730
8731
8732 cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", con,
8733 con->number, uri->values[0].string.text);
8734
8735 /*
8736 * Is the destination valid?
8737 */
8738
8739 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8740 {
8741 /*
8742 * Bad URI...
8743 */
8744
8745 send_ipp_status(con, IPP_NOT_FOUND,
8746 _("The printer or class does not exist."));
8747 return;
8748 }
8749
8750 /*
8751 * Check policy...
8752 */
8753
8754 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8755 {
8756 send_http_error(con, status, printer);
8757 return;
8758 }
8759
8760 /*
8761 * Reject jobs sent to the printer...
8762 */
8763
8764 printer->accepting = 0;
8765
8766 if ((attr = ippFindAttribute(con->request, "printer-state-message",
8767 IPP_TAG_TEXT)) == NULL)
8768 strlcpy(printer->state_message, "Rejecting Jobs",
8769 sizeof(printer->state_message));
8770 else
8771 strlcpy(printer->state_message, attr->values[0].string.text,
8772 sizeof(printer->state_message));
8773
8774 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
8775 "No longer accepting jobs.");
8776
8777 if (dtype & CUPS_PRINTER_CLASS)
8778 {
8779 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
8780
8781 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
8782 printer->name, get_username(con));
8783 }
8784 else
8785 {
8786 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
8787
8788 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
8789 printer->name, get_username(con));
8790 }
8791
8792 /*
8793 * Everything was ok, so return OK status...
8794 */
8795
8796 con->response->request.status.status_code = IPP_OK;
8797 }
8798
8799
8800 /*
8801 * 'release_held_new_jobs()' - Release pending/new jobs on a printer or class.
8802 */
8803
8804 static void
8805 release_held_new_jobs(
8806 cupsd_client_t *con, /* I - Connection */
8807 ipp_attribute_t *uri) /* I - Printer URI */
8808 {
8809 http_status_t status; /* Policy status */
8810 cups_ptype_t dtype; /* Destination type (printer/class) */
8811 cupsd_printer_t *printer; /* Printer data */
8812
8813
8814 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_held_new_jobs(%p[%d], %s)", con,
8815 con->number, uri->values[0].string.text);
8816
8817 /*
8818 * Is the destination valid?
8819 */
8820
8821 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8822 {
8823 /*
8824 * Bad URI...
8825 */
8826
8827 send_ipp_status(con, IPP_NOT_FOUND,
8828 _("The printer or class does not exist."));
8829 return;
8830 }
8831
8832 /*
8833 * Check policy...
8834 */
8835
8836 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8837 {
8838 send_http_error(con, status, printer);
8839 return;
8840 }
8841
8842 /*
8843 * Hold pending/new jobs sent to the printer...
8844 */
8845
8846 printer->holding_new_jobs = 0;
8847
8848 cupsdSetPrinterReasons(printer, "-hold-new-jobs");
8849
8850 if (dtype & CUPS_PRINTER_CLASS)
8851 cupsdLogMessage(CUPSD_LOG_INFO,
8852 "Class \"%s\" now printing pending/new jobs (\"%s\").",
8853 printer->name, get_username(con));
8854 else
8855 cupsdLogMessage(CUPSD_LOG_INFO,
8856 "Printer \"%s\" now printing pending/new jobs (\"%s\").",
8857 printer->name, get_username(con));
8858
8859 /*
8860 * Everything was ok, so return OK status...
8861 */
8862
8863 con->response->request.status.status_code = IPP_OK;
8864 }
8865
8866
8867 /*
8868 * 'release_job()' - Release a held print job.
8869 */
8870
8871 static void
8872 release_job(cupsd_client_t *con, /* I - Client connection */
8873 ipp_attribute_t *uri) /* I - Job or Printer URI */
8874 {
8875 ipp_attribute_t *attr; /* Current attribute */
8876 int jobid; /* Job ID */
8877 char scheme[HTTP_MAX_URI], /* Method portion of URI */
8878 username[HTTP_MAX_URI], /* Username portion of URI */
8879 host[HTTP_MAX_URI], /* Host portion of URI */
8880 resource[HTTP_MAX_URI]; /* Resource portion of URI */
8881 int port; /* Port portion of URI */
8882 cupsd_job_t *job; /* Job information */
8883
8884
8885 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", con,
8886 con->number, uri->values[0].string.text);
8887
8888 /*
8889 * See if we have a job URI or a printer URI...
8890 */
8891
8892 if (!strcmp(uri->name, "printer-uri"))
8893 {
8894 /*
8895 * Got a printer URI; see if we also have a job-id attribute...
8896 */
8897
8898 if ((attr = ippFindAttribute(con->request, "job-id",
8899 IPP_TAG_INTEGER)) == NULL)
8900 {
8901 send_ipp_status(con, IPP_BAD_REQUEST,
8902 _("Got a printer-uri attribute but no job-id."));
8903 return;
8904 }
8905
8906 jobid = attr->values[0].integer;
8907 }
8908 else
8909 {
8910 /*
8911 * Got a job URI; parse it to get the job ID...
8912 */
8913
8914 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8915 sizeof(scheme), username, sizeof(username), host,
8916 sizeof(host), &port, resource, sizeof(resource));
8917
8918 if (strncmp(resource, "/jobs/", 6))
8919 {
8920 /*
8921 * Not a valid URI!
8922 */
8923
8924 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
8925 uri->values[0].string.text);
8926 return;
8927 }
8928
8929 jobid = atoi(resource + 6);
8930 }
8931
8932 /*
8933 * See if the job exists...
8934 */
8935
8936 if ((job = cupsdFindJob(jobid)) == NULL)
8937 {
8938 /*
8939 * Nope - return a "not found" error...
8940 */
8941
8942 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
8943 return;
8944 }
8945
8946 /*
8947 * See if job is "held"...
8948 */
8949
8950 if (job->state_value != IPP_JOB_HELD)
8951 {
8952 /*
8953 * Nope - return a "not possible" error...
8954 */
8955
8956 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held."), jobid);
8957 return;
8958 }
8959
8960 /*
8961 * See if the job is owned by the requesting user...
8962 */
8963
8964 if (!validate_user(job, con, job->username, username, sizeof(username)))
8965 {
8966 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
8967 cupsdFindDest(job->dest));
8968 return;
8969 }
8970
8971 /*
8972 * Reset the job-hold-until value to "no-hold"...
8973 */
8974
8975 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
8976 IPP_TAG_KEYWORD)) == NULL)
8977 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
8978
8979 if (attr)
8980 {
8981 ippSetValueTag(job->attrs, &attr, IPP_TAG_KEYWORD);
8982 ippSetString(job->attrs, &attr, 0, "no-hold");
8983
8984 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
8985 "Job job-hold-until value changed by user.");
8986 ippSetString(job->attrs, &job->reasons, 0, "none");
8987 }
8988
8989 /*
8990 * Release the job and return...
8991 */
8992
8993 cupsdReleaseJob(job);
8994
8995 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
8996 "Job released by user.");
8997
8998 cupsdLogJob(job, CUPSD_LOG_INFO, "Released by \"%s\".", username);
8999
9000 con->response->request.status.status_code = IPP_OK;
9001
9002 cupsdCheckJobs();
9003 }
9004
9005
9006 /*
9007 * 'renew_subscription()' - Renew an existing subscription...
9008 */
9009
9010 static void
9011 renew_subscription(
9012 cupsd_client_t *con, /* I - Client connection */
9013 int sub_id) /* I - Subscription ID */
9014 {
9015 http_status_t status; /* Policy status */
9016 cupsd_subscription_t *sub; /* Subscription */
9017 ipp_attribute_t *lease; /* notify-lease-duration */
9018
9019
9020 cupsdLogMessage(CUPSD_LOG_DEBUG2,
9021 "renew_subscription(con=%p[%d], sub_id=%d)",
9022 con, con->number, sub_id);
9023
9024 /*
9025 * Is the subscription ID valid?
9026 */
9027
9028 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
9029 {
9030 /*
9031 * Bad subscription ID...
9032 */
9033
9034 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
9035 sub_id);
9036 return;
9037 }
9038
9039 if (sub->job)
9040 {
9041 /*
9042 * Job subscriptions cannot be renewed...
9043 */
9044
9045 send_ipp_status(con, IPP_NOT_POSSIBLE,
9046 _("Job subscriptions cannot be renewed."));
9047 return;
9048 }
9049
9050 /*
9051 * Check policy...
9052 */
9053
9054 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
9055 DefaultPolicyPtr,
9056 con, sub->owner)) != HTTP_OK)
9057 {
9058 send_http_error(con, status, sub->dest);
9059 return;
9060 }
9061
9062 /*
9063 * Renew the subscription...
9064 */
9065
9066 lease = ippFindAttribute(con->request, "notify-lease-duration",
9067 IPP_TAG_INTEGER);
9068
9069 sub->lease = lease ? lease->values[0].integer : DefaultLeaseDuration;
9070
9071 if (MaxLeaseDuration && (sub->lease == 0 || sub->lease > MaxLeaseDuration))
9072 {
9073 cupsdLogMessage(CUPSD_LOG_INFO,
9074 "renew_subscription: Limiting notify-lease-duration to "
9075 "%d seconds.",
9076 MaxLeaseDuration);
9077 sub->lease = MaxLeaseDuration;
9078 }
9079
9080 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
9081
9082 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
9083
9084 con->response->request.status.status_code = IPP_OK;
9085
9086 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
9087 "notify-lease-duration", sub->lease);
9088 }
9089
9090
9091 /*
9092 * 'restart_job()' - Restart an old print job.
9093 */
9094
9095 static void
9096 restart_job(cupsd_client_t *con, /* I - Client connection */
9097 ipp_attribute_t *uri) /* I - Job or Printer URI */
9098 {
9099 ipp_attribute_t *attr; /* Current attribute */
9100 int jobid; /* Job ID */
9101 cupsd_job_t *job; /* Job information */
9102 char scheme[HTTP_MAX_URI], /* Method portion of URI */
9103 username[HTTP_MAX_URI], /* Username portion of URI */
9104 host[HTTP_MAX_URI], /* Host portion of URI */
9105 resource[HTTP_MAX_URI]; /* Resource portion of URI */
9106 int port; /* Port portion of URI */
9107
9108
9109 cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con,
9110 con->number, uri->values[0].string.text);
9111
9112 /*
9113 * See if we have a job URI or a printer URI...
9114 */
9115
9116 if (!strcmp(uri->name, "printer-uri"))
9117 {
9118 /*
9119 * Got a printer URI; see if we also have a job-id attribute...
9120 */
9121
9122 if ((attr = ippFindAttribute(con->request, "job-id",
9123 IPP_TAG_INTEGER)) == NULL)
9124 {
9125 send_ipp_status(con, IPP_BAD_REQUEST,
9126 _("Got a printer-uri attribute but no job-id."));
9127 return;
9128 }
9129
9130 jobid = attr->values[0].integer;
9131 }
9132 else
9133 {
9134 /*
9135 * Got a job URI; parse it to get the job ID...
9136 */
9137
9138 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9139 sizeof(scheme), username, sizeof(username), host,
9140 sizeof(host), &port, resource, sizeof(resource));
9141
9142 if (strncmp(resource, "/jobs/", 6))
9143 {
9144 /*
9145 * Not a valid URI!
9146 */
9147
9148 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
9149 uri->values[0].string.text);
9150 return;
9151 }
9152
9153 jobid = atoi(resource + 6);
9154 }
9155
9156 /*
9157 * See if the job exists...
9158 */
9159
9160 if ((job = cupsdFindJob(jobid)) == NULL)
9161 {
9162 /*
9163 * Nope - return a "not found" error...
9164 */
9165
9166 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
9167 return;
9168 }
9169
9170 /*
9171 * See if job is in any of the "completed" states...
9172 */
9173
9174 if (job->state_value <= IPP_JOB_PROCESSING)
9175 {
9176 /*
9177 * Nope - return a "not possible" error...
9178 */
9179
9180 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete."),
9181 jobid);
9182 return;
9183 }
9184
9185 /*
9186 * See if we have retained the job files...
9187 */
9188
9189 cupsdLoadJob(job);
9190
9191 if (!job->attrs || job->num_files == 0)
9192 {
9193 /*
9194 * Nope - return a "not possible" error...
9195 */
9196
9197 send_ipp_status(con, IPP_NOT_POSSIBLE,
9198 _("Job #%d cannot be restarted - no files."), jobid);
9199 return;
9200 }
9201
9202 /*
9203 * See if the job is owned by the requesting user...
9204 */
9205
9206 if (!validate_user(job, con, job->username, username, sizeof(username)))
9207 {
9208 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9209 cupsdFindDest(job->dest));
9210 return;
9211 }
9212
9213 /*
9214 * See if the job-hold-until attribute is specified...
9215 */
9216
9217 if ((attr = ippFindAttribute(con->request, "job-hold-until",
9218 IPP_TAG_KEYWORD)) == NULL)
9219 attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
9220
9221 if (attr && strcmp(attr->values[0].string.text, "no-hold"))
9222 {
9223 /*
9224 * Return the job to a held state...
9225 */
9226
9227 cupsdLogJob(job, CUPSD_LOG_DEBUG,
9228 "Restarted by \"%s\" with job-hold-until=%s.",
9229 username, attr->values[0].string.text);
9230 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
9231
9232 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE,
9233 NULL, job, "Job restarted by user with job-hold-until=%s",
9234 attr->values[0].string.text);
9235 }
9236 else
9237 {
9238 /*
9239 * Restart the job...
9240 */
9241
9242 cupsdRestartJob(job);
9243 cupsdCheckJobs();
9244 }
9245
9246 cupsdLogJob(job, CUPSD_LOG_INFO, "Restarted by \"%s\".", username);
9247
9248 con->response->request.status.status_code = IPP_OK;
9249 }
9250
9251
9252 /*
9253 * 'save_auth_info()' - Save authentication information for a job.
9254 */
9255
9256 static void
9257 save_auth_info(
9258 cupsd_client_t *con, /* I - Client connection */
9259 cupsd_job_t *job, /* I - Job */
9260 ipp_attribute_t *auth_info) /* I - auth-info attribute, if any */
9261 {
9262 int i; /* Looping var */
9263 char filename[1024]; /* Job authentication filename */
9264 cups_file_t *fp; /* Job authentication file */
9265 char line[65536]; /* Line for file */
9266 cupsd_printer_t *dest; /* Destination printer/class */
9267
9268
9269 /*
9270 * This function saves the in-memory authentication information for
9271 * a job so that it can be used to authenticate with a remote host.
9272 * The information is stored in a file that is readable only by the
9273 * root user. The fields are Base-64 encoded, each on a separate line,
9274 * followed by random number (up to 1024) of newlines to limit the
9275 * amount of information that is exposed.
9276 *
9277 * Because of the potential for exposing of authentication information,
9278 * this functionality is only enabled when running cupsd as root.
9279 *
9280 * This caching only works for the Basic and BasicDigest authentication
9281 * types. Digest authentication cannot be cached this way, and in
9282 * the future Kerberos authentication may make all of this obsolete.
9283 *
9284 * Authentication information is saved whenever an authenticated
9285 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
9286 * performed.
9287 *
9288 * This information is deleted after a job is completed or canceled,
9289 * so reprints may require subsequent re-authentication.
9290 */
9291
9292 if (RunUser)
9293 return;
9294
9295 if ((dest = cupsdFindDest(job->dest)) == NULL)
9296 return;
9297
9298 /*
9299 * Create the authentication file and change permissions...
9300 */
9301
9302 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
9303 if ((fp = cupsFileOpen(filename, "w")) == NULL)
9304 {
9305 cupsdLogMessage(CUPSD_LOG_ERROR,
9306 "Unable to save authentication info to \"%s\" - %s",
9307 filename, strerror(errno));
9308 return;
9309 }
9310
9311 fchown(cupsFileNumber(fp), 0, 0);
9312 fchmod(cupsFileNumber(fp), 0400);
9313
9314 cupsFilePuts(fp, "CUPSD-AUTH-V3\n");
9315
9316 for (i = 0;
9317 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
9318 i ++)
9319 cupsdClearString(job->auth_env + i);
9320
9321 if (auth_info && auth_info->num_values == dest->num_auth_info_required)
9322 {
9323 /*
9324 * Write 1 to 3 auth values...
9325 */
9326
9327 for (i = 0;
9328 i < auth_info->num_values &&
9329 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
9330 i ++)
9331 {
9332 if (strcmp(dest->auth_info_required[i], "negotiate"))
9333 {
9334 httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text, (int)strlen(auth_info->values[i].string.text));
9335 cupsFilePutConf(fp, dest->auth_info_required[i], line);
9336 }
9337 else
9338 cupsFilePutConf(fp, dest->auth_info_required[i],
9339 auth_info->values[i].string.text);
9340
9341 if (!strcmp(dest->auth_info_required[i], "username"))
9342 cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s",
9343 auth_info->values[i].string.text);
9344 else if (!strcmp(dest->auth_info_required[i], "domain"))
9345 cupsdSetStringf(job->auth_env + i, "AUTH_DOMAIN=%s",
9346 auth_info->values[i].string.text);
9347 else if (!strcmp(dest->auth_info_required[i], "password"))
9348 cupsdSetStringf(job->auth_env + i, "AUTH_PASSWORD=%s",
9349 auth_info->values[i].string.text);
9350 else if (!strcmp(dest->auth_info_required[i], "negotiate"))
9351 cupsdSetStringf(job->auth_env + i, "AUTH_NEGOTIATE=%s",
9352 auth_info->values[i].string.text);
9353 else
9354 i --;
9355 }
9356 }
9357 else if (auth_info && auth_info->num_values == 2 &&
9358 dest->num_auth_info_required == 1 &&
9359 !strcmp(dest->auth_info_required[0], "negotiate"))
9360 {
9361 /*
9362 * Allow fallback to username+password for Kerberized queues...
9363 */
9364
9365 httpEncode64_2(line, sizeof(line), auth_info->values[0].string.text, (int)strlen(auth_info->values[0].string.text));
9366 cupsFilePutConf(fp, "username", line);
9367
9368 cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s",
9369 auth_info->values[0].string.text);
9370
9371 httpEncode64_2(line, sizeof(line), auth_info->values[1].string.text, (int)strlen(auth_info->values[1].string.text));
9372 cupsFilePutConf(fp, "password", line);
9373
9374 cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s",
9375 auth_info->values[1].string.text);
9376 }
9377 else if (con->username[0])
9378 {
9379 /*
9380 * Write the authenticated username...
9381 */
9382
9383 httpEncode64_2(line, sizeof(line), con->username, (int)strlen(con->username));
9384 cupsFilePutConf(fp, "username", line);
9385
9386 cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s", con->username);
9387
9388 /*
9389 * Write the authenticated password...
9390 */
9391
9392 httpEncode64_2(line, sizeof(line), con->password, (int)strlen(con->password));
9393 cupsFilePutConf(fp, "password", line);
9394
9395 cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s", con->password);
9396 }
9397
9398 #ifdef HAVE_GSSAPI
9399 if (con->gss_uid > 0)
9400 {
9401 cupsFilePrintf(fp, "uid %d\n", (int)con->gss_uid);
9402 cupsdSetStringf(&job->auth_uid, "AUTH_UID=%d", (int)con->gss_uid);
9403 }
9404 #endif /* HAVE_GSSAPI */
9405
9406 /*
9407 * Write a random number of newlines to the end of the file...
9408 */
9409
9410 for (i = (CUPS_RAND() % 1024); i >= 0; i --)
9411 cupsFilePutChar(fp, '\n');
9412
9413 /*
9414 * Close the file and return...
9415 */
9416
9417 cupsFileClose(fp);
9418 }
9419
9420
9421 /*
9422 * 'send_document()' - Send a file to a printer or class.
9423 */
9424
9425 static void
9426 send_document(cupsd_client_t *con, /* I - Client connection */
9427 ipp_attribute_t *uri) /* I - Printer URI */
9428 {
9429 ipp_attribute_t *attr; /* Current attribute */
9430 ipp_attribute_t *format; /* Request's document-format attribute */
9431 ipp_attribute_t *jformat; /* Job's document-format attribute */
9432 const char *default_format;/* document-format-default value */
9433 int jobid; /* Job ID number */
9434 cupsd_job_t *job; /* Current job */
9435 char job_uri[HTTP_MAX_URI],
9436 /* Job URI */
9437 scheme[HTTP_MAX_URI],
9438 /* Method portion of URI */
9439 username[HTTP_MAX_URI],
9440 /* Username portion of URI */
9441 host[HTTP_MAX_URI],
9442 /* Host portion of URI */
9443 resource[HTTP_MAX_URI];
9444 /* Resource portion of URI */
9445 int port; /* Port portion of URI */
9446 mime_type_t *filetype; /* Type of file */
9447 char super[MIME_MAX_SUPER],
9448 /* Supertype of file */
9449 type[MIME_MAX_TYPE],
9450 /* Subtype of file */
9451 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
9452 /* Textual name of mime type */
9453 char filename[1024]; /* Job filename */
9454 cupsd_printer_t *printer; /* Current printer */
9455 struct stat fileinfo; /* File information */
9456 int kbytes; /* Size of file */
9457 int compression; /* Type of compression */
9458 int start_job; /* Start the job? */
9459
9460
9461 cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con,
9462 con->number, uri->values[0].string.text);
9463
9464 /*
9465 * See if we have a job URI or a printer URI...
9466 */
9467
9468 if (!strcmp(uri->name, "printer-uri"))
9469 {
9470 /*
9471 * Got a printer URI; see if we also have a job-id attribute...
9472 */
9473
9474 if ((attr = ippFindAttribute(con->request, "job-id",
9475 IPP_TAG_INTEGER)) == NULL)
9476 {
9477 send_ipp_status(con, IPP_BAD_REQUEST,
9478 _("Got a printer-uri attribute but no job-id."));
9479 return;
9480 }
9481
9482 jobid = attr->values[0].integer;
9483 }
9484 else
9485 {
9486 /*
9487 * Got a job URI; parse it to get the job ID...
9488 */
9489
9490 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9491 sizeof(scheme), username, sizeof(username), host,
9492 sizeof(host), &port, resource, sizeof(resource));
9493
9494 if (strncmp(resource, "/jobs/", 6))
9495 {
9496 /*
9497 * Not a valid URI!
9498 */
9499
9500 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
9501 uri->values[0].string.text);
9502 return;
9503 }
9504
9505 jobid = atoi(resource + 6);
9506 }
9507
9508 /*
9509 * See if the job exists...
9510 */
9511
9512 if ((job = cupsdFindJob(jobid)) == NULL)
9513 {
9514 /*
9515 * Nope - return a "not found" error...
9516 */
9517
9518 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
9519 return;
9520 }
9521
9522 printer = cupsdFindDest(job->dest);
9523
9524 /*
9525 * See if the job is owned by the requesting user...
9526 */
9527
9528 if (!validate_user(job, con, job->username, username, sizeof(username)))
9529 {
9530 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9531 cupsdFindDest(job->dest));
9532 return;
9533 }
9534
9535 /*
9536 * OK, see if the client is sending the document compressed - CUPS
9537 * only supports "none" and "gzip".
9538 */
9539
9540 compression = CUPS_FILE_NONE;
9541
9542 if ((attr = ippFindAttribute(con->request, "compression",
9543 IPP_TAG_KEYWORD)) != NULL)
9544 {
9545 if (strcmp(attr->values[0].string.text, "none")
9546 #ifdef HAVE_LIBZ
9547 && strcmp(attr->values[0].string.text, "gzip")
9548 #endif /* HAVE_LIBZ */
9549 )
9550 {
9551 send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\"."),
9552 attr->values[0].string.text);
9553 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
9554 "compression", NULL, attr->values[0].string.text);
9555 return;
9556 }
9557
9558 #ifdef HAVE_LIBZ
9559 if (!strcmp(attr->values[0].string.text, "gzip"))
9560 compression = CUPS_FILE_GZIP;
9561 #endif /* HAVE_LIBZ */
9562 }
9563
9564 /*
9565 * Do we have a file to print?
9566 */
9567
9568 if ((attr = ippFindAttribute(con->request, "last-document",
9569 IPP_TAG_BOOLEAN)) == NULL)
9570 {
9571 send_ipp_status(con, IPP_BAD_REQUEST,
9572 _("Missing last-document attribute in request."));
9573 return;
9574 }
9575
9576 if (!con->filename)
9577 {
9578 /*
9579 * Check for an empty request with "last-document" set to true, which is
9580 * used to close an "open" job by RFC 2911, section 3.3.2.
9581 */
9582
9583 if (job->num_files > 0 && attr->values[0].boolean)
9584 goto last_document;
9585
9586 send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
9587 return;
9588 }
9589
9590 /*
9591 * Is it a format we support?
9592 */
9593
9594 cupsdLoadJob(job);
9595
9596 if ((format = ippFindAttribute(con->request, "document-format",
9597 IPP_TAG_MIMETYPE)) != NULL)
9598 {
9599 /*
9600 * Grab format from client...
9601 */
9602
9603 if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]",
9604 super, type) != 2)
9605 {
9606 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"."),
9607 format->values[0].string.text);
9608 return;
9609 }
9610
9611 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, ippGetString(format, 0, NULL));
9612 }
9613 else if ((default_format = cupsGetOption("document-format",
9614 printer->num_options,
9615 printer->options)) != NULL)
9616 {
9617 /*
9618 * Use default document format...
9619 */
9620
9621 if (sscanf(default_format, "%15[^/]/%255[^;]", super, type) != 2)
9622 {
9623 send_ipp_status(con, IPP_BAD_REQUEST,
9624 _("Bad document-format-default \"%s\"."), default_format);
9625 return;
9626 }
9627 }
9628 else
9629 {
9630 /*
9631 * No document format attribute? Auto-type it!
9632 */
9633
9634 strlcpy(super, "application", sizeof(super));
9635 strlcpy(type, "octet-stream", sizeof(type));
9636 }
9637
9638 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
9639 {
9640 /*
9641 * Auto-type the file...
9642 */
9643
9644 ipp_attribute_t *doc_name; /* document-name attribute */
9645
9646
9647 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Auto-typing file...");
9648
9649 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
9650 filetype = mimeFileType(MimeDatabase, con->filename,
9651 doc_name ? doc_name->values[0].string.text : NULL,
9652 &compression);
9653
9654 if (!filetype)
9655 filetype = mimeType(MimeDatabase, super, type);
9656
9657 if (filetype)
9658 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Request file type is %s/%s.",
9659 filetype->super, filetype->type);
9660
9661 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super, filetype->type);
9662 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, mimetype);
9663 }
9664 else
9665 filetype = mimeType(MimeDatabase, super, type);
9666
9667 if (filetype)
9668 {
9669 /*
9670 * Replace the document-format attribute value with the auto-typed or
9671 * default one.
9672 */
9673
9674 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9675 filetype->type);
9676
9677 if ((jformat = ippFindAttribute(job->attrs, "document-format",
9678 IPP_TAG_MIMETYPE)) != NULL)
9679 ippSetString(job->attrs, &jformat, 0, mimetype);
9680 else
9681 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
9682 "document-format", NULL, mimetype);
9683 }
9684 else if (!filetype)
9685 {
9686 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
9687 _("Unsupported document-format \"%s/%s\"."), super, type);
9688 cupsdLogMessage(CUPSD_LOG_INFO,
9689 "Hint: Do you have the raw file printing rules enabled?");
9690
9691 if (format)
9692 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9693 "document-format", NULL, format->values[0].string.text);
9694
9695 return;
9696 }
9697
9698 if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
9699 {
9700 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9701 filetype->type);
9702
9703 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
9704 _("Unsupported document-format \"%s\"."), mimetype);
9705
9706 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9707 "document-format", NULL, mimetype);
9708
9709 return;
9710 }
9711
9712 /*
9713 * Add the file to the job...
9714 */
9715
9716 if (add_file(con, job, filetype, compression))
9717 return;
9718
9719 if ((attr = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME)) != NULL)
9720 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
9721
9722 if (stat(con->filename, &fileinfo))
9723 kbytes = 0;
9724 else
9725 kbytes = (fileinfo.st_size + 1023) / 1024;
9726
9727 cupsdUpdateQuota(printer, job->username, 0, kbytes);
9728
9729 job->koctets += kbytes;
9730
9731 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
9732 attr->values[0].integer += kbytes;
9733
9734 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id, job->num_files);
9735 if (rename(con->filename, filename))
9736 {
9737 cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to rename job document file \"%s\": %s", filename, strerror(errno));
9738
9739 send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to rename job document file."));
9740 return;
9741 }
9742
9743 cupsdClearString(&con->filename);
9744
9745 cupsdLogJob(job, CUPSD_LOG_INFO, "File of type %s/%s queued by \"%s\".",
9746 filetype->super, filetype->type, job->username);
9747
9748 /*
9749 * Start the job if this is the last document...
9750 */
9751
9752 last_document:
9753
9754 if ((attr = ippFindAttribute(con->request, "last-document",
9755 IPP_TAG_BOOLEAN)) != NULL &&
9756 attr->values[0].boolean)
9757 {
9758 /*
9759 * See if we need to add the ending sheet...
9760 */
9761
9762 if (cupsdTimeoutJob(job))
9763 return;
9764
9765 if (job->state_value == IPP_JOB_STOPPED)
9766 {
9767 job->state->values[0].integer = IPP_JOB_PENDING;
9768 job->state_value = IPP_JOB_PENDING;
9769
9770 ippSetString(job->attrs, &job->reasons, 0, "none");
9771 }
9772 else if (job->state_value == IPP_JOB_HELD)
9773 {
9774 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9775 IPP_TAG_KEYWORD)) == NULL)
9776 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9777
9778 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
9779 {
9780 job->state->values[0].integer = IPP_JOB_PENDING;
9781 job->state_value = IPP_JOB_PENDING;
9782
9783 ippSetString(job->attrs, &job->reasons, 0, "none");
9784 }
9785 else
9786 ippSetString(job->attrs, &job->reasons, 0, "job-hold-until-specified");
9787 }
9788
9789 job->dirty = 1;
9790 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
9791
9792 start_job = 1;
9793 }
9794 else
9795 {
9796 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9797 IPP_TAG_KEYWORD)) == NULL)
9798 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9799
9800 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
9801 {
9802 job->state->values[0].integer = IPP_JOB_HELD;
9803 job->state_value = IPP_JOB_HELD;
9804 job->hold_until = time(NULL) + MultipleOperationTimeout;
9805
9806 ippSetString(job->attrs, &job->reasons, 0, "job-incoming");
9807
9808 job->dirty = 1;
9809 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
9810 }
9811
9812 start_job = 0;
9813 }
9814
9815 /*
9816 * Fill in the response info...
9817 */
9818
9819 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
9820 con->clientname, con->clientport, "/jobs/%d", jobid);
9821 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
9822 job_uri);
9823
9824 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
9825
9826 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
9827 job->state_value);
9828 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons",
9829 NULL, job->reasons->values[0].string.text);
9830
9831 con->response->request.status.status_code = IPP_OK;
9832
9833 /*
9834 * Start the job if necessary...
9835 */
9836
9837 if (start_job)
9838 cupsdCheckJobs();
9839 }
9840
9841
9842 /*
9843 * 'send_http_error()' - Send a HTTP error back to the IPP client.
9844 */
9845
9846 static void
9847 send_http_error(
9848 cupsd_client_t *con, /* I - Client connection */
9849 http_status_t status, /* I - HTTP status code */
9850 cupsd_printer_t *printer) /* I - Printer, if any */
9851 {
9852 ipp_attribute_t *uri; /* Request URI, if any */
9853
9854
9855 if ((uri = ippFindAttribute(con->request, "printer-uri",
9856 IPP_TAG_URI)) == NULL)
9857 uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI);
9858
9859 cupsdLogMessage(status == HTTP_FORBIDDEN ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
9860 "[Client %d] Returning HTTP %s for %s (%s) from %s",
9861 con->number, httpStatus(status),
9862 con->request ?
9863 ippOpString(con->request->request.op.operation_id) :
9864 "no operation-id",
9865 uri ? uri->values[0].string.text : "no URI",
9866 con->http->hostname);
9867
9868 if (printer)
9869 {
9870 int auth_type; /* Type of authentication required */
9871
9872
9873 auth_type = CUPSD_AUTH_NONE;
9874
9875 if (status == HTTP_UNAUTHORIZED &&
9876 printer->num_auth_info_required > 0 &&
9877 !strcmp(printer->auth_info_required[0], "negotiate") &&
9878 con->request &&
9879 (con->request->request.op.operation_id == IPP_PRINT_JOB ||
9880 con->request->request.op.operation_id == IPP_CREATE_JOB ||
9881 con->request->request.op.operation_id == CUPS_AUTHENTICATE_JOB))
9882 {
9883 /*
9884 * Creating and authenticating jobs requires Kerberos...
9885 */
9886
9887 auth_type = CUPSD_AUTH_NEGOTIATE;
9888 }
9889 else
9890 {
9891 /*
9892 * Use policy/location-defined authentication requirements...
9893 */
9894
9895 char resource[HTTP_MAX_URI]; /* Resource portion of URI */
9896 cupsd_location_t *auth; /* Pointer to authentication element */
9897
9898
9899 if (printer->type & CUPS_PRINTER_CLASS)
9900 snprintf(resource, sizeof(resource), "/classes/%s", printer->name);
9901 else
9902 snprintf(resource, sizeof(resource), "/printers/%s", printer->name);
9903
9904 if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
9905 auth->type == CUPSD_AUTH_NONE)
9906 auth = cupsdFindPolicyOp(printer->op_policy_ptr,
9907 con->request ?
9908 con->request->request.op.operation_id :
9909 IPP_PRINT_JOB);
9910
9911 if (auth)
9912 {
9913 if (auth->type == CUPSD_AUTH_DEFAULT)
9914 auth_type = cupsdDefaultAuthType();
9915 else
9916 auth_type = auth->type;
9917 }
9918 }
9919
9920 cupsdSendError(con, status, auth_type);
9921 }
9922 else
9923 cupsdSendError(con, status, CUPSD_AUTH_NONE);
9924
9925 ippDelete(con->response);
9926 con->response = NULL;
9927
9928 return;
9929 }
9930
9931
9932 /*
9933 * 'send_ipp_status()' - Send a status back to the IPP client.
9934 */
9935
9936 static void
9937 send_ipp_status(cupsd_client_t *con, /* I - Client connection */
9938 ipp_status_t status, /* I - IPP status code */
9939 const char *message,/* I - Status message */
9940 ...) /* I - Additional args as needed */
9941 {
9942 va_list ap; /* Pointer to additional args */
9943 char formatted[1024]; /* Formatted errror message */
9944
9945
9946 va_start(ap, message);
9947 vsnprintf(formatted, sizeof(formatted),
9948 _cupsLangString(con->language, message), ap);
9949 va_end(ap);
9950
9951 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
9952 ippOpString(con->request->request.op.operation_id),
9953 ippErrorString(status), formatted);
9954
9955 con->response->request.status.status_code = status;
9956
9957 if (ippFindAttribute(con->response, "attributes-charset",
9958 IPP_TAG_ZERO) == NULL)
9959 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
9960 "attributes-charset", NULL, "utf-8");
9961
9962 if (ippFindAttribute(con->response, "attributes-natural-language",
9963 IPP_TAG_ZERO) == NULL)
9964 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
9965 "attributes-natural-language", NULL, DefaultLanguage);
9966
9967 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
9968 "status-message", NULL, formatted);
9969 }
9970
9971
9972 /*
9973 * 'set_default()' - Set the default destination...
9974 */
9975
9976 static void
9977 set_default(cupsd_client_t *con, /* I - Client connection */
9978 ipp_attribute_t *uri) /* I - Printer URI */
9979 {
9980 http_status_t status; /* Policy status */
9981 cups_ptype_t dtype; /* Destination type (printer/class) */
9982 cupsd_printer_t *printer, /* Printer */
9983 *oldprinter; /* Old default printer */
9984
9985
9986 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con,
9987 con->number, uri->values[0].string.text);
9988
9989 /*
9990 * Is the destination valid?
9991 */
9992
9993 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
9994 {
9995 /*
9996 * Bad URI...
9997 */
9998
9999 send_ipp_status(con, IPP_NOT_FOUND,
10000 _("The printer or class does not exist."));
10001 return;
10002 }
10003
10004 /*
10005 * Check policy...
10006 */
10007
10008 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
10009 {
10010 send_http_error(con, status, NULL);
10011 return;
10012 }
10013
10014 /*
10015 * Set it as the default...
10016 */
10017
10018 oldprinter = DefaultPrinter;
10019 DefaultPrinter = printer;
10020
10021 if (oldprinter)
10022 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, oldprinter, NULL,
10023 "%s is no longer the default printer.", oldprinter->name);
10024
10025 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
10026 "%s is now the default printer.", printer->name);
10027
10028 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS | CUPSD_DIRTY_CLASSES |
10029 CUPSD_DIRTY_PRINTCAP);
10030
10031 cupsdLogMessage(CUPSD_LOG_INFO,
10032 "Default destination set to \"%s\" by \"%s\".",
10033 printer->name, get_username(con));
10034
10035 /*
10036 * Everything was ok, so return OK status...
10037 */
10038
10039 con->response->request.status.status_code = IPP_OK;
10040 }
10041
10042
10043 /*
10044 * 'set_job_attrs()' - Set job attributes.
10045 */
10046
10047 static void
10048 set_job_attrs(cupsd_client_t *con, /* I - Client connection */
10049 ipp_attribute_t *uri) /* I - Job URI */
10050 {
10051 ipp_attribute_t *attr, /* Current attribute */
10052 *attr2; /* Job attribute */
10053 int jobid; /* Job ID */
10054 cupsd_job_t *job; /* Current job */
10055 char scheme[HTTP_MAX_URI],
10056 /* Method portion of URI */
10057 username[HTTP_MAX_URI],
10058 /* Username portion of URI */
10059 host[HTTP_MAX_URI],
10060 /* Host portion of URI */
10061 resource[HTTP_MAX_URI];
10062 /* Resource portion of URI */
10063 int port; /* Port portion of URI */
10064 int event; /* Events? */
10065 int check_jobs; /* Check jobs? */
10066
10067
10068 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
10069 con->number, uri->values[0].string.text);
10070
10071 /*
10072 * Start with "everything is OK" status...
10073 */
10074
10075 con->response->request.status.status_code = IPP_OK;
10076
10077 /*
10078 * See if we have a job URI or a printer URI...
10079 */
10080
10081 if (!strcmp(uri->name, "printer-uri"))
10082 {
10083 /*
10084 * Got a printer URI; see if we also have a job-id attribute...
10085 */
10086
10087 if ((attr = ippFindAttribute(con->request, "job-id",
10088 IPP_TAG_INTEGER)) == NULL)
10089 {
10090 send_ipp_status(con, IPP_BAD_REQUEST,
10091 _("Got a printer-uri attribute but no job-id."));
10092 return;
10093 }
10094
10095 jobid = attr->values[0].integer;
10096 }
10097 else
10098 {
10099 /*
10100 * Got a job URI; parse it to get the job ID...
10101 */
10102
10103 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
10104 sizeof(scheme), username, sizeof(username), host,
10105 sizeof(host), &port, resource, sizeof(resource));
10106
10107 if (strncmp(resource, "/jobs/", 6))
10108 {
10109 /*
10110 * Not a valid URI!
10111 */
10112
10113 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
10114 uri->values[0].string.text);
10115 return;
10116 }
10117
10118 jobid = atoi(resource + 6);
10119 }
10120
10121 /*
10122 * See if the job exists...
10123 */
10124
10125 if ((job = cupsdFindJob(jobid)) == NULL)
10126 {
10127 /*
10128 * Nope - return a "not found" error...
10129 */
10130
10131 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
10132 return;
10133 }
10134
10135 /*
10136 * See if the job has been completed...
10137 */
10138
10139 if (job->state_value > IPP_JOB_STOPPED)
10140 {
10141 /*
10142 * Return a "not-possible" error...
10143 */
10144
10145 send_ipp_status(con, IPP_NOT_POSSIBLE,
10146 _("Job #%d is finished and cannot be altered."), jobid);
10147 return;
10148 }
10149
10150 /*
10151 * See if the job is owned by the requesting user...
10152 */
10153
10154 if (!validate_user(job, con, job->username, username, sizeof(username)))
10155 {
10156 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
10157 cupsdFindDest(job->dest));
10158 return;
10159 }
10160
10161 /*
10162 * See what the user wants to change.
10163 */
10164
10165 cupsdLoadJob(job);
10166
10167 check_jobs = 0;
10168 event = 0;
10169
10170 for (attr = con->request->attrs; attr; attr = attr->next)
10171 {
10172 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
10173 continue;
10174
10175 if (!strcmp(attr->name, "attributes-charset") ||
10176 !strcmp(attr->name, "attributes-natural-language") ||
10177 !strncmp(attr->name, "date-time-at-", 13) ||
10178 !strncmp(attr->name, "document-compression", 20) ||
10179 !strncmp(attr->name, "document-format", 15) ||
10180 !strcmp(attr->name, "job-detailed-status-messages") ||
10181 !strcmp(attr->name, "job-document-access-errors") ||
10182 !strcmp(attr->name, "job-id") ||
10183 !strcmp(attr->name, "job-impressions-completed") ||
10184 !strcmp(attr->name, "job-k-octets-completed") ||
10185 !strcmp(attr->name, "job-media-sheets-completed") ||
10186 !strcmp(attr->name, "job-originating-host-name") ||
10187 !strcmp(attr->name, "job-originating-user-name") ||
10188 !strcmp(attr->name, "job-pages-completed") ||
10189 !strcmp(attr->name, "job-printer-up-time") ||
10190 !strcmp(attr->name, "job-printer-uri") ||
10191 !strcmp(attr->name, "job-sheets") ||
10192 !strcmp(attr->name, "job-state-message") ||
10193 !strcmp(attr->name, "job-state-reasons") ||
10194 !strcmp(attr->name, "job-uri") ||
10195 !strcmp(attr->name, "number-of-documents") ||
10196 !strcmp(attr->name, "number-of-intervening-jobs") ||
10197 !strcmp(attr->name, "output-device-assigned") ||
10198 !strncmp(attr->name, "time-at-", 8))
10199 {
10200 /*
10201 * Read-only attrs!
10202 */
10203
10204 send_ipp_status(con, IPP_ATTRIBUTES_NOT_SETTABLE,
10205 _("%s cannot be changed."), attr->name);
10206
10207 attr2 = ippCopyAttribute(con->response, attr, 0);
10208 ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
10209 continue;
10210 }
10211
10212 if (!strcmp(attr->name, "job-priority"))
10213 {
10214 /*
10215 * Change the job priority...
10216 */
10217
10218 if (attr->value_tag != IPP_TAG_INTEGER)
10219 {
10220 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value."));
10221
10222 attr2 = ippCopyAttribute(con->response, attr, 0);
10223 ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
10224 }
10225 else if (job->state_value >= IPP_JOB_PROCESSING)
10226 {
10227 send_ipp_status(con, IPP_NOT_POSSIBLE,
10228 _("Job is completed and cannot be changed."));
10229 return;
10230 }
10231 else if (con->response->request.status.status_code == IPP_OK)
10232 {
10233 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-priority to %d",
10234 attr->values[0].integer);
10235 cupsdSetJobPriority(job, attr->values[0].integer);
10236
10237 check_jobs = 1;
10238 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED |
10239 CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED;
10240 }
10241 }
10242 else if (!strcmp(attr->name, "job-state"))
10243 {
10244 /*
10245 * Change the job state...
10246 */
10247
10248 if (attr->value_tag != IPP_TAG_ENUM)
10249 {
10250 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value."));
10251
10252 attr2 = ippCopyAttribute(con->response, attr, 0);
10253 ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
10254 }
10255 else
10256 {
10257 switch (attr->values[0].integer)
10258 {
10259 case IPP_JOB_PENDING :
10260 case IPP_JOB_HELD :
10261 if (job->state_value > IPP_JOB_HELD)
10262 {
10263 send_ipp_status(con, IPP_NOT_POSSIBLE,
10264 _("Job state cannot be changed."));
10265 return;
10266 }
10267 else if (con->response->request.status.status_code == IPP_OK)
10268 {
10269 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
10270 attr->values[0].integer);
10271 cupsdSetJobState(job, (ipp_jstate_t)attr->values[0].integer, CUPSD_JOB_DEFAULT, "Job state changed by \"%s\"", username);
10272 check_jobs = 1;
10273 }
10274 break;
10275
10276 case IPP_JOB_PROCESSING :
10277 case IPP_JOB_STOPPED :
10278 if (job->state_value != attr->values[0].integer)
10279 {
10280 send_ipp_status(con, IPP_NOT_POSSIBLE,
10281 _("Job state cannot be changed."));
10282 return;
10283 }
10284 break;
10285
10286 case IPP_JOB_CANCELED :
10287 case IPP_JOB_ABORTED :
10288 case IPP_JOB_COMPLETED :
10289 if (job->state_value > IPP_JOB_PROCESSING)
10290 {
10291 send_ipp_status(con, IPP_NOT_POSSIBLE,
10292 _("Job state cannot be changed."));
10293 return;
10294 }
10295 else if (con->response->request.status.status_code == IPP_OK)
10296 {
10297 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
10298 attr->values[0].integer);
10299 cupsdSetJobState(job, (ipp_jstate_t)attr->values[0].integer,
10300 CUPSD_JOB_DEFAULT,
10301 "Job state changed by \"%s\"", username);
10302 check_jobs = 1;
10303 }
10304 break;
10305 }
10306 }
10307 }
10308 else if (con->response->request.status.status_code != IPP_OK)
10309 continue;
10310 else if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10311 IPP_TAG_ZERO)) != NULL)
10312 {
10313 /*
10314 * Some other value; first free the old value...
10315 */
10316
10317 if (job->attrs->prev)
10318 job->attrs->prev->next = attr2->next;
10319 else
10320 job->attrs->attrs = attr2->next;
10321
10322 if (job->attrs->last == attr2)
10323 job->attrs->last = job->attrs->prev;
10324
10325 ippDeleteAttribute(NULL, attr2);
10326
10327 /*
10328 * Then copy the attribute...
10329 */
10330
10331 ippCopyAttribute(job->attrs, attr, 0);
10332
10333 /*
10334 * See if the job-name or job-hold-until is being changed.
10335 */
10336
10337 if (!strcmp(attr->name, "job-hold-until"))
10338 {
10339 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-hold-until to %s",
10340 attr->values[0].string.text);
10341 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
10342
10343 if (!strcmp(attr->values[0].string.text, "no-hold"))
10344 {
10345 cupsdReleaseJob(job);
10346 check_jobs = 1;
10347 }
10348 else
10349 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT,
10350 "Job held by \"%s\".", username);
10351
10352 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
10353 }
10354 }
10355 else if (attr->value_tag == IPP_TAG_DELETEATTR)
10356 {
10357 /*
10358 * Delete the attribute...
10359 */
10360
10361 if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10362 IPP_TAG_ZERO)) != NULL)
10363 {
10364 if (job->attrs->prev)
10365 job->attrs->prev->next = attr2->next;
10366 else
10367 job->attrs->attrs = attr2->next;
10368
10369 if (attr2 == job->attrs->last)
10370 job->attrs->last = job->attrs->prev;
10371
10372 ippDeleteAttribute(NULL, attr2);
10373
10374 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
10375 }
10376 }
10377 else
10378 {
10379 /*
10380 * Add new option by copying it...
10381 */
10382
10383 ippCopyAttribute(job->attrs, attr, 0);
10384
10385 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
10386 }
10387 }
10388
10389 /*
10390 * Save the job...
10391 */
10392
10393 job->dirty = 1;
10394 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
10395
10396 /*
10397 * Send events as needed...
10398 */
10399
10400 if (event & CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED)
10401 cupsdAddEvent(CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED,
10402 cupsdFindDest(job->dest), job,
10403 "Job priority changed by user.");
10404
10405 if (event & CUPSD_EVENT_JOB_STATE)
10406 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
10407 job->state_value == IPP_JOB_HELD ?
10408 "Job held by user." : "Job restarted by user.");
10409
10410 if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
10411 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
10412 "Job options changed by user.");
10413
10414 /*
10415 * Start jobs if possible...
10416 */
10417
10418 if (check_jobs)
10419 cupsdCheckJobs();
10420 }
10421
10422
10423 /*
10424 * 'set_printer_attrs()' - Set printer attributes.
10425 */
10426
10427 static void
10428 set_printer_attrs(cupsd_client_t *con, /* I - Client connection */
10429 ipp_attribute_t *uri) /* I - Printer */
10430 {
10431 http_status_t status; /* Policy status */
10432 cups_ptype_t dtype; /* Destination type (printer/class) */
10433 cupsd_printer_t *printer; /* Printer/class */
10434 ipp_attribute_t *attr; /* Printer attribute */
10435 int changed = 0; /* Was anything changed? */
10436
10437
10438 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_attrs(%p[%d], %s)", con,
10439 con->number, uri->values[0].string.text);
10440
10441 /*
10442 * Is the destination valid?
10443 */
10444
10445 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10446 {
10447 /*
10448 * Bad URI...
10449 */
10450
10451 send_ipp_status(con, IPP_NOT_FOUND,
10452 _("The printer or class does not exist."));
10453 return;
10454 }
10455
10456 /*
10457 * Check policy...
10458 */
10459
10460 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10461 {
10462 send_http_error(con, status, printer);
10463 return;
10464 }
10465
10466 /*
10467 * Return a list of attributes that can be set via Set-Printer-Attributes.
10468 */
10469
10470 if ((attr = ippFindAttribute(con->request, "printer-location",
10471 IPP_TAG_TEXT)) != NULL)
10472 {
10473 cupsdSetString(&printer->location, attr->values[0].string.text);
10474 changed = 1;
10475 }
10476
10477 if ((attr = ippFindAttribute(con->request, "printer-geo-location", IPP_TAG_URI)) != NULL && !strncmp(attr->values[0].string.text, "geo:", 4))
10478 {
10479 cupsdSetString(&printer->geo_location, attr->values[0].string.text);
10480 changed = 1;
10481 }
10482
10483 if ((attr = ippFindAttribute(con->request, "printer-organization", IPP_TAG_TEXT)) != NULL)
10484 {
10485 cupsdSetString(&printer->organization, attr->values[0].string.text);
10486 changed = 1;
10487 }
10488
10489 if ((attr = ippFindAttribute(con->request, "printer-organizational-unit", IPP_TAG_TEXT)) != NULL)
10490 {
10491 cupsdSetString(&printer->organizational_unit, attr->values[0].string.text);
10492 changed = 1;
10493 }
10494
10495 if ((attr = ippFindAttribute(con->request, "printer-info",
10496 IPP_TAG_TEXT)) != NULL)
10497 {
10498 cupsdSetString(&printer->info, attr->values[0].string.text);
10499 changed = 1;
10500 }
10501
10502 /*
10503 * Update the printer attributes and return...
10504 */
10505
10506 if (changed)
10507 {
10508 printer->config_time = time(NULL);
10509
10510 cupsdSetPrinterAttrs(printer);
10511 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
10512
10513 cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG, printer, NULL,
10514 "Printer \"%s\" description or location changed by \"%s\".",
10515 printer->name, get_username(con));
10516
10517 cupsdLogMessage(CUPSD_LOG_INFO,
10518 "Printer \"%s\" description or location changed by \"%s\".",
10519 printer->name, get_username(con));
10520 }
10521
10522 con->response->request.status.status_code = IPP_OK;
10523 }
10524
10525
10526 /*
10527 * 'set_printer_defaults()' - Set printer default options from a request.
10528 */
10529
10530 static void
10531 set_printer_defaults(
10532 cupsd_client_t *con, /* I - Client connection */
10533 cupsd_printer_t *printer) /* I - Printer */
10534 {
10535 int i; /* Looping var */
10536 ipp_attribute_t *attr; /* Current attribute */
10537 size_t namelen; /* Length of attribute name */
10538 char name[256], /* New attribute name */
10539 value[256]; /* String version of integer attrs */
10540
10541
10542 for (attr = con->request->attrs; attr; attr = attr->next)
10543 {
10544 /*
10545 * Skip non-printer attributes...
10546 */
10547
10548 if (attr->group_tag != IPP_TAG_PRINTER || !attr->name)
10549 continue;
10550
10551 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_defaults: %s", attr->name);
10552
10553 if (!strcmp(attr->name, "job-sheets-default"))
10554 {
10555 /*
10556 * Only allow keywords and names...
10557 */
10558
10559 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10560 continue;
10561
10562 /*
10563 * Only allow job-sheets-default to be set when running without a
10564 * system high classification level...
10565 */
10566
10567 if (Classification)
10568 continue;
10569
10570 cupsdSetString(&printer->job_sheets[0], attr->values[0].string.text);
10571
10572 if (attr->num_values > 1)
10573 cupsdSetString(&printer->job_sheets[1], attr->values[1].string.text);
10574 else
10575 cupsdSetString(&printer->job_sheets[1], "none");
10576 }
10577 else if (!strcmp(attr->name, "requesting-user-name-allowed"))
10578 {
10579 cupsdFreeStrings(&(printer->users));
10580
10581 printer->deny_users = 0;
10582
10583 if (attr->value_tag == IPP_TAG_NAME &&
10584 (attr->num_values > 1 ||
10585 strcmp(attr->values[0].string.text, "all")))
10586 {
10587 for (i = 0; i < attr->num_values; i ++)
10588 cupsdAddString(&(printer->users), attr->values[i].string.text);
10589 }
10590 }
10591 else if (!strcmp(attr->name, "requesting-user-name-denied"))
10592 {
10593 cupsdFreeStrings(&(printer->users));
10594
10595 printer->deny_users = 1;
10596
10597 if (attr->value_tag == IPP_TAG_NAME &&
10598 (attr->num_values > 1 ||
10599 strcmp(attr->values[0].string.text, "none")))
10600 {
10601 for (i = 0; i < attr->num_values; i ++)
10602 cupsdAddString(&(printer->users), attr->values[i].string.text);
10603 }
10604 }
10605 else if (!strcmp(attr->name, "job-quota-period"))
10606 {
10607 if (attr->value_tag != IPP_TAG_INTEGER)
10608 continue;
10609
10610 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-quota-period to %d...",
10611 attr->values[0].integer);
10612 cupsdFreeQuotas(printer);
10613
10614 printer->quota_period = attr->values[0].integer;
10615 }
10616 else if (!strcmp(attr->name, "job-k-limit"))
10617 {
10618 if (attr->value_tag != IPP_TAG_INTEGER)
10619 continue;
10620
10621 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-k-limit to %d...",
10622 attr->values[0].integer);
10623 cupsdFreeQuotas(printer);
10624
10625 printer->k_limit = attr->values[0].integer;
10626 }
10627 else if (!strcmp(attr->name, "job-page-limit"))
10628 {
10629 if (attr->value_tag != IPP_TAG_INTEGER)
10630 continue;
10631
10632 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-page-limit to %d...",
10633 attr->values[0].integer);
10634 cupsdFreeQuotas(printer);
10635
10636 printer->page_limit = attr->values[0].integer;
10637 }
10638 else if (!strcmp(attr->name, "printer-op-policy"))
10639 {
10640 cupsd_policy_t *p; /* Policy */
10641
10642
10643 if (attr->value_tag != IPP_TAG_NAME)
10644 continue;
10645
10646 if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
10647 {
10648 cupsdLogMessage(CUPSD_LOG_DEBUG,
10649 "Setting printer-op-policy to \"%s\"...",
10650 attr->values[0].string.text);
10651 cupsdSetString(&printer->op_policy, attr->values[0].string.text);
10652 printer->op_policy_ptr = p;
10653 }
10654 else
10655 {
10656 send_ipp_status(con, IPP_NOT_POSSIBLE,
10657 _("Unknown printer-op-policy \"%s\"."),
10658 attr->values[0].string.text);
10659 return;
10660 }
10661 }
10662 else if (!strcmp(attr->name, "printer-error-policy"))
10663 {
10664 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10665 continue;
10666
10667 if (strcmp(attr->values[0].string.text, "retry-current-job") &&
10668 ((printer->type & CUPS_PRINTER_CLASS) ||
10669 (strcmp(attr->values[0].string.text, "abort-job") &&
10670 strcmp(attr->values[0].string.text, "retry-job") &&
10671 strcmp(attr->values[0].string.text, "stop-printer"))))
10672 {
10673 send_ipp_status(con, IPP_NOT_POSSIBLE,
10674 _("Unknown printer-error-policy \"%s\"."),
10675 attr->values[0].string.text);
10676 return;
10677 }
10678
10679 cupsdLogMessage(CUPSD_LOG_DEBUG,
10680 "Setting printer-error-policy to \"%s\"...",
10681 attr->values[0].string.text);
10682 cupsdSetString(&printer->error_policy, attr->values[0].string.text);
10683 }
10684
10685 /*
10686 * Skip any other non-default attributes...
10687 */
10688
10689 namelen = strlen(attr->name);
10690 if (namelen < 9 || strcmp(attr->name + namelen - 8, "-default") ||
10691 namelen > (sizeof(name) - 1) || attr->num_values != 1)
10692 continue;
10693
10694 /*
10695 * OK, anything else must be a user-defined default...
10696 */
10697
10698 strlcpy(name, attr->name, sizeof(name));
10699 name[namelen - 8] = '\0'; /* Strip "-default" */
10700
10701 switch (attr->value_tag)
10702 {
10703 case IPP_TAG_DELETEATTR :
10704 printer->num_options = cupsRemoveOption(name,
10705 printer->num_options,
10706 &(printer->options));
10707 cupsdLogMessage(CUPSD_LOG_DEBUG,
10708 "Deleting %s", attr->name);
10709 break;
10710
10711 case IPP_TAG_NAME :
10712 case IPP_TAG_TEXT :
10713 case IPP_TAG_KEYWORD :
10714 case IPP_TAG_URI :
10715 printer->num_options = cupsAddOption(name,
10716 attr->values[0].string.text,
10717 printer->num_options,
10718 &(printer->options));
10719 cupsdLogMessage(CUPSD_LOG_DEBUG,
10720 "Setting %s to \"%s\"...", attr->name,
10721 attr->values[0].string.text);
10722 break;
10723
10724 case IPP_TAG_BOOLEAN :
10725 printer->num_options = cupsAddOption(name,
10726 attr->values[0].boolean ?
10727 "true" : "false",
10728 printer->num_options,
10729 &(printer->options));
10730 cupsdLogMessage(CUPSD_LOG_DEBUG,
10731 "Setting %s to %s...", attr->name,
10732 attr->values[0].boolean ? "true" : "false");
10733 break;
10734
10735 case IPP_TAG_INTEGER :
10736 case IPP_TAG_ENUM :
10737 sprintf(value, "%d", attr->values[0].integer);
10738 printer->num_options = cupsAddOption(name, value,
10739 printer->num_options,
10740 &(printer->options));
10741 cupsdLogMessage(CUPSD_LOG_DEBUG,
10742 "Setting %s to %s...", attr->name, value);
10743 break;
10744
10745 case IPP_TAG_RANGE :
10746 sprintf(value, "%d-%d", attr->values[0].range.lower,
10747 attr->values[0].range.upper);
10748 printer->num_options = cupsAddOption(name, value,
10749 printer->num_options,
10750 &(printer->options));
10751 cupsdLogMessage(CUPSD_LOG_DEBUG,
10752 "Setting %s to %s...", attr->name, value);
10753 break;
10754
10755 case IPP_TAG_RESOLUTION :
10756 sprintf(value, "%dx%d%s", attr->values[0].resolution.xres,
10757 attr->values[0].resolution.yres,
10758 attr->values[0].resolution.units == IPP_RES_PER_INCH ?
10759 "dpi" : "dpcm");
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 default :
10768 /* Do nothing for other values */
10769 break;
10770 }
10771 }
10772 }
10773
10774
10775 /*
10776 * 'start_printer()' - Start a printer.
10777 */
10778
10779 static void
10780 start_printer(cupsd_client_t *con, /* I - Client connection */
10781 ipp_attribute_t *uri) /* I - Printer URI */
10782 {
10783 int i; /* Temporary variable */
10784 http_status_t status; /* Policy status */
10785 cups_ptype_t dtype; /* Destination type (printer/class) */
10786 cupsd_printer_t *printer; /* Printer data */
10787
10788
10789 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", con,
10790 con->number, uri->values[0].string.text);
10791
10792 /*
10793 * Is the destination valid?
10794 */
10795
10796 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10797 {
10798 /*
10799 * Bad URI...
10800 */
10801
10802 send_ipp_status(con, IPP_NOT_FOUND,
10803 _("The printer or class does not exist."));
10804 return;
10805 }
10806
10807 /*
10808 * Check policy...
10809 */
10810
10811 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10812 {
10813 send_http_error(con, status, printer);
10814 return;
10815 }
10816
10817 /*
10818 * Start the printer...
10819 */
10820
10821 printer->state_message[0] = '\0';
10822
10823 cupsdStartPrinter(printer, 1);
10824
10825 if (dtype & CUPS_PRINTER_CLASS)
10826 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".",
10827 printer->name, get_username(con));
10828 else
10829 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".",
10830 printer->name, get_username(con));
10831
10832 cupsdCheckJobs();
10833
10834 /*
10835 * Check quotas...
10836 */
10837
10838 if ((i = check_quotas(con, printer)) < 0)
10839 {
10840 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
10841 return;
10842 }
10843 else if (i == 0)
10844 {
10845 send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Not allowed to print."));
10846 return;
10847 }
10848
10849 /*
10850 * Everything was ok, so return OK status...
10851 */
10852
10853 con->response->request.status.status_code = IPP_OK;
10854 }
10855
10856
10857 /*
10858 * 'stop_printer()' - Stop a printer.
10859 */
10860
10861 static void
10862 stop_printer(cupsd_client_t *con, /* I - Client connection */
10863 ipp_attribute_t *uri) /* I - Printer URI */
10864 {
10865 http_status_t status; /* Policy status */
10866 cups_ptype_t dtype; /* Destination type (printer/class) */
10867 cupsd_printer_t *printer; /* Printer data */
10868 ipp_attribute_t *attr; /* printer-state-message attribute */
10869
10870
10871 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", con,
10872 con->number, uri->values[0].string.text);
10873
10874 /*
10875 * Is the destination valid?
10876 */
10877
10878 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10879 {
10880 /*
10881 * Bad URI...
10882 */
10883
10884 send_ipp_status(con, IPP_NOT_FOUND,
10885 _("The printer or class does not exist."));
10886 return;
10887 }
10888
10889 /*
10890 * Check policy...
10891 */
10892
10893 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10894 {
10895 send_http_error(con, status, printer);
10896 return;
10897 }
10898
10899 /*
10900 * Stop the printer...
10901 */
10902
10903 if ((attr = ippFindAttribute(con->request, "printer-state-message",
10904 IPP_TAG_TEXT)) == NULL)
10905 strlcpy(printer->state_message, "Paused", sizeof(printer->state_message));
10906 else
10907 {
10908 strlcpy(printer->state_message, attr->values[0].string.text,
10909 sizeof(printer->state_message));
10910 }
10911
10912 cupsdStopPrinter(printer, 1);
10913
10914 if (dtype & CUPS_PRINTER_CLASS)
10915 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".",
10916 printer->name, get_username(con));
10917 else
10918 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".",
10919 printer->name, get_username(con));
10920
10921 /*
10922 * Everything was ok, so return OK status...
10923 */
10924
10925 con->response->request.status.status_code = IPP_OK;
10926 }
10927
10928
10929 /*
10930 * 'url_encode_attr()' - URL-encode a string attribute.
10931 */
10932
10933 static void
10934 url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */
10935 char *buffer,/* I - String buffer */
10936 size_t bufsize)/* I - Size of buffer */
10937 {
10938 int i; /* Looping var */
10939 char *bufptr, /* Pointer into buffer */
10940 *bufend; /* End of buffer */
10941
10942
10943 strlcpy(buffer, attr->name, bufsize);
10944 bufptr = buffer + strlen(buffer);
10945 bufend = buffer + bufsize - 1;
10946
10947 for (i = 0; i < attr->num_values; i ++)
10948 {
10949 if (bufptr >= bufend)
10950 break;
10951
10952 if (i)
10953 *bufptr++ = ',';
10954 else
10955 *bufptr++ = '=';
10956
10957 if (bufptr >= bufend)
10958 break;
10959
10960 *bufptr++ = '\'';
10961
10962 bufptr = url_encode_string(attr->values[i].string.text, bufptr, (size_t)(bufend - bufptr + 1));
10963
10964 if (bufptr >= bufend)
10965 break;
10966
10967 *bufptr++ = '\'';
10968 }
10969
10970 *bufptr = '\0';
10971 }
10972
10973
10974 /*
10975 * 'url_encode_string()' - URL-encode a string.
10976 */
10977
10978 static char * /* O - End of string */
10979 url_encode_string(const char *s, /* I - String */
10980 char *buffer, /* I - String buffer */
10981 size_t bufsize) /* I - Size of buffer */
10982 {
10983 char *bufptr, /* Pointer into buffer */
10984 *bufend; /* End of buffer */
10985 static const char *hex = "0123456789ABCDEF";
10986 /* Hex digits */
10987
10988
10989 bufptr = buffer;
10990 bufend = buffer + bufsize - 1;
10991
10992 while (*s && bufptr < bufend)
10993 {
10994 if (*s == ' ' || *s == '%' || *s == '+')
10995 {
10996 if (bufptr >= (bufend - 2))
10997 break;
10998
10999 *bufptr++ = '%';
11000 *bufptr++ = hex[(*s >> 4) & 15];
11001 *bufptr++ = hex[*s & 15];
11002
11003 s ++;
11004 }
11005 else if (*s == '\'' || *s == '\\')
11006 {
11007 if (bufptr >= (bufend - 1))
11008 break;
11009
11010 *bufptr++ = '\\';
11011 *bufptr++ = *s++;
11012 }
11013 else
11014 *bufptr++ = *s++;
11015 }
11016
11017 *bufptr = '\0';
11018
11019 return (bufptr);
11020 }
11021
11022
11023 /*
11024 * 'user_allowed()' - See if a user is allowed to print to a queue.
11025 */
11026
11027 static int /* O - 0 if not allowed, 1 if allowed */
11028 user_allowed(cupsd_printer_t *p, /* I - Printer or class */
11029 const char *username) /* I - Username */
11030 {
11031 struct passwd *pw; /* User password data */
11032 char baseuser[256], /* Base username */
11033 *baseptr, /* Pointer to "@" in base username */
11034 *name; /* Current user name */
11035
11036
11037 if (cupsArrayCount(p->users) == 0)
11038 return (1);
11039
11040 if (!strcmp(username, "root"))
11041 return (1);
11042
11043 if (strchr(username, '@'))
11044 {
11045 /*
11046 * Strip @REALM for username check...
11047 */
11048
11049 strlcpy(baseuser, username, sizeof(baseuser));
11050
11051 if ((baseptr = strchr(baseuser, '@')) != NULL)
11052 *baseptr = '\0';
11053
11054 username = baseuser;
11055 }
11056
11057 pw = getpwnam(username);
11058 endpwent();
11059
11060 for (name = (char *)cupsArrayFirst(p->users);
11061 name;
11062 name = (char *)cupsArrayNext(p->users))
11063 {
11064 if (name[0] == '@')
11065 {
11066 /*
11067 * Check group membership...
11068 */
11069
11070 if (cupsdCheckGroup(username, pw, name + 1))
11071 break;
11072 }
11073 else if (name[0] == '#')
11074 {
11075 /*
11076 * Check UUID...
11077 */
11078
11079 if (cupsdCheckGroup(username, pw, name))
11080 break;
11081 }
11082 else if (!_cups_strcasecmp(username, name))
11083 break;
11084 }
11085
11086 return ((name != NULL) != p->deny_users);
11087 }
11088
11089
11090 /*
11091 * 'validate_job()' - Validate printer options and destination.
11092 */
11093
11094 static void
11095 validate_job(cupsd_client_t *con, /* I - Client connection */
11096 ipp_attribute_t *uri) /* I - Printer URI */
11097 {
11098 http_status_t status; /* Policy status */
11099 ipp_attribute_t *attr; /* Current attribute */
11100 #ifdef HAVE_SSL
11101 ipp_attribute_t *auth_info; /* auth-info attribute */
11102 #endif /* HAVE_SSL */
11103 ipp_attribute_t *format, /* Document-format attribute */
11104 *name; /* Job-name attribute */
11105 cups_ptype_t dtype; /* Destination type (printer/class) */
11106 char super[MIME_MAX_SUPER],
11107 /* Supertype of file */
11108 type[MIME_MAX_TYPE];
11109 /* Subtype of file */
11110 cupsd_printer_t *printer; /* Printer */
11111
11112
11113 cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", con,
11114 con->number, uri->values[0].string.text);
11115
11116 /*
11117 * OK, see if the client is sending the document compressed - CUPS
11118 * doesn't support compression yet...
11119 */
11120
11121 if ((attr = ippFindAttribute(con->request, "compression",
11122 IPP_TAG_KEYWORD)) != NULL)
11123 {
11124 if (strcmp(attr->values[0].string.text, "none")
11125 #ifdef HAVE_LIBZ
11126 && strcmp(attr->values[0].string.text, "gzip")
11127 #endif /* HAVE_LIBZ */
11128 )
11129 {
11130 send_ipp_status(con, IPP_ATTRIBUTES,
11131 _("Unsupported 'compression' value \"%s\"."),
11132 attr->values[0].string.text);
11133 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
11134 "compression", NULL, attr->values[0].string.text);
11135 return;
11136 }
11137 }
11138
11139 /*
11140 * Is it a format we support?
11141 */
11142
11143 if ((format = ippFindAttribute(con->request, "document-format",
11144 IPP_TAG_MIMETYPE)) != NULL)
11145 {
11146 if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]",
11147 super, type) != 2)
11148 {
11149 send_ipp_status(con, IPP_BAD_REQUEST,
11150 _("Bad 'document-format' value \"%s\"."),
11151 format->values[0].string.text);
11152 return;
11153 }
11154
11155 if ((strcmp(super, "application") || strcmp(type, "octet-stream")) &&
11156 !mimeType(MimeDatabase, super, type))
11157 {
11158 cupsdLogMessage(CUPSD_LOG_INFO,
11159 "Hint: Do you have the raw file printing rules enabled?");
11160 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
11161 _("Unsupported 'document-format' value \"%s\"."),
11162 format->values[0].string.text);
11163 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
11164 "document-format", NULL, format->values[0].string.text);
11165 return;
11166 }
11167 }
11168
11169 /*
11170 * Is the job-name valid?
11171 */
11172
11173 if ((name = ippFindAttribute(con->request, "job-name", IPP_TAG_ZERO)) != NULL)
11174 {
11175 int bad_name = 0; /* Is the job-name value bad? */
11176
11177 if ((name->value_tag != IPP_TAG_NAME && name->value_tag != IPP_TAG_NAMELANG) ||
11178 name->num_values != 1)
11179 {
11180 bad_name = 1;
11181 }
11182 else
11183 {
11184 /*
11185 * Validate that job-name conforms to RFC 5198 (Network Unicode) and
11186 * IPP Everywhere requirements for "name" values...
11187 */
11188
11189 const unsigned char *nameptr; /* Pointer into "job-name" attribute */
11190
11191 for (nameptr = (unsigned char *)name->values[0].string.text;
11192 *nameptr;
11193 nameptr ++)
11194 {
11195 if (*nameptr < ' ' && *nameptr != '\t')
11196 break;
11197 else if (*nameptr == 0x7f)
11198 break;
11199 else if ((*nameptr & 0xe0) == 0xc0)
11200 {
11201 if ((nameptr[1] & 0xc0) != 0x80)
11202 break;
11203
11204 nameptr ++;
11205 }
11206 else if ((*nameptr & 0xf0) == 0xe0)
11207 {
11208 if ((nameptr[1] & 0xc0) != 0x80 ||
11209 (nameptr[2] & 0xc0) != 0x80)
11210 break;
11211
11212 nameptr += 2;
11213 }
11214 else if ((*nameptr & 0xf8) == 0xf0)
11215 {
11216 if ((nameptr[1] & 0xc0) != 0x80 ||
11217 (nameptr[2] & 0xc0) != 0x80 ||
11218 (nameptr[3] & 0xc0) != 0x80)
11219 break;
11220
11221 nameptr += 3;
11222 }
11223 else if (*nameptr & 0x80)
11224 break;
11225 }
11226
11227 if (*nameptr)
11228 bad_name = 1;
11229 }
11230
11231 if (bad_name)
11232 {
11233 if (StrictConformance)
11234 {
11235 send_ipp_status(con, IPP_ATTRIBUTES,
11236 _("Unsupported 'job-name' value."));
11237 ippCopyAttribute(con->response, name, 0);
11238 return;
11239 }
11240 else
11241 {
11242 cupsdLogMessage(CUPSD_LOG_WARN,
11243 "Unsupported 'job-name' value, deleting from request.");
11244 ippDeleteAttribute(con->request, name);
11245 }
11246 }
11247 }
11248
11249 /*
11250 * Is the destination valid?
11251 */
11252
11253 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
11254 {
11255 /*
11256 * Bad URI...
11257 */
11258
11259 send_ipp_status(con, IPP_NOT_FOUND,
11260 _("The printer or class does not exist."));
11261 return;
11262 }
11263
11264 /*
11265 * Check policy...
11266 */
11267
11268 #ifdef HAVE_SSL
11269 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
11270 #endif /* HAVE_SSL */
11271
11272 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11273 {
11274 send_http_error(con, status, printer);
11275 return;
11276 }
11277 else if (printer->num_auth_info_required == 1 &&
11278 !strcmp(printer->auth_info_required[0], "negotiate") &&
11279 !con->username[0])
11280 {
11281 send_http_error(con, HTTP_UNAUTHORIZED, printer);
11282 return;
11283 }
11284 #ifdef HAVE_SSL
11285 else if (auth_info && !con->http->tls &&
11286 !httpAddrLocalhost(con->http->hostaddr))
11287 {
11288 /*
11289 * Require encryption of auth-info over non-local connections...
11290 */
11291
11292 send_http_error(con, HTTP_UPGRADE_REQUIRED, printer);
11293 return;
11294 }
11295 #endif /* HAVE_SSL */
11296
11297 /*
11298 * Everything was ok, so return OK status...
11299 */
11300
11301 con->response->request.status.status_code = IPP_OK;
11302 }
11303
11304
11305 /*
11306 * 'validate_name()' - Make sure the printer name only contains valid chars.
11307 */
11308
11309 static int /* O - 0 if name is no good, 1 if good */
11310 validate_name(const char *name) /* I - Name to check */
11311 {
11312 const char *ptr; /* Pointer into name */
11313
11314
11315 /*
11316 * Scan the whole name...
11317 */
11318
11319 for (ptr = name; *ptr; ptr ++)
11320 if ((*ptr > 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
11321 return (0);
11322
11323 /*
11324 * All the characters are good; validate the length, too...
11325 */
11326
11327 return ((ptr - name) < 128);
11328 }
11329
11330
11331 /*
11332 * 'validate_user()' - Validate the user for the request.
11333 */
11334
11335 static int /* O - 1 if permitted, 0 otherwise */
11336 validate_user(cupsd_job_t *job, /* I - Job */
11337 cupsd_client_t *con, /* I - Client connection */
11338 const char *owner, /* I - Owner of job/resource */
11339 char *username, /* O - Authenticated username */
11340 size_t userlen) /* I - Length of username */
11341 {
11342 cupsd_printer_t *printer; /* Printer for job */
11343
11344
11345 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);
11346
11347 /*
11348 * Validate input...
11349 */
11350
11351 if (!con || !owner || !username || userlen <= 0)
11352 return (0);
11353
11354 /*
11355 * Get the best authenticated username that is available.
11356 */
11357
11358 strlcpy(username, get_username(con), userlen);
11359
11360 /*
11361 * Check the username against the owner...
11362 */
11363
11364 printer = cupsdFindDest(job->dest);
11365
11366 return (cupsdCheckPolicy(printer ? printer->op_policy_ptr : DefaultPolicyPtr,
11367 con, owner) == HTTP_OK);
11368 }
11369
11370
11371 /*
11372 * End of "$Id$".
11373 */