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