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