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