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