]> Pileus Git - ~andy/lamechat/commitdiff
Refactor chat.
authorAndy Spencer <andy753421@gmail.com>
Sun, 1 Oct 2017 05:23:47 +0000 (05:23 +0000)
committerAndy Spencer <andy753421@gmail.com>
Sun, 1 Oct 2017 19:02:57 +0000 (19:02 +0000)
chat.c
chat.h
conf.c
conf.h
irc.c
view.c
xmpp.c

diff --git a/chat.c b/chat.c
index e35271a9b6470daea18c1a8ebf912c6b613443eb..4aa4ce63abd84d044949ecae6ddd57f0307b49a9 100644 (file)
--- a/chat.c
+++ b/chat.c
 #include "view.h"
 
 /* Global data */
-log_t *chat_log;
-int    chat_len;
+server_t  *servers;
+channel_t *channels;
+message_t *messages;
+int        history;
 
 /* Local data */
-static buf_t log_buf;
+static buf_t srv_buf;
+static buf_t chan_buf;
+static buf_t msg_buf;
 
-/* Local functions */
+static const char *proto_map[] = {
+       [IRC]  "irc",
+       [XMPP] "xmpp",
+};
 
 /* View init */
 void chat_init(void)
 {
-       chat_log = (log_t*)log_buf.data;
-       chat_len = 0;
+       servers  = (server_t*)srv_buf.data;
+       channels = (channel_t*)chan_buf.data;
+       messages = (message_t*)msg_buf.data;
+       history  = 0;
 
        irc_init();
        xmpp_init();
@@ -47,8 +56,61 @@ void chat_init(void)
 
 void chat_config(const char *group, const char *name, const char *key, const char *value)
 {
-       irc_config(group, name, key, value);
-       xmpp_config(group, name, key, value);
+       int        protocol = -1;
+       server_t  *server   = NULL;
+       channel_t *channel  = NULL;
+
+       if (match(group, "server")) {
+               server = find_server(name);
+               if (match(key, "")) {
+                       get_name(name);
+               }
+               else if (match(key, "protocol")) {
+                       protocol = get_enum(value, proto_map, N_ELEMENTS(proto_map));
+                       server = add_server(name, protocol);
+               }
+               else if (server) {
+                       protocol = server->protocol;
+               }
+               else {
+                       error("No server found for [%s]", name);
+               }
+       }
+
+       if (match(group, "channel")) {
+               channel = find_channel(name);
+               if (match(key, "")) {
+                       get_name(name);
+               }
+               else if (match(key, "server")) {
+                       server = find_server(get_string(value));
+                       if (!server)
+                               error("server '%s' does not exist", name);
+                       channel = add_channel(name, server);
+                       protocol = server->protocol;
+                       server = NULL;
+               }
+               else if (channel) {
+                       protocol = channel->server->protocol;
+               }
+               else {
+                       error("No channel found for [%s]", name);
+               }
+       }
+
+       debug("chat_config: [%s-%s \"%s\"] %s = %s",
+                       protocol>=0 ? proto_map[protocol] : "none",
+                       group, name, key, value,
+                       server, channel);
+
+       switch (protocol) {
+               case IRC:
+                       irc_config(server, channel, group, name, key, value);
+                       break;
+               case XMPP:
+                       xmpp_config(server, channel, group, name, key, value);
+                       break;
+       }
 }
 
 void chat_notice(const char *channel, const char *from, const char *fmt, ...)
@@ -63,35 +125,38 @@ void chat_notice(const char *channel, const char *from, const char *fmt, ...)
        chat_recv(channel, from, buf);
 }
 
-void chat_recv(const char *channel, const char *from, const char *msg)
+void chat_recv(const char *channel, const char *from, const char *text)
 {
-       append(&log_buf, NULL, sizeof(log_t));
-       chat_log = (log_t*)log_buf.data;
+       append(&msg_buf, NULL, sizeof(message_t));
+       messages = (message_t*)msg_buf.data;
 
-       log_t *log = &chat_log[chat_len];
-       log->when = time(NULL);
-       log->from = strcopy(from);
-       log->channel = strcopy(channel);
-       log->msg  = strcopy(msg);
+       message_t *msg = &messages[history];
+       msg->channel = find_channel(channel);
+       msg->when = time(NULL);
+       msg->from = strcopy(from);
+       msg->text = strcopy(text);
 
-       chat_len++;
+       history++;
        view_draw();
 }
 
