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