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