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