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