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