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