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