1/* 2 * "$Id: admin.c 11433 2013-11-20 18:57:44Z msweet $" 3 * 4 * Administration CGI for CUPS. 5 * 6 * Copyright 2007-2012 by Apple Inc. 7 * Copyright 1997-2007 by Easy Software Products. 8 * 9 * These coded instructions, statements, and computer programs are the 10 * property of Apple Inc. and are protected by Federal copyright 11 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 12 * which should have been included with this file. If this file is 13 * file is missing or damaged, see the license at "http://www.cups.org/". 14 * 15 * Contents: 16 * 17 * main() - Main entry for CGI. 18 * choose_device_cb() - Add a device to the device selection page. 19 * do_add_rss_subscription() - Add a RSS subscription. 20 * do_am_class() - Add or modify a class. 21 * do_am_printer() - Add or modify a printer. 22 * do_cancel_subscription() - Cancel a subscription. 23 * do_config_server() - Configure server settings. 24 * do_delete_class() - Delete a class. 25 * do_delete_printer() - Delete a printer. 26 * do_export() - Export printers to Samba. 27 * do_list_printers() - List available printers. 28 * do_menu() - Show the main menu. 29 * do_set_allowed_users() - Set the allowed/denied users for a queue. 30 * do_set_default() - Set the server default printer/class. 31 * do_set_options() - Configure the default options for a queue. 32 * do_set_sharing() - Set printer-is-shared value. 33 * get_option_value() - Return the value of an option. 34 * get_points() - Get a value in points. 35 */ 36 37/* 38 * Include necessary headers... 39 */ 40 41#include "cgi-private.h" 42#include <cups/adminutil.h> 43#include <cups/ppd.h> 44#include <errno.h> 45#include <unistd.h> 46#include <fcntl.h> 47#include <sys/wait.h> 48#include <limits.h> 49 50 51/* 52 * Local globals... 53 */ 54 55static int current_device = 0; /* Current device shown */ 56 57 58/* 59 * Local functions... 60 */ 61 62static void choose_device_cb(const char *device_class, 63 const char *device_id, const char *device_info, 64 const char *device_make_and_model, 65 const char *device_uri, 66 const char *device_location, 67 const char *title); 68static void do_add_rss_subscription(http_t *http); 69static void do_am_class(http_t *http, int modify); 70static void do_am_printer(http_t *http, int modify); 71static void do_cancel_subscription(http_t *http); 72static void do_config_server(http_t *http); 73static void do_delete_class(http_t *http); 74static void do_delete_printer(http_t *http); 75static void do_export(http_t *http); 76static void do_list_printers(http_t *http); 77static void do_menu(http_t *http); 78static void do_set_allowed_users(http_t *http); 79static void do_set_default(http_t *http); 80static void do_set_options(http_t *http, int is_class); 81static void do_set_sharing(http_t *http); 82static char *get_option_value(ppd_file_t *ppd, const char *name, 83 char *buffer, size_t bufsize); 84static double get_points(double number, const char *uval); 85 86 87/* 88 * 'main()' - Main entry for CGI. 89 */ 90 91int /* O - Exit status */ 92main(int argc, /* I - Number of command-line arguments */ 93 char *argv[]) /* I - Command-line arguments */ 94{ 95 http_t *http; /* Connection to the server */ 96 const char *op; /* Operation name */ 97 98 99 /* 100 * Connect to the HTTP server... 101 */ 102 103 fputs("DEBUG: admin.cgi started...\n", stderr); 104 105 http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption()); 106 107 if (!http) 108 { 109 perror("ERROR: Unable to connect to cupsd"); 110 fprintf(stderr, "DEBUG: cupsServer()=\"%s\"\n", 111 cupsServer() ? cupsServer() : "(null)"); 112 fprintf(stderr, "DEBUG: ippPort()=%d\n", ippPort()); 113 fprintf(stderr, "DEBUG: cupsEncryption()=%d\n", cupsEncryption()); 114 exit(1); 115 } 116 117 fprintf(stderr, "DEBUG: http=%p\n", http); 118 119 /* 120 * Set the web interface section... 121 */ 122 123 cgiSetVariable("SECTION", "admin"); 124 cgiSetVariable("REFRESH_PAGE", ""); 125 126 /* 127 * See if we have form data... 128 */ 129 130 if (!cgiInitialize() || !cgiGetVariable("OP")) 131 { 132 /* 133 * Nope, send the administration menu... 134 */ 135 136 fputs("DEBUG: No form data, showing main menu...\n", stderr); 137 138 do_menu(http); 139 } 140 else if ((op = cgiGetVariable("OP")) != NULL && cgiIsPOST()) 141 { 142 /* 143 * Do the operation... 144 */ 145 146 fprintf(stderr, "DEBUG: op=\"%s\"...\n", op); 147 148 if (!*op) 149 { 150 const char *printer = getenv("PRINTER_NAME"), 151 /* Printer or class name */ 152 *server_port = getenv("SERVER_PORT"); 153 /* Port number string */ 154 int port = atoi(server_port ? server_port : "0"); 155 /* Port number */ 156 char uri[1024]; /* URL */ 157 158 if (printer) 159 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), 160 getenv("HTTPS") ? "https" : "http", NULL, 161 getenv("SERVER_NAME"), port, "/%s/%s", 162 cgiGetVariable("IS_CLASS") ? "classes" : "printers", 163 printer); 164 else 165 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), 166 getenv("HTTPS") ? "https" : "http", NULL, 167 getenv("SERVER_NAME"), port, "/admin"); 168 169 printf("Location: %s\n\n", uri); 170 } 171 else if (!strcmp(op, "set-allowed-users")) 172 do_set_allowed_users(http); 173 else if (!strcmp(op, "set-as-default")) 174 do_set_default(http); 175 else if (!strcmp(op, "set-sharing")) 176 do_set_sharing(http); 177 else if (!strcmp(op, "find-new-printers") || 178 !strcmp(op, "list-available-printers")) 179 do_list_printers(http); 180 else if (!strcmp(op, "add-class")) 181 do_am_class(http, 0); 182 else if (!strcmp(op, "add-printer")) 183 do_am_printer(http, 0); 184 else if (!strcmp(op, "modify-class")) 185 do_am_class(http, 1); 186 else if (!strcmp(op, "modify-printer")) 187 do_am_printer(http, 1); 188 else if (!strcmp(op, "delete-class")) 189 do_delete_class(http); 190 else if (!strcmp(op, "delete-printer")) 191 do_delete_printer(http); 192 else if (!strcmp(op, "set-class-options")) 193 do_set_options(http, 1); 194 else if (!strcmp(op, "set-printer-options")) 195 do_set_options(http, 0); 196 else if (!strcmp(op, "config-server")) 197 do_config_server(http); 198 else if (!strcmp(op, "export-samba")) 199 do_export(http); 200 else if (!strcmp(op, "add-rss-subscription")) 201 do_add_rss_subscription(http); 202 else if (!strcmp(op, "cancel-subscription")) 203 do_cancel_subscription(http); 204 else 205 { 206 /* 207 * Bad operation code - display an error... 208 */ 209 210 cgiStartHTML(cgiText(_("Administration"))); 211 cgiCopyTemplateLang("error-op.tmpl"); 212 cgiEndHTML(); 213 } 214 } 215 else if (op && !strcmp(op, "redirect")) 216 { 217 const char *url; /* Redirection URL... */ 218 char prefix[1024]; /* URL prefix */ 219 220 221 if (getenv("HTTPS")) 222 snprintf(prefix, sizeof(prefix), "https://%s:%s", 223 getenv("SERVER_NAME"), getenv("SERVER_PORT")); 224 else 225 snprintf(prefix, sizeof(prefix), "http://%s:%s", 226 getenv("SERVER_NAME"), getenv("SERVER_PORT")); 227 228 fprintf(stderr, "DEBUG: redirecting with prefix %s!\n", prefix); 229 230 if ((url = cgiGetVariable("URL")) != NULL) 231 { 232 char encoded[1024], /* Encoded URL string */ 233 *ptr; /* Pointer into encoded string */ 234 235 236 ptr = encoded; 237 if (*url != '/') 238 *ptr++ = '/'; 239 240 for (; *url && ptr < (encoded + sizeof(encoded) - 4); url ++) 241 { 242 if (strchr("%@&+ <>#=", *url) || *url < ' ' || *url & 128) 243 { 244 /* 245 * Percent-encode this character; safe because we have at least 4 246 * bytes left in the array... 247 */ 248 249 sprintf(ptr, "%%%02X", *url & 255); 250 ptr += 3; 251 } 252 else 253 *ptr++ = *url; 254 } 255 256 *ptr = '\0'; 257 258 if (*url) 259 { 260 /* 261 * URL was too long, just redirect to the admin page... 262 */ 263 264 printf("Location: %s/admin\n\n", prefix); 265 } 266 else 267 { 268 /* 269 * URL is OK, redirect there... 270 */ 271 272 printf("Location: %s%s\n\n", prefix, encoded); 273 } 274 } 275 else 276 printf("Location: %s/admin\n\n", prefix); 277 } 278 else 279 { 280 /* 281 * Form data but no operation code - display an error... 282 */ 283 284 cgiStartHTML(cgiText(_("Administration"))); 285 cgiCopyTemplateLang("error-op.tmpl"); 286 cgiEndHTML(); 287 } 288 289 /* 290 * Close the HTTP server connection... 291 */ 292 293 httpClose(http); 294 295 /* 296 * Return with no errors... 297 */ 298 299 return (0); 300} 301 302 303/* 304 * 'choose_device_cb()' - Add a device to the device selection page. 305 */ 306 307static void 308choose_device_cb( 309 const char *device_class, /* I - Class */ 310 const char *device_id, /* I - 1284 device ID */ 311 const char *device_info, /* I - Description */ 312 const char *device_make_and_model, /* I - Make and model */ 313 const char *device_uri, /* I - Device URI */ 314 const char *device_location, /* I - Location */ 315 const char *title) /* I - Page title */ 316{ 317 /* 318 * For modern browsers, start a multi-part page so we can show that something 319 * is happening. Non-modern browsers just get everything at the end... 320 */ 321 322 if (current_device == 0 && cgiSupportsMultipart()) 323 { 324 cgiStartMultipart(); 325 cgiStartHTML(title); 326 cgiCopyTemplateLang("choose-device.tmpl"); 327 cgiEndHTML(); 328 fflush(stdout); 329 } 330 331 332 /* 333 * Add the device to the array... 334 */ 335 336 cgiSetArray("device_class", current_device, device_class); 337 cgiSetArray("device_id", current_device, device_id); 338 cgiSetArray("device_info", current_device, device_info); 339 cgiSetArray("device_make_and_model", current_device, device_make_and_model); 340 cgiSetArray("device_uri", current_device, device_uri); 341 cgiSetArray("device_location", current_device, device_location); 342 343 current_device ++; 344} 345 346 347/* 348 * 'do_add_rss_subscription()' - Add a RSS subscription. 349 */ 350 351static void 352do_add_rss_subscription(http_t *http) /* I - HTTP connection */ 353{ 354 ipp_t *request, /* IPP request data */ 355 *response; /* IPP response data */ 356 char rss_uri[1024]; /* RSS notify-recipient URI */ 357 int num_events; /* Number of events */ 358 const char *events[12], /* Subscribed events */ 359 *subscription_name, /* Subscription name */ 360 *printer_uri, /* Printer URI */ 361 *ptr, /* Pointer into name */ 362 *user; /* Username */ 363 int max_events; /* Maximum number of events */ 364 365 366 /* 367 * See if we have all of the required information... 368 */ 369 370 subscription_name = cgiGetVariable("SUBSCRIPTION_NAME"); 371 printer_uri = cgiGetVariable("PRINTER_URI"); 372 num_events = 0; 373 374 if (cgiGetVariable("EVENT_JOB_CREATED")) 375 events[num_events ++] = "job-created"; 376 if (cgiGetVariable("EVENT_JOB_COMPLETED")) 377 events[num_events ++] = "job-completed"; 378 if (cgiGetVariable("EVENT_JOB_STOPPED")) 379 events[num_events ++] = "job-stopped"; 380 if (cgiGetVariable("EVENT_JOB_CONFIG_CHANGED")) 381 events[num_events ++] = "job-config-changed"; 382 if (cgiGetVariable("EVENT_PRINTER_STOPPED")) 383 events[num_events ++] = "printer-stopped"; 384 if (cgiGetVariable("EVENT_PRINTER_ADDED")) 385 events[num_events ++] = "printer-added"; 386 if (cgiGetVariable("EVENT_PRINTER_MODIFIED")) 387 events[num_events ++] = "printer-modified"; 388 if (cgiGetVariable("EVENT_PRINTER_DELETED")) 389 events[num_events ++] = "printer-deleted"; 390 if (cgiGetVariable("EVENT_SERVER_STARTED")) 391 events[num_events ++] = "server-started"; 392 if (cgiGetVariable("EVENT_SERVER_STOPPED")) 393 events[num_events ++] = "server-stopped"; 394 if (cgiGetVariable("EVENT_SERVER_RESTARTED")) 395 events[num_events ++] = "server-restarted"; 396 if (cgiGetVariable("EVENT_SERVER_AUDIT")) 397 events[num_events ++] = "server-audit"; 398 399 if ((ptr = cgiGetVariable("MAX_EVENTS")) != NULL) 400 max_events = atoi(ptr); 401 else 402 max_events = 0; 403 404 if (!subscription_name || !printer_uri || !num_events || 405 max_events <= 0 || max_events > 9999) 406 { 407 /* 408 * Don't have everything we need, so get the available printers 409 * and classes and (re)show the add page... 410 */ 411 412 if (cgiGetVariable("EVENT_JOB_CREATED")) 413 cgiSetVariable("EVENT_JOB_CREATED", "CHECKED"); 414 if (cgiGetVariable("EVENT_JOB_COMPLETED")) 415 cgiSetVariable("EVENT_JOB_COMPLETED", "CHECKED"); 416 if (cgiGetVariable("EVENT_JOB_STOPPED")) 417 cgiSetVariable("EVENT_JOB_STOPPED", "CHECKED"); 418 if (cgiGetVariable("EVENT_JOB_CONFIG_CHANGED")) 419 cgiSetVariable("EVENT_JOB_CONFIG_CHANGED", "CHECKED"); 420 if (cgiGetVariable("EVENT_PRINTER_STOPPED")) 421 cgiSetVariable("EVENT_PRINTER_STOPPED", "CHECKED"); 422 if (cgiGetVariable("EVENT_PRINTER_ADDED")) 423 cgiSetVariable("EVENT_PRINTER_ADDED", "CHECKED"); 424 if (cgiGetVariable("EVENT_PRINTER_MODIFIED")) 425 cgiSetVariable("EVENT_PRINTER_MODIFIED", "CHECKED"); 426 if (cgiGetVariable("EVENT_PRINTER_DELETED")) 427 cgiSetVariable("EVENT_PRINTER_DELETED", "CHECKED"); 428 if (cgiGetVariable("EVENT_SERVER_STARTED")) 429 cgiSetVariable("EVENT_SERVER_STARTED", "CHECKED"); 430 if (cgiGetVariable("EVENT_SERVER_STOPPED")) 431 cgiSetVariable("EVENT_SERVER_STOPPED", "CHECKED"); 432 if (cgiGetVariable("EVENT_SERVER_RESTARTED")) 433 cgiSetVariable("EVENT_SERVER_RESTARTED", "CHECKED"); 434 if (cgiGetVariable("EVENT_SERVER_AUDIT")) 435 cgiSetVariable("EVENT_SERVER_AUDIT", "CHECKED"); 436 437 request = ippNewRequest(CUPS_GET_PRINTERS); 438 response = cupsDoRequest(http, request, "/"); 439 440 cgiSetIPPVars(response, NULL, NULL, NULL, 0); 441 442 ippDelete(response); 443 444 cgiStartHTML(cgiText(_("Add RSS Subscription"))); 445 446 cgiCopyTemplateLang("add-rss-subscription.tmpl"); 447 448 cgiEndHTML(); 449 return; 450 } 451 452 /* 453 * Make sure we have a username... 454 */ 455 456 if ((user = getenv("REMOTE_USER")) == NULL) 457 { 458 puts("Status: 401\n"); 459 exit(0); 460 } 461 462 /* 463 * Validate the subscription name... 464 */ 465 466 for (ptr = subscription_name; *ptr; ptr ++) 467 if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || 468 *ptr == '?' || *ptr == '#') 469 break; 470 471 if (*ptr) 472 { 473 cgiSetVariable("ERROR", 474 cgiText(_("The subscription name may not " 475 "contain spaces, slashes (/), question marks (?), " 476 "or the pound sign (#)."))); 477 cgiStartHTML(_("Add RSS Subscription")); 478 cgiCopyTemplateLang("error.tmpl"); 479 cgiEndHTML(); 480 return; 481 } 482 483 /* 484 * Add the subscription... 485 */ 486 487 ptr = subscription_name + strlen(subscription_name) - 4; 488 if (ptr < subscription_name || strcmp(ptr, ".rss")) 489 httpAssembleURIf(HTTP_URI_CODING_ALL, rss_uri, sizeof(rss_uri), "rss", 490 NULL, NULL, 0, "/%s.rss?max_events=%d", subscription_name, 491 max_events); 492 else 493 httpAssembleURIf(HTTP_URI_CODING_ALL, rss_uri, sizeof(rss_uri), "rss", 494 NULL, NULL, 0, "/%s?max_events=%d", subscription_name, 495 max_events); 496 497 request = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION); 498 499 if (!_cups_strcasecmp(printer_uri, "#ALL#")) 500 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 501 NULL, "ipp://localhost/"); 502 else 503 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 504 NULL, printer_uri); 505 506 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", 507 NULL, user); 508 509 ippAddString(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI, 510 "notify-recipient-uri", NULL, rss_uri); 511 ippAddStrings(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, "notify-events", 512 num_events, NULL, events); 513 ippAddInteger(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, 514 "notify-lease-duration", 0); 515 516 ippDelete(cupsDoRequest(http, request, "/")); 517 518 if (cupsLastError() == IPP_NOT_AUTHORIZED) 519 { 520 puts("Status: 401\n"); 521 exit(0); 522 } 523 else if (cupsLastError() > IPP_OK_CONFLICT) 524 { 525 cgiStartHTML(_("Add RSS Subscription")); 526 cgiShowIPPError(_("Unable to add RSS subscription")); 527 } 528 else 529 { 530 /* 531 * Redirect successful updates back to the admin page... 532 */ 533 534 cgiSetVariable("refresh_page", "5;URL=/admin"); 535 cgiStartHTML(_("Add RSS Subscription")); 536 cgiCopyTemplateLang("subscription-added.tmpl"); 537 } 538 539 cgiEndHTML(); 540} 541 542 543/* 544 * 'do_am_class()' - Add or modify a class. 545 */ 546 547static void 548do_am_class(http_t *http, /* I - HTTP connection */ 549 int modify) /* I - Modify the printer? */ 550{ 551 int i, j; /* Looping vars */ 552 int element; /* Element number */ 553 int num_printers; /* Number of printers */ 554 ipp_t *request, /* IPP request */ 555 *response; /* IPP response */ 556 ipp_attribute_t *attr; /* member-uris attribute */ 557 char uri[HTTP_MAX_URI]; /* Device or printer URI */ 558 const char *name, /* Pointer to class name */ 559 *op, /* Operation name */ 560 *ptr; /* Pointer to CGI variable */ 561 const char *title; /* Title of page */ 562 static const char * const pattrs[] = /* Requested printer attributes */ 563 { 564 "member-names", 565 "printer-info", 566 "printer-location" 567 }; 568 569 570 title = cgiText(modify ? _("Modify Class") : _("Add Class")); 571 op = cgiGetVariable("OP"); 572 name = cgiGetVariable("PRINTER_NAME"); 573 574 if (cgiGetVariable("PRINTER_LOCATION") == NULL) 575 { 576 /* 577 * Build a CUPS_GET_PRINTERS request, which requires the 578 * following attributes: 579 * 580 * attributes-charset 581 * attributes-natural-language 582 */ 583 584 request = ippNewRequest(CUPS_GET_PRINTERS); 585 586 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type", 587 CUPS_PRINTER_LOCAL); 588 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask", 589 CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE); 590 591 /* 592 * Do the request and get back a response... 593 */ 594 595 cgiClearVariables(); 596 if (op) 597 cgiSetVariable("OP", op); 598 if (name) 599 cgiSetVariable("PRINTER_NAME", name); 600 601 if ((response = cupsDoRequest(http, request, "/")) != NULL) 602 { 603 /* 604 * Create MEMBER_URIS and MEMBER_NAMES arrays... 605 */ 606 607 for (element = 0, attr = response->attrs; 608 attr != NULL; 609 attr = attr->next) 610 if (attr->name && !strcmp(attr->name, "printer-uri-supported")) 611 { 612 if ((ptr = strrchr(attr->values[0].string.text, '/')) != NULL && 613 (!name || _cups_strcasecmp(name, ptr + 1))) 614 { 615 /* 616 * Don't show the current class... 617 */ 618 619 cgiSetArray("MEMBER_URIS", element, attr->values[0].string.text); 620 element ++; 621 } 622 } 623 624 for (element = 0, attr = response->attrs; 625 attr != NULL; 626 attr = attr->next) 627 if (attr->name && !strcmp(attr->name, "printer-name")) 628 { 629 if (!name || _cups_strcasecmp(name, attr->values[0].string.text)) 630 { 631 /* 632 * Don't show the current class... 633 */ 634 635 cgiSetArray("MEMBER_NAMES", element, attr->values[0].string.text); 636 element ++; 637 } 638 } 639 640 num_printers = cgiGetSize("MEMBER_URIS"); 641 642 ippDelete(response); 643 } 644 else 645 num_printers = 0; 646 647 if (modify) 648 { 649 /* 650 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the 651 * following attributes: 652 * 653 * attributes-charset 654 * attributes-natural-language 655 * printer-uri 656 */ 657 658 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); 659 660 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 661 "localhost", 0, "/classes/%s", name); 662 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 663 NULL, uri); 664 665 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 666 "requested-attributes", 667 (int)(sizeof(pattrs) / sizeof(pattrs[0])), 668 NULL, pattrs); 669 670 /* 671 * Do the request and get back a response... 672 */ 673 674 if ((response = cupsDoRequest(http, request, "/")) != NULL) 675 { 676 if ((attr = ippFindAttribute(response, "member-names", 677 IPP_TAG_NAME)) != NULL) 678 { 679 /* 680 * Mark any current members in the class... 681 */ 682 683 for (j = 0; j < num_printers; j ++) 684 cgiSetArray("MEMBER_SELECTED", j, ""); 685 686 for (i = 0; i < attr->num_values; i ++) 687 { 688 for (j = 0; j < num_printers; j ++) 689 { 690 if (!_cups_strcasecmp(attr->values[i].string.text, 691 cgiGetArray("MEMBER_NAMES", j))) 692 { 693 cgiSetArray("MEMBER_SELECTED", j, "SELECTED"); 694 break; 695 } 696 } 697 } 698 } 699 700 if ((attr = ippFindAttribute(response, "printer-info", 701 IPP_TAG_TEXT)) != NULL) 702 cgiSetVariable("PRINTER_INFO", attr->values[0].string.text); 703 704 if ((attr = ippFindAttribute(response, "printer-location", 705 IPP_TAG_TEXT)) != NULL) 706 cgiSetVariable("PRINTER_LOCATION", attr->values[0].string.text); 707 708 ippDelete(response); 709 } 710 711 /* 712 * Update the location and description of an existing printer... 713 */ 714 715 cgiStartHTML(title); 716 cgiCopyTemplateLang("modify-class.tmpl"); 717 } 718 else 719 { 720 /* 721 * Get the name, location, and description for a new printer... 722 */ 723 724 cgiStartHTML(title); 725 cgiCopyTemplateLang("add-class.tmpl"); 726 } 727 728 cgiEndHTML(); 729 730 return; 731 } 732 733 if (!name) 734 { 735 cgiStartHTML(title); 736 cgiSetVariable("ERROR", cgiText(_("Missing form variable"))); 737 cgiCopyTemplateLang("error.tmpl"); 738 cgiEndHTML(); 739 return; 740 } 741 742 for (ptr = name; *ptr; ptr ++) 743 if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#') 744 break; 745 746 if (*ptr || ptr == name || strlen(name) > 127) 747 { 748 cgiSetVariable("ERROR", 749 cgiText(_("The class name may only contain up to " 750 "127 printable characters and may not " 751 "contain spaces, slashes (/), or the " 752 "pound sign (#)."))); 753 cgiStartHTML(title); 754 cgiCopyTemplateLang("error.tmpl"); 755 cgiEndHTML(); 756 return; 757 } 758 759 /* 760 * Build a CUPS_ADD_CLASS request, which requires the following 761 * attributes: 762 * 763 * attributes-charset 764 * attributes-natural-language 765 * printer-uri 766 * printer-location 767 * printer-info 768 * printer-is-accepting-jobs 769 * printer-state 770 * member-uris 771 */ 772 773 request = ippNewRequest(CUPS_ADD_CLASS); 774 775 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 776 "localhost", 0, "/classes/%s", name); 777 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 778 NULL, uri); 779 780 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", 781 NULL, cgiGetVariable("PRINTER_LOCATION")); 782 783 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", 784 NULL, cgiGetVariable("PRINTER_INFO")); 785 786 ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1); 787 788 ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", 789 IPP_PRINTER_IDLE); 790 791 if ((num_printers = cgiGetSize("MEMBER_URIS")) > 0) 792 { 793 attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_URI, "member-uris", 794 num_printers, NULL, NULL); 795 for (i = 0; i < num_printers; i ++) 796 attr->values[i].string.text = _cupsStrAlloc(cgiGetArray("MEMBER_URIS", i)); 797 } 798 799 /* 800 * Do the request and get back a response... 801 */ 802 803 ippDelete(cupsDoRequest(http, request, "/admin/")); 804 805 if (cupsLastError() == IPP_NOT_AUTHORIZED) 806 { 807 puts("Status: 401\n"); 808 exit(0); 809 } 810 else if (cupsLastError() > IPP_OK_CONFLICT) 811 { 812 cgiStartHTML(title); 813 cgiShowIPPError(modify ? _("Unable to modify class") : 814 _("Unable to add class")); 815 } 816 else 817 { 818 /* 819 * Redirect successful updates back to the class page... 820 */ 821 822 char refresh[1024]; /* Refresh URL */ 823 824 cgiFormEncode(uri, name, sizeof(uri)); 825 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=/classes/%s", 826 uri); 827 cgiSetVariable("refresh_page", refresh); 828 829 cgiStartHTML(title); 830 831 if (modify) 832 cgiCopyTemplateLang("class-modified.tmpl"); 833 else 834 cgiCopyTemplateLang("class-added.tmpl"); 835 } 836 837 cgiEndHTML(); 838} 839 840 841/* 842 * 'do_am_printer()' - Add or modify a printer. 843 */ 844 845static void 846do_am_printer(http_t *http, /* I - HTTP connection */ 847 int modify) /* I - Modify the printer? */ 848{ 849 int i; /* Looping var */ 850 ipp_attribute_t *attr; /* Current attribute */ 851 ipp_t *request, /* IPP request */ 852 *response, /* IPP response */ 853 *oldinfo; /* Old printer information */ 854 const cgi_file_t *file; /* Uploaded file, if any */ 855 const char *var; /* CGI variable */ 856 char uri[HTTP_MAX_URI], /* Device or printer URI */ 857 *uriptr; /* Pointer into URI */ 858 int maxrate; /* Maximum baud rate */ 859 char baudrate[255]; /* Baud rate string */ 860 const char *name, /* Pointer to class name */ 861 *ptr; /* Pointer to CGI variable */ 862 const char *title; /* Title of page */ 863 static int baudrates[] = /* Baud rates */ 864 { 865 1200, 866 2400, 867 4800, 868 9600, 869 19200, 870 38400, 871 57600, 872 115200, 873 230400, 874 460800 875 }; 876 877 878 ptr = cgiGetVariable("DEVICE_URI"); 879 fprintf(stderr, "DEBUG: do_am_printer: DEVICE_URI=\"%s\"\n", 880 ptr ? ptr : "(null)"); 881 882 title = cgiText(modify ? _("Modify Printer") : _("Add Printer")); 883 884 if (modify) 885 { 886 /* 887 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the 888 * following attributes: 889 * 890 * attributes-charset 891 * attributes-natural-language 892 * printer-uri 893 */ 894 895 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); 896 897 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 898 "localhost", 0, "/printers/%s", 899 cgiGetVariable("PRINTER_NAME")); 900 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 901 NULL, uri); 902 903 /* 904 * Do the request and get back a response... 905 */ 906 907 oldinfo = cupsDoRequest(http, request, "/"); 908 } 909 else 910 oldinfo = NULL; 911 912 file = cgiGetFile(); 913 914 if (file) 915 { 916 fprintf(stderr, "DEBUG: file->tempfile=%s\n", file->tempfile); 917 fprintf(stderr, "DEBUG: file->name=%s\n", file->name); 918 fprintf(stderr, "DEBUG: file->filename=%s\n", file->filename); 919 fprintf(stderr, "DEBUG: file->mimetype=%s\n", file->mimetype); 920 } 921 922 if ((name = cgiGetVariable("PRINTER_NAME")) != NULL) 923 { 924 for (ptr = name; *ptr; ptr ++) 925 if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#') 926 break; 927 928 if (*ptr || ptr == name || strlen(name) > 127) 929 { 930 cgiSetVariable("ERROR", 931 cgiText(_("The printer name may only contain up to " 932 "127 printable characters and may not " 933 "contain spaces, slashes (/), or the " 934 "pound sign (#)."))); 935 cgiStartHTML(title); 936 cgiCopyTemplateLang("error.tmpl"); 937 cgiEndHTML(); 938 return; 939 } 940 } 941 942 if ((var = cgiGetVariable("DEVICE_URI")) != NULL) 943 { 944 if ((uriptr = strrchr(var, '|')) != NULL) 945 { 946 /* 947 * Extract make and make/model from device URI string... 948 */ 949 950 char make[1024], /* Make string */ 951 *makeptr; /* Pointer into make */ 952 953 954 *uriptr++ = '\0'; 955 956 strlcpy(make, uriptr, sizeof(make)); 957 958 if ((makeptr = strchr(make, ' ')) != NULL) 959 *makeptr = '\0'; 960 else if ((makeptr = strchr(make, '-')) != NULL) 961 *makeptr = '\0'; 962 else if (!_cups_strncasecmp(make, "laserjet", 8) || 963 !_cups_strncasecmp(make, "deskjet", 7) || 964 !_cups_strncasecmp(make, "designjet", 9)) 965 strlcpy(make, "HP", sizeof(make)); 966 else if (!_cups_strncasecmp(make, "phaser", 6)) 967 strlcpy(make, "Xerox", sizeof(make)); 968 else if (!_cups_strncasecmp(make, "stylus", 6)) 969 strlcpy(make, "Epson", sizeof(make)); 970 else 971 strlcpy(make, "Generic", sizeof(make)); 972 973 if (!cgiGetVariable("CURRENT_MAKE")) 974 cgiSetVariable("CURRENT_MAKE", make); 975 976 if (!cgiGetVariable("CURRENT_MAKE_AND_MODEL")) 977 cgiSetVariable("CURRENT_MAKE_AND_MODEL", uriptr); 978 979 if (!modify) 980 { 981 char template[128], /* Template name */ 982 *tptr; /* Pointer into template name */ 983 984 cgiSetVariable("PRINTER_INFO", uriptr); 985 986 for (tptr = template; 987 tptr < (template + sizeof(template) - 1) && *uriptr; 988 uriptr ++) 989 if (isalnum(*uriptr & 255) || *uriptr == '_' || *uriptr == '-' || 990 *uriptr == '.') 991 *tptr++ = *uriptr; 992 else if ((*uriptr == ' ' || *uriptr == '/') && tptr > template && 993 tptr[-1] != '_') 994 *tptr++ = '_'; 995 else if (*uriptr == '?' || *uriptr == '(') 996 break; 997 998 *tptr = '\0'; 999 1000 cgiSetVariable("TEMPLATE_NAME", template); 1001 } 1002 } 1003 } 1004 1005 if (!var) 1006 { 1007 /* 1008 * Look for devices so the user can pick something... 1009 */ 1010 1011 if ((attr = ippFindAttribute(oldinfo, "device-uri", IPP_TAG_URI)) != NULL) 1012 { 1013 strlcpy(uri, attr->values[0].string.text, sizeof(uri)); 1014 if ((uriptr = strchr(uri, ':')) != NULL && strncmp(uriptr, "://", 3) == 0) 1015 *uriptr = '\0'; 1016 1017 cgiSetVariable("CURRENT_DEVICE_URI", attr->values[0].string.text); 1018 cgiSetVariable("CURRENT_DEVICE_SCHEME", uri); 1019 } 1020 1021 /* 1022 * Scan for devices for up to 30 seconds... 1023 */ 1024 1025 fputs("DEBUG: Getting list of devices...\n", stderr); 1026 1027 current_device = 0; 1028 if (cupsGetDevices(http, 5, CUPS_INCLUDE_ALL, CUPS_EXCLUDE_NONE, 1029 (cups_device_cb_t)choose_device_cb, 1030 (void *)title) == IPP_OK) 1031 { 1032 fputs("DEBUG: Got device list!\n", stderr); 1033 1034 if (cgiSupportsMultipart()) 1035 cgiStartMultipart(); 1036 1037 cgiSetVariable("CUPS_GET_DEVICES_DONE", "1"); 1038 cgiStartHTML(title); 1039 cgiCopyTemplateLang("choose-device.tmpl"); 1040 cgiEndHTML(); 1041 1042 if (cgiSupportsMultipart()) 1043 cgiEndMultipart(); 1044 } 1045 else 1046 { 1047 fprintf(stderr, 1048 "ERROR: CUPS-Get-Devices request failed with status %x: %s\n", 1049 cupsLastError(), cupsLastErrorString()); 1050 if (cupsLastError() == IPP_NOT_AUTHORIZED) 1051 { 1052 puts("Status: 401\n"); 1053 exit(0); 1054 } 1055 else 1056 { 1057 cgiStartHTML(title); 1058 cgiShowIPPError(modify ? _("Unable to modify printer") : 1059 _("Unable to add printer")); 1060 cgiEndHTML(); 1061 return; 1062 } 1063 } 1064 } 1065 else if (!strchr(var, '/') || 1066 (!strncmp(var, "lpd://", 6) && !strchr(var + 6, '/'))) 1067 { 1068 if ((attr = ippFindAttribute(oldinfo, "device-uri", IPP_TAG_URI)) != NULL) 1069 { 1070 /* 1071 * Set the current device URI for the form to the old one... 1072 */ 1073 1074 if (strncmp(attr->values[0].string.text, var, strlen(var)) == 0) 1075 cgiSetVariable("CURRENT_DEVICE_URI", attr->values[0].string.text); 1076 } 1077 1078 /* 1079 * User needs to set the full URI... 1080 */ 1081 1082 cgiStartHTML(title); 1083 cgiCopyTemplateLang("choose-uri.tmpl"); 1084 cgiEndHTML(); 1085 } 1086 else if (!strncmp(var, "serial:", 7) && !cgiGetVariable("BAUDRATE")) 1087 { 1088 /* 1089 * Need baud rate, parity, etc. 1090 */ 1091 1092 if ((var = strchr(var, '?')) != NULL && 1093 strncmp(var, "?baud=", 6) == 0) 1094 maxrate = atoi(var + 6); 1095 else 1096 maxrate = 19200; 1097 1098 for (i = 0; i < 10; i ++) 1099 if (baudrates[i] > maxrate) 1100 break; 1101 else 1102 { 1103 sprintf(baudrate, "%d", baudrates[i]); 1104 cgiSetArray("BAUDRATES", i, baudrate); 1105 } 1106 1107 cgiStartHTML(title); 1108 cgiCopyTemplateLang("choose-serial.tmpl"); 1109 cgiEndHTML(); 1110 } 1111 else if (!name || !cgiGetVariable("PRINTER_LOCATION")) 1112 { 1113 cgiStartHTML(title); 1114 1115 if (modify) 1116 { 1117 /* 1118 * Update the location and description of an existing printer... 1119 */ 1120 1121 if (oldinfo) 1122 { 1123 if ((attr = ippFindAttribute(oldinfo, "printer-info", 1124 IPP_TAG_TEXT)) != NULL) 1125 cgiSetVariable("PRINTER_INFO", attr->values[0].string.text); 1126 1127 if ((attr = ippFindAttribute(oldinfo, "printer-location", 1128 IPP_TAG_TEXT)) != NULL) 1129 cgiSetVariable("PRINTER_LOCATION", attr->values[0].string.text); 1130 1131 if ((attr = ippFindAttribute(oldinfo, "printer-is-shared", 1132 IPP_TAG_BOOLEAN)) != NULL) 1133 cgiSetVariable("PRINTER_IS_SHARED", 1134 attr->values[0].boolean ? "1" : "0"); 1135 } 1136 1137 cgiCopyTemplateLang("modify-printer.tmpl"); 1138 } 1139 else 1140 { 1141 /* 1142 * Get the name, location, and description for a new printer... 1143 */ 1144 1145#ifdef __APPLE__ 1146 if (!strncmp(var, "usb:", 4)) 1147 cgiSetVariable("printer_is_shared", "1"); 1148 else 1149#endif /* __APPLE__ */ 1150 cgiSetVariable("printer_is_shared", "0"); 1151 1152 cgiCopyTemplateLang("add-printer.tmpl"); 1153 } 1154 1155 cgiEndHTML(); 1156 1157 if (oldinfo) 1158 ippDelete(oldinfo); 1159 1160 return; 1161 } 1162 else if (!file && 1163 (!cgiGetVariable("PPD_NAME") || cgiGetVariable("SELECT_MAKE"))) 1164 { 1165 if (modify && !cgiGetVariable("SELECT_MAKE")) 1166 { 1167 /* 1168 * Get the PPD file... 1169 */ 1170 1171 int fd; /* PPD file */ 1172 char filename[1024]; /* PPD filename */ 1173 ppd_file_t *ppd; /* PPD information */ 1174 char buffer[1024]; /* Buffer */ 1175 int bytes; /* Number of bytes */ 1176 http_status_t get_status; /* Status of GET */ 1177 1178 1179 /* TODO: Use cupsGetFile() API... */ 1180 snprintf(uri, sizeof(uri), "/printers/%s.ppd", name); 1181 1182 if (httpGet(http, uri)) 1183 httpGet(http, uri); 1184 1185 while ((get_status = httpUpdate(http)) == HTTP_CONTINUE); 1186 1187 if (get_status != HTTP_OK) 1188 { 1189 httpFlush(http); 1190 1191 fprintf(stderr, "ERROR: Unable to get PPD file %s: %d - %s\n", 1192 uri, get_status, httpStatus(get_status)); 1193 } 1194 else if ((fd = cupsTempFd(filename, sizeof(filename))) >= 0) 1195 { 1196 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0) 1197 write(fd, buffer, bytes); 1198 1199 close(fd); 1200 1201 if ((ppd = ppdOpenFile(filename)) != NULL) 1202 { 1203 if (ppd->manufacturer) 1204 cgiSetVariable("CURRENT_MAKE", ppd->manufacturer); 1205 1206 if (ppd->nickname) 1207 cgiSetVariable("CURRENT_MAKE_AND_MODEL", ppd->nickname); 1208 1209 ppdClose(ppd); 1210 unlink(filename); 1211 } 1212 else 1213 { 1214 fprintf(stderr, "ERROR: Unable to open PPD file %s: %s\n", 1215 filename, ppdErrorString(ppdLastError(&bytes))); 1216 } 1217 } 1218 else 1219 { 1220 httpFlush(http); 1221 1222 fprintf(stderr, 1223 "ERROR: Unable to create temporary file for PPD file: %s\n", 1224 strerror(errno)); 1225 } 1226 } 1227 1228 /* 1229 * Build a CUPS_GET_PPDS request, which requires the following 1230 * attributes: 1231 * 1232 * attributes-charset 1233 * attributes-natural-language 1234 * printer-uri 1235 */ 1236 1237 request = ippNewRequest(CUPS_GET_PPDS); 1238 1239 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 1240 NULL, "ipp://localhost/printers/"); 1241 1242 if ((var = cgiGetVariable("PPD_MAKE")) == NULL) 1243 var = cgiGetVariable("CURRENT_MAKE"); 1244 if (var && !cgiGetVariable("SELECT_MAKE")) 1245 { 1246 const char *make_model; /* Make and model */ 1247 1248 1249 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, 1250 "ppd-make", NULL, var); 1251 1252 if ((make_model = cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL) 1253 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, 1254 "ppd-make-and-model", NULL, make_model); 1255 } 1256 else 1257 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 1258 "requested-attributes", NULL, "ppd-make"); 1259 1260 /* 1261 * Do the request and get back a response... 1262 */ 1263 1264 if ((response = cupsDoRequest(http, request, "/")) != NULL) 1265 { 1266 /* 1267 * Got the list of PPDs, see if the user has selected a make... 1268 */ 1269 1270 if (cgiSetIPPVars(response, NULL, NULL, NULL, 0) == 0 && !modify) 1271 { 1272 /* 1273 * No PPD files with this make, try again with all makes... 1274 */ 1275 1276 ippDelete(response); 1277 1278 request = ippNewRequest(CUPS_GET_PPDS); 1279 1280 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 1281 NULL, "ipp://localhost/printers/"); 1282 1283 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 1284 "requested-attributes", NULL, "ppd-make"); 1285 1286 if ((response = cupsDoRequest(http, request, "/")) != NULL) 1287 cgiSetIPPVars(response, NULL, NULL, NULL, 0); 1288 1289 cgiStartHTML(title); 1290 cgiCopyTemplateLang("choose-make.tmpl"); 1291 cgiEndHTML(); 1292 } 1293 else if (!var || cgiGetVariable("SELECT_MAKE")) 1294 { 1295 cgiStartHTML(title); 1296 cgiCopyTemplateLang("choose-make.tmpl"); 1297 cgiEndHTML(); 1298 } 1299 else 1300 { 1301 /* 1302 * Let the user choose a model... 1303 */ 1304 1305 cgiStartHTML(title); 1306 if (!cgiGetVariable("PPD_MAKE")) 1307 cgiSetVariable("PPD_MAKE", cgiGetVariable("CURRENT_MAKE")); 1308 if (!modify) 1309 cgiSetVariable("CURRENT_MAKE_AND_MODEL", 1310 cgiGetArray("PPD_MAKE_AND_MODEL", 0)); 1311 cgiCopyTemplateLang("choose-model.tmpl"); 1312 cgiEndHTML(); 1313 } 1314 1315 ippDelete(response); 1316 } 1317 else 1318 { 1319 cgiStartHTML(title); 1320 cgiShowIPPError(_("Unable to get list of printer drivers")); 1321 cgiCopyTemplateLang("error.tmpl"); 1322 cgiEndHTML(); 1323 } 1324 } 1325 else 1326 { 1327 /* 1328 * Build a CUPS_ADD_PRINTER request, which requires the following 1329 * attributes: 1330 * 1331 * attributes-charset 1332 * attributes-natural-language 1333 * printer-uri 1334 * printer-location 1335 * printer-info 1336 * ppd-name 1337 * device-uri 1338 * printer-is-accepting-jobs 1339 * printer-is-shared 1340 * printer-state 1341 */ 1342 1343 request = ippNewRequest(CUPS_ADD_PRINTER); 1344 1345 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 1346 "localhost", 0, "/printers/%s", 1347 cgiGetVariable("PRINTER_NAME")); 1348 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 1349 NULL, uri); 1350 1351 if (!file) 1352 { 1353 var = cgiGetVariable("PPD_NAME"); 1354 if (strcmp(var, "__no_change__")) 1355 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name", 1356 NULL, var); 1357 } 1358 1359 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", 1360 NULL, cgiGetVariable("PRINTER_LOCATION")); 1361 1362 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", 1363 NULL, cgiGetVariable("PRINTER_INFO")); 1364 1365 strlcpy(uri, cgiGetVariable("DEVICE_URI"), sizeof(uri)); 1366 1367 /* 1368 * Strip make and model from URI... 1369 */ 1370 1371 if ((uriptr = strrchr(uri, '|')) != NULL) 1372 *uriptr = '\0'; 1373 1374 if (!strncmp(uri, "serial:", 7)) 1375 { 1376 /* 1377 * Update serial port URI to include baud rate, etc. 1378 */ 1379 1380 if ((uriptr = strchr(uri, '?')) == NULL) 1381 uriptr = uri + strlen(uri); 1382 1383 snprintf(uriptr, sizeof(uri) - (uriptr - uri), 1384 "?baud=%s+bits=%s+parity=%s+flow=%s", 1385 cgiGetVariable("BAUDRATE"), cgiGetVariable("BITS"), 1386 cgiGetVariable("PARITY"), cgiGetVariable("FLOW")); 1387 } 1388 1389 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", 1390 NULL, uri); 1391 1392 ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1); 1393 1394 var = cgiGetVariable("printer_is_shared"); 1395 ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-shared", 1396 var && (!strcmp(var, "1") || !strcmp(var, "on"))); 1397 1398 ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", 1399 IPP_PRINTER_IDLE); 1400 1401 /* 1402 * Do the request and get back a response... 1403 */ 1404 1405 if (file) 1406 ippDelete(cupsDoFileRequest(http, request, "/admin/", file->tempfile)); 1407 else 1408 ippDelete(cupsDoRequest(http, request, "/admin/")); 1409 1410 if (cupsLastError() == IPP_NOT_AUTHORIZED) 1411 { 1412 puts("Status: 401\n"); 1413 exit(0); 1414 } 1415 else if (cupsLastError() > IPP_OK_CONFLICT) 1416 { 1417 cgiStartHTML(title); 1418 cgiShowIPPError(modify ? _("Unable to modify printer") : 1419 _("Unable to add printer")); 1420 } 1421 else if (modify) 1422 { 1423 /* 1424 * Redirect successful updates back to the printer page... 1425 */ 1426 1427 char refresh[1024]; /* Refresh URL */ 1428 1429 1430 cgiFormEncode(uri, name, sizeof(uri)); 1431 1432 snprintf(refresh, sizeof(refresh), 1433 "5;/admin/?OP=redirect&URL=/printers/%s", uri); 1434 1435 cgiSetVariable("refresh_page", refresh); 1436 1437 cgiStartHTML(title); 1438 1439 cgiCopyTemplateLang("printer-modified.tmpl"); 1440 } 1441 else 1442 { 1443 /* 1444 * Set the printer options... 1445 */ 1446 1447 cgiSetVariable("OP", "set-printer-options"); 1448 do_set_options(http, 0); 1449 return; 1450 } 1451 1452 cgiEndHTML(); 1453 } 1454 1455 if (oldinfo) 1456 ippDelete(oldinfo); 1457} 1458 1459 1460/* 1461 * 'do_cancel_subscription()' - Cancel a subscription. 1462 */ 1463 1464static void 1465do_cancel_subscription(http_t *http)/* I - HTTP connection */ 1466{ 1467 ipp_t *request; /* IPP request data */ 1468 const char *var, /* Form variable */ 1469 *user; /* Username */ 1470 int id; /* Subscription ID */ 1471 1472 1473 /* 1474 * See if we have all of the required information... 1475 */ 1476 1477 if ((var = cgiGetVariable("NOTIFY_SUBSCRIPTION_ID")) != NULL) 1478 id = atoi(var); 1479 else 1480 id = 0; 1481 1482 if (id <= 0) 1483 { 1484 cgiSetVariable("ERROR", cgiText(_("Bad subscription ID"))); 1485 cgiStartHTML(_("Cancel RSS Subscription")); 1486 cgiCopyTemplateLang("error.tmpl"); 1487 cgiEndHTML(); 1488 return; 1489 } 1490 1491 /* 1492 * Require a username... 1493 */ 1494 1495 if ((user = getenv("REMOTE_USER")) == NULL) 1496 { 1497 puts("Status: 401\n"); 1498 exit(0); 1499 } 1500 1501 /* 1502 * Cancel the subscription... 1503 */ 1504 1505 request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION); 1506 1507 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 1508 NULL, "ipp://localhost/"); 1509 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, 1510 "notify-subscription-id", id); 1511 1512 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", 1513 NULL, user); 1514 1515 ippDelete(cupsDoRequest(http, request, "/")); 1516 1517 if (cupsLastError() == IPP_NOT_AUTHORIZED) 1518 { 1519 puts("Status: 401\n"); 1520 exit(0); 1521 } 1522 else if (cupsLastError() > IPP_OK_CONFLICT) 1523 { 1524 cgiStartHTML(_("Cancel RSS Subscription")); 1525 cgiShowIPPError(_("Unable to cancel RSS subscription")); 1526 } 1527 else 1528 { 1529 /* 1530 * Redirect successful updates back to the admin page... 1531 */ 1532 1533 cgiSetVariable("refresh_page", "5;URL=/admin"); 1534 cgiStartHTML(_("Cancel RSS Subscription")); 1535 cgiCopyTemplateLang("subscription-canceled.tmpl"); 1536 } 1537 1538 cgiEndHTML(); 1539} 1540 1541 1542/* 1543 * 'do_config_server()' - Configure server settings. 1544 */ 1545 1546static void 1547do_config_server(http_t *http) /* I - HTTP connection */ 1548{ 1549 if (cgiGetVariable("CHANGESETTINGS")) 1550 { 1551 /* 1552 * Save basic setting changes... 1553 */ 1554 1555 int num_settings; /* Number of server settings */ 1556 cups_option_t *settings; /* Server settings */ 1557 int advanced, /* Advanced settings shown? */ 1558 changed; /* Have settings changed? */ 1559 const char *debug_logging, /* DEBUG_LOGGING value */ 1560 *preserve_jobs = NULL, 1561 /* PRESERVE_JOBS value */ 1562 *remote_admin, /* REMOTE_ADMIN value */ 1563 *remote_any, /* REMOTE_ANY value */ 1564 *share_printers,/* SHARE_PRINTERS value */ 1565 *user_cancel_any, 1566 /* USER_CANCEL_ANY value */ 1567 *browse_web_if = NULL, 1568 /* BrowseWebIF value */ 1569 *preserve_job_history = NULL, 1570 /* PreserveJobHistory value */ 1571 *preserve_job_files = NULL, 1572 /* PreserveJobFiles value */ 1573 *max_clients = NULL, 1574 /* MaxClients value */ 1575 *max_jobs = NULL, 1576 /* MaxJobs value */ 1577 *max_log_size = NULL; 1578 /* MaxLogSize value */ 1579 const char *current_browse_web_if, 1580 /* BrowseWebIF value */ 1581 *current_preserve_job_history, 1582 /* PreserveJobHistory value */ 1583 *current_preserve_job_files, 1584 /* PreserveJobFiles value */ 1585 *current_max_clients, 1586 /* MaxClients value */ 1587 *current_max_jobs, 1588 /* MaxJobs value */ 1589 *current_max_log_size; 1590 /* MaxLogSize value */ 1591#ifdef HAVE_GSSAPI 1592 char default_auth_type[255]; 1593 /* DefaultAuthType value */ 1594 const char *val; /* Setting value */ 1595#endif /* HAVE_GSSAPI */ 1596 1597 1598 /* 1599 * Get the checkbox values from the form... 1600 */ 1601 1602 debug_logging = cgiGetVariable("DEBUG_LOGGING") ? "1" : "0"; 1603 remote_admin = cgiGetVariable("REMOTE_ADMIN") ? "1" : "0"; 1604 remote_any = cgiGetVariable("REMOTE_ANY") ? "1" : "0"; 1605 share_printers = cgiGetVariable("SHARE_PRINTERS") ? "1" : "0"; 1606 user_cancel_any = cgiGetVariable("USER_CANCEL_ANY") ? "1" : "0"; 1607 1608 advanced = cgiGetVariable("ADVANCEDSETTINGS") != NULL; 1609 if (advanced) 1610 { 1611 /* 1612 * Get advanced settings... 1613 */ 1614 1615 browse_web_if = cgiGetVariable("BROWSE_WEB_IF") ? "Yes" : "No"; 1616 max_clients = cgiGetVariable("MAX_CLIENTS"); 1617 max_log_size = cgiGetVariable("MAX_LOG_SIZE"); 1618 preserve_jobs = cgiGetVariable("PRESERVE_JOBS"); 1619 1620 if (preserve_jobs) 1621 { 1622 max_jobs = cgiGetVariable("MAX_JOBS"); 1623 preserve_job_history = cgiGetVariable("PRESERVE_JOB_HISTORY"); 1624 preserve_job_files = cgiGetVariable("PRESERVE_JOB_FILES"); 1625 1626 if (!max_jobs || atoi(max_jobs) < 0) 1627 max_jobs = "500"; 1628 1629 if (!preserve_job_history) 1630 preserve_job_history = "On"; 1631 1632 if (!preserve_job_files) 1633 preserve_job_files = "1d"; 1634 } 1635 else 1636 { 1637 max_jobs = "0"; 1638 preserve_job_history = "No"; 1639 preserve_job_files = "No"; 1640 } 1641 1642 if (!max_clients || atoi(max_clients) <= 0) 1643 max_clients = "100"; 1644 1645 if (!max_log_size || atoi(max_log_size) <= 0.0) 1646 max_log_size = "1m"; 1647 } 1648 1649 /* 1650 * Get the current server settings... 1651 */ 1652 1653 if (!cupsAdminGetServerSettings(http, &num_settings, &settings)) 1654 { 1655 cgiStartHTML(cgiText(_("Change Settings"))); 1656 cgiSetVariable("MESSAGE", 1657 cgiText(_("Unable to change server settings"))); 1658 cgiSetVariable("ERROR", cupsLastErrorString()); 1659 cgiCopyTemplateLang("error.tmpl"); 1660 cgiEndHTML(); 1661 return; 1662 } 1663 1664#ifdef HAVE_GSSAPI 1665 /* 1666 * Get authentication settings... 1667 */ 1668 1669 if (cgiGetVariable("KERBEROS")) 1670 strlcpy(default_auth_type, "Negotiate", sizeof(default_auth_type)); 1671 else 1672 { 1673 val = cupsGetOption("DefaultAuthType", num_settings, settings); 1674 1675 if (!val || !_cups_strcasecmp(val, "Negotiate")) 1676 strlcpy(default_auth_type, "Basic", sizeof(default_auth_type)); 1677 else 1678 strlcpy(default_auth_type, val, sizeof(default_auth_type)); 1679 } 1680 1681 fprintf(stderr, "DEBUG: DefaultAuthType %s\n", default_auth_type); 1682#endif /* HAVE_GSSAPI */ 1683 1684 if ((current_browse_web_if = cupsGetOption("BrowseWebIF", num_settings, 1685 settings)) == NULL) 1686 current_browse_web_if = "No"; 1687 1688 if ((current_preserve_job_history = cupsGetOption("PreserveJobHistory", 1689 num_settings, 1690 settings)) == NULL) 1691 current_preserve_job_history = "Yes"; 1692 1693 if ((current_preserve_job_files = cupsGetOption("PreserveJobFiles", 1694 num_settings, 1695 settings)) == NULL) 1696 current_preserve_job_files = "1d"; 1697 1698 if ((current_max_clients = cupsGetOption("MaxClients", num_settings, 1699 settings)) == NULL) 1700 current_max_clients = "100"; 1701 1702 if ((current_max_jobs = cupsGetOption("MaxJobs", num_settings, 1703 settings)) == NULL) 1704 current_max_jobs = "500"; 1705 1706 if ((current_max_log_size = cupsGetOption("MaxLogSize", num_settings, 1707 settings)) == NULL) 1708 current_max_log_size = "1m"; 1709 1710 /* 1711 * See if the settings have changed... 1712 */ 1713 1714 changed = strcmp(debug_logging, cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, 1715 num_settings, settings)) || 1716 strcmp(remote_admin, cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, 1717 num_settings, settings)) || 1718 strcmp(remote_any, cupsGetOption(CUPS_SERVER_REMOTE_ANY, 1719 num_settings, settings)) || 1720 strcmp(share_printers, cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, 1721 num_settings, settings)) || 1722#ifdef HAVE_GSSAPI 1723 !cupsGetOption("DefaultAuthType", num_settings, settings) || 1724 strcmp(default_auth_type, cupsGetOption("DefaultAuthType", 1725 num_settings, settings)) || 1726#endif /* HAVE_GSSAPI */ 1727 strcmp(user_cancel_any, cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, 1728 num_settings, settings)); 1729 1730 if (advanced && !changed) 1731 changed = _cups_strcasecmp(browse_web_if, current_browse_web_if) || 1732 _cups_strcasecmp(preserve_job_history, current_preserve_job_history) || 1733 _cups_strcasecmp(preserve_job_files, current_preserve_job_files) || 1734 _cups_strcasecmp(max_clients, current_max_clients) || 1735 _cups_strcasecmp(max_jobs, current_max_jobs) || 1736 _cups_strcasecmp(max_log_size, current_max_log_size); 1737 1738 if (changed) 1739 { 1740 /* 1741 * Settings *have* changed, so save the changes... 1742 */ 1743 1744 cupsFreeOptions(num_settings, settings); 1745 1746 num_settings = 0; 1747 num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING, 1748 debug_logging, num_settings, &settings); 1749 num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN, 1750 remote_admin, num_settings, &settings); 1751 num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ANY, 1752 remote_any, num_settings, &settings); 1753 num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS, 1754 share_printers, num_settings, &settings); 1755 num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY, 1756 user_cancel_any, num_settings, &settings); 1757#ifdef HAVE_GSSAPI 1758 num_settings = cupsAddOption("DefaultAuthType", default_auth_type, 1759 num_settings, &settings); 1760#endif /* HAVE_GSSAPI */ 1761 1762 if (advanced) 1763 { 1764 /* 1765 * Add advanced settings... 1766 */ 1767 1768 if (_cups_strcasecmp(browse_web_if, current_browse_web_if)) 1769 num_settings = cupsAddOption("BrowseWebIF", browse_web_if, 1770 num_settings, &settings); 1771 if (_cups_strcasecmp(preserve_job_history, current_preserve_job_history)) 1772 num_settings = cupsAddOption("PreserveJobHistory", 1773 preserve_job_history, num_settings, 1774 &settings); 1775 if (_cups_strcasecmp(preserve_job_files, current_preserve_job_files)) 1776 num_settings = cupsAddOption("PreserveJobFiles", preserve_job_files, 1777 num_settings, &settings); 1778 if (_cups_strcasecmp(max_clients, current_max_clients)) 1779 num_settings = cupsAddOption("MaxClients", max_clients, num_settings, 1780 &settings); 1781 if (_cups_strcasecmp(max_jobs, current_max_jobs)) 1782 num_settings = cupsAddOption("MaxJobs", max_jobs, num_settings, 1783 &settings); 1784 if (_cups_strcasecmp(max_log_size, current_max_log_size)) 1785 num_settings = cupsAddOption("MaxLogSize", max_log_size, num_settings, 1786 &settings); 1787 } 1788 1789 if (!cupsAdminSetServerSettings(http, num_settings, settings)) 1790 { 1791 if (cupsLastError() == IPP_NOT_AUTHORIZED) 1792 { 1793 puts("Status: 401\n"); 1794 exit(0); 1795 } 1796 1797 cgiStartHTML(cgiText(_("Change Settings"))); 1798 cgiSetVariable("MESSAGE", 1799 cgiText(_("Unable to change server settings"))); 1800 cgiSetVariable("ERROR", cupsLastErrorString()); 1801 cgiCopyTemplateLang("error.tmpl"); 1802 } 1803 else 1804 { 1805 if (advanced) 1806 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&" 1807 "URL=/admin/?ADVANCEDSETTINGS=YES"); 1808 else 1809 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect"); 1810 cgiStartHTML(cgiText(_("Change Settings"))); 1811 cgiCopyTemplateLang("restart.tmpl"); 1812 } 1813 } 1814 else 1815 { 1816 /* 1817 * No changes... 1818 */ 1819 1820 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect"); 1821 cgiStartHTML(cgiText(_("Change Settings"))); 1822 cgiCopyTemplateLang("norestart.tmpl"); 1823 } 1824 1825 cupsFreeOptions(num_settings, settings); 1826 1827 cgiEndHTML(); 1828 } 1829 else if (cgiGetVariable("SAVECHANGES") && cgiGetVariable("CUPSDCONF")) 1830 { 1831 /* 1832 * Save hand-edited config file... 1833 */ 1834 1835 http_status_t status; /* PUT status */ 1836 char tempfile[1024]; /* Temporary new cupsd.conf */ 1837 int tempfd; /* Temporary file descriptor */ 1838 cups_file_t *temp; /* Temporary file */ 1839 const char *start, /* Start of line */ 1840 *end; /* End of line */ 1841 1842 1843 /* 1844 * Create a temporary file for the new cupsd.conf file... 1845 */ 1846 1847 if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) 1848 { 1849 cgiStartHTML(cgiText(_("Edit Configuration File"))); 1850 cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file"))); 1851 cgiSetVariable("ERROR", strerror(errno)); 1852 cgiCopyTemplateLang("error.tmpl"); 1853 cgiEndHTML(); 1854 1855 perror(tempfile); 1856 return; 1857 } 1858 1859 if ((temp = cupsFileOpenFd(tempfd, "w")) == NULL) 1860 { 1861 cgiStartHTML(cgiText(_("Edit Configuration File"))); 1862 cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file"))); 1863 cgiSetVariable("ERROR", strerror(errno)); 1864 cgiCopyTemplateLang("error.tmpl"); 1865 cgiEndHTML(); 1866 1867 perror(tempfile); 1868 close(tempfd); 1869 unlink(tempfile); 1870 return; 1871 } 1872 1873 /* 1874 * Copy the cupsd.conf text from the form variable... 1875 */ 1876 1877 start = cgiGetVariable("CUPSDCONF"); 1878 while (start) 1879 { 1880 if ((end = strstr(start, "\r\n")) == NULL) 1881 if ((end = strstr(start, "\n")) == NULL) 1882 end = start + strlen(start); 1883 1884 cupsFileWrite(temp, start, end - start); 1885 cupsFilePutChar(temp, '\n'); 1886 1887 if (*end == '\r') 1888 start = end + 2; 1889 else if (*end == '\n') 1890 start = end + 1; 1891 else 1892 start = NULL; 1893 } 1894 1895 cupsFileClose(temp); 1896 1897 /* 1898 * Upload the configuration file to the server... 1899 */ 1900 1901 status = cupsPutFile(http, "/admin/conf/cupsd.conf", tempfile); 1902 1903 if (status == HTTP_UNAUTHORIZED) 1904 { 1905 puts("Status: 401\n"); 1906 unlink(tempfile); 1907 exit(0); 1908 } 1909 else if (status != HTTP_CREATED) 1910 { 1911 cgiSetVariable("MESSAGE", 1912 cgiText(_("Unable to upload cupsd.conf file"))); 1913 cgiSetVariable("ERROR", httpStatus(status)); 1914 1915 cgiStartHTML(cgiText(_("Edit Configuration File"))); 1916 cgiCopyTemplateLang("error.tmpl"); 1917 } 1918 else 1919 { 1920 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect"); 1921 1922 cgiStartHTML(cgiText(_("Edit Configuration File"))); 1923 cgiCopyTemplateLang("restart.tmpl"); 1924 } 1925 1926 cgiEndHTML(); 1927 1928 unlink(tempfile); 1929 } 1930 else 1931 { 1932 struct stat info; /* cupsd.conf information */ 1933 cups_file_t *cupsd; /* cupsd.conf file */ 1934 char *buffer, /* Buffer for entire file */ 1935 *bufptr, /* Pointer into buffer */ 1936 *bufend; /* End of buffer */ 1937 int ch; /* Character from file */ 1938 char filename[1024]; /* Filename */ 1939 const char *server_root; /* Location of config files */ 1940 1941 1942 /* 1943 * Locate the cupsd.conf file... 1944 */ 1945 1946 if ((server_root = getenv("CUPS_SERVERROOT")) == NULL) 1947 server_root = CUPS_SERVERROOT; 1948 1949 snprintf(filename, sizeof(filename), "%s/cupsd.conf", server_root); 1950 1951 /* 1952 * Figure out the size... 1953 */ 1954 1955 if (stat(filename, &info)) 1956 { 1957 cgiStartHTML(cgiText(_("Edit Configuration File"))); 1958 cgiSetVariable("MESSAGE", 1959 cgiText(_("Unable to access cupsd.conf file"))); 1960 cgiSetVariable("ERROR", strerror(errno)); 1961 cgiCopyTemplateLang("error.tmpl"); 1962 cgiEndHTML(); 1963 1964 perror(filename); 1965 return; 1966 } 1967 1968 if (info.st_size > (1024 * 1024)) 1969 { 1970 cgiStartHTML(cgiText(_("Edit Configuration File"))); 1971 cgiSetVariable("MESSAGE", 1972 cgiText(_("Unable to access cupsd.conf file"))); 1973 cgiSetVariable("ERROR", 1974 cgiText(_("Unable to edit cupsd.conf files larger than " 1975 "1MB"))); 1976 cgiCopyTemplateLang("error.tmpl"); 1977 cgiEndHTML(); 1978 1979 fprintf(stderr, "ERROR: \"%s\" too large (%ld) to edit!\n", filename, 1980 (long)info.st_size); 1981 return; 1982 } 1983 1984 /* 1985 * Open the cupsd.conf file... 1986 */ 1987 1988 if ((cupsd = cupsFileOpen(filename, "r")) == NULL) 1989 { 1990 /* 1991 * Unable to open - log an error... 1992 */ 1993 1994 cgiStartHTML(cgiText(_("Edit Configuration File"))); 1995 cgiSetVariable("MESSAGE", 1996 cgiText(_("Unable to access cupsd.conf file"))); 1997 cgiSetVariable("ERROR", strerror(errno)); 1998 cgiCopyTemplateLang("error.tmpl"); 1999 cgiEndHTML(); 2000 2001 perror(filename); 2002 return; 2003 } 2004 2005 /* 2006 * Allocate memory and load the file into a string buffer... 2007 */ 2008 2009 if ((buffer = calloc(1, info.st_size + 1)) != NULL) 2010 { 2011 cupsFileRead(cupsd, buffer, info.st_size); 2012 cgiSetVariable("CUPSDCONF", buffer); 2013 free(buffer); 2014 } 2015 2016 cupsFileClose(cupsd); 2017 2018 /* 2019 * Then get the default cupsd.conf file and put that into a string as 2020 * well... 2021 */ 2022 2023 strlcat(filename, ".default", sizeof(filename)); 2024 2025 if (!stat(filename, &info) && info.st_size < (1024 * 1024) && 2026 (cupsd = cupsFileOpen(filename, "r")) != NULL) 2027 { 2028 if ((buffer = calloc(1, 2 * info.st_size + 1)) != NULL) 2029 { 2030 bufend = buffer + 2 * info.st_size - 1; 2031 2032 for (bufptr = buffer; 2033 bufptr < bufend && (ch = cupsFileGetChar(cupsd)) != EOF;) 2034 { 2035 if (ch == '\\' || ch == '\"') 2036 { 2037 *bufptr++ = '\\'; 2038 *bufptr++ = ch; 2039 } 2040 else if (ch == '\n') 2041 { 2042 *bufptr++ = '\\'; 2043 *bufptr++ = 'n'; 2044 } 2045 else if (ch == '\t') 2046 { 2047 *bufptr++ = '\\'; 2048 *bufptr++ = 't'; 2049 } 2050 else if (ch >= ' ') 2051 *bufptr++ = ch; 2052 } 2053 2054 *bufptr = '\0'; 2055 2056 cgiSetVariable("CUPSDCONF_DEFAULT", buffer); 2057 free(buffer); 2058 } 2059 2060 cupsFileClose(cupsd); 2061 } 2062 2063 /* 2064 * Show the current config file... 2065 */ 2066 2067 cgiStartHTML(cgiText(_("Edit Configuration File"))); 2068 2069 cgiCopyTemplateLang("edit-config.tmpl"); 2070 2071 cgiEndHTML(); 2072 } 2073} 2074 2075 2076/* 2077 * 'do_delete_class()' - Delete a class. 2078 */ 2079 2080static void 2081do_delete_class(http_t *http) /* I - HTTP connection */ 2082{ 2083 ipp_t *request; /* IPP request */ 2084 char uri[HTTP_MAX_URI]; /* Job URI */ 2085 const char *pclass; /* Printer class name */ 2086 2087 2088 /* 2089 * Get form variables... 2090 */ 2091 2092 if (cgiGetVariable("CONFIRM") == NULL) 2093 { 2094 cgiStartHTML(cgiText(_("Delete Class"))); 2095 cgiCopyTemplateLang("class-confirm.tmpl"); 2096 cgiEndHTML(); 2097 return; 2098 } 2099 2100 if ((pclass = cgiGetVariable("PRINTER_NAME")) != NULL) 2101 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 2102 "localhost", 0, "/classes/%s", pclass); 2103 else 2104 { 2105 cgiStartHTML(cgiText(_("Delete Class"))); 2106 cgiSetVariable("ERROR", cgiText(_("Missing form variable"))); 2107 cgiCopyTemplateLang("error.tmpl"); 2108 cgiEndHTML(); 2109 return; 2110 } 2111 2112 /* 2113 * Build a CUPS_DELETE_CLASS request, which requires the following 2114 * attributes: 2115 * 2116 * attributes-charset 2117 * attributes-natural-language 2118 * printer-uri 2119 */ 2120 2121 request = ippNewRequest(CUPS_DELETE_CLASS); 2122 2123 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 2124 NULL, uri); 2125 2126 /* 2127 * Do the request and get back a response... 2128 */ 2129 2130 ippDelete(cupsDoRequest(http, request, "/admin/")); 2131 2132 /* 2133 * Show the results... 2134 */ 2135 2136 if (cupsLastError() == IPP_NOT_AUTHORIZED) 2137 { 2138 puts("Status: 401\n"); 2139 exit(0); 2140 } 2141 else if (cupsLastError() <= IPP_OK_CONFLICT) 2142 { 2143 /* 2144 * Redirect successful updates back to the classes page... 2145 */ 2146 2147 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/classes"); 2148 } 2149 2150 cgiStartHTML(cgiText(_("Delete Class"))); 2151 2152 if (cupsLastError() > IPP_OK_CONFLICT) 2153 cgiShowIPPError(_("Unable to delete class")); 2154 else 2155 cgiCopyTemplateLang("class-deleted.tmpl"); 2156 2157 cgiEndHTML(); 2158} 2159 2160 2161/* 2162 * 'do_delete_printer()' - Delete a printer. 2163 */ 2164 2165static void 2166do_delete_printer(http_t *http) /* I - HTTP connection */ 2167{ 2168 ipp_t *request; /* IPP request */ 2169 char uri[HTTP_MAX_URI]; /* Job URI */ 2170 const char *printer; /* Printer printer name */ 2171 2172 2173 /* 2174 * Get form variables... 2175 */ 2176 2177 if (cgiGetVariable("CONFIRM") == NULL) 2178 { 2179 cgiStartHTML(cgiText(_("Delete Printer"))); 2180 cgiCopyTemplateLang("printer-confirm.tmpl"); 2181 cgiEndHTML(); 2182 return; 2183 } 2184 2185 if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL) 2186 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 2187 "localhost", 0, "/printers/%s", printer); 2188 else 2189 { 2190 cgiStartHTML(cgiText(_("Delete Printer"))); 2191 cgiSetVariable("ERROR", cgiText(_("Missing form variable"))); 2192 cgiCopyTemplateLang("error.tmpl"); 2193 cgiEndHTML(); 2194 return; 2195 } 2196 2197 /* 2198 * Build a CUPS_DELETE_PRINTER request, which requires the following 2199 * attributes: 2200 * 2201 * attributes-charset 2202 * attributes-natural-language 2203 * printer-uri 2204 */ 2205 2206 request = ippNewRequest(CUPS_DELETE_PRINTER); 2207 2208 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 2209 NULL, uri); 2210 2211 /* 2212 * Do the request and get back a response... 2213 */ 2214 2215 ippDelete(cupsDoRequest(http, request, "/admin/")); 2216 2217 /* 2218 * Show the results... 2219 */ 2220 2221 if (cupsLastError() == IPP_NOT_AUTHORIZED) 2222 { 2223 puts("Status: 401\n"); 2224 exit(0); 2225 } 2226 else if (cupsLastError() <= IPP_OK_CONFLICT) 2227 { 2228 /* 2229 * Redirect successful updates back to the printers page... 2230 */ 2231 2232 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/printers"); 2233 } 2234 2235 cgiStartHTML(cgiText(_("Delete Printer"))); 2236 2237 if (cupsLastError() > IPP_OK_CONFLICT) 2238 cgiShowIPPError(_("Unable to delete printer")); 2239 else 2240 cgiCopyTemplateLang("printer-deleted.tmpl"); 2241 2242 cgiEndHTML(); 2243} 2244 2245 2246/* 2247 * 'do_export()' - Export printers to Samba. 2248 */ 2249 2250static void 2251do_export(http_t *http) /* I - HTTP connection */ 2252{ 2253 int i, j; /* Looping vars */ 2254 ipp_t *request, /* IPP request */ 2255 *response; /* IPP response */ 2256 const char *username, /* Samba username */ 2257 *password, /* Samba password */ 2258 *export_all; /* Export all printers? */ 2259 int export_count, /* Number of printers to export */ 2260 printer_count; /* Number of available printers */ 2261 const char *name, /* What name to pull */ 2262 *dest; /* Current destination */ 2263 char ppd[1024]; /* PPD file */ 2264 2265 2266 /* 2267 * Get form data... 2268 */ 2269 2270 username = cgiGetVariable("USERNAME"); 2271 password = cgiGetVariable("PASSWORD"); 2272 export_all = cgiGetVariable("EXPORT_ALL"); 2273 export_count = cgiGetSize("EXPORT_NAME"); 2274 2275 /* 2276 * Get list of available printers... 2277 */ 2278 2279 cgiSetSize("PRINTER_NAME", 0); 2280 cgiSetSize("PRINTER_EXPORT", 0); 2281 2282 request = ippNewRequest(CUPS_GET_PRINTERS); 2283 2284 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, 2285 "printer-type", 0); 2286 2287 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, 2288 "printer-type-mask", CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE); 2289 2290 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 2291 "requested-attributes", NULL, "printer-name"); 2292 2293 if ((response = cupsDoRequest(http, request, "/")) != NULL) 2294 { 2295 cgiSetIPPVars(response, NULL, NULL, NULL, 0); 2296 ippDelete(response); 2297 2298 if (!export_all) 2299 { 2300 printer_count = cgiGetSize("PRINTER_NAME"); 2301 2302 for (i = 0; i < printer_count; i ++) 2303 { 2304 dest = cgiGetArray("PRINTER_NAME", i); 2305 2306 for (j = 0; j < export_count; j ++) 2307 if (!_cups_strcasecmp(dest, cgiGetArray("EXPORT_NAME", j))) 2308 break; 2309 2310 cgiSetArray("PRINTER_EXPORT", i, j < export_count ? "Y" : ""); 2311 } 2312 } 2313 } 2314 2315 /* 2316 * Export or get the printers to export... 2317 */ 2318 2319 if (username && *username && password && *password && 2320 (export_all || export_count > 0)) 2321 { 2322 /* 2323 * Do export... 2324 */ 2325 2326 fputs("DEBUG: Export printers...\n", stderr); 2327 2328 if (export_all) 2329 { 2330 name = "PRINTER_NAME"; 2331 export_count = cgiGetSize("PRINTER_NAME"); 2332 } 2333 else 2334 name = "EXPORT_NAME"; 2335 2336 for (i = 0; i < export_count; i ++) 2337 { 2338 dest = cgiGetArray(name, i); 2339 2340 if (!cupsAdminCreateWindowsPPD(http, dest, ppd, sizeof(ppd))) 2341 break; 2342 2343 j = cupsAdminExportSamba(dest, ppd, "localhost", username, password, 2344 stderr); 2345 2346 unlink(ppd); 2347 2348 if (!j) 2349 break; 2350 } 2351 2352 if (i < export_count) 2353 cgiSetVariable("ERROR", cupsLastErrorString()); 2354 else 2355 { 2356 cgiStartHTML(cgiText(_("Export Printers to Samba"))); 2357 cgiCopyTemplateLang("samba-exported.tmpl"); 2358 cgiEndHTML(); 2359 return; 2360 } 2361 } 2362 else if (username && !*username) 2363 cgiSetVariable("ERROR", 2364 cgiText(_("A Samba username is required to export " 2365 "printer drivers"))); 2366 else if (username && (!password || !*password)) 2367 cgiSetVariable("ERROR", 2368 cgiText(_("A Samba password is required to export " 2369 "printer drivers"))); 2370 2371 /* 2372 * Show form... 2373 */ 2374 2375 cgiStartHTML(cgiText(_("Export Printers to Samba"))); 2376 cgiCopyTemplateLang("samba-export.tmpl"); 2377 cgiEndHTML(); 2378} 2379 2380 2381/* 2382 * 'do_list_printers()' - List available printers. 2383 */ 2384 2385static void 2386do_list_printers(http_t *http) /* I - HTTP connection */ 2387{ 2388 ipp_t *request, /* IPP request */ 2389 *response; /* IPP response */ 2390 ipp_attribute_t *attr; /* IPP attribute */ 2391 2392 2393 cgiStartHTML(cgiText(_("List Available Printers"))); 2394 fflush(stdout); 2395 2396 /* 2397 * Get the list of printers and their devices... 2398 */ 2399 2400 request = ippNewRequest(CUPS_GET_PRINTERS); 2401 2402 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 2403 "requested-attributes", NULL, "device-uri"); 2404 2405 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type", 2406 CUPS_PRINTER_LOCAL); 2407 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask", 2408 CUPS_PRINTER_LOCAL); 2409 2410 if ((response = cupsDoRequest(http, request, "/")) != NULL) 2411 { 2412 /* 2413 * Got the printer list, now load the devices... 2414 */ 2415 2416 int i; /* Looping var */ 2417 cups_array_t *printer_devices; /* Printer devices for local printers */ 2418 char *printer_device; /* Current printer device */ 2419 2420 2421 /* 2422 * Allocate an array and copy the device strings... 2423 */ 2424 2425 printer_devices = cupsArrayNew((cups_array_func_t)strcmp, NULL); 2426 2427 for (attr = ippFindAttribute(response, "device-uri", IPP_TAG_URI); 2428 attr; 2429 attr = ippFindNextAttribute(response, "device-uri", IPP_TAG_URI)) 2430 { 2431 cupsArrayAdd(printer_devices, _cupsStrAlloc(attr->values[0].string.text)); 2432 } 2433 2434 /* 2435 * Free the printer list and get the device list... 2436 */ 2437 2438 ippDelete(response); 2439 2440 request = ippNewRequest(CUPS_GET_DEVICES); 2441 2442 if ((response = cupsDoRequest(http, request, "/")) != NULL) 2443 { 2444 /* 2445 * Got the device list, let's parse it... 2446 */ 2447 2448 const char *device_uri, /* device-uri attribute value */ 2449 *device_make_and_model, /* device-make-and-model value */ 2450 *device_info; /* device-info value */ 2451 2452 2453 for (i = 0, attr = response->attrs; attr; attr = attr->next) 2454 { 2455 /* 2456 * Skip leading attributes until we hit a device... 2457 */ 2458 2459 while (attr && attr->group_tag != IPP_TAG_PRINTER) 2460 attr = attr->next; 2461 2462 if (!attr) 2463 break; 2464 2465 /* 2466 * Pull the needed attributes from this device... 2467 */ 2468 2469 device_info = NULL; 2470 device_make_and_model = NULL; 2471 device_uri = NULL; 2472 2473 while (attr && attr->group_tag == IPP_TAG_PRINTER) 2474 { 2475 if (!strcmp(attr->name, "device-info") && 2476 attr->value_tag == IPP_TAG_TEXT) 2477 device_info = attr->values[0].string.text; 2478 2479 if (!strcmp(attr->name, "device-make-and-model") && 2480 attr->value_tag == IPP_TAG_TEXT) 2481 device_make_and_model = attr->values[0].string.text; 2482 2483 if (!strcmp(attr->name, "device-uri") && 2484 attr->value_tag == IPP_TAG_URI) 2485 device_uri = attr->values[0].string.text; 2486 2487 attr = attr->next; 2488 } 2489 2490 /* 2491 * See if we have everything needed... 2492 */ 2493 2494 if (device_info && device_make_and_model && device_uri && 2495 _cups_strcasecmp(device_make_and_model, "unknown") && 2496 strchr(device_uri, ':')) 2497 { 2498 /* 2499 * Yes, now see if there is already a printer for this 2500 * device... 2501 */ 2502 2503 if (!cupsArrayFind(printer_devices, (void *)device_uri)) 2504 { 2505 /* 2506 * Not found, so it must be a new printer... 2507 */ 2508 2509 char option[1024], /* Form variables for this device */ 2510 *option_ptr; /* Pointer into string */ 2511 const char *ptr; /* Pointer into device string */ 2512 2513 2514 /* 2515 * Format the printer name variable for this device... 2516 * 2517 * We use the device-info string first, then device-uri, 2518 * and finally device-make-and-model to come up with a 2519 * suitable name. 2520 */ 2521 2522 if (_cups_strncasecmp(device_info, "unknown", 7)) 2523 ptr = device_info; 2524 else if ((ptr = strstr(device_uri, "://")) != NULL) 2525 ptr += 3; 2526 else 2527 ptr = device_make_and_model; 2528 2529 for (option_ptr = option; 2530 option_ptr < (option + sizeof(option) - 1) && *ptr; 2531 ptr ++) 2532 if (isalnum(*ptr & 255) || *ptr == '_' || *ptr == '-' || 2533 *ptr == '.') 2534 *option_ptr++ = *ptr; 2535 else if ((*ptr == ' ' || *ptr == '/') && option_ptr > option && 2536 option_ptr[-1] != '_') 2537 *option_ptr++ = '_'; 2538 else if (*ptr == '?' || *ptr == '(') 2539 break; 2540 2541 *option_ptr = '\0'; 2542 2543 cgiSetArray("TEMPLATE_NAME", i, option); 2544 2545 /* 2546 * Finally, set the form variables for this printer... 2547 */ 2548 2549 cgiSetArray("device_info", i, device_info); 2550 cgiSetArray("device_make_and_model", i, device_make_and_model); 2551 cgiSetArray("device_uri", i, device_uri); 2552 i ++; 2553 } 2554 } 2555 2556 if (!attr) 2557 break; 2558 } 2559 2560 ippDelete(response); 2561 2562 /* 2563 * Free the device list... 2564 */ 2565 2566 for (printer_device = (char *)cupsArrayFirst(printer_devices); 2567 printer_device; 2568 printer_device = (char *)cupsArrayNext(printer_devices)) 2569 _cupsStrFree(printer_device); 2570 2571 cupsArrayDelete(printer_devices); 2572 } 2573 } 2574 2575 /* 2576 * Finally, show the printer list... 2577 */ 2578 2579 cgiCopyTemplateLang("list-available-printers.tmpl"); 2580 2581 cgiEndHTML(); 2582} 2583 2584 2585/* 2586 * 'do_menu()' - Show the main menu. 2587 */ 2588 2589static void 2590do_menu(http_t *http) /* I - HTTP connection */ 2591{ 2592 int num_settings; /* Number of server settings */ 2593 cups_option_t *settings; /* Server settings */ 2594 const char *val; /* Setting value */ 2595 char filename[1024]; /* Temporary filename */ 2596 const char *datadir; /* Location of data files */ 2597 ipp_t *request, /* IPP request */ 2598 *response; /* IPP response */ 2599 2600 2601 /* 2602 * Get the current server settings... 2603 */ 2604 2605 if (!cupsAdminGetServerSettings(http, &num_settings, &settings)) 2606 { 2607 cgiSetVariable("SETTINGS_MESSAGE", 2608 cgiText(_("Unable to open cupsd.conf file:"))); 2609 cgiSetVariable("SETTINGS_ERROR", cupsLastErrorString()); 2610 } 2611 2612 if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings, 2613 settings)) != NULL && atoi(val)) 2614 cgiSetVariable("DEBUG_LOGGING", "CHECKED"); 2615 2616 if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings, 2617 settings)) != NULL && atoi(val)) 2618 cgiSetVariable("REMOTE_ADMIN", "CHECKED"); 2619 2620 if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, num_settings, 2621 settings)) != NULL && atoi(val)) 2622 cgiSetVariable("REMOTE_ANY", "CHECKED"); 2623 2624 if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings, 2625 settings)) != NULL && atoi(val)) 2626 cgiSetVariable("SHARE_PRINTERS", "CHECKED"); 2627 2628 if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, num_settings, 2629 settings)) != NULL && atoi(val)) 2630 cgiSetVariable("USER_CANCEL_ANY", "CHECKED"); 2631 2632#ifdef HAVE_GSSAPI 2633 cgiSetVariable("HAVE_GSSAPI", "1"); 2634 2635 if ((val = cupsGetOption("DefaultAuthType", num_settings, 2636 settings)) != NULL && !_cups_strcasecmp(val, "Negotiate")) 2637 cgiSetVariable("KERBEROS", "CHECKED"); 2638 else 2639#endif /* HAVE_GSSAPI */ 2640 cgiSetVariable("KERBEROS", ""); 2641 2642 if ((val = cupsGetOption("BrowseWebIF", num_settings, 2643 settings)) == NULL) 2644 val = "No"; 2645 2646 if (!_cups_strcasecmp(val, "yes") || !_cups_strcasecmp(val, "on") || 2647 !_cups_strcasecmp(val, "true")) 2648 cgiSetVariable("BROWSE_WEB_IF", "CHECKED"); 2649 2650 if ((val = cupsGetOption("PreserveJobHistory", num_settings, 2651 settings)) == NULL) 2652 val = "Yes"; 2653 2654 if (val && 2655 (!_cups_strcasecmp(val, "0") || !_cups_strcasecmp(val, "no") || 2656 !_cups_strcasecmp(val, "off") || !_cups_strcasecmp(val, "false") || 2657 !_cups_strcasecmp(val, "disabled"))) 2658 { 2659 cgiSetVariable("PRESERVE_JOB_HISTORY", "0"); 2660 cgiSetVariable("PRESERVE_JOB_FILES", "0"); 2661 } 2662 else 2663 { 2664 cgiSetVariable("PRESERVE_JOBS", "CHECKED"); 2665 cgiSetVariable("PRESERVE_JOB_HISTORY", val); 2666 2667 if ((val = cupsGetOption("PreserveJobFiles", num_settings, 2668 settings)) == NULL) 2669 val = "1d"; 2670 2671 cgiSetVariable("PRESERVE_JOB_FILES", val); 2672 2673 } 2674 2675 if ((val = cupsGetOption("MaxClients", num_settings, settings)) == NULL) 2676 val = "100"; 2677 2678 cgiSetVariable("MAX_CLIENTS", val); 2679 2680 if ((val = cupsGetOption("MaxJobs", num_settings, settings)) == NULL) 2681 val = "500"; 2682 2683 cgiSetVariable("MAX_JOBS", val); 2684 2685 if ((val = cupsGetOption("MaxLogSize", num_settings, settings)) == NULL) 2686 val = "1m"; 2687 2688 cgiSetVariable("MAX_LOG_SIZE", val); 2689 2690 cupsFreeOptions(num_settings, settings); 2691 2692 /* 2693 * See if Samba and the Windows drivers are installed... 2694 */ 2695 2696 if ((datadir = getenv("CUPS_DATADIR")) == NULL) 2697 datadir = CUPS_DATADIR; 2698 2699 snprintf(filename, sizeof(filename), "%s/drivers/pscript5.dll", datadir); 2700 if (!access(filename, R_OK)) 2701 { 2702 /* 2703 * Found Windows 2000 driver file, see if we have smbclient and 2704 * rpcclient... 2705 */ 2706 2707 if (cupsFileFind("smbclient", getenv("PATH"), 1, filename, 2708 sizeof(filename)) && 2709 cupsFileFind("rpcclient", getenv("PATH"), 1, filename, 2710 sizeof(filename))) 2711 cgiSetVariable("HAVE_SAMBA", "Y"); 2712 else 2713 { 2714 if (!cupsFileFind("smbclient", getenv("PATH"), 1, filename, 2715 sizeof(filename))) 2716 fputs("ERROR: smbclient not found!\n", stderr); 2717 2718 if (!cupsFileFind("rpcclient", getenv("PATH"), 1, filename, 2719 sizeof(filename))) 2720 fputs("ERROR: rpcclient not found!\n", stderr); 2721 } 2722 } 2723 else 2724 perror(filename); 2725 2726 /* 2727 * Subscriptions... 2728 */ 2729 2730 request = ippNewRequest(IPP_GET_SUBSCRIPTIONS); 2731 2732 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 2733 NULL, "ipp://localhost/"); 2734 2735 if ((response = cupsDoRequest(http, request, "/")) != NULL) 2736 { 2737 cgiSetIPPVars(response, NULL, NULL, NULL, 0); 2738 ippDelete(response); 2739 } 2740 2741 /* 2742 * Finally, show the main menu template... 2743 */ 2744 2745 cgiStartHTML(cgiText(_("Administration"))); 2746 2747 cgiCopyTemplateLang("admin.tmpl"); 2748 2749 cgiEndHTML(); 2750} 2751 2752 2753/* 2754 * 'do_set_allowed_users()' - Set the allowed/denied users for a queue. 2755 */ 2756 2757static void 2758do_set_allowed_users(http_t *http) /* I - HTTP connection */ 2759{ 2760 int i; /* Looping var */ 2761 ipp_t *request, /* IPP request */ 2762 *response; /* IPP response */ 2763 char uri[HTTP_MAX_URI]; /* Printer URI */ 2764 const char *printer, /* Printer name (purge-jobs) */ 2765 *is_class, /* Is a class? */ 2766 *users, /* List of users or groups */ 2767 *type; /* Allow/deny type */ 2768 int num_users; /* Number of users */ 2769 char *ptr, /* Pointer into users string */ 2770 *end, /* Pointer to end of users string */ 2771 quote; /* Quote character */ 2772 ipp_attribute_t *attr; /* Attribute */ 2773 static const char * const attrs[] = /* Requested attributes */ 2774 { 2775 "requesting-user-name-allowed", 2776 "requesting-user-name-denied" 2777 }; 2778 2779 2780 is_class = cgiGetVariable("IS_CLASS"); 2781 printer = cgiGetVariable("PRINTER_NAME"); 2782 2783 if (!printer) 2784 { 2785 cgiSetVariable("ERROR", cgiText(_("Missing form variable"))); 2786 cgiStartHTML(cgiText(_("Set Allowed Users"))); 2787 cgiCopyTemplateLang("error.tmpl"); 2788 cgiEndHTML(); 2789 return; 2790 } 2791 2792 users = cgiGetVariable("users"); 2793 type = cgiGetVariable("type"); 2794 2795 if (!users || !type || 2796 (strcmp(type, "requesting-user-name-allowed") && 2797 strcmp(type, "requesting-user-name-denied"))) 2798 { 2799 /* 2800 * Build a Get-Printer-Attributes request, which requires the following 2801 * attributes: 2802 * 2803 * attributes-charset 2804 * attributes-natural-language 2805 * printer-uri 2806 * requested-attributes 2807 */ 2808 2809 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); 2810 2811 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 2812 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", 2813 printer); 2814 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 2815 NULL, uri); 2816 2817 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 2818 "requested-attributes", 2819 (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs); 2820 2821 /* 2822 * Do the request and get back a response... 2823 */ 2824 2825 if ((response = cupsDoRequest(http, request, "/")) != NULL) 2826 { 2827 cgiSetIPPVars(response, NULL, NULL, NULL, 0); 2828 2829 ippDelete(response); 2830 } 2831 2832 cgiStartHTML(cgiText(_("Set Allowed Users"))); 2833 2834 if (cupsLastError() == IPP_NOT_AUTHORIZED) 2835 { 2836 puts("Status: 401\n"); 2837 exit(0); 2838 } 2839 else if (cupsLastError() > IPP_OK_CONFLICT) 2840 cgiShowIPPError(_("Unable to get printer attributes")); 2841 else 2842 cgiCopyTemplateLang("users.tmpl"); 2843 2844 cgiEndHTML(); 2845 } 2846 else 2847 { 2848 /* 2849 * Save the changes... 2850 */ 2851 2852 for (num_users = 0, ptr = (char *)users; *ptr; num_users ++) 2853 { 2854 /* 2855 * Skip whitespace and commas... 2856 */ 2857 2858 while (*ptr == ',' || isspace(*ptr & 255)) 2859 ptr ++; 2860 2861 if (!*ptr) 2862 break; 2863 2864 if (*ptr == '\'' || *ptr == '\"') 2865 { 2866 /* 2867 * Scan quoted name... 2868 */ 2869 2870 quote = *ptr++; 2871 2872 for (end = ptr; *end; end ++) 2873 if (*end == quote) 2874 break; 2875 } 2876 else 2877 { 2878 /* 2879 * Scan space or comma-delimited name... 2880 */ 2881 2882 for (end = ptr; *end; end ++) 2883 if (isspace(*end & 255) || *end == ',') 2884 break; 2885 } 2886 2887 /* 2888 * Advance to the next name... 2889 */ 2890 2891 ptr = end; 2892 } 2893 2894 /* 2895 * Build a CUPS-Add-Printer/Class request, which requires the following 2896 * attributes: 2897 * 2898 * attributes-charset 2899 * attributes-natural-language 2900 * printer-uri 2901 * requesting-user-name-{allowed,denied} 2902 */ 2903 2904 request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER); 2905 2906 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 2907 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", 2908 printer); 2909 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 2910 NULL, uri); 2911 2912 if (num_users == 0) 2913 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, 2914 "requesting-user-name-allowed", NULL, "all"); 2915 else 2916 { 2917 attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME, 2918 type, num_users, NULL, NULL); 2919 2920 for (i = 0, ptr = (char *)users; *ptr; i ++) 2921 { 2922 /* 2923 * Skip whitespace and commas... 2924 */ 2925 2926 while (*ptr == ',' || isspace(*ptr & 255)) 2927 ptr ++; 2928 2929 if (!*ptr) 2930 break; 2931 2932 if (*ptr == '\'' || *ptr == '\"') 2933 { 2934 /* 2935 * Scan quoted name... 2936 */ 2937 2938 quote = *ptr++; 2939 2940 for (end = ptr; *end; end ++) 2941 if (*end == quote) 2942 break; 2943 } 2944 else 2945 { 2946 /* 2947 * Scan space or comma-delimited name... 2948 */ 2949 2950 for (end = ptr; *end; end ++) 2951 if (isspace(*end & 255) || *end == ',') 2952 break; 2953 } 2954 2955 /* 2956 * Terminate the name... 2957 */ 2958 2959 if (*end) 2960 *end++ = '\0'; 2961 2962 /* 2963 * Add the name... 2964 */ 2965 2966 attr->values[i].string.text = _cupsStrAlloc(ptr); 2967 2968 /* 2969 * Advance to the next name... 2970 */ 2971 2972 ptr = end; 2973 } 2974 } 2975 2976 /* 2977 * Do the request and get back a response... 2978 */ 2979 2980 ippDelete(cupsDoRequest(http, request, "/admin/")); 2981 2982 if (cupsLastError() == IPP_NOT_AUTHORIZED) 2983 { 2984 puts("Status: 401\n"); 2985 exit(0); 2986 } 2987 else if (cupsLastError() > IPP_OK_CONFLICT) 2988 { 2989 cgiStartHTML(cgiText(_("Set Allowed Users"))); 2990 cgiShowIPPError(_("Unable to change printer")); 2991 } 2992 else 2993 { 2994 /* 2995 * Redirect successful updates back to the printer page... 2996 */ 2997 2998 char url[1024], /* Printer/class URL */ 2999 refresh[1024]; /* Refresh URL */ 3000 3001 3002 cgiRewriteURL(uri, url, sizeof(url), NULL); 3003 cgiFormEncode(uri, url, sizeof(uri)); 3004 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", 3005 uri); 3006 cgiSetVariable("refresh_page", refresh); 3007 3008 cgiStartHTML(cgiText(_("Set Allowed Users"))); 3009 3010 cgiCopyTemplateLang(is_class ? "class-modified.tmpl" : 3011 "printer-modified.tmpl"); 3012 } 3013 3014 cgiEndHTML(); 3015 } 3016} 3017 3018 3019/* 3020 * 'do_set_default()' - Set the server default printer/class. 3021 */ 3022 3023static void 3024do_set_default(http_t *http) /* I - HTTP connection */ 3025{ 3026 const char *title; /* Page title */ 3027 ipp_t *request; /* IPP request */ 3028 char uri[HTTP_MAX_URI]; /* Printer URI */ 3029 const char *printer, /* Printer name (purge-jobs) */ 3030 *is_class; /* Is a class? */ 3031 3032 3033 is_class = cgiGetVariable("IS_CLASS"); 3034 printer = cgiGetVariable("PRINTER_NAME"); 3035 title = cgiText(_("Set As Server Default")); 3036 3037 if (!printer) 3038 { 3039 cgiSetVariable("ERROR", cgiText(_("Missing form variable"))); 3040 cgiStartHTML(title); 3041 cgiCopyTemplateLang("error.tmpl"); 3042 cgiEndHTML(); 3043 return; 3044 } 3045 3046 /* 3047 * Build a printer request, which requires the following 3048 * attributes: 3049 * 3050 * attributes-charset 3051 * attributes-natural-language 3052 * printer-uri 3053 */ 3054 3055 request = ippNewRequest(CUPS_SET_DEFAULT); 3056 3057 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 3058 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", 3059 printer); 3060 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 3061 NULL, uri); 3062 3063 /* 3064 * Do the request and get back a response... 3065 */ 3066 3067 ippDelete(cupsDoRequest(http, request, "/admin/")); 3068 3069 if (cupsLastError() == IPP_NOT_AUTHORIZED) 3070 { 3071 puts("Status: 401\n"); 3072 exit(0); 3073 } 3074 else if (cupsLastError() > IPP_OK_CONFLICT) 3075 { 3076 cgiStartHTML(title); 3077 cgiShowIPPError(_("Unable to set server default")); 3078 } 3079 else 3080 { 3081 /* 3082 * Redirect successful updates back to the printer page... 3083 */ 3084 3085 char url[1024], /* Printer/class URL */ 3086 refresh[1024]; /* Refresh URL */ 3087 3088 3089 cgiRewriteURL(uri, url, sizeof(url), NULL); 3090 cgiFormEncode(uri, url, sizeof(uri)); 3091 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri); 3092 cgiSetVariable("refresh_page", refresh); 3093 3094 cgiStartHTML(title); 3095 cgiCopyTemplateLang("printer-default.tmpl"); 3096 } 3097 3098 cgiEndHTML(); 3099} 3100 3101 3102/* 3103 * 'do_set_options()' - Configure the default options for a queue. 3104 */ 3105 3106static void 3107do_set_options(http_t *http, /* I - HTTP connection */ 3108 int is_class) /* I - Set options for class? */ 3109{ 3110 int i, j, k, m; /* Looping vars */ 3111 int have_options; /* Have options? */ 3112 ipp_t *request, /* IPP request */ 3113 *response; /* IPP response */ 3114 ipp_attribute_t *attr; /* IPP attribute */ 3115 char uri[HTTP_MAX_URI]; /* Job URI */ 3116 const char *var; /* Variable value */ 3117 const char *printer; /* Printer printer name */ 3118 const char *filename; /* PPD filename */ 3119 char tempfile[1024]; /* Temporary filename */ 3120 cups_file_t *in, /* Input file */ 3121 *out; /* Output file */ 3122 char line[1024], /* Line from PPD file */ 3123 value[1024], /* Option value */ 3124 keyword[1024], /* Keyword from Default line */ 3125 *keyptr; /* Pointer into keyword... */ 3126 ppd_file_t *ppd; /* PPD file */ 3127 ppd_group_t *group; /* Option group */ 3128 ppd_option_t *option; /* Option */ 3129 ppd_coption_t *coption; /* Custom option */ 3130 ppd_cparam_t *cparam; /* Custom parameter */ 3131 ppd_attr_t *ppdattr; /* PPD attribute */ 3132 const char *title; /* Page title */ 3133 3134 3135 title = cgiText(is_class ? _("Set Class Options") : _("Set Printer Options")); 3136 3137 fprintf(stderr, "DEBUG: do_set_options(http=%p, is_class=%d)\n", http, 3138 is_class); 3139 3140 /* 3141 * Get the printer name... 3142 */ 3143 3144 if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL) 3145 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 3146 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", 3147 printer); 3148 else 3149 { 3150 cgiSetVariable("ERROR", cgiText(_("Missing form variable"))); 3151 cgiStartHTML(title); 3152 cgiCopyTemplateLang("error.tmpl"); 3153 cgiEndHTML(); 3154 return; 3155 } 3156 3157 fprintf(stderr, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer, uri); 3158 3159 /* 3160 * If the user clicks on the Auto-Configure button, send an AutoConfigure 3161 * command file to the printer... 3162 */ 3163 3164 if (cgiGetVariable("AUTOCONFIGURE")) 3165 { 3166 cgiPrintCommand(http, printer, "AutoConfigure", "Set Default Options"); 3167 return; 3168 } 3169 3170 /* 3171 * Get the PPD file... 3172 */ 3173 3174 if (is_class) 3175 filename = NULL; 3176 else 3177 filename = cupsGetPPD2(http, printer); 3178 3179 if (filename) 3180 { 3181 fprintf(stderr, "DEBUG: Got PPD file: \"%s\"\n", filename); 3182 3183 if ((ppd = ppdOpenFile(filename)) == NULL) 3184 { 3185 cgiSetVariable("ERROR", ppdErrorString(ppdLastError(&i))); 3186 cgiSetVariable("MESSAGE", cgiText(_("Unable to open PPD file"))); 3187 cgiStartHTML(title); 3188 cgiCopyTemplateLang("error.tmpl"); 3189 cgiEndHTML(); 3190 return; 3191 } 3192 } 3193 else 3194 { 3195 fputs("DEBUG: No PPD file\n", stderr); 3196 ppd = NULL; 3197 } 3198 3199 if (cgiGetVariable("job_sheets_start") != NULL || 3200 cgiGetVariable("job_sheets_end") != NULL) 3201 have_options = 1; 3202 else 3203 have_options = 0; 3204 3205 if (ppd) 3206 { 3207 ppdMarkDefaults(ppd); 3208 3209 for (option = ppdFirstOption(ppd); 3210 option; 3211 option = ppdNextOption(ppd)) 3212 { 3213 if ((var = cgiGetVariable(option->keyword)) != NULL) 3214 { 3215 have_options = 1; 3216 ppdMarkOption(ppd, option->keyword, var); 3217 fprintf(stderr, "DEBUG: Set %s to %s...\n", option->keyword, var); 3218 } 3219 else 3220 fprintf(stderr, "DEBUG: Didn't find %s...\n", option->keyword); 3221 } 3222 } 3223 3224 if (!have_options || ppdConflicts(ppd)) 3225 { 3226 /* 3227 * Show the options to the user... 3228 */ 3229 3230 fputs("DEBUG: Showing options...\n", stderr); 3231 3232 /* 3233 * Show auto-configure button if supported... 3234 */ 3235 3236 if (ppd) 3237 { 3238 if (ppd->num_filters == 0 || 3239 ((ppdattr = ppdFindAttr(ppd, "cupsCommands", NULL)) != NULL && 3240 ppdattr->value && strstr(ppdattr->value, "AutoConfigure"))) 3241 cgiSetVariable("HAVE_AUTOCONFIGURE", "YES"); 3242 else 3243 { 3244 for (i = 0; i < ppd->num_filters; i ++) 3245 if (!strncmp(ppd->filters[i], "application/vnd.cups-postscript", 31)) 3246 { 3247 cgiSetVariable("HAVE_AUTOCONFIGURE", "YES"); 3248 break; 3249 } 3250 } 3251 } 3252 3253 /* 3254 * Get the printer attributes... 3255 */ 3256 3257 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); 3258 3259 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 3260 "localhost", 0, "/printers/%s", printer); 3261 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 3262 NULL, uri); 3263 3264 response = cupsDoRequest(http, request, "/"); 3265 3266 /* 3267 * List the groups used as "tabs"... 3268 */ 3269 3270 i = 0; 3271 3272 if (ppd) 3273 { 3274 for (group = ppd->groups; 3275 i < ppd->num_groups; 3276 i ++, group ++) 3277 { 3278 cgiSetArray("GROUP_ID", i, group->name); 3279 3280 if (!strcmp(group->name, "InstallableOptions")) 3281 cgiSetArray("GROUP", i, cgiText(_("Options Installed"))); 3282 else 3283 cgiSetArray("GROUP", i, group->text); 3284 } 3285 } 3286 3287 if (ippFindAttribute(response, "job-sheets-supported", IPP_TAG_ZERO)) 3288 { 3289 cgiSetArray("GROUP_ID", i, "CUPS_BANNERS"); 3290 cgiSetArray("GROUP", i ++, cgiText(_("Banners"))); 3291 } 3292 3293 if (ippFindAttribute(response, "printer-error-policy-supported", 3294 IPP_TAG_ZERO) || 3295 ippFindAttribute(response, "printer-op-policy-supported", 3296 IPP_TAG_ZERO)) 3297 { 3298 cgiSetArray("GROUP_ID", i, "CUPS_POLICIES"); 3299 cgiSetArray("GROUP", i ++, cgiText(_("Policies"))); 3300 } 3301 3302 if ((attr = ippFindAttribute(response, "port-monitor-supported", 3303 IPP_TAG_NAME)) != NULL && attr->num_values > 1) 3304 { 3305 cgiSetArray("GROUP_ID", i, "CUPS_PORT_MONITOR"); 3306 cgiSetArray("GROUP", i, cgiText(_("Port Monitor"))); 3307 } 3308 3309 cgiStartHTML(cgiText(_("Set Printer Options"))); 3310 cgiCopyTemplateLang("set-printer-options-header.tmpl"); 3311 3312 if (ppd) 3313 { 3314 ppdLocalize(ppd); 3315 3316 if (ppdConflicts(ppd)) 3317 { 3318 for (i = ppd->num_groups, k = 0, group = ppd->groups; 3319 i > 0; 3320 i --, group ++) 3321 for (j = group->num_options, option = group->options; 3322 j > 0; 3323 j --, option ++) 3324 if (option->conflicted) 3325 { 3326 cgiSetArray("ckeyword", k, option->keyword); 3327 cgiSetArray("ckeytext", k, option->text); 3328 3329 for (m = 0; m < option->num_choices; m ++) 3330 { 3331 if (option->choices[m].marked) 3332 { 3333 cgiSetArray("cchoice", k, option->choices[m].text); 3334 break; 3335 } 3336 } 3337 3338 k ++; 3339 } 3340 3341 cgiCopyTemplateLang("option-conflict.tmpl"); 3342 } 3343 3344 for (i = ppd->num_groups, group = ppd->groups; 3345 i > 0; 3346 i --, group ++) 3347 { 3348 for (j = group->num_options, option = group->options; 3349 j > 0; 3350 j --, option ++) 3351 { 3352 if (!strcmp(option->keyword, "PageRegion")) 3353 continue; 3354 3355 if (option->num_choices > 1) 3356 break; 3357 } 3358 3359 if (j == 0) 3360 continue; 3361 3362 cgiSetVariable("GROUP_ID", group->name); 3363 3364 if (!strcmp(group->name, "InstallableOptions")) 3365 cgiSetVariable("GROUP", cgiText(_("Options Installed"))); 3366 else 3367 cgiSetVariable("GROUP", group->text); 3368 3369 cgiCopyTemplateLang("option-header.tmpl"); 3370 3371 for (j = group->num_options, option = group->options; 3372 j > 0; 3373 j --, option ++) 3374 { 3375 if (!strcmp(option->keyword, "PageRegion") || option->num_choices < 2) 3376 continue; 3377 3378 cgiSetVariable("KEYWORD", option->keyword); 3379 cgiSetVariable("KEYTEXT", option->text); 3380 3381 if (option->conflicted) 3382 cgiSetVariable("CONFLICTED", "1"); 3383 else 3384 cgiSetVariable("CONFLICTED", "0"); 3385 3386 cgiSetSize("CHOICES", 0); 3387 cgiSetSize("TEXT", 0); 3388 for (k = 0, m = 0; k < option->num_choices; k ++) 3389 { 3390 cgiSetArray("CHOICES", m, option->choices[k].choice); 3391 cgiSetArray("TEXT", m, option->choices[k].text); 3392 3393 m ++; 3394 3395 if (option->choices[k].marked) 3396 cgiSetVariable("DEFCHOICE", option->choices[k].choice); 3397 } 3398 3399 cgiSetSize("PARAMS", 0); 3400 cgiSetSize("PARAMTEXT", 0); 3401 cgiSetSize("PARAMVALUE", 0); 3402 cgiSetSize("INPUTTYPE", 0); 3403 3404 if ((coption = ppdFindCustomOption(ppd, option->keyword))) 3405 { 3406 const char *units = NULL; /* Units value, if any */ 3407 3408 cgiSetVariable("ISCUSTOM", "1"); 3409 3410 for (cparam = ppdFirstCustomParam(coption), m = 0; 3411 cparam; 3412 cparam = ppdNextCustomParam(coption), m ++) 3413 { 3414 if (!_cups_strcasecmp(option->keyword, "PageSize") && 3415 _cups_strcasecmp(cparam->name, "Width") && 3416 _cups_strcasecmp(cparam->name, "Height")) 3417 { 3418 m --; 3419 continue; 3420 } 3421 3422 cgiSetArray("PARAMS", m, cparam->name); 3423 cgiSetArray("PARAMTEXT", m, cparam->text); 3424 cgiSetArray("INPUTTYPE", m, "text"); 3425 3426 switch (cparam->type) 3427 { 3428 case PPD_CUSTOM_POINTS : 3429 if (!_cups_strncasecmp(option->defchoice, "Custom.", 7)) 3430 { 3431 units = option->defchoice + strlen(option->defchoice) - 2; 3432 3433 if (strcmp(units, "mm") && strcmp(units, "cm") && 3434 strcmp(units, "in") && strcmp(units, "ft")) 3435 { 3436 if (units[1] == 'm') 3437 units ++; 3438 else 3439 units = "pt"; 3440 } 3441 } 3442 else 3443 units = "pt"; 3444 3445 if (!strcmp(units, "mm")) 3446 snprintf(value, sizeof(value), "%g", 3447 cparam->current.custom_points / 72.0 * 25.4); 3448 else if (!strcmp(units, "cm")) 3449 snprintf(value, sizeof(value), "%g", 3450 cparam->current.custom_points / 72.0 * 2.54); 3451 else if (!strcmp(units, "in")) 3452 snprintf(value, sizeof(value), "%g", 3453 cparam->current.custom_points / 72.0); 3454 else if (!strcmp(units, "ft")) 3455 snprintf(value, sizeof(value), "%g", 3456 cparam->current.custom_points / 72.0 / 12.0); 3457 else if (!strcmp(units, "m")) 3458 snprintf(value, sizeof(value), "%g", 3459 cparam->current.custom_points / 72.0 * 0.0254); 3460 else 3461 snprintf(value, sizeof(value), "%g", 3462 cparam->current.custom_points); 3463 cgiSetArray("PARAMVALUE", m, value); 3464 break; 3465 3466 case PPD_CUSTOM_CURVE : 3467 case PPD_CUSTOM_INVCURVE : 3468 case PPD_CUSTOM_REAL : 3469 snprintf(value, sizeof(value), "%g", 3470 cparam->current.custom_real); 3471 cgiSetArray("PARAMVALUE", m, value); 3472 break; 3473 3474 case PPD_CUSTOM_INT: 3475 snprintf(value, sizeof(value), "%d", 3476 cparam->current.custom_int); 3477 cgiSetArray("PARAMVALUE", m, value); 3478 break; 3479 3480 case PPD_CUSTOM_PASSCODE: 3481 case PPD_CUSTOM_PASSWORD: 3482 if (cparam->current.custom_password) 3483 cgiSetArray("PARAMVALUE", m, 3484 cparam->current.custom_password); 3485 else 3486 cgiSetArray("PARAMVALUE", m, ""); 3487 cgiSetArray("INPUTTYPE", m, "password"); 3488 break; 3489 3490 case PPD_CUSTOM_STRING: 3491 if (cparam->current.custom_string) 3492 cgiSetArray("PARAMVALUE", m, 3493 cparam->current.custom_string); 3494 else 3495 cgiSetArray("PARAMVALUE", m, ""); 3496 break; 3497 } 3498 } 3499 3500 if (units) 3501 { 3502 cgiSetArray("PARAMS", m, "Units"); 3503 cgiSetArray("PARAMTEXT", m, cgiText(_("Units"))); 3504 cgiSetArray("PARAMVALUE", m, units); 3505 } 3506 } 3507 else 3508 cgiSetVariable("ISCUSTOM", "0"); 3509 3510 switch (option->ui) 3511 { 3512 case PPD_UI_BOOLEAN : 3513 cgiCopyTemplateLang("option-boolean.tmpl"); 3514 break; 3515 case PPD_UI_PICKONE : 3516 cgiCopyTemplateLang("option-pickone.tmpl"); 3517 break; 3518 case PPD_UI_PICKMANY : 3519 cgiCopyTemplateLang("option-pickmany.tmpl"); 3520 break; 3521 } 3522 } 3523 3524 cgiCopyTemplateLang("option-trailer.tmpl"); 3525 } 3526 } 3527 3528 if ((attr = ippFindAttribute(response, "job-sheets-supported", 3529 IPP_TAG_ZERO)) != NULL) 3530 { 3531 /* 3532 * Add the job sheets options... 3533 */ 3534 3535 cgiSetVariable("GROUP_ID", "CUPS_BANNERS"); 3536 cgiSetVariable("GROUP", cgiText(_("Banners"))); 3537 cgiCopyTemplateLang("option-header.tmpl"); 3538 3539 cgiSetSize("CHOICES", attr->num_values); 3540 cgiSetSize("TEXT", attr->num_values); 3541 for (k = 0; k < attr->num_values; k ++) 3542 { 3543 cgiSetArray("CHOICES", k, attr->values[k].string.text); 3544 cgiSetArray("TEXT", k, attr->values[k].string.text); 3545 } 3546 3547 attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO); 3548 3549 cgiSetVariable("KEYWORD", "job_sheets_start"); 3550 cgiSetVariable("KEYTEXT", 3551 /* TRANSLATORS: Banner/cover sheet before the print job. */ 3552 cgiText(_("Starting Banner"))); 3553 cgiSetVariable("DEFCHOICE", attr != NULL ? 3554 attr->values[0].string.text : ""); 3555 3556 cgiCopyTemplateLang("option-pickone.tmpl"); 3557 3558 cgiSetVariable("KEYWORD", "job_sheets_end"); 3559 cgiSetVariable("KEYTEXT", 3560 /* TRANSLATORS: Banner/cover sheet after the print job. */ 3561 cgiText(_("Ending Banner"))); 3562 cgiSetVariable("DEFCHOICE", attr != NULL && attr->num_values > 1 ? 3563 attr->values[1].string.text : ""); 3564 3565 cgiCopyTemplateLang("option-pickone.tmpl"); 3566 3567 cgiCopyTemplateLang("option-trailer.tmpl"); 3568 } 3569 3570 if (ippFindAttribute(response, "printer-error-policy-supported", 3571 IPP_TAG_ZERO) || 3572 ippFindAttribute(response, "printer-op-policy-supported", 3573 IPP_TAG_ZERO)) 3574 { 3575 /* 3576 * Add the error and operation policy options... 3577 */ 3578 3579 cgiSetVariable("GROUP_ID", "CUPS_POLICIES"); 3580 cgiSetVariable("GROUP", cgiText(_("Policies"))); 3581 cgiCopyTemplateLang("option-header.tmpl"); 3582 3583 /* 3584 * Error policy... 3585 */ 3586 3587 attr = ippFindAttribute(response, "printer-error-policy-supported", 3588 IPP_TAG_ZERO); 3589 3590 if (attr) 3591 { 3592 cgiSetSize("CHOICES", attr->num_values); 3593 cgiSetSize("TEXT", attr->num_values); 3594 for (k = 0; k < attr->num_values; k ++) 3595 { 3596 cgiSetArray("CHOICES", k, attr->values[k].string.text); 3597 cgiSetArray("TEXT", k, attr->values[k].string.text); 3598 } 3599 3600 attr = ippFindAttribute(response, "printer-error-policy", 3601 IPP_TAG_ZERO); 3602 3603 cgiSetVariable("KEYWORD", "printer_error_policy"); 3604 cgiSetVariable("KEYTEXT", cgiText(_("Error Policy"))); 3605 cgiSetVariable("DEFCHOICE", attr == NULL ? 3606 "" : attr->values[0].string.text); 3607 } 3608 3609 cgiCopyTemplateLang("option-pickone.tmpl"); 3610 3611 /* 3612 * Operation policy... 3613 */ 3614 3615 attr = ippFindAttribute(response, "printer-op-policy-supported", 3616 IPP_TAG_ZERO); 3617 3618 if (attr) 3619 { 3620 cgiSetSize("CHOICES", attr->num_values); 3621 cgiSetSize("TEXT", attr->num_values); 3622 for (k = 0; k < attr->num_values; k ++) 3623 { 3624 cgiSetArray("CHOICES", k, attr->values[k].string.text); 3625 cgiSetArray("TEXT", k, attr->values[k].string.text); 3626 } 3627 3628 attr = ippFindAttribute(response, "printer-op-policy", IPP_TAG_ZERO); 3629 3630 cgiSetVariable("KEYWORD", "printer_op_policy"); 3631 cgiSetVariable("KEYTEXT", cgiText(_("Operation Policy"))); 3632 cgiSetVariable("DEFCHOICE", attr == NULL ? 3633 "" : attr->values[0].string.text); 3634 3635 cgiCopyTemplateLang("option-pickone.tmpl"); 3636 } 3637 3638 cgiCopyTemplateLang("option-trailer.tmpl"); 3639 } 3640 3641 /* 3642 * Binary protocol support... 3643 */ 3644 3645 if ((attr = ippFindAttribute(response, "port-monitor-supported", 3646 IPP_TAG_NAME)) != NULL && attr->num_values > 1) 3647 { 3648 cgiSetVariable("GROUP_ID", "CUPS_PORT_MONITOR"); 3649 cgiSetVariable("GROUP", cgiText(_("Port Monitor"))); 3650 3651 cgiSetSize("CHOICES", attr->num_values); 3652 cgiSetSize("TEXT", attr->num_values); 3653 3654 for (i = 0; i < attr->num_values; i ++) 3655 { 3656 cgiSetArray("CHOICES", i, attr->values[i].string.text); 3657 cgiSetArray("TEXT", i, attr->values[i].string.text); 3658 } 3659 3660 attr = ippFindAttribute(response, "port-monitor", IPP_TAG_NAME); 3661 cgiSetVariable("KEYWORD", "port_monitor"); 3662 cgiSetVariable("KEYTEXT", cgiText(_("Port Monitor"))); 3663 cgiSetVariable("DEFCHOICE", attr ? attr->values[0].string.text : "none"); 3664 3665 cgiCopyTemplateLang("option-header.tmpl"); 3666 cgiCopyTemplateLang("option-pickone.tmpl"); 3667 cgiCopyTemplateLang("option-trailer.tmpl"); 3668 } 3669 3670 cgiCopyTemplateLang("set-printer-options-trailer.tmpl"); 3671 cgiEndHTML(); 3672 3673 ippDelete(response); 3674 } 3675 else 3676 { 3677 /* 3678 * Set default options... 3679 */ 3680 3681 fputs("DEBUG: Setting options...\n", stderr); 3682 3683 if (filename) 3684 { 3685 out = cupsTempFile2(tempfile, sizeof(tempfile)); 3686 in = cupsFileOpen(filename, "r"); 3687 3688 if (!in || !out) 3689 { 3690 cgiSetVariable("ERROR", strerror(errno)); 3691 cgiStartHTML(cgiText(_("Set Printer Options"))); 3692 cgiCopyTemplateLang("error.tmpl"); 3693 cgiEndHTML(); 3694 3695 if (in) 3696 cupsFileClose(in); 3697 3698 if (out) 3699 { 3700 cupsFileClose(out); 3701 unlink(tempfile); 3702 } 3703 3704 unlink(filename); 3705 return; 3706 } 3707 3708 while (cupsFileGets(in, line, sizeof(line))) 3709 { 3710 if (!strncmp(line, "*cupsProtocol:", 14)) 3711 continue; 3712 else if (strncmp(line, "*Default", 8)) 3713 cupsFilePrintf(out, "%s\n", line); 3714 else 3715 { 3716 /* 3717 * Get default option name... 3718 */ 3719 3720 strlcpy(keyword, line + 8, sizeof(keyword)); 3721 3722 for (keyptr = keyword; *keyptr; keyptr ++) 3723 if (*keyptr == ':' || isspace(*keyptr & 255)) 3724 break; 3725 3726 *keyptr = '\0'; 3727 3728 if (!strcmp(keyword, "PageRegion") || 3729 !strcmp(keyword, "PaperDimension") || 3730 !strcmp(keyword, "ImageableArea")) 3731 var = get_option_value(ppd, "PageSize", value, sizeof(value)); 3732 else 3733 var = get_option_value(ppd, keyword, value, sizeof(value)); 3734 3735 if (!var) 3736 cupsFilePrintf(out, "%s\n", line); 3737 else 3738 cupsFilePrintf(out, "*Default%s: %s\n", keyword, var); 3739 } 3740 } 3741 3742 cupsFileClose(in); 3743 cupsFileClose(out); 3744 } 3745 else 3746 { 3747 /* 3748 * Make sure temporary filename is cleared when there is no PPD... 3749 */ 3750 3751 tempfile[0] = '\0'; 3752 } 3753 3754 /* 3755 * Build a CUPS_ADD_MODIFY_CLASS/PRINTER request, which requires the 3756 * following attributes: 3757 * 3758 * attributes-charset 3759 * attributes-natural-language 3760 * printer-uri 3761 * job-sheets-default 3762 * printer-error-policy 3763 * printer-op-policy 3764 * [ppd file] 3765 */ 3766 3767 request = ippNewRequest(is_class ? CUPS_ADD_MODIFY_CLASS : 3768 CUPS_ADD_MODIFY_PRINTER); 3769 3770 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 3771 NULL, uri); 3772 3773 attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME, 3774 "job-sheets-default", 2, NULL, NULL); 3775 attr->values[0].string.text = _cupsStrAlloc(cgiGetVariable("job_sheets_start")); 3776 attr->values[1].string.text = _cupsStrAlloc(cgiGetVariable("job_sheets_end")); 3777 3778 if ((var = cgiGetVariable("printer_error_policy")) != NULL) 3779 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, 3780 "printer-error-policy", NULL, var); 3781 3782 if ((var = cgiGetVariable("printer_op_policy")) != NULL) 3783 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, 3784 "printer-op-policy", NULL, var); 3785 3786 if ((var = cgiGetVariable("port_monitor")) != NULL) 3787 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, 3788 "port-monitor", NULL, var); 3789 3790 /* 3791 * Do the request and get back a response... 3792 */ 3793 3794 if (filename) 3795 ippDelete(cupsDoFileRequest(http, request, "/admin/", tempfile)); 3796 else 3797 ippDelete(cupsDoRequest(http, request, "/admin/")); 3798 3799 if (cupsLastError() == IPP_NOT_AUTHORIZED) 3800 { 3801 puts("Status: 401\n"); 3802 exit(0); 3803 } 3804 else if (cupsLastError() > IPP_OK_CONFLICT) 3805 { 3806 cgiStartHTML(title); 3807 cgiShowIPPError(_("Unable to set options")); 3808 } 3809 else 3810 { 3811 /* 3812 * Redirect successful updates back to the printer page... 3813 */ 3814 3815 char refresh[1024]; /* Refresh URL */ 3816 3817 3818 cgiFormEncode(uri, printer, sizeof(uri)); 3819 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=/%s/%s", 3820 is_class ? "classes" : "printers", uri); 3821 cgiSetVariable("refresh_page", refresh); 3822 3823 cgiStartHTML(title); 3824 3825 cgiCopyTemplateLang("printer-configured.tmpl"); 3826 } 3827 3828 cgiEndHTML(); 3829 3830 if (filename) 3831 unlink(tempfile); 3832 } 3833 3834 if (filename) 3835 unlink(filename); 3836} 3837 3838 3839/* 3840 * 'do_set_sharing()' - Set printer-is-shared value. 3841 */ 3842 3843static void 3844do_set_sharing(http_t *http) /* I - HTTP connection */ 3845{ 3846 ipp_t *request, /* IPP request */ 3847 *response; /* IPP response */ 3848 char uri[HTTP_MAX_URI]; /* Printer URI */ 3849 const char *printer, /* Printer name */ 3850 *is_class, /* Is a class? */ 3851 *shared; /* Sharing value */ 3852 3853 3854 is_class = cgiGetVariable("IS_CLASS"); 3855 printer = cgiGetVariable("PRINTER_NAME"); 3856 shared = cgiGetVariable("SHARED"); 3857 3858 if (!printer || !shared) 3859 { 3860 cgiSetVariable("ERROR", cgiText(_("Missing form variable"))); 3861 cgiStartHTML(cgiText(_("Set Publishing"))); 3862 cgiCopyTemplateLang("error.tmpl"); 3863 cgiEndHTML(); 3864 return; 3865 } 3866 3867 /* 3868 * Build a CUPS-Add-Printer/CUPS-Add-Class request, which requires the 3869 * following attributes: 3870 * 3871 * attributes-charset 3872 * attributes-natural-language 3873 * printer-uri 3874 * printer-is-shared 3875 */ 3876 3877 request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER); 3878 3879 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 3880 "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", 3881 printer); 3882 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 3883 NULL, uri); 3884 3885 ippAddBoolean(request, IPP_TAG_OPERATION, "printer-is-shared", atoi(shared)); 3886 3887 /* 3888 * Do the request and get back a response... 3889 */ 3890 3891 if ((response = cupsDoRequest(http, request, "/admin/")) != NULL) 3892 { 3893 cgiSetIPPVars(response, NULL, NULL, NULL, 0); 3894 3895 ippDelete(response); 3896 } 3897 3898 if (cupsLastError() == IPP_NOT_AUTHORIZED) 3899 { 3900 puts("Status: 401\n"); 3901 exit(0); 3902 } 3903 else if (cupsLastError() > IPP_OK_CONFLICT) 3904 { 3905 cgiStartHTML(cgiText(_("Set Publishing"))); 3906 cgiShowIPPError(_("Unable to change printer-is-shared attribute")); 3907 } 3908 else 3909 { 3910 /* 3911 * Redirect successful updates back to the printer page... 3912 */ 3913 3914 char url[1024], /* Printer/class URL */ 3915 refresh[1024]; /* Refresh URL */ 3916 3917 3918 cgiRewriteURL(uri, url, sizeof(url), NULL); 3919 cgiFormEncode(uri, url, sizeof(uri)); 3920 snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri); 3921 cgiSetVariable("refresh_page", refresh); 3922 3923 cgiStartHTML(cgiText(_("Set Publishing"))); 3924 cgiCopyTemplateLang(is_class ? "class-modified.tmpl" : 3925 "printer-modified.tmpl"); 3926 } 3927 3928 cgiEndHTML(); 3929} 3930 3931 3932/* 3933 * 'get_option_value()' - Return the value of an option. 3934 * 3935 * This function also handles generation of custom option values. 3936 */ 3937 3938static char * /* O - Value string or NULL on error */ 3939get_option_value( 3940 ppd_file_t *ppd, /* I - PPD file */ 3941 const char *name, /* I - Option name */ 3942 char *buffer, /* I - String buffer */ 3943 size_t bufsize) /* I - Size of buffer */ 3944{ 3945 char *bufptr, /* Pointer into buffer */ 3946 *bufend; /* End of buffer */ 3947 ppd_coption_t *coption; /* Custom option */ 3948 ppd_cparam_t *cparam; /* Current custom parameter */ 3949 char keyword[256]; /* Parameter name */ 3950 const char *val, /* Parameter value */ 3951 *uval; /* Units value */ 3952 long integer; /* Integer value */ 3953 double number, /* Number value */ 3954 number_points; /* Number in points */ 3955 3956 3957 /* 3958 * See if we have a custom option choice... 3959 */ 3960 3961 if ((val = cgiGetVariable(name)) == NULL) 3962 { 3963 /* 3964 * Option not found! 3965 */ 3966 3967 return (NULL); 3968 } 3969 else if (_cups_strcasecmp(val, "Custom") || 3970 (coption = ppdFindCustomOption(ppd, name)) == NULL) 3971 { 3972 /* 3973 * Not a custom choice... 3974 */ 3975 3976 strlcpy(buffer, val, bufsize); 3977 return (buffer); 3978 } 3979 3980 /* 3981 * OK, we have a custom option choice, format it... 3982 */ 3983 3984 *buffer = '\0'; 3985 3986 if (!strcmp(coption->keyword, "PageSize")) 3987 { 3988 const char *lval; /* Length string value */ 3989 double width, /* Width value */ 3990 width_points, /* Width in points */ 3991 length, /* Length value */ 3992 length_points; /* Length in points */ 3993 3994 3995 val = cgiGetVariable("PageSize.Width"); 3996 lval = cgiGetVariable("PageSize.Height"); 3997 uval = cgiGetVariable("PageSize.Units"); 3998 3999 if (!val || !lval || !uval || 4000 (width = strtod(val, NULL)) == 0.0 || 4001 (length = strtod(lval, NULL)) == 0.0 || 4002 (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") && 4003 strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m"))) 4004 return (NULL); 4005 4006 width_points = get_points(width, uval); 4007 length_points = get_points(length, uval); 4008 4009 if (width_points < ppd->custom_min[0] || 4010 width_points > ppd->custom_max[0] || 4011 length_points < ppd->custom_min[1] || 4012 length_points > ppd->custom_max[1]) 4013 return (NULL); 4014 4015 snprintf(buffer, bufsize, "Custom.%gx%g%s", width, length, uval); 4016 } 4017 else if (cupsArrayCount(coption->params) == 1) 4018 { 4019 cparam = ppdFirstCustomParam(coption); 4020 snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, cparam->name); 4021 4022 if ((val = cgiGetVariable(keyword)) == NULL) 4023 return (NULL); 4024 4025 switch (cparam->type) 4026 { 4027 case PPD_CUSTOM_CURVE : 4028 case PPD_CUSTOM_INVCURVE : 4029 case PPD_CUSTOM_REAL : 4030 if ((number = strtod(val, NULL)) == 0.0 || 4031 number < cparam->minimum.custom_real || 4032 number > cparam->maximum.custom_real) 4033 return (NULL); 4034 4035 snprintf(buffer, bufsize, "Custom.%g", number); 4036 break; 4037 4038 case PPD_CUSTOM_INT : 4039 if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN || 4040 integer == LONG_MAX || 4041 integer < cparam->minimum.custom_int || 4042 integer > cparam->maximum.custom_int) 4043 return (NULL); 4044 4045 snprintf(buffer, bufsize, "Custom.%ld", integer); 4046 break; 4047 4048 case PPD_CUSTOM_POINTS : 4049 snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword); 4050 4051 if ((number = strtod(val, NULL)) == 0.0 || 4052 (uval = cgiGetVariable(keyword)) == NULL || 4053 (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") && 4054 strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m"))) 4055 return (NULL); 4056 4057 number_points = get_points(number, uval); 4058 if (number_points < cparam->minimum.custom_points || 4059 number_points > cparam->maximum.custom_points) 4060 return (NULL); 4061 4062 snprintf(buffer, bufsize, "Custom.%g%s", number, uval); 4063 break; 4064 4065 case PPD_CUSTOM_PASSCODE : 4066 for (uval = val; *uval; uval ++) 4067 if (!isdigit(*uval & 255)) 4068 return (NULL); 4069 4070 case PPD_CUSTOM_PASSWORD : 4071 case PPD_CUSTOM_STRING : 4072 integer = (long)strlen(val); 4073 if (integer < cparam->minimum.custom_string || 4074 integer > cparam->maximum.custom_string) 4075 return (NULL); 4076 4077 snprintf(buffer, bufsize, "Custom.%s", val); 4078 break; 4079 } 4080 } 4081 else 4082 { 4083 const char *prefix = "{"; /* Prefix string */ 4084 4085 4086 bufptr = buffer; 4087 bufend = buffer + bufsize; 4088 4089 for (cparam = ppdFirstCustomParam(coption); 4090 cparam; 4091 cparam = ppdNextCustomParam(coption)) 4092 { 4093 snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, 4094 cparam->name); 4095 4096 if ((val = cgiGetVariable(keyword)) == NULL) 4097 return (NULL); 4098 4099 snprintf(bufptr, bufend - bufptr, "%s%s=", prefix, cparam->name); 4100 bufptr += strlen(bufptr); 4101 prefix = " "; 4102 4103 switch (cparam->type) 4104 { 4105 case PPD_CUSTOM_CURVE : 4106 case PPD_CUSTOM_INVCURVE : 4107 case PPD_CUSTOM_REAL : 4108 if ((number = strtod(val, NULL)) == 0.0 || 4109 number < cparam->minimum.custom_real || 4110 number > cparam->maximum.custom_real) 4111 return (NULL); 4112 4113 snprintf(bufptr, bufend - bufptr, "%g", number); 4114 break; 4115 4116 case PPD_CUSTOM_INT : 4117 if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN || 4118 integer == LONG_MAX || 4119 integer < cparam->minimum.custom_int || 4120 integer > cparam->maximum.custom_int) 4121 return (NULL); 4122 4123 snprintf(bufptr, bufend - bufptr, "%ld", integer); 4124 break; 4125 4126 case PPD_CUSTOM_POINTS : 4127 snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword); 4128 4129 if ((number = strtod(val, NULL)) == 0.0 || 4130 (uval = cgiGetVariable(keyword)) == NULL || 4131 (strcmp(uval, "pt") && strcmp(uval, "in") && 4132 strcmp(uval, "ft") && strcmp(uval, "cm") && 4133 strcmp(uval, "mm") && strcmp(uval, "m"))) 4134 return (NULL); 4135 4136 number_points = get_points(number, uval); 4137 if (number_points < cparam->minimum.custom_points || 4138 number_points > cparam->maximum.custom_points) 4139 return (NULL); 4140 4141 snprintf(bufptr, bufend - bufptr, "%g%s", number, uval); 4142 break; 4143 4144 case PPD_CUSTOM_PASSCODE : 4145 for (uval = val; *uval; uval ++) 4146 if (!isdigit(*uval & 255)) 4147 return (NULL); 4148 4149 case PPD_CUSTOM_PASSWORD : 4150 case PPD_CUSTOM_STRING : 4151 integer = (long)strlen(val); 4152 if (integer < cparam->minimum.custom_string || 4153 integer > cparam->maximum.custom_string) 4154 return (NULL); 4155 4156 if ((bufptr + 2) > bufend) 4157 return (NULL); 4158 4159 bufend --; 4160 *bufptr++ = '\"'; 4161 4162 while (*val && bufptr < bufend) 4163 { 4164 if (*val == '\\' || *val == '\"') 4165 { 4166 if ((bufptr + 1) >= bufend) 4167 return (NULL); 4168 4169 *bufptr++ = '\\'; 4170 } 4171 4172 *bufptr++ = *val++; 4173 } 4174 4175 if (bufptr >= bufend) 4176 return (NULL); 4177 4178 *bufptr++ = '\"'; 4179 *bufptr = '\0'; 4180 bufend ++; 4181 break; 4182 } 4183 4184 bufptr += strlen(bufptr); 4185 } 4186 4187 if (bufptr == buffer || (bufend - bufptr) < 2) 4188 return (NULL); 4189 4190 memcpy(bufptr, "}", 2); 4191 } 4192 4193 return (buffer); 4194} 4195 4196 4197/* 4198 * 'get_points()' - Get a value in points. 4199 */ 4200 4201static double /* O - Number in points */ 4202get_points(double number, /* I - Original number */ 4203 const char *uval) /* I - Units */ 4204{ 4205 if (!strcmp(uval, "mm")) /* Millimeters */ 4206 return (number * 72.0 / 25.4); 4207 else if (!strcmp(uval, "cm")) /* Centimeters */ 4208 return (number * 72.0 / 2.54); 4209 else if (!strcmp(uval, "in")) /* Inches */ 4210 return (number * 72.0); 4211 else if (!strcmp(uval, "ft")) /* Feet */ 4212 return (number * 72.0 * 12.0); 4213 else if (!strcmp(uval, "m")) /* Meters */ 4214 return (number * 72.0 / 0.0254); 4215 else /* Points */ 4216 return (number); 4217} 4218 4219 4220/* 4221 * End of "$Id: admin.c 11433 2013-11-20 18:57:44Z msweet $". 4222 */ 4223