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