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