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