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