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