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