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