]> Pileus Git - ~andy/lamechat/commitdiff
XMPP Connect
authorAndy Spencer <andy753421@gmail.com>
Tue, 12 Sep 2017 07:55:48 +0000 (07:55 +0000)
committerAndy Spencer <andy753421@gmail.com>
Tue, 12 Sep 2017 07:55:48 +0000 (07:55 +0000)
irc.c
makefile
util.c
util.h
xmpp.c

diff --git a/irc.c b/irc.c
index 87014428da2165cf9a33e9157f95a81f905d3b5f..ec5a27267ae9f9a7d9971c82c7f7d07566076924 100644 (file)
--- a/irc.c
+++ b/irc.c
@@ -46,9 +46,9 @@ typedef enum {
 } irc_state_t;
 
 typedef struct irc_server_t {
+       const char *name;
        const char *protocol;
        int         connect;
-       const char *name;
        const char *host;
        int         port;
        const char *nick;
@@ -218,14 +218,28 @@ static void recv_line(irc_server_t *srv, const char *line)
                chat_recv(dst, from, msg);
 }
 
-static void on_poll(int fd, irc_server_t *srv)
+static void on_poll(void *_srv, int fd)
 {
        static char buf[BUF_LEN];
        static char hostname[512];
+
+       irc_server_t *srv = _srv;
        int len;
 
        /* Handle Input */
        len = recv(fd, buf, sizeof(buf), 0);
+       if (len < 0) {
+               debug("recv: error: %s -- %s", srv->name, strerror(errno));
+       }
+       if (len == 0) {
+               debug("recv: close: %s", srv->name);
+               srv->state = IRC_DEAD;
+               poll_del(&srv->poll);
+               return;
+       }
+       if (len > 0) {
+               debug("recv: ready: %s -- [%.*s]", srv->name, len, buf);
+       }
        for (int i = 0; i < len; i++) {
                if (srv->in_len < BUF_LEN) {
                        srv->in_buf[srv->in_len] = buf[i];
@@ -308,13 +322,13 @@ static void irc_connect(irc_server_t *srv)
        if ((sock = socket(addrs->ai_family,
                           addrs->ai_socktype,
                           addrs->ai_protocol)) < 0)
-               error("Error opening web socket");
+               error("Error opening irc socket");
 
        if ((flags = fcntl(sock, F_GETFL, 0)) < 0)
-               error("Error getting web socket flags");
+               error("Error getting irc socket flags");
 
        if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0)
-               error("Error setting web socket non-blocking");
+               error("Error setting irc socket non-blocking");
 
        if (connect(sock, addrs->ai_addr, addrs->ai_addrlen) < 0)
                if (errno != EINPROGRESS)
@@ -324,7 +338,7 @@ static void irc_connect(irc_server_t *srv)
 
        /* Setup server */
        srv->state = IRC_USER;
-       poll_add(&srv->poll, sock, (cb_t)on_poll, srv);
+       poll_add(&srv->poll, sock, on_poll, srv);
 }
 
 /* IRC functions */
index bd61505fc3f7569c769f19b48e4a5d54148fe7e9..0a99140d71b6fbb6b5cb9851fee74a6ecf4a3348 100644 (file)
--- a/makefile
+++ b/makefile
@@ -11,7 +11,7 @@ MANPREFIX ?= $(PREFIX)/share/man
 # Compiler
 GCC       ?= gcc
 CFLAGS    ?= -Wall --std=c99
-LDFLAGS   ?= -lncursesw -lcurl -lexpat
+LDFLAGS   ?= -lncursesw -lexpat -lcrypto
 
 # Sources
 PROG      ?= lamechat
diff --git a/util.c b/util.c
index c6568967d4b4751cf61c2b7d733746f27e4decff..29edf581d5fcbd6f2f9aa038fde077e2f8d8660e 100644 (file)
--- a/util.c
+++ b/util.c
@@ -223,7 +223,23 @@ int poll_run(int timeout)
                        continue;
                if (!(poll->cb))
                        continue;
-               poll->cb(poll->fd, poll->data);
+               //debug("poll fd=%d count=%d,%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+                //        poll->fd, count,
+                //        event.events & EPOLLIN      ? " in"      : "",
+                //        event.events & EPOLLPRI     ? " pri"     : "",
+                //        event.events & EPOLLOUT     ? " out"     : "",
+                //        event.events & EPOLLRDNORM  ? " rdnorm"  : "",
+                //        event.events & EPOLLRDBAND  ? " rdband"  : "",
+                //        event.events & EPOLLWRNORM  ? " wrnorm"  : "",
+                //        event.events & EPOLLWRBAND  ? " wrband"  : "",
+                //        event.events & EPOLLMSG     ? " msg"     : "",
+                //        event.events & EPOLLERR     ? " err"     : "",
+                //        event.events & EPOLLHUP     ? " hup"     : "",
+                //        event.events & EPOLLRDHUP   ? " rdhup"   : "",
+                //        event.events & EPOLLWAKEUP  ? " wakeup"  : "",
+                //        event.events & EPOLLONESHOT ? " oneshot" : "",
+                //        event.events & EPOLLET      ? " et"      : "");
+               poll->cb(poll->data, poll->fd);
                return running;
        }
        return running;
