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