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