} irc_command_t;
/* Local functions */
-static void srv_notice(irc_server_t *srv, const char *from, const char *fmt, ...)
+static void srv_notice(irc_server_t *srv, const char *fmt, ...)
{
static char buf[1024];
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- chat_recv(&srv->system.channel, from, buf);
+ chat_recv(&srv->system.channel, NULL, buf);
}
-static void chan_notice(irc_channel_t *chan, const char *from, const char *fmt, ...)
+static void chan_notice(irc_channel_t *chan, const char *fmt, ...)
{
static char buf[1024];
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- chat_recv(&chan->channel, from, buf);
+ chat_recv(&chan->channel, NULL, buf);
}
static irc_channel_t *find_dest(irc_server_t *srv, const char *dest, int create)
static void join_channel(irc_server_t *srv, irc_channel_t *chan)
{
- chan_notice(chan, NULL, "Joining Channel: %s", chan->channel.name);
+ chan_notice(chan, "Joining Channel: %s", chan->channel.name);
net_print(&srv->net, "JOIN %s\n", chan->dest);
net_print(&srv->net, "TOPIC %s\n", chan->dest);
net_print(&srv->net, "WHO %s\n", chan->dest);
static void part_channel(irc_server_t *srv, irc_channel_t *chan)
{
- chan_notice(chan, NULL, "Leaving Channel: %s", chan->channel.name);
+ chan_notice(chan, "Leaving Channel: %s", chan->channel.name);
net_print(&srv->net, "PART %s\n", chan->dest);
}
srv->net.err = on_err;
srv->net.data = srv;
- srv_notice(srv, NULL, "Joining Server: %s", srv->server.name);
+ srv_notice(srv, "Joining Server: %s", srv->server.name);
net_open(&srv->net, srv->host, srv->port);
if (srv->tls)
}
if (srv->state == IRC_RECV_TLS) {
if (match(cmd, "CAP") && match(arg, "ACK")) {
- srv_notice(srv, from, "Start TLS proceeding");
+ srv_notice(srv, "Start TLS proceeding");
srv->state = IRC_SEND_STARTTLS;
}
if (match(cmd, "CAP") && match(arg, "NAK")) {
- srv_notice(srv, from, "Start TLS unsupported");
+ srv_notice(srv, "Start TLS unsupported");
srv->state = IRC_SEND_END;
}
}
}
if (srv->state == IRC_RECV_SASL) {
if (match(cmd, "CAP") && match(arg, "ACK")) {
- srv_notice(srv, from, "SASL auth proceeding");
+ srv_notice(srv, "SASL auth proceeding");
srv->state = IRC_SEND_PLAIN;
}
if (match(cmd, "CAP") && match(arg, "NAK")) {
- srv_notice(srv, from, "SASL auth unsupported");
+ srv_notice(srv, "SASL auth unsupported");
srv->state = IRC_SEND_END;
}
}
}
if (srv->state == IRC_RECV_STATUS) {
if (match(cmd, "903")) {
- srv_notice(srv, from, "SASL auth succeeded");
+ srv_notice(srv, "SASL auth succeeded");
srv->state = IRC_SEND_END;
}
if (match(cmd, "904") ||
match(cmd, "905") ||
match(cmd, "906") ||
match(cmd, "907")) {
- srv_notice(srv, from, "SASL auth failed");
+ srv_notice(srv, "SASL auth failed");
srv->state = IRC_DEAD;
}
}
}
if (match(cmd, "TOPIC")) {
chan = find_dest(srv, arg, 1);
- chan_notice(chan, NULL, "Topic changed to %s", msg);
+ chan_notice(chan, "Topic changed to %s", msg);
}
if (match(cmd, "331") || match(cmd, "322")) {
chan = find_dest(srv, arg, 1);
- chan_notice(chan, NULL, "Topic: %s", msg);
+ chan_notice(chan, "Topic: %s", msg);
}
if (match(cmd, "353") && prefix(arg, "@", &arg)) {
chan = find_dest(srv, arg, 1);
- chan_notice(chan, NULL, "Members: %s", msg);
+ chan_notice(chan, "Members: %s", msg);
}
if (match(cmd, "PRIVMSG") && dst[0] == '#') {
chan = find_dest(srv, dst, 1);
match(cmd, "372") ||
match(cmd, "375") ||
match(cmd, "376"))
- srv_notice(srv, from, "%s", msg);
+ srv_notice(srv, "%s", msg);
}
/* IRC functions */
}
}
-void irc_send(message_t *msg)
+void irc_send(channel_t *channel, const char *text)
{
- irc_channel_t *chan = (irc_channel_t*)msg->channel;
- irc_server_t *srv = (irc_server_t*)msg->channel->server;
- const char *txt = msg->text;
+ irc_channel_t *chan = (irc_channel_t*)channel;
+ irc_server_t *srv = (irc_server_t*)channel->server;
const char *arg;
- if (txt[0] == '/') {
- if (prefix(txt, "/join", &arg)) {
+ if (text[0] == '/') {
+ if (prefix(text, "/join", &arg)) {
if (arg)
chan = find_dest(srv, arg, 1);
if (chan)
join_channel(srv, chan);
}
- else if (prefix(txt, "/part", &arg)) {
+ else if (prefix(text, "/part", &arg)) {
if (arg)
chan = find_dest(srv, arg, 0);
if (chan)
part_channel(srv, chan);
}
+ else if (prefix(text, "/query", &arg)) {
+ if (!arg) {
+ chan_notice(chan, "usage: /query <user>");
+ return;
+ }
+ chan = find_dest(srv, arg, 0);
+ chan_notice(chan, "User: %s", arg);
+ }
else {
- debug("unknown: [%s]", txt);
- chan_notice(chan, "unknown command %s", txt);
+ chan_notice(chan, "Unknown command %s", text);
}
} else {
if (chan == &srv->system) {
- strset(&msg->text, "Cannot send to server");
+ chan_notice(chan, "Cannot send to server");
}
else if (!chan->dest) {
- strset(&msg->text, "No destination for message");
+ chan_notice(chan, "No destination for message");
}
else {
- net_print(&srv->net, "PRIVMSG %s :%s\n", chan->dest, msg->text);
- msg->from = strcopy(srv->nick);
+ net_print(&srv->net, "PRIVMSG %s :%s\n", chan->dest, text);
+ chat_recv(channel, srv->nick, text);
}
}
}
XMPP_RECV_JID,
XMPP_SEND_PRESENCE,
XMPP_READY,
+ XMPP_IN_IQ,
+ XMPP_IN_MESSAGE,
+ XMPP_IN_PRESENCE,
} xmpp_state_t;
typedef struct {
- channel_t channel;
- char dest[JID_LEN];
+ channel_t channel;
+ char dest[JID_LEN];
- const char *room;
- int join;
+ const char *room;
+ int join;
} xmpp_channel_t;
typedef struct {
- server_t server;
- xmpp_channel_t system;
-
- int connect;
- const char *host;
- int port;
- const char *muc;
- const char *nick;
- const char *jid;
- const char *user;
- const char *pass;
-
- net_t net;
- XML_Parser expat;
- xmpp_state_t state;
- buf_t buf;
- int indent;
-
- int in_error;
-
- char msg_from[JID_LEN];
- char msg_usr[JID_LEN];
- char msg_srv[JID_LEN];
- char msg_res[JID_LEN];
+ server_t server;
+ xmpp_channel_t system;
+
+ int connect;
+ const char *host;
+ int port;
+ const char *muc;
+ const char *nick;
+ const char *jid;
+ const char *user;
+ const char *pass;
+
+ net_t net;
+ XML_Parser expat;
+ xmpp_state_t state;
+ buf_t buf;
+ int indent;
+
+ int in_error;
+
+ char msg_jid[JID_LEN];
+ char msg_usr[JID_LEN];
+ char msg_srv[JID_LEN];
+ char msg_res[JID_LEN];
+ char *msg_from;
+ xmpp_channel_t *msg_chan;
} xmpp_server_t;
/* Helper functions */
}
}
- debug("JID: '%s' usr=[%s] srv=[%s] res=[%s]",
- jid, usr, srv, res);
+ //debug("JID: '%s' usr=[%s] srv=[%s] res=[%s]",
+ // jid, usr, srv, res);
}
static xmpp_channel_t *find_dest(xmpp_server_t *srv,
jid_srv[0] ? jid_srv :
is_muc ? srv->muc : srv->host);
+ /* Server channels */
+ if (match(jid, srv->host))
+ return &srv->system;
+
/* Find existing channels */
for (channel_t *cur = channels; cur; cur = cur->next) {
if (cur->server != &srv->server)
const char *start, const char **attrs,
const char *end, const char *data)
{
+ xmpp_channel_t *chan = NULL;
+
/* Debug print */
if (data)
debug("%*s \"%s\"", srv->indent*4, "", data);
srv->state = XMPP_READY;
}
- /* Info queries */
+ /* Start message */
if (srv->state == XMPP_READY) {
- if (match(start, "item"))
+ srv->state = match(start, "iq") ? XMPP_IN_IQ :
+ match(start, "message") ? XMPP_IN_MESSAGE :
+ match(start, "presence") ? XMPP_IN_PRESENCE :
+ XMPP_READY;
+ if (srv->state != XMPP_READY) {
+ strncpy(srv->msg_jid, find_attr(attrs, "from"), JID_LEN);
+ split_jid(srv->msg_jid, srv->msg_usr,
+ srv->msg_srv, srv->msg_res);
+
+ if (match(srv->msg_srv, srv->muc)) {
+ srv->msg_from = srv->msg_res[0] ? srv->msg_res : NULL;
+ srv->msg_chan = find_dest(srv, srv->msg_jid, 1);
+ } else {
+ srv->msg_from = srv->msg_usr[0] ? srv->msg_usr : NULL;
+ srv->msg_chan = find_dest(srv, srv->msg_jid, 0);
+ }
+
+ debug("xmpp: %s -- jid=[%s] from=[%s] chan=[%s]",
+ start,
+ srv->msg_jid, srv->msg_from,
+ srv->msg_chan->channel.name);
+ }
+ }
+ if (srv->state == XMPP_IN_IQ ||
+ srv->state == XMPP_IN_MESSAGE ||
+ srv->state == XMPP_IN_PRESENCE) {
+ if (srv->msg_chan && srv->msg_chan != &srv->system)
+ chan = srv->msg_chan;
+ }
+
+ /* Info/Queries */
+ if (srv->state == XMPP_IN_IQ) {
+ if (match(start, "item") && chan) {
+ static char res[JID_LEN];
+ split_jid(find_attr(attrs, "jid"),
+ NULL, NULL, res);
+ chan_notice(chan, "user: %s", res);
+ }
+ if (match(start, "item") && !chan) {
srv_notice(srv, "item: [%s] %s",
find_attr(attrs, "jid"),
find_attr(attrs, "name"));
- if (match(start, "identity"))
+ }
+ if (match(start, "identity")) {
srv_notice(srv, "identity: %s",
find_attr(attrs, "name"));
- if (match(start, "feature"))
+ }
+ if (match(start, "feature")) {
srv_notice(srv, "feature: %s",
find_attr(attrs, "var"));
+ }
+ if (match(start, "field")) {
+ debug("xmpp: %s -- type=[%s] label=[%s]", end,
+ find_attr(attrs, "type"),
+ find_attr(attrs, "label"));
+ if (!find_attr(attrs, "label"))
+ return;
+ chan_notice(chan, "%-36s -- %s (%s)",
+ find_attr(attrs, "var"),
+ find_attr(attrs, "label"),
+ find_attr(attrs, "type"));
+ }
+ if (match(end, "title")) {
+ debug("xmpp: title -- jid=[%s]",
+ end, srv->msg_jid);
+ chan_notice(chan, "Title: %s", data);
+ }
+ if (match(end, "instructions")) {
+ debug("xmpp: instructions -- jid=[%s]",
+ end, srv->msg_jid);
+ chan_notice(chan, "%s", data);
+ }
}
- /* Receive messages */
- if (srv->state == XMPP_READY) {
- if (match(start, "message")) {
- strncpy(srv->msg_from, find_attr(attrs,"from"),
- JID_LEN);
- split_jid(srv->msg_from, srv->msg_usr,
- srv->msg_srv, srv->msg_res);
+ /* Messages */
+ if (srv->state == XMPP_IN_MESSAGE) {
+ if (match(end, "body") && data) {
+ debug("xmpp: body (%s) -- chan=[%s] jid=[%s] from=[%s]",
+ srv->msg_from == srv->msg_usr ? "user" : "chat" ,
+ srv->msg_chan->channel.name,
+ srv->msg_jid, srv->msg_from);
+ chat_recv(&chan->channel, srv->msg_from, data);
}
- if (match(end, "message")) {
- srv->msg_from[0] = '\0';
- srv->msg_usr[0] = '\0';
- srv->msg_srv[0] = '\0';
- srv->msg_res[0] = '\0';
+ }
+
+ /* Presence */
+ if (srv->state == XMPP_IN_PRESENCE) {
+ static char alias[JID_LEN];
+ const char *jid;
+ if (match(start, "item")) {
+ if ((jid = find_attr(attrs, "jid")))
+ strncpy(alias, jid, JID_LEN);
}
- if (match(end, "body") && data) {
- xmpp_channel_t *chan;
- debug("xmpp: jid -> ready");
- if (match(srv->msg_srv, srv->muc)) {
- chan = find_dest(srv, srv->msg_from, 1);
- chat_recv(&chan->channel, srv->msg_res, data);
- } else {
- chan = find_dest(srv, srv->msg_from, 0);
- chat_recv(&chan->channel, srv->msg_usr, data);
- }
+ if (match(end, "presence") && chan) {
+ if (alias[0])
+ chan_notice(chan, "%s (%s) entered room.",
+ srv->msg_from, alias);
+ else
+ chan_notice(chan, "%s entered room.",
+ srv->msg_from);
+ alias[0] = '\0';
}
}
+ /* End messages */
+ if (srv->state == XMPP_IN_IQ ||
+ srv->state == XMPP_IN_MESSAGE ||
+ srv->state == XMPP_IN_PRESENCE) {
+ if (match(end, "iq") ||
+ match(end, "message") ||
+ match(end, "presence")) {
+ srv->state = XMPP_READY;
+
+ srv->msg_jid[0] = '\0';
+ srv->msg_usr[0] = '\0';
+ srv->msg_srv[0] = '\0';
+ srv->msg_res[0] = '\0';
+ srv->msg_from = NULL;
+ srv->msg_chan = NULL;
+ }
+ }
/* Error handling */
if (match(start, "stream:error"))
srv->in_error = 1;
}
}
-void xmpp_send(message_t *msg)
+void xmpp_send(channel_t *channel, const char *text)
{
- xmpp_channel_t *chan = (xmpp_channel_t*)msg->channel;
- xmpp_server_t *srv = (xmpp_server_t*)msg->channel->server;
- const char *txt = msg->text;
+ xmpp_channel_t *chan = (xmpp_channel_t*)channel;
+ xmpp_server_t *srv = (xmpp_server_t*)channel->server;
const char *arg;
/* Handle commands */
- if (txt[0] == '/') {
- if (prefix(txt, "/items", &arg)) {
+ if (text[0] == '/') {
+ if (prefix(text, "/items", &arg)) {
net_print(&srv->net,
"<iq id='items' type='get' from='%s' to='%s'>"
- "<query xmlns='http://jabber.org/protocol/disco#items' />"
+ "<query xmlns='http://jabber.org/protocol/disco#items'/>"
"</iq>",
srv->jid, arg ?: srv->host);
}
- else if (prefix(txt, "/info", &arg)) {
+ else if (prefix(text, "/info", &arg)) {
net_print(&srv->net,
"<iq id='info' type='get' from='%s' to='%s'>"
- "<query xmlns='http://jabber.org/protocol/disco#info' />"
+ "<query xmlns='http://jabber.org/protocol/disco#info'/>"
"</iq>",
srv->jid, arg ?: srv->host);
}
- else if (prefix(txt, "/list", &arg)) {
+ else if (prefix(text, "/names", &arg)) {
+ if (arg)
+ chan = find_dest(srv, arg, 1);
+ if (chan == &srv->system) {
+ chan_notice(chan, "Cannot get names from server");
+ return;
+ }
net_print(&srv->net,
"<iq id='list' type='get' from='%s' to='%s'>"
- "<query xmlns='http://jabber.org/protocol/disco#items' />"
+ "<query xmlns='http://jabber.org/protocol/disco#items'/>"
"</iq>",
- srv->jid, arg ?: srv->muc);
+ srv->jid, chan->dest);
}
- else if (prefix(txt, "/join", &arg)) {
+ else if (prefix(text, "/join", &arg)) {
if (!arg) {
chan_notice(chan, "usage: /join <channel>");
return;
}
- chan = find_dest(srv, arg, 1);
net_print(&srv->net,
"<presence id='join' from='%s' to='%s/%s'>"
"<x xmlns='http://jabber.org/protocol/muc'/>"
"</presence>",
srv->jid, chan->dest, srv->nick);
+ chan = find_dest(srv, arg, 1);
+ chan_notice(chan, "Room: %s", arg);
+ }
+ else if (prefix(text, "/config", &arg)) {
+ if (arg) {
+ chan_notice(chan, "Unimplemented: /config <arg>");
+ return;
+ }
+ net_print(&srv->net,
+ "<iq id='config' type='get' from='%s' to='%s'>"
+ "<query xmlns='http://jabber.org/protocol/muc#owner'/>"
+ "</iq>",
+ srv->jid, chan->dest);
}
- else if (prefix(txt, "/query", &arg)) {
+ else if (prefix(text, "/query", &arg)) {
if (!arg) {
chan_notice(chan, "usage: /query <user>");
return;
}
chan = find_dest(srv, arg, 0);
- chan_notice(chan, "query <user>");
+ chan_notice(chan, "User: %s", arg);
}
else {
- debug("unknown: [%s]", txt);
- chan_notice(chan, "unknown command %s", txt);
+ chan_notice(chan, "Unknown command %s", text);
}
} else {
- debug("message: [%s]", txt);
- if (chan->room) {
+ debug("message: [%s]", text);
+ if (chan == &srv->system) {
+ chan_notice(chan, "Cannot send to server");
+ }
+ else if (!chan->dest) {
+ chan_notice(chan, "No destination for message");
+ }
+ else if (chan->room) {
net_print(&srv->net,
"<message id='chat' from='%s' to='%s' type='groupchat'>"
"<body>%s</body>"
"</message>",
- srv->jid, chan->dest, txt);
+ srv->jid, chan->dest, text);
} else {
net_print(&srv->net,
"<message id='chat' from='%s' to='%s'>"
"<body>%s</body>"
"</message>",
- srv->jid, chan->dest, txt);
+ srv->jid, chan->dest, text);
+ chat_recv(channel, srv->nick, text);
}
}
}