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