More consistant protocol output. Read PROTOCOL for more info.
[pwmd.git] / src / pwmserver.c
blob3b12b2408105496b09e0e8f68842b163286100db
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006 Ben Kibbey <bjk@luxsci.net>
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.
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.
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
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <ctype.h>
25 #include <string.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <sys/wait.h>
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
37 #include "xml.h"
38 #include "pwmserver.h"
40 void catchsig(int sig)
42 int status;
44 switch (sig) {
45 case SIGCHLD:
46 waitpid(-1, &status, 0);
47 break;
48 default:
49 shutdown(sfd, SHUT_RDWR);
50 close(sfd);
51 quit = 1;
52 break;
56 void usage(char *pn)
58 printf(
59 "Usage: %s [-h] [-p <port>]\n"
60 " -p alternate port (%i)\n"
61 " -h this help text\n",
62 pn, DEFAULT_PORT);
63 exit(EXIT_SUCCESS);
66 int send_to_client(int fd, char *fmt, ...)
68 va_list ap;
69 char *buf;
70 int ret = 0;
72 va_start(ap, fmt);
73 vasprintf(&buf, fmt, ap);
74 va_end(ap);
76 while (1) {
77 fd_set fds;
79 FD_ZERO(&fds);
80 FD_SET(fd, &fds);
82 switch (select(fd + 1, NULL, &fds, NULL, NULL)) {
83 case -1:
84 warn("select()");
85 continue;
86 case 0:
87 //timeout
88 continue;
89 default:
90 break;
93 if (send(fd, buf, strlen(buf), 0) == -1) {
94 ret = 1;
95 warn("send()");
98 break;
101 free(buf);
102 return ret;
105 int parse_account(struct client_s *cl, char *str)
107 char *p;
108 int i = 0;
110 if (cl->req) {
111 for (i = 0; cl->req[i]; i++)
112 free(cl->req[i]);
115 i = 0;
117 while ((p = strsep(&str, "/")) != NULL) {
118 cl->req = realloc(cl->req, (i + 2) * sizeof(xmlChar *));
119 cl->req[i++] = xmlStrdup((xmlChar *)p);
120 cl->req[i] = NULL;
123 if (!i)
124 return 1;
126 i = 0;
128 if (find_account(cl->reader, cl->req[i++])) {
129 send_to_client(cl->fd, "ERR account \"%s\" not found\n", cl->req[0]);
130 return 1;
134 * We are at the position in the document where the account was found.
135 * Search through the account for the wanted elements.
137 for (; cl->req[i]; i++) {
138 xmlNodePtr n;
140 if (find_element(cl->reader, cl->req[i], cl->req[i+1] != NULL)) {
141 send_to_client(cl->fd, "ERR could not find element \"%s\"",
142 cl->req[i]);
143 return 1;
147 * We are at the end of the element list. Save the result.
149 if (!cl->req[i+1]) {
150 n = xmlTextReaderCurrentNode(cl->reader);
152 if (send_to_client(cl->fd, "BEGIN %li\n", xmlStrlen(n->content)) == 0) {
153 if (send_to_client(cl->fd, "%s", n->content) == 0)
154 send_to_client(cl->fd, "\nOK\n");
159 return 0;
162 void client_help(int fd, const char *what)
164 char *line;
166 if (!what || !*what)
167 line =
168 "NFO Try 'help topic' for details\n"
169 "NFO auth get quit\n";
170 else if (strcasecmp(what, "get") == 0)
171 line =
172 "NFO syntax: get account[/element[/...]]\n"
173 "NFO <account> is the account to work on and <element>\n"
174 "NFO is the account elements wanted.\n"
175 "NFO -\n"
176 "NFO Example: get isp/imap/port\n"
177 "NFO get isp/username\n";
178 else if (strcasecmp(what, "quit") == 0)
179 line =
180 "NFO syntax: quit\n"
181 "NFO close the connection\n";
182 else if (strcasecmp(what, "auth") == 0)
183 line =
184 "NFO syntax: auth username password\n";
185 else
186 line = "NFO unknown command\n";
188 send_to_client(fd, line);
191 char *skip_space(char *str)
193 while (isspace(*str))
194 str++;
196 return str;
200 * The filename will be username.xml.
202 int authenticate_client(struct client_s *cl, char *str)
204 char *user = NULL, *pass = NULL;
205 char buf[FILENAME_MAX];
207 if ((user = strsep(&str, " ")) == NULL)
208 return 1;
210 pass = str;
211 snprintf(buf, sizeof(buf), "%s.xml", user);
214 if (access(argv[optind], R_OK) != 0 && errno == ENOENT) {
215 root = create_doc(&doc, "accounts");
217 else if (errno)
218 err(EXIT_FAILURE, "%s", argv[optind]);
221 cl->filename = strdup(buf);
222 cl->state = STATE_AUTH;
224 switch (open_xml(cl->filename, &cl->doc, &cl->root, &cl->reader)) {
225 case 1:
226 send_to_client(cl->fd, "ERR XML parse error\n");
227 return 1;
228 case 2:
229 send_to_client(cl->fd, "ERR XMLReaderWalker() failed\n");
230 return 1;
231 default:
232 break;
235 return 0;
238 int input_parser(struct client_s *cl, char *str)
240 char *p, *t;
242 while ((p = strsep(&str, "\n")) != NULL) {
243 if (strcasecmp(p, "quit") == 0)
244 return P_QUIT;
245 else if (strcasecmp(p, "help") == 0)
246 client_help(cl->fd, NULL);
247 else if (strncasecmp(p, "help ", 5) == 0) {
248 t = p + 5;
249 t = skip_space(t);
250 client_help(cl->fd, t);
252 else if (strncasecmp(p, "get ", 4) == 0) {
253 t = p + 4;
254 t = skip_space(t);
256 if (cl->state != STATE_AUTH)
257 send_to_client(cl->fd, "ERR not authenticated\n");
258 else {
259 // FIXME reuse existing cl->doc and cl->reader handles.
260 if (cl->doc) {
261 xmlFreeTextReader(cl->reader);
262 xmlFreeDoc(cl->doc);
264 if (!open_xml(cl->filename, &cl->doc, &cl->root, &cl->reader)) {
265 cl->doc->children = cl->root;
266 xmlReaderNewWalker(cl->reader, cl->doc);
268 if (parse_account(cl, t)) {
269 send_to_client(cl->fd, "ERR element parse error\n");
275 else if (strncasecmp(p, "auth ", 5) == 0) {
276 if (cl->state == STATE_AUTH)
277 send_to_client(cl->fd, "ERR already authenticated\n");
278 else {
279 t = p + 5;
280 t = skip_space(t);
282 if (!authenticate_client(cl, t))
283 send_to_client(cl->fd, "OK authenticated\n");
288 return P_OK;
292 * Called every time a connection is made.
293 * FIXME protocol
294 * FIXME SSL
296 void doit(int rfd)
298 struct client_s *cl = calloc(1, sizeof(struct client_s));
300 cl->state = STATE_CONNECTED;
301 cl->fd = rfd;
302 send_to_client(rfd, "NFO Type help for available commands\n");
304 while (1) {
305 fd_set rfds, wfds;
306 char buf[LINE_MAX] = {0};
308 FD_ZERO(&rfds);
309 FD_SET(cl->fd, &rfds);
310 FD_ZERO(&wfds);
311 FD_SET(cl->fd, &wfds);
313 switch (select(cl->fd + 1, &rfds, NULL, NULL, NULL)) {
314 case -1:
315 warn("select()");
316 break;
317 case 0:
318 // timeout
319 continue;
320 default:
321 break;
324 if (FD_ISSET(cl->fd, &rfds)) {
325 int len = recv(cl->fd, buf, sizeof(buf), 0);
327 buf[len - 1] = 0;
329 if (len == -1) {
330 warn("recv()");
331 continue;
333 else if (len == 0)
334 break;
336 switch (input_parser(cl, buf)) {
337 case P_QUIT:
338 goto done;
339 default:
340 break;
345 done:
346 shutdown(cl->fd, SHUT_RDWR);
347 close(cl->fd);
348 _exit(EXIT_SUCCESS);
351 int main(int argc, char *argv[])
353 int opt;
354 int rfd;
355 struct sockaddr_in laddr, raddr;
356 socklen_t slen;
357 int yes = 0;
358 int port = DEFAULT_PORT;
360 while ((opt = getopt(argc, argv, "hp:")) != EOF) {
361 switch (opt) {
362 case 'p':
363 port = atoi(optarg);
364 break;
365 default:
366 usage(argv[0]);
370 if ((sfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
371 err(EXIT_FAILURE, "socket()");
373 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
374 err(EXIT_FAILURE, "setsockopt()");
376 laddr.sin_family = AF_INET;
377 laddr.sin_port = htons(port);
378 laddr.sin_addr.s_addr = INADDR_ANY;
379 memset(&(laddr.sin_zero), 0, 8);
381 if (bind(sfd, (struct sockaddr *)&laddr, sizeof(struct sockaddr)) == -1)
382 err(EXIT_FAILURE, "bind()");
384 if (listen(sfd, 10) == -1)
385 err(EXIT_FAILURE, "listen()");
387 signal(SIGCHLD, catchsig);
388 signal(SIGTERM, catchsig);
389 signal(SIGINT, catchsig);
391 while (1) {
392 if (quit)
393 break;
395 slen = sizeof(struct sockaddr_in);
397 if ((rfd = accept(sfd, (struct sockaddr_in *)&raddr, &slen)) == -1) {
398 if (errno != EBADF)
399 warn("accept");
401 continue;
404 switch (fork()) {
405 case -1:
406 warn("fork()");
407 break;
408 case 0:
409 doit(rfd);
410 break;
411 default:
412 break;
416 exit(EXIT_SUCCESS);