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