]> 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/*
d09495fa 2 * "$Id: ipp.c 5907 2006-08-30 02:18:28Z 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);
f7faf1f5 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
d09495fa 633 * clients do not implement chunking properly, we cannot use
f301802f 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 {
ef416fc2 641 httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n");
d09495fa 642 cupsdFlushHeader(con);
643
644 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
ef416fc2 645 }
646 else
f301802f 647#endif /* CUPSD_USE_CHUNKING */
ef416fc2 648 {
d09495fa 649 size_t length; /* Length of response */
ef416fc2 650
d09495fa 651
652 length = ippLength(con->response);
ef416fc2 653
654 httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n\r\n",
d09495fa 655 CUPS_LLCAST length);
656 cupsdFlushHeader(con);
657
658 con->http.data_encoding = HTTP_ENCODE_LENGTH;
659 con->http.data_remaining = length;
ef416fc2 660 }
661
662 cupsdLogMessage(CUPSD_LOG_DEBUG2,
663 "cupsdProcessIPPRequest: Adding fd %d to OutputSet...",
664 con->http.fd);
665
666 FD_SET(con->http.fd, OutputSet);
667
668 /*
669 * Tell the caller the response header was sent successfully...
670 */
671
672 return (1);
673 }
674 else
675 {
676 /*
677 * Tell the caller the response header could not be sent...
678 */
679
680 return (0);
681 }
682 }
683 else
684 {
685 /*
686 * Sending data from a subprocess like cups-deviced; tell the caller
687 * everything is A-OK so far...
688 */
689
690 return (1);
691 }
692}
693
694
695/*
696 * 'accept_jobs()' - Accept print jobs to a printer.
697 */
698
699static void
700accept_jobs(cupsd_client_t *con, /* I - Client connection */
701 ipp_attribute_t *uri) /* I - Printer or class URI */
702{
703 http_status_t status; /* Policy status */
704 cups_ptype_t dtype; /* Destination type (printer or class) */
f7faf1f5 705 char method[HTTP_MAX_URI], /* Method portion of URI */
706 username[HTTP_MAX_URI], /* Username portion of URI */
707 host[HTTP_MAX_URI], /* Host portion of URI */
708 resource[HTTP_MAX_URI]; /* Resource portion of URI */
709 int port; /* Port portion of URI */
710 const char *name; /* Printer name */
ef416fc2 711 cupsd_printer_t *printer; /* Printer data */
712
713
714 cupsdLogMessage(CUPSD_LOG_DEBUG2, "accept_jobs(%p[%d], %s)", con,
715 con->http.fd, uri->values[0].string.text);
716
717 /*
718 * Is the destination valid?
719 */
720
f7faf1f5 721 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
722 sizeof(method), username, sizeof(username), host,
723 sizeof(host), &port, resource, sizeof(resource));
724
725 if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
ef416fc2 726 {
727 /*
728 * Bad URI...
729 */
730
731 send_ipp_status(con, IPP_NOT_FOUND,
732 _("The printer or class was not found."));
733 return;
734 }
735
736 /*
737 * Check policy...
738 */
739
740 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
741 {
742 send_http_error(con, status);
743 return;
744 }
745
746 /*
747 * Accept jobs sent to the printer...
748 */
749
750 printer->accepting = 1;
751 printer->state_message[0] = '\0';
752
753 cupsdAddPrinterHistory(printer);
754
755 if (dtype & CUPS_PRINTER_CLASS)
756 cupsdSaveAllClasses();
757 else
758 cupsdSaveAllPrinters();
759
f7faf1f5 760 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" now accepting jobs (\"%s\").", name,
761 get_username(con));
ef416fc2 762
763 /*
764 * Everything was ok, so return OK status...
765 */
766
767 con->response->request.status.status_code = IPP_OK;
768}
769
770
771/*
772 * 'add_class()' - Add a class to the system.
773 */
774
775static void
776add_class(cupsd_client_t *con, /* I - Client connection */
777 ipp_attribute_t *uri) /* I - URI of class */
778{
779 http_status_t status; /* Policy status */
780 int i; /* Looping var */
781 char method[HTTP_MAX_URI], /* Method portion of URI */
782 username[HTTP_MAX_URI], /* Username portion of URI */
783 host[HTTP_MAX_URI], /* Host portion of URI */
784 resource[HTTP_MAX_URI]; /* Resource portion of URI */
785 int port; /* Port portion of URI */
786 cupsd_printer_t *pclass, /* Class */
787 *member; /* Member printer/class */
788 cups_ptype_t dtype; /* Destination type */
ef416fc2 789 ipp_attribute_t *attr; /* Printer attribute */
790 int modify; /* Non-zero if we just modified */
b423cd4c 791 char newname[IPP_MAX_NAME]; /* New class name */
e00b005a 792 int need_restart_job; /* Need to restart job? */
ef416fc2 793
794
795 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_class(%p[%d], %s)", con,
796 con->http.fd, uri->values[0].string.text);
797
798 /*
799 * Do we have a valid URI?
800 */
801
a4d04587 802 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
803 sizeof(method), username, sizeof(username), host,
804 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 805
806
fa73b229 807 if (strncmp(resource, "/classes/", 9) || strlen(resource) == 9)
ef416fc2 808 {
809 /*
810 * No, return an error...
811 */
812
813 send_ipp_status(con, IPP_BAD_REQUEST,
814 _("The printer-uri must be of the form "
815 "\"ipp://HOSTNAME/classes/CLASSNAME\"."));
816 return;
817 }
818
819 /*
820 * Do we have a valid printer name?
821 */
822
823 if (!validate_name(resource + 9))
824 {
825 /*
826 * No, return an error...
827 */
828
829 send_ipp_status(con, IPP_BAD_REQUEST,
830 _("The printer-uri \"%s\" contains invalid characters."),
831 uri->values[0].string.text);
832 return;
833 }
834
835 /*
836 * Check policy...
837 */
838
839 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
840 {
841 send_http_error(con, status);
842 return;
843 }
844
845 /*
846 * See if the class already exists; if not, create a new class...
847 */
848
849 if ((pclass = cupsdFindClass(resource + 9)) == NULL)
850 {
851 /*
852 * Class doesn't exist; see if we have a printer of the same name...
853 */
854
855 if ((pclass = cupsdFindPrinter(resource + 9)) != NULL &&
856 !(pclass->type & CUPS_PRINTER_REMOTE))
857 {
858 /*
859 * Yes, return an error...
860 */
861
862 send_ipp_status(con, IPP_NOT_POSSIBLE,
863 _("A printer named \"%s\" already exists!"),
864 resource + 9);
865 return;
866 }
867
868 /*
869 * No, add the pclass...
870 */
871
872 pclass = cupsdAddClass(resource + 9);
873 modify = 0;
874 }
875 else if (pclass->type & CUPS_PRINTER_IMPLICIT)
876 {
877 /*
878 * Rename the implicit class to "AnyClass" or remove it...
879 */
880
881 if (ImplicitAnyClasses)
882 {
b423cd4c 883 snprintf(newname, sizeof(newname), "Any%s", resource + 9);
884 cupsdRenamePrinter(pclass, newname);
ef416fc2 885 }
886 else
887 cupsdDeletePrinter(pclass, 1);
888
889 /*
890 * Add the class as a new local class...
891 */
892
893 pclass = cupsdAddClass(resource + 9);
894 modify = 0;
895 }
896 else if (pclass->type & CUPS_PRINTER_REMOTE)
897 {
898 /*
899 * Rename the remote class to "Class"...
900 */
901
b423cd4c 902 snprintf(newname, sizeof(newname), "%s@%s", resource + 9, pclass->hostname);
903 cupsdRenamePrinter(pclass, newname);
ef416fc2 904
905 /*
906 * Add the class as a new local class...
907 */
908
909 pclass = cupsdAddClass(resource + 9);
910 modify = 0;
911 }
912 else
913 modify = 1;
914
915 /*
916 * Look for attributes and copy them over as needed...
917 */
918
e00b005a 919 need_restart_job = 0;
920
fa73b229 921 if ((attr = ippFindAttribute(con->request, "printer-location",
922 IPP_TAG_TEXT)) != NULL)
ef416fc2 923 cupsdSetString(&pclass->location, attr->values[0].string.text);
924
fa73b229 925 if ((attr = ippFindAttribute(con->request, "printer-info",
926 IPP_TAG_TEXT)) != NULL)
ef416fc2 927 cupsdSetString(&pclass->info, attr->values[0].string.text);
928
fa73b229 929 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs",
930 IPP_TAG_BOOLEAN)) != NULL)
ef416fc2 931 {
932 cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
933 pclass->name, attr->values[0].boolean, pclass->accepting);
934
935 pclass->accepting = attr->values[0].boolean;
936 cupsdAddPrinterHistory(pclass);
937 }
938
fa73b229 939 if ((attr = ippFindAttribute(con->request, "printer-is-shared",
940 IPP_TAG_BOOLEAN)) != NULL)
ef416fc2 941 {
942 if (pclass->shared && !attr->values[0].boolean)
943 cupsdSendBrowseDelete(pclass);
944
945 cupsdLogMessage(CUPSD_LOG_INFO,
946 "Setting %s printer-is-shared to %d (was %d.)",
947 pclass->name, attr->values[0].boolean, pclass->shared);
948
949 pclass->shared = attr->values[0].boolean;
950 }
951
fa73b229 952 if ((attr = ippFindAttribute(con->request, "printer-state",
953 IPP_TAG_ENUM)) != NULL)
ef416fc2 954 {
955 if (attr->values[0].integer != IPP_PRINTER_IDLE &&
956 attr->values[0].integer != IPP_PRINTER_STOPPED)
957 {
958 send_ipp_status(con, IPP_BAD_REQUEST,
959 _("Attempt to set %s printer-state to bad value %d!"),
960 pclass->name, attr->values[0].integer);
961 return;
962 }
963
964 cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)", pclass->name,
965 attr->values[0].integer, pclass->state);
966
967 if (attr->values[0].integer == IPP_PRINTER_STOPPED)
968 cupsdStopPrinter(pclass, 0);
969 else
e00b005a 970 {
ef416fc2 971 cupsdSetPrinterState(pclass, (ipp_pstate_t)(attr->values[0].integer), 0);
e00b005a 972 need_restart_job = 1;
973 }
ef416fc2 974 }
fa73b229 975 if ((attr = ippFindAttribute(con->request, "printer-state-message",
976 IPP_TAG_TEXT)) != NULL)
ef416fc2 977 {
978 strlcpy(pclass->state_message, attr->values[0].string.text,
979 sizeof(pclass->state_message));
980 cupsdAddPrinterHistory(pclass);
981 }
fa73b229 982 if ((attr = ippFindAttribute(con->request, "member-uris",
983 IPP_TAG_URI)) != NULL)
ef416fc2 984 {
985 /*
986 * Clear the printer array as needed...
987 */
988
e00b005a 989 need_restart_job = 1;
990
ef416fc2 991 if (pclass->num_printers > 0)
992 {
993 free(pclass->printers);
994 pclass->num_printers = 0;
995 }
996
997 /*
998 * Add each printer or class that is listed...
999 */
1000
1001 for (i = 0; i < attr->num_values; i ++)
1002 {
1003 /*
1004 * Search for the printer or class URI...
1005 */
1006
f7faf1f5 1007 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text, method,
1008 sizeof(method), username, sizeof(username), host,
1009 sizeof(host), &port, resource, sizeof(resource));
1010
d09495fa 1011 if (!cupsdValidateDest(host, resource, &dtype, &member))
ef416fc2 1012 {
1013 /*
1014 * Bad URI...
1015 */
1016
1017 send_ipp_status(con, IPP_NOT_FOUND,
1018 _("The printer or class was not found."));
1019 return;
1020 }
1021
1022 /*
1023 * Add it to the class...
1024 */
1025
1026 cupsdAddPrinterToClass(pclass, member);
1027 }
1028 }
1029
b423cd4c 1030 set_printer_defaults(con, pclass);
1031
ef416fc2 1032 /*
1033 * Update the printer class attributes and return...
1034 */
1035
1036 cupsdSetPrinterAttrs(pclass);
1037 cupsdSaveAllClasses();
e00b005a 1038
1039 if (need_restart_job && pclass->job)
1040 {
1041 cupsd_job_t *job;
1042
1043 /*
1044 * Stop the current job and then restart it below...
1045 */
1046
1047 job = (cupsd_job_t *)pclass->job;
1048
1049 cupsdStopJob(job, 1);
bd7854cb 1050
e00b005a 1051 job->state->values[0].integer = IPP_JOB_PENDING;
bd7854cb 1052 job->state_value = IPP_JOB_PENDING;
e00b005a 1053 }
1054
1055 if (need_restart_job)
1056 cupsdCheckJobs();
ef416fc2 1057
1058 cupsdWritePrintcap();
1059
1060 if (modify)
1061 {
1062 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, pclass, NULL,
1063 "Class \"%s\" modified by \"%s\".", pclass->name,
e00b005a 1064 get_username(con));
ef416fc2 1065
1066 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" modified by \"%s\".",
e00b005a 1067 pclass->name, get_username(con));
ef416fc2 1068 }
1069 else
1070 {
1071 cupsdAddPrinterHistory(pclass);
1072
1073 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, pclass, NULL,
1074 "New class \"%s\" added by \"%s\".", pclass->name,
e00b005a 1075 get_username(con));
ef416fc2 1076
1077 cupsdLogMessage(CUPSD_LOG_INFO, "New class \"%s\" added by \"%s\".",
e00b005a 1078 pclass->name, get_username(con));
ef416fc2 1079 }
1080
1081 con->response->request.status.status_code = IPP_OK;
1082}
1083
1084
1085/*
1086 * 'add_file()' - Add a file to a job.
1087 */
1088
1089static int /* O - 0 on success, -1 on error */
1090add_file(cupsd_client_t *con, /* I - Connection to client */
1091 cupsd_job_t *job, /* I - Job to add to */
1092 mime_type_t *filetype, /* I - Type of file */
1093 int compression) /* I - Compression */
1094{
1095 mime_type_t **filetypes; /* New filetypes array... */
1096 int *compressions; /* New compressions array... */
1097
1098
1099 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1100 "add_file(con=%p[%d], job=%d, filetype=%s/%s, compression=%d)",
1101 con, con->http.fd, job->id, filetype->super, filetype->type,
1102 compression);
1103
1104 /*
1105 * Add the file to the job...
1106 */
1107
1108 if (job->num_files == 0)
1109 {
1110 compressions = (int *)malloc(sizeof(int));
1111 filetypes = (mime_type_t **)malloc(sizeof(mime_type_t *));
1112 }
1113 else
1114 {
1115 compressions = (int *)realloc(job->compressions,
1116 (job->num_files + 1) * sizeof(int));
1117 filetypes = (mime_type_t **)realloc(job->filetypes,
1118 (job->num_files + 1) *
1119 sizeof(mime_type_t *));
1120 }
1121
fa73b229 1122 if (!compressions || !filetypes)
ef416fc2 1123 {
1124 cupsdCancelJob(job, 1);
1125
1126 send_ipp_status(con, IPP_INTERNAL_ERROR,
1127 _("Unable to allocate memory for file types!"));
1128 return (-1);
1129 }
1130
1131 job->compressions = compressions;
1132 job->compressions[job->num_files] = compression;
1133 job->filetypes = filetypes;
1134 job->filetypes[job->num_files] = filetype;
1135
1136 job->num_files ++;
1137
1138 return (0);
1139}
1140
1141
1142/*
b423cd4c 1143 * 'add_job()' - Add a job to a print queue.
ef416fc2 1144 */
1145
b423cd4c 1146static cupsd_job_t * /* O - Job object */
1147add_job(cupsd_client_t *con, /* I - Client connection */
f7faf1f5 1148 ipp_attribute_t *uri, /* I - printer-uri */
1149 cupsd_printer_t **dprinter, /* I - Destination printer */
80ca4592 1150 mime_type_t *filetype) /* I - First print file type, if any */
ef416fc2 1151{
b423cd4c 1152 http_status_t status; /* Policy status */
1153 ipp_attribute_t *attr; /* Current attribute */
f7faf1f5 1154 const char *dest; /* Destination */
1155 cups_ptype_t dtype; /* Destination type (printer or class) */
b423cd4c 1156 const char *val; /* Default option value */
1157 int priority; /* Job priority */
1158 char *title; /* Job name/title */
1159 cupsd_job_t *job; /* Current job */
f7faf1f5 1160 char job_uri[HTTP_MAX_URI], /* Job URI */
1161 method[HTTP_MAX_URI], /* Method portion of URI */
1162 username[HTTP_MAX_URI], /* Username portion of URI */
1163 host[HTTP_MAX_URI], /* Host portion of URI */
1164 resource[HTTP_MAX_URI]; /* Resource portion of URI */
1165 int port; /* Port portion of URI */
1166 cupsd_printer_t *printer; /* Printer data */
b423cd4c 1167 int kbytes; /* Size of print file */
1168 int i; /* Looping var */
1169 int lowerpagerange; /* Page range bound */
ef416fc2 1170
1171
f7faf1f5 1172 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %s)", con,
1173 con->http.fd, uri->values[0].string.text);
1174
1175 /*
1176 * Is the destination valid?
1177 */
1178
1179 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
1180 sizeof(method), username, sizeof(username), host,
1181 sizeof(host), &port, resource, sizeof(resource));
1182
1183 if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
1184 {
1185 /*
1186 * Bad URI...
1187 */
1188
1189 send_ipp_status(con, IPP_NOT_FOUND,
1190 _("The printer or class was not found."));
1191 return (NULL);
1192 }
1193
1194 if (dprinter)
1195 *dprinter = printer;
ef416fc2 1196
b423cd4c 1197 /*
1198 * Check remote printing to non-shared printer...
1199 */
ef416fc2 1200
b423cd4c 1201 if (!printer->shared &&
1202 strcasecmp(con->http.hostname, "localhost") &&
1203 strcasecmp(con->http.hostname, ServerName))
1204 {
1205 send_ipp_status(con, IPP_NOT_AUTHORIZED,
1206 _("The printer or class is not shared!"));
1207 return (NULL);
1208 }
ef416fc2 1209
b423cd4c 1210 /*
1211 * Check policy...
1212 */
ef416fc2 1213
b423cd4c 1214 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
1215 {
1216 send_http_error(con, status);
1217 return (NULL);
1218 }
1219 else if ((printer->type & CUPS_PRINTER_AUTHENTICATED) && !con->username[0])
1220 {
1221 send_http_error(con, HTTP_UNAUTHORIZED);
1222 return (NULL);
1223 }
ef416fc2 1224
b423cd4c 1225 /*
1226 * See if the printer is accepting jobs...
1227 */
1228
1229 if (!printer->accepting)
1230 {
1231 send_ipp_status(con, IPP_NOT_ACCEPTING,
1232 _("Destination \"%s\" is not accepting jobs."),
f7faf1f5 1233 dest);
b423cd4c 1234 return (NULL);
ef416fc2 1235 }
ef416fc2 1236
b423cd4c 1237 /*
80ca4592 1238 * Validate job template attributes; for now just document-format,
1239 * copies, and page-ranges...
b423cd4c 1240 */
ef416fc2 1241
80ca4592 1242 if (filetype && printer->filetypes &&
1243 !cupsArrayFind(printer->filetypes, filetype))
1244 {
1245 char mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
1246 /* MIME media type string */
1247
1248
1249 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
1250 filetype->type);
1251
1252 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
1253 _("Unsupported format \'%s\'!"), mimetype);
1254
1255 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
1256 "document-format", NULL, mimetype);
1257
1258 return (NULL);
1259 }
1260
b423cd4c 1261 if ((attr = ippFindAttribute(con->request, "copies",
1262 IPP_TAG_INTEGER)) != NULL)
1263 {
1264 if (attr->values[0].integer < 1 || attr->values[0].integer > MaxCopies)
1265 {
1266 send_ipp_status(con, IPP_ATTRIBUTES, _("Bad copies value %d."),
1267 attr->values[0].integer);
1268 ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER,
1269 "copies", attr->values[0].integer);
1270 return (NULL);
1271 }
1272 }
ef416fc2 1273
b423cd4c 1274 if ((attr = ippFindAttribute(con->request, "page-ranges",
1275 IPP_TAG_RANGE)) != NULL)
1276 {
1277 for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
1278 {
1279 if (attr->values[i].range.lower < lowerpagerange ||
1280 attr->values[i].range.lower > attr->values[i].range.upper)
1281 {
1282 send_ipp_status(con, IPP_BAD_REQUEST,
1283 _("Bad page-ranges values %d-%d."),
1284 attr->values[i].range.lower,
1285 attr->values[i].range.upper);
1286 return (NULL);
1287 }
ef416fc2 1288
b423cd4c 1289 lowerpagerange = attr->values[i].range.upper + 1;
1290 }
1291 }
ef416fc2 1292
1293 /*
b423cd4c 1294 * Make sure we aren't over our limit...
ef416fc2 1295 */
1296
b423cd4c 1297 if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs)
1298 cupsdCleanJobs();
ef416fc2 1299
f7faf1f5 1300 if (cupsArrayCount(Jobs) >= MaxJobs && MaxJobs)
b423cd4c 1301 {
1302 send_ipp_status(con, IPP_NOT_POSSIBLE,
1303 _("Too many active jobs."));
1304 return (NULL);
1305 }
1306
1307 if (!check_quotas(con, printer))
1308 {
1309 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
1310 return (NULL);
1311 }
ef416fc2 1312
1313 /*
b423cd4c 1314 * Create the job and set things up...
ef416fc2 1315 */
1316
b423cd4c 1317 if ((attr = ippFindAttribute(con->request, "job-priority",
1318 IPP_TAG_INTEGER)) != NULL)
1319 priority = attr->values[0].integer;
1320 else
ef416fc2 1321 {
b423cd4c 1322 if ((val = cupsGetOption("job-priority", printer->num_options,
1323 printer->options)) != NULL)
1324 priority = atoi(val);
1325 else
1326 priority = 50;
ef416fc2 1327
b423cd4c 1328 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
1329 priority);
1330 }
ef416fc2 1331
b423cd4c 1332 if ((attr = ippFindAttribute(con->request, "job-name",
1333 IPP_TAG_NAME)) != NULL)
1334 title = attr->values[0].string.text;
1335 else
1336 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
1337 title = "Untitled");
ef416fc2 1338
b423cd4c 1339 if ((job = cupsdAddJob(priority, printer->name)) == NULL)
1340 {
1341 send_ipp_status(con, IPP_INTERNAL_ERROR,
f7faf1f5 1342 _("Unable to add job for destination \"%s\"!"), dest);
b423cd4c 1343 return (NULL);
1344 }
ef416fc2 1345
f7faf1f5 1346 job->dtype = dtype;
b423cd4c 1347 job->attrs = con->request;
1348 con->request = NULL;
ef416fc2 1349
b423cd4c 1350 add_job_uuid(con, job);
1351 apply_printer_defaults(printer, job);
ef416fc2 1352
b423cd4c 1353 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
ef416fc2 1354
b423cd4c 1355 if (con->username[0])
1356 {
1357 cupsdSetString(&job->username, con->username);
ef416fc2 1358
1359 if (attr)
b423cd4c 1360 cupsdSetString(&attr->values[0].string.text, con->username);
1361
1362 save_auth_info(con, job);
ef416fc2 1363 }
b423cd4c 1364 else if (attr)
1365 {
1366 cupsdLogMessage(CUPSD_LOG_DEBUG,
1367 "add_job: requesting-user-name=\"%s\"",
1368 attr->values[0].string.text);
ef416fc2 1369
b423cd4c 1370 cupsdSetString(&job->username, attr->values[0].string.text);
1371 }
1372 else
1373 cupsdSetString(&job->username, "anonymous");
ef416fc2 1374
b423cd4c 1375 if (!attr)
1376 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
1377 "job-originating-user-name", NULL, job->username);
1378 else
1379 {
1380 attr->group_tag = IPP_TAG_JOB;
757d2cad 1381 _cupsStrFree(attr->name);
1382 attr->name = _cupsStrAlloc("job-originating-user-name");
b423cd4c 1383 }
ef416fc2 1384
b423cd4c 1385 if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
1386 IPP_TAG_ZERO)) != NULL)
ef416fc2 1387 {
b423cd4c 1388 /*
1389 * Request contains a job-originating-host-name attribute; validate it...
1390 */
ef416fc2 1391
b423cd4c 1392 if (attr->value_tag != IPP_TAG_NAME ||
1393 attr->num_values != 1 ||
1394 strcmp(con->http.hostname, "localhost"))
ef416fc2 1395 {
1396 /*
b423cd4c 1397 * Can't override the value if we aren't connected via localhost.
1398 * Also, we can only have 1 value and it must be a name value.
ef416fc2 1399 */
1400
b423cd4c 1401 switch (attr->value_tag)
1402 {
1403 case IPP_TAG_STRING :
1404 case IPP_TAG_TEXTLANG :
1405 case IPP_TAG_NAMELANG :
1406 case IPP_TAG_TEXT :
1407 case IPP_TAG_NAME :
1408 case IPP_TAG_KEYWORD :
1409 case IPP_TAG_URI :
1410 case IPP_TAG_URISCHEME :
1411 case IPP_TAG_CHARSET :
1412 case IPP_TAG_LANGUAGE :
1413 case IPP_TAG_MIMETYPE :
1414 /*
1415 * Free old strings...
1416 */
ef416fc2 1417
b423cd4c 1418 for (i = 0; i < attr->num_values; i ++)
1419 {
757d2cad 1420 _cupsStrFree(attr->values[i].string.text);
b423cd4c 1421 attr->values[i].string.text = NULL;
1422 if (attr->values[i].string.charset)
1423 {
757d2cad 1424 _cupsStrFree(attr->values[i].string.charset);
b423cd4c 1425 attr->values[i].string.charset = NULL;
1426 }
1427 }
ef416fc2 1428
b423cd4c 1429 default :
1430 break;
1431 }
ef416fc2 1432
b423cd4c 1433 /*
1434 * Use the default connection hostname instead...
1435 */
bd7854cb 1436
b423cd4c 1437 attr->value_tag = IPP_TAG_NAME;
1438 attr->num_values = 1;
757d2cad 1439 attr->values[0].string.text = _cupsStrAlloc(con->http.hostname);
b423cd4c 1440 }
bd7854cb 1441
b423cd4c 1442 attr->group_tag = IPP_TAG_JOB;
1443 }
1444 else
1445 {
1446 /*
1447 * No job-originating-host-name attribute, so use the hostname from
1448 * the connection...
1449 */
bd7854cb 1450
b423cd4c 1451 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
1452 "job-originating-host-name", NULL, con->http.hostname);
1453 }
bd7854cb 1454
b423cd4c 1455 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
1456 time(NULL));
1457 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1458 "time-at-processing", 0);
1459 attr->value_tag = IPP_TAG_NOVALUE;
1460 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1461 "time-at-completed", 0);
1462 attr->value_tag = IPP_TAG_NOVALUE;
bd7854cb 1463
1464 /*
b423cd4c 1465 * Add remaining job attributes...
bd7854cb 1466 */
1467
b423cd4c 1468 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1469 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
1470 "job-state", IPP_JOB_STOPPED);
d09495fa 1471 job->state_value = (ipp_jstate_t)job->state->values[0].integer;
b423cd4c 1472 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1473 "job-media-sheets-completed", 0);
1474 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
1475 printer->uri);
1476 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
1477 title);
bd7854cb 1478
b423cd4c 1479 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
1480 IPP_TAG_INTEGER)) != NULL)
1481 attr->values[0].integer = 0;
1482 else
1483 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1484 "job-k-octets", 0);
bd7854cb 1485
b423cd4c 1486 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
1487 IPP_TAG_KEYWORD)) == NULL)
1488 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
1489 if (!attr)
ef416fc2 1490 {
b423cd4c 1491 if ((val = cupsGetOption("job-hold-until", printer->num_options,
1492 printer->options)) == NULL)
1493 val = "no-hold";
ef416fc2 1494
b423cd4c 1495 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1496 "job-hold-until", NULL, val);
ef416fc2 1497 }
b423cd4c 1498 if (attr && strcmp(attr->values[0].string.text, "no-hold") &&
1499 !(printer->type & CUPS_PRINTER_REMOTE))
ef416fc2 1500 {
1501 /*
b423cd4c 1502 * Hold job until specified time...
ef416fc2 1503 */
1504
b423cd4c 1505 cupsdSetJobHoldUntil(job, attr->values[0].string.text);
e53920b9 1506
1507 job->state->values[0].integer = IPP_JOB_HELD;
1508 job->state_value = IPP_JOB_HELD;
ef416fc2 1509 }
b423cd4c 1510 else if (job->attrs->request.op.operation_id == IPP_CREATE_JOB)
ef416fc2 1511 {
e53920b9 1512 job->hold_until = time(NULL) + 60;
b423cd4c 1513 job->state->values[0].integer = IPP_JOB_HELD;
1514 job->state_value = IPP_JOB_HELD;
1515 }
1516 else
1517 {
1518 job->state->values[0].integer = IPP_JOB_PENDING;
1519 job->state_value = IPP_JOB_PENDING;
ef416fc2 1520 }
1521
b423cd4c 1522 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
1523 Classification)
ef416fc2 1524 {
1525 /*
b423cd4c 1526 * Add job sheets options...
ef416fc2 1527 */
1528
b423cd4c 1529 if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1530 IPP_TAG_ZERO)) == NULL)
ef416fc2 1531 {
b423cd4c 1532 cupsdLogMessage(CUPSD_LOG_DEBUG,
1533 "Adding default job-sheets values \"%s,%s\"...",
1534 printer->job_sheets[0], printer->job_sheets[1]);
ef416fc2 1535
b423cd4c 1536 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
1537 2, NULL, NULL);
757d2cad 1538 attr->values[0].string.text = _cupsStrAlloc(printer->job_sheets[0]);
1539 attr->values[1].string.text = _cupsStrAlloc(printer->job_sheets[1]);
ef416fc2 1540 }
1541
b423cd4c 1542 job->job_sheets = attr;
ef416fc2 1543
ef416fc2 1544 /*
b423cd4c 1545 * Enforce classification level if set...
ef416fc2 1546 */
1547
b423cd4c 1548 if (Classification)
ef416fc2 1549 {
b423cd4c 1550 cupsdLogMessage(CUPSD_LOG_INFO,
1551 "Classification=\"%s\", ClassifyOverride=%d",
1552 Classification ? Classification : "(null)",
1553 ClassifyOverride);
ef416fc2 1554
b423cd4c 1555 if (ClassifyOverride)
1556 {
1557 if (!strcmp(attr->values[0].string.text, "none") &&
1558 (attr->num_values == 1 ||
1559 !strcmp(attr->values[1].string.text, "none")))
1560 {
1561 /*
1562 * Force the leading banner to have the classification on it...
1563 */
ef416fc2 1564
b423cd4c 1565 cupsdSetString(&attr->values[0].string.text, Classification);
e00b005a 1566
b423cd4c 1567 cupsdLogMessage(CUPSD_LOG_NOTICE, "[Job %d] CLASSIFICATION FORCED "
1568 "job-sheets=\"%s,none\", "
1569 "job-originating-user-name=\"%s\"",
1570 job->id, Classification, job->username);
1571 }
1572 else if (attr->num_values == 2 &&
1573 strcmp(attr->values[0].string.text,
1574 attr->values[1].string.text) &&
1575 strcmp(attr->values[0].string.text, "none") &&
1576 strcmp(attr->values[1].string.text, "none"))
1577 {
1578 /*
1579 * Can't put two different security markings on the same document!
1580 */
ef416fc2 1581
b423cd4c 1582 cupsdSetString(&attr->values[1].string.text, attr->values[0].string.text);
ef416fc2 1583
b423cd4c 1584 cupsdLogMessage(CUPSD_LOG_NOTICE, "[Job %d] CLASSIFICATION FORCED "
1585 "job-sheets=\"%s,%s\", "
1586 "job-originating-user-name=\"%s\"",
1587 job->id, attr->values[0].string.text,
1588 attr->values[1].string.text, job->username);
1589 }
1590 else if (strcmp(attr->values[0].string.text, Classification) &&
1591 strcmp(attr->values[0].string.text, "none") &&
1592 (attr->num_values == 1 ||
1593 (strcmp(attr->values[1].string.text, Classification) &&
1594 strcmp(attr->values[1].string.text, "none"))))
1595 {
1596 if (attr->num_values == 1)
1597 cupsdLogMessage(CUPSD_LOG_NOTICE,
1598 "[Job %d] CLASSIFICATION OVERRIDDEN "
1599 "job-sheets=\"%s\", "
1600 "job-originating-user-name=\"%s\"",
1601 job->id, attr->values[0].string.text, job->username);
1602 else
1603 cupsdLogMessage(CUPSD_LOG_NOTICE,
1604 "[Job %d] CLASSIFICATION OVERRIDDEN "
1605 "job-sheets=\"%s,%s\",fffff "
1606 "job-originating-user-name=\"%s\"",
1607 job->id, attr->values[0].string.text,
1608 attr->values[1].string.text, job->username);
1609 }
1610 }
1611 else if (strcmp(attr->values[0].string.text, Classification) &&
1612 (attr->num_values == 1 ||
1613 strcmp(attr->values[1].string.text, Classification)))
ef416fc2 1614 {
1615 /*
b423cd4c 1616 * Force the banner to have the classification on it...
ef416fc2 1617 */
1618
b423cd4c 1619 if (attr->num_values > 1 &&
1620 !strcmp(attr->values[0].string.text, attr->values[1].string.text))
1621 {
1622 cupsdSetString(&(attr->values[0].string.text), Classification);
1623 cupsdSetString(&(attr->values[1].string.text), Classification);
1624 }
1625 else
1626 {
1627 if (attr->num_values == 1 ||
1628 strcmp(attr->values[0].string.text, "none"))
1629 cupsdSetString(&(attr->values[0].string.text), Classification);
ef416fc2 1630
b423cd4c 1631 if (attr->num_values > 1 &&
1632 strcmp(attr->values[1].string.text, "none"))
1633 cupsdSetString(&(attr->values[1].string.text), Classification);
1634 }
ef416fc2 1635
b423cd4c 1636 if (attr->num_values > 1)
1637 cupsdLogMessage(CUPSD_LOG_NOTICE,
1638 "[Job %d] CLASSIFICATION FORCED "
1639 "job-sheets=\"%s,%s\", "
1640 "job-originating-user-name=\"%s\"",
1641 job->id, attr->values[0].string.text,
1642 attr->values[1].string.text, job->username);
1643 else
1644 cupsdLogMessage(CUPSD_LOG_NOTICE,
1645 "[Job %d] CLASSIFICATION FORCED "
1646 "job-sheets=\"%s\", "
1647 "job-originating-user-name=\"%s\"",
1648 job->id, Classification, job->username);
ef416fc2 1649 }
1650 }
1651
b423cd4c 1652 /*
1653 * See if we need to add the starting sheet...
1654 */
ef416fc2 1655
b423cd4c 1656 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
ef416fc2 1657 {
b423cd4c 1658 cupsdLogMessage(CUPSD_LOG_INFO,
1659 "Adding start banner page \"%s\" to job %d.",
1660 attr->values[0].string.text, job->id);
ef416fc2 1661
b423cd4c 1662 kbytes = copy_banner(con, job, attr->values[0].string.text);
ef416fc2 1663
b423cd4c 1664 cupsdUpdateQuota(printer, job->username, 0, kbytes);
1665 }
ef416fc2 1666 }
b423cd4c 1667 else if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1668 IPP_TAG_ZERO)) != NULL)
1669 job->sheets = attr;
ef416fc2 1670
b423cd4c 1671 /*
1672 * Fill in the response info...
1673 */
ef416fc2 1674
b423cd4c 1675 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
1676 LocalPort, job->id);
ef416fc2 1677
b423cd4c 1678 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
1679 job_uri);
ef416fc2 1680
b423cd4c 1681 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
ef416fc2 1682
b423cd4c 1683 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
1684 job->state_value);
1685 add_job_state_reasons(con, job);
ef416fc2 1686
b423cd4c 1687 con->response->request.status.status_code = IPP_OK;
ef416fc2 1688
b423cd4c 1689 /*
1690 * Add any job subscriptions...
1691 */
ef416fc2 1692
b423cd4c 1693 add_job_subscriptions(con, job);
ef416fc2 1694
b423cd4c 1695 /*
1696 * Set all but the first two attributes to the job attributes group...
1697 */
ef416fc2 1698
b423cd4c 1699 for (attr = job->attrs->attrs->next->next; attr; attr = attr->next)
1700 attr->group_tag = IPP_TAG_JOB;
ef416fc2 1701
1702 /*
b423cd4c 1703 * Fire the "job created" event...
ef416fc2 1704 */
1705
b423cd4c 1706 cupsdAddEvent(CUPSD_EVENT_JOB_CREATED, printer, job, "Job created.");
ef416fc2 1707
1708 /*
b423cd4c 1709 * Return the new job...
ef416fc2 1710 */
1711
b423cd4c 1712 return (job);
1713}
ef416fc2 1714
ef416fc2 1715
b423cd4c 1716/*
1717 * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
1718 * upon the job and printer state...
1719 */
ef416fc2 1720
b423cd4c 1721static void
1722add_job_state_reasons(
1723 cupsd_client_t *con, /* I - Client connection */
1724 cupsd_job_t *job) /* I - Job info */
1725{
1726 cupsd_printer_t *dest; /* Destination printer */
ef416fc2 1727
ef416fc2 1728
b423cd4c 1729 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job_state_reasons(%p[%d], %d)",
1730 con, con->http.fd, job ? job->id : 0);
ef416fc2 1731
d09495fa 1732 switch (job ? job->state_value : IPP_JOB_CANCELED)
ef416fc2 1733 {
b423cd4c 1734 case IPP_JOB_PENDING :
80ca4592 1735 dest = cupsdFindDest(job->dest);
bd7854cb 1736
b423cd4c 1737 if (dest && dest->state == IPP_PRINTER_STOPPED)
1738 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1739 "job-state-reasons", NULL, "printer-stopped");
1740 else
1741 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1742 "job-state-reasons", NULL, "none");
1743 break;
ef416fc2 1744
b423cd4c 1745 case IPP_JOB_HELD :
1746 if (ippFindAttribute(job->attrs, "job-hold-until",
1747 IPP_TAG_KEYWORD) != NULL ||
1748 ippFindAttribute(job->attrs, "job-hold-until",
1749 IPP_TAG_NAME) != NULL)
1750 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1751 "job-state-reasons", NULL, "job-hold-until-specified");
1752 else
1753 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1754 "job-state-reasons", NULL, "job-incoming");
1755 break;
ef416fc2 1756
b423cd4c 1757 case IPP_JOB_PROCESSING :
1758 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1759 "job-state-reasons", NULL, "job-printing");
1760 break;
ef416fc2 1761
b423cd4c 1762 case IPP_JOB_STOPPED :
1763 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1764 "job-state-reasons", NULL, "job-stopped");
1765 break;
ef416fc2 1766
d09495fa 1767 case IPP_JOB_CANCELED :
b423cd4c 1768 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1769 "job-state-reasons", NULL, "job-canceled-by-user");
1770 break;
ef416fc2 1771
b423cd4c 1772 case IPP_JOB_ABORTED :
1773 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1774 "job-state-reasons", NULL, "aborted-by-system");
1775 break;
ef416fc2 1776
b423cd4c 1777 case IPP_JOB_COMPLETED :
1778 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1779 "job-state-reasons", NULL, "job-completed-successfully");
1780 break;
ef416fc2 1781 }
ef416fc2 1782}
1783
1784
1785/*
b423cd4c 1786 * 'add_job_subscriptions()' - Add any subcriptions for a job.
ef416fc2 1787 */
1788
1789static void
b423cd4c 1790add_job_subscriptions(
1791 cupsd_client_t *con, /* I - Client connection */
1792 cupsd_job_t *job) /* I - Newly created job */
ef416fc2 1793{
b423cd4c 1794 int i; /* Looping var */
1795 ipp_attribute_t *prev, /* Previous attribute */
1796 *next, /* Next attribute */
1797 *attr; /* Current attribute */
1798 cupsd_subscription_t *sub; /* Subscription object */
1799 const char *recipient, /* notify-recipient-uri */
1800 *pullmethod; /* notify-pull-method */
1801 ipp_attribute_t *user_data; /* notify-user-data */
1802 int interval; /* notify-time-interval */
1803 unsigned mask; /* notify-events */
ef416fc2 1804
ef416fc2 1805
b423cd4c 1806 /*
1807 * Find the first subscription group attribute; return if we have
1808 * none...
1809 */
ef416fc2 1810
b423cd4c 1811 for (attr = job->attrs->attrs, prev = NULL;
1812 attr;
1813 prev = attr, attr = attr->next)
1814 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
1815 break;
ef416fc2 1816
b423cd4c 1817 if (!attr)
1818 return;
ef416fc2 1819
b423cd4c 1820 /*
1821 * Process the subscription attributes in the request...
1822 */
ef416fc2 1823
b423cd4c 1824 while (attr)
1825 {
1826 recipient = NULL;
1827 pullmethod = NULL;
1828 user_data = NULL;
1829 interval = 0;
1830 mask = CUPSD_EVENT_NONE;
ef416fc2 1831
b423cd4c 1832 while (attr && attr->group_tag != IPP_TAG_ZERO)
1833 {
1834 if (!strcmp(attr->name, "notify-recipient") &&
1835 attr->value_tag == IPP_TAG_URI)
1836 recipient = attr->values[0].string.text;
1837 else if (!strcmp(attr->name, "notify-pull-method") &&
1838 attr->value_tag == IPP_TAG_KEYWORD)
1839 pullmethod = attr->values[0].string.text;
1840 else if (!strcmp(attr->name, "notify-charset") &&
1841 attr->value_tag == IPP_TAG_CHARSET &&
1842 strcmp(attr->values[0].string.text, "us-ascii") &&
1843 strcmp(attr->values[0].string.text, "utf-8"))
1844 {
1845 send_ipp_status(con, IPP_CHARSET,
1846 _("Character set \"%s\" not supported!"),
1847 attr->values[0].string.text);
1848 return;
1849 }
1850 else if (!strcmp(attr->name, "notify-natural-language") &&
1851 (attr->value_tag != IPP_TAG_LANGUAGE ||
1852 strcmp(attr->values[0].string.text, DefaultLanguage)))
1853 {
1854 send_ipp_status(con, IPP_CHARSET,
1855 _("Language \"%s\" not supported!"),
1856 attr->values[0].string.text);
1857 return;
1858 }
1859 else if (!strcmp(attr->name, "notify-user-data") &&
1860 attr->value_tag == IPP_TAG_STRING)
1861 {
1862 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
1863 {
1864 send_ipp_status(con, IPP_REQUEST_VALUE,
1865 _("The notify-user-data value is too large "
1866 "(%d > 63 octets)!"),
1867 attr->values[0].unknown.length);
1868 return;
1869 }
ef416fc2 1870
b423cd4c 1871 user_data = attr;
1872 }
1873 else if (!strcmp(attr->name, "notify-events") &&
1874 attr->value_tag == IPP_TAG_KEYWORD)
1875 {
1876 for (i = 0; i < attr->num_values; i ++)
1877 mask |= cupsdEventValue(attr->values[i].string.text);
1878 }
1879 else if (!strcmp(attr->name, "notify-lease-duration"))
1880 {
1881 send_ipp_status(con, IPP_BAD_REQUEST,
1882 _("The notify-lease-duration attribute cannot be "
1883 "used with job subscriptions."));
1884 return;
1885 }
1886 else if (!strcmp(attr->name, "notify-time-interval") &&
1887 attr->value_tag == IPP_TAG_INTEGER)
1888 interval = attr->values[0].integer;
ef416fc2 1889
b423cd4c 1890 attr = attr->next;
1891 }
ef416fc2 1892
b423cd4c 1893 if (!recipient && !pullmethod)
1894 break;
ef416fc2 1895
b423cd4c 1896 if (mask == CUPSD_EVENT_NONE)
1897 mask = CUPSD_EVENT_JOB_COMPLETED;
ef416fc2 1898
b423cd4c 1899 sub = cupsdAddSubscription(mask, cupsdFindDest(job->dest), job, recipient,
1900 0);
ef416fc2 1901
b423cd4c 1902 sub->interval = interval;
ef416fc2 1903
b423cd4c 1904 cupsdSetString(&sub->owner, job->username);
ef416fc2 1905
b423cd4c 1906 if (user_data)
ef416fc2 1907 {
b423cd4c 1908 sub->user_data_len = user_data->values[0].unknown.length;
1909 memcpy(sub->user_data, user_data->values[0].unknown.data,
1910 sub->user_data_len);
ef416fc2 1911 }
1912
b423cd4c 1913 ippAddSeparator(con->response);
1914 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
1915 "notify-subscription-id", sub->id);
ef416fc2 1916
b423cd4c 1917 if (attr)
1918 attr = attr->next;
ef416fc2 1919 }
1920
b423cd4c 1921 cupsdSaveAllSubscriptions();
1922
ef416fc2 1923 /*
b423cd4c 1924 * Remove all of the subscription attributes from the job request...
ef416fc2 1925 */
1926
b423cd4c 1927 for (attr = job->attrs->attrs, prev = NULL; attr; attr = next)
ef416fc2 1928 {
b423cd4c 1929 next = attr->next;
ef416fc2 1930
b423cd4c 1931 if (attr->group_tag == IPP_TAG_SUBSCRIPTION ||
1932 attr->group_tag == IPP_TAG_ZERO)
1933 {
1934 /*
1935 * Free and remove this attribute...
1936 */
ef416fc2 1937
757d2cad 1938 _ippFreeAttr(attr);
ef416fc2 1939
b423cd4c 1940 if (prev)
1941 prev->next = next;
1942 else
1943 job->attrs->attrs = next;
1944 }
1945 else
1946 prev = attr;
ef416fc2 1947 }
1948
b423cd4c 1949 job->attrs->last = prev;
1950 job->attrs->current = prev;
1951}
ef416fc2 1952
ef416fc2 1953
b423cd4c 1954/*
1955 * 'add_job_uuid()' - Add job-uuid attribute to a job.
1956 *
1957 * See RFC 4122 for the definition of UUIDs and the format.
1958 */
1959
1960static void
1961add_job_uuid(cupsd_client_t *con, /* I - Client connection */
1962 cupsd_job_t *job) /* I - Job */
1963{
1964 char uuid[1024]; /* job-uuid string */
b423cd4c 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
d09495fa 1973 if (ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI))
b423cd4c 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 */
f7faf1f5 2770 const char *dest; /* Destination */
b423cd4c 2771 cups_ptype_t dtype; /* Destination type */
f7faf1f5 2772 char method[HTTP_MAX_URI], /* Method portion of URI */
b423cd4c 2773 userpass[HTTP_MAX_URI], /* Username portion of URI */
f7faf1f5 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
f7faf1f5 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 }
f7faf1f5 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\".",
d09495fa 2875 purge ? "purged" : "canceled", get_username(con));
ef416fc2 2876 }
b423cd4c 2877 else
ef416fc2 2878 {
2879 /*
b423cd4c 2880 * Check policy...
ef416fc2 2881 */
2882
f7faf1f5 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
f7faf1f5 2893 cupsdCancelJobs(dest, username, purge);
b423cd4c 2894
2895 cupsdLogMessage(CUPSD_LOG_INFO, "All jobs on \"%s\" were %s by \"%s\".",
d09495fa 2896 dest, purge ? "purged" : "canceled", 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 */
f7faf1f5 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 */
f7faf1f5 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
f7faf1f5 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 &&
f7faf1f5 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!"),
f7faf1f5 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
f7faf1f5 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 /*
d09495fa 3046 * See if the job is already completed, canceled, or aborted; if so,
b423cd4c 3047 * we can't cancel...
3048 */
3049
d09495fa 3050 if (job->state_value >= IPP_JOB_CANCELED)
b423cd4c 3051 {
e1d6a774 3052 switch (job->state_value)
3053 {
d09495fa 3054 case IPP_JOB_CANCELED :
e1d6a774 3055 send_ipp_status(con, IPP_NOT_POSSIBLE,
d09495fa 3056 _("Job #%d is already canceled - can\'t cancel."),
e1d6a774 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,
d09495fa 3081 "Job canceled by \"%s\".", username);
ef416fc2 3082
b423cd4c 3083 cupsdCancelJob(job, 0);
3084 cupsdCheckJobs();
ef416fc2 3085
d09495fa 3086 cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was canceled by \"%s\".", jobid,
b423cd4c 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,
8ca02f3c 4337 (printer->type & CUPS_PRINTER_CLASS) ?
4338 "/classes/%s" : "/printers/%s", printer->name);
b423cd4c 4339 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
4340 "printer-uri-supported", NULL, printer_uri);
4341 cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-uri-supported=\"%s\"",
4342 printer_uri);
4343 }
ef416fc2 4344
b423cd4c 4345 if (!ra || cupsArrayFind(ra, "queued-job-count"))
4346 add_queued_job_count(con, printer);
ef416fc2 4347
b423cd4c 4348 copy_attrs(con->response, printer->attrs, ra, IPP_TAG_ZERO, 0);
4349 copy_attrs(con->response, CommonData, ra, IPP_TAG_ZERO, IPP_TAG_COPY);
4350}
ef416fc2 4351
ef416fc2 4352
b423cd4c 4353/*
4354 * 'copy_subscription_attrs()' - Copy subscription attributes.
4355 */
ef416fc2 4356
b423cd4c 4357static void
4358copy_subscription_attrs(
4359 cupsd_client_t *con, /* I - Client connection */
4360 cupsd_subscription_t *sub, /* I - Subscription */
4361 cups_array_t *ra) /* I - Requested attributes array */
4362{
4363 ipp_attribute_t *attr; /* Current attribute */
4364 char printer_uri[HTTP_MAX_URI];
4365 /* Printer URI */
4366 int count; /* Number of events */
4367 unsigned mask; /* Current event mask */
4368 const char *name; /* Current event name */
ef416fc2 4369
ef416fc2 4370
b423cd4c 4371 /*
4372 * Copy the subscription attributes to the response using the
4373 * requested-attributes attribute that may be provided by the client.
4374 */
ef416fc2 4375
b423cd4c 4376 if (!ra || cupsArrayFind(ra, "notify-events"))
4377 {
4378 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
4379 {
4380 /*
4381 * Simple event list...
4382 */
ef416fc2 4383
b423cd4c 4384 ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
d09495fa 4385 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
b423cd4c 4386 "notify-events", NULL, name);
4387 }
4388 else
4389 {
4390 /*
4391 * Complex event list...
4392 */
ef416fc2 4393
b423cd4c 4394 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
4395 if (sub->mask & mask)
4396 count ++;
ef416fc2 4397
b423cd4c 4398 attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
d09495fa 4399 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
b423cd4c 4400 "notify-events", count, NULL, NULL);
ef416fc2 4401
b423cd4c 4402 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
4403 if (sub->mask & mask)
4404 {
4405 attr->values[count].string.text =
4406 (char *)cupsdEventName((cupsd_eventmask_t)mask);
ef416fc2 4407
b423cd4c 4408 count ++;
4409 }
ef416fc2 4410 }
b423cd4c 4411 }
ef416fc2 4412
b423cd4c 4413 if (sub->job && (!ra || cupsArrayFind(ra, "notify-job-id")))
4414 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
4415 "notify-job-id", sub->job->id);
ef416fc2 4416
b423cd4c 4417 if (!sub->job && (!ra || cupsArrayFind(ra, "notify-lease-duration")))
4418 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
4419 "notify-lease-duration", sub->lease);
ef416fc2 4420
b423cd4c 4421 if (sub->dest && (!ra || cupsArrayFind(ra, "notify-printer-uri")))
4422 {
4423 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
4424 "ipp", NULL, con->servername, con->serverport,
4425 "/printers/%s", sub->dest->name);
4426 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
4427 "notify-printer-uri", NULL, printer_uri);
ef416fc2 4428 }
ef416fc2 4429
b423cd4c 4430 if (sub->recipient && (!ra || cupsArrayFind(ra, "notify-recipient-uri")))
4431 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
4432 "notify-recipient-uri", NULL, sub->recipient);
4433 else if (!ra || cupsArrayFind(ra, "notify-pull-method"))
4434 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
4435 "notify-pull-method", NULL, "ippget");
ef416fc2 4436
b423cd4c 4437 if (!ra || cupsArrayFind(ra, "notify-subscriber-user-name"))
4438 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
4439 "notify-subscriber-user-name", NULL, sub->owner);
ef416fc2 4440
b423cd4c 4441 if (!ra || cupsArrayFind(ra, "notify-subscription-id"))
4442 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
4443 "notify-subscription-id", sub->id);
ef416fc2 4444
b423cd4c 4445 if (!ra || cupsArrayFind(ra, "notify-time-interval"))
4446 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
4447 "notify-time-interval", sub->interval);
ef416fc2 4448
b423cd4c 4449 if (sub->user_data_len > 0 && (!ra || cupsArrayFind(ra, "notify-user-data")))
4450 ippAddOctetString(con->response, IPP_TAG_SUBSCRIPTION, "notify-user-data",
4451 sub->user_data, sub->user_data_len);
4452}
ef416fc2 4453
ef416fc2 4454
b423cd4c 4455/*
4456 * 'create_job()' - Print a file to a printer or class.
4457 */
4458
4459static void
4460create_job(cupsd_client_t *con, /* I - Client connection */
4461 ipp_attribute_t *uri) /* I - Printer URI */
4462{
f7faf1f5 4463 cupsd_job_t *job; /* New job */
b423cd4c 4464
ef416fc2 4465
b423cd4c 4466 cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con,
4467 con->http.fd, uri->values[0].string.text);
ef416fc2 4468
4469 /*
b423cd4c 4470 * Create the job object...
ef416fc2 4471 */
4472
f7faf1f5 4473 if ((job = add_job(con, uri, NULL, NULL)) == NULL)
b423cd4c 4474 return;
ef416fc2 4475
4476 /*
4477 * Save and log the job...
4478 */
4479
4480 cupsdSaveJob(job);
4481
4482 cupsdLogMessage(CUPSD_LOG_INFO, "Job %d created on \"%s\" by \"%s\".",
4483 job->id, job->dest, job->username);
ef416fc2 4484}
4485
4486
4487/*
4488 * 'create_requested_array()' - Create an array for the requested-attributes.
4489 */
4490
4491static cups_array_t * /* O - Array of attributes or NULL */
4492create_requested_array(ipp_t *request) /* I - IPP request */
4493{
4494 int i; /* Looping var */
4495 ipp_attribute_t *requested; /* requested-attributes attribute */
4496 cups_array_t *ra; /* Requested attributes array */
4497 char *value; /* Current value */
4498
4499
4500 /*
4501 * Get the requested-attributes attribute, and return NULL if we don't
4502 * have one...
4503 */
4504
4505 if ((requested = ippFindAttribute(request, "requested-attributes",
4506 IPP_TAG_KEYWORD)) == NULL)
4507 return (NULL);
4508
4509 /*
4510 * If the attribute contains a single "all" keyword, return NULL...
4511 */
4512
4513 if (requested->num_values == 1 &&
4514 !strcmp(requested->values[0].string.text, "all"))
4515 return (NULL);
4516
4517 /*
4518 * Create an array using "strcmp" as the comparison function...
4519 */
4520
4521 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4522
4523 for (i = 0; i < requested->num_values; i ++)
4524 {
4525 value = requested->values[i].string.text;
4526
4527 if (!strcmp(value, "job-template"))
4528 {
4529 cupsArrayAdd(ra, "copies");
4530 cupsArrayAdd(ra, "copies-default");
4531 cupsArrayAdd(ra, "copies-supported");
4532 cupsArrayAdd(ra, "finishings");
4533 cupsArrayAdd(ra, "finishings-default");
4534 cupsArrayAdd(ra, "finishings-supported");
4535 cupsArrayAdd(ra, "job-hold-until");
4536 cupsArrayAdd(ra, "job-hold-until-default");
4537 cupsArrayAdd(ra, "job-hold-until-supported");
4538 cupsArrayAdd(ra, "job-priority");
4539 cupsArrayAdd(ra, "job-priority-default");
4540 cupsArrayAdd(ra, "job-priority-supported");
4541 cupsArrayAdd(ra, "job-sheets");
4542 cupsArrayAdd(ra, "job-sheets-default");
4543 cupsArrayAdd(ra, "job-sheets-supported");
4544 cupsArrayAdd(ra, "media");
4545 cupsArrayAdd(ra, "media-default");
4546 cupsArrayAdd(ra, "media-supported");
4547 cupsArrayAdd(ra, "multiple-document-handling");
4548 cupsArrayAdd(ra, "multiple-document-handling-default");
4549 cupsArrayAdd(ra, "multiple-document-handling-supported");
4550 cupsArrayAdd(ra, "number-up");
4551 cupsArrayAdd(ra, "number-up-default");
4552 cupsArrayAdd(ra, "number-up-supported");
4553 cupsArrayAdd(ra, "orientation-requested");
4554 cupsArrayAdd(ra, "orientation-requested-default");
4555 cupsArrayAdd(ra, "orientation-requested-supported");
4556 cupsArrayAdd(ra, "page-ranges");
4557 cupsArrayAdd(ra, "page-ranges-supported");
4558 cupsArrayAdd(ra, "printer-resolution");
4559 cupsArrayAdd(ra, "printer-resolution-default");
4560 cupsArrayAdd(ra, "printer-resolution-supported");
4561 cupsArrayAdd(ra, "print-quality");
4562 cupsArrayAdd(ra, "print-quality-default");
4563 cupsArrayAdd(ra, "print-quality-supported");
4564 cupsArrayAdd(ra, "sides");
4565 cupsArrayAdd(ra, "sides-default");
4566 cupsArrayAdd(ra, "sides-supported");
4567 }
4568 else if (!strcmp(value, "job-description"))
4569 {
4570 cupsArrayAdd(ra, "date-time-at-completed");
4571 cupsArrayAdd(ra, "date-time-at-creation");
4572 cupsArrayAdd(ra, "date-time-at-processing");
4573 cupsArrayAdd(ra, "job-detailed-status-message");
4574 cupsArrayAdd(ra, "job-document-access-errors");
4575 cupsArrayAdd(ra, "job-id");
4576 cupsArrayAdd(ra, "job-impressions");
4577 cupsArrayAdd(ra, "job-impressions-completed");
4578 cupsArrayAdd(ra, "job-k-octets");
4579 cupsArrayAdd(ra, "job-k-octets-processed");
4580 cupsArrayAdd(ra, "job-media-sheets");
4581 cupsArrayAdd(ra, "job-media-sheets-completed");
4582 cupsArrayAdd(ra, "job-message-from-operator");
4583 cupsArrayAdd(ra, "job-more-info");
4584 cupsArrayAdd(ra, "job-name");
4585 cupsArrayAdd(ra, "job-originating-user-name");
4586 cupsArrayAdd(ra, "job-printer-up-time");
4587 cupsArrayAdd(ra, "job-printer-uri");
4588 cupsArrayAdd(ra, "job-state");
4589 cupsArrayAdd(ra, "job-state-message");
4590 cupsArrayAdd(ra, "job-state-reasons");
4591 cupsArrayAdd(ra, "job-uri");
4592 cupsArrayAdd(ra, "number-of-documents");
4593 cupsArrayAdd(ra, "number-of-intervening-jobs");
4594 cupsArrayAdd(ra, "output-device-assigned");
4595 cupsArrayAdd(ra, "time-at-completed");
4596 cupsArrayAdd(ra, "time-at-creation");
4597 cupsArrayAdd(ra, "time-at-processing");
4598 }
4599 else if (!strcmp(value, "printer-description"))
4600 {
4601 cupsArrayAdd(ra, "charset-configured");
4602 cupsArrayAdd(ra, "charset-supported");
4603 cupsArrayAdd(ra, "color-supported");
4604 cupsArrayAdd(ra, "compression-supported");
4605 cupsArrayAdd(ra, "document-format-default");
4606 cupsArrayAdd(ra, "document-format-supported");
4607 cupsArrayAdd(ra, "generated-natural-language-supported");
4608 cupsArrayAdd(ra, "ipp-versions-supported");
4609 cupsArrayAdd(ra, "job-impressions-supported");
4610 cupsArrayAdd(ra, "job-k-octets-supported");
4611 cupsArrayAdd(ra, "job-media-sheets-supported");
4612 cupsArrayAdd(ra, "multiple-document-jobs-supported");
4613 cupsArrayAdd(ra, "multiple-operation-time-out");
4614 cupsArrayAdd(ra, "natural-language-configured");
4615 cupsArrayAdd(ra, "notify-attributes-supported");
4616 cupsArrayAdd(ra, "notify-lease-duration-default");
4617 cupsArrayAdd(ra, "notify-lease-duration-supported");
4618 cupsArrayAdd(ra, "notify-max-events-supported");
f301802f 4619 cupsArrayAdd(ra, "notify-events-default");
4620 cupsArrayAdd(ra, "notify-events-supported");
ef416fc2 4621 cupsArrayAdd(ra, "notify-pull-method-supported");
4622 cupsArrayAdd(ra, "notify-schemes-supported");
4623 cupsArrayAdd(ra, "operations-supported");
4624 cupsArrayAdd(ra, "pages-per-minute");
4625 cupsArrayAdd(ra, "pages-per-minute-color");
4626 cupsArrayAdd(ra, "pdl-override-supported");
4627 cupsArrayAdd(ra, "printer-current-time");
4628 cupsArrayAdd(ra, "printer-driver-installer");
4629 cupsArrayAdd(ra, "printer-info");
4630 cupsArrayAdd(ra, "printer-is-accepting-jobs");
4631 cupsArrayAdd(ra, "printer-location");
4632 cupsArrayAdd(ra, "printer-make-and-model");
4633 cupsArrayAdd(ra, "printer-message-from-operator");
4634 cupsArrayAdd(ra, "printer-more-info");
4635 cupsArrayAdd(ra, "printer-more-info-manufacturer");
4636 cupsArrayAdd(ra, "printer-name");
4637 cupsArrayAdd(ra, "printer-state");
4638 cupsArrayAdd(ra, "printer-state-message");
4639 cupsArrayAdd(ra, "printer-state-reasons");
4640 cupsArrayAdd(ra, "printer-up-time");
4641 cupsArrayAdd(ra, "printer-uri-supported");
4642 cupsArrayAdd(ra, "queued-job-count");
4643 cupsArrayAdd(ra, "reference-uri-schemes-supported");
4644 cupsArrayAdd(ra, "uri-authentication-supported");
4645 cupsArrayAdd(ra, "uri-security-supported");
4646 }
4647 else if (!strcmp(value, "subscription-template"))
4648 {
4649 cupsArrayAdd(ra, "notify-attributes");
4650 cupsArrayAdd(ra, "notify-charset");
4651 cupsArrayAdd(ra, "notify-events");
4652 cupsArrayAdd(ra, "notify-lease-duration");
4653 cupsArrayAdd(ra, "notify-natural-language");
4654 cupsArrayAdd(ra, "notify-pull-method");
4655 cupsArrayAdd(ra, "notify-recipient-uri");
4656 cupsArrayAdd(ra, "notify-time-interval");
4657 cupsArrayAdd(ra, "notify-user-data");
4658 }
4659 else
4660 cupsArrayAdd(ra, value);
4661 }
4662
4663 return (ra);
4664}
4665
4666
4667/*
4668 * 'create_subscription()' - Create a notification subscription.
4669 */
4670
4671static void
4672create_subscription(
4673 cupsd_client_t *con, /* I - Client connection */
4674 ipp_attribute_t *uri) /* I - Printer URI */
4675{
4676 http_status_t status; /* Policy status */
4677 int i; /* Looping var */
4678 ipp_attribute_t *attr; /* Current attribute */
ef416fc2 4679 cups_ptype_t dtype; /* Destination type (printer or class) */
ed486911 4680 char scheme[HTTP_MAX_URI],
4681 /* Scheme portion of URI */
ef416fc2 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
ed486911 4721 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
4722 sizeof(scheme), userpass, sizeof(userpass), host,
a4d04587 4723 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 4724
4725 if (!strcmp(resource, "/"))
4726 {
ef416fc2 4727 dtype = (cups_ptype_t)0;
4728 printer = NULL;
4729 }
4730 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
4731 {
ef416fc2 4732 dtype = (cups_ptype_t)0;
4733 printer = NULL;
4734 }
4735 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
4736 {
ef416fc2 4737 dtype = CUPS_PRINTER_CLASS;
4738 printer = NULL;
4739 }
d09495fa 4740 else if (!cupsdValidateDest(host, resource, &dtype, &printer))
ef416fc2 4741 {
4742 /*
4743 * Bad URI...
4744 */
4745
4746 send_ipp_status(con, IPP_NOT_FOUND,
4747 _("The printer or class was not found."));
4748 return;
4749 }
4750
4751 /*
4752 * Check policy...
4753 */
4754
4755 if (printer)
4756 {
f7faf1f5 4757 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
ef416fc2 4758 {
4759 send_http_error(con, status);
4760 return;
4761 }
4762 }
4763 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
4764 {
4765 send_http_error(con, status);
4766 return;
4767 }
4768
4769 /*
4770 * Get the user that is requesting the subscription...
4771 */
4772
e00b005a 4773 username = get_username(con);
ef416fc2 4774
4775 /*
4776 * Find the first subscription group attribute; return if we have
4777 * none...
4778 */
4779
4780 for (attr = con->request->attrs; attr; attr = attr->next)
4781 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
4782 break;
4783
4784 if (!attr)
4785 {
4786 send_ipp_status(con, IPP_BAD_REQUEST,
4787 _("No subscription attributes in request!"));
4788 return;
4789 }
4790
4791 /*
4792 * Process the subscription attributes in the request...
4793 */
4794
fa73b229 4795 con->response->request.status.status_code = IPP_BAD_REQUEST;
4796
ef416fc2 4797 while (attr)
4798 {
4799 recipient = NULL;
4800 pullmethod = NULL;
4801 user_data = NULL;
4802 interval = 0;
4803 lease = DefaultLeaseDuration;
4804 jobid = 0;
4805 mask = CUPSD_EVENT_NONE;
4806
4807 while (attr && attr->group_tag != IPP_TAG_ZERO)
4808 {
4809 if (!strcmp(attr->name, "notify-recipient") &&
4810 attr->value_tag == IPP_TAG_URI)
ed486911 4811 {
4812 /*
4813 * Validate the recipient scheme against the ServerBin/notifier
4814 * directory...
4815 */
4816
4817 char notifier[1024]; /* Notifier filename */
4818
4819
ef416fc2 4820 recipient = attr->values[0].string.text;
ed486911 4821
8ca02f3c 4822 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
4823 scheme, sizeof(scheme), userpass, sizeof(userpass),
4824 host, sizeof(host), &port,
4825 resource, sizeof(resource)) < HTTP_URI_OK)
ed486911 4826 {
4827 send_ipp_status(con, IPP_NOT_POSSIBLE,
4828 _("Bad notify-recipient URI \"%s\"!"), recipient);
4829 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
4830 "notify-status-code", IPP_URI_SCHEME);
4831 return;
4832 }
4833
4834 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
4835 scheme);
4836 if (access(notifier, X_OK))
4837 {
4838 send_ipp_status(con, IPP_NOT_POSSIBLE,
4839 _("notify-recipient URI \"%s\" uses unknown scheme!"),
4840 recipient);
4841 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
4842 "notify-status-code", IPP_URI_SCHEME);
4843 return;
4844 }
4845 }
ef416fc2 4846 else if (!strcmp(attr->name, "notify-pull-method") &&
4847 attr->value_tag == IPP_TAG_KEYWORD)
ed486911 4848 {
ef416fc2 4849 pullmethod = attr->values[0].string.text;
ed486911 4850
4851 if (strcmp(pullmethod, "ippget"))
4852 {
4853 send_ipp_status(con, IPP_NOT_POSSIBLE,
4854 _("Bad notify-pull-method \"%s\"!"), pullmethod);
4855 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
4856 "notify-status-code", IPP_ATTRIBUTES);
4857 return;
4858 }
4859 }
ef416fc2 4860 else if (!strcmp(attr->name, "notify-charset") &&
4861 attr->value_tag == IPP_TAG_CHARSET &&
4862 strcmp(attr->values[0].string.text, "us-ascii") &&
4863 strcmp(attr->values[0].string.text, "utf-8"))
4864 {
4865 send_ipp_status(con, IPP_CHARSET,
4866 _("Character set \"%s\" not supported!"),
4867 attr->values[0].string.text);
4868 return;
4869 }
4870 else if (!strcmp(attr->name, "notify-natural-language") &&
4871 (attr->value_tag != IPP_TAG_LANGUAGE ||
4872 strcmp(attr->values[0].string.text, DefaultLanguage)))
4873 {
4874 send_ipp_status(con, IPP_CHARSET,
4875 _("Language \"%s\" not supported!"),
4876 attr->values[0].string.text);
4877 return;
4878 }
4879 else if (!strcmp(attr->name, "notify-user-data") &&
4880 attr->value_tag == IPP_TAG_STRING)
4881 {
4882 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
4883 {
4884 send_ipp_status(con, IPP_REQUEST_VALUE,
4885 _("The notify-user-data value is too large "
4886 "(%d > 63 octets)!"),
4887 attr->values[0].unknown.length);
4888 return;
4889 }
4890
4891 user_data = attr;
4892 }
4893 else if (!strcmp(attr->name, "notify-events") &&
4894 attr->value_tag == IPP_TAG_KEYWORD)
4895 {
4896 for (i = 0; i < attr->num_values; i ++)
4897 mask |= cupsdEventValue(attr->values[i].string.text);
4898 }
4899 else if (!strcmp(attr->name, "notify-lease-duration") &&
4900 attr->value_tag == IPP_TAG_INTEGER)
4901 lease = attr->values[0].integer;
4902 else if (!strcmp(attr->name, "notify-time-interval") &&
4903 attr->value_tag == IPP_TAG_INTEGER)
4904 interval = attr->values[0].integer;
4905 else if (!strcmp(attr->name, "notify-job-id") &&
4906 attr->value_tag == IPP_TAG_INTEGER)
4907 jobid = attr->values[0].integer;
4908
4909 attr = attr->next;
4910 }
4911
bd7854cb 4912 if (recipient)
4913 cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
4914 if (pullmethod)
4915 cupsdLogMessage(CUPSD_LOG_DEBUG, "pullmethod=\"%s\"", pullmethod);
4916 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-lease-duration=%d", lease);
4917 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-time-interval=%d", interval);
fa73b229 4918
ef416fc2 4919 if (!recipient && !pullmethod)
4920 break;
4921
4922 if (mask == CUPSD_EVENT_NONE)
4923 {
4924 if (jobid)
4925 mask = CUPSD_EVENT_JOB_COMPLETED;
4926 else if (printer)
4927 mask = CUPSD_EVENT_PRINTER_STATE_CHANGED;
4928 else
4929 {
4930 send_ipp_status(con, IPP_BAD_REQUEST,
4931 _("notify-events not specified!"));
4932 return;
4933 }
4934 }
4935
bd7854cb 4936 if (MaxLeaseDuration && (lease == 0 || lease > MaxLeaseDuration))
ef416fc2 4937 {
4938 cupsdLogMessage(CUPSD_LOG_INFO,
4939 "create_subscription: Limiting notify-lease-duration to "
4940 "%d seconds.",
4941 MaxLeaseDuration);
4942 lease = MaxLeaseDuration;
4943 }
4944
4945 if (jobid)
4946 {
4947 if ((job = cupsdFindJob(jobid)) == NULL)
4948 {
4949 send_ipp_status(con, IPP_NOT_FOUND, _("Job %d not found!"), jobid);
4950 return;
4951 }
4952 }
4953 else
4954 job = NULL;
4955
4956 sub = cupsdAddSubscription(mask, printer, job, recipient, 0);
4957
fa73b229 4958 if (job)
4959 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d",
4960 sub->id, job->id);
4961 else if (printer)
4962 cupsdLogMessage(CUPSD_LOG_DEBUG,
4963 "Added subscription %d for printer \"%s\"",
4964 sub->id, printer->name);
4965 else
4966 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for server",
4967 sub->id);
4968
ef416fc2 4969 sub->interval = interval;
4970 sub->lease = lease;
bd7854cb 4971 sub->expire = lease ? time(NULL) + lease : 0;
ef416fc2 4972
4973 cupsdSetString(&sub->owner, username);
4974
4975 if (user_data)
4976 {
4977 sub->user_data_len = user_data->values[0].unknown.length;
4978 memcpy(sub->user_data, user_data->values[0].unknown.data,
4979 sub->user_data_len);
4980 }
4981
4982 ippAddSeparator(con->response);
4983 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
4984 "notify-subscription-id", sub->id);
4985
fa73b229 4986 con->response->request.status.status_code = IPP_OK;
4987
ef416fc2 4988 if (attr)
4989 attr = attr->next;
4990 }
4991
4992 cupsdSaveAllSubscriptions();
4993
ef416fc2 4994}
4995
4996
4997/*
4998 * 'delete_printer()' - Remove a printer or class from the system.
4999 */
5000
5001static void
5002delete_printer(cupsd_client_t *con, /* I - Client connection */
5003 ipp_attribute_t *uri) /* I - URI of printer or class */
5004{
5005 http_status_t status; /* Policy status */
f7faf1f5 5006 const char *dest; /* Destination */
ef416fc2 5007 cups_ptype_t dtype; /* Destination type (printer or class) */
f7faf1f5 5008 char method[HTTP_MAX_URI], /* Method portion of URI */
5009 username[HTTP_MAX_URI], /* Username portion of URI */
5010 host[HTTP_MAX_URI], /* Host portion of URI */
5011 resource[HTTP_MAX_URI]; /* Resource portion of URI */
5012 int port; /* Port portion of URI */
ef416fc2 5013 cupsd_printer_t *printer; /* Printer/class */
5014 char filename[1024]; /* Script/PPD filename */
5015
5016
5017 cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", con,
5018 con->http.fd, uri->values[0].string.text);
5019
5020 /*
5021 * Do we have a valid URI?
5022 */
5023
f7faf1f5 5024 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
5025 sizeof(method), username, sizeof(username), host,
5026 sizeof(host), &port, resource, sizeof(resource));
5027
5028 if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
ef416fc2 5029 {
5030 /*
5031 * Bad URI...
5032 */
5033
5034 send_ipp_status(con, IPP_NOT_FOUND,
5035 _("The printer or class was not found."));
5036 return;
5037 }
5038
5039 /*
5040 * Check policy...
5041 */
5042
5043 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5044 {
5045 send_http_error(con, status);
5046 return;
5047 }
5048
5049 /*
5050 * Remove old jobs...
5051 */
5052
f7faf1f5 5053 cupsdCancelJobs(dest, NULL, 1);
ef416fc2 5054
5055 /*
5056 * Remove old subscriptions and send a "deleted printer" event...
5057 */
5058
5059 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
5060 "%s \"%s\" deleted by \"%s\".",
5061 (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
f7faf1f5 5062 dest, get_username(con));
ef416fc2 5063
5064 cupsdExpireSubscriptions(printer, NULL);
5065
5066 /*
5067 * Remove any old PPD or script files...
5068 */
5069
f7faf1f5 5070 snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot, dest);
ef416fc2 5071 unlink(filename);
5072
f7faf1f5 5073 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest);
ef416fc2 5074 unlink(filename);
5075
5076 if (dtype & CUPS_PRINTER_CLASS)
5077 {
f7faf1f5 5078 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".", dest,
5079 get_username(con));
ef416fc2 5080
5081 cupsdDeletePrinter(printer, 0);
5082 cupsdSaveAllClasses();
5083 }
5084 else
5085 {
f7faf1f5 5086 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".", dest,
5087 get_username(con));
ef416fc2 5088
5089 cupsdDeletePrinter(printer, 0);
5090 cupsdSaveAllPrinters();
5091 }
5092
5093 cupsdWritePrintcap();
5094
5095 /*
5096 * Return with no errors...
5097 */
5098
5099 con->response->request.status.status_code = IPP_OK;
5100}
5101
5102
5103/*
5104 * 'get_default()' - Get the default destination.
5105 */
5106
5107static void
5108get_default(cupsd_client_t *con) /* I - Client connection */
5109{
fa73b229 5110 http_status_t status; /* Policy status */
5111 cups_array_t *ra; /* Requested attributes array */
ef416fc2 5112
5113
5114 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->http.fd);
5115
5116 /*
5117 * Check policy...
5118 */
5119
5120 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5121 {
5122 send_http_error(con, status);
5123 return;
5124 }
5125
fa73b229 5126 if (DefaultPrinter)
ef416fc2 5127 {
fa73b229 5128 ra = create_requested_array(con->request);
ef416fc2 5129
fa73b229 5130 copy_printer_attrs(con, DefaultPrinter, ra);
ef416fc2 5131
fa73b229 5132 cupsArrayDelete(ra);
ef416fc2 5133
fa73b229 5134 con->response->request.status.status_code = IPP_OK;
ef416fc2 5135 }
5136 else
5137 send_ipp_status(con, IPP_NOT_FOUND, _("No default printer"));
5138}
5139
5140
5141/*
5142 * 'get_devices()' - Get the list of available devices on the local system.
5143 */
5144
5145static void
5146get_devices(cupsd_client_t *con) /* I - Client connection */
5147{
5148 http_status_t status; /* Policy status */
ef416fc2 5149 ipp_attribute_t *limit, /* Limit attribute */
5150 *requested; /* requested-attributes attribute */
5151 char command[1024], /* cups-deviced command */
5152 options[1024], /* Options to pass to command */
89d46774 5153 requested_str[256];
5154 /* String for requested attributes */
ef416fc2 5155
5156
5157 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->http.fd);
5158
5159 /*
5160 * Check policy...
5161 */
5162
5163 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5164 {
5165 send_http_error(con, status);
5166 return;
5167 }
5168
5169 /*
5170 * Run cups-deviced command with the given options...
5171 */
5172
5173 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
5174 requested = ippFindAttribute(con->request, "requested-attributes",
5175 IPP_TAG_KEYWORD);
5176
5177 if (requested)
89d46774 5178 url_encode_attr(requested, requested_str, sizeof(requested_str));
ef416fc2 5179 else
89d46774 5180 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
ef416fc2 5181
5182 snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
5183 snprintf(options, sizeof(options),
89d46774 5184 "%d+%d+%d+%s",
ef416fc2 5185 con->request->request.op.request_id,
e1d6a774 5186 limit ? limit->values[0].integer : 0, (int)User,
89d46774 5187 requested_str);
ef416fc2 5188
5189 if (cupsdSendCommand(con, command, options, 1))
5190 {
5191 /*
5192 * Command started successfully, don't send an IPP response here...
5193 */
5194
5195 ippDelete(con->response);
5196 con->response = NULL;
5197 }
5198 else
5199 {
5200 /*
5201 * Command failed, return "internal error" so the user knows something
5202 * went wrong...
5203 */
5204
5205 send_ipp_status(con, IPP_INTERNAL_ERROR,
5206 _("cups-deviced failed to execute."));
5207 }
5208}
5209
5210
5211/*
fa73b229 5212 * 'get_job_attrs()' - Get job attributes.
ef416fc2 5213 */
5214
5215static void
fa73b229 5216get_job_attrs(cupsd_client_t *con, /* I - Client connection */
5217 ipp_attribute_t *uri) /* I - Job URI */
ef416fc2 5218{
5219 http_status_t status; /* Policy status */
fa73b229 5220 ipp_attribute_t *attr; /* Current attribute */
5221 int jobid; /* Job ID */
5222 cupsd_job_t *job; /* Current job */
ef416fc2 5223 char method[HTTP_MAX_URI], /* Method portion of URI */
5224 username[HTTP_MAX_URI], /* Username portion of URI */
5225 host[HTTP_MAX_URI], /* Host portion of URI */
5226 resource[HTTP_MAX_URI]; /* Resource portion of URI */
5227 int port; /* Port portion of URI */
fa73b229 5228 cups_array_t *ra; /* Requested attributes array */
ef416fc2 5229
5230
fa73b229 5231 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", con,
5232 con->http.fd, uri->values[0].string.text);
ef416fc2 5233
5234 /*
fa73b229 5235 * See if we have a job URI or a printer URI...
5236 */
5237
5238 if (!strcmp(uri->name, "printer-uri"))
5239 {
5240 /*
5241 * Got a printer URI; see if we also have a job-id attribute...
5242 */
5243
5244 if ((attr = ippFindAttribute(con->request, "job-id",
5245 IPP_TAG_INTEGER)) == NULL)
5246 {
5247 send_ipp_status(con, IPP_BAD_REQUEST,
5248 _("Got a printer-uri attribute but no job-id!"));
5249 return;
5250 }
5251
5252 jobid = attr->values[0].integer;
5253 }
5254 else
5255 {
5256 /*
5257 * Got a job URI; parse it to get the job ID...
5258 */
5259
a4d04587 5260 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
5261 sizeof(method), username, sizeof(username), host,
5262 sizeof(host), &port, resource, sizeof(resource));
fa73b229 5263
5264 if (strncmp(resource, "/jobs/", 6))
5265 {
5266 /*
5267 * Not a valid URI!
5268 */
5269
5270 send_ipp_status(con, IPP_BAD_REQUEST,
5271 _("Bad job-uri attribute \"%s\"!"),
5272 uri->values[0].string.text);
5273 return;
5274 }
5275
5276 jobid = atoi(resource + 6);
5277 }
5278
5279 /*
5280 * See if the job exists...
5281 */
5282
5283 if ((job = cupsdFindJob(jobid)) == NULL)
5284 {
5285 /*
5286 * Nope - return a "not found" error...
5287 */
5288
5289 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
5290 return;
5291 }
5292
5293 /*
5294 * Check policy...
5295 */
5296
5297 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5298 {
5299 send_http_error(con, status);
5300 return;
5301 }
5302
5303 /*
5304 * Copy attributes...
5305 */
5306
bd7854cb 5307 cupsdLoadJob(job);
5308
fa73b229 5309 ra = create_requested_array(con->request);
5310 copy_job_attrs(con, job, ra);
5311 cupsArrayDelete(ra);
5312
5313 con->response->request.status.status_code = IPP_OK;
5314}
5315
5316
5317/*
5318 * 'get_jobs()' - Get a list of jobs for the specified printer.
5319 */
5320
5321static void
5322get_jobs(cupsd_client_t *con, /* I - Client connection */
5323 ipp_attribute_t *uri) /* I - Printer URI */
5324{
5325 http_status_t status; /* Policy status */
5326 ipp_attribute_t *attr; /* Current attribute */
5327 const char *dest; /* Destination */
5328 cups_ptype_t dtype; /* Destination type (printer or class) */
5329 cups_ptype_t dmask; /* Destination type mask */
f7faf1f5 5330 char method[HTTP_MAX_URI], /* Method portion of URI */
fa73b229 5331 username[HTTP_MAX_URI], /* Username portion of URI */
5332 host[HTTP_MAX_URI], /* Host portion of URI */
5333 resource[HTTP_MAX_URI]; /* Resource portion of URI */
5334 int port; /* Port portion of URI */
5335 int completed; /* Completed jobs? */
5336 int first_job_id; /* First job ID */
5337 int limit; /* Maximum number of jobs to return */
5338 int count; /* Number of jobs that match */
5339 cupsd_job_t *job; /* Current job pointer */
5340 cupsd_printer_t *printer; /* Printer */
5341 cups_array_t *list; /* Which job list... */
5342 cups_array_t *ra; /* Requested attributes array */
5343
5344
5345 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", con, con->http.fd,
5346 uri->values[0].string.text);
5347
5348 /*
5349 * Is the destination valid?
ef416fc2 5350 */
5351
f7faf1f5 5352 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
5353 sizeof(method), username, sizeof(username), host,
a4d04587 5354 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 5355
fa73b229 5356 if (!strcmp(resource, "/") ||
5357 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6))
ef416fc2 5358 {
5359 dest = NULL;
5360 dtype = (cups_ptype_t)0;
5361 dmask = (cups_ptype_t)0;
5362 printer = NULL;
5363 }
fa73b229 5364 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
ef416fc2 5365 {
5366 dest = NULL;
5367 dtype = (cups_ptype_t)0;
5368 dmask = CUPS_PRINTER_CLASS;
5369 printer = NULL;
5370 }
fa73b229 5371 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
ef416fc2 5372 {
5373 dest = NULL;
5374 dtype = CUPS_PRINTER_CLASS;
5375 dmask = CUPS_PRINTER_CLASS;
5376 printer = NULL;
5377 }
f7faf1f5 5378 else if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
ef416fc2 5379 {
5380 /*
5381 * Bad URI...
5382 */
5383
5384 send_ipp_status(con, IPP_NOT_FOUND,
5385 _("The printer or class was not found."));
5386 return;
5387 }
5388 else
5389 dmask = CUPS_PRINTER_CLASS;
5390
5391 /*
5392 * Check policy...
5393 */
5394
5395 if (printer)
5396 {
f7faf1f5 5397 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
ef416fc2 5398 {
5399 send_http_error(con, status);
5400 return;
5401 }
5402 }
5403 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5404 {
5405 send_http_error(con, status);
5406 return;
5407 }
5408
5409 /*
5410 * See if the "which-jobs" attribute have been specified...
5411 */
5412
fa73b229 5413 if ((attr = ippFindAttribute(con->request, "which-jobs",
5414 IPP_TAG_KEYWORD)) != NULL &&
ef416fc2 5415 !strcmp(attr->values[0].string.text, "completed"))
5416 {
5417 completed = 1;
5418 list = Jobs;
5419 }
5420 else if (attr && !strcmp(attr->values[0].string.text, "all"))
5421 {
5422 completed = 0;
5423 list = Jobs;
5424 }
5425 else
5426 {
5427 completed = 0;
5428 list = ActiveJobs;
5429 }
5430
5431 /*
5432 * See if they want to limit the number of jobs reported...
5433 */
5434
fa73b229 5435 if ((attr = ippFindAttribute(con->request, "limit",
5436 IPP_TAG_INTEGER)) != NULL)
ef416fc2 5437 limit = attr->values[0].integer;
5438 else
5439 limit = 1000000;
5440
5441 if ((attr = ippFindAttribute(con->request, "first-job-id",
5442 IPP_TAG_INTEGER)) != NULL)
5443 first_job_id = attr->values[0].integer;
5444 else
5445 first_job_id = 1;
5446
5447 /*
5448 * See if we only want to see jobs for a specific user...
5449 */
5450
fa73b229 5451 if ((attr = ippFindAttribute(con->request, "my-jobs",
5452 IPP_TAG_BOOLEAN)) != NULL &&
ef416fc2 5453 attr->values[0].boolean)
e00b005a 5454 strlcpy(username, get_username(con), sizeof(username));
ef416fc2 5455 else
5456 username[0] = '\0';
5457
fa73b229 5458 ra = create_requested_array(con->request);
ef416fc2 5459
5460 /*
5461 * OK, build a list of jobs for this printer...
5462 */
5463
5464 for (count = 0, job = (cupsd_job_t *)cupsArrayFirst(list);
5465 count < limit && job;
5466 job = (cupsd_job_t *)cupsArrayNext(list))
5467 {
5468 /*
5469 * Filter out jobs that don't match...
5470 */
5471
5472 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: job->id = %d", job->id);
5473
fa73b229 5474 if ((dest && strcmp(job->dest, dest)) &&
5475 (!job->printer || !dest || strcmp(job->printer->name, dest)))
ef416fc2 5476 continue;
5477 if ((job->dtype & dmask) != dtype &&
fa73b229 5478 (!job->printer || (job->printer->type & dmask) != dtype))
ef416fc2 5479 continue;
fa73b229 5480 if (username[0] && strcasecmp(username, job->username))
ef416fc2 5481 continue;
5482
bd7854cb 5483 if (completed && job->state_value <= IPP_JOB_STOPPED)
ef416fc2 5484 continue;
5485
5486 if (job->id < first_job_id)
5487 continue;
5488
bd7854cb 5489 cupsdLoadJob(job);
5490
5491 if (!job->attrs)
5492 continue;
5493
fa73b229 5494 if (count > 0)
5495 ippAddSeparator(con->response);
5496
ef416fc2 5497 count ++;
5498
5499 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count = %d", count);
5500
fa73b229 5501 copy_job_attrs(con, job, ra);
ef416fc2 5502 }
5503
fa73b229 5504 cupsArrayDelete(ra);
ef416fc2 5505
fa73b229 5506 con->response->request.status.status_code = IPP_OK;
ef416fc2 5507}
5508
5509
5510/*
5511 * 'get_notifications()' - Get events for a subscription.
5512 */
5513
5514static void
bd7854cb 5515get_notifications(cupsd_client_t *con) /* I - Client connection */
ef416fc2 5516{
bd7854cb 5517 int i, j; /* Looping vars */
5518 http_status_t status; /* Policy status */
5519 cupsd_subscription_t *sub; /* Subscription */
5520 ipp_attribute_t *ids, /* notify-subscription-ids */
5521 *sequences; /* notify-sequence-numbers */
5522 int min_seq; /* Minimum sequence number */
5523 int interval; /* Poll interval */
5524
5525
5526 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_subscription_attrs(con=%p[%d])",
5527 con, con->http.fd);
5528
5529 /*
5530 * Get subscription attributes...
5531 */
5532
5533 ids = ippFindAttribute(con->request, "notify-subscription-ids",
5534 IPP_TAG_INTEGER);
5535 sequences = ippFindAttribute(con->request, "notify-sequence-numbers",
5536 IPP_TAG_INTEGER);
5537
5538 if (!ids)
5539 {
5540 send_ipp_status(con, IPP_BAD_REQUEST,
5541 _("Missing notify-subscription-ids attribute!"));
5542 return;
5543 }
5544
5545 /*
5546 * Are the subscription IDs valid?
5547 */
5548
5549 for (i = 0, interval = 60; i < ids->num_values; i ++)
5550 {
5551 if ((sub = cupsdFindSubscription(ids->values[i].integer)) == NULL)
5552 {
5553 /*
5554 * Bad subscription ID...
5555 */
5556
5557 send_ipp_status(con, IPP_NOT_FOUND,
5558 _("notify-subscription-id %d no good!"),
5559 ids->values[i].integer);
5560 return;
5561 }
5562
5563 /*
5564 * Check policy...
5565 */
5566
5567 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
5568 DefaultPolicyPtr,
5569 con, sub->owner)) != HTTP_OK)
5570 {
5571 send_http_error(con, status);
5572 return;
5573 }
5574
5575 /*
5576 * Check the subscription type and update the interval accordingly.
5577 */
5578
5579 if (sub->job && sub->job->state_value == IPP_JOB_PROCESSING &&
5580 interval > 10)
5581 interval = 10;
5582 else if (sub->job && sub->job->state_value >= IPP_JOB_STOPPED)
5583 interval = 0;
5584 else if (sub->dest && sub->dest->state == IPP_PRINTER_PROCESSING &&
5585 interval > 30)
5586 interval = 30;
5587 }
5588
5589 /*
5590 * Tell the client to poll again in N seconds...
5591 */
5592
5593 if (interval > 0)
5594 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
5595 "notify-get-interval", interval);
5596
5597 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
5598 "printer-up-time", time(NULL));
5599
5600 /*
5601 * Copy the subscription event attributes to the response.
5602 */
5603
5604 con->response->request.status.status_code =
5605 interval ? IPP_OK : IPP_OK_EVENTS_COMPLETE;
5606
5607 for (i = 0; i < ids->num_values; i ++)
5608 {
5609 /*
5610 * Get the subscription and sequence number...
5611 */
5612
5613 sub = cupsdFindSubscription(ids->values[i].integer);
5614
5615 if (sequences && i < sequences->num_values)
5616 min_seq = sequences->values[i].integer;
5617 else
5618 min_seq = 1;
5619
5620 /*
5621 * If we don't have any new events, nothing to do here...
5622 */
5623
5624 if (min_seq > (sub->first_event_id + sub->num_events))
5625 continue;
5626
5627 /*
5628 * Otherwise copy all of the new events...
5629 */
5630
5631 if (sub->first_event_id > min_seq)
5632 j = 0;
5633 else
5634 j = min_seq - sub->first_event_id;
5635
b423cd4c 5636 for (; j < sub->num_events; j ++)
5637 {
5638 ippAddSeparator(con->response);
ef416fc2 5639
b423cd4c 5640 copy_attrs(con->response, sub->events[j]->attrs, NULL,
5641 IPP_TAG_EVENT_NOTIFICATION, 0);
5642 }
5643 }
5644}
ef416fc2 5645
ef416fc2 5646
b423cd4c 5647/*
5648 * 'get_ppds()' - Get the list of PPD files on the local system.
5649 */
ef416fc2 5650
b423cd4c 5651static void
5652get_ppds(cupsd_client_t *con) /* I - Client connection */
5653{
5654 http_status_t status; /* Policy status */
b423cd4c 5655 ipp_attribute_t *limit, /* Limit attribute */
5656 *make, /* ppd-make attribute */
5657 *requested; /* requested-attributes attribute */
5658 char command[1024], /* cups-deviced command */
5659 options[1024], /* Options to pass to command */
89d46774 5660 requested_str[256],
5661 /* String for requested attributes */
5662 make_str[256]; /* Escaped ppd-make string */
ef416fc2 5663
ef416fc2 5664
b423cd4c 5665 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->http.fd);
ef416fc2 5666
5667 /*
b423cd4c 5668 * Check policy...
ef416fc2 5669 */
5670
b423cd4c 5671 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
ef416fc2 5672 {
b423cd4c 5673 send_http_error(con, status);
5674 return;
ef416fc2 5675 }
ef416fc2 5676
b423cd4c 5677 /*
5678 * Run cups-driverd command with the given options...
5679 */
5680
5681 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
5682 make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
5683 requested = ippFindAttribute(con->request, "requested-attributes",
5684 IPP_TAG_KEYWORD);
5685
5686 if (requested)
89d46774 5687 url_encode_attr(requested, requested_str, sizeof(requested_str));
5688 else
5689 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
ef416fc2 5690
89d46774 5691 if (make)
5692 url_encode_attr(make, make_str, sizeof(make_str));
b423cd4c 5693 else
89d46774 5694 make_str[0] = '\0';
fa73b229 5695
b423cd4c 5696 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
89d46774 5697 snprintf(options, sizeof(options), "list+%d+%d+%s%s%s",
b423cd4c 5698 con->request->request.op.request_id,
5699 limit ? limit->values[0].integer : 0,
89d46774 5700 requested_str, make ? "%20" : "", make_str);
ef416fc2 5701
b423cd4c 5702 if (cupsdSendCommand(con, command, options, 0))
5703 {
5704 /*
5705 * Command started successfully, don't send an IPP response here...
5706 */
5707
5708 ippDelete(con->response);
5709 con->response = NULL;
5710 }
5711 else
5712 {
5713 /*
5714 * Command failed, return "internal error" so the user knows something
5715 * went wrong...
5716 */
5717
5718 send_ipp_status(con, IPP_INTERNAL_ERROR,
5719 _("cups-driverd failed to execute."));
5720 }
ef416fc2 5721}
5722
5723
5724/*
b423cd4c 5725 * 'get_printer_attrs()' - Get printer attributes.
ef416fc2 5726 */
5727
5728static void
b423cd4c 5729get_printer_attrs(cupsd_client_t *con, /* I - Client connection */
5730 ipp_attribute_t *uri) /* I - Printer URI */
ef416fc2 5731{
5732 http_status_t status; /* Policy status */
b423cd4c 5733 cups_ptype_t dtype; /* Destination type (printer or class) */
f7faf1f5 5734 char method[HTTP_MAX_URI],
5735 /* Method portion of URI */
5736 username[HTTP_MAX_URI],
5737 /* Username portion of URI */
5738 host[HTTP_MAX_URI],
5739 /* Host portion of URI */
5740 resource[HTTP_MAX_URI];
5741 /* Resource portion of URI */
5742 int port; /* Port portion of URI */
b423cd4c 5743 cupsd_printer_t *printer; /* Printer/class */
ef416fc2 5744 cups_array_t *ra; /* Requested attributes array */
5745
5746
b423cd4c 5747 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", con,
5748 con->http.fd, uri->values[0].string.text);
ef416fc2 5749
5750 /*
b423cd4c 5751 * Is the destination valid?
ef416fc2 5752 */
5753
f7faf1f5 5754 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
5755 sizeof(method), username, sizeof(username), host,
5756 sizeof(host), &port, resource, sizeof(resource));
5757
d09495fa 5758 if (!cupsdValidateDest(host, resource, &dtype, &printer))
ef416fc2 5759 {
5760 /*
b423cd4c 5761 * Bad URI...
ef416fc2 5762 */
5763
5764 send_ipp_status(con, IPP_NOT_FOUND,
b423cd4c 5765 _("The printer or class was not found."));
ef416fc2 5766 return;
5767 }
5768
5769 /*
5770 * Check policy...
5771 */
5772
b423cd4c 5773 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
ef416fc2 5774 {
5775 send_http_error(con, status);
5776 return;
5777 }
5778
5779 /*
b423cd4c 5780 * Send the attributes...
ef416fc2 5781 */
5782
5783 ra = create_requested_array(con->request);
5784
b423cd4c 5785 copy_printer_attrs(con, printer, ra);
ef416fc2 5786
5787 cupsArrayDelete(ra);
5788
5789 con->response->request.status.status_code = IPP_OK;
5790}
5791
5792
5793/*
b423cd4c 5794 * 'get_printers()' - Get a list of printers or classes.
ef416fc2 5795 */
5796
5797static void
b423cd4c 5798get_printers(cupsd_client_t *con, /* I - Client connection */
5799 int type) /* I - 0 or CUPS_PRINTER_CLASS */
ef416fc2 5800{
b423cd4c 5801 http_status_t status; /* Policy status */
5802 ipp_attribute_t *attr; /* Current attribute */
5803 int limit; /* Maximum number of printers to return */
5804 int count; /* Number of printers that match */
5805 cupsd_printer_t *printer; /* Current printer pointer */
5806 int printer_type, /* printer-type attribute */
5807 printer_mask; /* printer-type-mask attribute */
5808 char *location; /* Location string */
5809 const char *username; /* Current user */
5810 char *first_printer_name; /* first-printer-name attribute */
5811 cups_array_t *ra; /* Requested attributes array */
ef416fc2 5812
ef416fc2 5813
b423cd4c 5814 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con,
5815 con->http.fd, type);
ef416fc2 5816
5817 /*
5818 * Check policy...
5819 */
5820
b423cd4c 5821 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
ef416fc2 5822 {
5823 send_http_error(con, status);
5824 return;
5825 }
5826
5827 /*
b423cd4c 5828 * Check for printers...
ef416fc2 5829 */
5830
b423cd4c 5831 if (!Printers || !cupsArrayCount(Printers))
5832 {
5833 send_ipp_status(con, IPP_NOT_FOUND, _("No destinations added."));
5834 return;
5835 }
5836
5837 /*
5838 * See if they want to limit the number of printers reported...
5839 */
ef416fc2 5840
fa73b229 5841 if ((attr = ippFindAttribute(con->request, "limit",
5842 IPP_TAG_INTEGER)) != NULL)
ef416fc2 5843 limit = attr->values[0].integer;
5844 else
b423cd4c 5845 limit = 10000000;
5846
5847 if ((attr = ippFindAttribute(con->request, "first-printer-name",
5848 IPP_TAG_NAME)) != NULL)
5849 first_printer_name = attr->values[0].string.text;
5850 else
5851 first_printer_name = NULL;
ef416fc2 5852
5853 /*
b423cd4c 5854 * Support filtering...
ef416fc2 5855 */
5856
b423cd4c 5857 if ((attr = ippFindAttribute(con->request, "printer-type",
5858 IPP_TAG_ENUM)) != NULL)
5859 printer_type = attr->values[0].integer;
ef416fc2 5860 else
b423cd4c 5861 printer_type = 0;
ef416fc2 5862
b423cd4c 5863 if ((attr = ippFindAttribute(con->request, "printer-type-mask",
5864 IPP_TAG_ENUM)) != NULL)
5865 printer_mask = attr->values[0].integer;
ef416fc2 5866 else
b423cd4c 5867 printer_mask = 0;
e00b005a 5868
b423cd4c 5869 if ((attr = ippFindAttribute(con->request, "printer-location",
5870 IPP_TAG_TEXT)) != NULL)
5871 location = attr->values[0].string.text;
5872 else
5873 location = NULL;
e00b005a 5874
5875 if (con->username[0])
b423cd4c 5876 username = con->username;
e00b005a 5877 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
5878 IPP_TAG_NAME)) != NULL)
b423cd4c 5879 username = attr->values[0].string.text;
e00b005a 5880 else
b423cd4c 5881 username = NULL;
ef416fc2 5882
b423cd4c 5883 ra = create_requested_array(con->request);
ef416fc2 5884
5885 /*
b423cd4c 5886 * OK, build a list of printers for this printer...
ef416fc2 5887 */
5888
b423cd4c 5889 if (first_printer_name)
ef416fc2 5890 {
b423cd4c 5891 if ((printer = cupsdFindDest(first_printer_name)) == NULL)
5892 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
ef416fc2 5893 }
5894 else
b423cd4c 5895 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
ef416fc2 5896
b423cd4c 5897 for (count = 0;
5898 count < limit && printer;
5899 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
5900 {
5901 if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
5902 (printer->type & printer_mask) == printer_type &&
5903 (!location || !printer->location ||
5904 !strcasecmp(printer->location, location)))
ef416fc2 5905 {
5906 /*
b423cd4c 5907 * If HideImplicitMembers is enabled, see if this printer or class
5908 * is a member of an implicit class...
ef416fc2 5909 */
5910
b423cd4c 5911 if (ImplicitClasses && HideImplicitMembers &&
5912 printer->in_implicit_class)
5913 continue;
ef416fc2 5914
b423cd4c 5915 /*
5916 * If a username is specified, see if it is allowed or denied
5917 * access...
5918 */
ef416fc2 5919
b423cd4c 5920 if (printer->num_users && username && !user_allowed(printer, username))
5921 continue;
ef416fc2 5922
b423cd4c 5923 /*
5924 * Add the group separator as needed...
5925 */
ef416fc2 5926
b423cd4c 5927 if (count > 0)
5928 ippAddSeparator(con->response);
ef416fc2 5929
b423cd4c 5930 count ++;
ef416fc2 5931
b423cd4c 5932 /*
5933 * Send the attributes...
5934 */
ef416fc2 5935
b423cd4c 5936 copy_printer_attrs(con, printer, ra);
5937 }
ef416fc2 5938 }
5939
b423cd4c 5940 cupsArrayDelete(ra);
ef416fc2 5941
5942 con->response->request.status.status_code = IPP_OK;
5943}
5944
5945
5946/*
b423cd4c 5947 * 'get_subscription_attrs()' - Get subscription attributes.
ef416fc2 5948 */
5949
5950static void
b423cd4c 5951get_subscription_attrs(
5952 cupsd_client_t *con, /* I - Client connection */
5953 int sub_id) /* I - Subscription ID */
ef416fc2 5954{
b423cd4c 5955 http_status_t status; /* Policy status */
5956 cupsd_subscription_t *sub; /* Subscription */
5957 cups_array_t *ra; /* Requested attributes array */
ef416fc2 5958
5959
b423cd4c 5960 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5961 "get_subscription_attrs(con=%p[%d], sub_id=%d)",
5962 con, con->http.fd, sub_id);
ef416fc2 5963
fa73b229 5964 /*
b423cd4c 5965 * Is the subscription ID valid?
fa73b229 5966 */
5967
b423cd4c 5968 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
fa73b229 5969 {
5970 /*
b423cd4c 5971 * Bad subscription ID...
fa73b229 5972 */
5973
5974 send_ipp_status(con, IPP_NOT_FOUND,
b423cd4c 5975 _("notify-subscription-id %d no good!"), sub_id);
fa73b229 5976 return;
5977 }
5978
5979 /*
5980 * Check policy...
5981 */
5982
b423cd4c 5983 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
5984 DefaultPolicyPtr,
5985 con, sub->owner)) != HTTP_OK)
fa73b229 5986 {
5987 send_http_error(con, status);
5988 return;
5989 }
5990
ef416fc2 5991 /*
b423cd4c 5992 * Copy the subscription attributes to the response using the
5993 * requested-attributes attribute that may be provided by the client.
ef416fc2 5994 */
5995
b423cd4c 5996 ra = create_requested_array(con->request);
fa73b229 5997
b423cd4c 5998 copy_subscription_attrs(con, sub, ra);
ef416fc2 5999
b423cd4c 6000 cupsArrayDelete(ra);
fa73b229 6001
b423cd4c 6002 con->response->request.status.status_code = IPP_OK;
6003}
fa73b229 6004
fa73b229 6005
b423cd4c 6006/*
6007 * 'get_subscriptions()' - Get subscriptions.
6008 */
ef416fc2 6009
b423cd4c 6010static void
6011get_subscriptions(cupsd_client_t *con, /* I - Client connection */
6012 ipp_attribute_t *uri) /* I - Printer/job URI */
6013{
6014 http_status_t status; /* Policy status */
6015 int count; /* Number of subscriptions */
6016 int limit; /* Limit */
6017 cupsd_subscription_t *sub; /* Subscription */
6018 cups_array_t *ra; /* Requested attributes array */
6019 ipp_attribute_t *attr; /* Attribute */
6020 cups_ptype_t dtype; /* Destination type (printer or class) */
f7faf1f5 6021 char method[HTTP_MAX_URI],
6022 /* Method portion of URI */
b423cd4c 6023 username[HTTP_MAX_URI],
6024 /* Username portion of URI */
6025 host[HTTP_MAX_URI],
6026 /* Host portion of URI */
6027 resource[HTTP_MAX_URI];
6028 /* Resource portion of URI */
6029 int port; /* Port portion of URI */
6030 cupsd_job_t *job; /* Job pointer */
6031 cupsd_printer_t *printer; /* Printer */
fa73b229 6032
fa73b229 6033
b423cd4c 6034 cupsdLogMessage(CUPSD_LOG_DEBUG2,
6035 "get_subscriptions(con=%p[%d], uri=%s)",
6036 con, con->http.fd, uri->values[0].string.text);
6037
6038 /*
6039 * Is the destination valid?
6040 */
6041
f7faf1f5 6042 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
6043 sizeof(method), username, sizeof(username), host,
b423cd4c 6044 sizeof(host), &port, resource, sizeof(resource));
6045
6046 if (!strcmp(resource, "/") ||
6047 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6) ||
6048 (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) ||
6049 (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9))
6050 {
6051 printer = NULL;
6052 job = NULL;
ef416fc2 6053 }
b423cd4c 6054 else if (!strncmp(resource, "/jobs/", 6) && resource[6])
ef416fc2 6055 {
b423cd4c 6056 printer = NULL;
6057 job = cupsdFindJob(atoi(resource + 6));
ef416fc2 6058
b423cd4c 6059 if (!job)
ef416fc2 6060 {
b423cd4c 6061 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%s does not exist!"),
6062 resource + 6);
ef416fc2 6063 return;
6064 }
b423cd4c 6065 }
f7faf1f5 6066 else if (!cupsdValidateDest(host, resource, &dtype, &printer))
b423cd4c 6067 {
fa73b229 6068 /*
b423cd4c 6069 * Bad URI...
fa73b229 6070 */
6071
b423cd4c 6072 send_ipp_status(con, IPP_NOT_FOUND,
6073 _("The printer or class was not found."));
6074 return;
6075 }
6076 else if ((attr = ippFindAttribute(con->request, "notify-job-id",
6077 IPP_TAG_INTEGER)) != NULL)
6078 {
6079 job = cupsdFindJob(attr->values[0].integer);
ef416fc2 6080
b423cd4c 6081 if (!job)
fa73b229 6082 {
b423cd4c 6083 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"),
6084 attr->values[0].integer);
fa73b229 6085 return;
6086 }
ef416fc2 6087 }
b423cd4c 6088 else
6089 job = NULL;
ef416fc2 6090
6091 /*
b423cd4c 6092 * Check policy...
ef416fc2 6093 */
6094
b423cd4c 6095 if ((status = cupsdCheckPolicy(printer ? printer->op_policy_ptr :
6096 DefaultPolicyPtr,
6097 con, NULL)) != HTTP_OK)
ef416fc2 6098 {
b423cd4c 6099 send_http_error(con, status);
6100 return;
6101 }
ef416fc2 6102
b423cd4c 6103 /*
6104 * Copy the subscription attributes to the response using the
6105 * requested-attributes attribute that may be provided by the client.
6106 */
ef416fc2 6107
b423cd4c 6108 ra = create_requested_array(con->request);
ef416fc2 6109
b423cd4c 6110 if ((attr = ippFindAttribute(con->request, "limit",
6111 IPP_TAG_INTEGER)) != NULL)
6112 limit = attr->values[0].integer;
6113 else
6114 limit = 0;
ef416fc2 6115
b423cd4c 6116 /*
6117 * See if we only want to see subscriptions for a specific user...
6118 */
ef416fc2 6119
b423cd4c 6120 if ((attr = ippFindAttribute(con->request, "my-subscriptions",
6121 IPP_TAG_BOOLEAN)) != NULL &&
6122 attr->values[0].boolean)
6123 strlcpy(username, get_username(con), sizeof(username));
fa73b229 6124 else
b423cd4c 6125 username[0] = '\0';
ef416fc2 6126
b423cd4c 6127 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0;
6128 sub;
6129 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
6130 if ((!printer || sub->dest == printer) && (!job || sub->job == job) &&
6131 (!username[0] || !strcasecmp(username, sub->owner)))
fa73b229 6132 {
b423cd4c 6133 ippAddSeparator(con->response);
6134 copy_subscription_attrs(con, sub, ra);
ef416fc2 6135
b423cd4c 6136 count ++;
6137 if (limit && count >= limit)
6138 break;
6139 }
ef416fc2 6140
b423cd4c 6141 cupsArrayDelete(ra);
fa73b229 6142
b423cd4c 6143 if (count)
6144 con->response->request.status.status_code = IPP_OK;
6145 else
6146 send_ipp_status(con, IPP_NOT_FOUND, _("No subscriptions found."));
6147}
ef416fc2 6148
ef416fc2 6149
b423cd4c 6150/*
6151 * 'get_username()' - Get the username associated with a request.
6152 */
ef416fc2 6153
b423cd4c 6154static const char * /* O - Username */
6155get_username(cupsd_client_t *con) /* I - Connection */
6156{
6157 ipp_attribute_t *attr; /* Attribute */
ef416fc2 6158
ef416fc2 6159
b423cd4c 6160 if (con->username[0])
6161 return (con->username);
6162 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
6163 IPP_TAG_NAME)) != NULL)
6164 return (attr->values[0].string.text);
6165 else
6166 return ("anonymous");
ef416fc2 6167}
6168
6169
6170/*
b423cd4c 6171 * 'hold_job()' - Hold a print job.
ef416fc2 6172 */
6173
b423cd4c 6174static void
6175hold_job(cupsd_client_t *con, /* I - Client connection */
6176 ipp_attribute_t *uri) /* I - Job or Printer URI */
ef416fc2 6177{
b423cd4c 6178 ipp_attribute_t *attr, /* Current job-hold-until */
6179 *newattr; /* New job-hold-until */
6180 int jobid; /* Job ID */
6181 char method[HTTP_MAX_URI], /* Method portion of URI */
6182 username[HTTP_MAX_URI], /* Username portion of URI */
6183 host[HTTP_MAX_URI], /* Host portion of URI */
6184 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6185 int port; /* Port portion of URI */
6186 cupsd_job_t *job; /* Job information */
6187
ef416fc2 6188
b423cd4c 6189 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", con, con->http.fd,
6190 uri->values[0].string.text);
ef416fc2 6191
6192 /*
b423cd4c 6193 * See if we have a job URI or a printer URI...
ef416fc2 6194 */
6195
b423cd4c 6196 if (!strcmp(uri->name, "printer-uri"))
6197 {
6198 /*
6199 * Got a printer URI; see if we also have a job-id attribute...
6200 */
ef416fc2 6201
b423cd4c 6202 if ((attr = ippFindAttribute(con->request, "job-id",
6203 IPP_TAG_INTEGER)) == NULL)
6204 {
6205 send_ipp_status(con, IPP_BAD_REQUEST,
6206 _("Got a printer-uri attribute but no job-id!"));
6207 return;
6208 }
ef416fc2 6209
b423cd4c 6210 jobid = attr->values[0].integer;
6211 }
ef416fc2 6212 else
ef416fc2 6213 {
b423cd4c 6214 /*
6215 * Got a job URI; parse it to get the job ID...
6216 */
ef416fc2 6217
b423cd4c 6218 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
6219 sizeof(method), username, sizeof(username), host,
6220 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 6221
b423cd4c 6222 if (strncmp(resource, "/jobs/", 6))
6223 {
6224 /*
6225 * Not a valid URI!
6226 */
ef416fc2 6227
b423cd4c 6228 send_ipp_status(con, IPP_BAD_REQUEST,
6229 _("Bad job-uri attribute \"%s\"!"),
6230 uri->values[0].string.text);
6231 return;
6232 }
ef416fc2 6233
b423cd4c 6234 jobid = atoi(resource + 6);
6235 }
ef416fc2 6236
ef416fc2 6237 /*
b423cd4c 6238 * See if the job exists...
ef416fc2 6239 */
6240
b423cd4c 6241 if ((job = cupsdFindJob(jobid)) == NULL)
6242 {
6243 /*
6244 * Nope - return a "not found" error...
6245 */
6246
6247 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
6248 return;
6249 }
ef416fc2 6250
6251 /*
b423cd4c 6252 * See if the job is owned by the requesting user...
ef416fc2 6253 */
6254
b423cd4c 6255 if (!validate_user(job, con, job->username, username, sizeof(username)))
6256 {
6257 send_http_error(con, HTTP_UNAUTHORIZED);
6258 return;
6259 }
ef416fc2 6260
6261 /*
b423cd4c 6262 * Hold the job and return...
ef416fc2 6263 */
6264
b423cd4c 6265 cupsdHoldJob(job);
ef416fc2 6266
d09495fa 6267 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
6268 "Job held by user.");
6269
b423cd4c 6270 if ((newattr = ippFindAttribute(con->request, "job-hold-until",
6271 IPP_TAG_KEYWORD)) == NULL)
6272 newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
ef416fc2 6273
b423cd4c 6274 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
6275 IPP_TAG_KEYWORD)) == NULL)
6276 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
ef416fc2 6277
b423cd4c 6278 if (attr)
6279 {
6280 /*
6281 * Free the old hold value and copy the new one over...
6282 */
ef416fc2 6283
757d2cad 6284 _cupsStrFree(attr->values[0].string.text);
ef416fc2 6285
b423cd4c 6286 if (newattr)
ef416fc2 6287 {
b423cd4c 6288 attr->value_tag = newattr->value_tag;
6289 attr->values[0].string.text =
757d2cad 6290 _cupsStrAlloc(newattr->values[0].string.text);
b423cd4c 6291 }
6292 else
6293 {
6294 attr->value_tag = IPP_TAG_KEYWORD;
757d2cad 6295 attr->values[0].string.text = _cupsStrAlloc("indefinite");
ef416fc2 6296 }
6297
b423cd4c 6298 /*
6299 * Hold job until specified time...
6300 */
ef416fc2 6301
b423cd4c 6302 cupsdSetJobHoldUntil(job, attr->values[0].string.text);
d09495fa 6303
6304 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
6305 "Job job-hold-until value changed by user.");
b423cd4c 6306 }
ef416fc2 6307
b423cd4c 6308 cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was held by \"%s\".", jobid,
6309 username);
6310
6311 con->response->request.status.status_code = IPP_OK;
ef416fc2 6312}
6313
6314
6315/*
b423cd4c 6316 * 'move_job()' - Move a job to a new destination.
ef416fc2 6317 */
6318
6319static void
b423cd4c 6320move_job(cupsd_client_t *con, /* I - Client connection */
6321 ipp_attribute_t *uri) /* I - Job URI */
ef416fc2 6322{
6323 http_status_t status; /* Policy status */
6324 ipp_attribute_t *attr; /* Current attribute */
b423cd4c 6325 int jobid; /* Job ID */
ef416fc2 6326 cupsd_job_t *job; /* Current job */
d09495fa 6327 const char *src; /* Source printer/class */
b423cd4c 6328 cups_ptype_t stype, /* Source type (printer or class) */
6329 dtype; /* Destination type (printer or class) */
f7faf1f5 6330 char method[HTTP_MAX_URI], /* Method portion of URI */
ef416fc2 6331 username[HTTP_MAX_URI], /* Username portion of URI */
6332 host[HTTP_MAX_URI], /* Host portion of URI */
b423cd4c 6333 resource[HTTP_MAX_URI]; /* Resource portion of URI */
ef416fc2 6334 int port; /* Port portion of URI */
b423cd4c 6335 cupsd_printer_t *sprinter, /* Source printer */
6336 *dprinter; /* Destination printer */
ef416fc2 6337
6338
b423cd4c 6339 cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", con, con->http.fd,
ef416fc2 6340 uri->values[0].string.text);
6341
6342 /*
b423cd4c 6343 * Get the new printer or class...
ef416fc2 6344 */
6345
b423cd4c 6346 if ((attr = ippFindAttribute(con->request, "job-printer-uri",
6347 IPP_TAG_URI)) == NULL)
ef416fc2 6348 {
b423cd4c 6349 /*
6350 * Need job-printer-uri...
6351 */
ef416fc2 6352
b423cd4c 6353 send_ipp_status(con, IPP_BAD_REQUEST,
6354 _("job-printer-uri attribute missing!"));
6355 return;
ef416fc2 6356 }
b423cd4c 6357
f7faf1f5 6358 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, method,
6359 sizeof(method), username, sizeof(username), host,
6360 sizeof(host), &port, resource, sizeof(resource));
6361
d09495fa 6362 if (!cupsdValidateDest(host, resource, &dtype, &dprinter))
ef416fc2 6363 {
b423cd4c 6364 /*
6365 * Bad URI...
6366 */
ef416fc2 6367
b423cd4c 6368 send_ipp_status(con, IPP_NOT_FOUND,
6369 _("The printer or class was not found."));
6370 return;
ef416fc2 6371 }
6372
6373 /*
b423cd4c 6374 * Check policy...
ef416fc2 6375 */
6376
f7faf1f5 6377 if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con, NULL)) != HTTP_OK)
ef416fc2 6378 {
b423cd4c 6379 send_http_error(con, status);
ef416fc2 6380 return;
6381 }
6382
6383 /*
b423cd4c 6384 * See if we have a job URI or a printer URI...
ef416fc2 6385 */
6386
f7faf1f5 6387 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
6388 sizeof(method), username, sizeof(username), host,
b423cd4c 6389 sizeof(host), &port, resource, sizeof(resource));
6390
6391 if (!strcmp(uri->name, "printer-uri"))
ef416fc2 6392 {
6393 /*
b423cd4c 6394 * Got a printer URI; see if we also have a job-id attribute...
ef416fc2 6395 */
6396
b423cd4c 6397 if ((attr = ippFindAttribute(con->request, "job-id",
6398 IPP_TAG_INTEGER)) == NULL)
ef416fc2 6399 {
b423cd4c 6400 /*
6401 * Move all jobs...
6402 */
6403
f7faf1f5 6404 if ((src = cupsdValidateDest(host, resource, &stype, &sprinter)) == NULL)
b423cd4c 6405 {
6406 /*
6407 * Bad URI...
6408 */
6409
6410 send_ipp_status(con, IPP_NOT_FOUND,
6411 _("The printer or class was not found."));
6412 return;
6413 }
6414
6415 job = NULL;
6416 }
6417 else
6418 {
6419 /*
6420 * Otherwise, just move a single job...
6421 */
6422
6423 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
6424 {
6425 /*
6426 * Nope - return a "not found" error...
6427 */
6428
6429 send_ipp_status(con, IPP_NOT_FOUND,
6430 _("Job #%d does not exist!"), attr->values[0].integer);
6431 return;
6432 }
6433 else
6434 {
6435 /*
6436 * Job found, initialize source pointers...
6437 */
6438
6439 src = NULL;
6440 sprinter = NULL;
6441 }
ef416fc2 6442 }
6443 }
6444 else
6445 {
6446 /*
b423cd4c 6447 * Got a job URI; parse it to get the job ID...
ef416fc2 6448 */
6449
b423cd4c 6450 if (strncmp(resource, "/jobs/", 6))
6451 {
6452 /*
6453 * Not a valid URI!
6454 */
6455
6456 send_ipp_status(con, IPP_BAD_REQUEST,
6457 _("Bad job-uri attribute \"%s\"!"),
6458 uri->values[0].string.text);
6459 return;
6460 }
ef416fc2 6461
ef416fc2 6462 /*
b423cd4c 6463 * See if the job exists...
ef416fc2 6464 */
6465
b423cd4c 6466 jobid = atoi(resource + 6);
ef416fc2 6467
b423cd4c 6468 if ((job = cupsdFindJob(jobid)) == NULL)
ef416fc2 6469 {
6470 /*
b423cd4c 6471 * Nope - return a "not found" error...
ef416fc2 6472 */
6473
b423cd4c 6474 send_ipp_status(con, IPP_NOT_FOUND,
6475 _("Job #%d does not exist!"), jobid);
6476 return;
ef416fc2 6477 }
6478 else
b423cd4c 6479 {
6480 /*
6481 * Job found, initialize source pointers...
6482 */
ef416fc2 6483
b423cd4c 6484 src = NULL;
6485 sprinter = NULL;
6486 }
ef416fc2 6487 }
6488
ef416fc2 6489 /*
b423cd4c 6490 * Now move the job or jobs...
ef416fc2 6491 */
6492
b423cd4c 6493 if (job)
6494 {
6495 /*
6496 * See if the job has been completed...
6497 */
6498
6499 if (job->state_value > IPP_JOB_STOPPED)
6500 {
6501 /*
6502 * Return a "not-possible" error...
6503 */
6504
6505 send_ipp_status(con, IPP_NOT_POSSIBLE,
6506 _("Job #%d is finished and cannot be altered!"),
6507 job->id);
6508 return;
6509 }
ef416fc2 6510
b423cd4c 6511 /*
6512 * See if the job is owned by the requesting user...
6513 */
ef416fc2 6514
b423cd4c 6515 if (!validate_user(job, con, job->username, username, sizeof(username)))
6516 {
6517 send_http_error(con, HTTP_UNAUTHORIZED);
6518 return;
6519 }
ef416fc2 6520
ef416fc2 6521 /*
b423cd4c 6522 * Move the job to a different printer or class...
ef416fc2 6523 */
6524
e53920b9 6525 cupsdMoveJob(job, dprinter);
ef416fc2 6526 }
b423cd4c 6527 else
6528 {
6529 /*
6530 * Got the source printer, now look through the jobs...
6531 */
ef416fc2 6532
b423cd4c 6533 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
6534 job;
6535 job = (cupsd_job_t *)cupsArrayNext(Jobs))
6536 {
6537 /*
6538 * See if the job is pointing at the source printer or has not been
6539 * completed...
6540 */
ef416fc2 6541
b423cd4c 6542 if (strcasecmp(job->dest, src) ||
6543 job->state_value > IPP_JOB_STOPPED)
6544 continue;
ef416fc2 6545
b423cd4c 6546 /*
6547 * See if the job can be moved by the requesting user...
6548 */
ef416fc2 6549
b423cd4c 6550 if (!validate_user(job, con, job->username, username, sizeof(username)))
6551 continue;
ef416fc2 6552
b423cd4c 6553 /*
6554 * Move the job to a different printer or class...
6555 */
ef416fc2 6556
e53920b9 6557 cupsdMoveJob(job, dprinter);
b423cd4c 6558 }
ef416fc2 6559 }
6560
6561 /*
b423cd4c 6562 * Start jobs if possible...
ef416fc2 6563 */
6564
b423cd4c 6565 cupsdCheckJobs();
ef416fc2 6566
6567 /*
b423cd4c 6568 * Return with "everything is OK" status...
ef416fc2 6569 */
6570
b423cd4c 6571 con->response->request.status.status_code = IPP_OK;
6572}
ef416fc2 6573
ef416fc2 6574
b423cd4c 6575/*
6576 * 'ppd_add_default()' - Add a PPD default choice.
6577 */
ef416fc2 6578
b423cd4c 6579static int /* O - Number of defaults */
6580ppd_add_default(
6581 const char *option, /* I - Option name */
6582 const char *choice, /* I - Choice name */
6583 int num_defaults, /* I - Number of defaults */
6584 ppd_default_t **defaults) /* IO - Defaults */
6585{
6586 int i; /* Looping var */
6587 ppd_default_t *temp; /* Temporary defaults array */
ef416fc2 6588
bd7854cb 6589
ef416fc2 6590 /*
b423cd4c 6591 * First check if the option already has a default value; the PPD spec
6592 * says that the first one is used...
ef416fc2 6593 */
6594
b423cd4c 6595 for (i = 0, temp = *defaults; i < num_defaults; i ++)
6596 if (!strcmp(option, temp[i].option))
6597 return (num_defaults);
ef416fc2 6598
b423cd4c 6599 /*
6600 * Now add the option...
6601 */
ef416fc2 6602
b423cd4c 6603 if (num_defaults == 0)
6604 temp = malloc(sizeof(ppd_default_t));
ef416fc2 6605 else
b423cd4c 6606 temp = realloc(*defaults, (num_defaults + 1) * sizeof(ppd_default_t));
ef416fc2 6607
b423cd4c 6608 if (!temp)
ef416fc2 6609 {
b423cd4c 6610 cupsdLogMessage(CUPSD_LOG_ERROR, "ppd_add_default: Unable to add default value for \"%s\" - %s",
6611 option, strerror(errno));
6612 return (num_defaults);
ef416fc2 6613 }
6614
b423cd4c 6615 *defaults = temp;
6616 temp += num_defaults;
ef416fc2 6617
b423cd4c 6618 strlcpy(temp->option, option, sizeof(temp->option));
6619 strlcpy(temp->choice, choice, sizeof(temp->choice));
ef416fc2 6620
b423cd4c 6621 return (num_defaults + 1);
6622}
ef416fc2 6623
ef416fc2 6624
b423cd4c 6625/*
6626 * 'ppd_parse_line()' - Parse a PPD default line.
6627 */
ef416fc2 6628
b423cd4c 6629static int /* O - 0 on success, -1 on failure */
6630ppd_parse_line(const char *line, /* I - Line */
6631 char *option, /* O - Option name */
6632 int olen, /* I - Size of option name */
6633 char *choice, /* O - Choice name */
6634 int clen) /* I - Size of choice name */
6635{
6636 /*
6637 * Verify this is a default option line...
6638 */
ef416fc2 6639
b423cd4c 6640 if (strncmp(line, "*Default", 8))
6641 return (-1);
ef416fc2 6642
b423cd4c 6643 /*
6644 * Read the option name...
6645 */
6646
6647 for (line += 8, olen --; isalnum(*line & 255); line ++)
6648 if (olen > 0)
6649 {
6650 *option++ = *line;
6651 olen --;
ef416fc2 6652 }
6653
b423cd4c 6654 *option = '\0';
ef416fc2 6655
b423cd4c 6656 /*
6657 * Skip everything else up to the colon (:)...
6658 */
ef416fc2 6659
b423cd4c 6660 while (*line && *line != ':')
6661 line ++;
ef416fc2 6662
b423cd4c 6663 if (!*line)
6664 return (-1);
ef416fc2 6665
b423cd4c 6666 line ++;
ef416fc2 6667
b423cd4c 6668 /*
6669 * Now grab the option choice, skipping leading whitespace...
6670 */
ef416fc2 6671
b423cd4c 6672 while (isspace(*line & 255))
6673 line ++;
ef416fc2 6674
b423cd4c 6675 for (clen --; isalnum(*line & 255); line ++)
6676 if (clen > 0)
6677 {
6678 *choice++ = *line;
6679 clen --;
6680 }
ef416fc2 6681
b423cd4c 6682 *choice = '\0';
ef416fc2 6683
b423cd4c 6684 /*
6685 * Return with no errors...
6686 */
ef416fc2 6687
b423cd4c 6688 return (0);
6689}
ef416fc2 6690
ef416fc2 6691
b423cd4c 6692/*
6693 * 'print_job()' - Print a file to a printer or class.
6694 */
ef416fc2 6695
b423cd4c 6696static void
6697print_job(cupsd_client_t *con, /* I - Client connection */
6698 ipp_attribute_t *uri) /* I - Printer URI */
6699{
6700 ipp_attribute_t *attr; /* Current attribute */
6701 ipp_attribute_t *format; /* Document-format attribute */
6702 cupsd_job_t *job; /* New job */
6703 char filename[1024]; /* Job filename */
6704 mime_type_t *filetype; /* Type of file */
6705 char super[MIME_MAX_SUPER], /* Supertype of file */
6706 type[MIME_MAX_TYPE], /* Subtype of file */
6707 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
6708 /* Textual name of mime type */
6709 cupsd_printer_t *printer; /* Printer data */
6710 struct stat fileinfo; /* File information */
6711 int kbytes; /* Size of file */
6712 int compression; /* Document compression */
ef416fc2 6713
ef416fc2 6714
b423cd4c 6715 cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", con, con->http.fd,
6716 uri->values[0].string.text);
ef416fc2 6717
b423cd4c 6718 /*
6719 * Validate print file attributes, for now just document-format and
6720 * compression (CUPS only supports "none" and "gzip")...
6721 */
ef416fc2 6722
b423cd4c 6723 compression = CUPS_FILE_NONE;
ef416fc2 6724
b423cd4c 6725 if ((attr = ippFindAttribute(con->request, "compression",
6726 IPP_TAG_KEYWORD)) != NULL)
6727 {
6728 if (strcmp(attr->values[0].string.text, "none")
6729#ifdef HAVE_LIBZ
6730 && strcmp(attr->values[0].string.text, "gzip")
6731#endif /* HAVE_LIBZ */
6732 )
6733 {
6734 send_ipp_status(con, IPP_ATTRIBUTES,
6735 _("Unsupported compression \"%s\"!"),
6736 attr->values[0].string.text);
6737 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
6738 "compression", NULL, attr->values[0].string.text);
6739 return;
6740 }
ef416fc2 6741
b423cd4c 6742#ifdef HAVE_LIBZ
6743 if (!strcmp(attr->values[0].string.text, "gzip"))
6744 compression = CUPS_FILE_GZIP;
6745#endif /* HAVE_LIBZ */
6746 }
ef416fc2 6747
b423cd4c 6748 /*
6749 * Do we have a file to print?
6750 */
ef416fc2 6751
b423cd4c 6752 if (!con->filename)
6753 {
6754 send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?!"));
6755 return;
6756 }
ef416fc2 6757
b423cd4c 6758 /*
6759 * Is it a format we support?
6760 */
ef416fc2 6761
b423cd4c 6762 if ((format = ippFindAttribute(con->request, "document-format",
6763 IPP_TAG_MIMETYPE)) != NULL)
6764 {
6765 /*
6766 * Grab format from client...
6767 */
6768
f7faf1f5 6769 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
b423cd4c 6770 {
6771 send_ipp_status(con, IPP_BAD_REQUEST,
6772 _("Could not scan type \"%s\"!"),
6773 format->values[0].string.text);
6774 return;
ef416fc2 6775 }
b423cd4c 6776 }
6777 else
6778 {
6779 /*
f7faf1f5 6780 * No document format attribute? Auto-type it!
b423cd4c 6781 */
6782
6783 strcpy(super, "application");
6784 strcpy(type, "octet-stream");
6785 }
ef416fc2 6786
b423cd4c 6787 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
6788 {
ef416fc2 6789 /*
b423cd4c 6790 * Auto-type the file...
ef416fc2 6791 */
6792
b423cd4c 6793 ipp_attribute_t *doc_name; /* document-name attribute */
6794
6795
6796 cupsdLogMessage(CUPSD_LOG_DEBUG, "print_job: auto-typing file...");
6797
6798 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
6799 filetype = mimeFileType(MimeDatabase, con->filename,
6800 doc_name ? doc_name->values[0].string.text : NULL,
6801 &compression);
6802
f7faf1f5 6803 if (filetype)
6804 {
6805 /*
6806 * Replace the document-format attribute value with the auto-typed one.
6807 */
ef416fc2 6808
f7faf1f5 6809 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
6810 filetype->type);
b423cd4c 6811
f7faf1f5 6812 if (format)
6813 {
6814 _cupsStrFree(format->values[0].string.text);
ed486911 6815
f7faf1f5 6816 format->values[0].string.text = _cupsStrAlloc(mimetype);
6817 }
6818 else
6819 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
6820 "document-format", NULL, mimetype);
ef416fc2 6821 }
b423cd4c 6822 else
f7faf1f5 6823 filetype = mimeType(MimeDatabase, super, type);
ef416fc2 6824 }
f7faf1f5 6825 else
6826 filetype = mimeType(MimeDatabase, super, type);
6827
6828 if (!filetype)
b423cd4c 6829 {
6830 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
6831 _("Unsupported format \'%s/%s\'!"), super, type);
6832 cupsdLogMessage(CUPSD_LOG_INFO,
6833 "Hint: Do you have the raw file printing rules enabled?");
6834
6835 if (format)
6836 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
6837 "document-format", NULL, format->values[0].string.text);
6838
6839 return;
6840 }
6841
6842 cupsdLogMessage(CUPSD_LOG_DEBUG, "print_job: request file type is %s/%s.",
80ca4592 6843 filetype->super, filetype->type);
b423cd4c 6844
6845 /*
6846 * Read any embedded job ticket info from PS files...
6847 */
6848
6849 if (!strcasecmp(filetype->super, "application") &&
6850 !strcasecmp(filetype->type, "postscript"))
6851 read_ps_job_ticket(con);
6852
6853 /*
6854 * Create the job object...
6855 */
6856
f7faf1f5 6857 if ((job = add_job(con, uri, &printer, filetype)) == NULL)
b423cd4c 6858 return;
6859
6860 /*
6861 * Update quota data...
6862 */
6863
6864 if (stat(con->filename, &fileinfo))
6865 kbytes = 0;
6866 else
6867 kbytes = (fileinfo.st_size + 1023) / 1024;
6868
6869 cupsdUpdateQuota(printer, job->username, 0, kbytes);
6870
6871 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
6872 IPP_TAG_INTEGER)) != NULL)
6873 attr->values[0].integer += kbytes;
6874
ef416fc2 6875 /*
6876 * Add the job file...
6877 */
6878
6879 if (add_file(con, job, filetype, compression))
6880 return;
6881
6882 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
6883 job->num_files);
6884 rename(con->filename, filename);
6885 cupsdClearString(&con->filename);
6886
6887 /*
6888 * See if we need to add the ending sheet...
6889 */
6890
f301802f 6891 attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
6892
ef416fc2 6893 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
f301802f 6894 attr && attr->num_values > 1)
ef416fc2 6895 {
6896 /*
6897 * Yes...
6898 */
6899
6900 cupsdLogMessage(CUPSD_LOG_INFO, "Adding end banner page \"%s\" to job %d.",
6901 attr->values[1].string.text, job->id);
6902
6903 kbytes = copy_banner(con, job, attr->values[1].string.text);
6904
6905 cupsdUpdateQuota(printer, job->username, 0, kbytes);
6906 }
6907
ef416fc2 6908 /*
6909 * Log and save the job...
6910 */
6911
6912 cupsdLogMessage(CUPSD_LOG_INFO, "Job %d queued on \"%s\" by \"%s\".", job->id,
6913 job->dest, job->username);
6914 cupsdLogMessage(CUPSD_LOG_DEBUG, "Job %d hold_until = %d", job->id,
6915 (int)job->hold_until);
6916
6917 cupsdSaveJob(job);
6918
ef416fc2 6919 /*
6920 * Start the job if possible...
6921 */
6922
6923 cupsdCheckJobs();
6924}
6925
6926
6927/*
6928 * 'read_ps_job_ticket()' - Reads a job ticket embedded in a PS file.
6929 *
6930 * This function only gets called when printing a single PostScript
6931 * file using the Print-Job operation. It doesn't work for Create-Job +
6932 * Send-File, since the job attributes need to be set at job creation
6933 * time for banners to work. The embedded PS job ticket stuff is here
6934 * only to allow the Windows printer driver for CUPS to pass in JCL
6935 * options and IPP attributes which otherwise would be lost.
6936 *
6937 * The format of a PS job ticket is simple:
6938 *
6939 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
6940 *
6941 * %cupsJobTicket: attr1=value1
6942 * %cupsJobTicket: attr2=value2
6943 * ...
6944 * %cupsJobTicket: attrN=valueN
6945 *
6946 * Job ticket lines must appear immediately after the first line that
6947 * specifies PostScript format (%!PS-Adobe-3.0), and CUPS will stop
6948 * looking for job ticket info when it finds a line that does not begin
6949 * with "%cupsJobTicket:".
6950 *
6951 * The maximum length of a job ticket line, including the prefix, is
6952 * 255 characters to conform with the Adobe DSC.
6953 *
6954 * Read-only attributes are rejected with a notice to the error log in
6955 * case a malicious user tries anything. Since the job ticket is read
6956 * prior to attribute validation in print_job(), job ticket attributes
6957 * will go through the same validation as IPP attributes...
6958 */
6959
6960static void
6961read_ps_job_ticket(cupsd_client_t *con) /* I - Client connection */
6962{
6963 cups_file_t *fp; /* File to read from */
6964 char line[256]; /* Line data */
6965 int num_options; /* Number of options */
6966 cups_option_t *options; /* Options */
6967 ipp_t *ticket; /* New attributes */
6968 ipp_attribute_t *attr, /* Current attribute */
6969 *attr2, /* Job attribute */
6970 *prev2; /* Previous job attribute */
6971
6972
6973 /*
6974 * First open the print file...
6975 */
6976
6977 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
6978 {
6979 cupsdLogMessage(CUPSD_LOG_ERROR,
6980 "read_ps_job_ticket: Unable to open PostScript print file "
6981 "- %s",
6982 strerror(errno));
6983 return;
6984 }
6985
6986 /*
6987 * Skip the first line...
6988 */
6989
6990 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
6991 {
6992 cupsdLogMessage(CUPSD_LOG_ERROR,
6993 "read_ps_job_ticket: Unable to read from PostScript print "
6994 "file - %s",
6995 strerror(errno));
6996 cupsFileClose(fp);
6997 return;
6998 }
6999
fa73b229 7000 if (strncmp(line, "%!PS-Adobe-", 11))
ef416fc2 7001 {
7002 /*
7003 * Not a DSC-compliant file, so no job ticket info will be available...
7004 */
7005
7006 cupsFileClose(fp);
7007 return;
7008 }
7009
7010 /*
7011 * Read job ticket info from the file...
7012 */
7013
7014 num_options = 0;
7015 options = NULL;
7016
fa73b229 7017 while (cupsFileGets(fp, line, sizeof(line)))
ef416fc2 7018 {
7019 /*
7020 * Stop at the first non-ticket line...
7021 */
7022
fa73b229 7023 if (strncmp(line, "%cupsJobTicket:", 15))
ef416fc2 7024 break;
7025
7026 /*
7027 * Add the options to the option array...
7028 */
7029
7030 num_options = cupsParseOptions(line + 15, num_options, &options);
7031 }
7032
7033 /*
7034 * Done with the file; see if we have any options...
7035 */
7036
7037 cupsFileClose(fp);
7038
7039 if (num_options == 0)
7040 return;
7041
7042 /*
7043 * OK, convert the options to an attribute list, and apply them to
7044 * the request...
7045 */
7046
7047 ticket = ippNew();
7048 cupsEncodeOptions(ticket, num_options, options);
7049
7050 /*
7051 * See what the user wants to change.
7052 */
7053
fa73b229 7054 for (attr = ticket->attrs; attr; attr = attr->next)
ef416fc2 7055 {
7056 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
7057 continue;
7058
7059 if (!strcmp(attr->name, "job-originating-host-name") ||
7060 !strcmp(attr->name, "job-originating-user-name") ||
7061 !strcmp(attr->name, "job-media-sheets-completed") ||
7062 !strcmp(attr->name, "job-k-octets") ||
7063 !strcmp(attr->name, "job-id") ||
7064 !strncmp(attr->name, "job-state", 9) ||
7065 !strncmp(attr->name, "time-at-", 8))
7066 continue; /* Read-only attrs */
7067
fa73b229 7068 if ((attr2 = ippFindAttribute(con->request, attr->name,
7069 IPP_TAG_ZERO)) != NULL)
ef416fc2 7070 {
7071 /*
7072 * Some other value; first free the old value...
7073 */
7074
7075 if (con->request->attrs == attr2)
7076 {
7077 con->request->attrs = attr2->next;
7078 prev2 = NULL;
7079 }
7080 else
7081 {
fa73b229 7082 for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
ef416fc2 7083 if (prev2->next == attr2)
7084 {
7085 prev2->next = attr2->next;
7086 break;
7087 }
7088 }
7089
7090 if (con->request->last == attr2)
7091 con->request->last = prev2;
7092
757d2cad 7093 _ippFreeAttr(attr2);
ef416fc2 7094 }
7095
7096 /*
7097 * Add new option by copying it...
7098 */
7099
7100 copy_attribute(con->request, attr, 0);
7101 }
7102
7103 /*
7104 * Then free the attribute list and option array...
7105 */
7106
7107 ippDelete(ticket);
7108 cupsFreeOptions(num_options, options);
7109}
7110
7111
7112/*
7113 * 'reject_jobs()' - Reject print jobs to a printer.
7114 */
7115
7116static void
7117reject_jobs(cupsd_client_t *con, /* I - Client connection */
7118 ipp_attribute_t *uri) /* I - Printer or class URI */
7119{
7120 http_status_t status; /* Policy status */
7121 cups_ptype_t dtype; /* Destination type (printer or class) */
f7faf1f5 7122 char method[HTTP_MAX_URI], /* Method portion of URI */
7123 username[HTTP_MAX_URI], /* Username portion of URI */
7124 host[HTTP_MAX_URI], /* Host portion of URI */
7125 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7126 int port; /* Port portion of URI */
7127 const char *name; /* Printer name */
ef416fc2 7128 cupsd_printer_t *printer; /* Printer data */
7129 ipp_attribute_t *attr; /* printer-state-message text */
7130
7131
7132 cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", con,
7133 con->http.fd, uri->values[0].string.text);
7134
7135 /*
7136 * Is the destination valid?
7137 */
7138
f7faf1f5 7139 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
7140 sizeof(method), username, sizeof(username), host,
7141 sizeof(host), &port, resource, sizeof(resource));
7142
7143 if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
ef416fc2 7144 {
7145 /*
7146 * Bad URI...
7147 */
7148
7149 send_ipp_status(con, IPP_NOT_FOUND,
7150 _("The printer or class was not found."));
7151 return;
7152 }
7153
7154 /*
7155 * Check policy...
7156 */
7157
7158 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7159 {
7160 send_http_error(con, status);
7161 return;
7162 }
7163
7164 /*
7165 * Reject jobs sent to the printer...
7166 */
7167
7168 printer->accepting = 0;
7169
7170 if ((attr = ippFindAttribute(con->request, "printer-state-message",
7171 IPP_TAG_TEXT)) == NULL)
7172 strcpy(printer->state_message, "Rejecting Jobs");
7173 else
7174 strlcpy(printer->state_message, attr->values[0].string.text,
7175 sizeof(printer->state_message));
7176
7177 cupsdAddPrinterHistory(printer);
7178
7179 if (dtype & CUPS_PRINTER_CLASS)
7180 {
7181 cupsdSaveAllClasses();
7182
7183 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
f7faf1f5 7184 name, get_username(con));
ef416fc2 7185 }
7186 else
7187 {
7188 cupsdSaveAllPrinters();
7189
7190 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
f7faf1f5 7191 name, get_username(con));
ef416fc2 7192 }
7193
7194 /*
7195 * Everything was ok, so return OK status...
7196 */
7197
7198 con->response->request.status.status_code = IPP_OK;
7199}
7200
7201
7202/*
7203 * 'release_job()' - Release a held print job.
7204 */
7205
7206static void
7207release_job(cupsd_client_t *con, /* I - Client connection */
7208 ipp_attribute_t *uri) /* I - Job or Printer URI */
7209{
7210 ipp_attribute_t *attr; /* Current attribute */
7211 int jobid; /* Job ID */
7212 char method[HTTP_MAX_URI], /* Method portion of URI */
7213 username[HTTP_MAX_URI], /* Username portion of URI */
7214 host[HTTP_MAX_URI], /* Host portion of URI */
7215 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7216 int port; /* Port portion of URI */
7217 cupsd_job_t *job; /* Job information */
7218
7219
7220 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", con,
7221 con->http.fd, uri->values[0].string.text);
7222
7223 /*
7224 * See if we have a job URI or a printer URI...
7225 */
7226
7227 if (!strcmp(uri->name, "printer-uri"))
7228 {
7229 /*
7230 * Got a printer URI; see if we also have a job-id attribute...
7231 */
7232
fa73b229 7233 if ((attr = ippFindAttribute(con->request, "job-id",
7234 IPP_TAG_INTEGER)) == NULL)
ef416fc2 7235 {
7236 send_ipp_status(con, IPP_BAD_REQUEST,
7237 _("Got a printer-uri attribute but no job-id!"));
7238 return;
7239 }
7240
7241 jobid = attr->values[0].integer;
7242 }
7243 else
7244 {
7245 /*
7246 * Got a job URI; parse it to get the job ID...
7247 */
7248
a4d04587 7249 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
7250 sizeof(method), username, sizeof(username), host,
7251 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 7252
7253 if (strncmp(resource, "/jobs/", 6))
7254 {
7255 /*
7256 * Not a valid URI!
7257 */
7258
7259 send_ipp_status(con, IPP_BAD_REQUEST,
7260 _("Bad job-uri attribute \"%s\"!"),
7261 uri->values[0].string.text);
7262 return;
7263 }
7264
7265 jobid = atoi(resource + 6);
7266 }
7267
7268 /*
7269 * See if the job exists...
7270 */
7271
7272 if ((job = cupsdFindJob(jobid)) == NULL)
7273 {
7274 /*
7275 * Nope - return a "not found" error...
7276 */
7277
7278 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
7279 return;
7280 }
7281
7282 /*
7283 * See if job is "held"...
7284 */
7285
bd7854cb 7286 if (job->state_value != IPP_JOB_HELD)
ef416fc2 7287 {
7288 /*
7289 * Nope - return a "not possible" error...
7290 */
7291
7292 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held!"), jobid);
7293 return;
7294 }
7295
7296 /*
7297 * See if the job is owned by the requesting user...
7298 */
7299
7300 if (!validate_user(job, con, job->username, username, sizeof(username)))
7301 {
bd7854cb 7302 send_http_error(con, HTTP_UNAUTHORIZED);
ef416fc2 7303 return;
7304 }
7305
7306 /*
7307 * Reset the job-hold-until value to "no-hold"...
7308 */
7309
fa73b229 7310 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
7311 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 7312 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
7313
fa73b229 7314 if (attr)
ef416fc2 7315 {
757d2cad 7316 _cupsStrFree(attr->values[0].string.text);
4400e98d 7317
ef416fc2 7318 attr->value_tag = IPP_TAG_KEYWORD;
757d2cad 7319 attr->values[0].string.text = _cupsStrAlloc("no-hold");
d09495fa 7320
7321 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
7322 "Job job-hold-until value changed by user.");
ef416fc2 7323 }
7324
7325 /*
7326 * Release the job and return...
7327 */
7328
7329 cupsdReleaseJob(job);
7330
d09495fa 7331 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
7332 "Job released by user.");
7333
ef416fc2 7334 cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was released by \"%s\".", jobid,
7335 username);
7336
7337 con->response->request.status.status_code = IPP_OK;
7338}
7339
7340
7341/*
7342 * 'renew_subscription()' - Renew an existing subscription...
7343 */
7344
7345static void
7346renew_subscription(
7347 cupsd_client_t *con, /* I - Client connection */
7348 int sub_id) /* I - Subscription ID */
7349{
bd7854cb 7350 http_status_t status; /* Policy status */
7351 cupsd_subscription_t *sub; /* Subscription */
7352 ipp_attribute_t *lease; /* notify-lease-duration */
7353
7354
7355 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7356 "renew_subscription(con=%p[%d], sub_id=%d)",
7357 con, con->http.fd, sub_id);
7358
7359 /*
7360 * Is the subscription ID valid?
7361 */
7362
7363 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
7364 {
7365 /*
7366 * Bad subscription ID...
7367 */
7368
7369 send_ipp_status(con, IPP_NOT_FOUND,
7370 _("notify-subscription-id %d no good!"), sub_id);
7371 return;
7372 }
7373
7374 if (sub->job)
7375 {
7376 /*
7377 * Job subscriptions cannot be renewed...
7378 */
7379
7380 send_ipp_status(con, IPP_NOT_POSSIBLE,
7381 _("Job subscriptions cannot be renewed!"));
7382 return;
7383 }
7384
7385 /*
7386 * Check policy...
7387 */
7388
7389 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
7390 DefaultPolicyPtr,
7391 con, sub->owner)) != HTTP_OK)
7392 {
7393 send_http_error(con, status);
7394 return;
7395 }
7396
7397 /*
7398 * Renew the subscription...
7399 */
7400
7401 lease = ippFindAttribute(con->request, "notify-lease-duration",
7402 IPP_TAG_INTEGER);
7403
7404 sub->lease = lease ? lease->values[0].integer : DefaultLeaseDuration;
7405
7406 if (MaxLeaseDuration && (sub->lease == 0 || sub->lease > MaxLeaseDuration))
7407 {
7408 cupsdLogMessage(CUPSD_LOG_INFO,
7409 "renew_subscription: Limiting notify-lease-duration to "
7410 "%d seconds.",
7411 MaxLeaseDuration);
7412 sub->lease = MaxLeaseDuration;
7413 }
7414
7415 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
7416
7417 cupsdSaveAllSubscriptions();
7418
7419 con->response->request.status.status_code = IPP_OK;
ef416fc2 7420}
7421
7422
7423/*
7424 * 'restart_job()' - Restart an old print job.
7425 */
7426
7427static void
7428restart_job(cupsd_client_t *con, /* I - Client connection */
7429 ipp_attribute_t *uri) /* I - Job or Printer URI */
7430{
7431 ipp_attribute_t *attr; /* Current attribute */
7432 int jobid; /* Job ID */
7433 char method[HTTP_MAX_URI], /* Method portion of URI */
7434 username[HTTP_MAX_URI], /* Username portion of URI */
7435 host[HTTP_MAX_URI], /* Host portion of URI */
7436 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7437 int port; /* Port portion of URI */
7438 cupsd_job_t *job; /* Job information */
7439
7440
7441 cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con,
7442 con->http.fd, uri->values[0].string.text);
7443
7444 /*
7445 * See if we have a job URI or a printer URI...
7446 */
7447
7448 if (!strcmp(uri->name, "printer-uri"))
7449 {
7450 /*
7451 * Got a printer URI; see if we also have a job-id attribute...
7452 */
7453
fa73b229 7454 if ((attr = ippFindAttribute(con->request, "job-id",
7455 IPP_TAG_INTEGER)) == NULL)
ef416fc2 7456 {
7457 send_ipp_status(con, IPP_BAD_REQUEST,
7458 _("Got a printer-uri attribute but no job-id!"));
7459 return;
7460 }
7461
7462 jobid = attr->values[0].integer;
7463 }
7464 else
7465 {
7466 /*
7467 * Got a job URI; parse it to get the job ID...
7468 */
7469
a4d04587 7470 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
7471 sizeof(method), username, sizeof(username), host,
7472 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 7473
fa73b229 7474 if (strncmp(resource, "/jobs/", 6))
ef416fc2 7475 {
7476 /*
7477 * Not a valid URI!
7478 */
7479
7480 send_ipp_status(con, IPP_BAD_REQUEST,
7481 _("Bad job-uri attribute \"%s\"!"),
7482 uri->values[0].string.text);
7483 return;
7484 }
7485
7486 jobid = atoi(resource + 6);
7487 }
7488
7489 /*
7490 * See if the job exists...
7491 */
7492
7493 if ((job = cupsdFindJob(jobid)) == NULL)
7494 {
7495 /*
7496 * Nope - return a "not found" error...
7497 */
7498
7499 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
7500 return;
7501 }
7502
7503 /*
7504 * See if job is in any of the "completed" states...
7505 */
7506
bd7854cb 7507 if (job->state_value <= IPP_JOB_PROCESSING)
ef416fc2 7508 {
7509 /*
7510 * Nope - return a "not possible" error...
7511 */
7512
7513 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete!"),
7514 jobid);
7515 return;
7516 }
7517
7518 /*
7519 * See if we have retained the job files...
7520 */
7521
bd7854cb 7522 cupsdLoadJob(job);
7523
7524 if (!job->attrs ||job->num_files == 0)
ef416fc2 7525 {
7526 /*
7527 * Nope - return a "not possible" error...
7528 */
7529
7530 send_ipp_status(con, IPP_NOT_POSSIBLE,
7531 _("Job #%d cannot be restarted - no files!"), jobid);
7532 return;
7533 }
7534
7535 /*
7536 * See if the job is owned by the requesting user...
7537 */
7538
7539 if (!validate_user(job, con, job->username, username, sizeof(username)))
7540 {
bd7854cb 7541 send_http_error(con, HTTP_UNAUTHORIZED);
ef416fc2 7542 return;
7543 }
7544
7545 /*
7546 * Restart the job and return...
7547 */
7548
7549 cupsdRestartJob(job);
7550
7551 cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was restarted by \"%s\".", jobid,
7552 username);
7553
7554 con->response->request.status.status_code = IPP_OK;
7555}
7556
7557
7558/*
7559 * 'save_auth_info()' - Save authentication information for a job.
7560 */
7561
7562static void
7563save_auth_info(cupsd_client_t *con, /* I - Client connection */
7564 cupsd_job_t *job) /* I - Job */
7565{
7566 int i; /* Looping var */
7567 char filename[1024]; /* Job authentication filename */
7568 cups_file_t *fp; /* Job authentication file */
7569 char line[1024]; /* Line for file */
7570
7571
7572 /*
7573 * This function saves the in-memory authentication information for
7574 * a job so that it can be used to authenticate with a remote host.
7575 * The information is stored in a file that is readable only by the
7576 * root user. The username and password are Base-64 encoded, each
7577 * on a separate line, followed by random number (up to 1024) of
7578 * newlines to limit the amount of information that is exposed.
7579 *
7580 * Because of the potential for exposing of authentication information,
7581 * this functionality is only enabled when running cupsd as root.
7582 *
7583 * This caching only works for the Basic and BasicDigest authentication
7584 * types. Digest authentication cannot be cached this way, and in
7585 * the future Kerberos authentication may make all of this obsolete.
7586 *
7587 * Authentication information is saved whenever an authenticated
7588 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
7589 * performed.
7590 *
7591 * This information is deleted after a job is completed or canceled,
7592 * so reprints may require subsequent re-authentication.
7593 */
7594
7595 if (RunUser)
7596 return;
7597
7598 /*
7599 * Create the authentication file and change permissions...
7600 */
7601
7602 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
7603 if ((fp = cupsFileOpen(filename, "w")) == NULL)
7604 {
7605 cupsdLogMessage(CUPSD_LOG_ERROR,
7606 "Unable to save authentication info to \"%s\" - %s",
7607 filename, strerror(errno));
7608 return;
7609 }
7610
7611 fchown(cupsFileNumber(fp), 0, 0);
7612 fchmod(cupsFileNumber(fp), 0400);
7613
7614 /*
7615 * Write the authenticated username...
7616 */
7617
7618 httpEncode64_2(line, sizeof(line), con->username, strlen(con->username));
7619 cupsFilePrintf(fp, "%s\n", line);
7620
7621 /*
7622 * Write the authenticated password...
7623 */
7624
7625 httpEncode64_2(line, sizeof(line), con->password, strlen(con->password));
7626 cupsFilePrintf(fp, "%s\n", line);
7627
7628 /*
7629 * Write a random number of newlines to the end of the file...
7630 */
7631
7632 for (i = (rand() % 1024); i >= 0; i --)
7633 cupsFilePutChar(fp, '\n');
7634
7635 /*
7636 * Close the file and return...
7637 */
7638
7639 cupsFileClose(fp);
7640}
7641
7642
7643/*
7644 * 'send_document()' - Send a file to a printer or class.
7645 */
7646
7647static void
7648send_document(cupsd_client_t *con, /* I - Client connection */
7649 ipp_attribute_t *uri) /* I - Printer URI */
7650{
7651 ipp_attribute_t *attr; /* Current attribute */
7652 ipp_attribute_t *format; /* Document-format attribute */
7653 int jobid; /* Job ID number */
7654 cupsd_job_t *job; /* Current job */
7655 char job_uri[HTTP_MAX_URI],
7656 /* Job URI */
7657 method[HTTP_MAX_URI],
7658 /* Method portion of URI */
7659 username[HTTP_MAX_URI],
7660 /* Username portion of URI */
7661 host[HTTP_MAX_URI],
7662 /* Host portion of URI */
7663 resource[HTTP_MAX_URI];
7664 /* Resource portion of URI */
7665 int port; /* Port portion of URI */
7666 mime_type_t *filetype; /* Type of file */
7667 char super[MIME_MAX_SUPER],
7668 /* Supertype of file */
7669 type[MIME_MAX_TYPE],
7670 /* Subtype of file */
7671 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
7672 /* Textual name of mime type */
7673 char filename[1024]; /* Job filename */
7674 cupsd_printer_t *printer; /* Current printer */
7675 struct stat fileinfo; /* File information */
7676 int kbytes; /* Size of file */
7677 int compression; /* Type of compression */
7678
7679
7680 cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con,
7681 con->http.fd, uri->values[0].string.text);
7682
7683 /*
7684 * See if we have a job URI or a printer URI...
7685 */
7686
7687 if (!strcmp(uri->name, "printer-uri"))
7688 {
7689 /*
7690 * Got a printer URI; see if we also have a job-id attribute...
7691 */
7692
fa73b229 7693 if ((attr = ippFindAttribute(con->request, "job-id",
7694 IPP_TAG_INTEGER)) == NULL)
ef416fc2 7695 {
7696 send_ipp_status(con, IPP_BAD_REQUEST,
7697 _("Got a printer-uri attribute but no job-id!"));
7698 return;
7699 }
7700
7701 jobid = attr->values[0].integer;
7702 }
7703 else
7704 {
7705 /*
7706 * Got a job URI; parse it to get the job ID...
7707 */
7708
a4d04587 7709 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
7710 sizeof(method), username, sizeof(username), host,
7711 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 7712
7713 if (strncmp(resource, "/jobs/", 6))
7714 {
7715 /*
7716 * Not a valid URI!
7717 */
7718
7719 send_ipp_status(con, IPP_BAD_REQUEST,
7720 _("Bad job-uri attribute \"%s\"!"),
7721 uri->values[0].string.text);
7722 return;
7723 }
7724
7725 jobid = atoi(resource + 6);
7726 }
7727
7728 /*
7729 * See if the job exists...
7730 */
7731
7732 if ((job = cupsdFindJob(jobid)) == NULL)
7733 {
7734 /*
7735 * Nope - return a "not found" error...
7736 */
7737
7738 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
7739 return;
7740 }
7741
f301802f 7742 printer = cupsdFindDest(job->dest);
7743
ef416fc2 7744 /*
7745 * See if the job is owned by the requesting user...
7746 */
7747
7748 if (!validate_user(job, con, job->username, username, sizeof(username)))
7749 {
bd7854cb 7750 send_http_error(con, HTTP_UNAUTHORIZED);
ef416fc2 7751 return;
7752 }
7753
7754 /*
7755 * OK, see if the client is sending the document compressed - CUPS
7756 * only supports "none" and "gzip".
7757 */
7758
7759 compression = CUPS_FILE_NONE;
7760
fa73b229 7761 if ((attr = ippFindAttribute(con->request, "compression",
7762 IPP_TAG_KEYWORD)) != NULL)
ef416fc2 7763 {
7764 if (strcmp(attr->values[0].string.text, "none")
7765#ifdef HAVE_LIBZ
7766 && strcmp(attr->values[0].string.text, "gzip")
7767#endif /* HAVE_LIBZ */
7768 )
7769 {
7770 send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\"!"),
7771 attr->values[0].string.text);
7772 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
7773 "compression", NULL, attr->values[0].string.text);
7774 return;
7775 }
7776
7777#ifdef HAVE_LIBZ
7778 if (!strcmp(attr->values[0].string.text, "gzip"))
7779 compression = CUPS_FILE_GZIP;
7780#endif /* HAVE_LIBZ */
7781 }
7782
7783 /*
7784 * Do we have a file to print?
7785 */
7786
7787 if (!con->filename)
7788 {
7789 send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?!"));
7790 return;
7791 }
7792
7793 /*
7794 * Is it a format we support?
7795 */
7796
7797 if ((format = ippFindAttribute(con->request, "document-format",
7798 IPP_TAG_MIMETYPE)) != NULL)
7799 {
7800 /*
7801 * Grab format from client...
7802 */
7803
7804 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
7805 {
7806 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"!"),
7807 format->values[0].string.text);
7808 return;
7809 }
7810 }
7811 else
7812 {
7813 /*
7814 * No document format attribute? Auto-type it!
7815 */
7816
7817 strcpy(super, "application");
7818 strcpy(type, "octet-stream");
7819 }
7820
fa73b229 7821 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
ef416fc2 7822 {
7823 /*
7824 * Auto-type the file...
7825 */
7826
bd7854cb 7827 ipp_attribute_t *doc_name; /* document-name attribute */
7828
7829
ef416fc2 7830 cupsdLogMessage(CUPSD_LOG_DEBUG, "send_document: auto-typing file...");
7831
bd7854cb 7832 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
7833 filetype = mimeFileType(MimeDatabase, con->filename,
7834 doc_name ? doc_name->values[0].string.text : NULL,
7835 &compression);
ef416fc2 7836
f7faf1f5 7837 if (filetype)
ed486911 7838 {
f7faf1f5 7839 /*
7840 * Replace the document-format attribute value with the auto-typed one.
7841 */
ed486911 7842
f7faf1f5 7843 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
7844 filetype->type);
7845
7846 if (format)
7847 {
7848 _cupsStrFree(format->values[0].string.text);
7849 format->values[0].string.text = _cupsStrAlloc(mimetype);
7850 }
7851 else
7852 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
7853 "document-format", NULL, mimetype);
ed486911 7854 }
7855 else
f7faf1f5 7856 filetype = mimeType(MimeDatabase, super, type);
ed486911 7857 }
f7faf1f5 7858 else
7859 filetype = mimeType(MimeDatabase, super, type);
7860
7861 if (!filetype)
ef416fc2 7862 {
7863 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
7864 _("Unsupported format \'%s/%s\'!"), super, type);
7865 cupsdLogMessage(CUPSD_LOG_INFO,
7866 "Hint: Do you have the raw file printing rules enabled?");
7867
7868 if (format)
7869 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
7870 "document-format", NULL, format->values[0].string.text);
7871
7872 return;
7873 }
7874
80ca4592 7875 if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
7876 {
7877 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
7878 filetype->type);
7879
7880 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
7881 _("Unsupported format \'%s\'!"), mimetype);
7882
7883 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
7884 "document-format", NULL, mimetype);
7885
7886 return;
7887 }
7888
ef416fc2 7889 cupsdLogMessage(CUPSD_LOG_DEBUG,
7890 "send_document: request file type is %s/%s.",
7891 filetype->super, filetype->type);
7892
7893 /*
7894 * Add the file to the job...
7895 */
7896
bd7854cb 7897 cupsdLoadJob(job);
7898
ef416fc2 7899 if (add_file(con, job, filetype, compression))
7900 return;
7901
ef416fc2 7902 if (stat(con->filename, &fileinfo))
7903 kbytes = 0;
7904 else
7905 kbytes = (fileinfo.st_size + 1023) / 1024;
7906
7907 cupsdUpdateQuota(printer, job->username, 0, kbytes);
7908
fa73b229 7909 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
7910 IPP_TAG_INTEGER)) != NULL)
ef416fc2 7911 attr->values[0].integer += kbytes;
7912
7913 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
7914 job->num_files);
7915 rename(con->filename, filename);
7916
7917 cupsdClearString(&con->filename);
7918
7919 cupsdLogMessage(CUPSD_LOG_INFO,
7920 "File of type %s/%s queued in job #%d by \"%s\".",
7921 filetype->super, filetype->type, job->id, job->username);
7922
7923 /*
7924 * Start the job if this is the last document...
7925 */
7926
fa73b229 7927 if ((attr = ippFindAttribute(con->request, "last-document",
7928 IPP_TAG_BOOLEAN)) != NULL &&
ef416fc2 7929 attr->values[0].boolean)
7930 {
7931 /*
7932 * See if we need to add the ending sheet...
7933 */
7934
fa73b229 7935 if (printer &&
ef416fc2 7936 !(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
fa73b229 7937 (attr = ippFindAttribute(job->attrs, "job-sheets",
7938 IPP_TAG_ZERO)) != NULL &&
ef416fc2 7939 attr->num_values > 1)
7940 {
7941 /*
7942 * Yes...
7943 */
7944
7945 cupsdLogMessage(CUPSD_LOG_INFO,
7946 "Adding end banner page \"%s\" to job %d.",
7947 attr->values[1].string.text, job->id);
7948
7949 kbytes = copy_banner(con, job, attr->values[1].string.text);
7950
7951 cupsdUpdateQuota(printer, job->username, 0, kbytes);
7952 }
7953
bd7854cb 7954 if (job->state_value == IPP_JOB_STOPPED)
7955 {
ef416fc2 7956 job->state->values[0].integer = IPP_JOB_PENDING;
bd7854cb 7957 job->state_value = IPP_JOB_PENDING;
7958 }
7959 else if (job->state_value == IPP_JOB_HELD)
ef416fc2 7960 {
fa73b229 7961 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
7962 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 7963 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
7964
fa73b229 7965 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
bd7854cb 7966 {
ef416fc2 7967 job->state->values[0].integer = IPP_JOB_PENDING;
bd7854cb 7968 job->state_value = IPP_JOB_PENDING;
7969 }
ef416fc2 7970 }
7971
7972 cupsdSaveJob(job);
7973
7974 /*
7975 * Start the job if possible... Since cupsdCheckJobs() can cancel a
7976 * job if it doesn't print, we need to re-find the job afterwards...
7977 */
7978
7979 jobid = job->id;
7980
7981 cupsdCheckJobs();
7982
7983 job = cupsdFindJob(jobid);
7984 }
7985 else
7986 {
fa73b229 7987 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
7988 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 7989 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
7990
fa73b229 7991 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
ef416fc2 7992 {
7993 job->state->values[0].integer = IPP_JOB_HELD;
bd7854cb 7994 job->state_value = IPP_JOB_HELD;
ef416fc2 7995 job->hold_until = time(NULL) + 60;
7996 cupsdSaveJob(job);
7997 }
7998 }
7999
8000 /*
8001 * Fill in the response info...
8002 */
8003
8004 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
8005 LocalPort, jobid);
8006
8007 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
8008 job_uri);
8009
8010 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
8011
8012 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
d09495fa 8013 job ? job->state_value : IPP_JOB_CANCELED);
ef416fc2 8014 add_job_state_reasons(con, job);
8015
8016 con->response->request.status.status_code = IPP_OK;
8017}
8018
8019
8020/*
8021 * 'send_http_error()' - Send a HTTP error back to the IPP client.
8022 */
8023
8024static void
8025send_http_error(cupsd_client_t *con, /* I - Client connection */
8026 http_status_t status) /* I - HTTP status code */
8027{
8028 cupsdLogMessage(CUPSD_LOG_ERROR, "%s: %s",
8029 ippOpString(con->request->request.op.operation_id),
8030 httpStatus(status));
8031
8032 cupsdSendError(con, status);
8033
8034 ippDelete(con->response);
8035 con->response = NULL;
8036
8037 return;
8038}
8039
8040
8041/*
8042 * 'send_ipp_status()' - Send a status back to the IPP client.
8043 */
8044
8045static void
8046send_ipp_status(cupsd_client_t *con, /* I - Client connection */
8047 ipp_status_t status, /* I - IPP status code */
8048 const char *message, /* I - Status message */
8049 ...) /* I - Additional args as needed */
8050{
8051 va_list ap; /* Pointer to additional args */
8052 char formatted[1024]; /* Formatted errror message */
8053
8054
8055 if (message)
8056 {
8057 va_start(ap, message);
8058 vsnprintf(formatted, sizeof(formatted),
8059 _cupsLangString(con->language, message), ap);
8060 va_end(ap);
8061
d09495fa 8062 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
ef416fc2 8063 ippOpString(con->request->request.op.operation_id),
8064 ippErrorString(status), formatted);
8065 }
8066 else
d09495fa 8067 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s",
ef416fc2 8068 ippOpString(con->request->request.op.operation_id),
8069 ippErrorString(status));
8070
8071 con->response->request.status.status_code = status;
8072
fa73b229 8073 if (ippFindAttribute(con->response, "attributes-charset",
8074 IPP_TAG_ZERO) == NULL)
ef416fc2 8075 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
8076 "attributes-charset", NULL, DefaultCharset);
8077
8078 if (ippFindAttribute(con->response, "attributes-natural-language",
8079 IPP_TAG_ZERO) == NULL)
8080 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
8081 "attributes-natural-language", NULL, DefaultLanguage);
8082
8083 if (message)
8084 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
8085 "status-message", NULL, formatted);
8086}
8087
8088
8089/*
8090 * 'set_default()' - Set the default destination...
8091 */
8092
8093static void
8094set_default(cupsd_client_t *con, /* I - Client connection */
8095 ipp_attribute_t *uri) /* I - Printer URI */
8096{
8097 http_status_t status; /* Policy status */
8098 cups_ptype_t dtype; /* Destination type (printer or class) */
f7faf1f5 8099 char method[HTTP_MAX_URI],
8100 /* Method portion of URI */
8101 username[HTTP_MAX_URI],
8102 /* Username portion of URI */
8103 host[HTTP_MAX_URI],
8104 /* Host portion of URI */
8105 resource[HTTP_MAX_URI];
8106 /* Resource portion of URI */
8107 int port; /* Port portion of URI */
8108 const char *name; /* Printer name */
ef416fc2 8109 cupsd_printer_t *printer; /* Printer */
8110
8111
8112 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con,
8113 con->http.fd, uri->values[0].string.text);
8114
8115 /*
8116 * Is the destination valid?
8117 */
8118
f7faf1f5 8119 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
8120 sizeof(method), username, sizeof(username), host,
8121 sizeof(host), &port, resource, sizeof(resource));
8122
8123 if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
ef416fc2 8124 {
8125 /*
8126 * Bad URI...
8127 */
8128
8129 send_ipp_status(con, IPP_NOT_FOUND,
8130 _("The printer or class was not found."));
8131 return;
8132 }
8133
8134 /*
8135 * Check policy...
8136 */
8137
8138 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
8139 {
8140 send_http_error(con, status);
8141 return;
8142 }
8143
8144 /*
8145 * Set it as the default...
8146 */
8147
8148 DefaultPrinter = printer;
8149
8150 cupsdSaveAllPrinters();
8151 cupsdSaveAllClasses();
8152
8153 cupsdWritePrintcap();
8154
8155 cupsdLogMessage(CUPSD_LOG_INFO,
f7faf1f5 8156 "Default destination set to \"%s\" by \"%s\".", name,
8157 get_username(con));
ef416fc2 8158
8159 /*
8160 * Everything was ok, so return OK status...
8161 */
8162
8163 con->response->request.status.status_code = IPP_OK;
8164}
8165
8166
8167/*
8168 * 'set_job_attrs()' - Set job attributes.
8169 */
8170
8171static void
8172set_job_attrs(cupsd_client_t *con, /* I - Client connection */
8173 ipp_attribute_t *uri) /* I - Job URI */
8174{
8175 ipp_attribute_t *attr, /* Current attribute */
8176 *attr2; /* Job attribute */
8177 int jobid; /* Job ID */
8178 cupsd_job_t *job; /* Current job */
8179 char method[HTTP_MAX_URI],
8180 /* Method portion of URI */
8181 username[HTTP_MAX_URI],
8182 /* Username portion of URI */
8183 host[HTTP_MAX_URI],
8184 /* Host portion of URI */
8185 resource[HTTP_MAX_URI];
8186 /* Resource portion of URI */
8187 int port; /* Port portion of URI */
d09495fa 8188 int event; /* Events? */
ef416fc2 8189
8190
8191 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
8192 con->http.fd, uri->values[0].string.text);
8193
8194 /*
8195 * Start with "everything is OK" status...
8196 */
8197
8198 con->response->request.status.status_code = IPP_OK;
8199
8200 /*
8201 * See if we have a job URI or a printer URI...
8202 */
8203
fa73b229 8204 if (!strcmp(uri->name, "printer-uri"))
ef416fc2 8205 {
8206 /*
8207 * Got a printer URI; see if we also have a job-id attribute...
8208 */
8209
fa73b229 8210 if ((attr = ippFindAttribute(con->request, "job-id",
8211 IPP_TAG_INTEGER)) == NULL)
ef416fc2 8212 {
8213 send_ipp_status(con, IPP_BAD_REQUEST,
8214 _("Got a printer-uri attribute but no job-id!"));
8215 return;
8216 }
8217
8218 jobid = attr->values[0].integer;
8219 }
8220 else
8221 {
8222 /*
8223 * Got a job URI; parse it to get the job ID...
8224 */
8225
a4d04587 8226 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
8227 sizeof(method), username, sizeof(username), host,
8228 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 8229
fa73b229 8230 if (strncmp(resource, "/jobs/", 6))
ef416fc2 8231 {
8232 /*
8233 * Not a valid URI!
8234 */
8235
8236 send_ipp_status(con, IPP_BAD_REQUEST,
8237 _("Bad job-uri attribute \"%s\"!"),
8238 uri->values[0].string.text);
8239 return;
8240 }
8241
8242 jobid = atoi(resource + 6);
8243 }
8244
8245 /*
8246 * See if the job exists...
8247 */
8248
8249 if ((job = cupsdFindJob(jobid)) == NULL)
8250 {
8251 /*
8252 * Nope - return a "not found" error...
8253 */
8254
8255 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
8256 return;
8257 }
8258
8259 /*
8260 * See if the job has been completed...
8261 */
8262
bd7854cb 8263 if (job->state_value > IPP_JOB_STOPPED)
ef416fc2 8264 {
8265 /*
8266 * Return a "not-possible" error...
8267 */
8268
8269 send_ipp_status(con, IPP_NOT_POSSIBLE,
8270 _("Job #%d is finished and cannot be altered!"), jobid);
8271 return;
8272 }
8273
8274 /*
8275 * See if the job is owned by the requesting user...
8276 */
8277
8278 if (!validate_user(job, con, job->username, username, sizeof(username)))
8279 {
bd7854cb 8280 send_http_error(con, HTTP_UNAUTHORIZED);
ef416fc2 8281 return;
8282 }
8283
8284 /*
8285 * See what the user wants to change.
8286 */
8287
bd7854cb 8288 cupsdLoadJob(job);
8289
d09495fa 8290 event = 0;
8291
fa73b229 8292 for (attr = con->request->attrs; attr; attr = attr->next)
ef416fc2 8293 {
8294 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
8295 continue;
8296
8297 if (!strcmp(attr->name, "attributes-charset") ||
8298 !strcmp(attr->name, "attributes-natural-language") ||
8299 !strcmp(attr->name, "document-compression") ||
8300 !strcmp(attr->name, "document-format") ||
8301 !strcmp(attr->name, "job-detailed-status-messages") ||
8302 !strcmp(attr->name, "job-document-access-errors") ||
8303 !strcmp(attr->name, "job-id") ||
8304 !strcmp(attr->name, "job-k-octets") ||
8305 !strcmp(attr->name, "job-originating-host-name") ||
8306 !strcmp(attr->name, "job-originating-user-name") ||
8307 !strcmp(attr->name, "job-printer-up-time") ||
8308 !strcmp(attr->name, "job-printer-uri") ||
8309 !strcmp(attr->name, "job-sheets") ||
8310 !strcmp(attr->name, "job-state-message") ||
8311 !strcmp(attr->name, "job-state-reasons") ||
8312 !strcmp(attr->name, "job-uri") ||
8313 !strcmp(attr->name, "number-of-documents") ||
8314 !strcmp(attr->name, "number-of-intervening-jobs") ||
8315 !strcmp(attr->name, "output-device-assigned") ||
8316 !strncmp(attr->name, "date-time-at-", 13) ||
8317 !strncmp(attr->name, "job-impressions", 15) ||
8318 !strncmp(attr->name, "job-k-octets", 12) ||
8319 !strncmp(attr->name, "job-media-sheets", 16) ||
8320 !strncmp(attr->name, "time-at-", 8))
8321 {
8322 /*
8323 * Read-only attrs!
8324 */
8325
8326 send_ipp_status(con, IPP_ATTRIBUTES_NOT_SETTABLE,
8327 _("%s cannot be changed."), attr->name);
8328
8329 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
8330 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
8331
8332 continue;
8333 }
8334
8335 if (!strcmp(attr->name, "job-priority"))
8336 {
8337 /*
8338 * Change the job priority...
8339 */
8340
8341 if (attr->value_tag != IPP_TAG_INTEGER)
8342 {
8343 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value!"));
8344
8345 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
8346 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
8347 }
bd7854cb 8348 else if (job->state_value >= IPP_JOB_PROCESSING)
ef416fc2 8349 {
8350 send_ipp_status(con, IPP_NOT_POSSIBLE,
8351 _("Job is completed and cannot be changed."));
8352 return;
8353 }
8354 else if (con->response->request.status.status_code == IPP_OK)
d09495fa 8355 {
ef416fc2 8356 cupsdSetJobPriority(job, attr->values[0].integer);
d09495fa 8357 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
8358 }
ef416fc2 8359 }
8360 else if (!strcmp(attr->name, "job-state"))
8361 {
8362 /*
8363 * Change the job state...
8364 */
8365
8366 if (attr->value_tag != IPP_TAG_ENUM)
8367 {
8368 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value!"));
8369
8370 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
8371 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
8372 }
8373 else
8374 {
8375 switch (attr->values[0].integer)
8376 {
8377 case IPP_JOB_PENDING :
8378 case IPP_JOB_HELD :
bd7854cb 8379 if (job->state_value > IPP_JOB_HELD)
ef416fc2 8380 {
8381 send_ipp_status(con, IPP_NOT_POSSIBLE,
8382 _("Job state cannot be changed."));
8383 return;
8384 }
8385 else if (con->response->request.status.status_code == IPP_OK)
bd7854cb 8386 {
ef416fc2 8387 job->state->values[0].integer = attr->values[0].integer;
d09495fa 8388 job->state_value = (ipp_jstate_t)attr->values[0].integer;
8389
8390 event |= CUPSD_EVENT_JOB_STATE;
bd7854cb 8391 }
ef416fc2 8392 break;
8393
8394 case IPP_JOB_PROCESSING :
8395 case IPP_JOB_STOPPED :
bd7854cb 8396 if (job->state_value != attr->values[0].integer)
ef416fc2 8397 {
8398 send_ipp_status(con, IPP_NOT_POSSIBLE,
8399 _("Job state cannot be changed."));
8400 return;
8401 }
8402 break;
8403
d09495fa 8404 case IPP_JOB_CANCELED :
ef416fc2 8405 case IPP_JOB_ABORTED :
8406 case IPP_JOB_COMPLETED :
bd7854cb 8407 if (job->state_value > IPP_JOB_PROCESSING)
ef416fc2 8408 {
8409 send_ipp_status(con, IPP_NOT_POSSIBLE,
8410 _("Job state cannot be changed."));
8411 return;
8412 }
8413 else if (con->response->request.status.status_code == IPP_OK)
8414 {
8415 cupsdCancelJob(job, 0);
8416
8417 if (JobHistory)
8418 {
8419 job->state->values[0].integer = attr->values[0].integer;
d09495fa 8420 job->state_value = (ipp_jstate_t)attr->values[0].integer;
ef416fc2 8421 cupsdSaveJob(job);
8422 }
8423 }
8424 break;
8425 }
8426 }
8427 }
8428 else if (con->response->request.status.status_code != IPP_OK)
8429 continue;
fa73b229 8430 else if ((attr2 = ippFindAttribute(job->attrs, attr->name,
8431 IPP_TAG_ZERO)) != NULL)
ef416fc2 8432 {
8433 /*
8434 * Some other value; first free the old value...
8435 */
8436
8437 if (job->attrs->prev)
8438 job->attrs->prev->next = attr2->next;
8439 else
8440 job->attrs->attrs = attr2->next;
8441
8442 if (job->attrs->last == attr2)
8443 job->attrs->last = job->attrs->prev;
8444
757d2cad 8445 _ippFreeAttr(attr2);
ef416fc2 8446
8447 /*
8448 * Then copy the attribute...
8449 */
8450
8451 copy_attribute(job->attrs, attr, 0);
8452
8453 /*
8454 * See if the job-name or job-hold-until is being changed.
8455 */
8456
8457 if (!strcmp(attr->name, "job-hold-until"))
8458 {
8459 cupsdSetJobHoldUntil(job, attr->values[0].string.text);
8460
8461 if (!strcmp(attr->values[0].string.text, "no-hold"))
8462 cupsdReleaseJob(job);
8463 else
8464 cupsdHoldJob(job);
d09495fa 8465
8466 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
ef416fc2 8467 }
8468 }
8469 else if (attr->value_tag == IPP_TAG_DELETEATTR)
8470 {
8471 /*
8472 * Delete the attribute...
8473 */
8474
8475 if ((attr2 = ippFindAttribute(job->attrs, attr->name,
8476 IPP_TAG_ZERO)) != NULL)
8477 {
8478 if (job->attrs->prev)
8479 job->attrs->prev->next = attr2->next;
8480 else
8481 job->attrs->attrs = attr2->next;
8482
8483 if (attr2 == job->attrs->last)
8484 job->attrs->last = job->attrs->prev;
8485
757d2cad 8486 _ippFreeAttr(attr2);
d09495fa 8487
8488 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
ef416fc2 8489 }
8490 }
8491 else
8492 {
8493 /*
8494 * Add new option by copying it...
8495 */
8496
8497 copy_attribute(job->attrs, attr, 0);
d09495fa 8498
8499 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
ef416fc2 8500 }
8501 }
8502
8503 /*
8504 * Save the job...
8505 */
8506
8507 cupsdSaveJob(job);
8508
d09495fa 8509 /*
8510 * Send events as needed...
8511 */
8512
8513 if (event & CUPSD_EVENT_JOB_STATE)
8514 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
8515 job->state_value == IPP_JOB_HELD ?
8516 "Job held by user." : "Job restarted by user.");
8517
8518 if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
8519 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
8520 "Job options changed by user.");
8521
ef416fc2 8522 /*
8523 * Start jobs if possible...
8524 */
8525
8526 cupsdCheckJobs();
8527}
8528
8529
b423cd4c 8530/*
8531 * 'set_printer_defaults()' - Set printer default options from a request.
8532 */
8533
8534static void
8535set_printer_defaults(
8536 cupsd_client_t *con, /* I - Client connection */
8537 cupsd_printer_t *printer) /* I - Printer */
8538{
8539 int i; /* Looping var */
8540 ipp_attribute_t *attr; /* Current attribute */
8541 int namelen; /* Length of attribute name */
8542 char name[256], /* New attribute name */
8543 value[256]; /* String version of integer attrs */
8544
8545
8546 for (attr = con->request->attrs; attr; attr = attr->next)
8547 {
8548 /*
8549 * Skip non-printer attributes...
8550 */
8551
8552 if (attr->group_tag != IPP_TAG_PRINTER || !attr->name)
8553 continue;
8554
8555 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_defaults: %s", attr->name);
8556
8557 if (!strcmp(attr->name, "job-sheets-default"))
8558 {
8559 /*
8560 * Only allow keywords and names...
8561 */
8562
8563 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
8564 continue;
8565
8566 /*
8567 * Only allow job-sheets-default to be set when running without a
8568 * system high classification level...
8569 */
8570
8571 if (Classification)
8572 continue;
8573
8574 cupsdSetString(&printer->job_sheets[0], attr->values[0].string.text);
8575
8576 if (attr->num_values > 1)
8577 cupsdSetString(&printer->job_sheets[1], attr->values[1].string.text);
8578 else
8579 cupsdSetString(&printer->job_sheets[1], "none");
8580 }
8581 else if (!strcmp(attr->name, "requesting-user-name-allowed"))
8582 {
8583 cupsdFreePrinterUsers(printer);
8584
8585 printer->deny_users = 0;
8586
8587 if (attr->value_tag == IPP_TAG_NAME &&
8588 (attr->num_values > 1 ||
8589 strcmp(attr->values[0].string.text, "all")))
8590 {
8591 for (i = 0; i < attr->num_values; i ++)
8592 cupsdAddPrinterUser(printer, attr->values[i].string.text);
8593 }
8594 }
8595 else if (!strcmp(attr->name, "requesting-user-name-denied"))
8596 {
8597 cupsdFreePrinterUsers(printer);
8598
8599 printer->deny_users = 1;
8600
8601 if (attr->value_tag == IPP_TAG_NAME &&
8602 (attr->num_values > 1 ||
8603 strcmp(attr->values[0].string.text, "none")))
8604 {
8605 for (i = 0; i < attr->num_values; i ++)
8606 cupsdAddPrinterUser(printer, attr->values[i].string.text);
8607 }
8608 }
8609 else if (!strcmp(attr->name, "job-quota-period"))
8610 {
8611 if (attr->value_tag != IPP_TAG_INTEGER)
8612 continue;
8613
8614 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-quota-period to %d...",
8615 attr->values[0].integer);
8616 cupsdFreeQuotas(printer);
8617
8618 printer->quota_period = attr->values[0].integer;
8619 }
8620 else if (!strcmp(attr->name, "job-k-limit"))
8621 {
8622 if (attr->value_tag != IPP_TAG_INTEGER)
8623 continue;
8624
8625 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-k-limit to %d...",
8626 attr->values[0].integer);
8627 cupsdFreeQuotas(printer);
8628
8629 printer->k_limit = attr->values[0].integer;
8630 }
8631 else if (!strcmp(attr->name, "job-page-limit"))
8632 {
8633 if (attr->value_tag != IPP_TAG_INTEGER)
8634 continue;
8635
8636 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-page-limit to %d...",
8637 attr->values[0].integer);
8638 cupsdFreeQuotas(printer);
8639
8640 printer->page_limit = attr->values[0].integer;
8641 }
8642 else if (!strcmp(attr->name, "printer-op-policy"))
8643 {
8644 cupsd_policy_t *p; /* Policy */
8645
8646
8647 if (attr->value_tag != IPP_TAG_NAME)
8648 continue;
8649
8650 if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
8651 {
8652 cupsdLogMessage(CUPSD_LOG_DEBUG,
8653 "Setting printer-op-policy to \"%s\"...",
8654 attr->values[0].string.text);
8655 cupsdSetString(&printer->op_policy, attr->values[0].string.text);
8656 printer->op_policy_ptr = p;
8657 }
8658 else
8659 {
8660 send_ipp_status(con, IPP_NOT_POSSIBLE,
8661 _("Unknown printer-op-policy \"%s\"."),
8662 attr->values[0].string.text);
8663 return;
8664 }
8665 }
8666 else if (!strcmp(attr->name, "printer-error-policy"))
8667 {
8668 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
8669 continue;
8670
8671 if (strcmp(attr->values[0].string.text, "abort-job") &&
8672 strcmp(attr->values[0].string.text, "retry-job") &&
8673 strcmp(attr->values[0].string.text, "stop-printer"))
8674 {
8675 send_ipp_status(con, IPP_NOT_POSSIBLE,
8676 _("Unknown printer-error-policy \"%s\"."),
8677 attr->values[0].string.text);
8678 return;
8679 }
8680
8681 cupsdLogMessage(CUPSD_LOG_DEBUG,
8682 "Setting printer-error-policy to \"%s\"...",
8683 attr->values[0].string.text);
8684 cupsdSetString(&printer->error_policy, attr->values[0].string.text);
8685 }
f7faf1f5 8686 else if (!strcmp(attr->name, "document-format-default") ||
8687 !strcmp(attr->name, "notify-lease-duration-default") ||
f301802f 8688 !strcmp(attr->name, "notify-events-default"))
b423cd4c 8689 continue;
8690
8691 /*
8692 * Skip any other non-default attributes...
8693 */
8694
8695 namelen = strlen(attr->name);
8696 if (namelen < 9 || strcmp(attr->name + namelen - 8, "-default") ||
8697 namelen > (sizeof(name) - 1) || attr->num_values != 1)
8698 continue;
8699
8700 /*
8701 * OK, anything else must be a user-defined default...
8702 */
8703
8704 strlcpy(name, attr->name, sizeof(name));
8705 name[namelen - 8] = '\0'; /* Strip "-default" */
8706
8707 switch (attr->value_tag)
8708 {
8709 case IPP_TAG_DELETEATTR :
8710 printer->num_options = cupsRemoveOption(name,
8711 printer->num_options,
8712 &(printer->options));
8713 cupsdLogMessage(CUPSD_LOG_DEBUG,
8714 "Deleting %s", attr->name);
8715 break;
8716
8717 case IPP_TAG_NAME :
8718 case IPP_TAG_KEYWORD :
8719 case IPP_TAG_URI :
8720 printer->num_options = cupsAddOption(name,
8721 attr->values[0].string.text,
8722 printer->num_options,
8723 &(printer->options));
8724 cupsdLogMessage(CUPSD_LOG_DEBUG,
8725 "Setting %s to \"%s\"...", attr->name,
8726 attr->values[0].string.text);
8727 break;
8728
8729 case IPP_TAG_BOOLEAN :
8730 printer->num_options = cupsAddOption(name,
8731 attr->values[0].boolean ?
8732 "true" : "false",
8733 printer->num_options,
8734 &(printer->options));
8735 cupsdLogMessage(CUPSD_LOG_DEBUG,
8736 "Setting %s to %s...", attr->name,
8737 attr->values[0].boolean ? "true" : "false");
8738 break;
8739
8740 case IPP_TAG_INTEGER :
8741 case IPP_TAG_ENUM :
8742 sprintf(value, "%d", attr->values[0].integer);
8743 printer->num_options = cupsAddOption(name, value,
8744 printer->num_options,
8745 &(printer->options));
8746 cupsdLogMessage(CUPSD_LOG_DEBUG,
8747 "Setting %s to %s...", attr->name, value);
8748 break;
8749
8750 case IPP_TAG_RANGE :
8751 sprintf(value, "%d-%d", attr->values[0].range.lower,
8752 attr->values[0].range.upper);
8753 printer->num_options = cupsAddOption(name, value,
8754 printer->num_options,
8755 &(printer->options));
8756 cupsdLogMessage(CUPSD_LOG_DEBUG,
8757 "Setting %s to %s...", attr->name, value);
8758 break;
8759
8760 case IPP_TAG_RESOLUTION :
8761 sprintf(value, "%dx%d%s", attr->values[0].resolution.xres,
8762 attr->values[0].resolution.yres,
8763 attr->values[0].resolution.units == IPP_RES_PER_INCH ?
8764 "dpi" : "dpc");
8765 printer->num_options = cupsAddOption(name, value,
8766 printer->num_options,
8767 &(printer->options));
8768 cupsdLogMessage(CUPSD_LOG_DEBUG,
8769 "Setting %s to %s...", attr->name, value);
8770 break;
8771
8772 default :
8773 /* Do nothing for other values */
8774 break;
8775 }
8776 }
8777}
8778
8779
ef416fc2 8780/*
8781 * 'start_printer()' - Start a printer.
8782 */
8783
8784static void
8785start_printer(cupsd_client_t *con, /* I - Client connection */
8786 ipp_attribute_t *uri) /* I - Printer URI */
8787{
8788 http_status_t status; /* Policy status */
8789 cups_ptype_t dtype; /* Destination type (printer or class) */
f7faf1f5 8790 char method[HTTP_MAX_URI],
8791 /* Method portion of URI */
8792 username[HTTP_MAX_URI],
8793 /* Username portion of URI */
8794 host[HTTP_MAX_URI],
8795 /* Host portion of URI */
8796 resource[HTTP_MAX_URI];
8797 /* Resource portion of URI */
8798 int port; /* Port portion of URI */
8799 const char *name; /* Printer name */
ef416fc2 8800 cupsd_printer_t *printer; /* Printer data */
8801
8802
8803 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", con,
8804 con->http.fd, uri->values[0].string.text);
8805
8806 /*
8807 * Is the destination valid?
8808 */
8809
f7faf1f5 8810 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
8811 sizeof(method), username, sizeof(username), host,
8812 sizeof(host), &port, resource, sizeof(resource));
8813
8814 if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
ef416fc2 8815 {
8816 /*
8817 * Bad URI...
8818 */
8819
8820 send_ipp_status(con, IPP_NOT_FOUND,
8821 _("The printer or class was not found."));
8822 return;
8823 }
8824
8825 /*
8826 * Check policy...
8827 */
8828
8829 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8830 {
8831 send_http_error(con, status);
8832 return;
8833 }
8834
8835 /*
8836 * Start the printer...
8837 */
8838
8839 printer->state_message[0] = '\0';
8840
8841 cupsdStartPrinter(printer, 1);
8842
8843 if (dtype & CUPS_PRINTER_CLASS)
f7faf1f5 8844 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".", name,
8845 get_username(con));
e00b005a 8846 else
f7faf1f5 8847 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".", name,
8848 get_username(con));
ef416fc2 8849
8850 cupsdCheckJobs();
8851
8852 /*
8853 * Everything was ok, so return OK status...
8854 */
8855
8856 con->response->request.status.status_code = IPP_OK;
8857}
8858
8859
8860/*
8861 * 'stop_printer()' - Stop a printer.
8862 */
8863
8864static void
8865stop_printer(cupsd_client_t *con, /* I - Client connection */
8866 ipp_attribute_t *uri) /* I - Printer URI */
8867{
8868 http_status_t status; /* Policy status */
8869 cups_ptype_t dtype; /* Destination type (printer or class) */
f7faf1f5 8870 char method[HTTP_MAX_URI],
8871 /* Method portion of URI */
8872 username[HTTP_MAX_URI],
8873 /* Username portion of URI */
8874 host[HTTP_MAX_URI],
8875 /* Host portion of URI */
8876 resource[HTTP_MAX_URI];
8877 /* Resource portion of URI */
8878 int port; /* Port portion of URI */
8879 const char *name; /* Printer name */
ef416fc2 8880 cupsd_printer_t *printer; /* Printer data */
8881 ipp_attribute_t *attr; /* printer-state-message attribute */
8882
8883
8884 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", con,
8885 con->http.fd, uri->values[0].string.text);
8886
8887 /*
8888 * Is the destination valid?
8889 */
8890
f7faf1f5 8891 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
8892 sizeof(method), username, sizeof(username), host,
8893 sizeof(host), &port, resource, sizeof(resource));
8894
8895 if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
ef416fc2 8896 {
8897 /*
8898 * Bad URI...
8899 */
8900
8901 send_ipp_status(con, IPP_NOT_FOUND,
8902 _("The printer or class was not found."));
8903 return;
8904 }
8905
8906 /*
8907 * Check policy...
8908 */
8909
8910 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8911 {
8912 send_http_error(con, status);
8913 return;
8914 }
8915
8916 /*
8917 * Stop the printer...
8918 */
8919
8920 if ((attr = ippFindAttribute(con->request, "printer-state-message",
8921 IPP_TAG_TEXT)) == NULL)
8922 strcpy(printer->state_message, "Paused");
8923 else
8924 {
8925 strlcpy(printer->state_message, attr->values[0].string.text,
8926 sizeof(printer->state_message));
8927 }
8928
8929 cupsdStopPrinter(printer, 1);
8930
8931 if (dtype & CUPS_PRINTER_CLASS)
f7faf1f5 8932 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".", name,
8933 get_username(con));
ef416fc2 8934 else
f7faf1f5 8935 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".", name,
8936 get_username(con));
ef416fc2 8937
8938 /*
8939 * Everything was ok, so return OK status...
8940 */
8941
8942 con->response->request.status.status_code = IPP_OK;
8943}
8944
8945
89d46774 8946/*
8947 * 'url_encode_attr()' - URL-encode a string attribute.
8948 */
8949
8950static void
8951url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */
8952 char *buffer,/* I - String buffer */
8953 int bufsize)/* I - Size of buffer */
8954{
8955 int i; /* Looping var */
8956 char *bufptr, /* Pointer into buffer */
8957 *bufend, /* End of buffer */
8958 *valptr; /* Pointer into value */
8959
8960
8961 strlcpy(buffer, attr->name, bufsize);
8962 bufptr = buffer + strlen(buffer);
8963 bufend = buffer + bufsize - 1;
8964
8965 for (i = 0; i < attr->num_values; i ++)
8966 {
8967 if (bufptr >= bufend)
8968 break;
8969
8970 if (i)
8971 *bufptr++ = ',';
8972 else
8973 *bufptr++ = '=';
8974
8975 if (bufptr >= bufend)
8976 break;
8977
8978 *bufptr++ = '\'';
8979
8980 for (valptr = attr->values[i].string.text;
8981 *valptr && bufptr < bufend;
8982 valptr ++)
8983 if (*valptr == ' ')
8984 {
8985 if (bufptr >= (bufend - 2))
8986 break;
8987
8988 *bufptr++ = '%';
8989 *bufptr++ = '2';
8990 *bufptr++ = '0';
8991 }
8992 else if (*valptr == '\'' || *valptr == '\\')
8993 {
8994 *bufptr++ = '\\';
8995 *bufptr++ = *valptr;
8996 }
8997 else
8998 *bufptr++ = *valptr;
8999
9000 if (bufptr >= bufend)
9001 break;
9002
9003 *bufptr++ = '\'';
9004 }
9005
9006 *bufptr = '\0';
9007}
9008
9009
ef416fc2 9010/*
9011 * 'user_allowed()' - See if a user is allowed to print to a queue.
9012 */
9013
9014static int /* O - 0 if not allowed, 1 if allowed */
9015user_allowed(cupsd_printer_t *p, /* I - Printer or class */
9016 const char *username) /* I - Username */
9017{
9018 int i; /* Looping var */
9019 struct passwd *pw; /* User password data */
9020
9021
9022 if (p->num_users == 0)
9023 return (1);
9024
9025 if (!strcmp(username, "root"))
9026 return (1);
9027
9028 pw = getpwnam(username);
9029 endpwent();
9030
9031 for (i = 0; i < p->num_users; i ++)
9032 {
9033 if (p->users[i][0] == '@')
9034 {
9035 /*
9036 * Check group membership...
9037 */
9038
9039 if (cupsdCheckGroup(username, pw, p->users[i] + 1))
9040 break;
9041 }
9042 else if (!strcasecmp(username, p->users[i]))
9043 break;
9044 }
9045
9046 return ((i < p->num_users) != p->deny_users);
9047}
9048
9049
9050/*
9051 * 'validate_job()' - Validate printer options and destination.
9052 */
9053
9054static void
9055validate_job(cupsd_client_t *con, /* I - Client connection */
9056 ipp_attribute_t *uri) /* I - Printer URI */
9057{
9058 http_status_t status; /* Policy status */
9059 ipp_attribute_t *attr; /* Current attribute */
9060 ipp_attribute_t *format; /* Document-format attribute */
9061 cups_ptype_t dtype; /* Destination type (printer or class) */
f7faf1f5 9062 char method[HTTP_MAX_URI],
9063 /* Method portion of URI */
9064 username[HTTP_MAX_URI],
9065 /* Username portion of URI */
9066 host[HTTP_MAX_URI],
9067 /* Host portion of URI */
9068 resource[HTTP_MAX_URI];
9069 /* Resource portion of URI */
9070 int port; /* Port portion of URI */
ef416fc2 9071 char super[MIME_MAX_SUPER],
9072 /* Supertype of file */
9073 type[MIME_MAX_TYPE];
9074 /* Subtype of file */
9075 cupsd_printer_t *printer; /* Printer */
9076
9077
9078 cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", con,
9079 con->http.fd, uri->values[0].string.text);
9080
9081 /*
9082 * OK, see if the client is sending the document compressed - CUPS
9083 * doesn't support compression yet...
9084 */
9085
fa73b229 9086 if ((attr = ippFindAttribute(con->request, "compression",
9087 IPP_TAG_KEYWORD)) != NULL &&
9088 !strcmp(attr->values[0].string.text, "none"))
ef416fc2 9089 {
9090 send_ipp_status(con, IPP_ATTRIBUTES,
9091 _("Unsupported compression attribute %s!"),
9092 attr->values[0].string.text);
9093 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
9094 "compression", NULL, attr->values[0].string.text);
9095 return;
9096 }
9097
9098 /*
9099 * Is it a format we support?
9100 */
9101
9102 if ((format = ippFindAttribute(con->request, "document-format",
9103 IPP_TAG_MIMETYPE)) != NULL)
9104 {
9105 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
9106 {
9107 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"!"),
9108 format->values[0].string.text);
9109 return;
9110 }
9111
fa73b229 9112 if ((strcmp(super, "application") || strcmp(type, "octet-stream")) &&
9113 !mimeType(MimeDatabase, super, type))
ef416fc2 9114 {
9115 cupsdLogMessage(CUPSD_LOG_INFO,
9116 "Hint: Do you have the raw file printing rules enabled?");
9117 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
9118 _("Unsupported format \"%s\"!"),
9119 format->values[0].string.text);
9120 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9121 "document-format", NULL, format->values[0].string.text);
9122 return;
9123 }
9124 }
9125
9126 /*
9127 * Is the destination valid?
9128 */
9129
f7faf1f5 9130 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
9131 sizeof(method), username, sizeof(username), host,
9132 sizeof(host), &port, resource, sizeof(resource));
9133
9134 if (cupsdValidateDest(host, resource, &dtype, &printer) == NULL)
ef416fc2 9135 {
9136 /*
9137 * Bad URI...
9138 */
9139
9140 send_ipp_status(con, IPP_NOT_FOUND,
9141 _("The printer or class was not found."));
9142 return;
9143 }
9144
9145 /*
9146 * Check policy...
9147 */
9148
9149 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
9150 {
9151 send_http_error(con, status);
9152 return;
9153 }
9154
9155 /*
9156 * Everything was ok, so return OK status...
9157 */
9158
9159 con->response->request.status.status_code = IPP_OK;
9160}
9161
9162
9163/*
9164 * 'validate_name()' - Make sure the printer name only contains valid chars.
9165 */
9166
9167static int /* O - 0 if name is no good, 1 if name is good */
9168validate_name(const char *name) /* I - Name to check */
9169{
9170 const char *ptr; /* Pointer into name */
9171
9172
9173 /*
9174 * Scan the whole name...
9175 */
9176
9177 for (ptr = name; *ptr; ptr ++)
9178 if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
9179 return (0);
9180
9181 /*
9182 * All the characters are good; validate the length, too...
9183 */
9184
9185 return ((ptr - name) < 128);
9186}
9187
9188
9189/*
9190 * 'validate_user()' - Validate the user for the request.
9191 */
9192
9193static int /* O - 1 if permitted, 0 otherwise */
9194validate_user(cupsd_job_t *job, /* I - Job */
9195 cupsd_client_t *con, /* I - Client connection */
9196 const char *owner, /* I - Owner of job/resource */
9197 char *username, /* O - Authenticated username */
9198 int userlen) /* I - Length of username */
9199{
ef416fc2 9200 cupsd_printer_t *printer; /* Printer for job */
9201
9202
9203 cupsdLogMessage(CUPSD_LOG_DEBUG2,
9204 "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, "
9205 "userlen=%d)",
9206 job ? job->id : 0, con->http.fd, owner ? owner : "(null)",
9207 username, userlen);
9208
9209 /*
9210 * Validate input...
9211 */
9212
9213 if (!con || !owner || !username || userlen <= 0)
9214 return (0);
9215
9216 /*
9217 * Get the best authenticated username that is available.
9218 */
9219
e00b005a 9220 strlcpy(username, get_username(con), userlen);
ef416fc2 9221
9222 /*
9223 * Check the username against the owner...
9224 */
9225
80ca4592 9226 printer = cupsdFindDest(job->dest);
ef416fc2 9227
9228 return (cupsdCheckPolicy(printer ? printer->op_policy_ptr : DefaultPolicyPtr,
9229 con, owner) == HTTP_OK);
9230}
9231
9232
9233/*
d09495fa 9234 * End of "$Id: ipp.c 5907 2006-08-30 02:18:28Z mike $".
ef416fc2 9235 */