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