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