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