The daemon code is no longer needed. We'll use only the server and
[pwmd.git] / src / pwmd.c
bloba10c43721eeea958e3c430cbbc1805ea8915e717
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>
32 #include <fcntl.h>
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
38 #include "xml.h"
39 #include "pwmd.h"
41 void catchsig(int sig)
43 int status;
45 switch (sig) {
46 case SIGCHLD:
47 waitpid(-1, &status, 0);
48 break;
49 default:
50 shutdown(sfd, SHUT_RDWR);
51 close(sfd);
52 quit = 1;
53 break;
57 void usage(char *pn)
59 printf(
60 "Usage: %s [-h] [-p <port>]\n"
61 " -p alternate port (%i)\n"
62 " -h this help text\n",
63 pn, DEFAULT_PORT);
64 exit(EXIT_SUCCESS);
67 int send_to_client(int fd, char *fmt, ...)
69 va_list ap;
70 char *buf;
71 int ret = 0;
73 va_start(ap, fmt);
74 vasprintf(&buf, fmt, ap);
75 va_end(ap);
77 while (1) {
78 fd_set fds;
80 FD_ZERO(&fds);
81 FD_SET(fd, &fds);
83 switch (select(fd + 1, NULL, &fds, NULL, NULL)) {
84 case -1:
85 warn("select()");
86 continue;
87 case 0:
88 //timeout
89 continue;
90 default:
91 break;
94 if (send(fd, buf, strlen(buf), 0) == -1) {
95 ret = 1;
96 warn("send()");
99 break;
102 free(buf);
103 return ret;
106 int parse_account(struct client_s *cl, char *str)
108 char *p;
109 int i = 0;
111 if (cl->req) {
112 for (i = 0; cl->req[i]; i++)
113 free(cl->req[i]);
116 i = 0;
118 while ((p = strsep(&str, "/")) != NULL) {
119 cl->req = realloc(cl->req, (i + 2) * sizeof(xmlChar *));
120 cl->req[i++] = xmlStrdup((xmlChar *)p);
121 cl->req[i] = NULL;
124 if (!i)
125 return 1;
127 i = 0;
129 if (find_account(cl->reader, cl->req[i++])) {
130 send_to_client(cl->fd, "ERR account \"%s\" not found\n", cl->req[0]);
131 return 1;
135 * We are at the position in the document where the account was found.
136 * Search through the account for the wanted elements.
138 for (; cl->req[i]; i++) {
139 xmlNodePtr n;
141 if (find_element(cl->reader, cl->req[i], cl->req[i+1] != NULL)) {
142 send_to_client(cl->fd, "ERR could not find element \"%s\"",
143 cl->req[i]);
144 return 1;
148 * We are at the end of the element list. Save the result.
150 if (!cl->req[i+1]) {
151 n = xmlTextReaderCurrentNode(cl->reader);
153 if (send_to_client(cl->fd, "BEGIN %li\n", xmlStrlen(n->content)) == 0) {
154 if (send_to_client(cl->fd, "%s", n->content) == 0)
155 send_to_client(cl->fd, "\nOK\n");
160 return 0;
163 void client_help(int fd, const char *what)
165 char *line;
167 if (!what || !*what)
168 line =
169 "NFO Try 'help topic' for details\n"
170 "NFO auth get quit\n";
171 else if (strcasecmp(what, "get") == 0)
172 line =
173 "NFO syntax: get account[/element[/...]]\n"
174 "NFO <account> is the account to work on and <element>\n"
175 "NFO is the account elements wanted.\n"
176 "NFO -\n"
177 "NFO Example: get isp/imap/port\n"
178 "NFO get isp/username\n";
179 else if (strcasecmp(what, "quit") == 0)
180 line =
181 "NFO syntax: quit\n"
182 "NFO close the connection\n";
183 else if (strcasecmp(what, "auth") == 0)
184 line =
185 "NFO syntax: auth username\n";
186 else
187 line = "NFO unknown command\n";
189 send_to_client(fd, line);
192 char *skip_space(char *str)
194 while (isspace(*str))
195 str++;
197 return str;
201 * The filename will be username.xml.gpg.
203 int authenticate_client(struct client_s *cl, char *user)
205 char buf[LINE_MAX];
206 FILE *fp;
207 char *xml = NULL;
208 int t = 0;
210 snprintf(buf, sizeof(buf), "%s.xml.gpg", user);
211 cl->filename = strdup(buf);
212 snprintf(buf, sizeof(buf), "gpg -o - %s 2>/dev/null", cl->filename);
214 if ((fp = popen(buf, "r")) == NULL) {
215 warn("popen()");
216 return 1;
219 while (1) {
220 int len = fread(buf, 1, sizeof(buf), fp);
222 if (len == 0)
223 break;
225 xml = realloc(xml, t + len + 1);
226 memcpy(&xml[t], buf, len);
227 t += len;
228 xml[t] = 0;
231 cl->xml = xml;
232 cl->len = t;
233 pclose(fp);
236 * Not needed anymore.
238 close(0);
239 close(1);
240 close(2);
242 switch (open_xml(cl->xml, cl->len, &cl->doc, &cl->root, &cl->reader)) {
243 case 1:
244 send_to_client(cl->fd, "ERR XML parse error\n");
245 return 1;
246 case 2:
247 send_to_client(cl->fd, "ERR XMLReaderWalker() failed\n");
248 return 1;
249 default:
250 break;
253 cl->state = STATE_AUTH;
254 return 0;
257 int input_parser(struct client_s *cl, char *str)
259 char *p, *t;
261 while ((p = strsep(&str, "\n")) != NULL) {
262 if (strcasecmp(p, "quit") == 0)
263 return P_QUIT;
264 else if (strcasecmp(p, "help") == 0)
265 client_help(cl->fd, NULL);
266 else if (strncasecmp(p, "help ", 5) == 0) {
267 t = p + 5;
268 t = skip_space(t);
269 client_help(cl->fd, t);
271 else if (strncasecmp(p, "get ", 4) == 0) {
272 t = p + 4;
273 t = skip_space(t);
275 if (cl->state != STATE_AUTH)
276 send_to_client(cl->fd, "ERR not authenticated\n");
277 else {
278 // FIXME reuse existing cl->doc and cl->reader handles.
279 if (cl->doc) {
280 xmlFreeTextReader(cl->reader);
281 xmlFreeDoc(cl->doc);
283 if (!open_xml(cl->xml, cl->len, &cl->doc, &cl->root, &cl->reader)) {
284 cl->doc->children = cl->root;
285 xmlReaderNewWalker(cl->reader, cl->doc);
287 if (parse_account(cl, t)) {
288 send_to_client(cl->fd, "ERR element parse error\n");
294 else if (strncasecmp(p, "auth ", 5) == 0) {
295 if (cl->state == STATE_AUTH)
296 send_to_client(cl->fd, "ERR already authenticated\n");
297 else {
298 t = p + 5;
299 t = skip_space(t);
301 if (!authenticate_client(cl, t))
302 send_to_client(cl->fd, "OK authenticated\n");
305 else
306 send_to_client(cl->fd, "ERR invalid command or command syntax\n");
309 return P_OK;
313 * Called every time a connection is made.
314 * FIXME protocol
315 * FIXME SSL
317 void doit(int rfd)
319 struct client_s *cl = calloc(1, sizeof(struct client_s));
321 cl->state = STATE_CONNECTED;
322 cl->fd = rfd;
323 send_to_client(rfd, "NFO Type help for available commands\n");
325 while (1) {
326 fd_set rfds, wfds;
327 char buf[LINE_MAX] = {0};
329 FD_ZERO(&rfds);
330 FD_SET(cl->fd, &rfds);
331 FD_ZERO(&wfds);
332 FD_SET(cl->fd, &wfds);
334 switch (select(cl->fd + 1, &rfds, NULL, NULL, NULL)) {
335 case -1:
336 warn("select()");
337 break;
338 case 0:
339 // timeout
340 continue;
341 default:
342 break;
345 if (FD_ISSET(cl->fd, &rfds)) {
346 int len = recv(cl->fd, buf, sizeof(buf), 0);
348 buf[len - 1] = 0;
350 if (len == -1) {
351 warn("recv()");
352 continue;
354 else if (len == 0)
355 break;
357 switch (input_parser(cl, buf)) {
358 case P_QUIT:
359 goto done;
360 default:
361 break;
366 done:
367 if (cl->xml)
368 memset(cl->xml, 0, cl->len);
370 shutdown(cl->fd, SHUT_RDWR);
371 close(cl->fd);
372 _exit(EXIT_SUCCESS);
375 int main(int argc, char *argv[])
377 int opt;
378 int rfd;
379 struct sockaddr_in laddr, raddr;
380 socklen_t slen;
381 int yes = 0;
382 int port = DEFAULT_PORT;
384 while ((opt = getopt(argc, argv, "hp:")) != EOF) {
385 switch (opt) {
386 case 'p':
387 port = atoi(optarg);
388 break;
389 default:
390 usage(argv[0]);
394 if ((sfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
395 err(EXIT_FAILURE, "socket()");
397 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
398 err(EXIT_FAILURE, "setsockopt()");
400 laddr.sin_family = AF_INET;
401 laddr.sin_port = htons(port);
402 laddr.sin_addr.s_addr = INADDR_ANY;
403 memset(&(laddr.sin_zero), 0, 8);
405 if (bind(sfd, (struct sockaddr *)&laddr, sizeof(struct sockaddr)) == -1)
406 err(EXIT_FAILURE, "bind()");
408 if (listen(sfd, 10) == -1)
409 err(EXIT_FAILURE, "listen()");
411 signal(SIGCHLD, catchsig);
412 signal(SIGTERM, catchsig);
413 signal(SIGINT, catchsig);
415 while (1) {
416 if (quit)
417 break;
419 slen = sizeof(struct sockaddr_in);
421 if ((rfd = accept(sfd, (struct sockaddr_in *)&raddr, &slen)) == -1) {
422 if (errno != EBADF)
423 warn("accept");
425 continue;
428 switch (fork()) {
429 case -1:
430 warn("fork()");
431 break;
432 case 0:
433 doit(rfd);
434 break;
435 default:
436 break;
440 exit(EXIT_SUCCESS);