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