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 write_dtd(FILE *fp
)
45 "<?xml version=\"1.0\"?>\n"
46 "<!DOCTYPE accounts [\n"
47 "!ELEMENT accounts (account*)>\n"
48 "!ELEMENT account (name, username?, password?, smtp?, pop?, imap?)>\n"
49 "!ELEMENT name (#PCDATA)>\n"
50 "!ELEMENT username (#PCDATA)>\n"
51 "!ELEMENT password (#PCDATA)>\n"
52 "!ELEMENT smtp (host, port, ssl)>\n"
53 "!ELEMENT pop (host, port, ssl)>\n"
54 "!ELEMENT imap (host, port, ssl)>\n"
55 "!ELEMENT host (#PCDATA)>\n"
56 "!ELEMENT port (#PCDATA)>\n"
57 "!ELEMENT ssl (#PCDATA)>\n"
62 void catchsig(int sig
)
72 xmlNodePtr
find_node(xmlNodePtr root
, xmlChar
*name
)
76 for (n
= root
->children
; n
; n
= n
->next
) {
77 if (strcmp((char *)name
, (char *)n
->name
) == 0)
84 xmlNodePtr
create_doc(xmlDocPtr
*doc
, const char *root
)
86 xmlDocPtr d
= xmlNewDoc((xmlChar
*)"1.0");
87 xmlNodePtr n
= xmlNewNode(NULL
, (xmlChar
*)root
);
90 xmlDocSetRootElement(d
, n
);
94 int add_node(xmlNodePtr root
, char **exp
)
97 xmlNodePtr n
= NULL
, l
= root
;
99 for (p
= exp
; *p
; p
++) {
100 n
= find_node(l
, (xmlChar
*)*p
);
102 // non-matching element.
105 xmlNodeAddContent(n
, (xmlChar
*)*p
);
109 xmlNewChild(l
, NULL
, (xmlChar
*)*p
, NULL
);
110 l
->next
->type
= XML_ELEMENT_NODE
;
121 if (!exp
&& p
&& n
) {
122 xmlNodeAddContent(n
, (xmlChar
*)p
);
127 warnx("add: parent node \"%s\" does not exist", p
);
131 xmlNewChild(n
, NULL
, (xmlChar
*)p
, (xmlChar
*)"value");
140 "Usage: %s [-h] [-p <port>]\n"
141 " -p alternate port (%i)\n"
142 " -h this help text\n",
147 int find_account(xmlTextReaderPtr reader
, xmlChar
*name
)
152 while (xmlTextReaderRead(reader
) == 1) {
153 if ((n
= xmlTextReaderCurrentNode(reader
)) == NULL
)
157 * "name" is the element that holds the account name. It would be nice
158 * to have an element the is the account name but I wouldn't know how
159 * to do it with the DTD.
161 if (xmlTextReaderDepth(reader
) == 1 &&
162 xmlStrcmp(n
->name
, (xmlChar
*)"name") &&
163 xmlTextReaderNodeType(reader
) == XML_READER_TYPE_ELEMENT
) {
165 if (xmlTextReaderNext(reader
) != 1)
167 type
= xmlTextReaderNodeType(reader
);
168 } while (type
!= -1 && type
!= XML_READER_TYPE_TEXT
);
170 if ((n
= xmlTextReaderCurrentNode(reader
)) == NULL
)
173 if (xmlStrcmp(n
->content
, name
) == 0)
181 int find_element(xmlTextReaderPtr reader
, xmlChar
*e
, int more
)
186 while (xmlTextReaderRead(reader
) == 1) {
187 if ((n
= xmlTextReaderCurrentNode(reader
)) == NULL
)
190 if (xmlStrcmp(n
->name
, e
) == 0 &&
191 xmlTextReaderNodeType(reader
) == XML_READER_TYPE_ELEMENT
) {
196 * FIXME account/service without details (account/service/host).
199 if (xmlTextReaderNext(reader
) != 1)
201 type
= xmlTextReaderNodeType(reader
);
202 } while (type
!= -1 && type
!= XML_READER_TYPE_TEXT
);
203 return (type
== XML_READER_TYPE_TEXT
) ? 0 : 1;
210 void send_to_client(int fd
, char *fmt
, ...)
216 vasprintf(&buf
, fmt
, ap
);
219 if (send(fd
, buf
, strlen(buf
), 0) == -1)
230 if (access(argv
[optind
], R_OK
) != 0 && errno
== ENOENT
) {
231 root
= create_doc(&doc
, "accounts");
234 err(EXIT_FAILURE
, "%s", argv
[optind
]);
237 if ((doc
= xmlReadFile(argv
[optind
++], NULL
, 0)) == NULL
)
238 errx(EXIT_FAILURE
, "parse error");
240 root
= doc
->children
;
243 if ((reader
= xmlReaderWalker(doc
)) == NULL
)
244 errx(EXIT_FAILURE
, "xmlReaderWalker() failed");
248 while ((p
= strsep(&s
, "/")) != NULL
) {
249 exp
= realloc(exp
, (i
+ 2) * sizeof(xmlChar
*));
250 exp
[i
++] = xmlStrdup((xmlChar
*)p
);
255 errx(EXIT_FAILURE
, "account expression parse error");
259 if (find_account(reader
, exp
[i
++]))
260 errx(EXIT_FAILURE
, "account not found");
263 * We are at the position in the document where the account was found.
264 * Search through the account for the wanted elements.
266 for (; exp
[i
]; i
++) {
269 if (find_element(reader
, exp
[i
], exp
[i
+1] != NULL
))
270 errx(EXIT_FAILURE
, "could not find element %s", exp
[i
]);
273 * We are at the end of the element list. Save the result.
276 n
= xmlTextReaderCurrentNode(reader
);
277 result
= xmlStrdup(n
->content
);
282 for (i
= 0; exp
[i
]; i
++)
288 xmlFreeTextReader(reader
);
295 int parse_account(struct client_s
*cl
, char *str
)
301 for (i
= 0; cl
->req
[i
]; i
++)
307 while ((p
= strsep(&str
, "/")) != NULL
) {
308 cl
->req
= realloc(cl
->req
, (i
+ 2) * sizeof(xmlChar
*));
309 cl
->req
[i
++] = xmlStrdup((xmlChar
*)p
);
318 if (find_account(cl
->reader
, cl
->req
[i
++])) {
319 send_to_client(cl
->fd
, "000 account \"%s\" not found\n", cl
->req
[0]);
324 * We are at the position in the document where the account was found.
325 * Search through the account for the wanted elements.
327 for (; cl
->req
[i
]; i
++) {
330 if (find_element(cl
->reader
, cl
->req
[i
], cl
->req
[i
+1] != NULL
)) {
331 send_to_client(cl
->fd
, "could not find element \"%s\"",
337 * We are at the end of the element list. Save the result.
340 n
= xmlTextReaderCurrentNode(cl
->reader
);
341 send_to_client(cl
->fd
, "%s\n", n
->content
);
348 void client_help(int fd
, const char *what
)
354 "000 Try 'help topic' for details\n"
355 "000 auth get quit\n";
356 else if (strcasecmp(what
, "get") == 0)
358 "000 syntax: get account[/element[/...]]\n"
359 "000 <account> is the account to work on and <element>\n"
360 "000 is the account elements wanted.\n"
362 "000 Example: get isp/imap/port\n"
363 "000 get isp/username\n";
364 else if (strcasecmp(what
, "quit") == 0)
367 "000 close the connection\n";
368 else if (strcasecmp(what
, "auth") == 0)
370 "000 syntax: auth username password\n";
372 line
= "000 unknown command\n";
374 send_to_client(fd
, line
);
377 char *skip_space(char *str
)
379 while (isspace(*str
))
385 int open_xml(struct client_s
*cl
)
387 if ((cl
->doc
= xmlReadFile(cl
->filename
, NULL
, 0)) == NULL
) {
388 send_to_client(cl
->fd
, "000 error while parsing XML\n");
392 cl
->root
= xmlDocGetRootElement(cl
->doc
);
394 if ((cl
->reader
= xmlReaderWalker(cl
->doc
)) == NULL
) {
395 send_to_client(cl
->fd
, "xmlReaderWalker() failed");
403 * The filename will be username.xml.
405 int authenticate_client(struct client_s
*cl
, char *str
)
407 char *user
= NULL
, *pass
= NULL
;
408 char buf
[FILENAME_MAX
];
410 if ((user
= strsep(&str
, " ")) == NULL
)
414 snprintf(buf
, sizeof(buf
), "%s.xml", user
);
417 if (access(argv[optind], R_OK) != 0 && errno == ENOENT) {
418 root = create_doc(&doc, "accounts");
421 err(EXIT_FAILURE, "%s", argv[optind]);
424 cl
->filename
= strdup(buf
);
425 cl
->state
= STATE_AUTH
;
430 int input_parser(struct client_s
*cl
, char *str
)
434 while ((p
= strsep(&str
, "\n")) != NULL
) {
435 if (strcasecmp(p
, "quit") == 0)
437 else if (strcasecmp(p
, "help") == 0)
438 client_help(cl
->fd
, NULL
);
439 else if (strncasecmp(p
, "help ", 5) == 0) {
442 client_help(cl
->fd
, t
);
444 else if (strncasecmp(p
, "get ", 4) == 0) {
448 if (cl
->state
!= STATE_AUTH
) {
449 send_to_client(cl
->fd
, "000 not authenticated\n");
452 // FIXME reuse existing cl->doc and cl->reader handles.
454 xmlFreeTextReader(cl
->reader
);
458 cl
->doc
->children
= cl
->root
;
459 xmlReaderNewWalker(cl
->reader
, cl
->doc
);
461 if (parse_account(cl
, t
)) {
462 send_to_client(cl
->fd
, "000 parse error\n");
467 else if (strncasecmp(p
, "auth ", 5) == 0) {
471 if (!authenticate_client(cl
, t
))
472 send_to_client(cl
->fd
, "000 authenticated\n");
480 * Called every time a connection is made.
486 struct client_s
*cl
= calloc(1, sizeof(struct client_s
));
488 cl
->state
= STATE_CONNECTED
;
490 send_to_client(rfd
, "000 Type help for available commands\n");
494 char buf
[LINE_MAX
] = {0};
498 FD_SET(cl
->fd
, &rfds
);
500 FD_SET(cl
->fd
, &wfds
);
505 switch (select(cl
->fd
+ 1, &rfds
, &wfds
, NULL
, NULL
)) {
516 if (FD_ISSET(cl
->fd
, &rfds
)) {
517 int len
= recv(cl
->fd
, buf
, sizeof(buf
), 0);
528 switch (input_parser(cl
, buf
)) {
538 shutdown(cl
->fd
, SHUT_RDWR
);
542 int main(int argc
, char *argv
[])
546 struct sockaddr_in laddr
, raddr
;
549 int port
= DEFAULT_PORT
;
551 while ((opt
= getopt(argc
, argv
, "hp:")) != EOF
) {
561 if ((sfd
= socket(PF_INET
, SOCK_STREAM
, 0)) == -1)
562 err(EXIT_FAILURE
, "socket()");
564 if (setsockopt(sfd
, SOL_SOCKET
, SO_REUSEADDR
, &yes
, sizeof(int)) == -1)
565 err(EXIT_FAILURE
, "setsockopt()");
567 laddr
.sin_family
= AF_INET
;
568 laddr
.sin_port
= htons(port
);
569 laddr
.sin_addr
.s_addr
= INADDR_ANY
;
570 memset(&(laddr
.sin_zero
), 0, 8);
572 if (bind(sfd
, (struct sockaddr
*)&laddr
, sizeof(struct sockaddr
)) == -1)
573 err(EXIT_FAILURE
, "bind()");
575 if (listen(sfd
, 10) == -1)
576 err(EXIT_FAILURE
, "listen()");
578 signal(SIGCHLD
, catchsig
);
581 slen
= sizeof(struct sockaddr_in
);
583 if ((rfd
= accept(sfd
, (struct sockaddr_in
*)&raddr
, &slen
)) == -1) {