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