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