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