]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/ipp.c
Merge changes from CUPS 1.4svn-r7696.
[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 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1674 "job-k-octets", 0);
1675
1676 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
1677 IPP_TAG_KEYWORD)) == NULL)
1678 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
1679 if (!attr)
1680 {
1681 if ((val = cupsGetOption("job-hold-until", printer->num_options,
1682 printer->options)) == NULL)
1683 val = "no-hold";
1684
1685 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1686 "job-hold-until", NULL, val);
1687 }
1688 if (attr && strcmp(attr->values[0].string.text, "no-hold") &&
1689 !(printer->type & CUPS_PRINTER_REMOTE))
1690 {
1691 /*
1692 * Hold job until specified time...
1693 */
1694
1695 cupsdSetJobHoldUntil(job, attr->values[0].string.text);
1696
1697 job->state->values[0].integer = IPP_JOB_HELD;
1698 job->state_value = IPP_JOB_HELD;
1699 }
1700 else if (job->attrs->request.op.operation_id == IPP_CREATE_JOB)
1701 {
1702 job->hold_until = time(NULL) + 60;
1703 job->state->values[0].integer = IPP_JOB_HELD;
1704 job->state_value = IPP_JOB_HELD;
1705 }
1706 else
1707 {
1708 job->state->values[0].integer = IPP_JOB_PENDING;
1709 job->state_value = IPP_JOB_PENDING;
1710 }
1711
1712 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
1713 Classification)
1714 {
1715 /*
1716 * Add job sheets options...
1717 */
1718
1719 if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1720 IPP_TAG_ZERO)) == NULL)
1721 {
1722 cupsdLogMessage(CUPSD_LOG_DEBUG,
1723 "Adding default job-sheets values \"%s,%s\"...",
1724 printer->job_sheets[0], printer->job_sheets[1]);
1725
1726 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
1727 2, NULL, NULL);
1728 attr->values[0].string.text = _cupsStrAlloc(printer->job_sheets[0]);
1729 attr->values[1].string.text = _cupsStrAlloc(printer->job_sheets[1]);
1730 }
1731
1732 job->job_sheets = attr;
1733
1734 /*
1735 * Enforce classification level if set...
1736 */
1737
1738 if (Classification)
1739 {
1740 cupsdLogMessage(CUPSD_LOG_INFO,
1741 "Classification=\"%s\", ClassifyOverride=%d",
1742 Classification ? Classification : "(null)",
1743 ClassifyOverride);
1744
1745 if (ClassifyOverride)
1746 {
1747 if (!strcmp(attr->values[0].string.text, "none") &&
1748 (attr->num_values == 1 ||
1749 !strcmp(attr->values[1].string.text, "none")))
1750 {
1751 /*
1752 * Force the leading banner to have the classification on it...
1753 */
1754
1755 cupsdSetString(&attr->values[0].string.text, Classification);
1756
1757 cupsdLogJob(job, CUPSD_LOG_NOTICE, "CLASSIFICATION FORCED "
1758 "job-sheets=\"%s,none\", "
1759 "job-originating-user-name=\"%s\"",
1760 Classification, job->username);
1761 }
1762 else if (attr->num_values == 2 &&
1763 strcmp(attr->values[0].string.text,
1764 attr->values[1].string.text) &&
1765 strcmp(attr->values[0].string.text, "none") &&
1766 strcmp(attr->values[1].string.text, "none"))
1767 {
1768 /*
1769 * Can't put two different security markings on the same document!
1770 */
1771
1772 cupsdSetString(&attr->values[1].string.text, attr->values[0].string.text);
1773
1774 cupsdLogJob(job, CUPSD_LOG_NOTICE, "CLASSIFICATION FORCED "
1775 "job-sheets=\"%s,%s\", "
1776 "job-originating-user-name=\"%s\"",
1777 attr->values[0].string.text,
1778 attr->values[1].string.text, job->username);
1779 }
1780 else if (strcmp(attr->values[0].string.text, Classification) &&
1781 strcmp(attr->values[0].string.text, "none") &&
1782 (attr->num_values == 1 ||
1783 (strcmp(attr->values[1].string.text, Classification) &&
1784 strcmp(attr->values[1].string.text, "none"))))
1785 {
1786 if (attr->num_values == 1)
1787 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1788 "CLASSIFICATION OVERRIDDEN "
1789 "job-sheets=\"%s\", "
1790 "job-originating-user-name=\"%s\"",
1791 attr->values[0].string.text, job->username);
1792 else
1793 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1794 "CLASSIFICATION OVERRIDDEN "
1795 "job-sheets=\"%s,%s\",fffff "
1796 "job-originating-user-name=\"%s\"",
1797 attr->values[0].string.text,
1798 attr->values[1].string.text, job->username);
1799 }
1800 }
1801 else if (strcmp(attr->values[0].string.text, Classification) &&
1802 (attr->num_values == 1 ||
1803 strcmp(attr->values[1].string.text, Classification)))
1804 {
1805 /*
1806 * Force the banner to have the classification on it...
1807 */
1808
1809 if (attr->num_values > 1 &&
1810 !strcmp(attr->values[0].string.text, attr->values[1].string.text))
1811 {
1812 cupsdSetString(&(attr->values[0].string.text), Classification);
1813 cupsdSetString(&(attr->values[1].string.text), Classification);
1814 }
1815 else
1816 {
1817 if (attr->num_values == 1 ||
1818 strcmp(attr->values[0].string.text, "none"))
1819 cupsdSetString(&(attr->values[0].string.text), Classification);
1820
1821 if (attr->num_values > 1 &&
1822 strcmp(attr->values[1].string.text, "none"))
1823 cupsdSetString(&(attr->values[1].string.text), Classification);
1824 }
1825
1826 if (attr->num_values > 1)
1827 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1828 "CLASSIFICATION FORCED "
1829 "job-sheets=\"%s,%s\", "
1830 "job-originating-user-name=\"%s\"",
1831 attr->values[0].string.text,
1832 attr->values[1].string.text, job->username);
1833 else
1834 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1835 "CLASSIFICATION FORCED "
1836 "job-sheets=\"%s\", "
1837 "job-originating-user-name=\"%s\"",
1838 Classification, job->username);
1839 }
1840 }
1841
1842 /*
1843 * See if we need to add the starting sheet...
1844 */
1845
1846 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
1847 {
1848 cupsdLogJob(job, CUPSD_LOG_INFO, "Adding start banner page \"%s\".",
1849 attr->values[0].string.text);
1850
1851 if ((kbytes = copy_banner(con, job, attr->values[0].string.text)) < 0)
1852 {
1853 cupsdDeleteJob(job);
1854 return (NULL);
1855 }
1856
1857 cupsdUpdateQuota(printer, job->username, 0, kbytes);
1858 }
1859 }
1860 else if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1861 IPP_TAG_ZERO)) != NULL)
1862 job->sheets = attr;
1863
1864 /*
1865 * Fill in the response info...
1866 */
1867
1868 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
1869 LocalPort, job->id);
1870
1871 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
1872 job_uri);
1873
1874 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1875
1876 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
1877 job->state_value);
1878 add_job_state_reasons(con, job);
1879
1880 con->response->request.status.status_code = IPP_OK;
1881
1882 /*
1883 * Add any job subscriptions...
1884 */
1885
1886 add_job_subscriptions(con, job);
1887
1888 /*
1889 * Set all but the first two attributes to the job attributes group...
1890 */
1891
1892 for (attr = job->attrs->attrs->next->next; attr; attr = attr->next)
1893 attr->group_tag = IPP_TAG_JOB;
1894
1895 /*
1896 * Fire the "job created" event...
1897 */
1898
1899 cupsdAddEvent(CUPSD_EVENT_JOB_CREATED, printer, job, "Job created.");
1900
1901 /*
1902 * Return the new job...
1903 */
1904
1905 return (job);
1906 }
1907
1908
1909 /*
1910 * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
1911 * upon the job and printer state...
1912 */
1913
1914 static void
1915 add_job_state_reasons(
1916 cupsd_client_t *con, /* I - Client connection */
1917 cupsd_job_t *job) /* I - Job info */
1918 {
1919 cupsd_printer_t *dest; /* Destination printer */
1920
1921
1922 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job_state_reasons(%p[%d], %d)",
1923 con, con->http.fd, job ? job->id : 0);
1924
1925 switch (job ? job->state_value : IPP_JOB_CANCELED)
1926 {
1927 case IPP_JOB_PENDING :
1928 dest = cupsdFindDest(job->dest);
1929
1930 if (dest && dest->state == IPP_PRINTER_STOPPED)
1931 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1932 "job-state-reasons", NULL, "printer-stopped");
1933 else
1934 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1935 "job-state-reasons", NULL, "none");
1936 break;
1937
1938 case IPP_JOB_HELD :
1939 if (ippFindAttribute(job->attrs, "job-hold-until",
1940 IPP_TAG_KEYWORD) != NULL ||
1941 ippFindAttribute(job->attrs, "job-hold-until",
1942 IPP_TAG_NAME) != NULL)
1943 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1944 "job-state-reasons", NULL, "job-hold-until-specified");
1945 else
1946 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1947 "job-state-reasons", NULL, "job-incoming");
1948 break;
1949
1950 case IPP_JOB_PROCESSING :
1951 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1952 "job-state-reasons", NULL, "job-printing");
1953 break;
1954
1955 case IPP_JOB_STOPPED :
1956 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1957 "job-state-reasons", NULL, "job-stopped");
1958 break;
1959
1960 case IPP_JOB_CANCELED :
1961 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1962 "job-state-reasons", NULL, "job-canceled-by-user");
1963 break;
1964
1965 case IPP_JOB_ABORTED :
1966 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1967 "job-state-reasons", NULL, "aborted-by-system");
1968 break;
1969
1970 case IPP_JOB_COMPLETED :
1971 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1972 "job-state-reasons", NULL, "job-completed-successfully");
1973 break;
1974 }
1975 }
1976
1977
1978 /*
1979 * 'add_job_subscriptions()' - Add any subcriptions for a job.
1980 */
1981
1982 static void
1983 add_job_subscriptions(
1984 cupsd_client_t *con, /* I - Client connection */
1985 cupsd_job_t *job) /* I - Newly created job */
1986 {
1987 int i; /* Looping var */
1988 ipp_attribute_t *prev, /* Previous attribute */
1989 *next, /* Next attribute */
1990 *attr; /* Current attribute */
1991 cupsd_subscription_t *sub; /* Subscription object */
1992 const char *recipient, /* notify-recipient-uri */
1993 *pullmethod; /* notify-pull-method */
1994 ipp_attribute_t *user_data; /* notify-user-data */
1995 int interval; /* notify-time-interval */
1996 unsigned mask; /* notify-events */
1997
1998
1999 /*
2000 * Find the first subscription group attribute; return if we have
2001 * none...
2002 */
2003
2004 for (attr = job->attrs->attrs, prev = NULL;
2005 attr;
2006 prev = attr, attr = attr->next)
2007 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
2008 break;
2009
2010 if (!attr)
2011 return;
2012
2013 /*
2014 * Process the subscription attributes in the request...
2015 */
2016
2017 while (attr)
2018 {
2019 recipient = NULL;
2020 pullmethod = NULL;
2021 user_data = NULL;
2022 interval = 0;
2023 mask = CUPSD_EVENT_NONE;
2024
2025 while (attr && attr->group_tag != IPP_TAG_ZERO)
2026 {
2027 if (!strcmp(attr->name, "notify-recipient-uri") &&
2028 attr->value_tag == IPP_TAG_URI)
2029 {
2030 /*
2031 * Validate the recipient scheme against the ServerBin/notifier
2032 * directory...
2033 */
2034
2035 char notifier[1024], /* Notifier filename */
2036 scheme[HTTP_MAX_URI], /* Scheme portion of URI */
2037 userpass[HTTP_MAX_URI], /* Username portion of URI */
2038 host[HTTP_MAX_URI], /* Host portion of URI */
2039 resource[HTTP_MAX_URI]; /* Resource portion of URI */
2040 int port; /* Port portion of URI */
2041
2042
2043 recipient = attr->values[0].string.text;
2044
2045 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
2046 scheme, sizeof(scheme), userpass, sizeof(userpass),
2047 host, sizeof(host), &port,
2048 resource, sizeof(resource)) < HTTP_URI_OK)
2049 {
2050 send_ipp_status(con, IPP_NOT_POSSIBLE,
2051 _("Bad notify-recipient-uri URI \"%s\"!"), recipient);
2052 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2053 "notify-status-code", IPP_URI_SCHEME);
2054 return;
2055 }
2056
2057 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
2058 scheme);
2059 if (access(notifier, X_OK))
2060 {
2061 send_ipp_status(con, IPP_NOT_POSSIBLE,
2062 _("notify-recipient-uri URI \"%s\" uses unknown "
2063 "scheme!"), recipient);
2064 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2065 "notify-status-code", IPP_URI_SCHEME);
2066 return;
2067 }
2068
2069 if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
2070 {
2071 send_ipp_status(con, IPP_NOT_POSSIBLE,
2072 _("notify-recipient-uri URI \"%s\" is already used!"),
2073 recipient);
2074 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2075 "notify-status-code", IPP_ATTRIBUTES);
2076 return;
2077 }
2078 }
2079 else if (!strcmp(attr->name, "notify-pull-method") &&
2080 attr->value_tag == IPP_TAG_KEYWORD)
2081 {
2082 pullmethod = attr->values[0].string.text;
2083
2084 if (strcmp(pullmethod, "ippget"))
2085 {
2086 send_ipp_status(con, IPP_NOT_POSSIBLE,
2087 _("Bad notify-pull-method \"%s\"!"), pullmethod);
2088 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2089 "notify-status-code", IPP_ATTRIBUTES);
2090 return;
2091 }
2092 }
2093 else if (!strcmp(attr->name, "notify-charset") &&
2094 attr->value_tag == IPP_TAG_CHARSET &&
2095 strcmp(attr->values[0].string.text, "us-ascii") &&
2096 strcmp(attr->values[0].string.text, "utf-8"))
2097 {
2098 send_ipp_status(con, IPP_CHARSET,
2099 _("Character set \"%s\" not supported!"),
2100 attr->values[0].string.text);
2101 return;
2102 }
2103 else if (!strcmp(attr->name, "notify-natural-language") &&
2104 (attr->value_tag != IPP_TAG_LANGUAGE ||
2105 strcmp(attr->values[0].string.text, DefaultLanguage)))
2106 {
2107 send_ipp_status(con, IPP_CHARSET,
2108 _("Language \"%s\" not supported!"),
2109 attr->values[0].string.text);
2110 return;
2111 }
2112 else if (!strcmp(attr->name, "notify-user-data") &&
2113 attr->value_tag == IPP_TAG_STRING)
2114 {
2115 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
2116 {
2117 send_ipp_status(con, IPP_REQUEST_VALUE,
2118 _("The notify-user-data value is too large "
2119 "(%d > 63 octets)!"),
2120 attr->values[0].unknown.length);
2121 return;
2122 }
2123
2124 user_data = attr;
2125 }
2126 else if (!strcmp(attr->name, "notify-events") &&
2127 attr->value_tag == IPP_TAG_KEYWORD)
2128 {
2129 for (i = 0; i < attr->num_values; i ++)
2130 mask |= cupsdEventValue(attr->values[i].string.text);
2131 }
2132 else if (!strcmp(attr->name, "notify-lease-duration"))
2133 {
2134 send_ipp_status(con, IPP_BAD_REQUEST,
2135 _("The notify-lease-duration attribute cannot be "
2136 "used with job subscriptions."));
2137 return;
2138 }
2139 else if (!strcmp(attr->name, "notify-time-interval") &&
2140 attr->value_tag == IPP_TAG_INTEGER)
2141 interval = attr->values[0].integer;
2142
2143 attr = attr->next;
2144 }
2145
2146 if (!recipient && !pullmethod)
2147 break;
2148
2149 if (mask == CUPSD_EVENT_NONE)
2150 mask = CUPSD_EVENT_JOB_COMPLETED;
2151
2152 sub = cupsdAddSubscription(mask, cupsdFindDest(job->dest), job, recipient,
2153 0);
2154
2155 sub->interval = interval;
2156
2157 cupsdSetString(&sub->owner, job->username);
2158
2159 if (user_data)
2160 {
2161 sub->user_data_len = user_data->values[0].unknown.length;
2162 memcpy(sub->user_data, user_data->values[0].unknown.data,
2163 sub->user_data_len);
2164 }
2165
2166 ippAddSeparator(con->response);
2167 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
2168 "notify-subscription-id", sub->id);
2169
2170 if (attr)
2171 attr = attr->next;
2172 }
2173
2174 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
2175
2176 /*
2177 * Remove all of the subscription attributes from the job 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, option = printer->options;
3484 i > 0;
3485 i --, option ++)
3486 if (!ippFindAttribute(job->attrs, option->name, IPP_TAG_ZERO))
3487 {
3488 num_options = cupsAddOption(option->name, option->value, num_options,
3489 &options);
3490 }
3491
3492 /*
3493 * Encode these options as attributes in the job object...
3494 */
3495
3496 cupsEncodeOptions2(job->attrs, num_options, options, IPP_TAG_JOB);
3497 cupsFreeOptions(num_options, options);
3498 }
3499
3500
3501 /*
3502 * 'authenticate_job()' - Set job authentication info.
3503 */
3504
3505 static void
3506 authenticate_job(cupsd_client_t *con, /* I - Client connection */
3507 ipp_attribute_t *uri) /* I - Job URI */
3508 {
3509 ipp_attribute_t *attr, /* job-id attribute */
3510 *auth_info; /* auth-info attribute */
3511 int jobid; /* Job ID */
3512 cupsd_job_t *job; /* Current job */
3513 char method[HTTP_MAX_URI],
3514 /* Method portion of URI */
3515 username[HTTP_MAX_URI],
3516 /* Username portion of URI */
3517 host[HTTP_MAX_URI],
3518 /* Host portion of URI */
3519 resource[HTTP_MAX_URI];
3520 /* Resource portion of URI */
3521 int port; /* Port portion of URI */
3522
3523
3524 cupsdLogMessage(CUPSD_LOG_DEBUG2, "authenticate_job(%p[%d], %s)",
3525 con, con->http.fd, uri->values[0].string.text);
3526
3527 /*
3528 * Start with "everything is OK" status...
3529 */
3530
3531 con->response->request.status.status_code = IPP_OK;
3532
3533 /*
3534 * See if we have a job URI or a printer URI...
3535 */
3536
3537 if (!strcmp(uri->name, "printer-uri"))
3538 {
3539 /*
3540 * Got a printer URI; see if we also have a job-id attribute...
3541 */
3542
3543 if ((attr = ippFindAttribute(con->request, "job-id",
3544 IPP_TAG_INTEGER)) == NULL)
3545 {
3546 send_ipp_status(con, IPP_BAD_REQUEST,
3547 _("Got a printer-uri attribute but no job-id!"));
3548 return;
3549 }
3550
3551 jobid = attr->values[0].integer;
3552 }
3553 else
3554 {
3555 /*
3556 * Got a job URI; parse it to get the job ID...
3557 */
3558
3559 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
3560 sizeof(method), username, sizeof(username), host,
3561 sizeof(host), &port, resource, sizeof(resource));
3562
3563 if (strncmp(resource, "/jobs/", 6))
3564 {
3565 /*
3566 * Not a valid URI!
3567 */
3568
3569 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri attribute \"%s\"!"),
3570 uri->values[0].string.text);
3571 return;
3572 }
3573
3574 jobid = atoi(resource + 6);
3575 }
3576
3577 /*
3578 * See if the job exists...
3579 */
3580
3581 if ((job = cupsdFindJob(jobid)) == NULL)
3582 {
3583 /*
3584 * Nope - return a "not found" error...
3585 */
3586
3587 send_ipp_status(con, IPP_NOT_FOUND,
3588 _("Job #%d does not exist!"), jobid);
3589 return;
3590 }
3591
3592 /*
3593 * See if the job has been completed...
3594 */
3595
3596 if (job->state_value != IPP_JOB_HELD)
3597 {
3598 /*
3599 * Return a "not-possible" error...
3600 */
3601
3602 send_ipp_status(con, IPP_NOT_POSSIBLE,
3603 _("Job #%d is not held for authentication!"),
3604 jobid);
3605 return;
3606 }
3607
3608 /*
3609 * See if we have already authenticated...
3610 */
3611
3612 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
3613
3614 if (!con->username[0] && !auth_info)
3615 {
3616 cupsd_printer_t *printer; /* Job destination */
3617
3618
3619 /*
3620 * No auth data. If we need to authenticate via Kerberos, send a
3621 * HTTP auth challenge, otherwise just return an IPP error...
3622 */
3623
3624 printer = cupsdFindDest(job->dest);
3625
3626 if (printer && printer->num_auth_info_required > 0 &&
3627 !strcmp(printer->auth_info_required[0], "negotiate"))
3628 send_http_error(con, HTTP_UNAUTHORIZED, printer);
3629 else
3630 send_ipp_status(con, IPP_NOT_AUTHORIZED,
3631 _("No authentication information provided!"));
3632 return;
3633 }
3634
3635 /*
3636 * See if the job is owned by the requesting user...
3637 */
3638
3639 if (!validate_user(job, con, job->username, username, sizeof(username)))
3640 {
3641 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
3642 return;
3643 }
3644
3645 /*
3646 * Save the authentication information for this job...
3647 */
3648
3649 save_auth_info(con, job, auth_info);
3650
3651 /*
3652 * Reset the job-hold-until value to "no-hold"...
3653 */
3654
3655 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
3656 IPP_TAG_KEYWORD)) == NULL)
3657 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
3658
3659 if (attr)
3660 {
3661 attr->value_tag = IPP_TAG_KEYWORD;
3662 cupsdSetString(&(attr->values[0].string.text), "no-hold");
3663 }
3664
3665 /*
3666 * Release the job and return...
3667 */
3668
3669 cupsdReleaseJob(job);
3670
3671 cupsdLogJob(job, CUPSD_LOG_INFO, "Authenticated by \"%s\".", con->username);
3672 }
3673
3674
3675 /*
3676 * 'cancel_all_jobs()' - Cancel all print jobs.
3677 */
3678
3679 static void
3680 cancel_all_jobs(cupsd_client_t *con, /* I - Client connection */
3681 ipp_attribute_t *uri) /* I - Job or Printer URI */
3682 {
3683 http_status_t status; /* Policy status */
3684 cups_ptype_t dtype; /* Destination type */
3685 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
3686 userpass[HTTP_MAX_URI], /* Username portion of URI */
3687 hostname[HTTP_MAX_URI], /* Host portion of URI */
3688 resource[HTTP_MAX_URI]; /* Resource portion of URI */
3689 int port; /* Port portion of URI */
3690 ipp_attribute_t *attr; /* Attribute in request */
3691 const char *username; /* Username */
3692 int purge; /* Purge? */
3693 cupsd_printer_t *printer; /* Printer */
3694
3695
3696 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_all_jobs(%p[%d], %s)", con,
3697 con->http.fd, uri->values[0].string.text);
3698
3699 /*
3700 * See if we have a printer URI...
3701 */
3702
3703 if (strcmp(uri->name, "printer-uri"))
3704 {
3705 send_ipp_status(con, IPP_BAD_REQUEST,
3706 _("The printer-uri attribute is required!"));
3707 return;
3708 }
3709
3710 /*
3711 * Get the username (if any) for the jobs we want to cancel (only if
3712 * "my-jobs" is specified...
3713 */
3714
3715 if ((attr = ippFindAttribute(con->request, "my-jobs",
3716 IPP_TAG_BOOLEAN)) != NULL &&
3717 attr->values[0].boolean)
3718 {
3719 if ((attr = ippFindAttribute(con->request, "requesting-user-name",
3720 IPP_TAG_NAME)) != NULL)
3721 username = attr->values[0].string.text;
3722 else
3723 {
3724 send_ipp_status(con, IPP_BAD_REQUEST,
3725 _("Missing requesting-user-name attribute!"));
3726 return;
3727 }
3728 }
3729 else
3730 username = NULL;
3731
3732 /*
3733 * Look for the "purge-jobs" attribute...
3734 */
3735
3736 if ((attr = ippFindAttribute(con->request, "purge-jobs",
3737 IPP_TAG_BOOLEAN)) != NULL)
3738 purge = attr->values[0].boolean;
3739 else
3740 purge = 1;
3741
3742 /*
3743 * And if the destination is valid...
3744 */
3745
3746 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
3747 {
3748 /*
3749 * Bad URI?
3750 */
3751
3752 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text,
3753 scheme, sizeof(scheme), userpass, sizeof(userpass),
3754 hostname, sizeof(hostname), &port,
3755 resource, sizeof(resource));
3756
3757 if ((!strncmp(resource, "/printers/", 10) && resource[10]) ||
3758 (!strncmp(resource, "/classes/", 9) && resource[9]))
3759 {
3760 send_ipp_status(con, IPP_NOT_FOUND,
3761 _("The printer or class was not found."));
3762 return;
3763 }
3764
3765 /*
3766 * Check policy...
3767 */
3768
3769 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
3770 {
3771 send_http_error(con, status, NULL);
3772 return;
3773 }
3774
3775 /*
3776 * Cancel all jobs on all printers...
3777 */
3778
3779 cupsdCancelJobs(NULL, username, purge);
3780
3781 cupsdLogMessage(CUPSD_LOG_INFO, "All jobs were %s by \"%s\".",
3782 purge ? "purged" : "canceled", get_username(con));
3783 }
3784 else
3785 {
3786 /*
3787 * Check policy...
3788 */
3789
3790 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
3791 NULL)) != HTTP_OK)
3792 {
3793 send_http_error(con, status, printer);
3794 return;
3795 }
3796
3797 /*
3798 * Cancel all of the jobs on the named printer...
3799 */
3800
3801 cupsdCancelJobs(printer->name, username, purge);
3802
3803 cupsdLogMessage(CUPSD_LOG_INFO, "All jobs on \"%s\" were %s by \"%s\".",
3804 printer->name, purge ? "purged" : "canceled",
3805 get_username(con));
3806 }
3807
3808 con->response->request.status.status_code = IPP_OK;
3809 }
3810
3811
3812 /*
3813 * 'cancel_job()' - Cancel a print job.
3814 */
3815
3816 static void
3817 cancel_job(cupsd_client_t *con, /* I - Client connection */
3818 ipp_attribute_t *uri) /* I - Job or Printer URI */
3819 {
3820 ipp_attribute_t *attr; /* Current attribute */
3821 int jobid; /* Job ID */
3822 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
3823 username[HTTP_MAX_URI], /* Username portion of URI */
3824 host[HTTP_MAX_URI], /* Host portion of URI */
3825 resource[HTTP_MAX_URI]; /* Resource portion of URI */
3826 int port; /* Port portion of URI */
3827 cupsd_job_t *job; /* Job information */
3828 cups_ptype_t dtype; /* Destination type (printer/class) */
3829 cupsd_printer_t *printer; /* Printer data */
3830 int purge; /* Purge the job? */
3831
3832
3833 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_job(%p[%d], %s)", con,
3834 con->http.fd, uri->values[0].string.text);
3835
3836 /*
3837 * See if we have a job URI or a printer URI...
3838 */
3839
3840 if (!strcmp(uri->name, "printer-uri"))
3841 {
3842 /*
3843 * Got a printer URI; see if we also have a job-id attribute...
3844 */
3845
3846 if ((attr = ippFindAttribute(con->request, "job-id",
3847 IPP_TAG_INTEGER)) == NULL)
3848 {
3849 send_ipp_status(con, IPP_BAD_REQUEST,
3850 _("Got a printer-uri attribute but no job-id!"));
3851 return;
3852 }
3853
3854 if ((jobid = attr->values[0].integer) == 0)
3855 {
3856 /*
3857 * Find the current job on the specified printer...
3858 */
3859
3860 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
3861 {
3862 /*
3863 * Bad URI...
3864 */
3865
3866 send_ipp_status(con, IPP_NOT_FOUND,
3867 _("The printer or class was not found."));
3868 return;
3869 }
3870
3871 /*
3872 * See if the printer is currently printing a job...
3873 */
3874
3875 if (printer->job)
3876 jobid = ((cupsd_job_t *)printer->job)->id;
3877 else
3878 {
3879 /*
3880 * No, see if there are any pending jobs...
3881 */
3882
3883 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
3884 job;
3885 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
3886 if (job->state_value <= IPP_JOB_PROCESSING &&
3887 !strcasecmp(job->dest, printer->name))
3888 break;
3889
3890 if (job)
3891 jobid = job->id;
3892 else
3893 {
3894 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
3895 job;
3896 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
3897 if (job->state_value == IPP_JOB_STOPPED &&
3898 !strcasecmp(job->dest, printer->name))
3899 break;
3900
3901 if (job)
3902 jobid = job->id;
3903 else
3904 {
3905 send_ipp_status(con, IPP_NOT_POSSIBLE, _("No active jobs on %s!"),
3906 printer->name);
3907 return;
3908 }
3909 }
3910 }
3911 }
3912 }
3913 else
3914 {
3915 /*
3916 * Got a job URI; parse it to get the job ID...
3917 */
3918
3919 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
3920 sizeof(scheme), username, sizeof(username), host,
3921 sizeof(host), &port, resource, sizeof(resource));
3922
3923 if (strncmp(resource, "/jobs/", 6))
3924 {
3925 /*
3926 * Not a valid URI!
3927 */
3928
3929 send_ipp_status(con, IPP_BAD_REQUEST,
3930 _("Bad job-uri attribute \"%s\"!"),
3931 uri->values[0].string.text);
3932 return;
3933 }
3934
3935 jobid = atoi(resource + 6);
3936 }
3937
3938 /*
3939 * Look for the "purge-job" attribute...
3940 */
3941
3942 if ((attr = ippFindAttribute(con->request, "purge-job",
3943 IPP_TAG_BOOLEAN)) != NULL)
3944 purge = attr->values[0].boolean;
3945 else
3946 purge = 0;
3947
3948 /*
3949 * See if the job exists...
3950 */
3951
3952 if ((job = cupsdFindJob(jobid)) == NULL)
3953 {
3954 /*
3955 * Nope - return a "not found" error...
3956 */
3957
3958 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
3959 return;
3960 }
3961
3962 /*
3963 * See if the job is owned by the requesting user...
3964 */
3965
3966 if (!validate_user(job, con, job->username, username, sizeof(username)))
3967 {
3968 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
3969 return;
3970 }
3971
3972 /*
3973 * See if the job is already completed, canceled, or aborted; if so,
3974 * we can't cancel...
3975 */
3976
3977 if (job->state_value >= IPP_JOB_CANCELED && !purge)
3978 {
3979 switch (job->state_value)
3980 {
3981 case IPP_JOB_CANCELED :
3982 send_ipp_status(con, IPP_NOT_POSSIBLE,
3983 _("Job #%d is already canceled - can\'t cancel."),
3984 jobid);
3985 break;
3986
3987 case IPP_JOB_ABORTED :
3988 send_ipp_status(con, IPP_NOT_POSSIBLE,
3989 _("Job #%d is already aborted - can\'t cancel."),
3990 jobid);
3991 break;
3992
3993 default :
3994 send_ipp_status(con, IPP_NOT_POSSIBLE,
3995 _("Job #%d is already completed - can\'t cancel."),
3996 jobid);
3997 break;
3998 }
3999
4000 return;
4001 }
4002
4003 /*
4004 * Cancel the job and return...
4005 */
4006
4007 cupsdCancelJob(job, purge, IPP_JOB_CANCELED);
4008 cupsdCheckJobs();
4009
4010 if (purge)
4011 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Purged by \"%s\".", jobid,
4012 username);
4013 else
4014 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Canceled by \"%s\".", jobid,
4015 username);
4016
4017 con->response->request.status.status_code = IPP_OK;
4018 }
4019
4020
4021 /*
4022 * 'cancel_subscription()' - Cancel a subscription.
4023 */
4024
4025 static void
4026 cancel_subscription(
4027 cupsd_client_t *con, /* I - Client connection */
4028 int sub_id) /* I - Subscription ID */
4029 {
4030 http_status_t status; /* Policy status */
4031 cupsd_subscription_t *sub; /* Subscription */
4032
4033
4034 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4035 "cancel_subscription(con=%p[%d], sub_id=%d)",
4036 con, con->http.fd, sub_id);
4037
4038 /*
4039 * Is the subscription ID valid?
4040 */
4041
4042 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
4043 {
4044 /*
4045 * Bad subscription ID...
4046 */
4047
4048 send_ipp_status(con, IPP_NOT_FOUND,
4049 _("notify-subscription-id %d no good!"), sub_id);
4050 return;
4051 }
4052
4053 /*
4054 * Check policy...
4055 */
4056
4057 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
4058 DefaultPolicyPtr,
4059 con, sub->owner)) != HTTP_OK)
4060 {
4061 send_http_error(con, status, sub->dest);
4062 return;
4063 }
4064
4065 /*
4066 * Cancel the subscription...
4067 */
4068
4069 cupsdDeleteSubscription(sub, 1);
4070
4071 con->response->request.status.status_code = IPP_OK;
4072 }
4073
4074
4075 /*
4076 * 'check_rss_recipient()' - Check that we do not have a duplicate RSS feed URI.
4077 */
4078
4079 static int /* O - 1 if OK, 0 if not */
4080 check_rss_recipient(
4081 const char *recipient) /* I - Recipient URI */
4082 {
4083 cupsd_subscription_t *sub; /* Current subscription */
4084
4085
4086 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
4087 sub;
4088 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
4089 if (sub->recipient)
4090 {
4091 /*
4092 * Compare the URIs up to the first ?...
4093 */
4094
4095 const char *r1, *r2;
4096
4097 for (r1 = recipient, r2 = sub->recipient;
4098 *r1 == *r2 && *r1 && *r1 != '?' && *r2 && *r2 != '?';
4099 r1 ++, r2 ++);
4100
4101 if (*r1 == *r2)
4102 return (0);
4103 }
4104
4105 return (1);
4106 }
4107
4108
4109 /*
4110 * 'check_quotas()' - Check quotas for a printer and user.
4111 */
4112
4113 static int /* O - 1 if OK, 0 if not */
4114 check_quotas(cupsd_client_t *con, /* I - Client connection */
4115 cupsd_printer_t *p) /* I - Printer or class */
4116 {
4117 int i; /* Looping var */
4118 char username[33]; /* Username */
4119 cupsd_quota_t *q; /* Quota data */
4120 #ifdef HAVE_MBR_UID_TO_UUID
4121 /*
4122 * Use Apple membership APIs which require that all names represent
4123 * valid user account or group records accessible by the server.
4124 */
4125
4126 uuid_t usr_uuid; /* UUID for job requesting user */
4127 uuid_t usr2_uuid; /* UUID for ACL user name entry */
4128 uuid_t grp_uuid; /* UUID for ACL group name entry */
4129 int mbr_err; /* Error from membership function */
4130 int is_member; /* Is this user a member? */
4131 #else
4132 /*
4133 * Use standard POSIX APIs for checking users and groups...
4134 */
4135
4136 struct passwd *pw; /* User password data */
4137 #endif /* HAVE_MBR_UID_TO_UUID */
4138
4139
4140 cupsdLogMessage(CUPSD_LOG_DEBUG2, "check_quotas(%p[%d], %p[%s])",
4141 con, con->http.fd, p, p->name);
4142
4143 /*
4144 * Figure out who is printing...
4145 */
4146
4147 strlcpy(username, get_username(con), sizeof(username));
4148
4149 /*
4150 * Check global active job limits for printers and users...
4151 */
4152
4153 if (MaxJobsPerPrinter)
4154 {
4155 /*
4156 * Check if there are too many pending jobs on this printer...
4157 */
4158
4159 if (cupsdGetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
4160 {
4161 cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for printer \"%s\"...",
4162 p->name);
4163 return (0);
4164 }
4165 }
4166
4167 if (MaxJobsPerUser)
4168 {
4169 /*
4170 * Check if there are too many pending jobs for this user...
4171 */
4172
4173 if (cupsdGetUserJobCount(username) >= MaxJobsPerUser)
4174 {
4175 cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for user \"%s\"...",
4176 username);
4177 return (0);
4178 }
4179 }
4180
4181 /*
4182 * Check against users...
4183 */
4184
4185 if (p->num_users == 0 && p->k_limit == 0 && p->page_limit == 0)
4186 return (1);
4187
4188 if (p->num_users)
4189 {
4190 #ifdef HAVE_MBR_UID_TO_UUID
4191 /*
4192 * Get UUID for job requesting user...
4193 */
4194
4195 if (mbr_user_name_to_uuid((char *)username, usr_uuid))
4196 {
4197 /*
4198 * Unknown user...
4199 */
4200
4201 cupsdLogMessage(CUPSD_LOG_DEBUG,
4202 "check_quotas: UUID lookup failed for user \"%s\"",
4203 username);
4204 cupsdLogMessage(CUPSD_LOG_INFO,
4205 "Denying user \"%s\" access to printer \"%s\" "
4206 "(unknown user)...",
4207 username, p->name);
4208 return (0);
4209 }
4210 #else
4211 /*
4212 * Get UID and GID of requesting user...
4213 */
4214
4215 pw = getpwnam(username);
4216 endpwent();
4217 #endif /* HAVE_MBR_UID_TO_UUID */
4218
4219 for (i = 0; i < p->num_users; i ++)
4220 if (p->users[i][0] == '@')
4221 {
4222 /*
4223 * Check group membership...
4224 */
4225
4226 #ifdef HAVE_MBR_UID_TO_UUID
4227 if (p->users[i][1] == '#')
4228 {
4229 if (uuid_parse((char *)p->users[i] + 2, grp_uuid))
4230 uuid_clear(grp_uuid);
4231 }
4232 else if ((mbr_err = mbr_group_name_to_uuid((char *)p->users[i] + 1,
4233 grp_uuid)) != 0)
4234 {
4235 /*
4236 * Invalid ACL entries are ignored for matching; just record a
4237 * warning in the log...
4238 */
4239
4240 cupsdLogMessage(CUPSD_LOG_DEBUG,
4241 "check_quotas: UUID lookup failed for ACL entry "
4242 "\"%s\" (err=%d)", p->users[i], mbr_err);
4243 cupsdLogMessage(CUPSD_LOG_WARN,
4244 "Access control entry \"%s\" not a valid group name; "
4245 "entry ignored", p->users[i]);
4246 }
4247
4248 if ((mbr_err = mbr_check_membership(usr_uuid, grp_uuid,
4249 &is_member)) != 0)
4250 {
4251 /*
4252 * At this point, there should be no errors, but check anyways...
4253 */
4254
4255 cupsdLogMessage(CUPSD_LOG_DEBUG,
4256 "check_quotas: group \"%s\" membership check "
4257 "failed (err=%d)", p->users[i] + 1, mbr_err);
4258 is_member = 0;
4259 }
4260
4261 /*
4262 * Stop if we found a match...
4263 */
4264
4265 if (is_member)
4266 break;
4267
4268 #else
4269 if (cupsdCheckGroup(username, pw, p->users[i] + 1))
4270 break;
4271 #endif /* HAVE_MBR_UID_TO_UUID */
4272 }
4273 #ifdef HAVE_MBR_UID_TO_UUID
4274 else
4275 {
4276 if (p->users[i][0] == '#')
4277 {
4278 if (uuid_parse((char *)p->users[i] + 1, usr2_uuid))
4279 uuid_clear(usr2_uuid);
4280 }
4281 else if ((mbr_err = mbr_user_name_to_uuid((char *)p->users[i],
4282 usr2_uuid)) != 0)
4283 {
4284 /*
4285 * Invalid ACL entries are ignored for matching; just record a
4286 * warning in the log...
4287 */
4288
4289 cupsdLogMessage(CUPSD_LOG_DEBUG,
4290 "check_quotas: UUID lookup failed for ACL entry "
4291 "\"%s\" (err=%d)", p->users[i], mbr_err);
4292 cupsdLogMessage(CUPSD_LOG_WARN,
4293 "Access control entry \"%s\" not a valid user name; "
4294 "entry ignored", p->users[i]);
4295 }
4296
4297 if ((mbr_err = mbr_check_membership(usr_uuid, usr2_uuid,
4298 &is_member)) != 0)
4299 {
4300 cupsdLogMessage(CUPSD_LOG_DEBUG,
4301 "check_quotas: User \"%s\" identity check failed "
4302 "(err=%d)", p->users[i], mbr_err);
4303 is_member = 0;
4304 }
4305
4306 if (is_member)
4307 break;
4308 }
4309 #else
4310 else if (!strcasecmp(username, p->users[i]))
4311 break;
4312 #endif /* HAVE_MBR_UID_TO_UUID */
4313
4314 if ((i < p->num_users) == p->deny_users)
4315 {
4316 cupsdLogMessage(CUPSD_LOG_INFO,
4317 "Denying user \"%s\" access to printer \"%s\"...",
4318 username, p->name);
4319 return (0);
4320 }
4321 }
4322
4323 /*
4324 * Check quotas...
4325 */
4326
4327 #ifdef __APPLE__
4328 if (AppleQuotas && (q = cupsdFindQuota(p, username)) != NULL)
4329 {
4330 /*
4331 * TODO: Define these special page count values as constants!
4332 */
4333
4334 if (q->page_count == -4) /* special case: unlimited user */
4335 {
4336 cupsdLogMessage(CUPSD_LOG_INFO,
4337 "User \"%s\" request approved for printer %s (%s): "
4338 "unlimited quota.",
4339 username, p->name, p->info);
4340 q->page_count = 0; /* allow user to print */
4341 return (1);
4342 }
4343 else if (q->page_count == -3) /* quota exceeded */
4344 {
4345 cupsdLogMessage(CUPSD_LOG_INFO,
4346 "User \"%s\" request denied for printer %s (%s): "
4347 "quota limit exceeded.",
4348 username, p->name, p->info);
4349 q->page_count = 2; /* force quota exceeded failure */
4350 return (-1);
4351 }
4352 else if (q->page_count == -2) /* quota disabled for user */
4353 {
4354 cupsdLogMessage(CUPSD_LOG_INFO,
4355 "User \"%s\" request denied for printer %s (%s): "
4356 "printing disabled for user.",
4357 username, p->name, p->info);
4358 q->page_count = 2; /* force quota exceeded failure */
4359 return (-1);
4360 }
4361 else if (q->page_count == -1) /* quota access error */
4362 {
4363 cupsdLogMessage(CUPSD_LOG_INFO,
4364 "User \"%s\" request denied for printer %s (%s): "
4365 "unable to determine quota limit.",
4366 username, p->name, p->info);
4367 q->page_count = 2; /* force quota exceeded failure */
4368 return (-1);
4369 }
4370 else if (q->page_count < 0) /* user not found or other error */
4371 {
4372 cupsdLogMessage(CUPSD_LOG_INFO,
4373 "User \"%s\" request denied for printer %s (%s): "
4374 "user disabled / missing quota.",
4375 username, p->name, p->info);
4376 q->page_count = 2; /* force quota exceeded failure */
4377 return (-1);
4378 }
4379 else /* page within user limits */
4380 {
4381 q->page_count = 0; /* allow user to print */
4382 return (1);
4383 }
4384 }
4385 else
4386 #endif /* __APPLE__ */
4387 if (p->k_limit || p->page_limit)
4388 {
4389 if ((q = cupsdUpdateQuota(p, username, 0, 0)) == NULL)
4390 {
4391 cupsdLogMessage(CUPSD_LOG_ERROR,
4392 "Unable to allocate quota data for user \"%s\"!",
4393 username);
4394 return (-1);
4395 }
4396
4397 if ((q->k_count >= p->k_limit && p->k_limit) ||
4398 (q->page_count >= p->page_limit && p->page_limit))
4399 {
4400 cupsdLogMessage(CUPSD_LOG_INFO, "User \"%s\" is over the quota limit...",
4401 username);
4402 return (-1);
4403 }
4404 }
4405
4406 /*
4407 * If we have gotten this far, we're done!
4408 */
4409
4410 return (1);
4411 }
4412
4413
4414 /*
4415 * 'copy_attribute()' - Copy a single attribute.
4416 */
4417
4418 static ipp_attribute_t * /* O - New attribute */
4419 copy_attribute(
4420 ipp_t *to, /* O - Destination request/response */
4421 ipp_attribute_t *attr, /* I - Attribute to copy */
4422 int quickcopy) /* I - Do a quick copy? */
4423 {
4424 int i; /* Looping var */
4425 ipp_attribute_t *toattr; /* Destination attribute */
4426
4427
4428 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4429 "copy_attribute(%p, %p[%s,%x,%x])", to, attr,
4430 attr->name ? attr->name : "(null)", attr->group_tag,
4431 attr->value_tag);
4432
4433 switch (attr->value_tag & ~IPP_TAG_COPY)
4434 {
4435 case IPP_TAG_ZERO :
4436 toattr = ippAddSeparator(to);
4437 break;
4438
4439 case IPP_TAG_INTEGER :
4440 case IPP_TAG_ENUM :
4441 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
4442 attr->name, attr->num_values, NULL);
4443
4444 for (i = 0; i < attr->num_values; i ++)
4445 toattr->values[i].integer = attr->values[i].integer;
4446 break;
4447
4448 case IPP_TAG_BOOLEAN :
4449 toattr = ippAddBooleans(to, attr->group_tag, attr->name,
4450 attr->num_values, NULL);
4451
4452 for (i = 0; i < attr->num_values; i ++)
4453 toattr->values[i].boolean = attr->values[i].boolean;
4454 break;
4455
4456 case IPP_TAG_STRING :
4457 case IPP_TAG_TEXT :
4458 case IPP_TAG_NAME :
4459 case IPP_TAG_KEYWORD :
4460 case IPP_TAG_URI :
4461 case IPP_TAG_URISCHEME :
4462 case IPP_TAG_CHARSET :
4463 case IPP_TAG_LANGUAGE :
4464 case IPP_TAG_MIMETYPE :
4465 toattr = ippAddStrings(to, attr->group_tag,
4466 (ipp_tag_t)(attr->value_tag | quickcopy),
4467 attr->name, attr->num_values, NULL, NULL);
4468
4469 if (quickcopy)
4470 {
4471 for (i = 0; i < attr->num_values; i ++)
4472 toattr->values[i].string.text = attr->values[i].string.text;
4473 }
4474 else
4475 {
4476 for (i = 0; i < attr->num_values; i ++)
4477 toattr->values[i].string.text =
4478 _cupsStrAlloc(attr->values[i].string.text);
4479 }
4480 break;
4481
4482 case IPP_TAG_DATE :
4483 toattr = ippAddDate(to, attr->group_tag, attr->name,
4484 attr->values[0].date);
4485 break;
4486
4487 case IPP_TAG_RESOLUTION :
4488 toattr = ippAddResolutions(to, attr->group_tag, attr->name,
4489 attr->num_values, IPP_RES_PER_INCH,
4490 NULL, NULL);
4491
4492 for (i = 0; i < attr->num_values; i ++)
4493 {
4494 toattr->values[i].resolution.xres = attr->values[i].resolution.xres;
4495 toattr->values[i].resolution.yres = attr->values[i].resolution.yres;
4496 toattr->values[i].resolution.units = attr->values[i].resolution.units;
4497 }
4498 break;
4499
4500 case IPP_TAG_RANGE :
4501 toattr = ippAddRanges(to, attr->group_tag, attr->name,
4502 attr->num_values, NULL, NULL);
4503
4504 for (i = 0; i < attr->num_values; i ++)
4505 {
4506 toattr->values[i].range.lower = attr->values[i].range.lower;
4507 toattr->values[i].range.upper = attr->values[i].range.upper;
4508 }
4509 break;
4510
4511 case IPP_TAG_TEXTLANG :
4512 case IPP_TAG_NAMELANG :
4513 toattr = ippAddStrings(to, attr->group_tag,
4514 (ipp_tag_t)(attr->value_tag | quickcopy),
4515 attr->name, attr->num_values, NULL, NULL);
4516
4517 if (quickcopy)
4518 {
4519 for (i = 0; i < attr->num_values; i ++)
4520 {
4521 toattr->values[i].string.charset = attr->values[i].string.charset;
4522 toattr->values[i].string.text = attr->values[i].string.text;
4523 }
4524 }
4525 else
4526 {
4527 for (i = 0; i < attr->num_values; i ++)
4528 {
4529 if (!i)
4530 toattr->values[i].string.charset =
4531 _cupsStrAlloc(attr->values[i].string.charset);
4532 else
4533 toattr->values[i].string.charset =
4534 toattr->values[0].string.charset;
4535
4536 toattr->values[i].string.text =
4537 _cupsStrAlloc(attr->values[i].string.text);
4538 }
4539 }
4540 break;
4541
4542 case IPP_TAG_BEGIN_COLLECTION :
4543 toattr = ippAddCollections(to, attr->group_tag, attr->name,
4544 attr->num_values, NULL);
4545
4546 for (i = 0; i < attr->num_values; i ++)
4547 {
4548 toattr->values[i].collection = ippNew();
4549 copy_attrs(toattr->values[i].collection, attr->values[i].collection,
4550 NULL, IPP_TAG_ZERO, 0);
4551 }
4552 break;
4553
4554 default :
4555 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
4556 attr->name, attr->num_values, NULL);
4557
4558 for (i = 0; i < attr->num_values; i ++)
4559 {
4560 toattr->values[i].unknown.length = attr->values[i].unknown.length;
4561
4562 if (toattr->values[i].unknown.length > 0)
4563 {
4564 if ((toattr->values[i].unknown.data =
4565 malloc(toattr->values[i].unknown.length)) == NULL)
4566 toattr->values[i].unknown.length = 0;
4567 else
4568 memcpy(toattr->values[i].unknown.data,
4569 attr->values[i].unknown.data,
4570 toattr->values[i].unknown.length);
4571 }
4572 }
4573 break; /* anti-compiler-warning-code */
4574 }
4575
4576 return (toattr);
4577 }
4578
4579
4580 /*
4581 * 'copy_attrs()' - Copy attributes from one request to another.
4582 */
4583
4584 static void
4585 copy_attrs(ipp_t *to, /* I - Destination request */
4586 ipp_t *from, /* I - Source request */
4587 cups_array_t *ra, /* I - Requested attributes */
4588 ipp_tag_t group, /* I - Group to copy */
4589 int quickcopy) /* I - Do a quick copy? */
4590 {
4591 ipp_attribute_t *fromattr; /* Source attribute */
4592
4593
4594 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4595 "copy_attrs(to=%p, from=%p, ra=%p, group=%x, quickcopy=%d)",
4596 to, from, ra, group, quickcopy);
4597
4598 if (!to || !from)
4599 return;
4600
4601 for (fromattr = from->attrs; fromattr; fromattr = fromattr->next)
4602 {
4603 /*
4604 * Filter attributes as needed...
4605 */
4606
4607 if ((group != IPP_TAG_ZERO && fromattr->group_tag != group &&
4608 fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
4609 continue;
4610
4611 if (!ra || cupsArrayFind(ra, fromattr->name))
4612 copy_attribute(to, fromattr, quickcopy);
4613 }
4614 }
4615
4616
4617 /*
4618 * 'copy_banner()' - Copy a banner file to the requests directory for the
4619 * specified job.
4620 */
4621
4622 static int /* O - Size of banner file in kbytes */
4623 copy_banner(cupsd_client_t *con, /* I - Client connection */
4624 cupsd_job_t *job, /* I - Job information */
4625 const char *name) /* I - Name of banner */
4626 {
4627 int i; /* Looping var */
4628 int kbytes; /* Size of banner file in kbytes */
4629 char filename[1024]; /* Job filename */
4630 cupsd_banner_t *banner; /* Pointer to banner */
4631 cups_file_t *in; /* Input file */
4632 cups_file_t *out; /* Output file */
4633 int ch; /* Character from file */
4634 char attrname[255], /* Name of attribute */
4635 *s; /* Pointer into name */
4636 ipp_attribute_t *attr; /* Attribute */
4637
4638
4639 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4640 "copy_banner(con=%p[%d], job=%p[%d], name=\"%s\")",
4641 con, con ? con->http.fd : -1, job, job->id,
4642 name ? name : "(null)");
4643
4644 /*
4645 * Find the banner; return if not found or "none"...
4646 */
4647
4648 if (!name || !strcmp(name, "none") ||
4649 (banner = cupsdFindBanner(name)) == NULL)
4650 return (0);
4651
4652 /*
4653 * Open the banner and job files...
4654 */
4655
4656 if (add_file(con, job, banner->filetype, 0))
4657 return (-1);
4658
4659 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
4660 job->num_files);
4661 if ((out = cupsFileOpen(filename, "w")) == NULL)
4662 {
4663 cupsdLogMessage(CUPSD_LOG_ERROR,
4664 "Unable to create banner job file %s - %s",
4665 filename, strerror(errno));
4666 job->num_files --;
4667 return (0);
4668 }
4669
4670 fchmod(cupsFileNumber(out), 0640);
4671 fchown(cupsFileNumber(out), RunUser, Group);
4672
4673 /*
4674 * Try the localized banner file under the subdirectory...
4675 */
4676
4677 strlcpy(attrname, job->attrs->attrs->next->values[0].string.text,
4678 sizeof(attrname));
4679 if (strlen(attrname) > 2 && attrname[2] == '-')
4680 {
4681 /*
4682 * Convert ll-cc to ll_CC...
4683 */
4684
4685 attrname[2] = '_';
4686 attrname[3] = toupper(attrname[3] & 255);
4687 attrname[4] = toupper(attrname[4] & 255);
4688 }
4689
4690 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
4691 attrname, name);
4692
4693 if (access(filename, 0) && strlen(attrname) > 2)
4694 {
4695 /*
4696 * Wasn't able to find "ll_CC" locale file; try the non-national
4697 * localization banner directory.
4698 */
4699
4700 attrname[2] = '\0';
4701
4702 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
4703 attrname, name);
4704 }
4705
4706 if (access(filename, 0))
4707 {
4708 /*
4709 * Use the non-localized banner file.
4710 */
4711
4712 snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
4713 }
4714
4715 if ((in = cupsFileOpen(filename, "r")) == NULL)
4716 {
4717 cupsFileClose(out);
4718 unlink(filename);
4719 cupsdLogMessage(CUPSD_LOG_ERROR,
4720 "Unable to open banner template file %s - %s",
4721 filename, strerror(errno));
4722 job->num_files --;
4723 return (0);
4724 }
4725
4726 /*
4727 * Parse the file to the end...
4728 */
4729
4730 while ((ch = cupsFileGetChar(in)) != EOF)
4731 if (ch == '{')
4732 {
4733 /*
4734 * Get an attribute name...
4735 */
4736
4737 for (s = attrname; (ch = cupsFileGetChar(in)) != EOF;)
4738 if (!isalpha(ch & 255) && ch != '-' && ch != '?')
4739 break;
4740 else if (s < (attrname + sizeof(attrname) - 1))
4741 *s++ = ch;
4742 else
4743 break;
4744
4745 *s = '\0';
4746
4747 if (ch != '}')
4748 {
4749 /*
4750 * Ignore { followed by stuff that is not an attribute name...
4751 */
4752
4753 cupsFilePrintf(out, "{%s%c", attrname, ch);
4754 continue;
4755 }
4756
4757 /*
4758 * See if it is defined...
4759 */
4760
4761 if (attrname[0] == '?')
4762 s = attrname + 1;
4763 else
4764 s = attrname;
4765
4766 if (!strcmp(s, "printer-name"))
4767 {
4768 cupsFilePuts(out, job->dest);
4769 continue;
4770 }
4771 else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
4772 {
4773 /*
4774 * See if we have a leading question mark...
4775 */
4776
4777 if (attrname[0] != '?')
4778 {
4779 /*
4780 * Nope, write to file as-is; probably a PostScript procedure...
4781 */
4782
4783 cupsFilePrintf(out, "{%s}", attrname);
4784 }
4785
4786 continue;
4787 }
4788
4789 /*
4790 * Output value(s)...
4791 */
4792
4793 for (i = 0; i < attr->num_values; i ++)
4794 {
4795 if (i)
4796 cupsFilePutChar(out, ',');
4797
4798 switch (attr->value_tag)
4799 {
4800 case IPP_TAG_INTEGER :
4801 case IPP_TAG_ENUM :
4802 if (!strncmp(s, "time-at-", 8))
4803 cupsFilePuts(out, cupsdGetDateTime(attr->values[i].integer));
4804 else
4805 cupsFilePrintf(out, "%d", attr->values[i].integer);
4806 break;
4807
4808 case IPP_TAG_BOOLEAN :
4809 cupsFilePrintf(out, "%d", attr->values[i].boolean);
4810 break;
4811
4812 case IPP_TAG_NOVALUE :
4813 cupsFilePuts(out, "novalue");
4814 break;
4815
4816 case IPP_TAG_RANGE :
4817 cupsFilePrintf(out, "%d-%d", attr->values[i].range.lower,
4818 attr->values[i].range.upper);
4819 break;
4820
4821 case IPP_TAG_RESOLUTION :
4822 cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
4823 attr->values[i].resolution.yres,
4824 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
4825 "dpi" : "dpc");
4826 break;
4827
4828 case IPP_TAG_URI :
4829 case IPP_TAG_STRING :
4830 case IPP_TAG_TEXT :
4831 case IPP_TAG_NAME :
4832 case IPP_TAG_KEYWORD :
4833 case IPP_TAG_CHARSET :
4834 case IPP_TAG_LANGUAGE :
4835 if (!strcasecmp(banner->filetype->type, "postscript"))
4836 {
4837 /*
4838 * Need to quote strings for PS banners...
4839 */
4840
4841 const char *p;
4842
4843 for (p = attr->values[i].string.text; *p; p ++)
4844 {
4845 if (*p == '(' || *p == ')' || *p == '\\')
4846 {
4847 cupsFilePutChar(out, '\\');
4848 cupsFilePutChar(out, *p);
4849 }
4850 else if (*p < 32 || *p > 126)
4851 cupsFilePrintf(out, "\\%03o", *p & 255);
4852 else
4853 cupsFilePutChar(out, *p);
4854 }
4855 }
4856 else
4857 cupsFilePuts(out, attr->values[i].string.text);
4858 break;
4859
4860 default :
4861 break; /* anti-compiler-warning-code */
4862 }
4863 }
4864 }
4865 else if (ch == '\\') /* Quoted char */
4866 {
4867 ch = cupsFileGetChar(in);
4868
4869 if (ch != '{') /* Only do special handling for \{ */
4870 cupsFilePutChar(out, '\\');
4871
4872 cupsFilePutChar(out, ch);
4873 }
4874 else
4875 cupsFilePutChar(out, ch);
4876
4877 cupsFileClose(in);
4878
4879 kbytes = (cupsFileTell(out) + 1023) / 1024;
4880
4881 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
4882 IPP_TAG_INTEGER)) != NULL)
4883 attr->values[0].integer += kbytes;
4884
4885 cupsFileClose(out);
4886
4887 return (kbytes);
4888 }
4889
4890
4891 /*
4892 * 'copy_file()' - Copy a PPD file or interface script...
4893 */
4894
4895 static int /* O - 0 = success, -1 = error */
4896 copy_file(const char *from, /* I - Source file */
4897 const char *to) /* I - Destination file */
4898 {
4899 cups_file_t *src, /* Source file */
4900 *dst; /* Destination file */
4901 int bytes; /* Bytes to read/write */
4902 char buffer[2048]; /* Copy buffer */
4903
4904
4905 cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_file(\"%s\", \"%s\")", from, to);
4906
4907 /*
4908 * Open the source and destination file for a copy...
4909 */
4910
4911 if ((src = cupsFileOpen(from, "rb")) == NULL)
4912 return (-1);
4913
4914 if ((dst = cupsFileOpen(to, "wb")) == NULL)
4915 {
4916 cupsFileClose(src);
4917 return (-1);
4918 }
4919
4920 /*
4921 * Copy the source file to the destination...
4922 */
4923
4924 while ((bytes = cupsFileRead(src, buffer, sizeof(buffer))) > 0)
4925 if (cupsFileWrite(dst, buffer, bytes) < bytes)
4926 {
4927 cupsFileClose(src);
4928 cupsFileClose(dst);
4929 return (-1);
4930 }
4931
4932 /*
4933 * Close both files and return...
4934 */
4935
4936 cupsFileClose(src);
4937
4938 return (cupsFileClose(dst));
4939 }
4940
4941
4942 /*
4943 * 'copy_model()' - Copy a PPD model file, substituting default values
4944 * as needed...
4945 */
4946
4947 static int /* O - 0 = success, -1 = error */
4948 copy_model(cupsd_client_t *con, /* I - Client connection */
4949 const char *from, /* I - Source file */
4950 const char *to) /* I - Destination file */
4951 {
4952 fd_set input; /* select() input set */
4953 struct timeval timeout; /* select() timeout */
4954 int maxfd; /* Max file descriptor for select() */
4955 char tempfile[1024]; /* Temporary PPD file */
4956 int tempfd; /* Temporary PPD file descriptor */
4957 int temppid; /* Process ID of cups-driverd */
4958 int temppipe[2]; /* Temporary pipes */
4959 char *argv[4], /* Command-line arguments */
4960 *envp[MAX_ENV]; /* Environment */
4961 cups_file_t *src, /* Source file */
4962 *dst; /* Destination file */
4963 ppd_file_t *ppd; /* PPD file */
4964 int bytes, /* Bytes from pipe */
4965 total; /* Total bytes from pipe */
4966 char buffer[2048]; /* Copy buffer */
4967 int i; /* Looping var */
4968 char option[PPD_MAX_NAME], /* Option name */
4969 choice[PPD_MAX_NAME]; /* Choice name */
4970 int num_defaults; /* Number of default options */
4971 cups_option_t *defaults; /* Default options */
4972 char cups_protocol[PPD_MAX_LINE];
4973 /* cupsProtocol attribute */
4974 int have_letter, /* Have Letter size */
4975 have_a4; /* Have A4 size */
4976 #ifdef HAVE_LIBPAPER
4977 char *paper_result; /* Paper size name from libpaper */
4978 char system_paper[64]; /* Paper size name buffer */
4979 #endif /* HAVE_LIBPAPER */
4980
4981
4982 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4983 "copy_model(con=%p, from=\"%s\", to=\"%s\")",
4984 con, from, to);
4985
4986 /*
4987 * Run cups-driverd to get the PPD file...
4988 */
4989
4990 argv[0] = "cups-driverd";
4991 argv[1] = "cat";
4992 argv[2] = (char *)from;
4993 argv[3] = NULL;
4994
4995 cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
4996
4997 snprintf(buffer, sizeof(buffer), "%s/daemon/cups-driverd", ServerBin);
4998 snprintf(tempfile, sizeof(tempfile), "%s/%d.ppd", TempDir, con->http.fd);
4999 tempfd = open(tempfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
5000 if (tempfd < 0)
5001 return (-1);
5002
5003 cupsdOpenPipe(temppipe);
5004
5005 cupsdLogMessage(CUPSD_LOG_DEBUG,
5006 "copy_model: Running \"cups-driverd cat %s\"...", from);
5007
5008 if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1],
5009 -1, -1, 0, DefaultProfile, &temppid))
5010 {
5011 close(tempfd);
5012 unlink(tempfile);
5013
5014 return (-1);
5015 }
5016
5017 close(temppipe[1]);
5018
5019 /*
5020 * Wait up to 30 seconds for the PPD file to be copied...
5021 */
5022
5023 total = 0;
5024
5025 if (temppipe[0] > CGIPipes[0])
5026 maxfd = temppipe[0] + 1;
5027 else
5028 maxfd = CGIPipes[0] + 1;
5029
5030 for (;;)
5031 {
5032 /*
5033 * See if we have data ready...
5034 */
5035
5036 bytes = 0;
5037
5038 FD_ZERO(&input);
5039 FD_SET(temppipe[0], &input);
5040 FD_SET(CGIPipes[0], &input);
5041
5042 timeout.tv_sec = 30;
5043 timeout.tv_usec = 0;
5044
5045 if ((i = select(maxfd, &input, NULL, NULL, &timeout)) < 0)
5046 {
5047 if (errno == EINTR)
5048 continue;
5049 else
5050 break;
5051 }
5052 else if (i == 0)
5053 {
5054 /*
5055 * We have timed out...
5056 */
5057
5058 break;
5059 }
5060
5061 if (FD_ISSET(temppipe[0], &input))
5062 {
5063 /*
5064 * Read the PPD file from the pipe, and write it to the PPD file.
5065 */
5066
5067 if ((bytes = read(temppipe[0], buffer, sizeof(buffer))) > 0)
5068 {
5069 if (write(tempfd, buffer, bytes) < bytes)
5070 break;
5071
5072 total += bytes;
5073 }
5074 else
5075 break;
5076 }
5077
5078 if (FD_ISSET(CGIPipes[0], &input))
5079 cupsdUpdateCGI();
5080 }
5081
5082 close(temppipe[0]);
5083 close(tempfd);
5084
5085 if (!total)
5086 {
5087 /*
5088 * No data from cups-deviced...
5089 */
5090
5091 cupsdLogMessage(CUPSD_LOG_ERROR, "copy_model: empty PPD file!");
5092 unlink(tempfile);
5093 return (-1);
5094 }
5095
5096 /*
5097 * Read the source file and see what page sizes are supported...
5098 */
5099
5100 if ((ppd = ppdOpenFile(tempfile)) == NULL)
5101 {
5102 unlink(tempfile);
5103 return (-1);
5104 }
5105
5106 have_letter = ppdPageSize(ppd, "Letter") != NULL;
5107 have_a4 = ppdPageSize(ppd, "A4") != NULL;
5108
5109 /*
5110 * Open the destination (if possible) and set the default options...
5111 */
5112
5113 num_defaults = 0;
5114 defaults = NULL;
5115 cups_protocol[0] = '\0';
5116
5117 if ((dst = cupsFileOpen(to, "rb")) != NULL)
5118 {
5119 /*
5120 * Read all of the default lines from the old PPD...
5121 */
5122
5123 while (cupsFileGets(dst, buffer, sizeof(buffer)))
5124 if (!strncmp(buffer, "*Default", 8))
5125 {
5126 /*
5127 * Add the default option...
5128 */
5129
5130 if (!ppd_parse_line(buffer, option, sizeof(option),
5131 choice, sizeof(choice)))
5132 {
5133 ppd_option_t *ppdo; /* PPD option */
5134
5135
5136 /*
5137 * Only add the default if the default hasn't already been
5138 * set and the choice exists in the new PPD...
5139 */
5140
5141 if (!cupsGetOption(option, num_defaults, defaults) &&
5142 (ppdo = ppdFindOption(ppd, option)) != NULL &&
5143 ppdFindChoice(ppdo, choice))
5144 num_defaults = cupsAddOption(option, choice, num_defaults,
5145 &defaults);
5146 }
5147 }
5148 else if (!strncmp(buffer, "*cupsProtocol:", 14))
5149 strlcpy(cups_protocol, buffer, sizeof(cups_protocol));
5150
5151 cupsFileClose(dst);
5152 }
5153 #ifdef HAVE_LIBPAPER
5154 else if ((paper_result = systempapername()) != NULL)
5155 {
5156 /*
5157 * Set the default media sizes from the systemwide default...
5158 */
5159
5160 strlcpy(system_paper, paper_result, sizeof(system_paper));
5161 system_paper[0] = toupper(system_paper[0] & 255);
5162
5163 if ((!strcmp(system_paper, "Letter") && have_letter) ||
5164 (!strcmp(system_paper, "A4") && have_a4))
5165 {
5166 num_defaults = cupsAddOption("PageSize", system_paper,
5167 num_defaults, &defaults);
5168 num_defaults = cupsAddOption("PageRegion", system_paper,
5169 num_defaults, &defaults);
5170 num_defaults = cupsAddOption("PaperDimension", system_paper,
5171 num_defaults, &defaults);
5172 num_defaults = cupsAddOption("ImageableArea", system_paper,
5173 num_defaults, &defaults);
5174 }
5175 }
5176 #endif /* HAVE_LIBPAPER */
5177 else
5178 {
5179 /*
5180 * Add the default media sizes...
5181 *
5182 * Note: These values are generally not valid for large-format devices
5183 * like plotters, however it is probably safe to say that those
5184 * users will configure the media size after initially adding
5185 * the device anyways...
5186 */
5187
5188 if (!DefaultLanguage ||
5189 !strcasecmp(DefaultLanguage, "C") ||
5190 !strcasecmp(DefaultLanguage, "POSIX") ||
5191 !strcasecmp(DefaultLanguage, "en") ||
5192 !strncasecmp(DefaultLanguage, "en.", 3) ||
5193 !strncasecmp(DefaultLanguage, "en_US", 5) ||
5194 !strncasecmp(DefaultLanguage, "en_CA", 5) ||
5195 !strncasecmp(DefaultLanguage, "fr_CA", 5))
5196 {
5197 /*
5198 * These are the only locales that will default to "letter" size...
5199 */
5200
5201 if (have_letter)
5202 {
5203 num_defaults = cupsAddOption("PageSize", "Letter", num_defaults,
5204 &defaults);
5205 num_defaults = cupsAddOption("PageRegion", "Letter", num_defaults,
5206 &defaults);
5207 num_defaults = cupsAddOption("PaperDimension", "Letter", num_defaults,
5208 &defaults);
5209 num_defaults = cupsAddOption("ImageableArea", "Letter", num_defaults,
5210 &defaults);
5211 }
5212 }
5213 else if (have_a4)
5214 {
5215 /*
5216 * The rest default to "a4" size...
5217 */
5218
5219 num_defaults = cupsAddOption("PageSize", "A4", num_defaults,
5220 &defaults);
5221 num_defaults = cupsAddOption("PageRegion", "A4", num_defaults,
5222 &defaults);
5223 num_defaults = cupsAddOption("PaperDimension", "A4", num_defaults,
5224 &defaults);
5225 num_defaults = cupsAddOption("ImageableArea", "A4", num_defaults,
5226 &defaults);
5227 }
5228 }
5229
5230 ppdClose(ppd);
5231
5232 /*
5233 * Open the source file for a copy...
5234 */
5235
5236 if ((src = cupsFileOpen(tempfile, "rb")) == NULL)
5237 {
5238 cupsFreeOptions(num_defaults, defaults);
5239 unlink(tempfile);
5240 return (-1);
5241 }
5242
5243 /*
5244 * Open the destination file for a copy...
5245 */
5246
5247 if ((dst = cupsFileOpen(to, "wb")) == NULL)
5248 {
5249 cupsFreeOptions(num_defaults, defaults);
5250 cupsFileClose(src);
5251 unlink(tempfile);
5252 return (-1);
5253 }
5254
5255 /*
5256 * Copy the source file to the destination...
5257 */
5258
5259 while (cupsFileGets(src, buffer, sizeof(buffer)))
5260 {
5261 if (!strncmp(buffer, "*Default", 8))
5262 {
5263 /*
5264 * Check for an previous default option choice...
5265 */
5266
5267 if (!ppd_parse_line(buffer, option, sizeof(option),
5268 choice, sizeof(choice)))
5269 {
5270 const char *val; /* Default option value */
5271
5272
5273 if ((val = cupsGetOption(option, num_defaults, defaults)) != NULL)
5274 {
5275 /*
5276 * Substitute the previous choice...
5277 */
5278
5279 snprintf(buffer, sizeof(buffer), "*Default%s: %s", option, val);
5280 }
5281 }
5282 }
5283
5284 cupsFilePrintf(dst, "%s\n", buffer);
5285 }
5286
5287 if (cups_protocol[0])
5288 cupsFilePrintf(dst, "%s\n", cups_protocol);
5289
5290 cupsFreeOptions(num_defaults, defaults);
5291
5292 /*
5293 * Close both files and return...
5294 */
5295
5296 cupsFileClose(src);
5297
5298 unlink(tempfile);
5299
5300 return (cupsFileClose(dst));
5301 }
5302
5303
5304 /*
5305 * 'copy_job_attrs()' - Copy job attributes.
5306 */
5307
5308 static void
5309 copy_job_attrs(cupsd_client_t *con, /* I - Client connection */
5310 cupsd_job_t *job, /* I - Job */
5311 cups_array_t *ra) /* I - Requested attributes array */
5312 {
5313 char job_uri[HTTP_MAX_URI]; /* Job URI */
5314
5315
5316 /*
5317 * Send the requested attributes for each job...
5318 */
5319
5320 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
5321 con->servername, con->serverport, "/jobs/%d",
5322 job->id);
5323
5324 if (!ra || cupsArrayFind(ra, "document-count"))
5325 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
5326 "document-count", job->num_files);
5327
5328 if (!ra || cupsArrayFind(ra, "job-more-info"))
5329 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
5330 "job-more-info", NULL, job_uri);
5331
5332 if (job->state_value > IPP_JOB_PROCESSING &&
5333 (!ra || cupsArrayFind(ra, "job-preserved")))
5334 ippAddBoolean(con->response, IPP_TAG_JOB, "job-preserved",
5335 job->num_files > 0);
5336
5337 if (!ra || cupsArrayFind(ra, "job-printer-up-time"))
5338 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
5339 "job-printer-up-time", time(NULL));
5340
5341 if (!ra || cupsArrayFind(ra, "job-state-reasons"))
5342 add_job_state_reasons(con, job);
5343
5344 if (!ra || cupsArrayFind(ra, "job-uri"))
5345 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
5346 "job-uri", NULL, job_uri);
5347
5348 copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0);
5349 }
5350
5351
5352 /*
5353 * 'copy_printer_attrs()' - Copy printer attributes.
5354 */
5355
5356 static void
5357 copy_printer_attrs(
5358 cupsd_client_t *con, /* I - Client connection */
5359 cupsd_printer_t *printer, /* I - Printer */
5360 cups_array_t *ra) /* I - Requested attributes array */
5361 {
5362 char printer_uri[HTTP_MAX_URI];
5363 /* Printer URI */
5364 time_t curtime; /* Current time */
5365 int i; /* Looping var */
5366 ipp_attribute_t *history; /* History collection */
5367
5368
5369 /*
5370 * Copy the printer attributes to the response using requested-attributes
5371 * and document-format attributes that may be provided by the client.
5372 */
5373
5374 curtime = time(NULL);
5375
5376 #ifdef __APPLE__
5377 if ((!ra || cupsArrayFind(ra, "com.apple.print.recoverable-message")) &&
5378 printer->recoverable)
5379 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
5380 "com.apple.print.recoverable-message", NULL,
5381 printer->recoverable);
5382 #endif /* __APPLE__ */
5383
5384 if (!ra || cupsArrayFind(ra, "marker-change-time"))
5385 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5386 "marker-change-time", printer->marker_time);
5387
5388 if (printer->num_printers > 0 &&
5389 (!ra || cupsArrayFind(ra, "member-uris")))
5390 {
5391 ipp_attribute_t *member_uris; /* member-uris attribute */
5392 cupsd_printer_t *p2; /* Printer in class */
5393 ipp_attribute_t *p2_uri; /* printer-uri-supported for class printer */
5394
5395
5396 if ((member_uris = ippAddStrings(con->response, IPP_TAG_PRINTER,
5397 IPP_TAG_URI, "member-uris",
5398 printer->num_printers, NULL,
5399 NULL)) != NULL)
5400 {
5401 for (i = 0; i < printer->num_printers; i ++)
5402 {
5403 p2 = printer->printers[i];
5404
5405 if ((p2_uri = ippFindAttribute(p2->attrs, "printer-uri-supported",
5406 IPP_TAG_URI)) != NULL)
5407 member_uris->values[i].string.text =
5408 _cupsStrAlloc(p2_uri->values[0].string.text);
5409 else
5410 {
5411 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri,
5412 sizeof(printer_uri), "ipp", NULL, con->servername,
5413 con->serverport,
5414 (p2->type & CUPS_PRINTER_CLASS) ?
5415 "/classes/%s" : "/printers/%s", p2->name);
5416 member_uris->values[i].string.text = _cupsStrAlloc(printer_uri);
5417 }
5418 }
5419 }
5420 }
5421
5422 if (printer->alert && (!ra || cupsArrayFind(ra, "printer-alert")))
5423 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_STRING,
5424 "printer-alert", NULL, printer->alert);
5425
5426 if (printer->alert_description &&
5427 (!ra || cupsArrayFind(ra, "printer-alert-description")))
5428 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
5429 "printer-alert-description", NULL,
5430 printer->alert_description);
5431
5432 if (!ra || cupsArrayFind(ra, "printer-current-time"))
5433 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
5434 ippTimeToDate(curtime));
5435
5436 #ifdef HAVE_DNSSD
5437 if (!ra || cupsArrayFind(ra, "printer-dns-sd-name"))
5438 {
5439 if (printer->reg_name)
5440 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
5441 "printer-dns-sd-name", NULL, printer->reg_name);
5442 else
5443 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
5444 "printer-dns-sd-name", 0);
5445 }
5446 #endif /* HAVE_DNSSD */
5447
5448 if (!ra || cupsArrayFind(ra, "printer-error-policy"))
5449 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
5450 "printer-error-policy", NULL, printer->error_policy);
5451
5452 if (!ra || cupsArrayFind(ra, "printer-is-accepting-jobs"))
5453 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
5454 printer->accepting);
5455
5456 if (!ra || cupsArrayFind(ra, "printer-is-shared"))
5457 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-shared",
5458 printer->shared);
5459
5460 if (!ra || cupsArrayFind(ra, "printer-op-policy"))
5461 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
5462 "printer-op-policy", NULL, printer->op_policy);
5463
5464 if (!ra || cupsArrayFind(ra, "printer-state"))
5465 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
5466 printer->state);
5467
5468 if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
5469 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5470 "printer-state-change-time", printer->state_time);
5471
5472 if (MaxPrinterHistory > 0 && printer->num_history > 0 &&
5473 cupsArrayFind(ra, "printer-state-history"))
5474 {
5475 /*
5476 * Printer history is only sent if specifically requested, so that
5477 * older CUPS/IPP clients won't barf on the collection attributes.
5478 */
5479
5480 history = ippAddCollections(con->response, IPP_TAG_PRINTER,
5481 "printer-state-history",
5482 printer->num_history, NULL);
5483
5484 for (i = 0; i < printer->num_history; i ++)
5485 copy_attrs(history->values[i].collection = ippNew(), printer->history[i],
5486 NULL, IPP_TAG_ZERO, 0);
5487 }
5488
5489 if (!ra || cupsArrayFind(ra, "printer-state-message"))
5490 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
5491 "printer-state-message", NULL, printer->state_message);
5492
5493 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
5494 add_printer_state_reasons(con, printer);
5495
5496 if (!ra || cupsArrayFind(ra, "printer-type"))
5497 {
5498 int type; /* printer-type value */
5499
5500
5501 /*
5502 * Add the CUPS-specific printer-type attribute...
5503 */
5504
5505 type = printer->type;
5506
5507 if (printer == DefaultPrinter)
5508 type |= CUPS_PRINTER_DEFAULT;
5509
5510 if (!printer->accepting)
5511 type |= CUPS_PRINTER_REJECTING;
5512
5513 if (!printer->shared)
5514 type |= CUPS_PRINTER_NOT_SHARED;
5515
5516 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-type",
5517 type);
5518 }
5519
5520 if (!ra || cupsArrayFind(ra, "printer-up-time"))
5521 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5522 "printer-up-time", curtime);
5523
5524 if ((!ra || cupsArrayFind(ra, "printer-uri-supported")) &&
5525 !ippFindAttribute(printer->attrs, "printer-uri-supported",
5526 IPP_TAG_URI))
5527 {
5528 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
5529 "ipp", NULL, con->servername, con->serverport,
5530 (printer->type & CUPS_PRINTER_CLASS) ?
5531 "/classes/%s" : "/printers/%s", printer->name);
5532 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
5533 "printer-uri-supported", NULL, printer_uri);
5534 cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-uri-supported=\"%s\"",
5535 printer_uri);
5536 }
5537
5538 if (!ra || cupsArrayFind(ra, "queued-job-count"))
5539 add_queued_job_count(con, printer);
5540
5541 copy_attrs(con->response, printer->attrs, ra, IPP_TAG_ZERO, 0);
5542 copy_attrs(con->response, CommonData, ra, IPP_TAG_ZERO, IPP_TAG_COPY);
5543 }
5544
5545
5546 /*
5547 * 'copy_subscription_attrs()' - Copy subscription attributes.
5548 */
5549
5550 static void
5551 copy_subscription_attrs(
5552 cupsd_client_t *con, /* I - Client connection */
5553 cupsd_subscription_t *sub, /* I - Subscription */
5554 cups_array_t *ra) /* I - Requested attributes array */
5555 {
5556 ipp_attribute_t *attr; /* Current attribute */
5557 char printer_uri[HTTP_MAX_URI];
5558 /* Printer URI */
5559 int count; /* Number of events */
5560 unsigned mask; /* Current event mask */
5561 const char *name; /* Current event name */
5562
5563
5564 /*
5565 * Copy the subscription attributes to the response using the
5566 * requested-attributes attribute that may be provided by the client.
5567 */
5568
5569 if (!ra || cupsArrayFind(ra, "notify-events"))
5570 {
5571 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
5572 {
5573 /*
5574 * Simple event list...
5575 */
5576
5577 ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
5578 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
5579 "notify-events", NULL, name);
5580 }
5581 else
5582 {
5583 /*
5584 * Complex event list...
5585 */
5586
5587 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
5588 if (sub->mask & mask)
5589 count ++;
5590
5591 attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
5592 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
5593 "notify-events", count, NULL, NULL);
5594
5595 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
5596 if (sub->mask & mask)
5597 {
5598 attr->values[count].string.text =
5599 (char *)cupsdEventName((cupsd_eventmask_t)mask);
5600
5601 count ++;
5602 }
5603 }
5604 }
5605
5606 if (sub->job && (!ra || cupsArrayFind(ra, "notify-job-id")))
5607 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5608 "notify-job-id", sub->job->id);
5609
5610 if (!sub->job && (!ra || cupsArrayFind(ra, "notify-lease-duration")))
5611 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5612 "notify-lease-duration", sub->lease);
5613
5614 if (sub->dest && (!ra || cupsArrayFind(ra, "notify-printer-uri")))
5615 {
5616 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
5617 "ipp", NULL, con->servername, con->serverport,
5618 "/printers/%s", sub->dest->name);
5619 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5620 "notify-printer-uri", NULL, printer_uri);
5621 }
5622
5623 if (sub->recipient && (!ra || cupsArrayFind(ra, "notify-recipient-uri")))
5624 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5625 "notify-recipient-uri", NULL, sub->recipient);
5626 else if (!ra || cupsArrayFind(ra, "notify-pull-method"))
5627 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
5628 "notify-pull-method", NULL, "ippget");
5629
5630 if (!ra || cupsArrayFind(ra, "notify-subscriber-user-name"))
5631 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
5632 "notify-subscriber-user-name", NULL, sub->owner);
5633
5634 if (!ra || cupsArrayFind(ra, "notify-subscription-id"))
5635 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5636 "notify-subscription-id", sub->id);
5637
5638 if (!ra || cupsArrayFind(ra, "notify-time-interval"))
5639 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5640 "notify-time-interval", sub->interval);
5641
5642 if (sub->user_data_len > 0 && (!ra || cupsArrayFind(ra, "notify-user-data")))
5643 ippAddOctetString(con->response, IPP_TAG_SUBSCRIPTION, "notify-user-data",
5644 sub->user_data, sub->user_data_len);
5645 }
5646
5647
5648 /*
5649 * 'create_job()' - Print a file to a printer or class.
5650 */
5651
5652 static void
5653 create_job(cupsd_client_t *con, /* I - Client connection */
5654 ipp_attribute_t *uri) /* I - Printer URI */
5655 {
5656 cupsd_printer_t *printer; /* Printer */
5657 cupsd_job_t *job; /* New job */
5658
5659
5660 cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con,
5661 con->http.fd, uri->values[0].string.text);
5662
5663 /*
5664 * Is the destination valid?
5665 */
5666
5667 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
5668 {
5669 /*
5670 * Bad URI...
5671 */
5672
5673 send_ipp_status(con, IPP_NOT_FOUND,
5674 _("The printer or class was not found."));
5675 return;
5676 }
5677
5678 /*
5679 * Create the job object...
5680 */
5681
5682 if ((job = add_job(con, printer, NULL)) == NULL)
5683 return;
5684
5685 job->pending_timeout = 1;
5686
5687 /*
5688 * Save and log the job...
5689 */
5690
5691 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
5692 job->dest, job->username);
5693 }
5694
5695
5696 /*
5697 * 'create_requested_array()' - Create an array for the requested-attributes.
5698 */
5699
5700 static cups_array_t * /* O - Array of attributes or NULL */
5701 create_requested_array(ipp_t *request) /* I - IPP request */
5702 {
5703 int i; /* Looping var */
5704 ipp_attribute_t *requested; /* requested-attributes attribute */
5705 cups_array_t *ra; /* Requested attributes array */
5706 char *value; /* Current value */
5707
5708
5709 /*
5710 * Get the requested-attributes attribute, and return NULL if we don't
5711 * have one...
5712 */
5713
5714 if ((requested = ippFindAttribute(request, "requested-attributes",
5715 IPP_TAG_KEYWORD)) == NULL)
5716 return (NULL);
5717
5718 /*
5719 * If the attribute contains a single "all" keyword, return NULL...
5720 */
5721
5722 if (requested->num_values == 1 &&
5723 !strcmp(requested->values[0].string.text, "all"))
5724 return (NULL);
5725
5726 /*
5727 * Create an array using "strcmp" as the comparison function...
5728 */
5729
5730 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
5731
5732 for (i = 0; i < requested->num_values; i ++)
5733 {
5734 value = requested->values[i].string.text;
5735
5736 if (!strcmp(value, "job-template"))
5737 {
5738 cupsArrayAdd(ra, "copies");
5739 cupsArrayAdd(ra, "copies-default");
5740 cupsArrayAdd(ra, "copies-supported");
5741 cupsArrayAdd(ra, "finishings");
5742 cupsArrayAdd(ra, "finishings-default");
5743 cupsArrayAdd(ra, "finishings-supported");
5744 cupsArrayAdd(ra, "job-hold-until");
5745 cupsArrayAdd(ra, "job-hold-until-default");
5746 cupsArrayAdd(ra, "job-hold-until-supported");
5747 cupsArrayAdd(ra, "job-priority");
5748 cupsArrayAdd(ra, "job-priority-default");
5749 cupsArrayAdd(ra, "job-priority-supported");
5750 cupsArrayAdd(ra, "job-sheets");
5751 cupsArrayAdd(ra, "job-sheets-default");
5752 cupsArrayAdd(ra, "job-sheets-supported");
5753 cupsArrayAdd(ra, "media");
5754 cupsArrayAdd(ra, "media-default");
5755 cupsArrayAdd(ra, "media-supported");
5756 cupsArrayAdd(ra, "multiple-document-handling");
5757 cupsArrayAdd(ra, "multiple-document-handling-default");
5758 cupsArrayAdd(ra, "multiple-document-handling-supported");
5759 cupsArrayAdd(ra, "number-up");
5760 cupsArrayAdd(ra, "number-up-default");
5761 cupsArrayAdd(ra, "number-up-supported");
5762 cupsArrayAdd(ra, "orientation-requested");
5763 cupsArrayAdd(ra, "orientation-requested-default");
5764 cupsArrayAdd(ra, "orientation-requested-supported");
5765 cupsArrayAdd(ra, "page-ranges");
5766 cupsArrayAdd(ra, "page-ranges-supported");
5767 cupsArrayAdd(ra, "printer-resolution");
5768 cupsArrayAdd(ra, "printer-resolution-default");
5769 cupsArrayAdd(ra, "printer-resolution-supported");
5770 cupsArrayAdd(ra, "print-quality");
5771 cupsArrayAdd(ra, "print-quality-default");
5772 cupsArrayAdd(ra, "print-quality-supported");
5773 cupsArrayAdd(ra, "sides");
5774 cupsArrayAdd(ra, "sides-default");
5775 cupsArrayAdd(ra, "sides-supported");
5776 }
5777 else if (!strcmp(value, "job-description"))
5778 {
5779 cupsArrayAdd(ra, "date-time-at-completed");
5780 cupsArrayAdd(ra, "date-time-at-creation");
5781 cupsArrayAdd(ra, "date-time-at-processing");
5782 cupsArrayAdd(ra, "job-detailed-status-message");
5783 cupsArrayAdd(ra, "job-document-access-errors");
5784 cupsArrayAdd(ra, "job-id");
5785 cupsArrayAdd(ra, "job-impressions");
5786 cupsArrayAdd(ra, "job-impressions-completed");
5787 cupsArrayAdd(ra, "job-k-octets");
5788 cupsArrayAdd(ra, "job-k-octets-processed");
5789 cupsArrayAdd(ra, "job-media-sheets");
5790 cupsArrayAdd(ra, "job-media-sheets-completed");
5791 cupsArrayAdd(ra, "job-message-from-operator");
5792 cupsArrayAdd(ra, "job-more-info");
5793 cupsArrayAdd(ra, "job-name");
5794 cupsArrayAdd(ra, "job-originating-user-name");
5795 cupsArrayAdd(ra, "job-printer-up-time");
5796 cupsArrayAdd(ra, "job-printer-uri");
5797 cupsArrayAdd(ra, "job-state");
5798 cupsArrayAdd(ra, "job-state-message");
5799 cupsArrayAdd(ra, "job-state-reasons");
5800 cupsArrayAdd(ra, "job-uri");
5801 cupsArrayAdd(ra, "number-of-documents");
5802 cupsArrayAdd(ra, "number-of-intervening-jobs");
5803 cupsArrayAdd(ra, "output-device-assigned");
5804 cupsArrayAdd(ra, "time-at-completed");
5805 cupsArrayAdd(ra, "time-at-creation");
5806 cupsArrayAdd(ra, "time-at-processing");
5807 }
5808 else if (!strcmp(value, "printer-description"))
5809 {
5810 cupsArrayAdd(ra, "charset-configured");
5811 cupsArrayAdd(ra, "charset-supported");
5812 cupsArrayAdd(ra, "color-supported");
5813 cupsArrayAdd(ra, "compression-supported");
5814 cupsArrayAdd(ra, "document-format-default");
5815 cupsArrayAdd(ra, "document-format-supported");
5816 cupsArrayAdd(ra, "generated-natural-language-supported");
5817 cupsArrayAdd(ra, "ipp-versions-supported");
5818 cupsArrayAdd(ra, "job-impressions-supported");
5819 cupsArrayAdd(ra, "job-k-octets-supported");
5820 cupsArrayAdd(ra, "job-media-sheets-supported");
5821 cupsArrayAdd(ra, "multiple-document-jobs-supported");
5822 cupsArrayAdd(ra, "multiple-operation-time-out");
5823 cupsArrayAdd(ra, "natural-language-configured");
5824 cupsArrayAdd(ra, "notify-attributes-supported");
5825 cupsArrayAdd(ra, "notify-lease-duration-default");
5826 cupsArrayAdd(ra, "notify-lease-duration-supported");
5827 cupsArrayAdd(ra, "notify-max-events-supported");
5828 cupsArrayAdd(ra, "notify-events-default");
5829 cupsArrayAdd(ra, "notify-events-supported");
5830 cupsArrayAdd(ra, "notify-pull-method-supported");
5831 cupsArrayAdd(ra, "notify-schemes-supported");
5832 cupsArrayAdd(ra, "operations-supported");
5833 cupsArrayAdd(ra, "pages-per-minute");
5834 cupsArrayAdd(ra, "pages-per-minute-color");
5835 cupsArrayAdd(ra, "pdl-override-supported");
5836 cupsArrayAdd(ra, "printer-alert");
5837 cupsArrayAdd(ra, "printer-alert-description");
5838 cupsArrayAdd(ra, "printer-commands");
5839 cupsArrayAdd(ra, "printer-current-time");
5840 cupsArrayAdd(ra, "printer-driver-installer");
5841 cupsArrayAdd(ra, "printer-dns-sd-name");
5842 cupsArrayAdd(ra, "printer-info");
5843 cupsArrayAdd(ra, "printer-is-accepting-jobs");
5844 cupsArrayAdd(ra, "printer-location");
5845 cupsArrayAdd(ra, "printer-make-and-model");
5846 cupsArrayAdd(ra, "printer-message-from-operator");
5847 cupsArrayAdd(ra, "printer-more-info");
5848 cupsArrayAdd(ra, "printer-more-info-manufacturer");
5849 cupsArrayAdd(ra, "printer-name");
5850 cupsArrayAdd(ra, "printer-state");
5851 cupsArrayAdd(ra, "printer-state-message");
5852 cupsArrayAdd(ra, "printer-state-reasons");
5853 cupsArrayAdd(ra, "printer-type");
5854 cupsArrayAdd(ra, "printer-up-time");
5855 cupsArrayAdd(ra, "printer-uri-supported");
5856 cupsArrayAdd(ra, "queued-job-count");
5857 cupsArrayAdd(ra, "reference-uri-schemes-supported");
5858 cupsArrayAdd(ra, "uri-authentication-supported");
5859 cupsArrayAdd(ra, "uri-security-supported");
5860 }
5861 else if (!strcmp(value, "printer-defaults"))
5862 {
5863 char *name; /* Option name */
5864
5865
5866 for (name = (char *)cupsArrayFirst(CommonDefaults);
5867 name;
5868 name = (char *)cupsArrayNext(CommonDefaults))
5869 cupsArrayAdd(ra, name);
5870 }
5871 else if (!strcmp(value, "subscription-template"))
5872 {
5873 cupsArrayAdd(ra, "notify-attributes");
5874 cupsArrayAdd(ra, "notify-charset");
5875 cupsArrayAdd(ra, "notify-events");
5876 cupsArrayAdd(ra, "notify-lease-duration");
5877 cupsArrayAdd(ra, "notify-natural-language");
5878 cupsArrayAdd(ra, "notify-pull-method");
5879 cupsArrayAdd(ra, "notify-recipient-uri");
5880 cupsArrayAdd(ra, "notify-time-interval");
5881 cupsArrayAdd(ra, "notify-user-data");
5882 }
5883 else
5884 cupsArrayAdd(ra, value);
5885 }
5886
5887 return (ra);
5888 }
5889
5890
5891 /*
5892 * 'create_subscription()' - Create a notification subscription.
5893 */
5894
5895 static void
5896 create_subscription(
5897 cupsd_client_t *con, /* I - Client connection */
5898 ipp_attribute_t *uri) /* I - Printer URI */
5899 {
5900 http_status_t status; /* Policy status */
5901 int i; /* Looping var */
5902 ipp_attribute_t *attr; /* Current attribute */
5903 cups_ptype_t dtype; /* Destination type (printer/class) */
5904 char scheme[HTTP_MAX_URI],
5905 /* Scheme portion of URI */
5906 userpass[HTTP_MAX_URI],
5907 /* Username portion of URI */
5908 host[HTTP_MAX_URI],
5909 /* Host portion of URI */
5910 resource[HTTP_MAX_URI];
5911 /* Resource portion of URI */
5912 int port; /* Port portion of URI */
5913 cupsd_printer_t *printer; /* Printer/class */
5914 cupsd_job_t *job; /* Job */
5915 int jobid; /* Job ID */
5916 cupsd_subscription_t *sub; /* Subscription object */
5917 const char *username, /* requesting-user-name or
5918 authenticated username */
5919 *recipient, /* notify-recipient-uri */
5920 *pullmethod; /* notify-pull-method */
5921 ipp_attribute_t *user_data; /* notify-user-data */
5922 int interval, /* notify-time-interval */
5923 lease; /* notify-lease-duration */
5924 unsigned mask; /* notify-events */
5925 ipp_attribute_t *notify_events,/* notify-events(-default) */
5926 *notify_lease; /* notify-lease-duration(-default) */
5927
5928
5929 #ifdef DEBUG
5930 for (attr = con->request->attrs; attr; attr = attr->next)
5931 {
5932 if (attr->group_tag != IPP_TAG_ZERO)
5933 cupsdLogMessage(CUPSD_LOG_DEBUG2, "g%04x v%04x %s", attr->group_tag,
5934 attr->value_tag, attr->name);
5935 else
5936 cupsdLogMessage(CUPSD_LOG_DEBUG2, "----SEP----");
5937 }
5938 #endif /* DEBUG */
5939
5940 /*
5941 * Is the destination valid?
5942 */
5943
5944 cupsdLogMessage(CUPSD_LOG_DEBUG,
5945 "cupsdCreateSubscription(con=%p(%d), uri=\"%s\")",
5946 con, con->http.fd, uri->values[0].string.text);
5947
5948 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
5949 sizeof(scheme), userpass, sizeof(userpass), host,
5950 sizeof(host), &port, resource, sizeof(resource));
5951
5952 if (!strcmp(resource, "/"))
5953 {
5954 dtype = (cups_ptype_t)0;
5955 printer = NULL;
5956 }
5957 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
5958 {
5959 dtype = (cups_ptype_t)0;
5960 printer = NULL;
5961 }
5962 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
5963 {
5964 dtype = CUPS_PRINTER_CLASS;
5965 printer = NULL;
5966 }
5967 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
5968 {
5969 /*
5970 * Bad URI...
5971 */
5972
5973 send_ipp_status(con, IPP_NOT_FOUND,
5974 _("The printer or class was not found."));
5975 return;
5976 }
5977
5978 /*
5979 * Check policy...
5980 */
5981
5982 if (printer)
5983 {
5984 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
5985 NULL)) != HTTP_OK)
5986 {
5987 send_http_error(con, status, printer);
5988 return;
5989 }
5990 }
5991 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5992 {
5993 send_http_error(con, status, NULL);
5994 return;
5995 }
5996
5997 /*
5998 * Get the user that is requesting the subscription...
5999 */
6000
6001 username = get_username(con);
6002
6003 /*
6004 * Find the first subscription group attribute; return if we have
6005 * none...
6006 */
6007
6008 for (attr = con->request->attrs; attr; attr = attr->next)
6009 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
6010 break;
6011
6012 if (!attr)
6013 {
6014 send_ipp_status(con, IPP_BAD_REQUEST,
6015 _("No subscription attributes in request!"));
6016 return;
6017 }
6018
6019 /*
6020 * Process the subscription attributes in the request...
6021 */
6022
6023 con->response->request.status.status_code = IPP_BAD_REQUEST;
6024
6025 while (attr)
6026 {
6027 recipient = NULL;
6028 pullmethod = NULL;
6029 user_data = NULL;
6030 interval = 0;
6031 lease = DefaultLeaseDuration;
6032 jobid = 0;
6033 mask = CUPSD_EVENT_NONE;
6034
6035 if (printer)
6036 {
6037 notify_events = ippFindAttribute(printer->attrs, "notify-events-default",
6038 IPP_TAG_KEYWORD);
6039 notify_lease = ippFindAttribute(printer->attrs,
6040 "notify-lease-duration-default",
6041 IPP_TAG_INTEGER);
6042
6043 if (notify_lease)
6044 lease = notify_lease->values[0].integer;
6045 }
6046 else
6047 {
6048 notify_events = NULL;
6049 notify_lease = NULL;
6050 }
6051
6052 while (attr && attr->group_tag != IPP_TAG_ZERO)
6053 {
6054 if (!strcmp(attr->name, "notify-recipient-uri") &&
6055 attr->value_tag == IPP_TAG_URI)
6056 {
6057 /*
6058 * Validate the recipient scheme against the ServerBin/notifier
6059 * directory...
6060 */
6061
6062 char notifier[1024]; /* Notifier filename */
6063
6064
6065 recipient = attr->values[0].string.text;
6066
6067 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
6068 scheme, sizeof(scheme), userpass, sizeof(userpass),
6069 host, sizeof(host), &port,
6070 resource, sizeof(resource)) < HTTP_URI_OK)
6071 {
6072 send_ipp_status(con, IPP_NOT_POSSIBLE,
6073 _("Bad notify-recipient-uri URI \"%s\"!"), recipient);
6074 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6075 "notify-status-code", IPP_URI_SCHEME);
6076 return;
6077 }
6078
6079 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
6080 scheme);
6081 if (access(notifier, X_OK))
6082 {
6083 send_ipp_status(con, IPP_NOT_POSSIBLE,
6084 _("notify-recipient-uri URI \"%s\" uses unknown "
6085 "scheme!"), recipient);
6086 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6087 "notify-status-code", IPP_URI_SCHEME);
6088 return;
6089 }
6090
6091 if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
6092 {
6093 send_ipp_status(con, IPP_NOT_POSSIBLE,
6094 _("notify-recipient-uri URI \"%s\" is already used!"),
6095 recipient);
6096 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6097 "notify-status-code", IPP_ATTRIBUTES);
6098 return;
6099 }
6100 }
6101 else if (!strcmp(attr->name, "notify-pull-method") &&
6102 attr->value_tag == IPP_TAG_KEYWORD)
6103 {
6104 pullmethod = attr->values[0].string.text;
6105
6106 if (strcmp(pullmethod, "ippget"))
6107 {
6108 send_ipp_status(con, IPP_NOT_POSSIBLE,
6109 _("Bad notify-pull-method \"%s\"!"), pullmethod);
6110 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6111 "notify-status-code", IPP_ATTRIBUTES);
6112 return;
6113 }
6114 }
6115 else if (!strcmp(attr->name, "notify-charset") &&
6116 attr->value_tag == IPP_TAG_CHARSET &&
6117 strcmp(attr->values[0].string.text, "us-ascii") &&
6118 strcmp(attr->values[0].string.text, "utf-8"))
6119 {
6120 send_ipp_status(con, IPP_CHARSET,
6121 _("Character set \"%s\" not supported!"),
6122 attr->values[0].string.text);
6123 return;
6124 }
6125 else if (!strcmp(attr->name, "notify-natural-language") &&
6126 (attr->value_tag != IPP_TAG_LANGUAGE ||
6127 strcmp(attr->values[0].string.text, DefaultLanguage)))
6128 {
6129 send_ipp_status(con, IPP_CHARSET,
6130 _("Language \"%s\" not supported!"),
6131 attr->values[0].string.text);
6132 return;
6133 }
6134 else if (!strcmp(attr->name, "notify-user-data") &&
6135 attr->value_tag == IPP_TAG_STRING)
6136 {
6137 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
6138 {
6139 send_ipp_status(con, IPP_REQUEST_VALUE,
6140 _("The notify-user-data value is too large "
6141 "(%d > 63 octets)!"),
6142 attr->values[0].unknown.length);
6143 return;
6144 }
6145
6146 user_data = attr;
6147 }
6148 else if (!strcmp(attr->name, "notify-events") &&
6149 attr->value_tag == IPP_TAG_KEYWORD)
6150 notify_events = attr;
6151 else if (!strcmp(attr->name, "notify-lease-duration") &&
6152 attr->value_tag == IPP_TAG_INTEGER)
6153 lease = attr->values[0].integer;
6154 else if (!strcmp(attr->name, "notify-time-interval") &&
6155 attr->value_tag == IPP_TAG_INTEGER)
6156 interval = attr->values[0].integer;
6157 else if (!strcmp(attr->name, "notify-job-id") &&
6158 attr->value_tag == IPP_TAG_INTEGER)
6159 jobid = attr->values[0].integer;
6160
6161 attr = attr->next;
6162 }
6163
6164 if (notify_events)
6165 {
6166 for (i = 0; i < notify_events->num_values; i ++)
6167 mask |= cupsdEventValue(notify_events->values[i].string.text);
6168 }
6169
6170 if (recipient)
6171 cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
6172 if (pullmethod)
6173 cupsdLogMessage(CUPSD_LOG_DEBUG, "pullmethod=\"%s\"", pullmethod);
6174 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-lease-duration=%d", lease);
6175 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-time-interval=%d", interval);
6176
6177 if (!recipient && !pullmethod)
6178 break;
6179
6180 if (mask == CUPSD_EVENT_NONE)
6181 {
6182 if (jobid)
6183 mask = CUPSD_EVENT_JOB_COMPLETED;
6184 else if (printer)
6185 mask = CUPSD_EVENT_PRINTER_STATE_CHANGED;
6186 else
6187 {
6188 send_ipp_status(con, IPP_BAD_REQUEST,
6189 _("notify-events not specified!"));
6190 return;
6191 }
6192 }
6193
6194 if (MaxLeaseDuration && (lease == 0 || lease > MaxLeaseDuration))
6195 {
6196 cupsdLogMessage(CUPSD_LOG_INFO,
6197 "create_subscription: Limiting notify-lease-duration to "
6198 "%d seconds.",
6199 MaxLeaseDuration);
6200 lease = MaxLeaseDuration;
6201 }
6202
6203 if (jobid)
6204 {
6205 if ((job = cupsdFindJob(jobid)) == NULL)
6206 {
6207 send_ipp_status(con, IPP_NOT_FOUND, _("Job %d not found!"), jobid);
6208 return;
6209 }
6210 }
6211 else
6212 job = NULL;
6213
6214 sub = cupsdAddSubscription(mask, printer, job, recipient, 0);
6215
6216 if (job)
6217 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d",
6218 sub->id, job->id);
6219 else if (printer)
6220 cupsdLogMessage(CUPSD_LOG_DEBUG,
6221 "Added subscription %d for printer \"%s\"",
6222 sub->id, printer->name);
6223 else
6224 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for server",
6225 sub->id);
6226
6227 sub->interval = interval;
6228 sub->lease = lease;
6229 sub->expire = lease ? time(NULL) + lease : 0;
6230
6231 cupsdSetString(&sub->owner, username);
6232
6233 if (user_data)
6234 {
6235 sub->user_data_len = user_data->values[0].unknown.length;
6236 memcpy(sub->user_data, user_data->values[0].unknown.data,
6237 sub->user_data_len);
6238 }
6239
6240 ippAddSeparator(con->response);
6241 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
6242 "notify-subscription-id", sub->id);
6243
6244 con->response->request.status.status_code = IPP_OK;
6245
6246 if (attr)
6247 attr = attr->next;
6248 }
6249
6250 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
6251 }
6252
6253
6254 /*
6255 * 'delete_printer()' - Remove a printer or class from the system.
6256 */
6257
6258 static void
6259 delete_printer(cupsd_client_t *con, /* I - Client connection */
6260 ipp_attribute_t *uri) /* I - URI of printer or class */
6261 {
6262 http_status_t status; /* Policy status */
6263 cups_ptype_t dtype; /* Destination type (printer/class) */
6264 cupsd_printer_t *printer; /* Printer/class */
6265 char filename[1024]; /* Script/PPD filename */
6266
6267
6268 cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", con,
6269 con->http.fd, uri->values[0].string.text);
6270
6271 /*
6272 * Do we have a valid URI?
6273 */
6274
6275 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
6276 {
6277 /*
6278 * Bad URI...
6279 */
6280
6281 send_ipp_status(con, IPP_NOT_FOUND,
6282 _("The printer or class was not found."));
6283 return;
6284 }
6285
6286 /*
6287 * Check policy...
6288 */
6289
6290 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6291 {
6292 send_http_error(con, status, NULL);
6293 return;
6294 }
6295
6296 /*
6297 * Remove old jobs...
6298 */
6299
6300 cupsdCancelJobs(printer->name, NULL, 1);
6301
6302 /*
6303 * Remove old subscriptions and send a "deleted printer" event...
6304 */
6305
6306 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
6307 "%s \"%s\" deleted by \"%s\".",
6308 (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
6309 printer->name, get_username(con));
6310
6311 cupsdExpireSubscriptions(printer, NULL);
6312
6313 /*
6314 * Remove any old PPD or script files...
6315 */
6316
6317 snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot,
6318 printer->name);
6319 unlink(filename);
6320
6321 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
6322 printer->name);
6323 unlink(filename);
6324
6325 #ifdef __APPLE__
6326 /*
6327 * Unregister color profiles...
6328 */
6329
6330 apple_unregister_profiles(printer);
6331 #endif /* __APPLE__ */
6332
6333 if (dtype & CUPS_PRINTER_CLASS)
6334 {
6335 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".",
6336 printer->name, get_username(con));
6337
6338 cupsdDeletePrinter(printer, 0);
6339 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
6340 }
6341 else
6342 {
6343 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".",
6344 printer->name, get_username(con));
6345
6346 cupsdDeletePrinter(printer, 0);
6347 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
6348 }
6349
6350 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
6351
6352 /*
6353 * Return with no errors...
6354 */
6355
6356 con->response->request.status.status_code = IPP_OK;
6357 }
6358
6359
6360 /*
6361 * 'get_default()' - Get the default destination.
6362 */
6363
6364 static void
6365 get_default(cupsd_client_t *con) /* I - Client connection */
6366 {
6367 http_status_t status; /* Policy status */
6368 cups_array_t *ra; /* Requested attributes array */
6369
6370
6371 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->http.fd);
6372
6373 /*
6374 * Check policy...
6375 */
6376
6377 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6378 {
6379 send_http_error(con, status, NULL);
6380 return;
6381 }
6382
6383 if (DefaultPrinter)
6384 {
6385 ra = create_requested_array(con->request);
6386
6387 copy_printer_attrs(con, DefaultPrinter, ra);
6388
6389 cupsArrayDelete(ra);
6390
6391 con->response->request.status.status_code = IPP_OK;
6392 }
6393 else
6394 send_ipp_status(con, IPP_NOT_FOUND, _("No default printer"));
6395 }
6396
6397
6398 /*
6399 * 'get_devices()' - Get the list of available devices on the local system.
6400 */
6401
6402 static void
6403 get_devices(cupsd_client_t *con) /* I - Client connection */
6404 {
6405 http_status_t status; /* Policy status */
6406 ipp_attribute_t *limit, /* limit attribute */
6407 *timeout, /* timeout attribute */
6408 *requested, /* requested-attributes attribute */
6409 *exclude; /* exclude-schemes attribute */
6410 char command[1024], /* cups-deviced command */
6411 options[1024], /* Options to pass to command */
6412 requested_str[256],
6413 /* String for requested attributes */
6414 exclude_str[512];
6415 /* String for excluded attributes */
6416
6417
6418 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->http.fd);
6419
6420 /*
6421 * Check policy...
6422 */
6423
6424 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6425 {
6426 send_http_error(con, status, NULL);
6427 return;
6428 }
6429
6430 /*
6431 * Run cups-deviced command with the given options...
6432 */
6433
6434 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
6435 timeout = ippFindAttribute(con->request, "timeout", IPP_TAG_INTEGER);
6436 requested = ippFindAttribute(con->request, "requested-attributes",
6437 IPP_TAG_KEYWORD);
6438 exclude = ippFindAttribute(con->request, "exclude-schemes", IPP_TAG_NAME);
6439
6440 if (requested)
6441 url_encode_attr(requested, requested_str, sizeof(requested_str));
6442 else
6443 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
6444
6445 if (exclude)
6446 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
6447 else
6448 exclude_str[0] = '\0';
6449
6450 snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
6451 snprintf(options, sizeof(options),
6452 "%d+%d+%d+%d+%s%s%s",
6453 con->request->request.op.request_id,
6454 limit ? limit->values[0].integer : 0,
6455 timeout ? timeout->values[0].integer : 10,
6456 (int)User,
6457 requested_str,
6458 exclude_str[0] ? "%20" : "", exclude_str);
6459
6460 if (cupsdSendCommand(con, command, options, 1))
6461 {
6462 /*
6463 * Command started successfully, don't send an IPP response here...
6464 */
6465
6466 ippDelete(con->response);
6467 con->response = NULL;
6468 }
6469 else
6470 {
6471 /*
6472 * Command failed, return "internal error" so the user knows something
6473 * went wrong...
6474 */
6475
6476 send_ipp_status(con, IPP_INTERNAL_ERROR,
6477 _("cups-deviced failed to execute."));
6478 }
6479 }
6480
6481
6482 /*
6483 * 'get_document()' - Get a copy of a job file.
6484 */
6485
6486 static void
6487 get_document(cupsd_client_t *con, /* I - Client connection */
6488 ipp_attribute_t *uri) /* I - Job URI */
6489 {
6490 http_status_t status; /* Policy status */
6491 ipp_attribute_t *attr; /* Current attribute */
6492 int jobid; /* Job ID */
6493 int docnum; /* Document number */
6494 cupsd_job_t *job; /* Current job */
6495 char method[HTTP_MAX_URI], /* Method portion of URI */
6496 username[HTTP_MAX_URI], /* Username portion of URI */
6497 host[HTTP_MAX_URI], /* Host portion of URI */
6498 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6499 int port; /* Port portion of URI */
6500 char filename[1024], /* Filename for document */
6501 format[1024]; /* Format for document */
6502
6503
6504 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", con,
6505 con->http.fd, uri->values[0].string.text);
6506
6507 /*
6508 * See if we have a job URI or a printer URI...
6509 */
6510
6511 if (!strcmp(uri->name, "printer-uri"))
6512 {
6513 /*
6514 * Got a printer URI; see if we also have a job-id attribute...
6515 */
6516
6517 if ((attr = ippFindAttribute(con->request, "job-id",
6518 IPP_TAG_INTEGER)) == NULL)
6519 {
6520 send_ipp_status(con, IPP_BAD_REQUEST,
6521 _("Got a printer-uri attribute but no job-id!"));
6522 return;
6523 }
6524
6525 jobid = attr->values[0].integer;
6526 }
6527 else
6528 {
6529 /*
6530 * Got a job URI; parse it to get the job ID...
6531 */
6532
6533 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
6534 sizeof(method), username, sizeof(username), host,
6535 sizeof(host), &port, resource, sizeof(resource));
6536
6537 if (strncmp(resource, "/jobs/", 6))
6538 {
6539 /*
6540 * Not a valid URI!
6541 */
6542
6543 send_ipp_status(con, IPP_BAD_REQUEST,
6544 _("Bad job-uri attribute \"%s\"!"),
6545 uri->values[0].string.text);
6546 return;
6547 }
6548
6549 jobid = atoi(resource + 6);
6550 }
6551
6552 /*
6553 * See if the job exists...
6554 */
6555
6556 if ((job = cupsdFindJob(jobid)) == NULL)
6557 {
6558 /*
6559 * Nope - return a "not found" error...
6560 */
6561
6562 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
6563 return;
6564 }
6565
6566 /*
6567 * Check policy...
6568 */
6569
6570 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6571 {
6572 send_http_error(con, status, NULL);
6573 return;
6574 }
6575
6576 /*
6577 * Get the document number...
6578 */
6579
6580 if ((attr = ippFindAttribute(con->request, "document-number",
6581 IPP_TAG_INTEGER)) == NULL)
6582 {
6583 send_ipp_status(con, IPP_BAD_REQUEST,
6584 _("Missing document-number attribute!"));
6585 return;
6586 }
6587
6588 if ((docnum = attr->values[0].integer) < 1 || docnum > job->num_files ||
6589 attr->num_values > 1)
6590 {
6591 send_ipp_status(con, IPP_NOT_FOUND, _("Document %d not found in job %d."),
6592 docnum, jobid);
6593 return;
6594 }
6595
6596 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, jobid,
6597 docnum);
6598 if ((con->file = open(filename, O_RDONLY)) == -1)
6599 {
6600 cupsdLogMessage(CUPSD_LOG_ERROR,
6601 "Unable to open document %d in job %d - %s", docnum, jobid,
6602 strerror(errno));
6603 send_ipp_status(con, IPP_NOT_FOUND,
6604 _("Unable to open document %d in job %d!"), docnum, jobid);
6605 return;
6606 }
6607
6608 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
6609
6610 cupsdLoadJob(job);
6611
6612 snprintf(format, sizeof(format), "%s/%s", job->filetypes[docnum - 1]->super,
6613 job->filetypes[docnum - 1]->type);
6614
6615 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
6616 NULL, format);
6617 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "document-number",
6618 docnum);
6619 if ((attr = ippFindAttribute(job->attrs, "document-name",
6620 IPP_TAG_NAME)) != NULL)
6621 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "document-name",
6622 NULL, attr->values[0].string.text);
6623 }
6624
6625
6626 /*
6627 * 'get_job_attrs()' - Get job attributes.
6628 */
6629
6630 static void
6631 get_job_attrs(cupsd_client_t *con, /* I - Client connection */
6632 ipp_attribute_t *uri) /* I - Job URI */
6633 {
6634 http_status_t status; /* Policy status */
6635 ipp_attribute_t *attr; /* Current attribute */
6636 int jobid; /* Job ID */
6637 cupsd_job_t *job; /* Current job */
6638 char method[HTTP_MAX_URI], /* Method portion of URI */
6639 username[HTTP_MAX_URI], /* Username portion of URI */
6640 host[HTTP_MAX_URI], /* Host portion of URI */
6641 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6642 int port; /* Port portion of URI */
6643 cups_array_t *ra; /* Requested attributes array */
6644
6645
6646 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", con,
6647 con->http.fd, uri->values[0].string.text);
6648
6649 /*
6650 * See if we have a job URI or a printer URI...
6651 */
6652
6653 if (!strcmp(uri->name, "printer-uri"))
6654 {
6655 /*
6656 * Got a printer URI; see if we also have a job-id attribute...
6657 */
6658
6659 if ((attr = ippFindAttribute(con->request, "job-id",
6660 IPP_TAG_INTEGER)) == NULL)
6661 {
6662 send_ipp_status(con, IPP_BAD_REQUEST,
6663 _("Got a printer-uri attribute but no job-id!"));
6664 return;
6665 }
6666
6667 jobid = attr->values[0].integer;
6668 }
6669 else
6670 {
6671 /*
6672 * Got a job URI; parse it to get the job ID...
6673 */
6674
6675 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
6676 sizeof(method), username, sizeof(username), host,
6677 sizeof(host), &port, resource, sizeof(resource));
6678
6679 if (strncmp(resource, "/jobs/", 6))
6680 {
6681 /*
6682 * Not a valid URI!
6683 */
6684
6685 send_ipp_status(con, IPP_BAD_REQUEST,
6686 _("Bad job-uri attribute \"%s\"!"),
6687 uri->values[0].string.text);
6688 return;
6689 }
6690
6691 jobid = atoi(resource + 6);
6692 }
6693
6694 /*
6695 * See if the job exists...
6696 */
6697
6698 if ((job = cupsdFindJob(jobid)) == NULL)
6699 {
6700 /*
6701 * Nope - return a "not found" error...
6702 */
6703
6704 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
6705 return;
6706 }
6707
6708 /*
6709 * Check policy...
6710 */
6711
6712 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6713 {
6714 send_http_error(con, status, NULL);
6715 return;
6716 }
6717
6718 /*
6719 * Copy attributes...
6720 */
6721
6722 cupsdLoadJob(job);
6723
6724 ra = create_requested_array(con->request);
6725 copy_job_attrs(con, job, ra);
6726 cupsArrayDelete(ra);
6727
6728 con->response->request.status.status_code = IPP_OK;
6729 }
6730
6731
6732 /*
6733 * 'get_jobs()' - Get a list of jobs for the specified printer.
6734 */
6735
6736 static void
6737 get_jobs(cupsd_client_t *con, /* I - Client connection */
6738 ipp_attribute_t *uri) /* I - Printer URI */
6739 {
6740 http_status_t status; /* Policy status */
6741 ipp_attribute_t *attr; /* Current attribute */
6742 const char *dest; /* Destination */
6743 cups_ptype_t dtype; /* Destination type (printer/class) */
6744 cups_ptype_t dmask; /* Destination type mask */
6745 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
6746 username[HTTP_MAX_URI], /* Username portion of URI */
6747 host[HTTP_MAX_URI], /* Host portion of URI */
6748 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6749 int port; /* Port portion of URI */
6750 int completed; /* Completed jobs? */
6751 int first_job_id; /* First job ID */
6752 int limit; /* Maximum number of jobs to return */
6753 int count; /* Number of jobs that match */
6754 cupsd_job_t *job; /* Current job pointer */
6755 cupsd_printer_t *printer; /* Printer */
6756 cups_array_t *list; /* Which job list... */
6757 cups_array_t *ra; /* Requested attributes array */
6758
6759
6760 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", con, con->http.fd,
6761 uri->values[0].string.text);
6762
6763 /*
6764 * Is the destination valid?
6765 */
6766
6767 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6768 sizeof(scheme), username, sizeof(username), host,
6769 sizeof(host), &port, resource, sizeof(resource));
6770
6771 if (!strcmp(resource, "/") ||
6772 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6))
6773 {
6774 dest = NULL;
6775 dtype = (cups_ptype_t)0;
6776 dmask = (cups_ptype_t)0;
6777 printer = NULL;
6778 }
6779 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
6780 {
6781 dest = NULL;
6782 dtype = (cups_ptype_t)0;
6783 dmask = CUPS_PRINTER_CLASS;
6784 printer = NULL;
6785 }
6786 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
6787 {
6788 dest = NULL;
6789 dtype = CUPS_PRINTER_CLASS;
6790 dmask = CUPS_PRINTER_CLASS;
6791 printer = NULL;
6792 }
6793 else if ((dest = cupsdValidateDest(uri->values[0].string.text, &dtype,
6794 &printer)) == NULL)
6795 {
6796 /*
6797 * Bad URI...
6798 */
6799
6800 send_ipp_status(con, IPP_NOT_FOUND,
6801 _("The printer or class was not found."));
6802 return;
6803 }
6804 else
6805 {
6806 dtype &= CUPS_PRINTER_CLASS;
6807 dmask = CUPS_PRINTER_CLASS;
6808 }
6809
6810 /*
6811 * Check policy...
6812 */
6813
6814 if (printer)
6815 {
6816 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
6817 NULL)) != HTTP_OK)
6818 {
6819 send_http_error(con, status, printer);
6820 return;
6821 }
6822 }
6823 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6824 {
6825 send_http_error(con, status, NULL);
6826 return;
6827 }
6828
6829 /*
6830 * See if the "which-jobs" attribute have been specified...
6831 */
6832
6833 if ((attr = ippFindAttribute(con->request, "which-jobs",
6834 IPP_TAG_KEYWORD)) != NULL &&
6835 !strcmp(attr->values[0].string.text, "completed"))
6836 {
6837 completed = 1;
6838 list = Jobs;
6839 }
6840 else if (attr && !strcmp(attr->values[0].string.text, "all"))
6841 {
6842 completed = 0;
6843 list = Jobs;
6844 }
6845 else if (attr && !strcmp(attr->values[0].string.text, "printing"))
6846 {
6847 completed = 0;
6848 list = PrintingJobs;
6849 }
6850 else
6851 {
6852 completed = 0;
6853 list = ActiveJobs;
6854 }
6855
6856 /*
6857 * See if they want to limit the number of jobs reported...
6858 */
6859
6860 if ((attr = ippFindAttribute(con->request, "limit",
6861 IPP_TAG_INTEGER)) != NULL)
6862 limit = attr->values[0].integer;
6863 else
6864 limit = 0;
6865
6866 if ((attr = ippFindAttribute(con->request, "first-job-id",
6867 IPP_TAG_INTEGER)) != NULL)
6868 first_job_id = attr->values[0].integer;
6869 else
6870 first_job_id = 1;
6871
6872 /*
6873 * See if we only want to see jobs for a specific user...
6874 */
6875
6876 if ((attr = ippFindAttribute(con->request, "my-jobs",
6877 IPP_TAG_BOOLEAN)) != NULL &&
6878 attr->values[0].boolean)
6879 strlcpy(username, get_username(con), sizeof(username));
6880 else
6881 username[0] = '\0';
6882
6883 ra = create_requested_array(con->request);
6884
6885 /*
6886 * OK, build a list of jobs for this printer...
6887 */
6888
6889 for (count = 0, job = (cupsd_job_t *)cupsArrayFirst(list);
6890 (limit <= 0 || count < limit) && job;
6891 job = (cupsd_job_t *)cupsArrayNext(list))
6892 {
6893 /*
6894 * Filter out jobs that don't match...
6895 */
6896
6897 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: job->id = %d", job->id);
6898
6899 if (!job->dest || !job->username)
6900 cupsdLoadJob(job);
6901
6902 if (!job->dest || !job->username)
6903 continue;
6904
6905 if ((dest && strcmp(job->dest, dest)) &&
6906 (!job->printer || !dest || strcmp(job->printer->name, dest)))
6907 continue;
6908 if ((job->dtype & dmask) != dtype &&
6909 (!job->printer || (job->printer->type & dmask) != dtype))
6910 continue;
6911 if (completed && job->state_value <= IPP_JOB_STOPPED)
6912 continue;
6913
6914 if (job->id < first_job_id)
6915 continue;
6916
6917 cupsdLoadJob(job);
6918
6919 if (!job->attrs)
6920 continue;
6921
6922 if (username[0] && strcasecmp(username, job->username))
6923 continue;
6924
6925 if (count > 0)
6926 ippAddSeparator(con->response);
6927
6928 count ++;
6929
6930 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count = %d", count);
6931
6932 copy_job_attrs(con, job, ra);
6933 }
6934
6935 cupsArrayDelete(ra);
6936
6937 con->response->request.status.status_code = IPP_OK;
6938 }
6939
6940
6941 /*
6942 * 'get_notifications()' - Get events for a subscription.
6943 */
6944
6945 static void
6946 get_notifications(cupsd_client_t *con) /* I - Client connection */
6947 {
6948 int i, j; /* Looping vars */
6949 http_status_t status; /* Policy status */
6950 cupsd_subscription_t *sub; /* Subscription */
6951 ipp_attribute_t *ids, /* notify-subscription-ids */
6952 *sequences; /* notify-sequence-numbers */
6953 int min_seq; /* Minimum sequence number */
6954 int interval; /* Poll interval */
6955
6956
6957 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_notifications(con=%p[%d])",
6958 con, con->http.fd);
6959
6960 /*
6961 * Get subscription attributes...
6962 */
6963
6964 ids = ippFindAttribute(con->request, "notify-subscription-ids",
6965 IPP_TAG_INTEGER);
6966 sequences = ippFindAttribute(con->request, "notify-sequence-numbers",
6967 IPP_TAG_INTEGER);
6968
6969 if (!ids)
6970 {
6971 send_ipp_status(con, IPP_BAD_REQUEST,
6972 _("Missing notify-subscription-ids attribute!"));
6973 return;
6974 }
6975
6976 /*
6977 * Are the subscription IDs valid?
6978 */
6979
6980 for (i = 0, interval = 60; i < ids->num_values; i ++)
6981 {
6982 if ((sub = cupsdFindSubscription(ids->values[i].integer)) == NULL)
6983 {
6984 /*
6985 * Bad subscription ID...
6986 */
6987
6988 send_ipp_status(con, IPP_NOT_FOUND,
6989 _("notify-subscription-id %d no good!"),
6990 ids->values[i].integer);
6991 return;
6992 }
6993
6994 /*
6995 * Check policy...
6996 */
6997
6998 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
6999 DefaultPolicyPtr,
7000 con, sub->owner)) != HTTP_OK)
7001 {
7002 send_http_error(con, status, sub->dest);
7003 return;
7004 }
7005
7006 /*
7007 * Check the subscription type and update the interval accordingly.
7008 */
7009
7010 if (sub->job && sub->job->state_value == IPP_JOB_PROCESSING &&
7011 interval > 10)
7012 interval = 10;
7013 else if (sub->job && sub->job->state_value >= IPP_JOB_STOPPED)
7014 interval = 0;
7015 else if (sub->dest && sub->dest->state == IPP_PRINTER_PROCESSING &&
7016 interval > 30)
7017 interval = 30;
7018 }
7019
7020 /*
7021 * Tell the client to poll again in N seconds...
7022 */
7023
7024 if (interval > 0)
7025 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
7026 "notify-get-interval", interval);
7027
7028 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
7029 "printer-up-time", time(NULL));
7030
7031 /*
7032 * Copy the subscription event attributes to the response.
7033 */
7034
7035 con->response->request.status.status_code =
7036 interval ? IPP_OK : IPP_OK_EVENTS_COMPLETE;
7037
7038 for (i = 0; i < ids->num_values; i ++)
7039 {
7040 /*
7041 * Get the subscription and sequence number...
7042 */
7043
7044 sub = cupsdFindSubscription(ids->values[i].integer);
7045
7046 if (sequences && i < sequences->num_values)
7047 min_seq = sequences->values[i].integer;
7048 else
7049 min_seq = 1;
7050
7051 /*
7052 * If we don't have any new events, nothing to do here...
7053 */
7054
7055 if (min_seq > (sub->first_event_id + sub->num_events))
7056 continue;
7057
7058 /*
7059 * Otherwise copy all of the new events...
7060 */
7061
7062 if (sub->first_event_id > min_seq)
7063 j = 0;
7064 else
7065 j = min_seq - sub->first_event_id;
7066
7067 for (; j < sub->num_events; j ++)
7068 {
7069 ippAddSeparator(con->response);
7070
7071 copy_attrs(con->response, sub->events[j]->attrs, NULL,
7072 IPP_TAG_EVENT_NOTIFICATION, 0);
7073 }
7074 }
7075 }
7076
7077
7078 /*
7079 * 'get_ppd()' - Get a named PPD from the local system.
7080 */
7081
7082 static void
7083 get_ppd(cupsd_client_t *con, /* I - Client connection */
7084 ipp_attribute_t *uri) /* I - Printer URI or PPD name */
7085 {
7086 http_status_t status; /* Policy status */
7087 cupsd_printer_t *dest; /* Destination */
7088 cups_ptype_t dtype; /* Destination type */
7089
7090
7091 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppd(%p[%d], %p[%s=%s])", con,
7092 con->http.fd, uri, uri->name, uri->values[0].string.text);
7093
7094 if (!strcmp(uri->name, "ppd-name"))
7095 {
7096 /*
7097 * Return a PPD file from cups-driverd...
7098 */
7099
7100 char command[1024], /* cups-driverd command */
7101 options[1024], /* Options to pass to command */
7102 ppd_name[1024]; /* ppd-name */
7103
7104
7105 /*
7106 * Check policy...
7107 */
7108
7109 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7110 {
7111 send_http_error(con, status, NULL);
7112 return;
7113 }
7114
7115 /*
7116 * Run cups-driverd command with the given options...
7117 */
7118
7119 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
7120 url_encode_string(uri->values[0].string.text, ppd_name, sizeof(ppd_name));
7121 snprintf(options, sizeof(options), "get+%d+%s",
7122 con->request->request.op.request_id, ppd_name);
7123
7124 if (cupsdSendCommand(con, command, options, 0))
7125 {
7126 /*
7127 * Command started successfully, don't send an IPP response here...
7128 */
7129
7130 ippDelete(con->response);
7131 con->response = NULL;
7132 }
7133 else
7134 {
7135 /*
7136 * Command failed, return "internal error" so the user knows something
7137 * went wrong...
7138 */
7139
7140 send_ipp_status(con, IPP_INTERNAL_ERROR,
7141 _("cups-driverd failed to execute."));
7142 }
7143 }
7144 else if (!strcmp(uri->name, "printer-uri") &&
7145 cupsdValidateDest(uri->values[0].string.text, &dtype, &dest))
7146 {
7147 int i; /* Looping var */
7148 char filename[1024]; /* PPD filename */
7149
7150
7151 /*
7152 * Check policy...
7153 */
7154
7155 if ((status = cupsdCheckPolicy(dest->op_policy_ptr, con, NULL)) != HTTP_OK)
7156 {
7157 send_http_error(con, status, dest);
7158 return;
7159 }
7160
7161 /*
7162 * See if we need the PPD for a class or remote printer...
7163 */
7164
7165 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
7166 dest->name);
7167
7168 if ((dtype & CUPS_PRINTER_REMOTE) && access(filename, 0))
7169 {
7170 con->response->request.status.status_code = CUPS_SEE_OTHER;
7171 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
7172 "printer-uri", NULL, dest->uri);
7173 return;
7174 }
7175 else if (dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
7176 {
7177 for (i = 0; i < dest->num_printers; i ++)
7178 if (!(dest->printers[i]->type &
7179 (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)))
7180 {
7181 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
7182 dest->printers[i]->name);
7183
7184 if (!access(filename, 0))
7185 break;
7186 }
7187
7188 if (i < dest->num_printers)
7189 dest = dest->printers[i];
7190 else
7191 {
7192 con->response->request.status.status_code = CUPS_SEE_OTHER;
7193 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
7194 "printer-uri", NULL, dest->printers[0]->uri);
7195 return;
7196 }
7197 }
7198
7199 /*
7200 * Found the printer with the PPD file, now see if there is one...
7201 */
7202
7203 if ((con->file = open(filename, O_RDONLY)) < 0)
7204 {
7205 send_ipp_status(con, IPP_NOT_FOUND,
7206 _("The PPD file \"%s\" could not be opened: %s"),
7207 uri->values[0].string.text, strerror(errno));
7208 return;
7209 }
7210
7211 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
7212
7213 con->pipe_pid = 0;
7214
7215 con->response->request.status.status_code = IPP_OK;
7216 }
7217 else
7218 send_ipp_status(con, IPP_NOT_FOUND,
7219 _("The PPD file \"%s\" could not be found."),
7220 uri->values[0].string.text);
7221 }
7222
7223
7224 /*
7225 * 'get_ppds()' - Get the list of PPD files on the local system.
7226 */
7227
7228 static void
7229 get_ppds(cupsd_client_t *con) /* I - Client connection */
7230 {
7231 http_status_t status; /* Policy status */
7232 ipp_attribute_t *limit, /* Limit attribute */
7233 *device, /* ppd-device-id attribute */
7234 *language, /* ppd-natural-language attribute */
7235 *make, /* ppd-make attribute */
7236 *model, /* ppd-make-and-model attribute */
7237 *model_number, /* ppd-model-number attribute */
7238 *product, /* ppd-product attribute */
7239 *psversion, /* ppd-psverion attribute */
7240 *type, /* ppd-type attribute */
7241 *requested; /* requested-attributes attribute */
7242 char command[1024], /* cups-driverd command */
7243 options[1024], /* Options to pass to command */
7244 device_str[256],/* Escaped ppd-device-id string */
7245 language_str[256],
7246 /* Escaped ppd-natural-language */
7247 make_str[256], /* Escaped ppd-make string */
7248 model_str[256], /* Escaped ppd-make-and-model string */
7249 model_number_str[256],
7250 /* ppd-model-number string */
7251 product_str[256],
7252 /* Escaped ppd-product string */
7253 psversion_str[256],
7254 /* Escaped ppd-psversion string */
7255 type_str[256], /* Escaped ppd-type string */
7256 requested_str[256];
7257 /* String for requested attributes */
7258
7259
7260 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->http.fd);
7261
7262 /*
7263 * Check policy...
7264 */
7265
7266 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7267 {
7268 send_http_error(con, status, NULL);
7269 return;
7270 }
7271
7272 /*
7273 * Run cups-driverd command with the given options...
7274 */
7275
7276 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
7277 device = ippFindAttribute(con->request, "ppd-device-id", IPP_TAG_TEXT);
7278 language = ippFindAttribute(con->request, "ppd-natural-language",
7279 IPP_TAG_LANGUAGE);
7280 make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
7281 model = ippFindAttribute(con->request, "ppd-make-and-model",
7282 IPP_TAG_TEXT);
7283 model_number = ippFindAttribute(con->request, "ppd-model-number",
7284 IPP_TAG_INTEGER);
7285 product = ippFindAttribute(con->request, "ppd-product", IPP_TAG_TEXT);
7286 psversion = ippFindAttribute(con->request, "ppd-psversion", IPP_TAG_TEXT);
7287 type = ippFindAttribute(con->request, "ppd-type", IPP_TAG_KEYWORD);
7288 requested = ippFindAttribute(con->request, "requested-attributes",
7289 IPP_TAG_KEYWORD);
7290
7291 if (requested)
7292 url_encode_attr(requested, requested_str, sizeof(requested_str));
7293 else
7294 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
7295
7296 if (device)
7297 url_encode_attr(device, device_str, sizeof(device_str));
7298 else
7299 device_str[0] = '\0';
7300
7301 if (language)
7302 url_encode_attr(language, language_str, sizeof(language_str));
7303 else
7304 language_str[0] = '\0';
7305
7306 if (make)
7307 url_encode_attr(make, make_str, sizeof(make_str));
7308 else
7309 make_str[0] = '\0';
7310
7311 if (model)
7312 url_encode_attr(model, model_str, sizeof(model_str));
7313 else
7314 model_str[0] = '\0';
7315
7316 if (model_number)
7317 snprintf(model_number_str, sizeof(model_number_str), "ppd-model-number=%d",
7318 model_number->values[0].integer);
7319 else
7320 model_number_str[0] = '\0';
7321
7322 if (product)
7323 url_encode_attr(product, product_str, sizeof(product_str));
7324 else
7325 product_str[0] = '\0';
7326
7327 if (psversion)
7328 url_encode_attr(psversion, psversion_str, sizeof(psversion_str));
7329 else
7330 psversion_str[0] = '\0';
7331
7332 if (type)
7333 url_encode_attr(type, type_str, sizeof(type_str));
7334 else
7335 type_str[0] = '\0';
7336
7337 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
7338 snprintf(options, sizeof(options),
7339 "list+%d+%d+%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
7340 con->request->request.op.request_id,
7341 limit ? limit->values[0].integer : 0,
7342 requested_str,
7343 device ? "%20" : "", device_str,
7344 language ? "%20" : "", language_str,
7345 make ? "%20" : "", make_str,
7346 model ? "%20" : "", model_str,
7347 model_number ? "%20" : "", model_number_str,
7348 product ? "%20" : "", product_str,
7349 psversion ? "%20" : "", psversion_str,
7350 type ? "%20" : "", type_str);
7351
7352 if (cupsdSendCommand(con, command, options, 0))
7353 {
7354 /*
7355 * Command started successfully, don't send an IPP response here...
7356 */
7357
7358 ippDelete(con->response);
7359 con->response = NULL;
7360 }
7361 else
7362 {
7363 /*
7364 * Command failed, return "internal error" so the user knows something
7365 * went wrong...
7366 */
7367
7368 send_ipp_status(con, IPP_INTERNAL_ERROR,
7369 _("cups-driverd failed to execute."));
7370 }
7371 }
7372
7373
7374 /*
7375 * 'get_printer_attrs()' - Get printer attributes.
7376 */
7377
7378 static void
7379 get_printer_attrs(cupsd_client_t *con, /* I - Client connection */
7380 ipp_attribute_t *uri) /* I - Printer URI */
7381 {
7382 http_status_t status; /* Policy status */
7383 cups_ptype_t dtype; /* Destination type (printer/class) */
7384 cupsd_printer_t *printer; /* Printer/class */
7385 cups_array_t *ra; /* Requested attributes array */
7386
7387
7388 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", con,
7389 con->http.fd, uri->values[0].string.text);
7390
7391 /*
7392 * Is the destination valid?
7393 */
7394
7395 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7396 {
7397 /*
7398 * Bad URI...
7399 */
7400
7401 send_ipp_status(con, IPP_NOT_FOUND,
7402 _("The printer or class was not found."));
7403 return;
7404 }
7405
7406 /*
7407 * Check policy...
7408 */
7409
7410 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7411 {
7412 send_http_error(con, status, printer);
7413 return;
7414 }
7415
7416 /*
7417 * Send the attributes...
7418 */
7419
7420 ra = create_requested_array(con->request);
7421
7422 copy_printer_attrs(con, printer, ra);
7423
7424 cupsArrayDelete(ra);
7425
7426 con->response->request.status.status_code = IPP_OK;
7427 }
7428
7429
7430 /*
7431 * 'get_printers()' - Get a list of printers or classes.
7432 */
7433
7434 static void
7435 get_printers(cupsd_client_t *con, /* I - Client connection */
7436 int type) /* I - 0 or CUPS_PRINTER_CLASS */
7437 {
7438 http_status_t status; /* Policy status */
7439 ipp_attribute_t *attr; /* Current attribute */
7440 int limit; /* Max number of printers to return */
7441 int count; /* Number of printers that match */
7442 cupsd_printer_t *printer; /* Current printer pointer */
7443 int printer_type, /* printer-type attribute */
7444 printer_mask; /* printer-type-mask attribute */
7445 char *location; /* Location string */
7446 const char *username; /* Current user */
7447 char *first_printer_name; /* first-printer-name attribute */
7448 cups_array_t *ra; /* Requested attributes array */
7449
7450
7451 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con,
7452 con->http.fd, type);
7453
7454 /*
7455 * Check policy...
7456 */
7457
7458 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7459 {
7460 send_http_error(con, status, NULL);
7461 return;
7462 }
7463
7464 /*
7465 * Check for printers...
7466 */
7467
7468 if (!Printers || !cupsArrayCount(Printers))
7469 {
7470 send_ipp_status(con, IPP_NOT_FOUND, _("No destinations added."));
7471 return;
7472 }
7473
7474 /*
7475 * See if they want to limit the number of printers reported...
7476 */
7477
7478 if ((attr = ippFindAttribute(con->request, "limit",
7479 IPP_TAG_INTEGER)) != NULL)
7480 limit = attr->values[0].integer;
7481 else
7482 limit = 10000000;
7483
7484 if ((attr = ippFindAttribute(con->request, "first-printer-name",
7485 IPP_TAG_NAME)) != NULL)
7486 first_printer_name = attr->values[0].string.text;
7487 else
7488 first_printer_name = NULL;
7489
7490 /*
7491 * Support filtering...
7492 */
7493
7494 if ((attr = ippFindAttribute(con->request, "printer-type",
7495 IPP_TAG_ENUM)) != NULL)
7496 printer_type = attr->values[0].integer;
7497 else
7498 printer_type = 0;
7499
7500 if ((attr = ippFindAttribute(con->request, "printer-type-mask",
7501 IPP_TAG_ENUM)) != NULL)
7502 printer_mask = attr->values[0].integer;
7503 else
7504 printer_mask = 0;
7505
7506 if ((attr = ippFindAttribute(con->request, "printer-location",
7507 IPP_TAG_TEXT)) != NULL)
7508 location = attr->values[0].string.text;
7509 else
7510 location = NULL;
7511
7512 if (con->username[0])
7513 username = con->username;
7514 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7515 IPP_TAG_NAME)) != NULL)
7516 username = attr->values[0].string.text;
7517 else
7518 username = NULL;
7519
7520 ra = create_requested_array(con->request);
7521
7522 /*
7523 * OK, build a list of printers for this printer...
7524 */
7525
7526 if (first_printer_name)
7527 {
7528 if ((printer = cupsdFindDest(first_printer_name)) == NULL)
7529 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
7530 }
7531 else
7532 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
7533
7534 for (count = 0;
7535 count < limit && printer;
7536 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
7537 {
7538 if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
7539 (printer->type & printer_mask) == printer_type &&
7540 (!location || !printer->location ||
7541 !strcasecmp(printer->location, location)))
7542 {
7543 /*
7544 * If HideImplicitMembers is enabled, see if this printer or class
7545 * is a member of an implicit class...
7546 */
7547
7548 if (ImplicitClasses && HideImplicitMembers &&
7549 printer->in_implicit_class)
7550 continue;
7551
7552 /*
7553 * If a username is specified, see if it is allowed or denied
7554 * access...
7555 */
7556
7557 if (!(printer->type & CUPS_PRINTER_AUTHENTICATED) &&
7558 printer->num_users && username && !user_allowed(printer, username))
7559 continue;
7560
7561 /*
7562 * Add the group separator as needed...
7563 */
7564
7565 if (count > 0)
7566 ippAddSeparator(con->response);
7567
7568 count ++;
7569
7570 /*
7571 * Send the attributes...
7572 */
7573
7574 copy_printer_attrs(con, printer, ra);
7575 }
7576 }
7577
7578 cupsArrayDelete(ra);
7579
7580 con->response->request.status.status_code = IPP_OK;
7581 }
7582
7583
7584 /*
7585 * 'get_subscription_attrs()' - Get subscription attributes.
7586 */
7587
7588 static void
7589 get_subscription_attrs(
7590 cupsd_client_t *con, /* I - Client connection */
7591 int sub_id) /* I - Subscription ID */
7592 {
7593 http_status_t status; /* Policy status */
7594 cupsd_subscription_t *sub; /* Subscription */
7595 cups_array_t *ra; /* Requested attributes array */
7596
7597
7598 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7599 "get_subscription_attrs(con=%p[%d], sub_id=%d)",
7600 con, con->http.fd, sub_id);
7601
7602 /*
7603 * Is the subscription ID valid?
7604 */
7605
7606 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
7607 {
7608 /*
7609 * Bad subscription ID...
7610 */
7611
7612 send_ipp_status(con, IPP_NOT_FOUND,
7613 _("notify-subscription-id %d no good!"), sub_id);
7614 return;
7615 }
7616
7617 /*
7618 * Check policy...
7619 */
7620
7621 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
7622 DefaultPolicyPtr,
7623 con, sub->owner)) != HTTP_OK)
7624 {
7625 send_http_error(con, status, sub->dest);
7626 return;
7627 }
7628
7629 /*
7630 * Copy the subscription attributes to the response using the
7631 * requested-attributes attribute that may be provided by the client.
7632 */
7633
7634 ra = create_requested_array(con->request);
7635
7636 copy_subscription_attrs(con, sub, ra);
7637
7638 cupsArrayDelete(ra);
7639
7640 con->response->request.status.status_code = IPP_OK;
7641 }
7642
7643
7644 /*
7645 * 'get_subscriptions()' - Get subscriptions.
7646 */
7647
7648 static void
7649 get_subscriptions(cupsd_client_t *con, /* I - Client connection */
7650 ipp_attribute_t *uri) /* I - Printer/job URI */
7651 {
7652 http_status_t status; /* Policy status */
7653 int count; /* Number of subscriptions */
7654 int limit; /* Limit */
7655 cupsd_subscription_t *sub; /* Subscription */
7656 cups_array_t *ra; /* Requested attributes array */
7657 ipp_attribute_t *attr; /* Attribute */
7658 cups_ptype_t dtype; /* Destination type (printer/class) */
7659 char scheme[HTTP_MAX_URI],
7660 /* Scheme portion of URI */
7661 username[HTTP_MAX_URI],
7662 /* Username portion of URI */
7663 host[HTTP_MAX_URI],
7664 /* Host portion of URI */
7665 resource[HTTP_MAX_URI];
7666 /* Resource portion of URI */
7667 int port; /* Port portion of URI */
7668 cupsd_job_t *job; /* Job pointer */
7669 cupsd_printer_t *printer; /* Printer */
7670
7671
7672 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7673 "get_subscriptions(con=%p[%d], uri=%s)",
7674 con, con->http.fd, uri->values[0].string.text);
7675
7676 /*
7677 * Is the destination valid?
7678 */
7679
7680 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7681 sizeof(scheme), username, sizeof(username), host,
7682 sizeof(host), &port, resource, sizeof(resource));
7683
7684 if (!strcmp(resource, "/") ||
7685 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6) ||
7686 (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) ||
7687 (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9))
7688 {
7689 printer = NULL;
7690 job = NULL;
7691 }
7692 else if (!strncmp(resource, "/jobs/", 6) && resource[6])
7693 {
7694 printer = NULL;
7695 job = cupsdFindJob(atoi(resource + 6));
7696
7697 if (!job)
7698 {
7699 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%s does not exist!"),
7700 resource + 6);
7701 return;
7702 }
7703 }
7704 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7705 {
7706 /*
7707 * Bad URI...
7708 */
7709
7710 send_ipp_status(con, IPP_NOT_FOUND,
7711 _("The printer or class was not found."));
7712 return;
7713 }
7714 else if ((attr = ippFindAttribute(con->request, "notify-job-id",
7715 IPP_TAG_INTEGER)) != NULL)
7716 {
7717 job = cupsdFindJob(attr->values[0].integer);
7718
7719 if (!job)
7720 {
7721 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"),
7722 attr->values[0].integer);
7723 return;
7724 }
7725 }
7726 else
7727 job = NULL;
7728
7729 /*
7730 * Check policy...
7731 */
7732
7733 if ((status = cupsdCheckPolicy(printer ? printer->op_policy_ptr :
7734 DefaultPolicyPtr,
7735 con, NULL)) != HTTP_OK)
7736 {
7737 send_http_error(con, status, printer);
7738 return;
7739 }
7740
7741 /*
7742 * Copy the subscription attributes to the response using the
7743 * requested-attributes attribute that may be provided by the client.
7744 */
7745
7746 ra = create_requested_array(con->request);
7747
7748 if ((attr = ippFindAttribute(con->request, "limit",
7749 IPP_TAG_INTEGER)) != NULL)
7750 limit = attr->values[0].integer;
7751 else
7752 limit = 0;
7753
7754 /*
7755 * See if we only want to see subscriptions for a specific user...
7756 */
7757
7758 if ((attr = ippFindAttribute(con->request, "my-subscriptions",
7759 IPP_TAG_BOOLEAN)) != NULL &&
7760 attr->values[0].boolean)
7761 strlcpy(username, get_username(con), sizeof(username));
7762 else
7763 username[0] = '\0';
7764
7765 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0;
7766 sub;
7767 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
7768 if ((!printer || sub->dest == printer) && (!job || sub->job == job) &&
7769 (!username[0] || !strcasecmp(username, sub->owner)))
7770 {
7771 ippAddSeparator(con->response);
7772 copy_subscription_attrs(con, sub, ra);
7773
7774 count ++;
7775 if (limit && count >= limit)
7776 break;
7777 }
7778
7779 cupsArrayDelete(ra);
7780
7781 if (count)
7782 con->response->request.status.status_code = IPP_OK;
7783 else
7784 send_ipp_status(con, IPP_NOT_FOUND, _("No subscriptions found."));
7785 }
7786
7787
7788 /*
7789 * 'get_username()' - Get the username associated with a request.
7790 */
7791
7792 static const char * /* O - Username */
7793 get_username(cupsd_client_t *con) /* I - Connection */
7794 {
7795 ipp_attribute_t *attr; /* Attribute */
7796
7797
7798 if (con->username[0])
7799 return (con->username);
7800 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7801 IPP_TAG_NAME)) != NULL)
7802 return (attr->values[0].string.text);
7803 else
7804 return ("anonymous");
7805 }
7806
7807
7808 /*
7809 * 'hold_job()' - Hold a print job.
7810 */
7811
7812 static void
7813 hold_job(cupsd_client_t *con, /* I - Client connection */
7814 ipp_attribute_t *uri) /* I - Job or Printer URI */
7815 {
7816 ipp_attribute_t *attr, /* Current job-hold-until */
7817 *newattr; /* New job-hold-until */
7818 int jobid; /* Job ID */
7819 char method[HTTP_MAX_URI], /* Method portion of URI */
7820 username[HTTP_MAX_URI], /* Username portion of URI */
7821 host[HTTP_MAX_URI], /* Host portion of URI */
7822 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7823 int port; /* Port portion of URI */
7824 cupsd_job_t *job; /* Job information */
7825
7826
7827 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", con, con->http.fd,
7828 uri->values[0].string.text);
7829
7830 /*
7831 * See if we have a job URI or a printer URI...
7832 */
7833
7834 if (!strcmp(uri->name, "printer-uri"))
7835 {
7836 /*
7837 * Got a printer URI; see if we also have a job-id attribute...
7838 */
7839
7840 if ((attr = ippFindAttribute(con->request, "job-id",
7841 IPP_TAG_INTEGER)) == NULL)
7842 {
7843 send_ipp_status(con, IPP_BAD_REQUEST,
7844 _("Got a printer-uri attribute but no job-id!"));
7845 return;
7846 }
7847
7848 jobid = attr->values[0].integer;
7849 }
7850 else
7851 {
7852 /*
7853 * Got a job URI; parse it to get the job ID...
7854 */
7855
7856 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
7857 sizeof(method), username, sizeof(username), host,
7858 sizeof(host), &port, resource, sizeof(resource));
7859
7860 if (strncmp(resource, "/jobs/", 6))
7861 {
7862 /*
7863 * Not a valid URI!
7864 */
7865
7866 send_ipp_status(con, IPP_BAD_REQUEST,
7867 _("Bad job-uri attribute \"%s\"!"),
7868 uri->values[0].string.text);
7869 return;
7870 }
7871
7872 jobid = atoi(resource + 6);
7873 }
7874
7875 /*
7876 * See if the job exists...
7877 */
7878
7879 if ((job = cupsdFindJob(jobid)) == NULL)
7880 {
7881 /*
7882 * Nope - return a "not found" error...
7883 */
7884
7885 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
7886 return;
7887 }
7888
7889 /*
7890 * See if the job is owned by the requesting user...
7891 */
7892
7893 if (!validate_user(job, con, job->username, username, sizeof(username)))
7894 {
7895 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
7896 return;
7897 }
7898
7899 /*
7900 * Hold the job and return...
7901 */
7902
7903 cupsdHoldJob(job);
7904
7905 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
7906 "Job held by user.");
7907
7908 if ((newattr = ippFindAttribute(con->request, "job-hold-until",
7909 IPP_TAG_KEYWORD)) == NULL)
7910 newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
7911
7912 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
7913 IPP_TAG_KEYWORD)) == NULL)
7914 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
7915
7916 if (attr)
7917 {
7918 /*
7919 * Free the old hold value and copy the new one over...
7920 */
7921
7922 _cupsStrFree(attr->values[0].string.text);
7923
7924 if (newattr)
7925 {
7926 attr->value_tag = newattr->value_tag;
7927 attr->values[0].string.text =
7928 _cupsStrAlloc(newattr->values[0].string.text);
7929 }
7930 else
7931 {
7932 attr->value_tag = IPP_TAG_KEYWORD;
7933 attr->values[0].string.text = _cupsStrAlloc("indefinite");
7934 }
7935
7936 /*
7937 * Hold job until specified time...
7938 */
7939
7940 cupsdSetJobHoldUntil(job, attr->values[0].string.text);
7941
7942 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
7943 "Job job-hold-until value changed by user.");
7944 }
7945
7946 cupsdLogJob(job, CUPSD_LOG_INFO, "Held by \"%s\".", username);
7947
7948 con->response->request.status.status_code = IPP_OK;
7949 }
7950
7951
7952 /*
7953 * 'move_job()' - Move a job to a new destination.
7954 */
7955
7956 static void
7957 move_job(cupsd_client_t *con, /* I - Client connection */
7958 ipp_attribute_t *uri) /* I - Job URI */
7959 {
7960 http_status_t status; /* Policy status */
7961 ipp_attribute_t *attr; /* Current attribute */
7962 int jobid; /* Job ID */
7963 cupsd_job_t *job; /* Current job */
7964 const char *src; /* Source printer/class */
7965 cups_ptype_t stype, /* Source type (printer or class) */
7966 dtype; /* Destination type (printer/class) */
7967 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
7968 username[HTTP_MAX_URI], /* Username portion of URI */
7969 host[HTTP_MAX_URI], /* Host portion of URI */
7970 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7971 int port; /* Port portion of URI */
7972 cupsd_printer_t *sprinter, /* Source printer */
7973 *dprinter; /* Destination printer */
7974
7975
7976 cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", con, con->http.fd,
7977 uri->values[0].string.text);
7978
7979 /*
7980 * Get the new printer or class...
7981 */
7982
7983 if ((attr = ippFindAttribute(con->request, "job-printer-uri",
7984 IPP_TAG_URI)) == NULL)
7985 {
7986 /*
7987 * Need job-printer-uri...
7988 */
7989
7990 send_ipp_status(con, IPP_BAD_REQUEST,
7991 _("job-printer-uri attribute missing!"));
7992 return;
7993 }
7994
7995 if (!cupsdValidateDest(attr->values[0].string.text, &dtype, &dprinter))
7996 {
7997 /*
7998 * Bad URI...
7999 */
8000
8001 send_ipp_status(con, IPP_NOT_FOUND,
8002 _("The printer or class was not found."));
8003 return;
8004 }
8005
8006 /*
8007 * See if we have a job URI or a printer URI...
8008 */
8009
8010 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8011 sizeof(scheme), username, sizeof(username), host,
8012 sizeof(host), &port, resource, sizeof(resource));
8013
8014 if (!strcmp(uri->name, "printer-uri"))
8015 {
8016 /*
8017 * Got a printer URI; see if we also have a job-id attribute...
8018 */
8019
8020 if ((attr = ippFindAttribute(con->request, "job-id",
8021 IPP_TAG_INTEGER)) == NULL)
8022 {
8023 /*
8024 * Move all jobs...
8025 */
8026
8027 if ((src = cupsdValidateDest(uri->values[0].string.text, &stype,
8028 &sprinter)) == NULL)
8029 {
8030 /*
8031 * Bad URI...
8032 */
8033
8034 send_ipp_status(con, IPP_NOT_FOUND,
8035 _("The printer or class was not found."));
8036 return;
8037 }
8038
8039 job = NULL;
8040 }
8041 else
8042 {
8043 /*
8044 * Otherwise, just move a single job...
8045 */
8046
8047 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
8048 {
8049 /*
8050 * Nope - return a "not found" error...
8051 */
8052
8053 send_ipp_status(con, IPP_NOT_FOUND,
8054 _("Job #%d does not exist!"), attr->values[0].integer);
8055 return;
8056 }
8057 else
8058 {
8059 /*
8060 * Job found, initialize source pointers...
8061 */
8062
8063 src = NULL;
8064 sprinter = NULL;
8065 }
8066 }
8067 }
8068 else
8069 {
8070 /*
8071 * Got a job URI; parse it to get the job ID...
8072 */
8073
8074 if (strncmp(resource, "/jobs/", 6))
8075 {
8076 /*
8077 * Not a valid URI!
8078 */
8079
8080 send_ipp_status(con, IPP_BAD_REQUEST,
8081 _("Bad job-uri attribute \"%s\"!"),
8082 uri->values[0].string.text);
8083 return;
8084 }
8085
8086 /*
8087 * See if the job exists...
8088 */
8089
8090 jobid = atoi(resource + 6);
8091
8092 if ((job = cupsdFindJob(jobid)) == NULL)
8093 {
8094 /*
8095 * Nope - return a "not found" error...
8096 */
8097
8098 send_ipp_status(con, IPP_NOT_FOUND,
8099 _("Job #%d does not exist!"), jobid);
8100 return;
8101 }
8102 else
8103 {
8104 /*
8105 * Job found, initialize source pointers...
8106 */
8107
8108 src = NULL;
8109 sprinter = NULL;
8110 }
8111 }
8112
8113 /*
8114 * Check the policy of the destination printer...
8115 */
8116
8117 if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con,
8118 job ? job->username : NULL)) != HTTP_OK)
8119 {
8120 send_http_error(con, status, dprinter);
8121 return;
8122 }
8123
8124 /*
8125 * Now move the job or jobs...
8126 */
8127
8128 if (job)
8129 {
8130 /*
8131 * See if the job has been completed...
8132 */
8133
8134 if (job->state_value > IPP_JOB_STOPPED)
8135 {
8136 /*
8137 * Return a "not-possible" error...
8138 */
8139
8140 send_ipp_status(con, IPP_NOT_POSSIBLE,
8141 _("Job #%d is finished and cannot be altered!"),
8142 job->id);
8143 return;
8144 }
8145
8146 /*
8147 * See if the job is owned by the requesting user...
8148 */
8149
8150 if (!validate_user(job, con, job->username, username, sizeof(username)))
8151 {
8152 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
8153 return;
8154 }
8155
8156 /*
8157 * Move the job to a different printer or class...
8158 */
8159
8160 cupsdMoveJob(job, dprinter);
8161 }
8162 else
8163 {
8164 /*
8165 * Got the source printer, now look through the jobs...
8166 */
8167
8168 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
8169 job;
8170 job = (cupsd_job_t *)cupsArrayNext(Jobs))
8171 {
8172 /*
8173 * See if the job is pointing at the source printer or has not been
8174 * completed...
8175 */
8176
8177 if (strcasecmp(job->dest, src) ||
8178 job->state_value > IPP_JOB_STOPPED)
8179 continue;
8180
8181 /*
8182 * See if the job can be moved by the requesting user...
8183 */
8184
8185 if (!validate_user(job, con, job->username, username, sizeof(username)))
8186 continue;
8187
8188 /*
8189 * Move the job to a different printer or class...
8190 */
8191
8192 cupsdMoveJob(job, dprinter);
8193 }
8194 }
8195
8196 /*
8197 * Start jobs if possible...
8198 */
8199
8200 cupsdCheckJobs();
8201
8202 /*
8203 * Return with "everything is OK" status...
8204 */
8205
8206 con->response->request.status.status_code = IPP_OK;
8207 }
8208
8209
8210 /*
8211 * 'ppd_parse_line()' - Parse a PPD default line.
8212 */
8213
8214 static int /* O - 0 on success, -1 on failure */
8215 ppd_parse_line(const char *line, /* I - Line */
8216 char *option, /* O - Option name */
8217 int olen, /* I - Size of option name */
8218 char *choice, /* O - Choice name */
8219 int clen) /* I - Size of choice name */
8220 {
8221 /*
8222 * Verify this is a default option line...
8223 */
8224
8225 if (strncmp(line, "*Default", 8))
8226 return (-1);
8227
8228 /*
8229 * Read the option name...
8230 */
8231
8232 for (line += 8, olen --; isalnum(*line & 255); line ++)
8233 if (olen > 0)
8234 {
8235 *option++ = *line;
8236 olen --;
8237 }
8238
8239 *option = '\0';
8240
8241 /*
8242 * Skip everything else up to the colon (:)...
8243 */
8244
8245 while (*line && *line != ':')
8246 line ++;
8247
8248 if (!*line)
8249 return (-1);
8250
8251 line ++;
8252
8253 /*
8254 * Now grab the option choice, skipping leading whitespace...
8255 */
8256
8257 while (isspace(*line & 255))
8258 line ++;
8259
8260 for (clen --; isalnum(*line & 255); line ++)
8261 if (clen > 0)
8262 {
8263 *choice++ = *line;
8264 clen --;
8265 }
8266
8267 *choice = '\0';
8268
8269 /*
8270 * Return with no errors...
8271 */
8272
8273 return (0);
8274 }
8275
8276
8277 /*
8278 * 'print_job()' - Print a file to a printer or class.
8279 */
8280
8281 static void
8282 print_job(cupsd_client_t *con, /* I - Client connection */
8283 ipp_attribute_t *uri) /* I - Printer URI */
8284 {
8285 ipp_attribute_t *attr; /* Current attribute */
8286 ipp_attribute_t *format; /* Document-format attribute */
8287 const char *default_format; /* document-format-default value */
8288 cupsd_job_t *job; /* New job */
8289 char filename[1024]; /* Job filename */
8290 mime_type_t *filetype; /* Type of file */
8291 char super[MIME_MAX_SUPER], /* Supertype of file */
8292 type[MIME_MAX_TYPE], /* Subtype of file */
8293 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
8294 /* Textual name of mime type */
8295 cupsd_printer_t *printer; /* Printer data */
8296 struct stat fileinfo; /* File information */
8297 int kbytes; /* Size of file */
8298 int compression; /* Document compression */
8299
8300
8301 cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", con, con->http.fd,
8302 uri->values[0].string.text);
8303
8304 /*
8305 * Validate print file attributes, for now just document-format and
8306 * compression (CUPS only supports "none" and "gzip")...
8307 */
8308
8309 compression = CUPS_FILE_NONE;
8310
8311 if ((attr = ippFindAttribute(con->request, "compression",
8312 IPP_TAG_KEYWORD)) != NULL)
8313 {
8314 if (strcmp(attr->values[0].string.text, "none")
8315 #ifdef HAVE_LIBZ
8316 && strcmp(attr->values[0].string.text, "gzip")
8317 #endif /* HAVE_LIBZ */
8318 )
8319 {
8320 send_ipp_status(con, IPP_ATTRIBUTES,
8321 _("Unsupported compression \"%s\"!"),
8322 attr->values[0].string.text);
8323 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
8324 "compression", NULL, attr->values[0].string.text);
8325 return;
8326 }
8327
8328 #ifdef HAVE_LIBZ
8329 if (!strcmp(attr->values[0].string.text, "gzip"))
8330 compression = CUPS_FILE_GZIP;
8331 #endif /* HAVE_LIBZ */
8332 }
8333
8334 /*
8335 * Do we have a file to print?
8336 */
8337
8338 if (!con->filename)
8339 {
8340 send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?!"));
8341 return;
8342 }
8343
8344 /*
8345 * Is the destination valid?
8346 */
8347
8348 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
8349 {
8350 /*
8351 * Bad URI...
8352 */
8353
8354 send_ipp_status(con, IPP_NOT_FOUND,
8355 _("The printer or class was not found."));
8356 return;
8357 }
8358
8359 /*
8360 * Is it a format we support?
8361 */
8362
8363 if ((format = ippFindAttribute(con->request, "document-format",
8364 IPP_TAG_MIMETYPE)) != NULL)
8365 {
8366 /*
8367 * Grab format from client...
8368 */
8369
8370 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super,
8371 type) != 2)
8372 {
8373 send_ipp_status(con, IPP_BAD_REQUEST,
8374 _("Could not scan type \"%s\"!"),
8375 format->values[0].string.text);
8376 return;
8377 }
8378 }
8379 else if ((default_format = cupsGetOption("document-format",
8380 printer->num_options,
8381 printer->options)) != NULL)
8382 {
8383 /*
8384 * Use default document format...
8385 */
8386
8387 if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
8388 {
8389 send_ipp_status(con, IPP_BAD_REQUEST,
8390 _("Could not scan type \"%s\"!"),
8391 default_format);
8392 return;
8393 }
8394 }
8395 else
8396 {
8397 /*
8398 * Auto-type it!
8399 */
8400
8401 strcpy(super, "application");
8402 strcpy(type, "octet-stream");
8403 }
8404
8405 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
8406 {
8407 /*
8408 * Auto-type the file...
8409 */
8410
8411 ipp_attribute_t *doc_name; /* document-name attribute */
8412
8413
8414 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job ???] Auto-typing file...");
8415
8416 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
8417 filetype = mimeFileType(MimeDatabase, con->filename,
8418 doc_name ? doc_name->values[0].string.text : NULL,
8419 &compression);
8420
8421 if (!filetype)
8422 filetype = mimeType(MimeDatabase, super, type);
8423
8424 cupsdLogMessage(CUPSD_LOG_INFO, "[Job ???] Request file type is %s/%s.",
8425 filetype->super, filetype->type);
8426 }
8427 else
8428 filetype = mimeType(MimeDatabase, super, type);
8429
8430 if (filetype &&
8431 (!format ||
8432 (!strcmp(super, "application") && !strcmp(type, "octet-stream"))))
8433 {
8434 /*
8435 * Replace the document-format attribute value with the auto-typed or
8436 * default one.
8437 */
8438
8439 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
8440 filetype->type);
8441
8442 if (format)
8443 {
8444 _cupsStrFree(format->values[0].string.text);
8445
8446 format->values[0].string.text = _cupsStrAlloc(mimetype);
8447 }
8448 else
8449 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
8450 "document-format", NULL, mimetype);
8451 }
8452 else if (!filetype)
8453 {
8454 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
8455 _("Unsupported format \'%s/%s\'!"), super, type);
8456 cupsdLogMessage(CUPSD_LOG_INFO,
8457 "Hint: Do you have the raw file printing rules enabled?");
8458
8459 if (format)
8460 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
8461 "document-format", NULL, format->values[0].string.text);
8462
8463 return;
8464 }
8465
8466 /*
8467 * Read any embedded job ticket info from PS files...
8468 */
8469
8470 if (!strcasecmp(filetype->super, "application") &&
8471 !strcasecmp(filetype->type, "postscript"))
8472 read_ps_job_ticket(con);
8473
8474 /*
8475 * Create the job object...
8476 */
8477
8478 if ((job = add_job(con, printer, filetype)) == NULL)
8479 return;
8480
8481 /*
8482 * Update quota data...
8483 */
8484
8485 if (stat(con->filename, &fileinfo))
8486 kbytes = 0;
8487 else
8488 kbytes = (fileinfo.st_size + 1023) / 1024;
8489
8490 cupsdUpdateQuota(printer, job->username, 0, kbytes);
8491
8492 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
8493 IPP_TAG_INTEGER)) != NULL)
8494 attr->values[0].integer += kbytes;
8495
8496 /*
8497 * Add the job file...
8498 */
8499
8500 if (add_file(con, job, filetype, compression))
8501 return;
8502
8503 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
8504 job->num_files);
8505 rename(con->filename, filename);
8506 cupsdClearString(&con->filename);
8507
8508 /*
8509 * See if we need to add the ending sheet...
8510 */
8511
8512 if (cupsdTimeoutJob(job))
8513 return;
8514
8515 /*
8516 * Log and save the job...
8517 */
8518
8519 cupsdLogJob(job, CUPSD_LOG_INFO,
8520 "File of type %s/%s queued by \"%s\".",
8521 filetype->super, filetype->type, job->username);
8522 cupsdLogJob(job, CUPSD_LOG_DEBUG, "hold_until=%d", (int)job->hold_until);
8523
8524 /*
8525 * Start the job if possible...
8526 */
8527
8528 cupsdCheckJobs();
8529 }
8530
8531
8532 /*
8533 * 'read_ps_job_ticket()' - Reads a job ticket embedded in a PS file.
8534 *
8535 * This function only gets called when printing a single PostScript
8536 * file using the Print-Job operation. It doesn't work for Create-Job +
8537 * Send-File, since the job attributes need to be set at job creation
8538 * time for banners to work. The embedded PS job ticket stuff is here
8539 * only to allow the Windows printer driver for CUPS to pass in JCL
8540 * options and IPP attributes which otherwise would be lost.
8541 *
8542 * The format of a PS job ticket is simple:
8543 *
8544 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
8545 *
8546 * %cupsJobTicket: attr1=value1
8547 * %cupsJobTicket: attr2=value2
8548 * ...
8549 * %cupsJobTicket: attrN=valueN
8550 *
8551 * Job ticket lines must appear immediately after the first line that
8552 * specifies PostScript format (%!PS-Adobe-3.0), and CUPS will stop
8553 * looking for job ticket info when it finds a line that does not begin
8554 * with "%cupsJobTicket:".
8555 *
8556 * The maximum length of a job ticket line, including the prefix, is
8557 * 255 characters to conform with the Adobe DSC.
8558 *
8559 * Read-only attributes are rejected with a notice to the error log in
8560 * case a malicious user tries anything. Since the job ticket is read
8561 * prior to attribute validation in print_job(), job ticket attributes
8562 * will go through the same validation as IPP attributes...
8563 */
8564
8565 static void
8566 read_ps_job_ticket(cupsd_client_t *con) /* I - Client connection */
8567 {
8568 cups_file_t *fp; /* File to read from */
8569 char line[256]; /* Line data */
8570 int num_options; /* Number of options */
8571 cups_option_t *options; /* Options */
8572 ipp_t *ticket; /* New attributes */
8573 ipp_attribute_t *attr, /* Current attribute */
8574 *attr2, /* Job attribute */
8575 *prev2; /* Previous job attribute */
8576
8577
8578 /*
8579 * First open the print file...
8580 */
8581
8582 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
8583 {
8584 cupsdLogMessage(CUPSD_LOG_ERROR,
8585 "read_ps_job_ticket: Unable to open PostScript print file "
8586 "- %s",
8587 strerror(errno));
8588 return;
8589 }
8590
8591 /*
8592 * Skip the first line...
8593 */
8594
8595 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
8596 {
8597 cupsdLogMessage(CUPSD_LOG_ERROR,
8598 "read_ps_job_ticket: Unable to read from PostScript print "
8599 "file - %s",
8600 strerror(errno));
8601 cupsFileClose(fp);
8602 return;
8603 }
8604
8605 if (strncmp(line, "%!PS-Adobe-", 11))
8606 {
8607 /*
8608 * Not a DSC-compliant file, so no job ticket info will be available...
8609 */
8610
8611 cupsFileClose(fp);
8612 return;
8613 }
8614
8615 /*
8616 * Read job ticket info from the file...
8617 */
8618
8619 num_options = 0;
8620 options = NULL;
8621
8622 while (cupsFileGets(fp, line, sizeof(line)))
8623 {
8624 /*
8625 * Stop at the first non-ticket line...
8626 */
8627
8628 if (strncmp(line, "%cupsJobTicket:", 15))
8629 break;
8630
8631 /*
8632 * Add the options to the option array...
8633 */
8634
8635 num_options = cupsParseOptions(line + 15, num_options, &options);
8636 }
8637
8638 /*
8639 * Done with the file; see if we have any options...
8640 */
8641
8642 cupsFileClose(fp);
8643
8644 if (num_options == 0)
8645 return;
8646
8647 /*
8648 * OK, convert the options to an attribute list, and apply them to
8649 * the request...
8650 */
8651
8652 ticket = ippNew();
8653 cupsEncodeOptions(ticket, num_options, options);
8654
8655 /*
8656 * See what the user wants to change.
8657 */
8658
8659 for (attr = ticket->attrs; attr; attr = attr->next)
8660 {
8661 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
8662 continue;
8663
8664 if (!strcmp(attr->name, "job-originating-host-name") ||
8665 !strcmp(attr->name, "job-originating-user-name") ||
8666 !strcmp(attr->name, "job-media-sheets-completed") ||
8667 !strcmp(attr->name, "job-k-octets") ||
8668 !strcmp(attr->name, "job-id") ||
8669 !strncmp(attr->name, "job-state", 9) ||
8670 !strncmp(attr->name, "time-at-", 8))
8671 continue; /* Read-only attrs */
8672
8673 if ((attr2 = ippFindAttribute(con->request, attr->name,
8674 IPP_TAG_ZERO)) != NULL)
8675 {
8676 /*
8677 * Some other value; first free the old value...
8678 */
8679
8680 if (con->request->attrs == attr2)
8681 {
8682 con->request->attrs = attr2->next;
8683 prev2 = NULL;
8684 }
8685 else
8686 {
8687 for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
8688 if (prev2->next == attr2)
8689 {
8690 prev2->next = attr2->next;
8691 break;
8692 }
8693 }
8694
8695 if (con->request->last == attr2)
8696 con->request->last = prev2;
8697
8698 _ippFreeAttr(attr2);
8699 }
8700
8701 /*
8702 * Add new option by copying it...
8703 */
8704
8705 copy_attribute(con->request, attr, 0);
8706 }
8707
8708 /*
8709 * Then free the attribute list and option array...
8710 */
8711
8712 ippDelete(ticket);
8713 cupsFreeOptions(num_options, options);
8714 }
8715
8716
8717 /*
8718 * 'reject_jobs()' - Reject print jobs to a printer.
8719 */
8720
8721 static void
8722 reject_jobs(cupsd_client_t *con, /* I - Client connection */
8723 ipp_attribute_t *uri) /* I - Printer or class URI */
8724 {
8725 http_status_t status; /* Policy status */
8726 cups_ptype_t dtype; /* Destination type (printer/class) */
8727 cupsd_printer_t *printer; /* Printer data */
8728 ipp_attribute_t *attr; /* printer-state-message text */
8729
8730
8731 cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", con,
8732 con->http.fd, uri->values[0].string.text);
8733
8734 /*
8735 * Is the destination valid?
8736 */
8737
8738 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8739 {
8740 /*
8741 * Bad URI...
8742 */
8743
8744 send_ipp_status(con, IPP_NOT_FOUND,
8745 _("The printer or class was not found."));
8746 return;
8747 }
8748
8749 /*
8750 * Check policy...
8751 */
8752
8753 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8754 {
8755 send_http_error(con, status, printer);
8756 return;
8757 }
8758
8759 /*
8760 * Reject jobs sent to the printer...
8761 */
8762
8763 printer->accepting = 0;
8764
8765 if ((attr = ippFindAttribute(con->request, "printer-state-message",
8766 IPP_TAG_TEXT)) == NULL)
8767 strcpy(printer->state_message, "Rejecting Jobs");
8768 else
8769 strlcpy(printer->state_message, attr->values[0].string.text,
8770 sizeof(printer->state_message));
8771
8772 cupsdAddPrinterHistory(printer);
8773
8774 if (dtype & CUPS_PRINTER_CLASS)
8775 {
8776 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
8777
8778 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
8779 printer->name, get_username(con));
8780 }
8781 else
8782 {
8783 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
8784
8785 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
8786 printer->name, get_username(con));
8787 }
8788
8789 /*
8790 * Everything was ok, so return OK status...
8791 */
8792
8793 con->response->request.status.status_code = IPP_OK;
8794 }
8795
8796
8797 /*
8798 * 'release_job()' - Release a held print job.
8799 */
8800
8801 static void
8802 release_job(cupsd_client_t *con, /* I - Client connection */
8803 ipp_attribute_t *uri) /* I - Job or Printer URI */
8804 {
8805 ipp_attribute_t *attr; /* Current attribute */
8806 int jobid; /* Job ID */
8807 char method[HTTP_MAX_URI], /* Method portion of URI */
8808 username[HTTP_MAX_URI], /* Username portion of URI */
8809 host[HTTP_MAX_URI], /* Host portion of URI */
8810 resource[HTTP_MAX_URI]; /* Resource portion of URI */
8811 int port; /* Port portion of URI */
8812 cupsd_job_t *job; /* Job information */
8813
8814
8815 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", con,
8816 con->http.fd, uri->values[0].string.text);
8817
8818 /*
8819 * See if we have a job URI or a printer URI...
8820 */
8821
8822 if (!strcmp(uri->name, "printer-uri"))
8823 {
8824 /*
8825 * Got a printer URI; see if we also have a job-id attribute...
8826 */
8827
8828 if ((attr = ippFindAttribute(con->request, "job-id",
8829 IPP_TAG_INTEGER)) == NULL)
8830 {
8831 send_ipp_status(con, IPP_BAD_REQUEST,
8832 _("Got a printer-uri attribute but no job-id!"));
8833 return;
8834 }
8835
8836 jobid = attr->values[0].integer;
8837 }
8838 else
8839 {
8840 /*
8841 * Got a job URI; parse it to get the job ID...
8842 */
8843
8844 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
8845 sizeof(method), username, sizeof(username), host,
8846 sizeof(host), &port, resource, sizeof(resource));
8847
8848 if (strncmp(resource, "/jobs/", 6))
8849 {
8850 /*
8851 * Not a valid URI!
8852 */
8853
8854 send_ipp_status(con, IPP_BAD_REQUEST,
8855 _("Bad job-uri attribute \"%s\"!"),
8856 uri->values[0].string.text);
8857 return;
8858 }
8859
8860 jobid = atoi(resource + 6);
8861 }
8862
8863 /*
8864 * See if the job exists...
8865 */
8866
8867 if ((job = cupsdFindJob(jobid)) == NULL)
8868 {
8869 /*
8870 * Nope - return a "not found" error...
8871 */
8872
8873 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
8874 return;
8875 }
8876
8877 /*
8878 * See if job is "held"...
8879 */
8880
8881 if (job->state_value != IPP_JOB_HELD)
8882 {
8883 /*
8884 * Nope - return a "not possible" error...
8885 */
8886
8887 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held!"), jobid);
8888 return;
8889 }
8890
8891 /*
8892 * See if the job is owned by the requesting user...
8893 */
8894
8895 if (!validate_user(job, con, job->username, username, sizeof(username)))
8896 {
8897 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
8898 return;
8899 }
8900
8901 /*
8902 * Reset the job-hold-until value to "no-hold"...
8903 */
8904
8905 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
8906 IPP_TAG_KEYWORD)) == NULL)
8907 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
8908
8909 if (attr)
8910 {
8911 _cupsStrFree(attr->values[0].string.text);
8912
8913 attr->value_tag = IPP_TAG_KEYWORD;
8914 attr->values[0].string.text = _cupsStrAlloc("no-hold");
8915
8916 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
8917 "Job job-hold-until value changed by user.");
8918 }
8919
8920 /*
8921 * Release the job and return...
8922 */
8923
8924 cupsdReleaseJob(job);
8925
8926 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
8927 "Job released by user.");
8928
8929 cupsdLogJob(job, CUPSD_LOG_INFO, "Released by \"%s\".", username);
8930
8931 con->response->request.status.status_code = IPP_OK;
8932 }
8933
8934
8935 /*
8936 * 'renew_subscription()' - Renew an existing subscription...
8937 */
8938
8939 static void
8940 renew_subscription(
8941 cupsd_client_t *con, /* I - Client connection */
8942 int sub_id) /* I - Subscription ID */
8943 {
8944 http_status_t status; /* Policy status */
8945 cupsd_subscription_t *sub; /* Subscription */
8946 ipp_attribute_t *lease; /* notify-lease-duration */
8947
8948
8949 cupsdLogMessage(CUPSD_LOG_DEBUG2,
8950 "renew_subscription(con=%p[%d], sub_id=%d)",
8951 con, con->http.fd, sub_id);
8952
8953 /*
8954 * Is the subscription ID valid?
8955 */
8956
8957 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
8958 {
8959 /*
8960 * Bad subscription ID...
8961 */
8962
8963 send_ipp_status(con, IPP_NOT_FOUND,
8964 _("notify-subscription-id %d no good!"), sub_id);
8965 return;
8966 }
8967
8968 if (sub->job)
8969 {
8970 /*
8971 * Job subscriptions cannot be renewed...
8972 */
8973
8974 send_ipp_status(con, IPP_NOT_POSSIBLE,
8975 _("Job subscriptions cannot be renewed!"));
8976 return;
8977 }
8978
8979 /*
8980 * Check policy...
8981 */
8982
8983 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
8984 DefaultPolicyPtr,
8985 con, sub->owner)) != HTTP_OK)
8986 {
8987 send_http_error(con, status, sub->dest);
8988 return;
8989 }
8990
8991 /*
8992 * Renew the subscription...
8993 */
8994
8995 lease = ippFindAttribute(con->request, "notify-lease-duration",
8996 IPP_TAG_INTEGER);
8997
8998 sub->lease = lease ? lease->values[0].integer : DefaultLeaseDuration;
8999
9000 if (MaxLeaseDuration && (sub->lease == 0 || sub->lease > MaxLeaseDuration))
9001 {
9002 cupsdLogMessage(CUPSD_LOG_INFO,
9003 "renew_subscription: Limiting notify-lease-duration to "
9004 "%d seconds.",
9005 MaxLeaseDuration);
9006 sub->lease = MaxLeaseDuration;
9007 }
9008
9009 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
9010
9011 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
9012
9013 con->response->request.status.status_code = IPP_OK;
9014
9015 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
9016 "notify-lease-duration", sub->lease);
9017 }
9018
9019
9020 /*
9021 * 'restart_job()' - Restart an old print job.
9022 */
9023
9024 static void
9025 restart_job(cupsd_client_t *con, /* I - Client connection */
9026 ipp_attribute_t *uri) /* I - Job or Printer URI */
9027 {
9028 ipp_attribute_t *attr; /* Current attribute */
9029 int jobid; /* Job ID */
9030 char method[HTTP_MAX_URI], /* Method portion of URI */
9031 username[HTTP_MAX_URI], /* Username portion of URI */
9032 host[HTTP_MAX_URI], /* Host portion of URI */
9033 resource[HTTP_MAX_URI]; /* Resource portion of URI */
9034 int port; /* Port portion of URI */
9035 cupsd_job_t *job; /* Job information */
9036
9037
9038 cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con,
9039 con->http.fd, uri->values[0].string.text);
9040
9041 /*
9042 * See if we have a job URI or a printer URI...
9043 */
9044
9045 if (!strcmp(uri->name, "printer-uri"))
9046 {
9047 /*
9048 * Got a printer URI; see if we also have a job-id attribute...
9049 */
9050
9051 if ((attr = ippFindAttribute(con->request, "job-id",
9052 IPP_TAG_INTEGER)) == NULL)
9053 {
9054 send_ipp_status(con, IPP_BAD_REQUEST,
9055 _("Got a printer-uri attribute but no job-id!"));
9056 return;
9057 }
9058
9059 jobid = attr->values[0].integer;
9060 }
9061 else
9062 {
9063 /*
9064 * Got a job URI; parse it to get the job ID...
9065 */
9066
9067 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
9068 sizeof(method), username, sizeof(username), host,
9069 sizeof(host), &port, resource, sizeof(resource));
9070
9071 if (strncmp(resource, "/jobs/", 6))
9072 {
9073 /*
9074 * Not a valid URI!
9075 */
9076
9077 send_ipp_status(con, IPP_BAD_REQUEST,
9078 _("Bad job-uri attribute \"%s\"!"),
9079 uri->values[0].string.text);
9080 return;
9081 }
9082
9083 jobid = atoi(resource + 6);
9084 }
9085
9086 /*
9087 * See if the job exists...
9088 */
9089
9090 if ((job = cupsdFindJob(jobid)) == NULL)
9091 {
9092 /*
9093 * Nope - return a "not found" error...
9094 */
9095
9096 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
9097 return;
9098 }
9099
9100 /*
9101 * See if job is in any of the "completed" states...
9102 */
9103
9104 if (job->state_value <= IPP_JOB_PROCESSING)
9105 {
9106 /*
9107 * Nope - return a "not possible" error...
9108 */
9109
9110 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete!"),
9111 jobid);
9112 return;
9113 }
9114
9115 /*
9116 * See if we have retained the job files...
9117 */
9118
9119 cupsdLoadJob(job);
9120
9121 if (!job->attrs || job->num_files == 0)
9122 {
9123 /*
9124 * Nope - return a "not possible" error...
9125 */
9126
9127 send_ipp_status(con, IPP_NOT_POSSIBLE,
9128 _("Job #%d cannot be restarted - no files!"), jobid);
9129 return;
9130 }
9131
9132 /*
9133 * See if the job is owned by the requesting user...
9134 */
9135
9136 if (!validate_user(job, con, job->username, username, sizeof(username)))
9137 {
9138 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
9139 return;
9140 }
9141
9142 /*
9143 * Restart the job and return...
9144 */
9145
9146 cupsdRestartJob(job);
9147
9148 cupsdLogJob(job, CUPSD_LOG_INFO, "Restarted by \"%s\".", username);
9149
9150 con->response->request.status.status_code = IPP_OK;
9151 }
9152
9153
9154 /*
9155 * 'save_auth_info()' - Save authentication information for a job.
9156 */
9157
9158 static void
9159 save_auth_info(
9160 cupsd_client_t *con, /* I - Client connection */
9161 cupsd_job_t *job, /* I - Job */
9162 ipp_attribute_t *auth_info) /* I - auth-info attribute, if any */
9163 {
9164 int i; /* Looping var */
9165 char filename[1024]; /* Job authentication filename */
9166 cups_file_t *fp; /* Job authentication file */
9167 char line[2048]; /* Line for file */
9168 cupsd_printer_t *dest; /* Destination printer/class */
9169
9170
9171 /*
9172 * This function saves the in-memory authentication information for
9173 * a job so that it can be used to authenticate with a remote host.
9174 * The information is stored in a file that is readable only by the
9175 * root user. The fields are Base-64 encoded, each on a separate line,
9176 * followed by random number (up to 1024) of newlines to limit the
9177 * amount of information that is exposed.
9178 *
9179 * Because of the potential for exposing of authentication information,
9180 * this functionality is only enabled when running cupsd as root.
9181 *
9182 * This caching only works for the Basic and BasicDigest authentication
9183 * types. Digest authentication cannot be cached this way, and in
9184 * the future Kerberos authentication may make all of this obsolete.
9185 *
9186 * Authentication information is saved whenever an authenticated
9187 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
9188 * performed.
9189 *
9190 * This information is deleted after a job is completed or canceled,
9191 * so reprints may require subsequent re-authentication.
9192 */
9193
9194 if (RunUser)
9195 return;
9196
9197 if ((dest = cupsdFindDest(job->dest)) == NULL)
9198 return;
9199
9200 /*
9201 * Create the authentication file and change permissions...
9202 */
9203
9204 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
9205 if ((fp = cupsFileOpen(filename, "w")) == NULL)
9206 {
9207 cupsdLogMessage(CUPSD_LOG_ERROR,
9208 "Unable to save authentication info to \"%s\" - %s",
9209 filename, strerror(errno));
9210 return;
9211 }
9212
9213 fchown(cupsFileNumber(fp), 0, 0);
9214 fchmod(cupsFileNumber(fp), 0400);
9215
9216 if (auth_info && auth_info->num_values == dest->num_auth_info_required)
9217 {
9218 /*
9219 * Write 1 to 3 auth values...
9220 */
9221
9222 cupsdClearString(&job->auth_username);
9223 cupsdClearString(&job->auth_domain);
9224 cupsdClearString(&job->auth_password);
9225
9226 for (i = 0; i < auth_info->num_values; i ++)
9227 {
9228 httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text,
9229 strlen(auth_info->values[i].string.text));
9230 cupsFilePrintf(fp, "%s\n", line);
9231
9232 if (!strcmp(dest->auth_info_required[i], "username"))
9233 cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s",
9234 auth_info->values[i].string.text);
9235 else if (!strcmp(dest->auth_info_required[i], "domain"))
9236 cupsdSetStringf(&job->auth_domain, "AUTH_DOMAIN=%s",
9237 auth_info->values[i].string.text);
9238 else if (!strcmp(dest->auth_info_required[i], "password"))
9239 cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s",
9240 auth_info->values[i].string.text);
9241 }
9242 }
9243 else if (con->username[0])
9244 {
9245 /*
9246 * Write the authenticated username...
9247 */
9248
9249 httpEncode64_2(line, sizeof(line), con->username, strlen(con->username));
9250 cupsFilePrintf(fp, "%s\n", line);
9251
9252 cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s", con->username);
9253 cupsdClearString(&job->auth_domain);
9254
9255 /*
9256 * Write the authenticated password...
9257 */
9258
9259 httpEncode64_2(line, sizeof(line), con->password, strlen(con->password));
9260 cupsFilePrintf(fp, "%s\n", line);
9261
9262 cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s", con->password);
9263 }
9264
9265 /*
9266 * Write a random number of newlines to the end of the file...
9267 */
9268
9269 for (i = (rand() % 1024); i >= 0; i --)
9270 cupsFilePutChar(fp, '\n');
9271
9272 /*
9273 * Close the file and return...
9274 */
9275
9276 cupsFileClose(fp);
9277
9278 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
9279 if (con->gss_have_creds)
9280 save_krb5_creds(con, job);
9281 else if (job->ccname)
9282 cupsdClearString(&(job->ccname));
9283 #endif /* HAVE_GSSAPI && HAVE_KRB5_H */
9284 }
9285
9286
9287 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
9288 /*
9289 * 'save_krb5_creds()' - Save Kerberos credentials for the job.
9290 */
9291
9292 static void
9293 save_krb5_creds(cupsd_client_t *con, /* I - Client connection */
9294 cupsd_job_t *job) /* I - Job */
9295 {
9296 # if !defined(HAVE_KRB5_CC_NEW_UNIQUE) && !defined(HAVE_HEIMDAL)
9297 cupsdLogMessage(CUPSD_LOG_INFO,
9298 "Sorry, your version of Kerberos does not support delegated "
9299 "credentials!");
9300 return;
9301
9302 # else
9303 krb5_error_code error; /* Kerberos error code */
9304 OM_uint32 major_status, /* Major status code */
9305 minor_status; /* Minor status code */
9306 krb5_principal principal; /* Kerberos principal */
9307
9308
9309 # ifdef __APPLE__
9310 /*
9311 * If the weak-linked GSSAPI/Kerberos library is not present, don't try
9312 * to use it...
9313 */
9314
9315 if (krb5_init_context == NULL)
9316 return;
9317 # endif /* __APPLE__ */
9318
9319 /*
9320 * We MUST create a file-based cache because memory-based caches are
9321 * only valid for the current process/address space.
9322 *
9323 * Due to various bugs/features in different versions of Kerberos, we
9324 * need either the krb5_cc_new_unique() function or Heimdal's version
9325 * of krb5_cc_gen_new() to create a new FILE: credential cache that
9326 * can be passed to the backend. These functions create a temporary
9327 * file (typically in /tmp) containing the cached credentials, which
9328 * are removed when we have successfully printed a job.
9329 */
9330
9331 # ifdef HAVE_KRB5_CC_NEW_UNIQUE
9332 if ((error = krb5_cc_new_unique(KerberosContext, "FILE", NULL,
9333 &(job->ccache))) != 0)
9334 # else /* HAVE_HEIMDAL */
9335 if ((error = krb5_cc_gen_new(KerberosContext, &krb5_fcc_ops,
9336 &(job->ccache))) != 0)
9337 # endif /* HAVE_KRB5_CC_NEW_UNIQUE */
9338 {
9339 cupsdLogMessage(CUPSD_LOG_ERROR,
9340 "Unable to create new credentials cache (%d/%s)",
9341 error, strerror(errno));
9342 job->ccache = NULL;
9343 return;
9344 }
9345
9346 if ((error = krb5_parse_name(KerberosContext, con->username, &principal)) != 0)
9347 {
9348 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to parse kerberos username (%d/%s)",
9349 error, strerror(errno));
9350 krb5_cc_destroy(KerberosContext, job->ccache);
9351 job->ccache = NULL;
9352 return;
9353 }
9354
9355 if ((error = krb5_cc_initialize(KerberosContext, job->ccache, principal)))
9356 {
9357 cupsdLogMessage(CUPSD_LOG_ERROR,
9358 "Unable to initialize credentials cache (%d/%s)", error,
9359 strerror(errno));
9360 krb5_cc_destroy(KerberosContext, job->ccache);
9361 krb5_free_principal(KerberosContext, principal);
9362 job->ccache = NULL;
9363 return;
9364 }
9365
9366 krb5_free_principal(KerberosContext, principal);
9367
9368 /*
9369 * Copy the user's credentials to the new cache file...
9370 */
9371
9372 major_status = gss_krb5_copy_ccache(&minor_status, con->gss_delegated_cred,
9373 job->ccache);
9374
9375 if (GSS_ERROR(major_status))
9376 {
9377 cupsdLogGSSMessage(CUPSD_LOG_ERROR, major_status, minor_status,
9378 "Unable to import client credentials cache");
9379 krb5_cc_destroy(KerberosContext, job->ccache);
9380 job->ccache = NULL;
9381 return;
9382 }
9383
9384 /*
9385 * Add the KRB5CCNAME environment variable to the job so that the
9386 * backend can use the credentials when printing.
9387 */
9388
9389 cupsdSetStringf(&(job->ccname), "KRB5CCNAME=FILE:%s",
9390 krb5_cc_get_name(KerberosContext, job->ccache));
9391
9392 cupsdLogJob(job, CUPSD_LOG_DEBUG2, "save_krb5_creds: %s", job->ccname);
9393 # endif /* HAVE_KRB5_CC_NEW_UNIQUE || HAVE_HEIMDAL */
9394 }
9395 #endif /* HAVE_GSSAPI && HAVE_KRB5_H */
9396
9397
9398 /*
9399 * 'send_document()' - Send a file to a printer or class.
9400 */
9401
9402 static void
9403 send_document(cupsd_client_t *con, /* I - Client connection */
9404 ipp_attribute_t *uri) /* I - Printer URI */
9405 {
9406 ipp_attribute_t *attr; /* Current attribute */
9407 ipp_attribute_t *format; /* Request's document-format attribute */
9408 ipp_attribute_t *jformat; /* Job's document-format attribute */
9409 const char *default_format;/* document-format-default value */
9410 int jobid; /* Job ID number */
9411 cupsd_job_t *job; /* Current job */
9412 char job_uri[HTTP_MAX_URI],
9413 /* Job URI */
9414 method[HTTP_MAX_URI],
9415 /* Method portion of URI */
9416 username[HTTP_MAX_URI],
9417 /* Username portion of URI */
9418 host[HTTP_MAX_URI],
9419 /* Host portion of URI */
9420 resource[HTTP_MAX_URI];
9421 /* Resource portion of URI */
9422 int port; /* Port portion of URI */
9423 mime_type_t *filetype; /* Type of file */
9424 char super[MIME_MAX_SUPER],
9425 /* Supertype of file */
9426 type[MIME_MAX_TYPE],
9427 /* Subtype of file */
9428 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
9429 /* Textual name of mime type */
9430 char filename[1024]; /* Job filename */
9431 cupsd_printer_t *printer; /* Current printer */
9432 struct stat fileinfo; /* File information */
9433 int kbytes; /* Size of file */
9434 int compression; /* Type of compression */
9435 int start_job; /* Start the job? */
9436
9437
9438 cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con,
9439 con->http.fd, uri->values[0].string.text);
9440
9441 /*
9442 * See if we have a job URI or a printer URI...
9443 */
9444
9445 if (!strcmp(uri->name, "printer-uri"))
9446 {
9447 /*
9448 * Got a printer URI; see if we also have a job-id attribute...
9449 */
9450
9451 if ((attr = ippFindAttribute(con->request, "job-id",
9452 IPP_TAG_INTEGER)) == NULL)
9453 {
9454 send_ipp_status(con, IPP_BAD_REQUEST,
9455 _("Got a printer-uri attribute but no job-id!"));
9456 return;
9457 }
9458
9459 jobid = attr->values[0].integer;
9460 }
9461 else
9462 {
9463 /*
9464 * Got a job URI; parse it to get the job ID...
9465 */
9466
9467 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
9468 sizeof(method), username, sizeof(username), host,
9469 sizeof(host), &port, resource, sizeof(resource));
9470
9471 if (strncmp(resource, "/jobs/", 6))
9472 {
9473 /*
9474 * Not a valid URI!
9475 */
9476
9477 send_ipp_status(con, IPP_BAD_REQUEST,
9478 _("Bad job-uri attribute \"%s\"!"),
9479 uri->values[0].string.text);
9480 return;
9481 }
9482
9483 jobid = atoi(resource + 6);
9484 }
9485
9486 /*
9487 * See if the job exists...
9488 */
9489
9490 if ((job = cupsdFindJob(jobid)) == NULL)
9491 {
9492 /*
9493 * Nope - return a "not found" error...
9494 */
9495
9496 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
9497 return;
9498 }
9499
9500 printer = cupsdFindDest(job->dest);
9501
9502 /*
9503 * See if the job is owned by the requesting user...
9504 */
9505
9506 if (!validate_user(job, con, job->username, username, sizeof(username)))
9507 {
9508 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
9509 return;
9510 }
9511
9512 /*
9513 * OK, see if the client is sending the document compressed - CUPS
9514 * only supports "none" and "gzip".
9515 */
9516
9517 compression = CUPS_FILE_NONE;
9518
9519 if ((attr = ippFindAttribute(con->request, "compression",
9520 IPP_TAG_KEYWORD)) != NULL)
9521 {
9522 if (strcmp(attr->values[0].string.text, "none")
9523 #ifdef HAVE_LIBZ
9524 && strcmp(attr->values[0].string.text, "gzip")
9525 #endif /* HAVE_LIBZ */
9526 )
9527 {
9528 send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\"!"),
9529 attr->values[0].string.text);
9530 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
9531 "compression", NULL, attr->values[0].string.text);
9532 return;
9533 }
9534
9535 #ifdef HAVE_LIBZ
9536 if (!strcmp(attr->values[0].string.text, "gzip"))
9537 compression = CUPS_FILE_GZIP;
9538 #endif /* HAVE_LIBZ */
9539 }
9540
9541 /*
9542 * Do we have a file to print?
9543 */
9544
9545 if (!con->filename)
9546 {
9547 send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?!"));
9548 return;
9549 }
9550
9551 /*
9552 * Is it a format we support?
9553 */
9554
9555 if ((format = ippFindAttribute(con->request, "document-format",
9556 IPP_TAG_MIMETYPE)) != NULL)
9557 {
9558 /*
9559 * Grab format from client...
9560 */
9561
9562 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
9563 super, type) != 2)
9564 {
9565 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"!"),
9566 format->values[0].string.text);
9567 return;
9568 }
9569 }
9570 else if ((default_format = cupsGetOption("document-format",
9571 printer->num_options,
9572 printer->options)) != NULL)
9573 {
9574 /*
9575 * Use default document format...
9576 */
9577
9578 if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
9579 {
9580 send_ipp_status(con, IPP_BAD_REQUEST,
9581 _("Could not scan type \"%s\"!"),
9582 default_format);
9583 return;
9584 }
9585 }
9586 else
9587 {
9588 /*
9589 * No document format attribute? Auto-type it!
9590 */
9591
9592 strcpy(super, "application");
9593 strcpy(type, "octet-stream");
9594 }
9595
9596 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
9597 {
9598 /*
9599 * Auto-type the file...
9600 */
9601
9602 ipp_attribute_t *doc_name; /* document-name attribute */
9603
9604
9605 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Auto-typing file...");
9606
9607 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
9608 filetype = mimeFileType(MimeDatabase, con->filename,
9609 doc_name ? doc_name->values[0].string.text : NULL,
9610 &compression);
9611
9612 if (!filetype)
9613 filetype = mimeType(MimeDatabase, super, type);
9614
9615 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Request file type is %s/%s.",
9616 filetype->super, filetype->type);
9617 }
9618 else
9619 filetype = mimeType(MimeDatabase, super, type);
9620
9621 jformat = ippFindAttribute(job->attrs, "document-format", IPP_TAG_MIMETYPE);
9622
9623 if (filetype &&
9624 (!jformat ||
9625 (!strcmp(super, "application") && !strcmp(type, "octet-stream"))))
9626 {
9627 /*
9628 * Replace the document-format attribute value with the auto-typed or
9629 * default one.
9630 */
9631
9632 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9633 filetype->type);
9634
9635 if (jformat)
9636 {
9637 _cupsStrFree(jformat->values[0].string.text);
9638
9639 jformat->values[0].string.text = _cupsStrAlloc(mimetype);
9640 }
9641 else
9642 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
9643 "document-format", NULL, mimetype);
9644 }
9645 else if (!filetype)
9646 {
9647 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
9648 _("Unsupported format \'%s/%s\'!"), super, type);
9649 cupsdLogMessage(CUPSD_LOG_INFO,
9650 "Hint: Do you have the raw file printing rules enabled?");
9651
9652 if (format)
9653 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9654 "document-format", NULL, format->values[0].string.text);
9655
9656 return;
9657 }
9658
9659 if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
9660 {
9661 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9662 filetype->type);
9663
9664 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
9665 _("Unsupported format \'%s\'!"), mimetype);
9666
9667 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9668 "document-format", NULL, mimetype);
9669
9670 return;
9671 }
9672
9673 /*
9674 * Add the file to the job...
9675 */
9676
9677 cupsdLoadJob(job);
9678
9679 if (add_file(con, job, filetype, compression))
9680 return;
9681
9682 if (stat(con->filename, &fileinfo))
9683 kbytes = 0;
9684 else
9685 kbytes = (fileinfo.st_size + 1023) / 1024;
9686
9687 cupsdUpdateQuota(printer, job->username, 0, kbytes);
9688
9689 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
9690 IPP_TAG_INTEGER)) != NULL)
9691 attr->values[0].integer += kbytes;
9692
9693 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
9694 job->num_files);
9695 rename(con->filename, filename);
9696
9697 cupsdClearString(&con->filename);
9698
9699 cupsdLogJob(job, CUPSD_LOG_INFO, "File of type %s/%s queued by \"%s\".",
9700 filetype->super, filetype->type, job->username);
9701
9702 /*
9703 * Start the job if this is the last document...
9704 */
9705
9706 if ((attr = ippFindAttribute(con->request, "last-document",
9707 IPP_TAG_BOOLEAN)) != NULL &&
9708 attr->values[0].boolean)
9709 {
9710 /*
9711 * See if we need to add the ending sheet...
9712 */
9713
9714 if (cupsdTimeoutJob(job))
9715 return;
9716
9717 if (job->state_value == IPP_JOB_STOPPED)
9718 {
9719 job->state->values[0].integer = IPP_JOB_PENDING;
9720 job->state_value = IPP_JOB_PENDING;
9721 }
9722 else if (job->state_value == IPP_JOB_HELD)
9723 {
9724 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9725 IPP_TAG_KEYWORD)) == NULL)
9726 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9727
9728 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
9729 {
9730 job->state->values[0].integer = IPP_JOB_PENDING;
9731 job->state_value = IPP_JOB_PENDING;
9732 }
9733 }
9734
9735 job->dirty = 1;
9736 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
9737
9738 start_job = 1;
9739 }
9740 else
9741 {
9742 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9743 IPP_TAG_KEYWORD)) == NULL)
9744 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9745
9746 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
9747 {
9748 job->state->values[0].integer = IPP_JOB_HELD;
9749 job->state_value = IPP_JOB_HELD;
9750 job->hold_until = time(NULL) + 60;
9751 job->dirty = 1;
9752
9753 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
9754 }
9755
9756 start_job = 0;
9757 }
9758
9759 /*
9760 * Fill in the response info...
9761 */
9762
9763 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
9764 LocalPort, jobid);
9765
9766 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
9767 job_uri);
9768
9769 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
9770
9771 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
9772 job->state_value);
9773 add_job_state_reasons(con, job);
9774
9775 con->response->request.status.status_code = IPP_OK;
9776
9777 /*
9778 * Start the job if necessary...
9779 */
9780
9781 if (start_job)
9782 cupsdCheckJobs();
9783 }
9784
9785
9786 /*
9787 * 'send_http_error()' - Send a HTTP error back to the IPP client.
9788 */
9789
9790 static void
9791 send_http_error(
9792 cupsd_client_t *con, /* I - Client connection */
9793 http_status_t status, /* I - HTTP status code */
9794 cupsd_printer_t *printer) /* I - Printer, if any */
9795 {
9796 cupsdLogMessage(CUPSD_LOG_ERROR, "%s: %s",
9797 ippOpString(con->request->request.op.operation_id),
9798 httpStatus(status));
9799
9800 if (status == HTTP_UNAUTHORIZED &&
9801 printer && printer->num_auth_info_required > 0 &&
9802 !strcmp(printer->auth_info_required[0], "negotiate"))
9803 cupsdSendError(con, status, CUPSD_AUTH_NEGOTIATE);
9804 else if (printer)
9805 {
9806 char resource[HTTP_MAX_URI]; /* Resource portion of URI */
9807 cupsd_location_t *auth; /* Pointer to authentication element */
9808 int auth_type; /* Type of authentication required */
9809
9810
9811 if (printer->type & CUPS_PRINTER_CLASS)
9812 snprintf(resource, sizeof(resource), "/classes/%s", printer->name);
9813 else
9814 snprintf(resource, sizeof(resource), "/printers/%s", printer->name);
9815
9816 if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
9817 auth->type == CUPSD_AUTH_NONE)
9818 auth = cupsdFindPolicyOp(printer->op_policy_ptr,
9819 con->request ?
9820 con->request->request.op.operation_id :
9821 IPP_PRINT_JOB);
9822
9823 if (!auth)
9824 auth_type = CUPSD_AUTH_NONE;
9825 else if (auth->type == CUPSD_AUTH_DEFAULT)
9826 auth_type = DefaultAuthType;
9827 else
9828 auth_type = auth->type;
9829
9830 cupsdSendError(con, status, auth_type);
9831 }
9832 else
9833 cupsdSendError(con, status, CUPSD_AUTH_NONE);
9834
9835 ippDelete(con->response);
9836 con->response = NULL;
9837
9838 return;
9839 }
9840
9841
9842 /*
9843 * 'send_ipp_status()' - Send a status back to the IPP client.
9844 */
9845
9846 static void
9847 send_ipp_status(cupsd_client_t *con, /* I - Client connection */
9848 ipp_status_t status, /* I - IPP status code */
9849 const char *message, /* I - Status message */
9850 ...) /* I - Additional args as needed */
9851 {
9852 va_list ap; /* Pointer to additional args */
9853 char formatted[1024]; /* Formatted errror message */
9854
9855
9856 va_start(ap, message);
9857 vsnprintf(formatted, sizeof(formatted),
9858 _cupsLangString(con->language, message), ap);
9859 va_end(ap);
9860
9861 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
9862 ippOpString(con->request->request.op.operation_id),
9863 ippErrorString(status), formatted);
9864
9865 con->response->request.status.status_code = status;
9866
9867 if (ippFindAttribute(con->response, "attributes-charset",
9868 IPP_TAG_ZERO) == NULL)
9869 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
9870 "attributes-charset", NULL, DefaultCharset);
9871
9872 if (ippFindAttribute(con->response, "attributes-natural-language",
9873 IPP_TAG_ZERO) == NULL)
9874 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
9875 "attributes-natural-language", NULL, DefaultLanguage);
9876
9877 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
9878 "status-message", NULL, formatted);
9879 }
9880
9881
9882 /*
9883 * 'set_default()' - Set the default destination...
9884 */
9885
9886 static void
9887 set_default(cupsd_client_t *con, /* I - Client connection */
9888 ipp_attribute_t *uri) /* I - Printer URI */
9889 {
9890 http_status_t status; /* Policy status */
9891 cups_ptype_t dtype; /* Destination type (printer/class) */
9892 cupsd_printer_t *printer, /* Printer */
9893 *oldprinter; /* Old default printer */
9894
9895
9896 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con,
9897 con->http.fd, uri->values[0].string.text);
9898
9899 /*
9900 * Is the destination valid?
9901 */
9902
9903 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
9904 {
9905 /*
9906 * Bad URI...
9907 */
9908
9909 send_ipp_status(con, IPP_NOT_FOUND,
9910 _("The printer or class was not found."));
9911 return;
9912 }
9913
9914 /*
9915 * Check policy...
9916 */
9917
9918 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
9919 {
9920 send_http_error(con, status, NULL);
9921 return;
9922 }
9923
9924 /*
9925 * Set it as the default...
9926 */
9927
9928 oldprinter = DefaultPrinter;
9929 DefaultPrinter = printer;
9930
9931 if (oldprinter)
9932 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, oldprinter, NULL,
9933 "%s is no longer the default printer.", oldprinter->name);
9934
9935 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
9936 "%s is now the default printer.", printer->name);
9937
9938 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS | CUPSD_DIRTY_CLASSES |
9939 CUPSD_DIRTY_REMOTE | CUPSD_DIRTY_PRINTCAP);
9940
9941 cupsdLogMessage(CUPSD_LOG_INFO,
9942 "Default destination set to \"%s\" by \"%s\".",
9943 printer->name, get_username(con));
9944
9945 /*
9946 * Everything was ok, so return OK status...
9947 */
9948
9949 con->response->request.status.status_code = IPP_OK;
9950 }
9951
9952
9953 /*
9954 * 'set_job_attrs()' - Set job attributes.
9955 */
9956
9957 static void
9958 set_job_attrs(cupsd_client_t *con, /* I - Client connection */
9959 ipp_attribute_t *uri) /* I - Job URI */
9960 {
9961 ipp_attribute_t *attr, /* Current attribute */
9962 *attr2; /* Job attribute */
9963 int jobid; /* Job ID */
9964 cupsd_job_t *job; /* Current job */
9965 char method[HTTP_MAX_URI],
9966 /* Method portion of URI */
9967 username[HTTP_MAX_URI],
9968 /* Username portion of URI */
9969 host[HTTP_MAX_URI],
9970 /* Host portion of URI */
9971 resource[HTTP_MAX_URI];
9972 /* Resource portion of URI */
9973 int port; /* Port portion of URI */
9974 int event; /* Events? */
9975
9976
9977 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
9978 con->http.fd, uri->values[0].string.text);
9979
9980 /*
9981 * Start with "everything is OK" status...
9982 */
9983
9984 con->response->request.status.status_code = IPP_OK;
9985
9986 /*
9987 * See if we have a job URI or a printer URI...
9988 */
9989
9990 if (!strcmp(uri->name, "printer-uri"))
9991 {
9992 /*
9993 * Got a printer URI; see if we also have a job-id attribute...
9994 */
9995
9996 if ((attr = ippFindAttribute(con->request, "job-id",
9997 IPP_TAG_INTEGER)) == NULL)
9998 {
9999 send_ipp_status(con, IPP_BAD_REQUEST,
10000 _("Got a printer-uri attribute but no job-id!"));
10001 return;
10002 }
10003
10004 jobid = attr->values[0].integer;
10005 }
10006 else
10007 {
10008 /*
10009 * Got a job URI; parse it to get the job ID...
10010 */
10011
10012 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
10013 sizeof(method), username, sizeof(username), host,
10014 sizeof(host), &port, resource, sizeof(resource));
10015
10016 if (strncmp(resource, "/jobs/", 6))
10017 {
10018 /*
10019 * Not a valid URI!
10020 */
10021
10022 send_ipp_status(con, IPP_BAD_REQUEST,
10023 _("Bad job-uri attribute \"%s\"!"),
10024 uri->values[0].string.text);
10025 return;
10026 }
10027
10028 jobid = atoi(resource + 6);
10029 }
10030
10031 /*
10032 * See if the job exists...
10033 */
10034
10035 if ((job = cupsdFindJob(jobid)) == NULL)
10036 {
10037 /*
10038 * Nope - return a "not found" error...
10039 */
10040
10041 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
10042 return;
10043 }
10044
10045 /*
10046 * See if the job has been completed...
10047 */
10048
10049 if (job->state_value > IPP_JOB_STOPPED)
10050 {
10051 /*
10052 * Return a "not-possible" error...
10053 */
10054
10055 send_ipp_status(con, IPP_NOT_POSSIBLE,
10056 _("Job #%d is finished and cannot be altered!"), jobid);
10057 return;
10058 }
10059
10060 /*
10061 * See if the job is owned by the requesting user...
10062 */
10063
10064 if (!validate_user(job, con, job->username, username, sizeof(username)))
10065 {
10066 send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
10067 return;
10068 }
10069
10070 /*
10071 * See what the user wants to change.
10072 */
10073
10074 cupsdLoadJob(job);
10075
10076 event = 0;
10077
10078 for (attr = con->request->attrs; attr; attr = attr->next)
10079 {
10080 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
10081 continue;
10082
10083 if (!strcmp(attr->name, "attributes-charset") ||
10084 !strcmp(attr->name, "attributes-natural-language") ||
10085 !strcmp(attr->name, "document-compression") ||
10086 !strcmp(attr->name, "document-format") ||
10087 !strcmp(attr->name, "job-detailed-status-messages") ||
10088 !strcmp(attr->name, "job-document-access-errors") ||
10089 !strcmp(attr->name, "job-id") ||
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-impressions", 15) ||
10104 !strncmp(attr->name, "job-k-octets", 12) ||
10105 !strncmp(attr->name, "job-media-sheets", 16) ||
10106 !strncmp(attr->name, "time-at-", 8))
10107 {
10108 /*
10109 * Read-only attrs!
10110 */
10111
10112 send_ipp_status(con, IPP_ATTRIBUTES_NOT_SETTABLE,
10113 _("%s cannot be changed."), attr->name);
10114
10115 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
10116 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
10117
10118 continue;
10119 }
10120
10121 if (!strcmp(attr->name, "job-priority"))
10122 {
10123 /*
10124 * Change the job priority...
10125 */
10126
10127 if (attr->value_tag != IPP_TAG_INTEGER)
10128 {
10129 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value!"));
10130
10131 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
10132 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
10133 }
10134 else if (job->state_value >= IPP_JOB_PROCESSING)
10135 {
10136 send_ipp_status(con, IPP_NOT_POSSIBLE,
10137 _("Job is completed and cannot be changed."));
10138 return;
10139 }
10140 else if (con->response->request.status.status_code == IPP_OK)
10141 {
10142 cupsdSetJobPriority(job, attr->values[0].integer);
10143 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED |
10144 CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED;
10145 }
10146 }
10147 else if (!strcmp(attr->name, "job-state"))
10148 {
10149 /*
10150 * Change the job state...
10151 */
10152
10153 if (attr->value_tag != IPP_TAG_ENUM)
10154 {
10155 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value!"));
10156
10157 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
10158 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
10159 }
10160 else
10161 {
10162 switch (attr->values[0].integer)
10163 {
10164 case IPP_JOB_PENDING :
10165 case IPP_JOB_HELD :
10166 if (job->state_value > IPP_JOB_HELD)
10167 {
10168 send_ipp_status(con, IPP_NOT_POSSIBLE,
10169 _("Job state cannot be changed."));
10170 return;
10171 }
10172 else if (con->response->request.status.status_code == IPP_OK)
10173 {
10174 job->state->values[0].integer = attr->values[0].integer;
10175 job->state_value = (ipp_jstate_t)attr->values[0].integer;
10176
10177 event |= CUPSD_EVENT_JOB_STATE;
10178 }
10179 break;
10180
10181 case IPP_JOB_PROCESSING :
10182 case IPP_JOB_STOPPED :
10183 if (job->state_value != attr->values[0].integer)
10184 {
10185 send_ipp_status(con, IPP_NOT_POSSIBLE,
10186 _("Job state cannot be changed."));
10187 return;
10188 }
10189 break;
10190
10191 case IPP_JOB_CANCELED :
10192 case IPP_JOB_ABORTED :
10193 case IPP_JOB_COMPLETED :
10194 if (job->state_value > IPP_JOB_PROCESSING)
10195 {
10196 send_ipp_status(con, IPP_NOT_POSSIBLE,
10197 _("Job state cannot be changed."));
10198 return;
10199 }
10200 else if (con->response->request.status.status_code == IPP_OK)
10201 cupsdCancelJob(job, 0, (ipp_jstate_t)attr->values[0].integer);
10202 break;
10203 }
10204 }
10205 }
10206 else if (con->response->request.status.status_code != IPP_OK)
10207 continue;
10208 else if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10209 IPP_TAG_ZERO)) != NULL)
10210 {
10211 /*
10212 * Some other value; first free the old value...
10213 */
10214
10215 if (job->attrs->prev)
10216 job->attrs->prev->next = attr2->next;
10217 else
10218 job->attrs->attrs = attr2->next;
10219
10220 if (job->attrs->last == attr2)
10221 job->attrs->last = job->attrs->prev;
10222
10223 _ippFreeAttr(attr2);
10224
10225 /*
10226 * Then copy the attribute...
10227 */
10228
10229 copy_attribute(job->attrs, attr, 0);
10230
10231 /*
10232 * See if the job-name or job-hold-until is being changed.
10233 */
10234
10235 if (!strcmp(attr->name, "job-hold-until"))
10236 {
10237 cupsdSetJobHoldUntil(job, attr->values[0].string.text);
10238
10239 if (!strcmp(attr->values[0].string.text, "no-hold"))
10240 cupsdReleaseJob(job);
10241 else
10242 cupsdHoldJob(job);
10243
10244 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
10245 }
10246 }
10247 else if (attr->value_tag == IPP_TAG_DELETEATTR)
10248 {
10249 /*
10250 * Delete the attribute...
10251 */
10252
10253 if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10254 IPP_TAG_ZERO)) != NULL)
10255 {
10256 if (job->attrs->prev)
10257 job->attrs->prev->next = attr2->next;
10258 else
10259 job->attrs->attrs = attr2->next;
10260
10261 if (attr2 == job->attrs->last)
10262 job->attrs->last = job->attrs->prev;
10263
10264 _ippFreeAttr(attr2);
10265
10266 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
10267 }
10268 }
10269 else
10270 {
10271 /*
10272 * Add new option by copying it...
10273 */
10274
10275 copy_attribute(job->attrs, attr, 0);
10276
10277 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
10278 }
10279 }
10280
10281 /*
10282 * Save the job...
10283 */
10284
10285 job->dirty = 1;
10286 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
10287
10288 /*
10289 * Send events as needed...
10290 */
10291
10292 if (event & CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED)
10293 cupsdAddEvent(CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED,
10294 cupsdFindDest(job->dest), job,
10295 "Job priority changed by user.");
10296
10297 if (event & CUPSD_EVENT_JOB_STATE)
10298 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
10299 job->state_value == IPP_JOB_HELD ?
10300 "Job held by user." : "Job restarted by user.");
10301
10302 if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
10303 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
10304 "Job options changed by user.");
10305
10306 /*
10307 * Start jobs if possible...
10308 */
10309
10310 cupsdCheckJobs();
10311 }
10312
10313
10314 /*
10315 * 'set_printer_defaults()' - Set printer default options from a request.
10316 */
10317
10318 static void
10319 set_printer_defaults(
10320 cupsd_client_t *con, /* I - Client connection */
10321 cupsd_printer_t *printer) /* I - Printer */
10322 {
10323 int i; /* Looping var */
10324 ipp_attribute_t *attr; /* Current attribute */
10325 int namelen; /* Length of attribute name */
10326 char name[256], /* New attribute name */
10327 value[256]; /* String version of integer attrs */
10328
10329
10330 for (attr = con->request->attrs; attr; attr = attr->next)
10331 {
10332 /*
10333 * Skip non-printer attributes...
10334 */
10335
10336 if (attr->group_tag != IPP_TAG_PRINTER || !attr->name)
10337 continue;
10338
10339 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_defaults: %s", attr->name);
10340
10341 if (!strcmp(attr->name, "job-sheets-default"))
10342 {
10343 /*
10344 * Only allow keywords and names...
10345 */
10346
10347 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10348 continue;
10349
10350 /*
10351 * Only allow job-sheets-default to be set when running without a
10352 * system high classification level...
10353 */
10354
10355 if (Classification)
10356 continue;
10357
10358 cupsdSetString(&printer->job_sheets[0], attr->values[0].string.text);
10359
10360 if (attr->num_values > 1)
10361 cupsdSetString(&printer->job_sheets[1], attr->values[1].string.text);
10362 else
10363 cupsdSetString(&printer->job_sheets[1], "none");
10364 }
10365 else if (!strcmp(attr->name, "requesting-user-name-allowed"))
10366 {
10367 cupsdFreePrinterUsers(printer);
10368
10369 printer->deny_users = 0;
10370
10371 if (attr->value_tag == IPP_TAG_NAME &&
10372 (attr->num_values > 1 ||
10373 strcmp(attr->values[0].string.text, "all")))
10374 {
10375 for (i = 0; i < attr->num_values; i ++)
10376 cupsdAddPrinterUser(printer, attr->values[i].string.text);
10377 }
10378 }
10379 else if (!strcmp(attr->name, "requesting-user-name-denied"))
10380 {
10381 cupsdFreePrinterUsers(printer);
10382
10383 printer->deny_users = 1;
10384
10385 if (attr->value_tag == IPP_TAG_NAME &&
10386 (attr->num_values > 1 ||
10387 strcmp(attr->values[0].string.text, "none")))
10388 {
10389 for (i = 0; i < attr->num_values; i ++)
10390 cupsdAddPrinterUser(printer, attr->values[i].string.text);
10391 }
10392 }
10393 else if (!strcmp(attr->name, "job-quota-period"))
10394 {
10395 if (attr->value_tag != IPP_TAG_INTEGER)
10396 continue;
10397
10398 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-quota-period to %d...",
10399 attr->values[0].integer);
10400 cupsdFreeQuotas(printer);
10401
10402 printer->quota_period = attr->values[0].integer;
10403 }
10404 else if (!strcmp(attr->name, "job-k-limit"))
10405 {
10406 if (attr->value_tag != IPP_TAG_INTEGER)
10407 continue;
10408
10409 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-k-limit to %d...",
10410 attr->values[0].integer);
10411 cupsdFreeQuotas(printer);
10412
10413 printer->k_limit = attr->values[0].integer;
10414 }
10415 else if (!strcmp(attr->name, "job-page-limit"))
10416 {
10417 if (attr->value_tag != IPP_TAG_INTEGER)
10418 continue;
10419
10420 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-page-limit to %d...",
10421 attr->values[0].integer);
10422 cupsdFreeQuotas(printer);
10423
10424 printer->page_limit = attr->values[0].integer;
10425 }
10426 else if (!strcmp(attr->name, "printer-op-policy"))
10427 {
10428 cupsd_policy_t *p; /* Policy */
10429
10430
10431 if (attr->value_tag != IPP_TAG_NAME)
10432 continue;
10433
10434 if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
10435 {
10436 cupsdLogMessage(CUPSD_LOG_DEBUG,
10437 "Setting printer-op-policy to \"%s\"...",
10438 attr->values[0].string.text);
10439 cupsdSetString(&printer->op_policy, attr->values[0].string.text);
10440 printer->op_policy_ptr = p;
10441 }
10442 else
10443 {
10444 send_ipp_status(con, IPP_NOT_POSSIBLE,
10445 _("Unknown printer-op-policy \"%s\"."),
10446 attr->values[0].string.text);
10447 return;
10448 }
10449 }
10450 else if (!strcmp(attr->name, "printer-error-policy"))
10451 {
10452 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10453 continue;
10454
10455 if (strcmp(attr->values[0].string.text, "abort-job") &&
10456 strcmp(attr->values[0].string.text, "retry-job") &&
10457 strcmp(attr->values[0].string.text, "stop-printer"))
10458 {
10459 send_ipp_status(con, IPP_NOT_POSSIBLE,
10460 _("Unknown printer-error-policy \"%s\"."),
10461 attr->values[0].string.text);
10462 return;
10463 }
10464
10465 cupsdLogMessage(CUPSD_LOG_DEBUG,
10466 "Setting printer-error-policy to \"%s\"...",
10467 attr->values[0].string.text);
10468 cupsdSetString(&printer->error_policy, attr->values[0].string.text);
10469 }
10470
10471 /*
10472 * Skip any other non-default attributes...
10473 */
10474
10475 namelen = strlen(attr->name);
10476 if (namelen < 9 || strcmp(attr->name + namelen - 8, "-default") ||
10477 namelen > (sizeof(name) - 1) || attr->num_values != 1)
10478 continue;
10479
10480 /*
10481 * OK, anything else must be a user-defined default...
10482 */
10483
10484 strlcpy(name, attr->name, sizeof(name));
10485 name[namelen - 8] = '\0'; /* Strip "-default" */
10486
10487 switch (attr->value_tag)
10488 {
10489 case IPP_TAG_DELETEATTR :
10490 printer->num_options = cupsRemoveOption(name,
10491 printer->num_options,
10492 &(printer->options));
10493 cupsdLogMessage(CUPSD_LOG_DEBUG,
10494 "Deleting %s", attr->name);
10495 break;
10496
10497 case IPP_TAG_NAME :
10498 case IPP_TAG_KEYWORD :
10499 case IPP_TAG_URI :
10500 printer->num_options = cupsAddOption(name,
10501 attr->values[0].string.text,
10502 printer->num_options,
10503 &(printer->options));
10504 cupsdLogMessage(CUPSD_LOG_DEBUG,
10505 "Setting %s to \"%s\"...", attr->name,
10506 attr->values[0].string.text);
10507 break;
10508
10509 case IPP_TAG_BOOLEAN :
10510 printer->num_options = cupsAddOption(name,
10511 attr->values[0].boolean ?
10512 "true" : "false",
10513 printer->num_options,
10514 &(printer->options));
10515 cupsdLogMessage(CUPSD_LOG_DEBUG,
10516 "Setting %s to %s...", attr->name,
10517 attr->values[0].boolean ? "true" : "false");
10518 break;
10519
10520 case IPP_TAG_INTEGER :
10521 case IPP_TAG_ENUM :
10522 sprintf(value, "%d", attr->values[0].integer);
10523 printer->num_options = cupsAddOption(name, value,
10524 printer->num_options,
10525 &(printer->options));
10526 cupsdLogMessage(CUPSD_LOG_DEBUG,
10527 "Setting %s to %s...", attr->name, value);
10528 break;
10529
10530 case IPP_TAG_RANGE :
10531 sprintf(value, "%d-%d", attr->values[0].range.lower,
10532 attr->values[0].range.upper);
10533 printer->num_options = cupsAddOption(name, value,
10534 printer->num_options,
10535 &(printer->options));
10536 cupsdLogMessage(CUPSD_LOG_DEBUG,
10537 "Setting %s to %s...", attr->name, value);
10538 break;
10539
10540 case IPP_TAG_RESOLUTION :
10541 sprintf(value, "%dx%d%s", attr->values[0].resolution.xres,
10542 attr->values[0].resolution.yres,
10543 attr->values[0].resolution.units == IPP_RES_PER_INCH ?
10544 "dpi" : "dpc");
10545 printer->num_options = cupsAddOption(name, value,
10546 printer->num_options,
10547 &(printer->options));
10548 cupsdLogMessage(CUPSD_LOG_DEBUG,
10549 "Setting %s to %s...", attr->name, value);
10550 break;
10551
10552 default :
10553 /* Do nothing for other values */
10554 break;
10555 }
10556 }
10557 }
10558
10559
10560 /*
10561 * 'start_printer()' - Start a printer.
10562 */
10563
10564 static void
10565 start_printer(cupsd_client_t *con, /* I - Client connection */
10566 ipp_attribute_t *uri) /* I - Printer URI */
10567 {
10568 http_status_t status; /* Policy status */
10569 cups_ptype_t dtype; /* Destination type (printer/class) */
10570 cupsd_printer_t *printer; /* Printer data */
10571
10572
10573 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", con,
10574 con->http.fd, uri->values[0].string.text);
10575
10576 /*
10577 * Is the destination valid?
10578 */
10579
10580 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10581 {
10582 /*
10583 * Bad URI...
10584 */
10585
10586 send_ipp_status(con, IPP_NOT_FOUND,
10587 _("The printer or class was not found."));
10588 return;
10589 }
10590
10591 /*
10592 * Check policy...
10593 */
10594
10595 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10596 {
10597 send_http_error(con, status, printer);
10598 return;
10599 }
10600
10601 /*
10602 * Start the printer...
10603 */
10604
10605 printer->state_message[0] = '\0';
10606
10607 cupsdStartPrinter(printer, 1);
10608
10609 if (dtype & CUPS_PRINTER_CLASS)
10610 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".",
10611 printer->name, get_username(con));
10612 else
10613 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".",
10614 printer->name, get_username(con));
10615
10616 cupsdCheckJobs();
10617
10618 /*
10619 * Everything was ok, so return OK status...
10620 */
10621
10622 con->response->request.status.status_code = IPP_OK;
10623 }
10624
10625
10626 /*
10627 * 'stop_printer()' - Stop a printer.
10628 */
10629
10630 static void
10631 stop_printer(cupsd_client_t *con, /* I - Client connection */
10632 ipp_attribute_t *uri) /* I - Printer URI */
10633 {
10634 http_status_t status; /* Policy status */
10635 cups_ptype_t dtype; /* Destination type (printer/class) */
10636 cupsd_printer_t *printer; /* Printer data */
10637 ipp_attribute_t *attr; /* printer-state-message attribute */
10638
10639
10640 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", con,
10641 con->http.fd, uri->values[0].string.text);
10642
10643 /*
10644 * Is the destination valid?
10645 */
10646
10647 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10648 {
10649 /*
10650 * Bad URI...
10651 */
10652
10653 send_ipp_status(con, IPP_NOT_FOUND,
10654 _("The printer or class was not found."));
10655 return;
10656 }
10657
10658 /*
10659 * Check policy...
10660 */
10661
10662 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10663 {
10664 send_http_error(con, status, printer);
10665 return;
10666 }
10667
10668 /*
10669 * Stop the printer...
10670 */
10671
10672 if ((attr = ippFindAttribute(con->request, "printer-state-message",
10673 IPP_TAG_TEXT)) == NULL)
10674 strcpy(printer->state_message, "Paused");
10675 else
10676 {
10677 strlcpy(printer->state_message, attr->values[0].string.text,
10678 sizeof(printer->state_message));
10679 }
10680
10681 cupsdStopPrinter(printer, 1);
10682
10683 if (dtype & CUPS_PRINTER_CLASS)
10684 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".",
10685 printer->name, get_username(con));
10686 else
10687 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".",
10688 printer->name, get_username(con));
10689
10690 /*
10691 * Everything was ok, so return OK status...
10692 */
10693
10694 con->response->request.status.status_code = IPP_OK;
10695 }
10696
10697
10698 /*
10699 * 'url_encode_attr()' - URL-encode a string attribute.
10700 */
10701
10702 static void
10703 url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */
10704 char *buffer,/* I - String buffer */
10705 int bufsize)/* I - Size of buffer */
10706 {
10707 int i; /* Looping var */
10708 char *bufptr, /* Pointer into buffer */
10709 *bufend; /* End of buffer */
10710
10711
10712 strlcpy(buffer, attr->name, bufsize);
10713 bufptr = buffer + strlen(buffer);
10714 bufend = buffer + bufsize - 1;
10715
10716 for (i = 0; i < attr->num_values; i ++)
10717 {
10718 if (bufptr >= bufend)
10719 break;
10720
10721 if (i)
10722 *bufptr++ = ',';
10723 else
10724 *bufptr++ = '=';
10725
10726 if (bufptr >= bufend)
10727 break;
10728
10729 *bufptr++ = '\'';
10730
10731 bufptr = url_encode_string(attr->values[i].string.text,
10732 bufptr, bufend - bufptr + 1);
10733
10734 if (bufptr >= bufend)
10735 break;
10736
10737 *bufptr++ = '\'';
10738 }
10739
10740 *bufptr = '\0';
10741 }
10742
10743
10744 /*
10745 * 'url_encode_string()' - URL-encode a string.
10746 */
10747
10748 static char * /* O - End of string */
10749 url_encode_string(const char *s, /* I - String */
10750 char *buffer, /* I - String buffer */
10751 int bufsize) /* I - Size of buffer */
10752 {
10753 char *bufptr, /* Pointer into buffer */
10754 *bufend; /* End of buffer */
10755 static const char *hex = "0123456789ABCDEF";
10756 /* Hex digits */
10757
10758
10759 bufptr = buffer;
10760 bufend = buffer + bufsize - 1;
10761
10762 while (*s && bufptr < bufend)
10763 {
10764 if (*s == ' ' || *s == '%')
10765 {
10766 if (bufptr >= (bufend - 2))
10767 break;
10768
10769 *bufptr++ = '%';
10770 *bufptr++ = hex[(*s >> 4) & 15];
10771 *bufptr++ = hex[*s & 15];
10772
10773 s ++;
10774 }
10775 else if (*s == '\'' || *s == '\\')
10776 {
10777 if (bufptr >= (bufend - 1))
10778 break;
10779
10780 *bufptr++ = '\\';
10781 *bufptr++ = *s++;
10782 }
10783 else
10784 *bufptr++ = *s++;
10785 }
10786
10787 *bufptr = '\0';
10788
10789 return (bufptr);
10790 }
10791
10792
10793 /*
10794 * 'user_allowed()' - See if a user is allowed to print to a queue.
10795 */
10796
10797 static int /* O - 0 if not allowed, 1 if allowed */
10798 user_allowed(cupsd_printer_t *p, /* I - Printer or class */
10799 const char *username) /* I - Username */
10800 {
10801 int i; /* Looping var */
10802 struct passwd *pw; /* User password data */
10803 char baseuser[256], /* Base username */
10804 *baseptr; /* Pointer to "@" in base username */
10805
10806
10807 if (p->num_users == 0)
10808 return (1);
10809
10810 if (!strcmp(username, "root"))
10811 return (1);
10812
10813 if (strchr(username, '@'))
10814 {
10815 /*
10816 * Strip @REALM for username check...
10817 */
10818
10819 strlcpy(baseuser, username, sizeof(baseuser));
10820
10821 if ((baseptr = strchr(baseuser, '@')) != NULL)
10822 *baseptr = '\0';
10823
10824 username = baseuser;
10825 }
10826
10827 pw = getpwnam(username);
10828 endpwent();
10829
10830 for (i = 0; i < p->num_users; i ++)
10831 {
10832 if (p->users[i][0] == '@')
10833 {
10834 /*
10835 * Check group membership...
10836 */
10837
10838 if (cupsdCheckGroup(username, pw, p->users[i] + 1))
10839 break;
10840 }
10841 else if (p->users[i][0] == '#')
10842 {
10843 /*
10844 * Check UUID...
10845 */
10846
10847 if (cupsdCheckGroup(username, pw, p->users[i]))
10848 break;
10849 }
10850 else if (!strcasecmp(username, p->users[i]))
10851 break;
10852 }
10853
10854 return ((i < p->num_users) != p->deny_users);
10855 }
10856
10857
10858 /*
10859 * 'validate_job()' - Validate printer options and destination.
10860 */
10861
10862 static void
10863 validate_job(cupsd_client_t *con, /* I - Client connection */
10864 ipp_attribute_t *uri) /* I - Printer URI */
10865 {
10866 http_status_t status; /* Policy status */
10867 ipp_attribute_t *attr; /* Current attribute */
10868 ipp_attribute_t *format; /* Document-format attribute */
10869 cups_ptype_t dtype; /* Destination type (printer/class) */
10870 char super[MIME_MAX_SUPER],
10871 /* Supertype of file */
10872 type[MIME_MAX_TYPE];
10873 /* Subtype of file */
10874 cupsd_printer_t *printer; /* Printer */
10875
10876
10877 cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", con,
10878 con->http.fd, uri->values[0].string.text);
10879
10880 /*
10881 * OK, see if the client is sending the document compressed - CUPS
10882 * doesn't support compression yet...
10883 */
10884
10885 if ((attr = ippFindAttribute(con->request, "compression",
10886 IPP_TAG_KEYWORD)) != NULL &&
10887 !strcmp(attr->values[0].string.text, "none"))
10888 {
10889 send_ipp_status(con, IPP_ATTRIBUTES,
10890 _("Unsupported compression attribute %s!"),
10891 attr->values[0].string.text);
10892 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
10893 "compression", NULL, attr->values[0].string.text);
10894 return;
10895 }
10896
10897 /*
10898 * Is it a format we support?
10899 */
10900
10901 if ((format = ippFindAttribute(con->request, "document-format",
10902 IPP_TAG_MIMETYPE)) != NULL)
10903 {
10904 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
10905 super, type) != 2)
10906 {
10907 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"!"),
10908 format->values[0].string.text);
10909 return;
10910 }
10911
10912 if ((strcmp(super, "application") || strcmp(type, "octet-stream")) &&
10913 !mimeType(MimeDatabase, super, type))
10914 {
10915 cupsdLogMessage(CUPSD_LOG_INFO,
10916 "Hint: Do you have the raw file printing rules enabled?");
10917 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
10918 _("Unsupported format \"%s\"!"),
10919 format->values[0].string.text);
10920 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
10921 "document-format", NULL, format->values[0].string.text);
10922 return;
10923 }
10924 }
10925
10926 /*
10927 * Is the destination valid?
10928 */
10929
10930 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10931 {
10932 /*
10933 * Bad URI...
10934 */
10935
10936 send_ipp_status(con, IPP_NOT_FOUND,
10937 _("The printer or class was not found."));
10938 return;
10939 }
10940
10941 /*
10942 * Check policy...
10943 */
10944
10945 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10946 {
10947 send_http_error(con, status, printer);
10948 return;
10949 }
10950
10951 /*
10952 * Everything was ok, so return OK status...
10953 */
10954
10955 con->response->request.status.status_code = IPP_OK;
10956 }
10957
10958
10959 /*
10960 * 'validate_name()' - Make sure the printer name only contains valid chars.
10961 */
10962
10963 static int /* O - 0 if name is no good, 1 if good */
10964 validate_name(const char *name) /* I - Name to check */
10965 {
10966 const char *ptr; /* Pointer into name */
10967
10968
10969 /*
10970 * Scan the whole name...
10971 */
10972
10973 for (ptr = name; *ptr; ptr ++)
10974 if ((*ptr > 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
10975 return (0);
10976
10977 /*
10978 * All the characters are good; validate the length, too...
10979 */
10980
10981 return ((ptr - name) < 128);
10982 }
10983
10984
10985 /*
10986 * 'validate_user()' - Validate the user for the request.
10987 */
10988
10989 static int /* O - 1 if permitted, 0 otherwise */
10990 validate_user(cupsd_job_t *job, /* I - Job */
10991 cupsd_client_t *con, /* I - Client connection */
10992 const char *owner, /* I - Owner of job/resource */
10993 char *username, /* O - Authenticated username */
10994 int userlen) /* I - Length of username */
10995 {
10996 cupsd_printer_t *printer; /* Printer for job */
10997
10998
10999 cupsdLogMessage(CUPSD_LOG_DEBUG2,
11000 "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, "
11001 "userlen=%d)",
11002 job->id, con ? con->http.fd : 0,
11003 owner ? owner : "(null)", username, userlen);
11004
11005 /*
11006 * Validate input...
11007 */
11008
11009 if (!con || !owner || !username || userlen <= 0)
11010 return (0);
11011
11012 /*
11013 * Get the best authenticated username that is available.
11014 */
11015
11016 strlcpy(username, get_username(con), userlen);
11017
11018 /*
11019 * Check the username against the owner...
11020 */
11021
11022 printer = cupsdFindDest(job->dest);
11023
11024 return (cupsdCheckPolicy(printer ? printer->op_policy_ptr : DefaultPolicyPtr,
11025 con, owner) == HTTP_OK);
11026 }
11027
11028
11029 /*
11030 * End of "$Id: ipp.c 7682 2008-06-21 00:06:02Z mike $".
11031 */