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