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