1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
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
26 #include <sys/socket.h>
27 #include <netinet/in.h>
41 void catchsig(int sig
)
47 waitpid(-1, &status
, 0);
50 shutdown(sfd
, SHUT_RDWR
);
60 "Usage: %s [-h] [-p <port>]\n"
61 " -p alternate port (%i)\n"
62 " -h this help text\n",
67 int send_to_client(int fd
, char *fmt
, ...)
74 vasprintf(&buf
, fmt
, ap
);
83 switch (select(fd
+ 1, NULL
, &fds
, NULL
, NULL
)) {
94 if (send(fd
, buf
, strlen(buf
), 0) == -1) {
106 int parse_account(struct client_s
*cl
, char *str
)
112 for (i
= 0; cl
->req
[i
]; i
++)
118 while ((p
= strsep(&str
, "/")) != NULL
) {
119 cl
->req
= realloc(cl
->req
, (i
+ 2) * sizeof(xmlChar
*));
120 cl
->req
[i
++] = xmlStrdup((xmlChar
*)p
);
129 if (find_account(cl
->reader
, cl
->req
[i
++])) {
130 send_to_client(cl
->fd
, "ERR account \"%s\" not found\n", cl
->req
[0]);
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
++) {
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\"",
148 * We are at the end of the element list. Save the result.
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");
163 void client_help(int fd
, const char *what
)
169 "NFO Try 'help topic' for details\n"
170 "NFO auth get quit\n";
171 else if (strcasecmp(what
, "get") == 0)
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"
177 "NFO Example: get isp/imap/port\n"
178 "NFO get isp/username\n";
179 else if (strcasecmp(what
, "quit") == 0)
182 "NFO close the connection\n";
183 else if (strcasecmp(what
, "auth") == 0)
185 "NFO syntax: auth username\n";
187 line
= "NFO unknown command\n";
189 send_to_client(fd
, line
);
192 char *skip_space(char *str
)
194 while (isspace(*str
))
201 * The filename will be username.xml.gpg.
203 int authenticate_client(struct client_s
*cl
, char *user
)
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
) {
220 int len
= fread(buf
, 1, sizeof(buf
), fp
);
225 xml
= realloc(xml
, t
+ len
+ 1);
226 memcpy(&xml
[t
], buf
, len
);
236 * Not needed anymore.
242 switch (open_xml(cl
->xml
, cl
->len
, &cl
->doc
, &cl
->root
, &cl
->reader
)) {
244 send_to_client(cl
->fd
, "ERR XML parse error\n");
247 send_to_client(cl
->fd
, "ERR XMLReaderWalker() failed\n");
253 cl
->state
= STATE_AUTH
;
257 int input_parser(struct client_s
*cl
, char *str
)
261 while ((p
= strsep(&str
, "\n")) != NULL
) {
262 if (strcasecmp(p
, "quit") == 0)
264 else if (strcasecmp(p
, "help") == 0)
265 client_help(cl
->fd
, NULL
);
266 else if (strncasecmp(p
, "help ", 5) == 0) {
269 client_help(cl
->fd
, t
);
271 else if (strncasecmp(p
, "get ", 4) == 0) {
275 if (cl
->state
!= STATE_AUTH
)
276 send_to_client(cl
->fd
, "ERR not authenticated\n");
278 // FIXME reuse existing cl->doc and cl->reader handles.
280 xmlFreeTextReader(cl
->reader
);
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");
301 if (!authenticate_client(cl
, t
))
302 send_to_client(cl
->fd
, "OK authenticated\n");
306 send_to_client(cl
->fd
, "ERR invalid command or command syntax\n");
313 * Called every time a connection is made.
319 struct client_s
*cl
= calloc(1, sizeof(struct client_s
));
321 cl
->state
= STATE_CONNECTED
;
323 send_to_client(rfd
, "NFO Type help for available commands\n");
327 char buf
[LINE_MAX
] = {0};
330 FD_SET(cl
->fd
, &rfds
);
332 FD_SET(cl
->fd
, &wfds
);
334 switch (select(cl
->fd
+ 1, &rfds
, NULL
, NULL
, NULL
)) {
345 if (FD_ISSET(cl
->fd
, &rfds
)) {
346 int len
= recv(cl
->fd
, buf
, sizeof(buf
), 0);
357 switch (input_parser(cl
, buf
)) {
368 memset(cl
->xml
, 0, cl
->len
);
370 shutdown(cl
->fd
, SHUT_RDWR
);
375 int main(int argc
, char *argv
[])
379 struct sockaddr_in laddr
, raddr
;
382 int port
= DEFAULT_PORT
;
384 while ((opt
= getopt(argc
, argv
, "hp:")) != EOF
) {
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
);
419 slen
= sizeof(struct sockaddr_in
);
421 if ((rfd
= accept(sfd
, (struct sockaddr_in
*)&raddr
, &slen
)) == -1) {