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