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