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