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