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