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