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