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