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