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