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