1 /* upsset - CGI program to manage read/write variables
2 
3    Copyright (C) 1999  Russell Kroll <rkroll@exploits.org>
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 
20 #include "common.h"
21 
22 #include <netdb.h>
23 #include <stdlib.h>
24 #include <netinet/in.h>
25 #include <sys/socket.h>
26 
27 #include "upsclient.h"
28 #include "cgilib.h"
29 #include "parseconf.h"
30 
31 struct list_t {
32 	char	*name;
33 	struct	list_t	*next;
34 };
35 
36 /* see the stock upsset.conf for the whole rant on what this is */
37 #define MAGIC_ENABLE_STRING "I_HAVE_SECURED_MY_CGI_DIRECTORY"
38 
39 #define HARD_UPSVAR_LIMIT_NUM	64
40 #define HARD_UPSVAR_LIMIT_LEN	256
41 
42 	char	*monups, *username, *password, *function, *upscommand;
43 
44 	/* set once the MAGIC_ENABLE_STRING is found in the upsset.conf */
45 	int	magic_string_set = 0;
46 
47 static	int	port;
48 static	char	*upsname, *hostname;
49 static	UPSCONN_t	ups;
50 
51 typedef struct {
52 	char	*var;
53 	char	*value;
54 	void	*next;
55 }	uvtype_t;
56 
57 	uvtype_t	*firstuv = NULL;
58 
parsearg(char * var,char * value)59 void parsearg(char *var, char *value)
60 {
61 	char	*ptr;
62 	uvtype_t	*last, *tmp = NULL;
63 	static	int upsvc = 0;
64 
65 	/* store variables from a SET command for the later commit */
66 	if (!strncmp(var, "UPSVAR_", 7)) {
67 
68 		/* if someone bombs us with variables, stop at some point */
69 		if (upsvc > HARD_UPSVAR_LIMIT_NUM)
70 			return;
71 
72 		/* same idea: throw out anything that's much too long */
73 		if (strlen(value) > HARD_UPSVAR_LIMIT_LEN)
74 			return;
75 
76 		ptr = strchr(var, '_');
77 
78 		if (!ptr)		/* sanity check */
79 			return;
80 
81 		ptr++;
82 
83 		tmp = last = firstuv;
84 		while (tmp) {
85 			last = tmp;
86 			tmp = tmp->next;
87 		}
88 
89 		tmp = xmalloc(sizeof(uvtype_t));
90 		tmp->var = xstrdup(ptr);
91 		tmp->value = xstrdup(value);
92 		tmp->next = NULL;
93 
94 		if (last)
95 			last->next = tmp;
96 		else
97 			firstuv = tmp;
98 
99 		upsvc++;
100 
101 		return;
102 	}
103 
104 	if (!strcmp(var, "username")) {
105 		free(username);
106 		username = xstrdup(value);
107 	}
108 
109 	if (!strcmp(var, "password")) {
110 		free(password);
111 		password = xstrdup(value);
112 	}
113 
114 	if (!strcmp(var, "function")) {
115 		free(function);
116 		function = xstrdup(value);
117 	}
118 
119 	if (!strcmp(var, "monups")) {
120 		free(monups);
121 		monups = xstrdup(value);
122 	}
123 
124 	if (!strcmp(var, "upscommand")) {
125 		free(upscommand);
126 		upscommand = xstrdup(value);
127 	}
128 }
129 
do_header(const char * title)130 static void do_header(const char *title)
131 {
132 	printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"\n");
133 	printf("	\"http://www.w3.org/TR/REC-html40/loose.dtd\">\n");
134 	printf("<HTML>\n");
135 	printf("<HEAD><TITLE>upsset: %s</TITLE></HEAD>\n", title);
136 
137 	printf("<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000EE\" VLINK=\"#551A8B\">\n");
138 
139 	printf("<TABLE BGCOLOR=\"#50A0A0\" ALIGN=\"CENTER\">\n");
140 	printf("<TR><TD>\n");
141 }
142 
start_table(void)143 static void start_table(void)
144 {
145 	printf("<TABLE CELLPADDING=\"5\" CELLSPACING=\"0\" ALIGN=\"CENTER\" WIDTH=\"100%%\">\n");
146 	printf("<TR><TH COLSPAN=2 BGCOLOR=\"#60B0B0\">\n");
147 	printf("<FONT SIZE=\"+2\">Network UPS Tools upsset %s</FONT>\n",
148 		UPS_VERSION);
149 	printf("</TH></TR>\n");
150 }
151 
152 /* propagate login details across pages - no cookies here! */
do_hidden(const char * next)153 static void do_hidden(const char *next)
154 {
155 	printf("<INPUT TYPE=\"HIDDEN\" NAME=\"username\" VALUE=\"%s\">\n",
156 		username);
157 	printf("<INPUT TYPE=\"HIDDEN\" NAME=\"password\" VALUE=\"%s\">\n",
158 		password);
159 
160 	if (next)
161 		printf("<INPUT TYPE=\"HIDDEN\" NAME=\"function\" VALUE=\"%s\">\n",
162 			next);
163 }
164 
165 /* generate SELECT chooser from hosts.conf entries */
upslist_arg(int numargs,char ** arg)166 static void upslist_arg(int numargs, char **arg)
167 {
168 	if (numargs < 3)
169 		return;
170 
171 	/* MONITOR <ups> <description> */
172 	if (!strcmp(arg[0], "MONITOR")) {
173 		printf("<OPTION VALUE=\"%s\"", arg[1]);
174 
175 		if (monups)
176 			if (!strcmp(monups, arg[1]))
177 				printf("SELECTED");
178 
179 		printf(">%s</OPTION>\n", arg[2]);
180 	}
181 }
182 
183 /* called for fatal errors in parseconf like malloc failures */
upsset_hosts_err(const char * errmsg)184 static void upsset_hosts_err(const char *errmsg)
185 {
186 	upslogx(LOG_ERR, "Fatal error in parseconf(hosts.conf): %s", errmsg);
187 }
188 
189 /* this defaults to wherever we are now, ups and function-wise */
do_pickups(const char * currfunc)190 static void do_pickups(const char *currfunc)
191 {
192 	char	hostfn[SMALLBUF];
193 	PCONF_CTX_t	ctx;
194 
195 	snprintf(hostfn, sizeof(hostfn), "%s/hosts.conf", confpath());
196 
197 	printf("<FORM METHOD=\"POST\" ACTION=\"upsset.cgi\">\n");
198 
199 	printf("Select UPS and function:\n<BR>\n");
200 
201 	pconf_init(&ctx, upsset_hosts_err);
202 
203 	if (!pconf_file_begin(&ctx, hostfn)) {
204 		pconf_finish(&ctx);
205 
206 		printf("Error: hosts.conf unavailable\n");
207 		printf("</FORM>\n");
208 
209 		/* stderr is for the admin - should wind up in error.log */
210 		fprintf(stderr, "upsset: %s\n", ctx.errmsg);
211 
212 		return;
213 	}
214 
215 	printf("<SELECT NAME=\"monups\">\n");
216 
217 	while (pconf_file_next(&ctx)) {
218 		if (pconf_parse_error(&ctx)) {
219 			upslogx(LOG_ERR, "Parse error: %s:%d: %s",
220 				hostfn, ctx.linenum, ctx.errmsg);
221 
222 			continue;
223 		}
224 
225 		upslist_arg(ctx.numargs, ctx.arglist);
226 	}
227 
228 	pconf_finish(&ctx);
229 
230 	printf("</SELECT>\n");
231 
232 	printf("<SELECT NAME=\"function\">\n");
233 
234 	/* FUTURE */
235 	/*	printf("<OPTION VALUE=\"showstatus\">Status</OPTION>\n");  */
236 
237 	/* TODO: clean this up */
238 
239 	if (!strcmp(currfunc, "showsettings"))
240 		printf("<OPTION VALUE=\"showsettings\" SELECTED>Settings</OPTION>\n");
241 	else
242 		printf("<OPTION VALUE=\"showsettings\">Settings</OPTION>\n");
243 
244 	if (!strcmp(currfunc, "showcmds"))
245 		printf("<OPTION VALUE=\"showcmds\" SELECTED>Commands</OPTION>\n");
246 	else
247 		printf("<OPTION VALUE=\"showcmds\">Commands</OPTION>\n");
248 
249 	printf("</SELECT>\n");
250 	do_hidden(NULL);
251 
252 	printf("<INPUT TYPE=\"SUBMIT\" VALUE=\"View\">\n");
253 	printf("</FORM>\n");
254 }
255 
error_page(const char * next,const char * title,const char * fmt,...)256 static void error_page(const char *next, const char *title,
257 	const char *fmt, ...)
258 {
259 	char	msg[SMALLBUF];
260 	va_list	ap;
261 
262 	va_start(ap, fmt);
263 	vsnprintf(msg, sizeof(msg), fmt, ap);
264 	va_end(ap);
265 
266 	do_header(title);
267 
268 	start_table();
269 	printf("<TR><TH COLSPAN=2 BGCOLOR=\"#60B0B0\">\n");
270 	printf("Error: %s\n", msg);
271 	printf("</TH></TR>\n");
272 
273 	printf("<TR><TD ALIGN=\"CENTER\" COLSPAN=2>\n");
274 	do_pickups(next);
275 	printf("</TD></TR>\n");
276 
277 	printf("</TABLE>\n");
278 	printf("</TD></TR></TABLE>\n");
279 	printf("</BODY></HTML>\n");
280 
281 	upscli_disconnect(&ups);
282 	exit(EXIT_SUCCESS);
283 }
284 
loginscreen(void)285 static void loginscreen(void)
286 {
287 	do_header("Login");
288 	printf("<FORM METHOD=\"POST\" ACTION=\"upsset.cgi\">\n");
289 	start_table();
290 
291 	printf("<TR BGCOLOR=\"#60B0B0\">\n");
292 	printf("<TH>Username</TH>\n");
293 	printf("<TD><INPUT TYPE=\"TEXT\" NAME=\"username\" VALUE=\"\"></TD>\n");
294 	printf("</TR>\n");
295 
296 	printf("<TR BGCOLOR=\"#60B0B0\">\n");
297 	printf("<TH>Password</TH>\n");
298 	printf("<TD><INPUT TYPE=\"PASSWORD\" NAME=\"password\" VALUE=\"\"></TD>\n");
299 	printf("</TR>\n");
300 
301 	printf("<TR><TD COLSPAN=2 ALIGN=\"CENTER\">\n");
302 	printf("<INPUT TYPE=\"HIDDEN\" NAME=\"function\" VALUE=\"pickups\">\n");
303 	printf("<INPUT TYPE=\"SUBMIT\" VALUE=\"Login\">\n");
304 	printf("<INPUT TYPE=\"RESET\" VALUE=\"Reset fields\">\n");
305 	printf("</TD></TR></TABLE>\n");
306 	printf("</FORM>\n");
307 	printf("</TD></TR></TABLE>\n");
308 	printf("</BODY></HTML>\n");
309 
310 	upscli_disconnect(&ups);
311 	exit(EXIT_SUCCESS);
312 }
313 
314 /* try to connect to upsd - generate an error page if it fails */
upsd_connect(void)315 static void upsd_connect(void)
316 {
317 	if (upscli_splitname(monups, &upsname, &hostname, &port) != 0) {
318 		error_page("showsettings", "UPS name is unusable",
319 			"Unable to split UPS name [%s]", monups);
320 		/* NOTREACHED */
321 	}
322 
323 	if (upscli_connect(&ups, hostname, port, 0) < 0) {
324 		error_page("showsettings", "Connect failure",
325 			"Unable to connect to %s: %s",
326 			monups, upscli_strerror(&ups));
327 		/* NOTREACHED */
328 	}
329 }
330 
print_cmd(const char * cmd)331 static void print_cmd(const char *cmd)
332 {
333 	int	ret;
334 	unsigned int	numq, numa;
335 	char	**answer;
336 	const	char	*query[4];
337 
338 	query[0] = "CMDDESC";
339 	query[1] = upsname;
340 	query[2] = cmd;
341 	numq = 3;
342 
343 	ret = upscli_get(&ups, numq, query, &numa, &answer);
344 
345 	if ((ret < 0) || (numa < numq))
346 		return;
347 
348 	/* CMDDESC <upsname> <cmdname> <desc> */
349 
350 	printf("<OPTION VALUE=\"%s\">%s</OPTION>\n", cmd, answer[3]);
351 }
352 
353 /* generate a list of instant commands */
showcmds(void)354 static void showcmds(void)
355 {
356 	int	ret;
357 	unsigned int	numq, numa;
358 	const	char	*query[2];
359 	char	**answer;
360 	struct	list_t	*lhead, *llast, *ltmp, *lnext;
361 	char	*desc;
362 
363 	if (!checkhost(monups, &desc))
364 		error_page("showsettings", "Access denied",
365 			"Access to that host is not authorized");
366 
367 	upsd_connect();
368 
369 	llast = lhead = NULL;
370 
371 	query[0] = "CMD";
372 	query[1] = upsname;
373 	numq = 2;
374 
375 	ret = upscli_list_start(&ups, numq, query);
376 
377 	if (ret < 0) {
378 		fprintf(stderr, "LIST CMD %s failed: %s\n",
379 			upsname, upscli_strerror(&ups));
380 
381 		error_page("showcmds", "Server protocol error",
382 			"LIST CMD command failed");
383 
384 		/* NOTREACHED */
385 	}
386 
387 	ret = upscli_list_next(&ups, numq, query, &numa, &answer);
388 
389 	while (ret == 1) {
390 
391 		/* CMD upsname cmdname */
392 		if (numa < 3) {
393 			fprintf(stderr, "Error: insufficient data "
394 				"(got %d args, need at least 3)\n", numa);
395 
396 			return;
397 		}
398 
399 		ltmp = xmalloc(sizeof(struct list_t));
400 		ltmp->name = xstrdup(answer[2]);
401 		ltmp->next = NULL;
402 
403 		if (llast)
404 			llast->next = ltmp;
405 		else
406 			lhead = ltmp;
407 
408 		llast = ltmp;
409 
410 		ret = upscli_list_next(&ups, numq, query, &numa, &answer);
411 	}
412 
413 	if (!lhead)
414 		error_page("showcmds", "No instant commands supported",
415 			"This UPS doesn't support any instant commands.");
416 
417 	do_header("Instant commands");
418 	printf("<FORM ACTION=\"upsset.cgi\" METHOD=\"POST\">\n");
419 	start_table();
420 
421 	/* include the description from checkhost() if present */
422 	if (desc)
423 		printf("<TR><TH BGCOLOR=\"#60B0B0\"COLSPAN=2>%s</TH></TR>\n",
424 			desc);
425 
426 	printf("<TR BGCOLOR=\"#60B0B0\" ALIGN=\"CENTER\">\n");
427 	printf("<TD>Instant commands</TD>\n");
428 
429 	printf("<TD>\n");
430 	printf("<SELECT NAME=\"upscommand\">\n");
431 
432 	/* provide a dummy do-nothing default choice */
433 	printf("<OPTION VALUE=\"\" SELECTED></OPTION>\n");
434 
435 	ltmp = lhead;
436 
437 	while (ltmp) {
438 		lnext = ltmp->next;
439 
440 		print_cmd(ltmp->name);
441 
442 		free(ltmp->name);
443 		free(ltmp);
444 		ltmp = lnext;
445 	}
446 
447 	printf("</SELECT>\n");
448 	printf("</TD></TR>\n");
449 
450 	printf("<TR BGCOLOR=\"#60B0B0\">\n");
451 	printf("<TD COLSPAN=\"2\" ALIGN=\"CENTER\">\n");
452 	do_hidden("docmd");
453 	printf("<INPUT TYPE=\"HIDDEN\" NAME=\"monups\" VALUE=\"%s\">\n", monups);
454 	printf("<INPUT TYPE=\"SUBMIT\" VALUE=\"Issue command\">\n");
455 	printf("<INPUT TYPE=\"RESET\" VALUE=\"Reset\">\n");
456 	printf("</TD></TR>\n");
457 	printf("</TABLE>\n");
458 	printf("</FORM>\n");
459 
460 	printf("<TR><TD ALIGN=\"CENTER\">\n");
461 	do_pickups("showcmds");
462 	printf("</TD></TR>\n");
463 
464 	printf("</TABLE>\n");
465 	printf("</BODY></HTML>\n");
466 
467 	upscli_disconnect(&ups);
468 	exit(EXIT_SUCCESS);
469 }
470 
471 /* handle setting authentication data in the server */
send_auth(const char * next)472 static void send_auth(const char *next)
473 {
474 	char	buf[SMALLBUF];
475 
476 	snprintf(buf, sizeof(buf), "USERNAME %s\n", username);
477 
478 	if (upscli_sendline(&ups, buf, strlen(buf)) < 0) {
479 		fprintf(stderr, "Can't set username: %s\n",
480 			upscli_strerror(&ups));
481 
482 		error_page(next, "Can't set username",
483 			"Set username failed: %s", upscli_strerror(&ups));
484 	}
485 
486 	if (upscli_readline(&ups, buf, sizeof(buf)) < 0) {
487 
488 		/* test for old upsd that doesn't do USERNAME */
489 		if (upscli_upserror(&ups) == UPSCLI_ERR_UNKCOMMAND) {
490 			error_page(next, "Protocol mismatch",
491 				"upsd version too old - USERNAME not supported");
492 		}
493 
494 		error_page(next, "Can't set user name",
495 			"Set user name failed: %s", upscli_strerror(&ups));
496 	}
497 
498 	snprintf(buf, sizeof(buf), "PASSWORD %s\n", password);
499 
500 	if (upscli_sendline(&ups, buf, strlen(buf)) < 0)
501 		error_page(next, "Can't set password",
502 			"Password set failed: %s", upscli_strerror(&ups));
503 
504 	if (upscli_readline(&ups, buf, sizeof(buf)) < 0)
505 		error_page(next, "Can't set password",
506 			"Password set failed: %s", upscli_strerror(&ups));
507 }
508 
docmd(void)509 static void docmd(void)
510 {
511 	char	buf[SMALLBUF], *desc;
512 
513 	if (!checkhost(monups, &desc))
514 		error_page("showsettings", "Access denied",
515 			"Access to that host is not authorized");
516 
517 	/* the user is messing with us */
518 	if (!upscommand)
519 		error_page("showcmds", "Form error",
520 			"No instant command selected");
521 
522 	/* (l)user took the default blank option */
523 	if (strlen(upscommand) == 0)
524 		error_page("showcmds", "Form error",
525 			"No instant command selected");
526 
527 	upsd_connect();
528 
529 	send_auth("showcmds");
530 
531 	snprintf(buf, sizeof(buf), "INSTCMD %s %s\n", upsname, upscommand);
532 
533 	if (upscli_sendline(&ups, buf, strlen(buf)) < 0) {
534 		do_header("Error while issuing command");
535 
536 		start_table();
537 
538 		printf("<TR><TD>Error sending command: %s\n</TD></TR>",
539 			upscli_strerror(&ups));
540 
541 		printf("<TR><TD ALIGN=\"CENTER\" COLSPAN=2>\n");
542 		do_pickups("showcmds");
543 		printf("</TD></TR>\n");
544 
545 		printf("</TABLE>\n");
546 
547 		printf("</TD></TR></TABLE>\n");
548 		printf("</BODY></HTML>\n");
549 
550 		upscli_disconnect(&ups);
551 		exit(EXIT_SUCCESS);
552 	}
553 
554 	if (upscli_readline(&ups, buf, sizeof(buf)) < 0) {
555 		do_header("Error while reading command response");
556 
557 		start_table();
558 
559 		printf("<TR><TD>Error reading command response: %s\n</TD></TR>",
560 			upscli_strerror(&ups));
561 
562 		printf("<TR><TD ALIGN=\"CENTER\" COLSPAN=2>\n");
563 		do_pickups("showcmds");
564 		printf("</TD></TR>\n");
565 
566 		printf("</TABLE>\n");
567 
568 		printf("</TD></TR></TABLE>\n");
569 		printf("</BODY></HTML>\n");
570 
571 		upscli_disconnect(&ups);
572 		exit(EXIT_SUCCESS);
573 	}
574 
575 	do_header("Issuing command");
576 	start_table();
577 
578 	printf("<TR><TD><PRE>\n");
579 	printf("Sending command: %s\n", upscommand);
580 	printf("Response: %s\n", buf);
581 	printf("</PRE></TD></TR>\n");
582 
583 	printf("<TR><TD ALIGN=\"CENTER\" COLSPAN=2>\n");
584 	do_pickups("showcmds");
585 	printf("</TD></TR>\n");
586 
587 	printf("</TABLE>\n");
588 	printf("</TD></TR></TABLE>\n");
589 	printf("</BODY></HTML>\n");
590 
591 	upscli_disconnect(&ups);
592 	exit(EXIT_SUCCESS);
593 }
594 
get_data(const char * type,const char * varname)595 static const char *get_data(const char *type, const char *varname)
596 {
597 	int	ret;
598 	unsigned int	numq, numa;
599 	char	**answer;
600 	const	char	*query[4];
601 
602 	query[0] = type;
603 	query[1] = upsname;
604 	query[2] = varname;
605 	numq = 3;
606 
607 	ret = upscli_get(&ups, numq, query, &numa, &answer);
608 
609 	if ((ret < 0) || (numa < numq))
610 		return NULL;
611 
612 	/* <type> <upsname> <varname> <desc> */
613 	return answer[3];
614 }
615 
do_string(const char * varname,int maxlen)616 static void do_string(const char *varname, int maxlen)
617 {
618 	const	char	*val;
619 
620 	val = get_data("VAR", varname);
621 
622 	if (!val) {
623 		printf("Unavailable\n");
624 		fprintf(stderr, "do_string: can't get current value of %s\n",
625 			varname);
626 		return;
627 	}
628 
629 	printf("<INPUT TYPE=\"TEXT\" NAME=\"UPSVAR_%s\" VALUE=\"%s\" "
630 		"SIZE=\"%d\">\n", varname, val, maxlen);
631 }
632 
do_enum(const char * varname)633 static void do_enum(const char *varname)
634 {
635 	int	ret;
636 	unsigned int	numq, numa;
637 	char	**answer, *val;
638 	const	char	*query[4], *tmp;
639 
640 	/* get current value */
641 	tmp = get_data("VAR", varname);
642 
643 	if (!tmp) {
644 		printf("Unavailable\n");
645 		fprintf(stderr, "do_enum: can't get current value of %s\n",
646 			varname);
647 		return;
648 	}
649 
650 	/* tmp is a pointer into answer - have to save it somewhere else */
651 	val = xstrdup(tmp);
652 
653 	query[0] = "ENUM";
654 	query[1] = upsname;
655 	query[2] = varname;
656 	numq = 3;
657 
658 	ret = upscli_list_start(&ups, numq, query);
659 
660 	if (ret < 0) {
661 		printf("Unavailable\n");
662 		fprintf(stderr, "Error doing ENUM %s %s: %s\n",
663 			upsname, varname, upscli_strerror(&ups));
664 		return;
665 	}
666 
667 	ret = upscli_list_next(&ups, numq, query, &numa, &answer);
668 
669 	printf("<SELECT NAME=\"UPSVAR_%s\">\n", varname);
670 
671 	while (ret == 1) {
672 
673 		/* ENUM <upsname> <varname> <value> */
674 
675 		if (numa < 4) {
676 			fprintf(stderr, "Error: insufficient data "
677 				"(got %d args, need at least 4)\n", numa);
678 
679 			free(val);
680 			return;
681 		}
682 
683 		printf("<OPTION VALUE=\"%s\" ", answer[3]);
684 
685 		if (!strcmp(answer[3], val))
686 			printf(" SELECTED");
687 
688 		printf(">%s</OPTION>\n", answer[3]);
689 
690 		ret = upscli_list_next(&ups, numq, query, &numa, &answer);
691 	}
692 
693 	free(val);
694 	printf("</SELECT>\n");
695 }
696 
do_type(const char * varname)697 static void do_type(const char *varname)
698 {
699 	int	ret;
700 	unsigned int	i, numq, numa;
701 	char	**answer;
702 	const	char	*query[4];
703 
704 	query[0] = "TYPE";
705 	query[1] = upsname;
706 	query[2] = varname;
707 	numq = 3;
708 
709 	ret = upscli_get(&ups, numq, query, &numa, &answer);
710 
711 	if ((ret < 0) || (numa < numq)) {
712 		printf("Unknown type\n");
713 		return;
714 	}
715 
716 	/* TYPE <upsname> <varname> <type>... */
717 	for (i = 3; i < numa; i++) {
718 
719 		if (!strcasecmp(answer[i], "ENUM")) {
720 			do_enum(varname);
721 			return;
722 		}
723 
724 		if (!strncasecmp(answer[i], "STRING:", 7)) {
725 			char	*ptr, len;
726 
727 			/* split out the :<len> data */
728 			ptr = strchr(answer[i], ':');
729 			*ptr++ = '\0';
730 			len = strtol(ptr, (char **) NULL, 10);
731 
732 			do_string(varname, len);
733 			return;
734 		}
735 
736 		/* ignore this one */
737 		if (!strcasecmp(answer[i], "RW"))
738 			continue;
739 
740 		printf("Unrecognized\n");
741 	}
742 }
743 
print_rw(const char * upsname,const char * varname)744 static void print_rw(const char *upsname, const char *varname)
745 {
746 	const	char	*tmp;
747 
748 	printf("<TR BGCOLOR=\"#60B0B0\" ALIGN=\"CENTER\">\n");
749 
750 	printf("<TD>");
751 
752 	tmp = get_data("DESC", varname);
753 
754 	if ((tmp) && (strcmp(tmp, "Unavailable") != 0))
755 		printf("%s", tmp);
756 	else
757 		printf("%s", varname);
758 
759 	printf("</TD>\n");
760 
761 	printf("<TD>\n");
762 	do_type(varname);
763 	printf("</TD>\n");
764 
765 	printf("</TR>\n");
766 }
767 
showsettings(void)768 static void showsettings(void)
769 {
770 	int	ret;
771 	unsigned int	numq, numa;
772 	const	char	*query[2];
773 	char	**answer, *desc = NULL;
774 	struct	list_t	*lhead, *llast, *ltmp, *lnext;
775 
776 	if (!checkhost(monups, &desc))
777 		error_page("showsettings", "Access denied",
778 			"Access to that host is not authorized");
779 
780 	upsd_connect();
781 
782 	query[0] = "RW";
783 	query[1] = upsname;
784 	numq = 2;
785 
786 	ret = upscli_list_start(&ups, numq, query);
787 
788 	if (ret < 0) {
789 		fprintf(stderr, "LIST RW %s failed: %s\n",
790 			upsname, upscli_strerror(&ups));
791 
792 		error_page("showsettings", "Server protocol error",
793 			"LIST RW command failed");
794 
795 		/* NOTREACHED */
796 	}
797 
798 	llast = lhead = NULL;
799 
800 	ret = upscli_list_next(&ups, numq, query, &numa, &answer);
801 
802 	while (ret == 1) {
803 
804 		/* sock this entry away for later */
805 
806 		ltmp = xmalloc(sizeof(struct list_t));
807 		ltmp->name = xstrdup(answer[2]);
808 		ltmp->next = NULL;
809 
810 		if (llast)
811 			llast->next = ltmp;
812 		else
813 			lhead = ltmp;
814 
815 		llast = ltmp;
816 
817 		ret = upscli_list_next(&ups, numq, query, &numa, &answer);
818 	}
819 
820 	do_header("Current settings");
821 	printf("<FORM ACTION=\"upsset.cgi\" METHOD=\"POST\">\n");
822 	start_table();
823 
824 	/* include the description from checkhost() if present */
825 	if (desc)
826 		printf("<TR><TH BGCOLOR=\"#60B0B0\"COLSPAN=2>%s</TH></TR>\n",
827 			desc);
828 
829 	printf("<TR BGCOLOR=\"#60B0B0\">\n");
830 	printf("<TH>Setting</TH>\n");
831 	printf("<TH>Value</TH></TR>\n");
832 
833 	/* use the list to get descriptions and types */
834 
835 	ltmp = lhead;
836 
837 	while (ltmp) {
838 		lnext = ltmp->next;
839 
840 		print_rw(upsname, ltmp->name);
841 
842 		free(ltmp->name);
843 		free(ltmp);
844 		ltmp = lnext;
845 	}
846 
847 	printf("<TR BGCOLOR=\"#60B0B0\">\n");
848 	printf("<TD COLSPAN=\"2\" ALIGN=\"CENTER\">\n");
849 	do_hidden("savesettings");
850 	printf("<INPUT TYPE=\"HIDDEN\" NAME=\"monups\" VALUE=\"%s\">\n", monups);
851 	printf("<INPUT TYPE=\"SUBMIT\" VALUE=\"Save changes\">\n");
852 	printf("<INPUT TYPE=\"RESET\" VALUE=\"Reset\">\n");
853 	printf("</TD></TR>\n");
854 	printf("</TABLE>\n");
855 	printf("</FORM>\n");
856 
857 	printf("<TR><TD ALIGN=\"CENTER\">\n");
858 	do_pickups("showsettings");
859 	printf("</TD></TR>\n");
860 
861 	printf("</TABLE>\n");
862 	printf("</BODY></HTML>\n");
863 
864 	upscli_disconnect(&ups);
865 	exit(EXIT_SUCCESS);
866 }
867 
setvar(const char * var,const char * val)868 static int setvar(const char *var, const char *val)
869 {
870 	char	buf[SMALLBUF], enc[SMALLBUF];
871 	const	char	*tmp;
872 
873 	/* get old value */
874 	tmp = get_data("VAR", var);
875 
876 	if (!tmp) {
877 		printf("Can't get old value for %s, aborting SET\n", var);
878 		return 0;
879 	}
880 
881 	/* don't send a SET if it hasn't chnaged */
882 	if (!strcmp(tmp, val))
883 		return 0;
884 
885 	printf("set %s to %s (was %s)\n", var, val, tmp);
886 
887 	snprintf(buf, sizeof(buf), "SET VAR %s %s \"%s\"\n",
888 		upsname, var, pconf_encode(val, enc, sizeof(enc)));
889 
890 	if (upscli_sendline(&ups, buf, strlen(buf)) < 0) {
891 		printf("Error: SET failed: %s\n", upscli_strerror(&ups));
892 		return 0;
893 	}
894 
895 	if (upscli_readline(&ups, buf, sizeof(buf)) < 0) {
896 		printf("Error: SET failed: %s\n", upscli_strerror(&ups));
897 		return 0;
898 	}
899 
900 	if (strncmp(buf, "OK", 2) != 0) {
901 		printf("Unexpected response: %s\n", buf);
902 		return 0;
903 	}
904 
905 	printf("OK\n");
906 	return 1;
907 }
908 
909 /* turn a form submission of settings into SET commands for upsd */
savesettings(void)910 static void savesettings(void)
911 {
912 	int	changed = 0;
913 	char	*desc;
914 	uvtype_t	*upsvar;
915 
916 	if (!checkhost(monups, &desc))
917 		error_page("showsettings", "Access denied",
918 			"Access to that host is not authorized");
919 
920 	upsd_connect();
921 
922 	upsvar = firstuv;
923 
924 	send_auth("showsettings");
925 
926 	do_header("Saving settings");
927 	start_table();
928 
929 	printf("<TR><TD><PRE>\n");
930 
931 	while (upsvar) {
932 		changed += setvar(upsvar->var, upsvar->value);
933 		upsvar = upsvar->next;
934 	}
935 
936 	if (changed == 0)
937 		printf("No settings changed.\n");
938 	else
939 		printf("Updated %d setting%s.\n",
940 			changed, changed == 1 ? "" : "s");
941 
942 	printf("</PRE></TD></TR>\n");
943 
944 	printf("<TR><TD ALIGN=\"CENTER\" COLSPAN=\"2\">\n");
945 	do_pickups("showsettings");
946 	printf("</TD></TR>\n");
947 
948 	printf("</TABLE>\n");
949 	printf("</TD></TR></TABLE>\n");
950 	printf("</BODY></HTML>\n");
951 
952 	upscli_disconnect(&ups);
953 	exit(EXIT_SUCCESS);
954 }
955 
initial_pickups(void)956 static void initial_pickups(void)
957 {
958 	do_header("Select a UPS");
959 	start_table();
960 
961 	printf("<TR><TD ALIGN=\"CENTER\" COLSPAN=\"2\">\n");
962 	do_pickups("");
963 	printf("</TD></TR>\n");
964 
965 	printf("</TABLE>\n");
966 	printf("</TD></TR></TABLE>\n");
967 	printf("</BODY></HTML>\n");
968 
969 	upscli_disconnect(&ups);
970 	exit(EXIT_SUCCESS);
971 }
972 
upsset_conf_err(const char * errmsg)973 static void upsset_conf_err(const char *errmsg)
974 {
975 	upslogx(LOG_ERR, "Fatal error in parseconf(upsset.conf): %s", errmsg);
976 }
977 
978 /* see if the user has confirmed their cgi directory's secure state */
check_conf(void)979 static void check_conf(void)
980 {
981 	char	fn[SMALLBUF];
982 	PCONF_CTX_t	ctx;
983 
984 	snprintf(fn, sizeof(fn), "%s/upsset.conf", confpath());
985 
986 	pconf_init(&ctx, upsset_conf_err);
987 
988 	if (!pconf_file_begin(&ctx, fn)) {
989 		pconf_finish(&ctx);
990 
991 		printf("<PRE>\n");
992 		printf("Error: Can't open upsset.conf to verify security settings.\n");
993 		printf("Refusing to start until this is fixed.\n");
994 		printf("</PRE>\n");
995 
996 		/* leave something in the httpd log for the admin */
997 		fprintf(stderr, "upsset.conf does not exist to permit execution\n");
998 		exit(EXIT_FAILURE);
999 	}
1000 
1001 	while (pconf_file_next(&ctx)) {
1002 		if (pconf_parse_error(&ctx)) {
1003 			upslogx(LOG_ERR, "Parse error: %s:%d: %s",
1004 				fn, ctx.linenum, ctx.errmsg);
1005 			continue;
1006 		}
1007 
1008 		if (ctx.numargs < 1)
1009 			continue;
1010 
1011 		if (!strcmp(ctx.arglist[0], MAGIC_ENABLE_STRING))
1012 			magic_string_set = 1;
1013 	}
1014 
1015 	pconf_finish(&ctx);
1016 
1017 	/* if we've been enabled, jump out of here and go to work */
1018 	if (magic_string_set == 1)
1019 		return;
1020 
1021 	printf("<PRE>\n");
1022 	printf("Error: Secure mode has not been enabled in upsset.conf.\n");
1023 	printf("Refusing to start until this is fixed.\n");
1024 	printf("</PRE>\n");
1025 
1026 	/* leave something in the httpd log for the admin */
1027 	fprintf(stderr, "upsset.conf does not permit execution\n");
1028 
1029 	exit(EXIT_FAILURE);
1030 }
1031 
main(int argc,char ** argv)1032 int main(int argc, char **argv)
1033 {
1034 	username = password = function = monups = NULL;
1035 
1036 	printf("Content-type: text/html\n\n");
1037 
1038 	/* see if the magic string is present in the config file */
1039 	check_conf();
1040 
1041 	/* see if there's anything waiting .. the server my not close STDIN properly */
1042 	if (1) {
1043 	    fd_set fds;
1044 	    struct timeval tv;
1045 
1046 	    FD_ZERO(&fds);
1047 	    FD_SET(STDIN_FILENO, &fds);
1048 	    tv.tv_sec = 0;
1049 	    tv.tv_usec = 250000; /* wait for up to 250ms  for a POST response */
1050 	    if ((select(STDIN_FILENO+1, &fds, 0, 0, &tv)) > 0)
1051 		extractpostargs();
1052 	}
1053 	if ((!username) || (!password) || (!function))
1054 		loginscreen();
1055 
1056 	if ((!strcmp(function, "pickups")) || (!monups))
1057 		initial_pickups();
1058 
1059 	if (!strcmp(function, "showsettings"))
1060 		showsettings();
1061 
1062 	if (!strcmp(function, "savesettings"))
1063 		savesettings();
1064 
1065 #if 0		/* FUTURE */
1066 	if (!strcmp(function, "showstatus"))
1067 		showstatus();
1068 #endif
1069 
1070 	if (!strcmp(function, "showcmds"))
1071 		showcmds();
1072 
1073 	if (!strcmp(function, "docmd"))
1074 		docmd();
1075 
1076 	printf("Error: Unhandled function name [%s]\n", function);
1077 
1078 	return 0;
1079 }
1080