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