diff --git a/util.h b/util.h
index 0e876ea3f4cb58b6a294a13e2b7814f16dafd9dd..ef0851459c3796923bbb4eccb208fbe8a6fc4e32 100644 (file)
--- a/util.h
+++ b/util.h
@@ -26,7 +26,7 @@
 #define new0(type) alloc0(sizeof(type))
 
 /* Types */
-typedef void (*cb_t)(int fd, void *data);
+typedef void (*cb_t)(void *data, int fd);
 
 typedef struct {
        void *data;
diff --git a/xmpp.c b/xmpp.c
index 3639b1cee11dec4d04d10e62e592f2e792e5f2a2..4dbcd170e2d9c45b66c4097827363259b3ee2ebe 100644 (file)
--- a/xmpp.c
+++ b/xmpp.c
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <expat.h>
+
 #include "util.h"
 #include "conf.h"
 
+/* XMPP Constants */
+#define BUF_LEN 1024
+
+/* XMPP types */
+typedef enum {
+       XMPP_DEAD,
+       XMPP_STREAM,
+       XMPP_STARTTLS,
+       XMPP_PROCEED,
+       XMPP_READY,
+} xmpp_state_t;
+
+typedef struct xmpp_server_t {
+       const char  *name;
+       const char  *protocol;
+       int          connect;
+       const char  *host;
+       int          port;
+       const char  *jid;
+       const char  *pass;
+
+       XML_Parser   expat;
+       xmpp_state_t state;
+       poll_t       poll;
+
+       char        in_buf[BUF_LEN];
+       int         in_len;
+       char        out_buf[BUF_LEN];
+       int         out_pos;
+       int         out_len;
+
+       buf_t       buf;
+       int         indent;
+
+       struct xmpp_server_t *next;
+} xmpp_server_t;
+
+typedef struct xmpp_channel_t {
+       const char  *name;
+       const char  *channel;
+       const char  *server;
+       int          join;
+       struct xmpp_channel_t *next;
+} xmpp_channel_t;
+
+/* Local data */
+static xmpp_server_t  *servers;
+static xmpp_channel_t *channels;
+
+/* Local functions */
+static xmpp_server_t *find_server(const char *name, int create)
+{
+       xmpp_server_t *cur = NULL, *last = NULL;
+       for (cur = servers; cur; last = cur, cur = cur->next)
+               if (match(cur->name, name))
+                       break;
+       if (!cur && create) {
+               cur = new0(xmpp_server_t);
+               cur->name = get_name(name);
+               if (last)
+                       last->next = cur;
+               else
+                       servers = cur;
+       }
+       return cur;
+}
+
+static xmpp_channel_t *find_channel(const char *name, int create)
+{
+       xmpp_channel_t *cur = NULL, *last = NULL;
+       for (cur = channels; cur; last = cur, cur = cur->next)
+               if (match(cur->name, name))
+                       break;
+       if (!cur && create) {
+               cur = new0(xmpp_channel_t);
+               cur->name = get_name(name);
+               if (last)
+                       last->next = cur;
+               else
+                       channels = cur;
+       }
+       return cur;
+}
+
+static int send_xml(xmpp_server_t *srv, const char *fmt, ...)
+{
+       int len, sent;
+       va_list ap;
+       if (srv->out_len)
+               return 0;
+
+       va_start(ap, fmt);
+       len = vsnprintf(srv->out_buf, BUF_LEN, fmt, ap);
+       va_end(ap);
+       if (len <= 0)
+               return 0;
+       if (len > BUF_LEN)
+               len = BUF_LEN;
+
+       //debug("xmpp send: [%.*s]", len, srv->out_buf);
+       sent = send(srv->poll.fd, srv->out_buf, len, 0);
+       if (sent == len)
+               return 1;
+
+       srv->out_len = len;
+       srv->out_pos = sent;
+       return 0;
+}
+
+static void on_start(void *_srv, const char *tag, const char **attrs)
+{
+       xmpp_server_t *srv = _srv;
+
+       /* Debug print */
+       debug("%*s<%s>",
+               srv->indent*4, "", tag);
+       for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
+               debug("%*s -- %s=\"%s\"%s",
+                       srv->indent*4+6, "",
+                       attrs[i+0], attrs[i+1],
+                       attrs[i+2] ? "" : ">");
+       }
+       srv->indent++;
+
+       /* Start TLS */
+       if (srv->state == XMPP_STARTTLS)
+               if (match(tag, "starttls"))
+                       srv->state = XMPP_PROCEED;
+}
+
+static void on_end(void *_srv, const char *tag)
+{
+       xmpp_server_t *srv = _srv;
+
+       /* Debug print */
+       srv->indent--;
+}
+
+static void on_data(void *_srv, const char *data, int len)
+{
+}
+
+static void on_poll(void *_srv, int fd)
+{
+       xmpp_server_t *srv = _srv;
+       static char buf[BUF_LEN];
+       int len;
+
+       /* Handle Input */
+       len = recv(srv->poll.fd, buf, sizeof(buf), 0);
+       if ((len < 0) && (errno != EAGAIN))
+               debug("xmpp recv: error, %s -- %s", srv->name, strerror(errno));
+       if (len == 0) {
+               debug("xmpp recv: close, %s", srv->name);
+               srv->state = XMPP_DEAD;
+               poll_del(&srv->poll);
+               return;
+       }
+       if (len > 0) {
+               //debug("xmpp recv: ready, %s -- [%.*s]", srv->name, len, buf);
+               XML_Parse(srv->expat, buf, len, 0);
+       }
+
+       /* Handle Output */
+       if (srv->out_len > 0) {
+               len = send(fd, &srv->out_buf[srv->out_pos],
+                              srv->out_len-srv->out_pos, 0);
+               if (len > 0)
+                       srv->out_pos += len;
+               if (srv->out_pos == srv->out_len) {
+                       srv->out_pos = 0;
+                       srv->out_len = 0;
+               }
+       }
+
+       /* Handle Errors */
+       int err = 0;
+       socklen_t elen = sizeof(err);
+       if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &elen))
+               error("Error getting socket opt");
+       if (err) {
+               debug("disconnect: %s -- %s", srv->name, strerror(err));
+               srv->state = XMPP_DEAD;
+               poll_del(&srv->poll);
+               return;
+       }
+
+       /* State machine */
+       if (srv->state == XMPP_STREAM) {
+               if (send_xml(srv,
+                    "<?xml version='1.0'?>"
+                    "<stream:stream"
+                    " from='%s'"
+                    " to='%s'"
+                    " version='1.0'"
+                    " xml:lang='en'"
+                    " xmlns='jabber:client'"
+                    " xmlns:stream='http://etherx.jabber.org/streams'>",
+                    srv->jid, srv->host))
+                       srv->state = XMPP_STARTTLS;
+       }
+       if (srv->state == XMPP_PROCEED) {
+               if (send_xml(srv, "<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
+                       debug("ready");
+                       srv->state = XMPP_READY;
+               }
+       }
+
+       /* Enable output poll */
+       if (srv->out_len) {
+               poll_ctl(&srv->poll, 1, 1, 1);
+       } else {
+               poll_ctl(&srv->poll, 1, 0, 1);
+       }
+}
+
+static void xmpp_connect(xmpp_server_t *srv)
+{
+       int sock, flags;
+       struct addrinfo *addrs = NULL;
+       struct addrinfo hints = {};
+       char service[16];
+
+       snprintf(service, sizeof(service), "%d", srv->port);
+       hints.ai_family   = AF_INET;
+       hints.ai_socktype = SOCK_STREAM;
+
+       /* Setup address */
+       if (getaddrinfo(srv->host, service, &hints, &addrs))
+               error("Error getting xmpp address info");
+
+       if ((sock = socket(addrs->ai_family,
+                          addrs->ai_socktype,
+                          addrs->ai_protocol)) < 0)
+               error("Error opening xmpp socket");
+
+       if ((flags = fcntl(sock, F_GETFL, 0)) < 0)
+               error("Error getting xmpp socket flags");
+
+       if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0)
+               error("Error setting xmpp socket non-blocking");
+
+       if (connect(sock, addrs->ai_addr, addrs->ai_addrlen) < 0)
+               if (errno != EINPROGRESS)
+                       error("Error connecting socket");
+
+       freeaddrinfo(addrs);
+
+       /* Setup Expat */
+       if (!(srv->expat = XML_ParserCreate(NULL)))
+               error("Error creating XML parser");
+       XML_SetUserData(srv->expat, srv);
+       XML_SetStartElementHandler(srv->expat, on_start);
+       XML_SetEndElementHandler(srv->expat, on_end);
+       XML_SetCharacterDataHandler(srv->expat, on_data);
+
+       /* Setup server */
+       srv->state = XMPP_STREAM;
+       poll_add(&srv->poll, sock, on_poll, srv);
+}
+
 /* XMPP functions */
 void xmpp_init(void)
 {
+       for (xmpp_server_t *cur = servers; cur; cur = cur->next) {
+               if (!match(cur->protocol, "xmpp"))
+                       continue;
+               if (!cur->port)
+                       cur->port = 5222;
+               if (!cur->jid)
+                       error("jid is required");
+               if (cur->connect)
+                       xmpp_connect(cur);
+       }
 }
 
 void xmpp_config(const char *group, const char *name, const char *key, const char *value)
 {
+       xmpp_server_t  *srv;
+       xmpp_channel_t *chan;
+
+       if (match(group, "server")) {
+               srv = find_server(name, 1);
+               if (match(key, "protocol") &&
+                   match(value, "xmpp"))
+                       srv->protocol = get_string(value);
+               if (match(srv->protocol, "xmpp")) {
+                       if (match(key, "connect"))
+                               srv->connect = get_bool(value);
+                       else if (match(key, "host"))
+                               srv->host = get_string(value);
+                       else if (match(key, "port"))
+                               srv->port = get_number(value);
+                       else if (match(key, "jid"))
+                               srv->jid = get_string(value);
+                       else if (match(key, "pass"))
+                               srv->pass = get_string(value);
+               }
+       } else if (match(group, "channel")) {
+               chan = find_channel(name, 1);
+               if (match(key, "server") &&
+                   find_server(value, 0))
+                       chan->server = get_string(value);
+               if (chan->server) {
+                       if (match(key, "channel"))
+                               chan->channel = get_string(value);
+                       else if (match(key, "join"))
+                               chan->join = get_bool(value);
+               }
+       }
 }
 
 void xmpp_send(const char *channel, const char *msg)
@@ -34,4 +348,3 @@ void xmpp_send(const char *channel, const char *msg)
 void xmpp_exit(void)
 {
 }
-