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