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