]> 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 6145 2006-12-06 20:10:16Z 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 = cupsAddOption("PageSize", system_paper,
4024 num_defaults, &defaults);
4025 num_defaults = cupsAddOption("PageRegion", system_paper,
4026 num_defaults, &defaults);
4027 num_defaults = cupsAddOption("PaperDimension", system_paper,
4028 num_defaults, &defaults);
4029 num_defaults = cupsAddOption("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 {
5376 dtype &= CUPS_PRINTER_CLASS;
5377 dmask = CUPS_PRINTER_CLASS;
5378 }
5379
5380 /*
5381 * Check policy...
5382 */
5383
5384 if (printer)
5385 {
5386 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
5387 {
5388 send_http_error(con, status);
5389 return;
5390 }
5391 }
5392 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5393 {
5394 send_http_error(con, status);
5395 return;
5396 }
5397
5398 /*
5399 * See if the "which-jobs" attribute have been specified...
5400 */
5401
5402 if ((attr = ippFindAttribute(con->request, "which-jobs",
5403 IPP_TAG_KEYWORD)) != NULL &&
5404 !strcmp(attr->values[0].string.text, "completed"))
5405 {
5406 completed = 1;
5407 list = Jobs;
5408 }
5409 else if (attr && !strcmp(attr->values[0].string.text, "all"))
5410 {
5411 completed = 0;
5412 list = Jobs;
5413 }
5414 else
5415 {
5416 completed = 0;
5417 list = ActiveJobs;
5418 }
5419
5420 /*
5421 * See if they want to limit the number of jobs reported...
5422 */
5423
5424 if ((attr = ippFindAttribute(con->request, "limit",
5425 IPP_TAG_INTEGER)) != NULL)
5426 limit = attr->values[0].integer;
5427 else
5428 limit = 1000000;
5429
5430 if ((attr = ippFindAttribute(con->request, "first-job-id",
5431 IPP_TAG_INTEGER)) != NULL)
5432 first_job_id = attr->values[0].integer;
5433 else
5434 first_job_id = 1;
5435
5436 /*
5437 * See if we only want to see jobs for a specific user...
5438 */
5439
5440 if ((attr = ippFindAttribute(con->request, "my-jobs",
5441 IPP_TAG_BOOLEAN)) != NULL &&
5442 attr->values[0].boolean)
5443 strlcpy(username, get_username(con), sizeof(username));
5444 else
5445 username[0] = '\0';
5446
5447 ra = create_requested_array(con->request);
5448
5449 /*
5450 * OK, build a list of jobs for this printer...
5451 */
5452
5453 for (count = 0, job = (cupsd_job_t *)cupsArrayFirst(list);
5454 count < limit && job;
5455 job = (cupsd_job_t *)cupsArrayNext(list))
5456 {
5457 /*
5458 * Filter out jobs that don't match...
5459 */
5460
5461 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: job->id = %d", job->id);
5462
5463 if ((dest && strcmp(job->dest, dest)) &&
5464 (!job->printer || !dest || strcmp(job->printer->name, dest)))
5465 continue;
5466 if ((job->dtype & dmask) != dtype &&
5467 (!job->printer || (job->printer->type & dmask) != dtype))
5468 continue;
5469 if (username[0] && strcasecmp(username, job->username))
5470 continue;
5471
5472 if (completed && job->state_value <= IPP_JOB_STOPPED)
5473 continue;
5474
5475 if (job->id < first_job_id)
5476 continue;
5477
5478 cupsdLoadJob(job);
5479
5480 if (!job->attrs)
5481 continue;
5482
5483 if (count > 0)
5484 ippAddSeparator(con->response);
5485
5486 count ++;
5487
5488 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count = %d", count);
5489
5490 copy_job_attrs(con, job, ra);
5491 }
5492
5493 cupsArrayDelete(ra);
5494
5495 con->response->request.status.status_code = IPP_OK;
5496 }
5497
5498
5499 /*
5500 * 'get_notifications()' - Get events for a subscription.
5501 */
5502
5503 static void
5504 get_notifications(cupsd_client_t *con) /* I - Client connection */
5505 {
5506 int i, j; /* Looping vars */
5507 http_status_t status; /* Policy status */
5508 cupsd_subscription_t *sub; /* Subscription */
5509 ipp_attribute_t *ids, /* notify-subscription-ids */
5510 *sequences; /* notify-sequence-numbers */
5511 int min_seq; /* Minimum sequence number */
5512 int interval; /* Poll interval */
5513
5514
5515 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_subscription_attrs(con=%p[%d])",
5516 con, con->http.fd);
5517
5518 /*
5519 * Get subscription attributes...
5520 */
5521
5522 ids = ippFindAttribute(con->request, "notify-subscription-ids",
5523 IPP_TAG_INTEGER);
5524 sequences = ippFindAttribute(con->request, "notify-sequence-numbers",
5525 IPP_TAG_INTEGER);
5526
5527 if (!ids)
5528 {
5529 send_ipp_status(con, IPP_BAD_REQUEST,
5530 _("Missing notify-subscription-ids attribute!"));
5531 return;
5532 }
5533
5534 /*
5535 * Are the subscription IDs valid?
5536 */
5537
5538 for (i = 0, interval = 60; i < ids->num_values; i ++)
5539 {
5540 if ((sub = cupsdFindSubscription(ids->values[i].integer)) == NULL)
5541 {
5542 /*
5543 * Bad subscription ID...
5544 */
5545
5546 send_ipp_status(con, IPP_NOT_FOUND,
5547 _("notify-subscription-id %d no good!"),
5548 ids->values[i].integer);
5549 return;
5550 }
5551
5552 /*
5553 * Check policy...
5554 */
5555
5556 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
5557 DefaultPolicyPtr,
5558 con, sub->owner)) != HTTP_OK)
5559 {
5560 send_http_error(con, status);
5561 return;
5562 }
5563
5564 /*
5565 * Check the subscription type and update the interval accordingly.
5566 */
5567
5568 if (sub->job && sub->job->state_value == IPP_JOB_PROCESSING &&
5569 interval > 10)
5570 interval = 10;
5571 else if (sub->job && sub->job->state_value >= IPP_JOB_STOPPED)
5572 interval = 0;
5573 else if (sub->dest && sub->dest->state == IPP_PRINTER_PROCESSING &&
5574 interval > 30)
5575 interval = 30;
5576 }
5577
5578 /*
5579 * Tell the client to poll again in N seconds...
5580 */
5581
5582 if (interval > 0)
5583 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
5584 "notify-get-interval", interval);
5585
5586 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
5587 "printer-up-time", time(NULL));
5588
5589 /*
5590 * Copy the subscription event attributes to the response.
5591 */
5592
5593 con->response->request.status.status_code =
5594 interval ? IPP_OK : IPP_OK_EVENTS_COMPLETE;
5595
5596 for (i = 0; i < ids->num_values; i ++)
5597 {
5598 /*
5599 * Get the subscription and sequence number...
5600 */
5601
5602 sub = cupsdFindSubscription(ids->values[i].integer);
5603
5604 if (sequences && i < sequences->num_values)
5605 min_seq = sequences->values[i].integer;
5606 else
5607 min_seq = 1;
5608
5609 /*
5610 * If we don't have any new events, nothing to do here...
5611 */
5612
5613 if (min_seq > (sub->first_event_id + sub->num_events))
5614 continue;
5615
5616 /*
5617 * Otherwise copy all of the new events...
5618 */
5619
5620 if (sub->first_event_id > min_seq)
5621 j = 0;
5622 else
5623 j = min_seq - sub->first_event_id;
5624
5625 for (; j < sub->num_events; j ++)
5626 {
5627 ippAddSeparator(con->response);
5628
5629 copy_attrs(con->response, sub->events[j]->attrs, NULL,
5630 IPP_TAG_EVENT_NOTIFICATION, 0);
5631 }
5632 }
5633 }
5634
5635
5636 /*
5637 * 'get_ppds()' - Get the list of PPD files on the local system.
5638 */
5639
5640 static void
5641 get_ppds(cupsd_client_t *con) /* I - Client connection */
5642 {
5643 http_status_t status; /* Policy status */
5644 ipp_attribute_t *limit, /* Limit attribute */
5645 *make, /* ppd-make attribute */
5646 *requested; /* requested-attributes attribute */
5647 char command[1024], /* cups-deviced command */
5648 options[1024], /* Options to pass to command */
5649 requested_str[256],
5650 /* String for requested attributes */
5651 make_str[256]; /* Escaped ppd-make string */
5652
5653
5654 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->http.fd);
5655
5656 /*
5657 * Check policy...
5658 */
5659
5660 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5661 {
5662 send_http_error(con, status);
5663 return;
5664 }
5665
5666 /*
5667 * Run cups-driverd command with the given options...
5668 */
5669
5670 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
5671 make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
5672 requested = ippFindAttribute(con->request, "requested-attributes",
5673 IPP_TAG_KEYWORD);
5674
5675 if (requested)
5676 url_encode_attr(requested, requested_str, sizeof(requested_str));
5677 else
5678 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
5679
5680 if (make)
5681 url_encode_attr(make, make_str, sizeof(make_str));
5682 else
5683 make_str[0] = '\0';
5684
5685 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
5686 snprintf(options, sizeof(options), "list+%d+%d+%s%s%s",
5687 con->request->request.op.request_id,
5688 limit ? limit->values[0].integer : 0,
5689 requested_str, make ? "%20" : "", make_str);
5690
5691 if (cupsdSendCommand(con, command, options, 0))
5692 {
5693 /*
5694 * Command started successfully, don't send an IPP response here...
5695 */
5696
5697 ippDelete(con->response);
5698 con->response = NULL;
5699 }
5700 else
5701 {
5702 /*
5703 * Command failed, return "internal error" so the user knows something
5704 * went wrong...
5705 */
5706
5707 send_ipp_status(con, IPP_INTERNAL_ERROR,
5708 _("cups-driverd failed to execute."));
5709 }
5710 }
5711
5712
5713 /*
5714 * 'get_printer_attrs()' - Get printer attributes.
5715 */
5716
5717 static void
5718 get_printer_attrs(cupsd_client_t *con, /* I - Client connection */
5719 ipp_attribute_t *uri) /* I - Printer URI */
5720 {
5721 http_status_t status; /* Policy status */
5722 cups_ptype_t dtype; /* Destination type (printer or class) */
5723 char method[HTTP_MAX_URI],
5724 /* Method portion of URI */
5725 username[HTTP_MAX_URI],
5726 /* Username portion of URI */
5727 host[HTTP_MAX_URI],
5728 /* Host portion of URI */
5729 resource[HTTP_MAX_URI];
5730 /* Resource portion of URI */
5731 int port; /* Port portion of URI */
5732 cupsd_printer_t *printer; /* Printer/class */
5733 cups_array_t *ra; /* Requested attributes array */
5734
5735
5736 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", con,
5737 con->http.fd, uri->values[0].string.text);
5738
5739 /*
5740 * Is the destination valid?
5741 */
5742
5743 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
5744 sizeof(method), username, sizeof(username), host,
5745 sizeof(host), &port, resource, sizeof(resource));
5746
5747 if (!cupsdValidateDest(host, resource, &dtype, &printer))
5748 {
5749 /*
5750 * Bad URI...
5751 */
5752
5753 send_ipp_status(con, IPP_NOT_FOUND,
5754 _("The printer or class was not found."));
5755 return;
5756 }
5757
5758 /*
5759 * Check policy...
5760 */
5761
5762 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
5763 {
5764 send_http_error(con, status);
5765 return;
5766 }
5767
5768 /*
5769 * Send the attributes...
5770 */
5771
5772 ra = create_requested_array(con->request);
5773
5774 copy_printer_attrs(con, printer, ra);
5775
5776 cupsArrayDelete(ra);
5777
5778 con->response->request.status.status_code = IPP_OK;
5779 }
5780
5781
5782 /*
5783 * 'get_printers()' - Get a list of printers or classes.
5784 */
5785
5786 static void
5787 get_printers(cupsd_client_t *con, /* I - Client connection */
5788 int type) /* I - 0 or CUPS_PRINTER_CLASS */
5789 {
5790 http_status_t status; /* Policy status */
5791 ipp_attribute_t *attr; /* Current attribute */
5792 int limit; /* Maximum number of printers to return */
5793 int count; /* Number of printers that match */
5794 cupsd_printer_t *printer; /* Current printer pointer */
5795 int printer_type, /* printer-type attribute */
5796 printer_mask; /* printer-type-mask attribute */
5797 char *location; /* Location string */
5798 const char *username; /* Current user */
5799 char *first_printer_name; /* first-printer-name attribute */
5800 cups_array_t *ra; /* Requested attributes array */
5801
5802
5803 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con,
5804 con->http.fd, type);
5805
5806 /*
5807 * Check policy...
5808 */
5809
5810 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5811 {
5812 send_http_error(con, status);
5813 return;
5814 }
5815
5816 /*
5817 * Check for printers...
5818 */
5819
5820 if (!Printers || !cupsArrayCount(Printers))
5821 {
5822 send_ipp_status(con, IPP_NOT_FOUND, _("No destinations added."));
5823 return;
5824 }
5825
5826 /*
5827 * See if they want to limit the number of printers reported...
5828 */
5829
5830 if ((attr = ippFindAttribute(con->request, "limit",
5831 IPP_TAG_INTEGER)) != NULL)
5832 limit = attr->values[0].integer;
5833 else
5834 limit = 10000000;
5835
5836 if ((attr = ippFindAttribute(con->request, "first-printer-name",
5837 IPP_TAG_NAME)) != NULL)
5838 first_printer_name = attr->values[0].string.text;
5839 else
5840 first_printer_name = NULL;
5841
5842 /*
5843 * Support filtering...
5844 */
5845
5846 if ((attr = ippFindAttribute(con->request, "printer-type",
5847 IPP_TAG_ENUM)) != NULL)
5848 printer_type = attr->values[0].integer;
5849 else
5850 printer_type = 0;
5851
5852 if ((attr = ippFindAttribute(con->request, "printer-type-mask",
5853 IPP_TAG_ENUM)) != NULL)
5854 printer_mask = attr->values[0].integer;
5855 else
5856 printer_mask = 0;
5857
5858 if ((attr = ippFindAttribute(con->request, "printer-location",
5859 IPP_TAG_TEXT)) != NULL)
5860 location = attr->values[0].string.text;
5861 else
5862 location = NULL;
5863
5864 if (con->username[0])
5865 username = con->username;
5866 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
5867 IPP_TAG_NAME)) != NULL)
5868 username = attr->values[0].string.text;
5869 else
5870 username = NULL;
5871
5872 ra = create_requested_array(con->request);
5873
5874 /*
5875 * OK, build a list of printers for this printer...
5876 */
5877
5878 if (first_printer_name)
5879 {
5880 if ((printer = cupsdFindDest(first_printer_name)) == NULL)
5881 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
5882 }
5883 else
5884 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
5885
5886 for (count = 0;
5887 count < limit && printer;
5888 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
5889 {
5890 if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
5891 (printer->type & printer_mask) == printer_type &&
5892 (!location || !printer->location ||
5893 !strcasecmp(printer->location, location)))
5894 {
5895 /*
5896 * If HideImplicitMembers is enabled, see if this printer or class
5897 * is a member of an implicit class...
5898 */
5899
5900 if (ImplicitClasses && HideImplicitMembers &&
5901 printer->in_implicit_class)
5902 continue;
5903
5904 /*
5905 * If a username is specified, see if it is allowed or denied
5906 * access...
5907 */
5908
5909 if (printer->num_users && username && !user_allowed(printer, username))
5910 continue;
5911
5912 /*
5913 * Add the group separator as needed...
5914 */
5915
5916 if (count > 0)
5917 ippAddSeparator(con->response);
5918
5919 count ++;
5920
5921 /*
5922 * Send the attributes...
5923 */
5924
5925 copy_printer_attrs(con, printer, ra);
5926 }
5927 }
5928
5929 cupsArrayDelete(ra);
5930
5931 con->response->request.status.status_code = IPP_OK;
5932 }
5933
5934
5935 /*
5936 * 'get_subscription_attrs()' - Get subscription attributes.
5937 */
5938
5939 static void
5940 get_subscription_attrs(
5941 cupsd_client_t *con, /* I - Client connection */
5942 int sub_id) /* I - Subscription ID */
5943 {
5944 http_status_t status; /* Policy status */
5945 cupsd_subscription_t *sub; /* Subscription */
5946 cups_array_t *ra; /* Requested attributes array */
5947
5948
5949 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5950 "get_subscription_attrs(con=%p[%d], sub_id=%d)",
5951 con, con->http.fd, sub_id);
5952
5953 /*
5954 * Is the subscription ID valid?
5955 */
5956
5957 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
5958 {
5959 /*
5960 * Bad subscription ID...
5961 */
5962
5963 send_ipp_status(con, IPP_NOT_FOUND,
5964 _("notify-subscription-id %d no good!"), sub_id);
5965 return;
5966 }
5967
5968 /*
5969 * Check policy...
5970 */
5971
5972 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
5973 DefaultPolicyPtr,
5974 con, sub->owner)) != HTTP_OK)
5975 {
5976 send_http_error(con, status);
5977 return;
5978 }
5979
5980 /*
5981 * Copy the subscription attributes to the response using the
5982 * requested-attributes attribute that may be provided by the client.
5983 */
5984
5985 ra = create_requested_array(con->request);
5986
5987 copy_subscription_attrs(con, sub, ra);
5988
5989 cupsArrayDelete(ra);
5990
5991 con->response->request.status.status_code = IPP_OK;
5992 }
5993
5994
5995 /*
5996 * 'get_subscriptions()' - Get subscriptions.
5997 */
5998
5999 static void
6000 get_subscriptions(cupsd_client_t *con, /* I - Client connection */
6001 ipp_attribute_t *uri) /* I - Printer/job URI */
6002 {
6003 http_status_t status; /* Policy status */
6004 int count; /* Number of subscriptions */
6005 int limit; /* Limit */
6006 cupsd_subscription_t *sub; /* Subscription */
6007 cups_array_t *ra; /* Requested attributes array */
6008 ipp_attribute_t *attr; /* Attribute */
6009 cups_ptype_t dtype; /* Destination type (printer or class) */
6010 char method[HTTP_MAX_URI],
6011 /* Method portion of URI */
6012 username[HTTP_MAX_URI],
6013 /* Username portion of URI */
6014 host[HTTP_MAX_URI],
6015 /* Host portion of URI */
6016 resource[HTTP_MAX_URI];
6017 /* Resource portion of URI */
6018 int port; /* Port portion of URI */
6019 cupsd_job_t *job; /* Job pointer */
6020 cupsd_printer_t *printer; /* Printer */
6021
6022
6023 cupsdLogMessage(CUPSD_LOG_DEBUG2,
6024 "get_subscriptions(con=%p[%d], uri=%s)",
6025 con, con->http.fd, uri->values[0].string.text);
6026
6027 /*
6028 * Is the destination valid?
6029 */
6030
6031 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
6032 sizeof(method), username, sizeof(username), host,
6033 sizeof(host), &port, resource, sizeof(resource));
6034
6035 if (!strcmp(resource, "/") ||
6036 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6) ||
6037 (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) ||
6038 (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9))
6039 {
6040 printer = NULL;
6041 job = NULL;
6042 }
6043 else if (!strncmp(resource, "/jobs/", 6) && resource[6])
6044 {
6045 printer = NULL;
6046 job = cupsdFindJob(atoi(resource + 6));
6047
6048 if (!job)
6049 {
6050 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%s does not exist!"),
6051 resource + 6);
6052 return;
6053 }
6054 }
6055 else if (!cupsdValidateDest(host, resource, &dtype, &printer))
6056 {
6057 /*
6058 * Bad URI...
6059 */
6060
6061 send_ipp_status(con, IPP_NOT_FOUND,
6062 _("The printer or class was not found."));
6063 return;
6064 }
6065 else if ((attr = ippFindAttribute(con->request, "notify-job-id",
6066 IPP_TAG_INTEGER)) != NULL)
6067 {
6068 job = cupsdFindJob(attr->values[0].integer);
6069
6070 if (!job)
6071 {
6072 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"),
6073 attr->values[0].integer);
6074 return;
6075 }
6076 }
6077 else
6078 job = NULL;
6079
6080 /*
6081 * Check policy...
6082 */
6083
6084 if ((status = cupsdCheckPolicy(printer ? printer->op_policy_ptr :
6085 DefaultPolicyPtr,
6086 con, NULL)) != HTTP_OK)
6087 {
6088 send_http_error(con, status);
6089 return;
6090 }
6091
6092 /*
6093 * Copy the subscription attributes to the response using the
6094 * requested-attributes attribute that may be provided by the client.
6095 */
6096
6097 ra = create_requested_array(con->request);
6098
6099 if ((attr = ippFindAttribute(con->request, "limit",
6100 IPP_TAG_INTEGER)) != NULL)
6101 limit = attr->values[0].integer;
6102 else
6103 limit = 0;
6104
6105 /*
6106 * See if we only want to see subscriptions for a specific user...
6107 */
6108
6109 if ((attr = ippFindAttribute(con->request, "my-subscriptions",
6110 IPP_TAG_BOOLEAN)) != NULL &&
6111 attr->values[0].boolean)
6112 strlcpy(username, get_username(con), sizeof(username));
6113 else
6114 username[0] = '\0';
6115
6116 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0;
6117 sub;
6118 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
6119 if ((!printer || sub->dest == printer) && (!job || sub->job == job) &&
6120 (!username[0] || !strcasecmp(username, sub->owner)))
6121 {
6122 ippAddSeparator(con->response);
6123 copy_subscription_attrs(con, sub, ra);
6124
6125 count ++;
6126 if (limit && count >= limit)
6127 break;
6128 }
6129
6130 cupsArrayDelete(ra);
6131
6132 if (count)
6133 con->response->request.status.status_code = IPP_OK;
6134 else
6135 send_ipp_status(con, IPP_NOT_FOUND, _("No subscriptions found."));
6136 }
6137
6138
6139 /*
6140 * 'get_username()' - Get the username associated with a request.
6141 */
6142
6143 static const char * /* O - Username */
6144 get_username(cupsd_client_t *con) /* I - Connection */
6145 {
6146 ipp_attribute_t *attr; /* Attribute */
6147
6148
6149 if (con->username[0])
6150 return (con->username);
6151 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
6152 IPP_TAG_NAME)) != NULL)
6153 return (attr->values[0].string.text);
6154 else
6155 return ("anonymous");
6156 }
6157
6158
6159 /*
6160 * 'hold_job()' - Hold a print job.
6161 */
6162
6163 static void
6164 hold_job(cupsd_client_t *con, /* I - Client connection */
6165 ipp_attribute_t *uri) /* I - Job or Printer URI */
6166 {
6167 ipp_attribute_t *attr, /* Current job-hold-until */
6168 *newattr; /* New job-hold-until */
6169 int jobid; /* Job ID */
6170 char method[HTTP_MAX_URI], /* Method portion of URI */
6171 username[HTTP_MAX_URI], /* Username portion of URI */
6172 host[HTTP_MAX_URI], /* Host portion of URI */
6173 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6174 int port; /* Port portion of URI */
6175 cupsd_job_t *job; /* Job information */
6176
6177
6178 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", con, con->http.fd,
6179 uri->values[0].string.text);
6180
6181 /*
6182 * See if we have a job URI or a printer URI...
6183 */
6184
6185 if (!strcmp(uri->name, "printer-uri"))
6186 {
6187 /*
6188 * Got a printer URI; see if we also have a job-id attribute...
6189 */
6190
6191 if ((attr = ippFindAttribute(con->request, "job-id",
6192 IPP_TAG_INTEGER)) == NULL)
6193 {
6194 send_ipp_status(con, IPP_BAD_REQUEST,
6195 _("Got a printer-uri attribute but no job-id!"));
6196 return;
6197 }
6198
6199 jobid = attr->values[0].integer;
6200 }
6201 else
6202 {
6203 /*
6204 * Got a job URI; parse it to get the job ID...
6205 */
6206
6207 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
6208 sizeof(method), username, sizeof(username), host,
6209 sizeof(host), &port, resource, sizeof(resource));
6210
6211 if (strncmp(resource, "/jobs/", 6))
6212 {
6213 /*
6214 * Not a valid URI!
6215 */
6216
6217 send_ipp_status(con, IPP_BAD_REQUEST,
6218 _("Bad job-uri attribute \"%s\"!"),
6219 uri->values[0].string.text);
6220 return;
6221 }
6222
6223 jobid = atoi(resource + 6);
6224 }
6225
6226 /*
6227 * See if the job exists...
6228 */
6229
6230 if ((job = cupsdFindJob(jobid)) == NULL)
6231 {
6232 /*
6233 * Nope - return a "not found" error...
6234 */
6235
6236 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
6237 return;
6238 }
6239
6240 /*
6241 * See if the job is owned by the requesting user...
6242 */
6243
6244 if (!validate_user(job, con, job->username, username, sizeof(username)))
6245 {
6246 send_http_error(con, HTTP_UNAUTHORIZED);
6247 return;
6248 }
6249
6250 /*
6251 * Hold the job and return...
6252 */
6253
6254 cupsdHoldJob(job);
6255
6256 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
6257 "Job held by user.");
6258
6259 if ((newattr = ippFindAttribute(con->request, "job-hold-until",
6260 IPP_TAG_KEYWORD)) == NULL)
6261 newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
6262
6263 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
6264 IPP_TAG_KEYWORD)) == NULL)
6265 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
6266
6267 if (attr)
6268 {
6269 /*
6270 * Free the old hold value and copy the new one over...
6271 */
6272
6273 _cupsStrFree(attr->values[0].string.text);
6274
6275 if (newattr)
6276 {
6277 attr->value_tag = newattr->value_tag;
6278 attr->values[0].string.text =
6279 _cupsStrAlloc(newattr->values[0].string.text);
6280 }
6281 else
6282 {
6283 attr->value_tag = IPP_TAG_KEYWORD;
6284 attr->values[0].string.text = _cupsStrAlloc("indefinite");
6285 }
6286
6287 /*
6288 * Hold job until specified time...
6289 */
6290
6291 cupsdSetJobHoldUntil(job, attr->values[0].string.text);
6292
6293 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
6294 "Job job-hold-until value changed by user.");
6295 }
6296
6297 cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was held by \"%s\".", jobid,
6298 username);
6299
6300 con->response->request.status.status_code = IPP_OK;
6301 }
6302
6303
6304 /*
6305 * 'move_job()' - Move a job to a new destination.
6306 */
6307
6308 static void
6309 move_job(cupsd_client_t *con, /* I - Client connection */
6310 ipp_attribute_t *uri) /* I - Job URI */
6311 {
6312 http_status_t status; /* Policy status */
6313 ipp_attribute_t *attr; /* Current attribute */
6314 int jobid; /* Job ID */
6315 cupsd_job_t *job; /* Current job */
6316 const char *src; /* Source printer/class */
6317 cups_ptype_t stype, /* Source type (printer or class) */
6318 dtype; /* Destination type (printer or class) */
6319 char method[HTTP_MAX_URI], /* Method portion of URI */
6320 username[HTTP_MAX_URI], /* Username portion of URI */
6321 host[HTTP_MAX_URI], /* Host portion of URI */
6322 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6323 int port; /* Port portion of URI */
6324 cupsd_printer_t *sprinter, /* Source printer */
6325 *dprinter; /* Destination printer */
6326
6327
6328 cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", con, con->http.fd,
6329 uri->values[0].string.text);
6330
6331 /*
6332 * Get the new printer or class...
6333 */
6334
6335 if ((attr = ippFindAttribute(con->request, "job-printer-uri",
6336 IPP_TAG_URI)) == NULL)
6337 {
6338 /*
6339 * Need job-printer-uri...
6340 */
6341
6342 send_ipp_status(con, IPP_BAD_REQUEST,
6343 _("job-printer-uri attribute missing!"));
6344 return;
6345 }
6346
6347 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, method,
6348 sizeof(method), username, sizeof(username), host,
6349 sizeof(host), &port, resource, sizeof(resource));
6350
6351 if (!cupsdValidateDest(host, resource, &dtype, &dprinter))
6352 {
6353 /*
6354 * Bad URI...
6355 */
6356
6357 send_ipp_status(con, IPP_NOT_FOUND,
6358 _("The printer or class was not found."));
6359 return;
6360 }
6361
6362 /*
6363 * Check policy...
6364 */
6365
6366 if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con, NULL)) != HTTP_OK)
6367 {
6368 send_http_error(con, status);
6369 return;
6370 }
6371
6372 /*
6373 * See if we have a job URI or a printer URI...
6374 */
6375
6376 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
6377 sizeof(method), username, sizeof(username), host,
6378 sizeof(host), &port, resource, sizeof(resource));
6379
6380 if (!strcmp(uri->name, "printer-uri"))
6381 {
6382 /*
6383 * Got a printer URI; see if we also have a job-id attribute...
6384 */
6385
6386 if ((attr = ippFindAttribute(con->request, "job-id",
6387 IPP_TAG_INTEGER)) == NULL)
6388 {
6389 /*
6390 * Move all jobs...
6391 */
6392
6393 if ((src = cupsdValidateDest(host, resource, &stype, &sprinter)) == NULL)
6394 {
6395 /*
6396 * Bad URI...
6397 */
6398
6399 send_ipp_status(con, IPP_NOT_FOUND,
6400 _("The printer or class was not found."));
6401 return;
6402 }
6403
6404 job = NULL;
6405 }
6406 else
6407 {
6408 /*
6409 * Otherwise, just move a single job...
6410 */
6411
6412 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
6413 {
6414 /*
6415 * Nope - return a "not found" error...
6416 */
6417
6418 send_ipp_status(con, IPP_NOT_FOUND,
6419 _("Job #%d does not exist!"), attr->values[0].integer);
6420 return;
6421 }
6422 else
6423 {
6424 /*
6425 * Job found, initialize source pointers...
6426 */
6427
6428 src = NULL;
6429 sprinter = NULL;
6430 }
6431 }
6432 }
6433 else
6434 {
6435 /*
6436 * Got a job URI; parse it to get the job ID...
6437 */
6438
6439 if (strncmp(resource, "/jobs/", 6))
6440 {
6441 /*
6442 * Not a valid URI!
6443 */
6444
6445 send_ipp_status(con, IPP_BAD_REQUEST,
6446 _("Bad job-uri attribute \"%s\"!"),
6447 uri->values[0].string.text);
6448 return;
6449 }
6450
6451 /*
6452 * See if the job exists...
6453 */
6454
6455 jobid = atoi(resource + 6);
6456
6457 if ((job = cupsdFindJob(jobid)) == NULL)
6458 {
6459 /*
6460 * Nope - return a "not found" error...
6461 */
6462
6463 send_ipp_status(con, IPP_NOT_FOUND,
6464 _("Job #%d does not exist!"), jobid);
6465 return;
6466 }
6467 else
6468 {
6469 /*
6470 * Job found, initialize source pointers...
6471 */
6472
6473 src = NULL;
6474 sprinter = NULL;
6475 }
6476 }
6477
6478 /*
6479 * Now move the job or jobs...
6480 */
6481
6482 if (job)
6483 {
6484 /*
6485 * See if the job has been completed...
6486 */
6487
6488 if (job->state_value > IPP_JOB_STOPPED)
6489 {
6490 /*
6491 * Return a "not-possible" error...
6492 */
6493
6494 send_ipp_status(con, IPP_NOT_POSSIBLE,
6495 _("Job #%d is finished and cannot be altered!"),
6496 job->id);
6497 return;
6498 }
6499
6500 /*
6501 * See if the job is owned by the requesting user...
6502 */
6503
6504 if (!validate_user(job, con, job->username, username, sizeof(username)))
6505 {
6506 send_http_error(con, HTTP_UNAUTHORIZED);
6507 return;
6508 }
6509
6510 /*
6511 * Move the job to a different printer or class...
6512 */
6513
6514 cupsdMoveJob(job, dprinter);
6515 }
6516 else
6517 {
6518 /*
6519 * Got the source printer, now look through the jobs...
6520 */
6521
6522 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
6523 job;
6524 job = (cupsd_job_t *)cupsArrayNext(Jobs))
6525 {
6526 /*
6527 * See if the job is pointing at the source printer or has not been
6528 * completed...
6529 */
6530
6531 if (strcasecmp(job->dest, src) ||
6532 job->state_value > IPP_JOB_STOPPED)
6533 continue;
6534
6535 /*
6536 * See if the job can be moved by the requesting user...
6537 */
6538
6539 if (!validate_user(job, con, job->username, username, sizeof(username)))
6540 continue;
6541
6542 /*
6543 * Move the job to a different printer or class...
6544 */
6545
6546 cupsdMoveJob(job, dprinter);
6547 }
6548 }
6549
6550 /*
6551 * Start jobs if possible...
6552 */
6553
6554 cupsdCheckJobs();
6555
6556 /*
6557 * Return with "everything is OK" status...
6558 */
6559
6560 con->response->request.status.status_code = IPP_OK;
6561 }
6562
6563
6564 /*
6565 * 'ppd_parse_line()' - Parse a PPD default line.
6566 */
6567
6568 static int /* O - 0 on success, -1 on failure */
6569 ppd_parse_line(const char *line, /* I - Line */
6570 char *option, /* O - Option name */
6571 int olen, /* I - Size of option name */
6572 char *choice, /* O - Choice name */
6573 int clen) /* I - Size of choice name */
6574 {
6575 /*
6576 * Verify this is a default option line...
6577 */
6578
6579 if (strncmp(line, "*Default", 8))
6580 return (-1);
6581
6582 /*
6583 * Read the option name...
6584 */
6585
6586 for (line += 8, olen --; isalnum(*line & 255); line ++)
6587 if (olen > 0)
6588 {
6589 *option++ = *line;
6590 olen --;
6591 }
6592
6593 *option = '\0';
6594
6595 /*
6596 * Skip everything else up to the colon (:)...
6597 */
6598
6599 while (*line && *line != ':')
6600 line ++;
6601
6602 if (!*line)
6603 return (-1);
6604
6605 line ++;
6606
6607 /*
6608 * Now grab the option choice, skipping leading whitespace...
6609 */
6610
6611 while (isspace(*line & 255))
6612 line ++;
6613
6614 for (clen --; isalnum(*line & 255); line ++)
6615 if (clen > 0)
6616 {
6617 *choice++ = *line;
6618 clen --;
6619 }
6620
6621 *choice = '\0';
6622
6623 /*
6624 * Return with no errors...
6625 */
6626
6627 return (0);
6628 }
6629
6630
6631 /*
6632 * 'print_job()' - Print a file to a printer or class.
6633 */
6634
6635 static void
6636 print_job(cupsd_client_t *con, /* I - Client connection */
6637 ipp_attribute_t *uri) /* I - Printer URI */
6638 {
6639 ipp_attribute_t *attr; /* Current attribute */
6640 ipp_attribute_t *format; /* Document-format attribute */
6641 cupsd_job_t *job; /* New job */
6642 char filename[1024]; /* Job filename */
6643 mime_type_t *filetype; /* Type of file */
6644 char super[MIME_MAX_SUPER], /* Supertype of file */
6645 type[MIME_MAX_TYPE], /* Subtype of file */
6646 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
6647 /* Textual name of mime type */
6648 cupsd_printer_t *printer; /* Printer data */
6649 struct stat fileinfo; /* File information */
6650 int kbytes; /* Size of file */
6651 int compression; /* Document compression */
6652
6653
6654 cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", con, con->http.fd,
6655 uri->values[0].string.text);
6656
6657 /*
6658 * Validate print file attributes, for now just document-format and
6659 * compression (CUPS only supports "none" and "gzip")...
6660 */
6661
6662 compression = CUPS_FILE_NONE;
6663
6664 if ((attr = ippFindAttribute(con->request, "compression",
6665 IPP_TAG_KEYWORD)) != NULL)
6666 {
6667 if (strcmp(attr->values[0].string.text, "none")
6668 #ifdef HAVE_LIBZ
6669 && strcmp(attr->values[0].string.text, "gzip")
6670 #endif /* HAVE_LIBZ */
6671 )
6672 {
6673 send_ipp_status(con, IPP_ATTRIBUTES,
6674 _("Unsupported compression \"%s\"!"),
6675 attr->values[0].string.text);
6676 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
6677 "compression", NULL, attr->values[0].string.text);
6678 return;
6679 }
6680
6681 #ifdef HAVE_LIBZ
6682 if (!strcmp(attr->values[0].string.text, "gzip"))
6683 compression = CUPS_FILE_GZIP;
6684 #endif /* HAVE_LIBZ */
6685 }
6686
6687 /*
6688 * Do we have a file to print?
6689 */
6690
6691 if (!con->filename)
6692 {
6693 send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?!"));
6694 return;
6695 }
6696
6697 /*
6698 * Is it a format we support?
6699 */
6700
6701 if ((format = ippFindAttribute(con->request, "document-format",
6702 IPP_TAG_MIMETYPE)) != NULL)
6703 {
6704 /*
6705 * Grab format from client...
6706 */
6707
6708 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
6709 {
6710 send_ipp_status(con, IPP_BAD_REQUEST,
6711 _("Could not scan type \"%s\"!"),
6712 format->values[0].string.text);
6713 return;
6714 }
6715 }
6716 else
6717 {
6718 /*
6719 * No document format attribute? Auto-type it!
6720 */
6721
6722 strcpy(super, "application");
6723 strcpy(type, "octet-stream");
6724 }
6725
6726 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
6727 {
6728 /*
6729 * Auto-type the file...
6730 */
6731
6732 ipp_attribute_t *doc_name; /* document-name attribute */
6733
6734
6735 cupsdLogMessage(CUPSD_LOG_DEBUG, "print_job: auto-typing file...");
6736
6737 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
6738 filetype = mimeFileType(MimeDatabase, con->filename,
6739 doc_name ? doc_name->values[0].string.text : NULL,
6740 &compression);
6741
6742 if (filetype)
6743 {
6744 /*
6745 * Replace the document-format attribute value with the auto-typed one.
6746 */
6747
6748 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
6749 filetype->type);
6750
6751 if (format)
6752 {
6753 _cupsStrFree(format->values[0].string.text);
6754
6755 format->values[0].string.text = _cupsStrAlloc(mimetype);
6756 }
6757 else
6758 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
6759 "document-format", NULL, mimetype);
6760 }
6761 else
6762 filetype = mimeType(MimeDatabase, super, type);
6763 }
6764 else
6765 filetype = mimeType(MimeDatabase, super, type);
6766
6767 if (!filetype)
6768 {
6769 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
6770 _("Unsupported format \'%s/%s\'!"), super, type);
6771 cupsdLogMessage(CUPSD_LOG_INFO,
6772 "Hint: Do you have the raw file printing rules enabled?");
6773
6774 if (format)
6775 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
6776 "document-format", NULL, format->values[0].string.text);
6777
6778 return;
6779 }
6780
6781 cupsdLogMessage(CUPSD_LOG_DEBUG, "print_job: request file type is %s/%s.",
6782 filetype->super, filetype->type);
6783
6784 /*
6785 * Read any embedded job ticket info from PS files...
6786 */
6787
6788 if (!strcasecmp(filetype->super, "application") &&
6789 !strcasecmp(filetype->type, "postscript"))
6790 read_ps_job_ticket(con);
6791
6792 /*
6793 * Create the job object...
6794 */
6795
6796 if ((job = add_job(con, uri, &printer, filetype)) == NULL)
6797 return;
6798
6799 /*
6800 * Update quota data...
6801 */
6802
6803 if (stat(con->filename, &fileinfo))
6804 kbytes = 0;
6805 else
6806 kbytes = (fileinfo.st_size + 1023) / 1024;
6807
6808 cupsdUpdateQuota(printer, job->username, 0, kbytes);
6809
6810 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
6811 IPP_TAG_INTEGER)) != NULL)
6812 attr->values[0].integer += kbytes;
6813
6814 /*
6815 * Add the job file...
6816 */
6817
6818 if (add_file(con, job, filetype, compression))
6819 return;
6820
6821 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
6822 job->num_files);
6823 rename(con->filename, filename);
6824 cupsdClearString(&con->filename);
6825
6826 /*
6827 * See if we need to add the ending sheet...
6828 */
6829
6830 attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
6831
6832 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
6833 attr && attr->num_values > 1)
6834 {
6835 /*
6836 * Yes...
6837 */
6838
6839 cupsdLogMessage(CUPSD_LOG_INFO, "Adding end banner page \"%s\" to job %d.",
6840 attr->values[1].string.text, job->id);
6841
6842 kbytes = copy_banner(con, job, attr->values[1].string.text);
6843
6844 cupsdUpdateQuota(printer, job->username, 0, kbytes);
6845 }
6846
6847 /*
6848 * Log and save the job...
6849 */
6850
6851 cupsdLogMessage(CUPSD_LOG_INFO, "Job %d queued on \"%s\" by \"%s\".", job->id,
6852 job->dest, job->username);
6853 cupsdLogMessage(CUPSD_LOG_DEBUG, "Job %d hold_until = %d", job->id,
6854 (int)job->hold_until);
6855
6856 cupsdSaveJob(job);
6857
6858 /*
6859 * Start the job if possible...
6860 */
6861
6862 cupsdCheckJobs();
6863 }
6864
6865
6866 /*
6867 * 'read_ps_job_ticket()' - Reads a job ticket embedded in a PS file.
6868 *
6869 * This function only gets called when printing a single PostScript
6870 * file using the Print-Job operation. It doesn't work for Create-Job +
6871 * Send-File, since the job attributes need to be set at job creation
6872 * time for banners to work. The embedded PS job ticket stuff is here
6873 * only to allow the Windows printer driver for CUPS to pass in JCL
6874 * options and IPP attributes which otherwise would be lost.
6875 *
6876 * The format of a PS job ticket is simple:
6877 *
6878 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
6879 *
6880 * %cupsJobTicket: attr1=value1
6881 * %cupsJobTicket: attr2=value2
6882 * ...
6883 * %cupsJobTicket: attrN=valueN
6884 *
6885 * Job ticket lines must appear immediately after the first line that
6886 * specifies PostScript format (%!PS-Adobe-3.0), and CUPS will stop
6887 * looking for job ticket info when it finds a line that does not begin
6888 * with "%cupsJobTicket:".
6889 *
6890 * The maximum length of a job ticket line, including the prefix, is
6891 * 255 characters to conform with the Adobe DSC.
6892 *
6893 * Read-only attributes are rejected with a notice to the error log in
6894 * case a malicious user tries anything. Since the job ticket is read
6895 * prior to attribute validation in print_job(), job ticket attributes
6896 * will go through the same validation as IPP attributes...
6897 */
6898
6899 static void
6900 read_ps_job_ticket(cupsd_client_t *con) /* I - Client connection */
6901 {
6902 cups_file_t *fp; /* File to read from */
6903 char line[256]; /* Line data */
6904 int num_options; /* Number of options */
6905 cups_option_t *options; /* Options */
6906 ipp_t *ticket; /* New attributes */
6907 ipp_attribute_t *attr, /* Current attribute */
6908 *attr2, /* Job attribute */
6909 *prev2; /* Previous job attribute */
6910
6911
6912 /*
6913 * First open the print file...
6914 */
6915
6916 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
6917 {
6918 cupsdLogMessage(CUPSD_LOG_ERROR,
6919 "read_ps_job_ticket: Unable to open PostScript print file "
6920 "- %s",
6921 strerror(errno));
6922 return;
6923 }
6924
6925 /*
6926 * Skip the first line...
6927 */
6928
6929 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
6930 {
6931 cupsdLogMessage(CUPSD_LOG_ERROR,
6932 "read_ps_job_ticket: Unable to read from PostScript print "
6933 "file - %s",
6934 strerror(errno));
6935 cupsFileClose(fp);
6936 return;
6937 }
6938
6939 if (strncmp(line, "%!PS-Adobe-", 11))
6940 {
6941 /*
6942 * Not a DSC-compliant file, so no job ticket info will be available...
6943 */
6944
6945 cupsFileClose(fp);
6946 return;
6947 }
6948
6949 /*
6950 * Read job ticket info from the file...
6951 */
6952
6953 num_options = 0;
6954 options = NULL;
6955
6956 while (cupsFileGets(fp, line, sizeof(line)))
6957 {
6958 /*
6959 * Stop at the first non-ticket line...
6960 */
6961
6962 if (strncmp(line, "%cupsJobTicket:", 15))
6963 break;
6964
6965 /*
6966 * Add the options to the option array...
6967 */
6968
6969 num_options = cupsParseOptions(line + 15, num_options, &options);
6970 }
6971
6972 /*
6973 * Done with the file; see if we have any options...
6974 */
6975
6976 cupsFileClose(fp);
6977
6978 if (num_options == 0)
6979 return;
6980
6981 /*
6982 * OK, convert the options to an attribute list, and apply them to
6983 * the request...
6984 */
6985
6986 ticket = ippNew();
6987 cupsEncodeOptions(ticket, num_options, options);
6988
6989 /*
6990 * See what the user wants to change.
6991 */
6992
6993 for (attr = ticket->attrs; attr; attr = attr->next)
6994 {
6995 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
6996 continue;
6997
6998 if (!strcmp(attr->name, "job-originating-host-name") ||
6999 !strcmp(attr->name, "job-originating-user-name") ||
7000 !strcmp(attr->name, "job-media-sheets-completed") ||
7001 !strcmp(attr->name, "job-k-octets") ||
7002 !strcmp(attr->name, "job-id") ||
7003 !strncmp(attr->name, "job-state", 9) ||
7004 !strncmp(attr->name, "time-at-", 8))
7005 continue; /* Read-only attrs */
7006
7007 if ((attr2 = ippFindAttribute(con->request, attr->name,
7008 IPP_TAG_ZERO)) != NULL)
7009 {
7010 /*
7011 * Some other value; first free the old value...
7012 */
7013
7014 if (con->request->attrs == attr2)
7015 {
7016 con->request->attrs = attr2->next;
7017 prev2 = NULL;
7018 }
7019 else
7020 {
7021 for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
7022 if (prev2->next == attr2)
7023 {
7024 prev2->next = attr2->next;
7025 break;
7026 }
7027 }
7028
7029 if (con->request->last == attr2)
7030 con->request->last = prev2;
7031
7032 _ippFreeAttr(attr2);
7033 }
7034
7035 /*
7036 * Add new option by copying it...
7037 */
7038
7039 copy_attribute(con->request, attr, 0);
7040 }
7041
7042 /*
7043 * Then free the attribute list and option array...
7044 */
7045
7046 ippDelete(ticket);
7047 cupsFreeOptions(num_options, options);
7048 }
7049
7050
7051 /*
7052 * 'reject_jobs()' - Reject print jobs to a printer.
7053 */
7054
7055 static void
7056 reject_jobs(cupsd_client_t *con, /* I - Client connection */
7057 ipp_attribute_t *uri) /* I - Printer or class URI */
7058 {
7059 http_status_t status; /* Policy status */
7060 cups_ptype_t dtype; /* Destination type (printer or class) */
7061 char method[HTTP_MAX_URI], /* Method portion of URI */
7062 username[HTTP_MAX_URI], /* Username portion of URI */
7063 host[HTTP_MAX_URI], /* Host portion of URI */
7064 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7065 int port; /* Port portion of URI */
7066 const char *name; /* Printer name */
7067 cupsd_printer_t *printer; /* Printer data */
7068 ipp_attribute_t *attr; /* printer-state-message text */
7069
7070
7071 cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", con,
7072 con->http.fd, uri->values[0].string.text);
7073
7074 /*
7075 * Is the destination valid?
7076 */
7077
7078 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
7079 sizeof(method), username, sizeof(username), host,
7080 sizeof(host), &port, resource, sizeof(resource));
7081
7082 if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
7083 {
7084 /*
7085 * Bad URI...
7086 */
7087
7088 send_ipp_status(con, IPP_NOT_FOUND,
7089 _("The printer or class was not found."));
7090 return;
7091 }
7092
7093 /*
7094 * Check policy...
7095 */
7096
7097 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7098 {
7099 send_http_error(con, status);
7100 return;
7101 }
7102
7103 /*
7104 * Reject jobs sent to the printer...
7105 */
7106
7107 printer->accepting = 0;
7108
7109 if ((attr = ippFindAttribute(con->request, "printer-state-message",
7110 IPP_TAG_TEXT)) == NULL)
7111 strcpy(printer->state_message, "Rejecting Jobs");
7112 else
7113 strlcpy(printer->state_message, attr->values[0].string.text,
7114 sizeof(printer->state_message));
7115
7116 cupsdAddPrinterHistory(printer);
7117
7118 if (dtype & CUPS_PRINTER_CLASS)
7119 {
7120 cupsdSaveAllClasses();
7121
7122 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
7123 name, get_username(con));
7124 }
7125 else
7126 {
7127 cupsdSaveAllPrinters();
7128
7129 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
7130 name, get_username(con));
7131 }
7132
7133 /*
7134 * Everything was ok, so return OK status...
7135 */
7136
7137 con->response->request.status.status_code = IPP_OK;
7138 }
7139
7140
7141 /*
7142 * 'release_job()' - Release a held print job.
7143 */
7144
7145 static void
7146 release_job(cupsd_client_t *con, /* I - Client connection */
7147 ipp_attribute_t *uri) /* I - Job or Printer URI */
7148 {
7149 ipp_attribute_t *attr; /* Current attribute */
7150 int jobid; /* Job ID */
7151 char method[HTTP_MAX_URI], /* Method portion of URI */
7152 username[HTTP_MAX_URI], /* Username portion of URI */
7153 host[HTTP_MAX_URI], /* Host portion of URI */
7154 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7155 int port; /* Port portion of URI */
7156 cupsd_job_t *job; /* Job information */
7157
7158
7159 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", con,
7160 con->http.fd, uri->values[0].string.text);
7161
7162 /*
7163 * See if we have a job URI or a printer URI...
7164 */
7165
7166 if (!strcmp(uri->name, "printer-uri"))
7167 {
7168 /*
7169 * Got a printer URI; see if we also have a job-id attribute...
7170 */
7171
7172 if ((attr = ippFindAttribute(con->request, "job-id",
7173 IPP_TAG_INTEGER)) == NULL)
7174 {
7175 send_ipp_status(con, IPP_BAD_REQUEST,
7176 _("Got a printer-uri attribute but no job-id!"));
7177 return;
7178 }
7179
7180 jobid = attr->values[0].integer;
7181 }
7182 else
7183 {
7184 /*
7185 * Got a job URI; parse it to get the job ID...
7186 */
7187
7188 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
7189 sizeof(method), username, sizeof(username), host,
7190 sizeof(host), &port, resource, sizeof(resource));
7191
7192 if (strncmp(resource, "/jobs/", 6))
7193 {
7194 /*
7195 * Not a valid URI!
7196 */
7197
7198 send_ipp_status(con, IPP_BAD_REQUEST,
7199 _("Bad job-uri attribute \"%s\"!"),
7200 uri->values[0].string.text);
7201 return;
7202 }
7203
7204 jobid = atoi(resource + 6);
7205 }
7206
7207 /*
7208 * See if the job exists...
7209 */
7210
7211 if ((job = cupsdFindJob(jobid)) == NULL)
7212 {
7213 /*
7214 * Nope - return a "not found" error...
7215 */
7216
7217 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
7218 return;
7219 }
7220
7221 /*
7222 * See if job is "held"...
7223 */
7224
7225 if (job->state_value != IPP_JOB_HELD)
7226 {
7227 /*
7228 * Nope - return a "not possible" error...
7229 */
7230
7231 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held!"), jobid);
7232 return;
7233 }
7234
7235 /*
7236 * See if the job is owned by the requesting user...
7237 */
7238
7239 if (!validate_user(job, con, job->username, username, sizeof(username)))
7240 {
7241 send_http_error(con, HTTP_UNAUTHORIZED);
7242 return;
7243 }
7244
7245 /*
7246 * Reset the job-hold-until value to "no-hold"...
7247 */
7248
7249 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
7250 IPP_TAG_KEYWORD)) == NULL)
7251 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
7252
7253 if (attr)
7254 {
7255 _cupsStrFree(attr->values[0].string.text);
7256
7257 attr->value_tag = IPP_TAG_KEYWORD;
7258 attr->values[0].string.text = _cupsStrAlloc("no-hold");
7259
7260 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
7261 "Job job-hold-until value changed by user.");
7262 }
7263
7264 /*
7265 * Release the job and return...
7266 */
7267
7268 cupsdReleaseJob(job);
7269
7270 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
7271 "Job released by user.");
7272
7273 cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was released by \"%s\".", jobid,
7274 username);
7275
7276 con->response->request.status.status_code = IPP_OK;
7277 }
7278
7279
7280 /*
7281 * 'renew_subscription()' - Renew an existing subscription...
7282 */
7283
7284 static void
7285 renew_subscription(
7286 cupsd_client_t *con, /* I - Client connection */
7287 int sub_id) /* I - Subscription ID */
7288 {
7289 http_status_t status; /* Policy status */
7290 cupsd_subscription_t *sub; /* Subscription */
7291 ipp_attribute_t *lease; /* notify-lease-duration */
7292
7293
7294 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7295 "renew_subscription(con=%p[%d], sub_id=%d)",
7296 con, con->http.fd, sub_id);
7297
7298 /*
7299 * Is the subscription ID valid?
7300 */
7301
7302 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
7303 {
7304 /*
7305 * Bad subscription ID...
7306 */
7307
7308 send_ipp_status(con, IPP_NOT_FOUND,
7309 _("notify-subscription-id %d no good!"), sub_id);
7310 return;
7311 }
7312
7313 if (sub->job)
7314 {
7315 /*
7316 * Job subscriptions cannot be renewed...
7317 */
7318
7319 send_ipp_status(con, IPP_NOT_POSSIBLE,
7320 _("Job subscriptions cannot be renewed!"));
7321 return;
7322 }
7323
7324 /*
7325 * Check policy...
7326 */
7327
7328 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
7329 DefaultPolicyPtr,
7330 con, sub->owner)) != HTTP_OK)
7331 {
7332 send_http_error(con, status);
7333 return;
7334 }
7335
7336 /*
7337 * Renew the subscription...
7338 */
7339
7340 lease = ippFindAttribute(con->request, "notify-lease-duration",
7341 IPP_TAG_INTEGER);
7342
7343 sub->lease = lease ? lease->values[0].integer : DefaultLeaseDuration;
7344
7345 if (MaxLeaseDuration && (sub->lease == 0 || sub->lease > MaxLeaseDuration))
7346 {
7347 cupsdLogMessage(CUPSD_LOG_INFO,
7348 "renew_subscription: Limiting notify-lease-duration to "
7349 "%d seconds.",
7350 MaxLeaseDuration);
7351 sub->lease = MaxLeaseDuration;
7352 }
7353
7354 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
7355
7356 cupsdSaveAllSubscriptions();
7357
7358 con->response->request.status.status_code = IPP_OK;
7359 }
7360
7361
7362 /*
7363 * 'restart_job()' - Restart an old print job.
7364 */
7365
7366 static void
7367 restart_job(cupsd_client_t *con, /* I - Client connection */
7368 ipp_attribute_t *uri) /* I - Job or Printer URI */
7369 {
7370 ipp_attribute_t *attr; /* Current attribute */
7371 int jobid; /* Job ID */
7372 char method[HTTP_MAX_URI], /* Method portion of URI */
7373 username[HTTP_MAX_URI], /* Username portion of URI */
7374 host[HTTP_MAX_URI], /* Host portion of URI */
7375 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7376 int port; /* Port portion of URI */
7377 cupsd_job_t *job; /* Job information */
7378
7379
7380 cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con,
7381 con->http.fd, uri->values[0].string.text);
7382
7383 /*
7384 * See if we have a job URI or a printer URI...
7385 */
7386
7387 if (!strcmp(uri->name, "printer-uri"))
7388 {
7389 /*
7390 * Got a printer URI; see if we also have a job-id attribute...
7391 */
7392
7393 if ((attr = ippFindAttribute(con->request, "job-id",
7394 IPP_TAG_INTEGER)) == NULL)
7395 {
7396 send_ipp_status(con, IPP_BAD_REQUEST,
7397 _("Got a printer-uri attribute but no job-id!"));
7398 return;
7399 }
7400
7401 jobid = attr->values[0].integer;
7402 }
7403 else
7404 {
7405 /*
7406 * Got a job URI; parse it to get the job ID...
7407 */
7408
7409 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
7410 sizeof(method), username, sizeof(username), host,
7411 sizeof(host), &port, resource, sizeof(resource));
7412
7413 if (strncmp(resource, "/jobs/", 6))
7414 {
7415 /*
7416 * Not a valid URI!
7417 */
7418
7419 send_ipp_status(con, IPP_BAD_REQUEST,
7420 _("Bad job-uri attribute \"%s\"!"),
7421 uri->values[0].string.text);
7422 return;
7423 }
7424
7425 jobid = atoi(resource + 6);
7426 }
7427
7428 /*
7429 * See if the job exists...
7430 */
7431
7432 if ((job = cupsdFindJob(jobid)) == NULL)
7433 {
7434 /*
7435 * Nope - return a "not found" error...
7436 */
7437
7438 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
7439 return;
7440 }
7441
7442 /*
7443 * See if job is in any of the "completed" states...
7444 */
7445
7446 if (job->state_value <= IPP_JOB_PROCESSING)
7447 {
7448 /*
7449 * Nope - return a "not possible" error...
7450 */
7451
7452 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete!"),
7453 jobid);
7454 return;
7455 }
7456
7457 /*
7458 * See if we have retained the job files...
7459 */
7460
7461 cupsdLoadJob(job);
7462
7463 if (!job->attrs || job->num_files == 0)
7464 {
7465 /*
7466 * Nope - return a "not possible" error...
7467 */
7468
7469 send_ipp_status(con, IPP_NOT_POSSIBLE,
7470 _("Job #%d cannot be restarted - no files!"), jobid);
7471 return;
7472 }
7473
7474 /*
7475 * See if the job is owned by the requesting user...
7476 */
7477
7478 if (!validate_user(job, con, job->username, username, sizeof(username)))
7479 {
7480 send_http_error(con, HTTP_UNAUTHORIZED);
7481 return;
7482 }
7483
7484 /*
7485 * Restart the job and return...
7486 */
7487
7488 cupsdRestartJob(job);
7489
7490 cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was restarted by \"%s\".", jobid,
7491 username);
7492
7493 con->response->request.status.status_code = IPP_OK;
7494 }
7495
7496
7497 /*
7498 * 'save_auth_info()' - Save authentication information for a job.
7499 */
7500
7501 static void
7502 save_auth_info(cupsd_client_t *con, /* I - Client connection */
7503 cupsd_job_t *job) /* I - Job */
7504 {
7505 int i; /* Looping var */
7506 char filename[1024]; /* Job authentication filename */
7507 cups_file_t *fp; /* Job authentication file */
7508 char line[1024]; /* Line for file */
7509
7510
7511 /*
7512 * This function saves the in-memory authentication information for
7513 * a job so that it can be used to authenticate with a remote host.
7514 * The information is stored in a file that is readable only by the
7515 * root user. The username and password are Base-64 encoded, each
7516 * on a separate line, followed by random number (up to 1024) of
7517 * newlines to limit the amount of information that is exposed.
7518 *
7519 * Because of the potential for exposing of authentication information,
7520 * this functionality is only enabled when running cupsd as root.
7521 *
7522 * This caching only works for the Basic and BasicDigest authentication
7523 * types. Digest authentication cannot be cached this way, and in
7524 * the future Kerberos authentication may make all of this obsolete.
7525 *
7526 * Authentication information is saved whenever an authenticated
7527 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
7528 * performed.
7529 *
7530 * This information is deleted after a job is completed or canceled,
7531 * so reprints may require subsequent re-authentication.
7532 */
7533
7534 if (RunUser)
7535 return;
7536
7537 /*
7538 * Create the authentication file and change permissions...
7539 */
7540
7541 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
7542 if ((fp = cupsFileOpen(filename, "w")) == NULL)
7543 {
7544 cupsdLogMessage(CUPSD_LOG_ERROR,
7545 "Unable to save authentication info to \"%s\" - %s",
7546 filename, strerror(errno));
7547 return;
7548 }
7549
7550 fchown(cupsFileNumber(fp), 0, 0);
7551 fchmod(cupsFileNumber(fp), 0400);
7552
7553 /*
7554 * Write the authenticated username...
7555 */
7556
7557 httpEncode64_2(line, sizeof(line), con->username, strlen(con->username));
7558 cupsFilePrintf(fp, "%s\n", line);
7559
7560 /*
7561 * Write the authenticated password...
7562 */
7563
7564 httpEncode64_2(line, sizeof(line), con->password, strlen(con->password));
7565 cupsFilePrintf(fp, "%s\n", line);
7566
7567 /*
7568 * Write a random number of newlines to the end of the file...
7569 */
7570
7571 for (i = (rand() % 1024); i >= 0; i --)
7572 cupsFilePutChar(fp, '\n');
7573
7574 /*
7575 * Close the file and return...
7576 */
7577
7578 cupsFileClose(fp);
7579 }
7580
7581
7582 /*
7583 * 'send_document()' - Send a file to a printer or class.
7584 */
7585
7586 static void
7587 send_document(cupsd_client_t *con, /* I - Client connection */
7588 ipp_attribute_t *uri) /* I - Printer URI */
7589 {
7590 ipp_attribute_t *attr; /* Current attribute */
7591 ipp_attribute_t *format; /* Document-format attribute */
7592 int jobid; /* Job ID number */
7593 cupsd_job_t *job; /* Current job */
7594 char job_uri[HTTP_MAX_URI],
7595 /* Job URI */
7596 method[HTTP_MAX_URI],
7597 /* Method portion of URI */
7598 username[HTTP_MAX_URI],
7599 /* Username portion of URI */
7600 host[HTTP_MAX_URI],
7601 /* Host portion of URI */
7602 resource[HTTP_MAX_URI];
7603 /* Resource portion of URI */
7604 int port; /* Port portion of URI */
7605 mime_type_t *filetype; /* Type of file */
7606 char super[MIME_MAX_SUPER],
7607 /* Supertype of file */
7608 type[MIME_MAX_TYPE],
7609 /* Subtype of file */
7610 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
7611 /* Textual name of mime type */
7612 char filename[1024]; /* Job filename */
7613 cupsd_printer_t *printer; /* Current printer */
7614 struct stat fileinfo; /* File information */
7615 int kbytes; /* Size of file */
7616 int compression; /* Type of compression */
7617
7618
7619 cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con,
7620 con->http.fd, uri->values[0].string.text);
7621
7622 /*
7623 * See if we have a job URI or a printer URI...
7624 */
7625
7626 if (!strcmp(uri->name, "printer-uri"))
7627 {
7628 /*
7629 * Got a printer URI; see if we also have a job-id attribute...
7630 */
7631
7632 if ((attr = ippFindAttribute(con->request, "job-id",
7633 IPP_TAG_INTEGER)) == NULL)
7634 {
7635 send_ipp_status(con, IPP_BAD_REQUEST,
7636 _("Got a printer-uri attribute but no job-id!"));
7637 return;
7638 }
7639
7640 jobid = attr->values[0].integer;
7641 }
7642 else
7643 {
7644 /*
7645 * Got a job URI; parse it to get the job ID...
7646 */
7647
7648 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
7649 sizeof(method), username, sizeof(username), host,
7650 sizeof(host), &port, resource, sizeof(resource));
7651
7652 if (strncmp(resource, "/jobs/", 6))
7653 {
7654 /*
7655 * Not a valid URI!
7656 */
7657
7658 send_ipp_status(con, IPP_BAD_REQUEST,
7659 _("Bad job-uri attribute \"%s\"!"),
7660 uri->values[0].string.text);
7661 return;
7662 }
7663
7664 jobid = atoi(resource + 6);
7665 }
7666
7667 /*
7668 * See if the job exists...
7669 */
7670
7671 if ((job = cupsdFindJob(jobid)) == NULL)
7672 {
7673 /*
7674 * Nope - return a "not found" error...
7675 */
7676
7677 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
7678 return;
7679 }
7680
7681 printer = cupsdFindDest(job->dest);
7682
7683 /*
7684 * See if the job is owned by the requesting user...
7685 */
7686
7687 if (!validate_user(job, con, job->username, username, sizeof(username)))
7688 {
7689 send_http_error(con, HTTP_UNAUTHORIZED);
7690 return;
7691 }
7692
7693 /*
7694 * OK, see if the client is sending the document compressed - CUPS
7695 * only supports "none" and "gzip".
7696 */
7697
7698 compression = CUPS_FILE_NONE;
7699
7700 if ((attr = ippFindAttribute(con->request, "compression",
7701 IPP_TAG_KEYWORD)) != NULL)
7702 {
7703 if (strcmp(attr->values[0].string.text, "none")
7704 #ifdef HAVE_LIBZ
7705 && strcmp(attr->values[0].string.text, "gzip")
7706 #endif /* HAVE_LIBZ */
7707 )
7708 {
7709 send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\"!"),
7710 attr->values[0].string.text);
7711 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
7712 "compression", NULL, attr->values[0].string.text);
7713 return;
7714 }
7715
7716 #ifdef HAVE_LIBZ
7717 if (!strcmp(attr->values[0].string.text, "gzip"))
7718 compression = CUPS_FILE_GZIP;
7719 #endif /* HAVE_LIBZ */
7720 }
7721
7722 /*
7723 * Do we have a file to print?
7724 */
7725
7726 if (!con->filename)
7727 {
7728 send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?!"));
7729 return;
7730 }
7731
7732 /*
7733 * Is it a format we support?
7734 */
7735
7736 if ((format = ippFindAttribute(con->request, "document-format",
7737 IPP_TAG_MIMETYPE)) != NULL)
7738 {
7739 /*
7740 * Grab format from client...
7741 */
7742
7743 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
7744 {
7745 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"!"),
7746 format->values[0].string.text);
7747 return;
7748 }
7749 }
7750 else
7751 {
7752 /*
7753 * No document format attribute? Auto-type it!
7754 */
7755
7756 strcpy(super, "application");
7757 strcpy(type, "octet-stream");
7758 }
7759
7760 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
7761 {
7762 /*
7763 * Auto-type the file...
7764 */
7765
7766 ipp_attribute_t *doc_name; /* document-name attribute */
7767
7768
7769 cupsdLogMessage(CUPSD_LOG_DEBUG, "send_document: auto-typing file...");
7770
7771 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
7772 filetype = mimeFileType(MimeDatabase, con->filename,
7773 doc_name ? doc_name->values[0].string.text : NULL,
7774 &compression);
7775
7776 if (filetype)
7777 {
7778 /*
7779 * Replace the document-format attribute value with the auto-typed one.
7780 */
7781
7782 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
7783 filetype->type);
7784
7785 if (format)
7786 {
7787 _cupsStrFree(format->values[0].string.text);
7788 format->values[0].string.text = _cupsStrAlloc(mimetype);
7789 }
7790 else
7791 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
7792 "document-format", NULL, mimetype);
7793 }
7794 else
7795 filetype = mimeType(MimeDatabase, super, type);
7796 }
7797 else
7798 filetype = mimeType(MimeDatabase, super, type);
7799
7800 if (!filetype)
7801 {
7802 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
7803 _("Unsupported format \'%s/%s\'!"), super, type);
7804 cupsdLogMessage(CUPSD_LOG_INFO,
7805 "Hint: Do you have the raw file printing rules enabled?");
7806
7807 if (format)
7808 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
7809 "document-format", NULL, format->values[0].string.text);
7810
7811 return;
7812 }
7813
7814 if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
7815 {
7816 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
7817 filetype->type);
7818
7819 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
7820 _("Unsupported format \'%s\'!"), mimetype);
7821
7822 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
7823 "document-format", NULL, mimetype);
7824
7825 return;
7826 }
7827
7828 cupsdLogMessage(CUPSD_LOG_DEBUG,
7829 "send_document: request file type is %s/%s.",
7830 filetype->super, filetype->type);
7831
7832 /*
7833 * Add the file to the job...
7834 */
7835
7836 cupsdLoadJob(job);
7837
7838 if (add_file(con, job, filetype, compression))
7839 return;
7840
7841 if (stat(con->filename, &fileinfo))
7842 kbytes = 0;
7843 else
7844 kbytes = (fileinfo.st_size + 1023) / 1024;
7845
7846 cupsdUpdateQuota(printer, job->username, 0, kbytes);
7847
7848 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
7849 IPP_TAG_INTEGER)) != NULL)
7850 attr->values[0].integer += kbytes;
7851
7852 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
7853 job->num_files);
7854 rename(con->filename, filename);
7855
7856 cupsdClearString(&con->filename);
7857
7858 cupsdLogMessage(CUPSD_LOG_INFO,
7859 "File of type %s/%s queued in job #%d by \"%s\".",
7860 filetype->super, filetype->type, job->id, job->username);
7861
7862 /*
7863 * Start the job if this is the last document...
7864 */
7865
7866 if ((attr = ippFindAttribute(con->request, "last-document",
7867 IPP_TAG_BOOLEAN)) != NULL &&
7868 attr->values[0].boolean)
7869 {
7870 /*
7871 * See if we need to add the ending sheet...
7872 */
7873
7874 if (printer &&
7875 !(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
7876 (attr = ippFindAttribute(job->attrs, "job-sheets",
7877 IPP_TAG_ZERO)) != NULL &&
7878 attr->num_values > 1)
7879 {
7880 /*
7881 * Yes...
7882 */
7883
7884 cupsdLogMessage(CUPSD_LOG_INFO,
7885 "Adding end banner page \"%s\" to job %d.",
7886 attr->values[1].string.text, job->id);
7887
7888 kbytes = copy_banner(con, job, attr->values[1].string.text);
7889
7890 cupsdUpdateQuota(printer, job->username, 0, kbytes);
7891 }
7892
7893 if (job->state_value == IPP_JOB_STOPPED)
7894 {
7895 job->state->values[0].integer = IPP_JOB_PENDING;
7896 job->state_value = IPP_JOB_PENDING;
7897 }
7898 else if (job->state_value == IPP_JOB_HELD)
7899 {
7900 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
7901 IPP_TAG_KEYWORD)) == NULL)
7902 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
7903
7904 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
7905 {
7906 job->state->values[0].integer = IPP_JOB_PENDING;
7907 job->state_value = IPP_JOB_PENDING;
7908 }
7909 }
7910
7911 cupsdSaveJob(job);
7912
7913 /*
7914 * Start the job if possible... Since cupsdCheckJobs() can cancel a
7915 * job if it doesn't print, we need to re-find the job afterwards...
7916 */
7917
7918 jobid = job->id;
7919
7920 cupsdCheckJobs();
7921
7922 job = cupsdFindJob(jobid);
7923 }
7924 else
7925 {
7926 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
7927 IPP_TAG_KEYWORD)) == NULL)
7928 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
7929
7930 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
7931 {
7932 job->state->values[0].integer = IPP_JOB_HELD;
7933 job->state_value = IPP_JOB_HELD;
7934 job->hold_until = time(NULL) + 60;
7935 cupsdSaveJob(job);
7936 }
7937 }
7938
7939 /*
7940 * Fill in the response info...
7941 */
7942
7943 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
7944 LocalPort, jobid);
7945
7946 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
7947 job_uri);
7948
7949 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
7950
7951 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
7952 job ? job->state_value : IPP_JOB_CANCELED);
7953 add_job_state_reasons(con, job);
7954
7955 con->response->request.status.status_code = IPP_OK;
7956 }
7957
7958
7959 /*
7960 * 'send_http_error()' - Send a HTTP error back to the IPP client.
7961 */
7962
7963 static void
7964 send_http_error(cupsd_client_t *con, /* I - Client connection */
7965 http_status_t status) /* I - HTTP status code */
7966 {
7967 cupsdLogMessage(CUPSD_LOG_ERROR, "%s: %s",
7968 ippOpString(con->request->request.op.operation_id),
7969 httpStatus(status));
7970
7971 cupsdSendError(con, status);
7972
7973 ippDelete(con->response);
7974 con->response = NULL;
7975
7976 return;
7977 }
7978
7979
7980 /*
7981 * 'send_ipp_status()' - Send a status back to the IPP client.
7982 */
7983
7984 static void
7985 send_ipp_status(cupsd_client_t *con, /* I - Client connection */
7986 ipp_status_t status, /* I - IPP status code */
7987 const char *message, /* I - Status message */
7988 ...) /* I - Additional args as needed */
7989 {
7990 va_list ap; /* Pointer to additional args */
7991 char formatted[1024]; /* Formatted errror message */
7992
7993
7994 if (message)
7995 {
7996 va_start(ap, message);
7997 vsnprintf(formatted, sizeof(formatted),
7998 _cupsLangString(con->language, message), ap);
7999 va_end(ap);
8000
8001 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
8002 ippOpString(con->request->request.op.operation_id),
8003 ippErrorString(status), formatted);
8004 }
8005 else
8006 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s",
8007 ippOpString(con->request->request.op.operation_id),
8008 ippErrorString(status));
8009
8010 con->response->request.status.status_code = status;
8011
8012 if (ippFindAttribute(con->response, "attributes-charset",
8013 IPP_TAG_ZERO) == NULL)
8014 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
8015 "attributes-charset", NULL, DefaultCharset);
8016
8017 if (ippFindAttribute(con->response, "attributes-natural-language",
8018 IPP_TAG_ZERO) == NULL)
8019 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
8020 "attributes-natural-language", NULL, DefaultLanguage);
8021
8022 if (message)
8023 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
8024 "status-message", NULL, formatted);
8025 }
8026
8027
8028 /*
8029 * 'set_default()' - Set the default destination...
8030 */
8031
8032 static void
8033 set_default(cupsd_client_t *con, /* I - Client connection */
8034 ipp_attribute_t *uri) /* I - Printer URI */
8035 {
8036 http_status_t status; /* Policy status */
8037 cups_ptype_t dtype; /* Destination type (printer or class) */
8038 char method[HTTP_MAX_URI],
8039 /* Method portion of URI */
8040 username[HTTP_MAX_URI],
8041 /* Username portion of URI */
8042 host[HTTP_MAX_URI],
8043 /* Host portion of URI */
8044 resource[HTTP_MAX_URI];
8045 /* Resource portion of URI */
8046 int port; /* Port portion of URI */
8047 const char *name; /* Printer name */
8048 cupsd_printer_t *printer; /* Printer */
8049
8050
8051 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con,
8052 con->http.fd, uri->values[0].string.text);
8053
8054 /*
8055 * Is the destination valid?
8056 */
8057
8058 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
8059 sizeof(method), username, sizeof(username), host,
8060 sizeof(host), &port, resource, sizeof(resource));
8061
8062 if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
8063 {
8064 /*
8065 * Bad URI...
8066 */
8067
8068 send_ipp_status(con, IPP_NOT_FOUND,
8069 _("The printer or class was not found."));
8070 return;
8071 }
8072
8073 /*
8074 * Check policy...
8075 */
8076
8077 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
8078 {
8079 send_http_error(con, status);
8080 return;
8081 }
8082
8083 /*
8084 * Set it as the default...
8085 */
8086
8087 DefaultPrinter = printer;
8088
8089 cupsdSaveAllPrinters();
8090 cupsdSaveAllClasses();
8091
8092 cupsdWritePrintcap();
8093
8094 cupsdLogMessage(CUPSD_LOG_INFO,
8095 "Default destination set to \"%s\" by \"%s\".", name,
8096 get_username(con));
8097
8098 /*
8099 * Everything was ok, so return OK status...
8100 */
8101
8102 con->response->request.status.status_code = IPP_OK;
8103 }
8104
8105
8106 /*
8107 * 'set_job_attrs()' - Set job attributes.
8108 */
8109
8110 static void
8111 set_job_attrs(cupsd_client_t *con, /* I - Client connection */
8112 ipp_attribute_t *uri) /* I - Job URI */
8113 {
8114 ipp_attribute_t *attr, /* Current attribute */
8115 *attr2; /* Job attribute */
8116 int jobid; /* Job ID */
8117 cupsd_job_t *job; /* Current job */
8118 char method[HTTP_MAX_URI],
8119 /* Method portion of URI */
8120 username[HTTP_MAX_URI],
8121 /* Username portion of URI */
8122 host[HTTP_MAX_URI],
8123 /* Host portion of URI */
8124 resource[HTTP_MAX_URI];
8125 /* Resource portion of URI */
8126 int port; /* Port portion of URI */
8127 int event; /* Events? */
8128
8129
8130 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
8131 con->http.fd, uri->values[0].string.text);
8132
8133 /*
8134 * Start with "everything is OK" status...
8135 */
8136
8137 con->response->request.status.status_code = IPP_OK;
8138
8139 /*
8140 * See if we have a job URI or a printer URI...
8141 */
8142
8143 if (!strcmp(uri->name, "printer-uri"))
8144 {
8145 /*
8146 * Got a printer URI; see if we also have a job-id attribute...
8147 */
8148
8149 if ((attr = ippFindAttribute(con->request, "job-id",
8150 IPP_TAG_INTEGER)) == NULL)
8151 {
8152 send_ipp_status(con, IPP_BAD_REQUEST,
8153 _("Got a printer-uri attribute but no job-id!"));
8154 return;
8155 }
8156
8157 jobid = attr->values[0].integer;
8158 }
8159 else
8160 {
8161 /*
8162 * Got a job URI; parse it to get the job ID...
8163 */
8164
8165 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
8166 sizeof(method), username, sizeof(username), host,
8167 sizeof(host), &port, resource, sizeof(resource));
8168
8169 if (strncmp(resource, "/jobs/", 6))
8170 {
8171 /*
8172 * Not a valid URI!
8173 */
8174
8175 send_ipp_status(con, IPP_BAD_REQUEST,
8176 _("Bad job-uri attribute \"%s\"!"),
8177 uri->values[0].string.text);
8178 return;
8179 }
8180
8181 jobid = atoi(resource + 6);
8182 }
8183
8184 /*
8185 * See if the job exists...
8186 */
8187
8188 if ((job = cupsdFindJob(jobid)) == NULL)
8189 {
8190 /*
8191 * Nope - return a "not found" error...
8192 */
8193
8194 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
8195 return;
8196 }
8197
8198 /*
8199 * See if the job has been completed...
8200 */
8201
8202 if (job->state_value > IPP_JOB_STOPPED)
8203 {
8204 /*
8205 * Return a "not-possible" error...
8206 */
8207
8208 send_ipp_status(con, IPP_NOT_POSSIBLE,
8209 _("Job #%d is finished and cannot be altered!"), jobid);
8210 return;
8211 }
8212
8213 /*
8214 * See if the job is owned by the requesting user...
8215 */
8216
8217 if (!validate_user(job, con, job->username, username, sizeof(username)))
8218 {
8219 send_http_error(con, HTTP_UNAUTHORIZED);
8220 return;
8221 }
8222
8223 /*
8224 * See what the user wants to change.
8225 */
8226
8227 cupsdLoadJob(job);
8228
8229 event = 0;
8230
8231 for (attr = con->request->attrs; attr; attr = attr->next)
8232 {
8233 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
8234 continue;
8235
8236 if (!strcmp(attr->name, "attributes-charset") ||
8237 !strcmp(attr->name, "attributes-natural-language") ||
8238 !strcmp(attr->name, "document-compression") ||
8239 !strcmp(attr->name, "document-format") ||
8240 !strcmp(attr->name, "job-detailed-status-messages") ||
8241 !strcmp(attr->name, "job-document-access-errors") ||
8242 !strcmp(attr->name, "job-id") ||
8243 !strcmp(attr->name, "job-k-octets") ||
8244 !strcmp(attr->name, "job-originating-host-name") ||
8245 !strcmp(attr->name, "job-originating-user-name") ||
8246 !strcmp(attr->name, "job-printer-up-time") ||
8247 !strcmp(attr->name, "job-printer-uri") ||
8248 !strcmp(attr->name, "job-sheets") ||
8249 !strcmp(attr->name, "job-state-message") ||
8250 !strcmp(attr->name, "job-state-reasons") ||
8251 !strcmp(attr->name, "job-uri") ||
8252 !strcmp(attr->name, "number-of-documents") ||
8253 !strcmp(attr->name, "number-of-intervening-jobs") ||
8254 !strcmp(attr->name, "output-device-assigned") ||
8255 !strncmp(attr->name, "date-time-at-", 13) ||
8256 !strncmp(attr->name, "job-impressions", 15) ||
8257 !strncmp(attr->name, "job-k-octets", 12) ||
8258 !strncmp(attr->name, "job-media-sheets", 16) ||
8259 !strncmp(attr->name, "time-at-", 8))
8260 {
8261 /*
8262 * Read-only attrs!
8263 */
8264
8265 send_ipp_status(con, IPP_ATTRIBUTES_NOT_SETTABLE,
8266 _("%s cannot be changed."), attr->name);
8267
8268 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
8269 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
8270
8271 continue;
8272 }
8273
8274 if (!strcmp(attr->name, "job-priority"))
8275 {
8276 /*
8277 * Change the job priority...
8278 */
8279
8280 if (attr->value_tag != IPP_TAG_INTEGER)
8281 {
8282 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value!"));
8283
8284 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
8285 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
8286 }
8287 else if (job->state_value >= IPP_JOB_PROCESSING)
8288 {
8289 send_ipp_status(con, IPP_NOT_POSSIBLE,
8290 _("Job is completed and cannot be changed."));
8291 return;
8292 }
8293 else if (con->response->request.status.status_code == IPP_OK)
8294 {
8295 cupsdSetJobPriority(job, attr->values[0].integer);
8296 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
8297 }
8298 }
8299 else if (!strcmp(attr->name, "job-state"))
8300 {
8301 /*
8302 * Change the job state...
8303 */
8304
8305 if (attr->value_tag != IPP_TAG_ENUM)
8306 {
8307 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value!"));
8308
8309 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
8310 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
8311 }
8312 else
8313 {
8314 switch (attr->values[0].integer)
8315 {
8316 case IPP_JOB_PENDING :
8317 case IPP_JOB_HELD :
8318 if (job->state_value > IPP_JOB_HELD)
8319 {
8320 send_ipp_status(con, IPP_NOT_POSSIBLE,
8321 _("Job state cannot be changed."));
8322 return;
8323 }
8324 else if (con->response->request.status.status_code == IPP_OK)
8325 {
8326 job->state->values[0].integer = attr->values[0].integer;
8327 job->state_value = (ipp_jstate_t)attr->values[0].integer;
8328
8329 event |= CUPSD_EVENT_JOB_STATE;
8330 }
8331 break;
8332
8333 case IPP_JOB_PROCESSING :
8334 case IPP_JOB_STOPPED :
8335 if (job->state_value != attr->values[0].integer)
8336 {
8337 send_ipp_status(con, IPP_NOT_POSSIBLE,
8338 _("Job state cannot be changed."));
8339 return;
8340 }
8341 break;
8342
8343 case IPP_JOB_CANCELED :
8344 case IPP_JOB_ABORTED :
8345 case IPP_JOB_COMPLETED :
8346 if (job->state_value > IPP_JOB_PROCESSING)
8347 {
8348 send_ipp_status(con, IPP_NOT_POSSIBLE,
8349 _("Job state cannot be changed."));
8350 return;
8351 }
8352 else if (con->response->request.status.status_code == IPP_OK)
8353 cupsdCancelJob(job, 0, (ipp_jstate_t)attr->values[0].integer);
8354 break;
8355 }
8356 }
8357 }
8358 else if (con->response->request.status.status_code != IPP_OK)
8359 continue;
8360 else if ((attr2 = ippFindAttribute(job->attrs, attr->name,
8361 IPP_TAG_ZERO)) != NULL)
8362 {
8363 /*
8364 * Some other value; first free the old value...
8365 */
8366
8367 if (job->attrs->prev)
8368 job->attrs->prev->next = attr2->next;
8369 else
8370 job->attrs->attrs = attr2->next;
8371
8372 if (job->attrs->last == attr2)
8373 job->attrs->last = job->attrs->prev;
8374
8375 _ippFreeAttr(attr2);
8376
8377 /*
8378 * Then copy the attribute...
8379 */
8380
8381 copy_attribute(job->attrs, attr, 0);
8382
8383 /*
8384 * See if the job-name or job-hold-until is being changed.
8385 */
8386
8387 if (!strcmp(attr->name, "job-hold-until"))
8388 {
8389 cupsdSetJobHoldUntil(job, attr->values[0].string.text);
8390
8391 if (!strcmp(attr->values[0].string.text, "no-hold"))
8392 cupsdReleaseJob(job);
8393 else
8394 cupsdHoldJob(job);
8395
8396 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
8397 }
8398 }
8399 else if (attr->value_tag == IPP_TAG_DELETEATTR)
8400 {
8401 /*
8402 * Delete the attribute...
8403 */
8404
8405 if ((attr2 = ippFindAttribute(job->attrs, attr->name,
8406 IPP_TAG_ZERO)) != NULL)
8407 {
8408 if (job->attrs->prev)
8409 job->attrs->prev->next = attr2->next;
8410 else
8411 job->attrs->attrs = attr2->next;
8412
8413 if (attr2 == job->attrs->last)
8414 job->attrs->last = job->attrs->prev;
8415
8416 _ippFreeAttr(attr2);
8417
8418 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
8419 }
8420 }
8421 else
8422 {
8423 /*
8424 * Add new option by copying it...
8425 */
8426
8427 copy_attribute(job->attrs, attr, 0);
8428
8429 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
8430 }
8431 }
8432
8433 /*
8434 * Save the job...
8435 */
8436
8437 cupsdSaveJob(job);
8438
8439 /*
8440 * Send events as needed...
8441 */
8442
8443 if (event & CUPSD_EVENT_JOB_STATE)
8444 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
8445 job->state_value == IPP_JOB_HELD ?
8446 "Job held by user." : "Job restarted by user.");
8447
8448 if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
8449 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
8450 "Job options changed by user.");
8451
8452 /*
8453 * Start jobs if possible...
8454 */
8455
8456 cupsdCheckJobs();
8457 }
8458
8459
8460 /*
8461 * 'set_printer_defaults()' - Set printer default options from a request.
8462 */
8463
8464 static void
8465 set_printer_defaults(
8466 cupsd_client_t *con, /* I - Client connection */
8467 cupsd_printer_t *printer) /* I - Printer */
8468 {
8469 int i; /* Looping var */
8470 ipp_attribute_t *attr; /* Current attribute */
8471 int namelen; /* Length of attribute name */
8472 char name[256], /* New attribute name */
8473 value[256]; /* String version of integer attrs */
8474
8475
8476 for (attr = con->request->attrs; attr; attr = attr->next)
8477 {
8478 /*
8479 * Skip non-printer attributes...
8480 */
8481
8482 if (attr->group_tag != IPP_TAG_PRINTER || !attr->name)
8483 continue;
8484
8485 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_defaults: %s", attr->name);
8486
8487 if (!strcmp(attr->name, "job-sheets-default"))
8488 {
8489 /*
8490 * Only allow keywords and names...
8491 */
8492
8493 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
8494 continue;
8495
8496 /*
8497 * Only allow job-sheets-default to be set when running without a
8498 * system high classification level...
8499 */
8500
8501 if (Classification)
8502 continue;
8503
8504 cupsdSetString(&printer->job_sheets[0], attr->values[0].string.text);
8505
8506 if (attr->num_values > 1)
8507 cupsdSetString(&printer->job_sheets[1], attr->values[1].string.text);
8508 else
8509 cupsdSetString(&printer->job_sheets[1], "none");
8510 }
8511 else if (!strcmp(attr->name, "requesting-user-name-allowed"))
8512 {
8513 cupsdFreePrinterUsers(printer);
8514
8515 printer->deny_users = 0;
8516
8517 if (attr->value_tag == IPP_TAG_NAME &&
8518 (attr->num_values > 1 ||
8519 strcmp(attr->values[0].string.text, "all")))
8520 {
8521 for (i = 0; i < attr->num_values; i ++)
8522 cupsdAddPrinterUser(printer, attr->values[i].string.text);
8523 }
8524 }
8525 else if (!strcmp(attr->name, "requesting-user-name-denied"))
8526 {
8527 cupsdFreePrinterUsers(printer);
8528
8529 printer->deny_users = 1;
8530
8531 if (attr->value_tag == IPP_TAG_NAME &&
8532 (attr->num_values > 1 ||
8533 strcmp(attr->values[0].string.text, "none")))
8534 {
8535 for (i = 0; i < attr->num_values; i ++)
8536 cupsdAddPrinterUser(printer, attr->values[i].string.text);
8537 }
8538 }
8539 else if (!strcmp(attr->name, "job-quota-period"))
8540 {
8541 if (attr->value_tag != IPP_TAG_INTEGER)
8542 continue;
8543
8544 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-quota-period to %d...",
8545 attr->values[0].integer);
8546 cupsdFreeQuotas(printer);
8547
8548 printer->quota_period = attr->values[0].integer;
8549 }
8550 else if (!strcmp(attr->name, "job-k-limit"))
8551 {
8552 if (attr->value_tag != IPP_TAG_INTEGER)
8553 continue;
8554
8555 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-k-limit to %d...",
8556 attr->values[0].integer);
8557 cupsdFreeQuotas(printer);
8558
8559 printer->k_limit = attr->values[0].integer;
8560 }
8561 else if (!strcmp(attr->name, "job-page-limit"))
8562 {
8563 if (attr->value_tag != IPP_TAG_INTEGER)
8564 continue;
8565
8566 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-page-limit to %d...",
8567 attr->values[0].integer);
8568 cupsdFreeQuotas(printer);
8569
8570 printer->page_limit = attr->values[0].integer;
8571 }
8572 else if (!strcmp(attr->name, "printer-op-policy"))
8573 {
8574 cupsd_policy_t *p; /* Policy */
8575
8576
8577 if (attr->value_tag != IPP_TAG_NAME)
8578 continue;
8579
8580 if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
8581 {
8582 cupsdLogMessage(CUPSD_LOG_DEBUG,
8583 "Setting printer-op-policy to \"%s\"...",
8584 attr->values[0].string.text);
8585 cupsdSetString(&printer->op_policy, attr->values[0].string.text);
8586 printer->op_policy_ptr = p;
8587 }
8588 else
8589 {
8590 send_ipp_status(con, IPP_NOT_POSSIBLE,
8591 _("Unknown printer-op-policy \"%s\"."),
8592 attr->values[0].string.text);
8593 return;
8594 }
8595 }
8596 else if (!strcmp(attr->name, "printer-error-policy"))
8597 {
8598 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
8599 continue;
8600
8601 if (strcmp(attr->values[0].string.text, "abort-job") &&
8602 strcmp(attr->values[0].string.text, "retry-job") &&
8603 strcmp(attr->values[0].string.text, "stop-printer"))
8604 {
8605 send_ipp_status(con, IPP_NOT_POSSIBLE,
8606 _("Unknown printer-error-policy \"%s\"."),
8607 attr->values[0].string.text);
8608 return;
8609 }
8610
8611 cupsdLogMessage(CUPSD_LOG_DEBUG,
8612 "Setting printer-error-policy to \"%s\"...",
8613 attr->values[0].string.text);
8614 cupsdSetString(&printer->error_policy, attr->values[0].string.text);
8615 }
8616 else if (!strcmp(attr->name, "document-format-default") ||
8617 !strcmp(attr->name, "notify-lease-duration-default") ||
8618 !strcmp(attr->name, "notify-events-default"))
8619 continue;
8620
8621 /*
8622 * Skip any other non-default attributes...
8623 */
8624
8625 namelen = strlen(attr->name);
8626 if (namelen < 9 || strcmp(attr->name + namelen - 8, "-default") ||
8627 namelen > (sizeof(name) - 1) || attr->num_values != 1)
8628 continue;
8629
8630 /*
8631 * OK, anything else must be a user-defined default...
8632 */
8633
8634 strlcpy(name, attr->name, sizeof(name));
8635 name[namelen - 8] = '\0'; /* Strip "-default" */
8636
8637 switch (attr->value_tag)
8638 {
8639 case IPP_TAG_DELETEATTR :
8640 printer->num_options = cupsRemoveOption(name,
8641 printer->num_options,
8642 &(printer->options));
8643 cupsdLogMessage(CUPSD_LOG_DEBUG,
8644 "Deleting %s", attr->name);
8645 break;
8646
8647 case IPP_TAG_NAME :
8648 case IPP_TAG_KEYWORD :
8649 case IPP_TAG_URI :
8650 printer->num_options = cupsAddOption(name,
8651 attr->values[0].string.text,
8652 printer->num_options,
8653 &(printer->options));
8654 cupsdLogMessage(CUPSD_LOG_DEBUG,
8655 "Setting %s to \"%s\"...", attr->name,
8656 attr->values[0].string.text);
8657 break;
8658
8659 case IPP_TAG_BOOLEAN :
8660 printer->num_options = cupsAddOption(name,
8661 attr->values[0].boolean ?
8662 "true" : "false",
8663 printer->num_options,
8664 &(printer->options));
8665 cupsdLogMessage(CUPSD_LOG_DEBUG,
8666 "Setting %s to %s...", attr->name,
8667 attr->values[0].boolean ? "true" : "false");
8668 break;
8669
8670 case IPP_TAG_INTEGER :
8671 case IPP_TAG_ENUM :
8672 sprintf(value, "%d", attr->values[0].integer);
8673 printer->num_options = cupsAddOption(name, value,
8674 printer->num_options,
8675 &(printer->options));
8676 cupsdLogMessage(CUPSD_LOG_DEBUG,
8677 "Setting %s to %s...", attr->name, value);
8678 break;
8679
8680 case IPP_TAG_RANGE :
8681 sprintf(value, "%d-%d", attr->values[0].range.lower,
8682 attr->values[0].range.upper);
8683 printer->num_options = cupsAddOption(name, value,
8684 printer->num_options,
8685 &(printer->options));
8686 cupsdLogMessage(CUPSD_LOG_DEBUG,
8687 "Setting %s to %s...", attr->name, value);
8688 break;
8689
8690 case IPP_TAG_RESOLUTION :
8691 sprintf(value, "%dx%d%s", attr->values[0].resolution.xres,
8692 attr->values[0].resolution.yres,
8693 attr->values[0].resolution.units == IPP_RES_PER_INCH ?
8694 "dpi" : "dpc");
8695 printer->num_options = cupsAddOption(name, value,
8696 printer->num_options,
8697 &(printer->options));
8698 cupsdLogMessage(CUPSD_LOG_DEBUG,
8699 "Setting %s to %s...", attr->name, value);
8700 break;
8701
8702 default :
8703 /* Do nothing for other values */
8704 break;
8705 }
8706 }
8707 }
8708
8709
8710 /*
8711 * 'start_printer()' - Start a printer.
8712 */
8713
8714 static void
8715 start_printer(cupsd_client_t *con, /* I - Client connection */
8716 ipp_attribute_t *uri) /* I - Printer URI */
8717 {
8718 http_status_t status; /* Policy status */
8719 cups_ptype_t dtype; /* Destination type (printer or class) */
8720 char method[HTTP_MAX_URI],
8721 /* Method portion of URI */
8722 username[HTTP_MAX_URI],
8723 /* Username portion of URI */
8724 host[HTTP_MAX_URI],
8725 /* Host portion of URI */
8726 resource[HTTP_MAX_URI];
8727 /* Resource portion of URI */
8728 int port; /* Port portion of URI */
8729 const char *name; /* Printer name */
8730 cupsd_printer_t *printer; /* Printer data */
8731
8732
8733 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", con,
8734 con->http.fd, uri->values[0].string.text);
8735
8736 /*
8737 * Is the destination valid?
8738 */
8739
8740 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
8741 sizeof(method), username, sizeof(username), host,
8742 sizeof(host), &port, resource, sizeof(resource));
8743
8744 if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
8745 {
8746 /*
8747 * Bad URI...
8748 */
8749
8750 send_ipp_status(con, IPP_NOT_FOUND,
8751 _("The printer or class was not found."));
8752 return;
8753 }
8754
8755 /*
8756 * Check policy...
8757 */
8758
8759 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8760 {
8761 send_http_error(con, status);
8762 return;
8763 }
8764
8765 /*
8766 * Start the printer...
8767 */
8768
8769 printer->state_message[0] = '\0';
8770
8771 cupsdStartPrinter(printer, 1);
8772
8773 if (dtype & CUPS_PRINTER_CLASS)
8774 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".", name,
8775 get_username(con));
8776 else
8777 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".", name,
8778 get_username(con));
8779
8780 cupsdCheckJobs();
8781
8782 /*
8783 * Everything was ok, so return OK status...
8784 */
8785
8786 con->response->request.status.status_code = IPP_OK;
8787 }
8788
8789
8790 /*
8791 * 'stop_printer()' - Stop a printer.
8792 */
8793
8794 static void
8795 stop_printer(cupsd_client_t *con, /* I - Client connection */
8796 ipp_attribute_t *uri) /* I - Printer URI */
8797 {
8798 http_status_t status; /* Policy status */
8799 cups_ptype_t dtype; /* Destination type (printer or class) */
8800 char method[HTTP_MAX_URI],
8801 /* Method portion of URI */
8802 username[HTTP_MAX_URI],
8803 /* Username portion of URI */
8804 host[HTTP_MAX_URI],
8805 /* Host portion of URI */
8806 resource[HTTP_MAX_URI];
8807 /* Resource portion of URI */
8808 int port; /* Port portion of URI */
8809 const char *name; /* Printer name */
8810 cupsd_printer_t *printer; /* Printer data */
8811 ipp_attribute_t *attr; /* printer-state-message attribute */
8812
8813
8814 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", con,
8815 con->http.fd, uri->values[0].string.text);
8816
8817 /*
8818 * Is the destination valid?
8819 */
8820
8821 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
8822 sizeof(method), username, sizeof(username), host,
8823 sizeof(host), &port, resource, sizeof(resource));
8824
8825 if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
8826 {
8827 /*
8828 * Bad URI...
8829 */
8830
8831 send_ipp_status(con, IPP_NOT_FOUND,
8832 _("The printer or class was not found."));
8833 return;
8834 }
8835
8836 /*
8837 * Check policy...
8838 */
8839
8840 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8841 {
8842 send_http_error(con, status);
8843 return;
8844 }
8845
8846 /*
8847 * Stop the printer...
8848 */
8849
8850 if ((attr = ippFindAttribute(con->request, "printer-state-message",
8851 IPP_TAG_TEXT)) == NULL)
8852 strcpy(printer->state_message, "Paused");
8853 else
8854 {
8855 strlcpy(printer->state_message, attr->values[0].string.text,
8856 sizeof(printer->state_message));
8857 }
8858
8859 cupsdStopPrinter(printer, 1);
8860
8861 if (dtype & CUPS_PRINTER_CLASS)
8862 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".", name,
8863 get_username(con));
8864 else
8865 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".", name,
8866 get_username(con));
8867
8868 /*
8869 * Everything was ok, so return OK status...
8870 */
8871
8872 con->response->request.status.status_code = IPP_OK;
8873 }
8874
8875
8876 /*
8877 * 'url_encode_attr()' - URL-encode a string attribute.
8878 */
8879
8880 static void
8881 url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */
8882 char *buffer,/* I - String buffer */
8883 int bufsize)/* I - Size of buffer */
8884 {
8885 int i; /* Looping var */
8886 char *bufptr, /* Pointer into buffer */
8887 *bufend, /* End of buffer */
8888 *valptr; /* Pointer into value */
8889
8890
8891 strlcpy(buffer, attr->name, bufsize);
8892 bufptr = buffer + strlen(buffer);
8893 bufend = buffer + bufsize - 1;
8894
8895 for (i = 0; i < attr->num_values; i ++)
8896 {
8897 if (bufptr >= bufend)
8898 break;
8899
8900 if (i)
8901 *bufptr++ = ',';
8902 else
8903 *bufptr++ = '=';
8904
8905 if (bufptr >= bufend)
8906 break;
8907
8908 *bufptr++ = '\'';
8909
8910 for (valptr = attr->values[i].string.text;
8911 *valptr && bufptr < bufend;
8912 valptr ++)
8913 if (*valptr == ' ')
8914 {
8915 if (bufptr >= (bufend - 2))
8916 break;
8917
8918 *bufptr++ = '%';
8919 *bufptr++ = '2';
8920 *bufptr++ = '0';
8921 }
8922 else if (*valptr == '\'' || *valptr == '\\')
8923 {
8924 *bufptr++ = '\\';
8925 *bufptr++ = *valptr;
8926 }
8927 else
8928 *bufptr++ = *valptr;
8929
8930 if (bufptr >= bufend)
8931 break;
8932
8933 *bufptr++ = '\'';
8934 }
8935
8936 *bufptr = '\0';
8937 }
8938
8939
8940 /*
8941 * 'user_allowed()' - See if a user is allowed to print to a queue.
8942 */
8943
8944 static int /* O - 0 if not allowed, 1 if allowed */
8945 user_allowed(cupsd_printer_t *p, /* I - Printer or class */
8946 const char *username) /* I - Username */
8947 {
8948 int i; /* Looping var */
8949 struct passwd *pw; /* User password data */
8950
8951
8952 if (p->num_users == 0)
8953 return (1);
8954
8955 if (!strcmp(username, "root"))
8956 return (1);
8957
8958 pw = getpwnam(username);
8959 endpwent();
8960
8961 for (i = 0; i < p->num_users; i ++)
8962 {
8963 if (p->users[i][0] == '@')
8964 {
8965 /*
8966 * Check group membership...
8967 */
8968
8969 if (cupsdCheckGroup(username, pw, p->users[i] + 1))
8970 break;
8971 }
8972 else if (!strcasecmp(username, p->users[i]))
8973 break;
8974 }
8975
8976 return ((i < p->num_users) != p->deny_users);
8977 }
8978
8979
8980 /*
8981 * 'validate_job()' - Validate printer options and destination.
8982 */
8983
8984 static void
8985 validate_job(cupsd_client_t *con, /* I - Client connection */
8986 ipp_attribute_t *uri) /* I - Printer URI */
8987 {
8988 http_status_t status; /* Policy status */
8989 ipp_attribute_t *attr; /* Current attribute */
8990 ipp_attribute_t *format; /* Document-format attribute */
8991 cups_ptype_t dtype; /* Destination type (printer or class) */
8992 char method[HTTP_MAX_URI],
8993 /* Method portion of URI */
8994 username[HTTP_MAX_URI],
8995 /* Username portion of URI */
8996 host[HTTP_MAX_URI],
8997 /* Host portion of URI */
8998 resource[HTTP_MAX_URI];
8999 /* Resource portion of URI */
9000 int port; /* Port portion of URI */
9001 char super[MIME_MAX_SUPER],
9002 /* Supertype of file */
9003 type[MIME_MAX_TYPE];
9004 /* Subtype of file */
9005 cupsd_printer_t *printer; /* Printer */
9006
9007
9008 cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", con,
9009 con->http.fd, uri->values[0].string.text);
9010
9011 /*
9012 * OK, see if the client is sending the document compressed - CUPS
9013 * doesn't support compression yet...
9014 */
9015
9016 if ((attr = ippFindAttribute(con->request, "compression",
9017 IPP_TAG_KEYWORD)) != NULL &&
9018 !strcmp(attr->values[0].string.text, "none"))
9019 {
9020 send_ipp_status(con, IPP_ATTRIBUTES,
9021 _("Unsupported compression attribute %s!"),
9022 attr->values[0].string.text);
9023 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
9024 "compression", NULL, attr->values[0].string.text);
9025 return;
9026 }
9027
9028 /*
9029 * Is it a format we support?
9030 */
9031
9032 if ((format = ippFindAttribute(con->request, "document-format",
9033 IPP_TAG_MIMETYPE)) != NULL)
9034 {
9035 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
9036 {
9037 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"!"),
9038 format->values[0].string.text);
9039 return;
9040 }
9041
9042 if ((strcmp(super, "application") || strcmp(type, "octet-stream")) &&
9043 !mimeType(MimeDatabase, super, type))
9044 {
9045 cupsdLogMessage(CUPSD_LOG_INFO,
9046 "Hint: Do you have the raw file printing rules enabled?");
9047 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
9048 _("Unsupported format \"%s\"!"),
9049 format->values[0].string.text);
9050 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9051 "document-format", NULL, format->values[0].string.text);
9052 return;
9053 }
9054 }
9055
9056 /*
9057 * Is the destination valid?
9058 */
9059
9060 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
9061 sizeof(method), username, sizeof(username), host,
9062 sizeof(host), &port, resource, sizeof(resource));
9063
9064 if (cupsdValidateDest(host, resource, &dtype, &printer) == NULL)
9065 {
9066 /*
9067 * Bad URI...
9068 */
9069
9070 send_ipp_status(con, IPP_NOT_FOUND,
9071 _("The printer or class was not found."));
9072 return;
9073 }
9074
9075 /*
9076 * Check policy...
9077 */
9078
9079 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
9080 {
9081 send_http_error(con, status);
9082 return;
9083 }
9084
9085 /*
9086 * Everything was ok, so return OK status...
9087 */
9088
9089 con->response->request.status.status_code = IPP_OK;
9090 }
9091
9092
9093 /*
9094 * 'validate_name()' - Make sure the printer name only contains valid chars.
9095 */
9096
9097 static int /* O - 0 if name is no good, 1 if name is good */
9098 validate_name(const char *name) /* I - Name to check */
9099 {
9100 const char *ptr; /* Pointer into name */
9101
9102
9103 /*
9104 * Scan the whole name...
9105 */
9106
9107 for (ptr = name; *ptr; ptr ++)
9108 if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
9109 return (0);
9110
9111 /*
9112 * All the characters are good; validate the length, too...
9113 */
9114
9115 return ((ptr - name) < 128);
9116 }
9117
9118
9119 /*
9120 * 'validate_user()' - Validate the user for the request.
9121 */
9122
9123 static int /* O - 1 if permitted, 0 otherwise */
9124 validate_user(cupsd_job_t *job, /* I - Job */
9125 cupsd_client_t *con, /* I - Client connection */
9126 const char *owner, /* I - Owner of job/resource */
9127 char *username, /* O - Authenticated username */
9128 int userlen) /* I - Length of username */
9129 {
9130 cupsd_printer_t *printer; /* Printer for job */
9131
9132
9133 cupsdLogMessage(CUPSD_LOG_DEBUG2,
9134 "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, "
9135 "userlen=%d)",
9136 job ? job->id : 0, con->http.fd, owner ? owner : "(null)",
9137 username, userlen);
9138
9139 /*
9140 * Validate input...
9141 */
9142
9143 if (!con || !owner || !username || userlen <= 0)
9144 return (0);
9145
9146 /*
9147 * Get the best authenticated username that is available.
9148 */
9149
9150 strlcpy(username, get_username(con), userlen);
9151
9152 /*
9153 * Check the username against the owner...
9154 */
9155
9156 printer = cupsdFindDest(job->dest);
9157
9158 return (cupsdCheckPolicy(printer ? printer->op_policy_ptr : DefaultPolicyPtr,
9159 con, owner) == HTTP_OK);
9160 }
9161
9162
9163 /*
9164 * End of "$Id: ipp.c 6145 2006-12-06 20:10:16Z mike $".
9165 */