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