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