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