-void chat_send(const char *channel, const char *msg)
+void chat_send(const char *channel_name, const char *text)
 {
-       append(&log_buf, NULL, sizeof(log_t));
-       chat_log = (log_t*)log_buf.data;
-
-       log_t *log = &chat_log[chat_len];
-       log->when = time(NULL);
-       log->from = "andy";
-       log->channel = strcopy(channel);
-       log->msg  = strcopy(msg);
-
-       chat_len++;
-       irc_send(channel, msg);
-       xmpp_send(channel, msg);
+       channel_t *channel = find_channel(channel_name);
+
+       append(&msg_buf, NULL, sizeof(message_t));
+       messages = (message_t*)msg_buf.data;
+
+       message_t *msg = &messages[history];
+       msg->channel = channel;
+       msg->when = time(NULL);
+       msg->from = strcopy("andy");
+       msg->text = strcopy(text);
+       history++;
+
+       irc_send(msg);
+       xmpp_send(msg);
+
        view_draw();
 }
 
@@ -100,7 +165,62 @@ void chat_exit(void)
        irc_exit();
        xmpp_exit();
 
-       for (int i = 0; i < chat_len; i++)
-               free((void*)chat_log[i].msg);
-       release(&log_buf);
+       for (int i = 0; i < history; i++)
+               free((void*)messages[i].text);
+       release(&msg_buf);
 }
+
+/* Server and channel function */
+server_t *add_server(const char *name, protocol_t protocol)
+{
+       server_t *server = NULL;
+       debug("adding %s server \"%s\"", proto_map[protocol], name);
+       switch (protocol) {
+               case IRC:
+                       server = irc_server();
+                       break;
+               case XMPP:
+                       server = xmpp_server();
+                       break;
+       }
+       server->next = servers;
+       server->name = strcopy(name);
+       server->protocol = protocol;
+       return (servers = server);
+}
+
+channel_t *add_channel(const char *name, server_t *server)
+{
+       channel_t *channel = NULL;
+       debug("adding %s channel \"%s\"", server->name, name);
+       switch (server->protocol) {
+               case IRC:
+                       channel = irc_channel();
+                       break;
+               case XMPP:
+                       channel = xmpp_channel();
+                       break;
+       }
+       channel->next = channels;
+       channel->name = strcopy(name);
+       channel->server = server;
+       return (channels = channel);
+}
+
+server_t *find_server(const char *name)
+{
+       for (server_t *cur = servers; cur; cur = cur->next)
+               if (match(cur->name, name))
+                       return cur;
+       return NULL;
+}
+
+channel_t *find_channel(const char *name)
+{
+       for (channel_t *cur = channels; cur; cur = cur->next) {
+               if (match(cur->name, name))
+                       return cur;
+       }
+       return NULL;
+}
+
diff --git a/chat.h b/chat.h
index 746999e4f26f61dde6ddcb7f9dcd9f52b821b855..fd1bb18bcec11020c1ee51f8773df718eb164ac2 100644 (file)
--- a/chat.h
+++ b/chat.h
 /* Message Types */
 typedef unsigned long long stamp_t;
 
-typedef struct {
-       stamp_t  when;
-       char    *channel;
-       char    *from;
-       char    *msg;
-} log_t;
+typedef struct server_t server_t;
+typedef struct channel_t channel_t;
 
