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