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