-typedef struct {
-       char    *name;
+typedef enum {
+       IRC,
+       XMPP,
+} protocol_t;
+
+typedef struct server_t {
+       protocol_t  protocol;
+       char       *name;
+       server_t   *next;
+} server_t;
+
+typedef struct channel_t {
+       server_t   *server;
+       char       *name;
+       channel_t  *next;
 } channel_t;
 
+typedef struct {
+       channel_t  *channel;
+       stamp_t     when;
+       char       *from;
+       char       *text;
+} message_t;
+
 /* Global Data */
-extern log_t *chat_log;
-extern int    chat_len;
+extern server_t  *servers;
+extern channel_t *channels;
+extern message_t *messages;
+extern int        history;
 
 /* Chat functions */
 void chat_init(void);
-void chat_config(const char *group, const char *name, const char *key, const char *value);
-void chat_notice(const char *channel, const char *from, const char *fmt, ...);
-void chat_recv(const char *channel, const char *from, const char *msg);
-void chat_send(const char *channel, const char *msg);
+void chat_config(const char *group, const char *name,
+                 const char *key, const char *value);
+void chat_notice(const char *channel_name, const char *from, const char *fmt, ...);
+void chat_recv(const char *channel_name, const char *from, const char *msg);
+void chat_send(const char *channel_name, const char *msg);
 void chat_exit(void);
 
+server_t  *add_server(const char *name, protocol_t protocol);
+channel_t *add_channel(const char *name, server_t *server);
+server_t  *find_server(const char *name);
+channel_t *find_channel(const char *name);
+
 /* Protocol functions */
 extern void irc_init(void);
-extern void irc_config(const char *group, const char *name, const char *key, const char *value);
-extern void irc_send(const char *channel, const char *msg);
+extern server_t *irc_server(void);
+extern channel_t *irc_channel(void);
+extern void irc_config(server_t *server, channel_t *channel,
+                       const char *group, const char *name,
+                       const char *key, const char *value);
+extern void irc_send(message_t *msg);
 extern void irc_exit(void);
 
 extern void xmpp_init(void);
-extern void xmpp_config(const char *group, const char *name, const char *key, const char *value);
-extern void xmpp_send(const char *channel, const char *msg);
+extern server_t *xmpp_server(void);
+extern channel_t *xmpp_channel(void);
+extern void xmpp_config(server_t *server, channel_t *channel,
+                        const char *group, const char *name,
+                        const char *key, const char *value);
+extern void xmpp_send(message_t *msg);
 extern void xmpp_exit(void);
diff --git a/conf.c b/conf.c
index 2268e92829acb82617e531e4cdb7b524cc07df2f..7d48ac0696780ca4fd454435cb5384a9bc4d4bbc 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -247,18 +247,21 @@ static void conf_load(const char *path, parser_t parser)
 
                /* Parse dynamic groups */
                if (line->group && line->name) {
+                       //debug("group: [%s \"%s\"]", line->group, line->name);
                        wasfound  = 0;
                        lastgroup = line->group;
                        lastname  = line->name;
                        lastkey   = NULL;
                        parser(line->group, line->name, "", "");
                        if (!wasfound)
-                               error("unknown group: line %d - [%s \"%s\"]",
+                               error("line %d: unknown group [%s \"%s\"]",
                                                lnum, group, name ?: "");
                }
 
                /* Parse static key/value pairs */
                if (group && line->key && line->value) {
+                       //debug("value: [%s \"%s\"] %s = '%s'",
+                       //      group, name, line->key, line->value);
                        wasfound  = 0;
                        lastgroup = group;
                        lastname  = name;
@@ -266,12 +269,10 @@ static void conf_load(const char *path, parser_t parser)
                        parser(group, name?:"", line->key, line->value);
                        if (!wasfound)
                                error("unknown setting: line %d - %s.%s.%s = '%s'\n",
-                                               lnum, group, name?:"", line->key, line->value);
+                                               lnum, group, name?:"",
+                                               line->key, line->value);
                }
 
-               /* debug printout */
-               debug("parse: %s.%s.%s = '%s'", group, name, line->key, line->value);
-
                /* save line formatting for the next write */
                if (prev == NULL)
                        settings = line;
diff --git a/conf.h b/conf.h
index 00872e40fa7c710c52fe8a3621905378a2f00ac5..5b792894e387dbb377d458cfe6046cdfb74d2af4 100644 (file)
--- a/conf.h
+++ b/conf.h
@@ -25,6 +25,7 @@ int get_bool(const char *value);
 int get_number(const char *value);
 char *get_string(const char *value);
 char *get_name(const char *name);
+const char *get_const(const char *name);
 
 /* Setters */
 void set_enum(const char *group, const char *name,
diff --git a/irc.c b/irc.c
index c06a785ce5897f29805cf8636b52fc11c962bec9..6deb88beaa4cc3e9f7cdbcc0fc8e0c3cd4c4dc56 100644 (file)
--- a/irc.c
+++ b/irc.c
@@ -54,9 +54,9 @@ typedef enum {
        IRC_READY,
 } irc_state_t;
 
-typedef struct irc_server_t {
-       const char *name;
-       const char *protocol;
+typedef struct {
+       server_t    server;
+
        int         connect;
        const char *host;
        int         port;
@@ -69,57 +69,16 @@ typedef struct irc_server_t {
        irc_state_t state;
        char        line[IRC_LINE];
        int         pos;
-
-       struct irc_server_t *next;
 } irc_server_t;
 
-typedef struct irc_channel_t {
-       const char *name;
-       const char *channel;
-       const char *server;
+typedef struct {
+       channel_t   channel;
+
+       const char *dest;
        int         join;
-       struct irc_channel_t *next;
 } irc_channel_t;
 
-/* Local data */
-static irc_server_t  *servers;
-static irc_channel_t *channels;
-
 /* Local functions */
-static irc_server_t *find_server(const char *name, int create)
-{
-       irc_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(irc_server_t);
-               cur->name = get_name(name);
-               if (last)
-                       last->next = cur;
-               else
-                       servers = cur;
-       }
-       return cur;
-}
-
-static irc_channel_t *find_channel(const char *name, int create)
-{
-       irc_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(irc_channel_t);
-               cur->name = get_name(name);
-               if (last)
-                       last->next = cur;
-               else
-                       channels = cur;
-       }
-       return cur;
-}
-
 static void recv_line(irc_server_t *srv, const char *line)
 {
        static char src[IRC_LINE]; // (:([^ ]+) +)?   
@@ -321,9 +280,12 @@ static void on_recv(void *_srv, char *buf, int len)
                        srv->state = IRC_RECV_WELCOME;
        }
        if (srv->state == IRC_JOIN) {
-               for (irc_channel_t *chan = channels; chan; chan = chan->next)
-                       if (chan->join)
-                               net_print(&srv->net, "JOIN %s\n", chan->channel);
+               for (channel_t *cur = channels; cur; cur = cur->next) {
+                       irc_channel_t *chan = (irc_channel_t*)cur;
+                       if (cur->server != &srv->server || !chan->join)
+                               continue;
+                       net_print(&srv->net, "JOIN %s\n", chan->dest);
+               }
                srv->state = IRC_READY;
        }
 }
@@ -345,68 +307,76 @@ static void irc_connect(irc_server_t *srv)
 /* IRC functions */
 void irc_init(void)
 {
-       for (irc_server_t *cur = servers; cur; cur = cur->next) {
-               if (!match(cur->protocol, "irc"))
+       for (server_t *cur = servers; cur; cur = cur->next) {
+               if (cur->protocol != IRC)
                        continue;
-               if (!cur->port)
-                       cur->port = cur->tls ? 6697 : 6667;
-               if (!cur->nick)
-                       cur->nick = strcopy(getenv("USER"));
-               if (!cur->nick)
-                       cur->nick = strcopy("lameuser");
-               if (cur->connect)
-                       irc_connect(cur);
+
+               irc_server_t *srv = (irc_server_t*)cur;
+               if (!srv->port)
+                       srv->port = srv->tls ? 6697 : 6667;
+               if (!srv->nick)
+                       srv->nick = strcopy(getenv("USER"));
+               if (!srv->nick)
+                       srv->nick = strcopy("lameuser");
+               if (srv->connect)
+                       irc_connect(srv);
+       }
+       for (channel_t *cur = channels; cur; cur = cur->next) {
+               if (cur->server->protocol != IRC)
+                       continue;
+
+               irc_channel_t *chan = (irc_channel_t*)cur;
+               if (!chan->dest)
+                       chan->dest = strcopy(cur->name);
        }
 }
 
-void irc_config(const char *group, const char *name, const char *key, const char *value)
+server_t *irc_server(void)
 {
-       irc_server_t  *srv;
-       irc_channel_t *chan;
-
-       if (match(group, "server")) {
-               srv = find_server(name, 1);
-               if (match(key, "protocol") &&
-                   match(value, "irc"))
-                       srv->protocol = get_string(value);
-               if (match(srv->protocol, "irc")) {
-                       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, "tls"))
-                               srv->tls = get_bool(value);
-                       else if (match(key, "nick"))
-                               srv->nick = get_string(value);
-                       else if (match(key, "auth"))
-                               srv->auth = 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);
-               }
+       return new0(irc_server_t);
+}
+
+channel_t *irc_channel(void)
+{
+       return new0(irc_channel_t);
+}
+
+void irc_config(server_t *server, channel_t *channel,
+                const char *group, const char *name,
+                const char *key, const char *value)
+{
+       irc_server_t  *srv  = (irc_server_t*)server;
+       irc_channel_t *chan = (irc_channel_t*)channel;
+
+       if (server) {
+               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, "tls"))
+                       srv->tls = get_bool(value);
+               else if (match(key, "nick"))
+                       srv->nick = get_string(value);
+               else if (match(key, "auth"))
+                       srv->auth = get_string(value);
+               else if (match(key, "pass"))
+                       srv->pass = get_string(value);
+       }
+       if (channel) {
+               if (match(key, "dest"))
+                       chan->dest = get_string(value);
+               else if (match(key, "join"))
+                       chan->join = get_bool(value);
        }
 }
 
