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