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