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