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