-void irc_send(const char *channel, const char *msg)
+void irc_send(message_t *msg)
 {
-       irc_channel_t *chan = find_channel(channel, 0);
-       if (!chan)
-               return;
-       irc_server_t *srv = find_server(chan->server, 0);
-       if (!srv)
-               return;
+       irc_channel_t *chan = (irc_channel_t*)msg->channel;
+       irc_server_t  *srv  = (irc_server_t*)msg->channel->server;
+
        net_print(&srv->net, "PRIVMSG %s :%s\n", chan->channel, msg);
 }
 
diff --git a/view.c b/view.c
index e108bbfe790366f8b8736e8b23bb6d7803839a78..d0c0727cacf4262b2400fb7f2e61bf8433fd23f1 100644 (file)
--- a/view.c
+++ b/view.c
@@ -83,25 +83,25 @@ static void print_word(char **msg, int *row, int *col, int indent, int print)
        *msg = end;
 }
 
-static void print_msg(log_t *log, int *row, int print)
+static void print_msg(message_t *msg, int *row, int print)
 {
        static char buf[512];
        char *hdr = buf;
-       char *msg = log->msg;
+       char *txt = msg->text;
        int col = 0, indent = 0;
-       time_t timep = log->when;
+       time_t timep = msg->when;
        struct tm *tm = localtime(&timep);
 
-       if (log->channel && log->from)
+       if (msg->channel && msg->from)
                snprintf(buf, sizeof(buf), "%02d:%02d [%s] %s: ",
                                tm->tm_hour, tm->tm_min,
-                               log->channel, log->from);
-       else if (log->channel)
+                               msg->channel->name, msg->from);
+       else if (msg->channel)
                snprintf(buf, sizeof(buf), "%02d:%02d [%s] *** ",
-                               tm->tm_hour, tm->tm_min, log->channel);
-       else if (log->from)
+                               tm->tm_hour, tm->tm_min, msg->channel->name);
+       else if (msg->from)
                snprintf(buf, sizeof(buf), "%02d:%02d *** %s: ",
-                               tm->tm_hour, tm->tm_min, log->from);
+                               tm->tm_hour, tm->tm_min, msg->from);
        else
                snprintf(buf, sizeof(buf), "%02d:%02d *** ",
                                tm->tm_hour, tm->tm_min);
