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