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