} 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;
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];
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)
/* 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 */
* 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)
void xmpp_exit(void)
{
}
-