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>
38 #include "pwmserver.h"
40 void catchsig(int sig
)
46 waitpid(-1, &status
, 0);
49 shutdown(sfd
, SHUT_RDWR
);
59 "Usage: %s [-h] [-p <port>]\n"
60 " -p alternate port (%i)\n"
61 " -h this help text\n",
66 int send_to_client(int fd
, char *fmt
, ...)
73 vasprintf(&buf
, fmt
, ap
);
82 switch (select(fd
+ 1, NULL
, &fds
, NULL
, NULL
)) {
93 if (send(fd
, buf
, strlen(buf
), 0) == -1) {
105 int parse_account(struct client_s
*cl
, char *str
)
111 for (i
= 0; cl
->req
[i
]; i
++)
117 while ((p
= strsep(&str
, "/")) != NULL
) {
118 cl
->req
= realloc(cl
->req
, (i
+ 2) * sizeof(xmlChar
*));
119 cl
->req
[i
++] = xmlStrdup((xmlChar
*)p
);
128 if (find_account(cl
->reader
, cl
->req
[i
++])) {
129 send_to_client(cl
->fd
, "ERR account \"%s\" not found\n", cl
->req
[0]);
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
++) {
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\"",
147 * We are at the end of the element list. Save the result.
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");
162 void client_help(int fd
, const char *what
)
168 "NFO Try 'help topic' for details\n"
169 "NFO auth get quit\n";
170 else if (strcasecmp(what
, "get") == 0)
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"
176 "NFO Example: get isp/imap/port\n"
177 "NFO get isp/username\n";
178 else if (strcasecmp(what
, "quit") == 0)
181 "NFO close the connection\n";
182 else if (strcasecmp(what
, "auth") == 0)
184 "NFO syntax: auth username password\n";
186 line
= "NFO unknown command\n";
188 send_to_client(fd
, line
);
191 char *skip_space(char *str
)
193 while (isspace(*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
)
211 snprintf(buf
, sizeof(buf
), "%s.xml", user
);
214 if (access(argv[optind], R_OK) != 0 && errno == ENOENT) {
215 root = create_doc(&doc, "accounts");
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
)) {
226 send_to_client(cl
->fd
, "ERR XML parse error\n");
229 send_to_client(cl
->fd
, "ERR XMLReaderWalker() failed\n");
238 int input_parser(struct client_s
*cl
, char *str
)
242 while ((p
= strsep(&str
, "\n")) != NULL
) {
243 if (strcasecmp(p
, "quit") == 0)
245 else if (strcasecmp(p
, "help") == 0)
246 client_help(cl
->fd
, NULL
);
247 else if (strncasecmp(p
, "help ", 5) == 0) {
250 client_help(cl
->fd
, t
);
252 else if (strncasecmp(p
, "get ", 4) == 0) {
256 if (cl
->state
!= STATE_AUTH
)
257 send_to_client(cl
->fd
, "ERR not authenticated\n");
259 // FIXME reuse existing cl->doc and cl->reader handles.
261 xmlFreeTextReader(cl
->reader
);
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");
282 if (!authenticate_client(cl
, t
))
283 send_to_client(cl
->fd
, "OK authenticated\n");
292 * Called every time a connection is made.
298 struct client_s
*cl
= calloc(1, sizeof(struct client_s
));
300 cl
->state
= STATE_CONNECTED
;
302 send_to_client(rfd
, "NFO Type help for available commands\n");
306 char buf
[LINE_MAX
] = {0};
309 FD_SET(cl
->fd
, &rfds
);
311 FD_SET(cl
->fd
, &wfds
);
313 switch (select(cl
->fd
+ 1, &rfds
, NULL
, NULL
, NULL
)) {
324 if (FD_ISSET(cl
->fd
, &rfds
)) {
325 int len
= recv(cl
->fd
, buf
, sizeof(buf
), 0);
336 switch (input_parser(cl
, buf
)) {
346 shutdown(cl
->fd
, SHUT_RDWR
);
351 int main(int argc
, char *argv
[])
355 struct sockaddr_in laddr
, raddr
;
358 int port
= DEFAULT_PORT
;
360 while ((opt
= getopt(argc
, argv
, "hp:")) != EOF
) {
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
);
395 slen
= sizeof(struct sockaddr_in
);
397 if ((rfd
= accept(sfd
, (struct sockaddr_in
*)&raddr
, &slen
)) == -1) {