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