@@ -110,8 +110,8 @@ static void print_msg(log_t *log, int *row, int print)
        while (*hdr)
                print_word(&hdr, row, &col, indent, print);
        indent = col;
-       while (*msg)
-               print_word(&msg, row, &col, indent, print);
+       while (*txt)
+               print_word(&txt, row, &col, indent, print);
 
        (*row)++;
 }
@@ -130,7 +130,7 @@ void draw_chat(void)
 {
        int space = LINES-3;
        int row   = 0;
-       int log   = chat_len-1;
+       int log   = history-1;
 
        /* Clear window */
        for (int i = 1; i < LINES-2; i++) {
@@ -140,15 +140,15 @@ void draw_chat(void)
 
        /* Compute lines */
        while (row < space && log >= 0)
-               print_msg(&chat_log[log--], &row, 0);
+               print_msg(&messages[log--], &row, 0);
 
        /* Compute skip lines */
        row = 1 + (space - row);
        log = log + 1;
 
        /* Print lines */
-       while (row <= space && log < chat_len)
-               print_msg(&chat_log[log++], &row, 1);
+       while (row <= space && log < history)
+               print_msg(&messages[log++], &row, 1);
 }
 
 void draw_status(void)
diff --git a/xmpp.c b/xmpp.c
index 75a65ff0e84623124d21a2be5520b90fcc2908dd..c15522c1e9ea7aed1e9908dd9d3f7bb615a8515f 100644 (file)
--- a/xmpp.c
+++ b/xmpp.c
@@ -49,9 +49,9 @@ typedef enum {
        XMPP_READY,
 } xmpp_state_t;
 
-typedef struct xmpp_server_t {
-       const char  *name;
-       const char  *protocol;
+typedef struct {
+       server_t     server;
+
        int          connect;
        const char  *host;
        int          port;
@@ -68,22 +68,15 @@ typedef struct xmpp_server_t {
        int          indent;
 
        int          in_error;
-
-       struct xmpp_server_t *next;
 } xmpp_server_t;
 
-typedef struct xmpp_channel_t {
-       const char  *name;
-       const char  *channel;
-       const char  *server;
+typedef struct {
+       channel_t    channel;
+
+       const char  *dest;
        int          join;
-       struct xmpp_channel_t *next;
 } xmpp_channel_t;
 
-/* Local data */
-static xmpp_server_t  *servers;
-static xmpp_channel_t *channels;
-
 /* Local functions */
 const char *find_attr(const char **attrs, const char *name)
 {
@@ -93,39 +86,6 @@ const char *find_attr(const char **attrs, const char *name)
        return NULL;
 }
 
-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 void on_start(void *_srv, const char *tag, const char **attrs)
 {
@@ -335,8 +295,9 @@ static void on_recv(void *_srv, char *buf, int len)
                }
        }
        if (srv->state == XMPP_PRESENCE) {
-               for (xmpp_channel_t *chan = channels; chan; chan = chan->next) {
-                       if (!chan->join)
+               for (channel_t *cur = channels; cur; cur = cur->next) {
+                       xmpp_channel_t *chan = (xmpp_channel_t*)cur;
+                       if (cur->server != &srv->server || !chan->join)
                                continue;
                        net_print(&srv->net,
                                "<presence id='join' from='%s' to='%s@%s/%s'>"
@@ -370,95 +331,102 @@ static void xmpp_connect(xmpp_server_t *srv)
 /* XMPP functions */
 void xmpp_init(void)
 {
-       for (xmpp_server_t *cur = servers; cur; cur = cur->next) {
-               if (!match(cur->protocol, "xmpp"))
+       for (server_t *cur = servers; cur; cur = cur->next) {
+               if (cur->protocol != XMPP)
                        continue;
-               if (!cur->port)
-                       cur->port = 5222;
-               if (!cur->jid)
+
+               xmpp_server_t *srv = (xmpp_server_t*)cur;
+               if (!srv->port)
+                       srv->port = 5222;
+               if (!srv->jid)
                        error("jid is required");
-               if (cur->connect)
-                       xmpp_connect(cur);
+               if (srv->connect)
+                       xmpp_connect(srv);
+       }
+       for (channel_t *cur = channels; cur; cur = cur->next) {
+               if (cur->server->protocol != XMPP)
+                       continue;
+
+               xmpp_channel_t *chan = (xmpp_channel_t*)cur;
+               if (!chan->dest)
+                       chan->dest = strcopy(cur->name);
        }
 }
 
-void xmpp_config(const char *group, const char *name, const char *key, const char *value)
+server_t *xmpp_server(void)
 {
-       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, "muc"))
-                               srv->muc = get_string(value);
-                       else if (match(key, "nick"))
-                               srv->nick = get_string(value);
-                       else if (match(key, "jid"))
-                               srv->jid = get_string(value);
-                       else if (match(key, "user"))
-                               srv->user = 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);
-               }
+       return new0(xmpp_server_t);
+}
+
+channel_t *xmpp_channel(void)
+{
+       return new0(xmpp_channel_t);
+}
+
+void xmpp_config(server_t *server, channel_t *channel,
+                 const char *group, const char *name,
+                 const char *key, const char *value)
+{
+       xmpp_server_t  *srv  = (xmpp_server_t*)server;
+       xmpp_channel_t *chan = (xmpp_channel_t*)channel;
+
+       if (server) {
+               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, "muc"))
+                       srv->muc = get_string(value);
+               else if (match(key, "nick"))
+                       srv->nick = get_string(value);
+               else if (match(key, "jid"))
+                       srv->jid = get_string(value);
+               else if (match(key, "user"))
+                       srv->user = get_string(value);
+               else if (match(key, "pass"))
+                       srv->pass = get_string(value);
+       }
+       if (channel) {
+               if (match(key, "dest"))
+                       chan->dest = get_string(value);
+               else if (match(key, "join"))
+                       chan->join = get_bool(value);
        }
 }
 
-void xmpp_send(const char *channel, const char *msg)
+void xmpp_send(message_t *msg)
 {
+       xmpp_channel_t *chan = (xmpp_channel_t*)msg->channel;
+       xmpp_server_t  *srv  = (xmpp_server_t*)msg->channel->server;
+       const char *txt = msg->text;
        const char *arg;
 
-       xmpp_channel_t *chan = find_channel(channel, 0);
-       if (!chan)
-               return;
-       xmpp_server_t *srv = find_server(chan->server, 0);
-       if (!srv || !srv->protocol)
-               return;
-
        /* Handle commands */
-       if (msg[0] == '/') {
-               if (prefix(msg, "/items", &arg)) {
+       if (txt[0] == '/') {
+               if (prefix(txt, "/items", &arg)) {
                        net_print(&srv->net,
                                "<iq id='items' type='get' from='%s' to='%s'>"
                                "<query xmlns='http://jabber.org/protocol/disco#items' />"
                                "</iq>",
                                srv->jid, arg ?: srv->host);
                }
-               else if (prefix(msg, "/info", &arg)) {
+               else if (prefix(txt, "/info", &arg)) {
                        net_print(&srv->net,
                                "<iq id='info' type='get' from='%s' to='%s'>"
                                "<query xmlns='http://jabber.org/protocol/disco#info' />"
                                "</iq>",
                                srv->jid, arg ?: srv->host);
                }
-               else if (prefix(msg, "/list", &arg)) {
+               else if (prefix(txt, "/list", &arg)) {
                        net_print(&srv->net,
                                "<iq id='list' type='get' from='%s' to='%s'>"
                                "<query xmlns='http://jabber.org/protocol/disco#items' />"
                                "</iq>",
                                srv->jid, arg ?: srv->muc);
                }
-               else if (prefix(msg, "/join", &arg)) {
+               else if (prefix(txt, "/join", &arg)) {
                        if (!arg) {
                                chat_notice(NULL, NULL, "usage: /join <channel>");
                                return;
@@ -470,15 +438,15 @@ void xmpp_send(const char *channel, const char *msg)
                                srv->jid, arg, srv->muc, srv->nick);
                }
                else {
-                       debug("unknown: [%s]", msg);
+                       debug("unknown: [%s]", txt);
                        chat_notice(NULL, NULL,
-                               "unknown command %s", msg);
+                               "unknown command %s", txt);
                }
        } else {
-               debug("message: [%s]", msg);
+               debug("message: [%s]", txt);
                net_print(&srv->net,
                        "<message to='%s'><body>%s</body></message>",
-                       "andy@pileus.org", msg);
+                       chan->dest, txt);
        }
 }