6 #include <libxml/tree.h>
7 #include <libxml/parser.h>
8 #include <libxml/xmlreader.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
17 #define DEFAULT_PORT 5555
27 xmlTextReaderPtr reader
;
42 void catchsig(int sig
)
52 xmlNodePtr
find_node(xmlNodePtr root
, xmlChar
*name
)
56 for (n
= root
->children
; n
; n
= n
->next
) {
57 if (strcmp((char *)name
, (char *)n
->name
) == 0)
64 xmlNodePtr
create_doc(xmlDocPtr
*doc
, const char *root
)
66 xmlDocPtr d
= xmlNewDoc((xmlChar
*)"1.0");
67 xmlNodePtr n
= xmlNewNode(NULL
, (xmlChar
*)root
);
70 xmlDocSetRootElement(d
, n
);
74 int add_node(xmlNodePtr root
, char **exp
)
77 xmlNodePtr n
= NULL
, l
= root
;
79 for (p
= exp
; *p
; p
++) {
80 n
= find_node(l
, (xmlChar
*)*p
);
82 // non-matching element.
85 xmlNodeAddContent(n
, (xmlChar
*)*p
);
89 xmlNewChild(l
, NULL
, (xmlChar
*)*p
, NULL
);
90 l
->next
->type
= XML_ELEMENT_NODE
;
101 if (!exp
&& p
&& n
) {
102 xmlNodeAddContent(n
, (xmlChar
*)p
);
107 warnx("add: parent node \"%s\" does not exist", p
);
111 xmlNewChild(n
, NULL
, (xmlChar
*)p
, (xmlChar
*)"value");
120 "Usage: %s [-h] [-p <port>]\n"
121 " -p alternate port (%i)\n"
122 " -h this help text\n",
127 int find_account(xmlTextReaderPtr reader
, xmlChar
*name
)
132 while (xmlTextReaderRead(reader
) == 1) {
133 if ((n
= xmlTextReaderCurrentNode(reader
)) == NULL
)
137 * "name" is the element that holds the account name. It would be nice
138 * to have an element the is the account name but I wouldn't know how
139 * to do it with the DTD.
141 if (xmlTextReaderDepth(reader
) == 1 &&
142 xmlStrcmp(n
->name
, (xmlChar
*)"name") &&
143 xmlTextReaderNodeType(reader
) == XML_READER_TYPE_ELEMENT
) {
145 if (xmlTextReaderNext(reader
) != 1)
147 type
= xmlTextReaderNodeType(reader
);
148 } while (type
!= -1 && type
!= XML_READER_TYPE_TEXT
);
150 if ((n
= xmlTextReaderCurrentNode(reader
)) == NULL
)
153 if (xmlStrcmp(n
->content
, name
) == 0)
161 int find_element(xmlTextReaderPtr reader
, xmlChar
*e
, int more
)
166 while (xmlTextReaderRead(reader
) == 1) {
167 if ((n
= xmlTextReaderCurrentNode(reader
)) == NULL
)
170 if (xmlStrcmp(n
->name
, e
) == 0 &&
171 xmlTextReaderNodeType(reader
) == XML_READER_TYPE_ELEMENT
) {
176 * FIXME account/service without details (account/service/host).
179 if (xmlTextReaderNext(reader
) != 1)
181 type
= xmlTextReaderNodeType(reader
);
182 } while (type
!= -1 && type
!= XML_READER_TYPE_TEXT
);
183 return (type
== XML_READER_TYPE_TEXT
) ? 0 : 1;
190 void send_to_client(int fd
, char *fmt
, ...)
196 vasprintf(&buf
, fmt
, ap
);
199 if (send(fd
, buf
, strlen(buf
), 0) == -1)
210 if (access(argv
[optind
], R_OK
) != 0 && errno
== ENOENT
) {
211 root
= create_doc(&doc
, "accounts");
214 err(EXIT_FAILURE
, "%s", argv
[optind
]);
217 if ((doc
= xmlReadFile(argv
[optind
++], NULL
, 0)) == NULL
)
218 errx(EXIT_FAILURE
, "parse error");
220 root
= doc
->children
;
223 if ((reader
= xmlReaderWalker(doc
)) == NULL
)
224 errx(EXIT_FAILURE
, "xmlReaderWalker() failed");
228 while ((p
= strsep(&s
, "/")) != NULL
) {
229 exp
= realloc(exp
, (i
+ 2) * sizeof(xmlChar
*));
230 exp
[i
++] = xmlStrdup((xmlChar
*)p
);
235 errx(EXIT_FAILURE
, "account expression parse error");
239 if (find_account(reader
, exp
[i
++]))
240 errx(EXIT_FAILURE
, "account not found");
243 * We are at the position in the document where the account was found.
244 * Search through the account for the wanted elements.
246 for (; exp
[i
]; i
++) {
249 if (find_element(reader
, exp
[i
], exp
[i
+1] != NULL
))
250 errx(EXIT_FAILURE
, "could not find element %s", exp
[i
]);
253 * We are at the end of the element list. Save the result.
256 n
= xmlTextReaderCurrentNode(reader
);
257 result
= xmlStrdup(n
->content
);
262 for (i
= 0; exp
[i
]; i
++)
268 xmlFreeTextReader(reader
);
275 int parse_account(struct client_s
*cl
, char *str
)
281 for (i
= 0; cl
->req
[i
]; i
++)
287 while ((p
= strsep(&str
, "/")) != NULL
) {
288 cl
->req
= realloc(cl
->req
, (i
+ 2) * sizeof(xmlChar
*));
289 cl
->req
[i
++] = xmlStrdup((xmlChar
*)p
);
298 if (find_account(cl
->reader
, cl
->req
[i
++])) {
299 send_to_client(cl
->fd
, "000 account \"%s\" not found\n", cl
->req
[0]);
304 * We are at the position in the document where the account was found.
305 * Search through the account for the wanted elements.
307 for (; cl
->req
[i
]; i
++) {
310 if (find_element(cl
->reader
, cl
->req
[i
], cl
->req
[i
+1] != NULL
)) {
311 send_to_client(cl
->fd
, "could not find element \"%s\"",
317 * We are at the end of the element list. Save the result.
320 n
= xmlTextReaderCurrentNode(cl
->reader
);
321 send_to_client(cl
->fd
, "%s\n", n
->content
);
328 void client_help(int fd
, const char *what
)
334 "000 Try 'help topic' for details\n"
335 "000 auth get quit\n";
336 else if (strcasecmp(what
, "get") == 0)
338 "000 syntax: get account[/element[/...]]\n"
339 "000 <account> is the account to work on and <element>\n"
340 "000 is the account elements wanted.\n"
342 "000 Example: get isp/imap/port\n"
343 "000 get isp/username\n";
344 else if (strcasecmp(what
, "quit") == 0)
347 "000 close the connection\n";
348 else if (strcasecmp(what
, "auth") == 0)
350 "000 syntax: auth username password\n";
352 line
= "000 unknown command\n";
354 send_to_client(fd
, line
);
357 char *skip_space(char *str
)
359 while (isspace(*str
))
365 int open_xml(struct client_s
*cl
)
367 if ((cl
->doc
= xmlReadFile(cl
->filename
, NULL
, 0)) == NULL
) {
368 send_to_client(cl
->fd
, "000 error while parsing XML\n");
372 cl
->root
= xmlDocGetRootElement(cl
->doc
);
374 if ((cl
->reader
= xmlReaderWalker(cl
->doc
)) == NULL
) {
375 send_to_client(cl
->fd
, "xmlReaderWalker() failed");
383 * The filename will be username.xml.
385 int authenticate_client(struct client_s
*cl
, char *str
)
387 char *user
= NULL
, *pass
= NULL
;
388 char buf
[FILENAME_MAX
];
390 if ((user
= strsep(&str
, " ")) == NULL
)
394 snprintf(buf
, sizeof(buf
), "%s.xml", user
);
397 if (access(argv[optind], R_OK) != 0 && errno == ENOENT) {
398 root = create_doc(&doc, "accounts");
401 err(EXIT_FAILURE, "%s", argv[optind]);
404 cl
->filename
= strdup(buf
);
405 cl
->state
= STATE_AUTH
;
410 int input_parser(struct client_s
*cl
, char *str
)
414 while ((p
= strsep(&str
, "\n")) != NULL
) {
415 if (strcasecmp(p
, "quit") == 0)
417 else if (strcasecmp(p
, "help") == 0)
418 client_help(cl
->fd
, NULL
);
419 else if (strncasecmp(p
, "help ", 5) == 0) {
422 client_help(cl
->fd
, t
);
424 else if (strncasecmp(p
, "get ", 4) == 0) {
428 if (cl
->state
!= STATE_AUTH
) {
429 send_to_client(cl
->fd
, "000 not authenticated\n");
432 // FIXME reuse existing cl->doc and cl->reader handles.
434 xmlFreeTextReader(cl
->reader
);
438 cl
->doc
->children
= cl
->root
;
439 xmlReaderNewWalker(cl
->reader
, cl
->doc
);
441 if (parse_account(cl
, t
)) {
442 send_to_client(cl
->fd
, "000 parse error\n");
447 else if (strncasecmp(p
, "auth ", 5) == 0) {
451 if (!authenticate_client(cl
, t
))
452 send_to_client(cl
->fd
, "000 authenticated\n");
460 * Called every time a connection is made.
466 struct client_s
*cl
= calloc(1, sizeof(struct client_s
));
468 cl
->state
= STATE_CONNECTED
;
470 send_to_client(rfd
, "000 Type help for available commands\n");
474 char buf
[LINE_MAX
] = {0};
478 FD_SET(cl
->fd
, &rfds
);
480 FD_SET(cl
->fd
, &wfds
);
485 switch (select(cl
->fd
+ 1, &rfds
, &wfds
, NULL
, NULL
)) {
496 if (FD_ISSET(cl
->fd
, &rfds
)) {
497 int len
= recv(cl
->fd
, buf
, sizeof(buf
), 0);
508 switch (input_parser(cl
, buf
)) {
518 shutdown(cl
->fd
, SHUT_RDWR
);
522 int main(int argc
, char *argv
[])
526 struct sockaddr_in laddr
, raddr
;
529 int port
= DEFAULT_PORT
;
531 while ((opt
= getopt(argc
, argv
, "hp:")) != EOF
) {
541 if ((sfd
= socket(PF_INET
, SOCK_STREAM
, 0)) == -1)
542 err(EXIT_FAILURE
, "socket()");
544 if (setsockopt(sfd
, SOL_SOCKET
, SO_REUSEADDR
, &yes
, sizeof(int)) == -1)
545 err(EXIT_FAILURE
, "setsockopt()");
547 laddr
.sin_family
= AF_INET
;
548 laddr
.sin_port
= htons(port
);
549 laddr
.sin_addr
.s_addr
= INADDR_ANY
;
550 memset(&(laddr
.sin_zero
), 0, 8);
552 if (bind(sfd
, (struct sockaddr
*)&laddr
, sizeof(struct sockaddr
)) == -1)
553 err(EXIT_FAILURE
, "bind()");
555 if (listen(sfd
, 10) == -1)
556 err(EXIT_FAILURE
, "listen()");
558 signal(SIGCHLD
, catchsig
);
561 slen
= sizeof(struct sockaddr_in
);
563 if ((rfd
= accept(sfd
, (struct sockaddr_in
*)&raddr
, &slen
)) == -1) {