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