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