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