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