#include "irc_net.h"
#include "log.h"
#include <stdlib.h>
#include <string.h>
/**
* Something happaned which caused our irc_conn to fail. Destroy it, and recover. XXX: somehow
*/
static void irc_net_error (struct irc_net *net, struct error_info *err)
{
struct irc_chan *chan = NULL;
// log an error
log_err_info(err, "irc_conn failed");
// destroy connection and set NULL
irc_conn_destroy(net->conn);
net->conn = NULL;
// update channel state
TAILQ_FOREACH(chan, &net->channels, node) {
// XXX: notify channel somehow
}
// XXX: reconnect?
}
/**
* Our irc_conn has registered. Join our channels
*/
static void irc_net_conn_registered (struct irc_conn *conn, void *arg)
{
struct irc_net *net = arg;
struct irc_chan *chan = NULL;
struct error_info err;
(void) conn;
// join our channels
TAILQ_FOREACH(chan, &net->channels, node) {
if ((ERROR_CODE(&err) = irc_chan_join(chan)))
// XXX: this should be some kind of irc_chan_error instead
irc_net_error(net, &err);
}
}
/**
* Our irc_conn has spontaneously failed, destroy it
*/
static void irc_net_conn_error (struct irc_conn *conn, struct error_info *err, void *arg)
{
struct irc_net *net = arg;
(void) conn;
irc_net_error(net, err);
}
/**
* Our irc_conn has quit succesfully
*/
static void irc_net_conn_quit (struct irc_conn *conn, void *arg)
{
struct irc_net *net = arg;
// clean up the conn
irc_conn_destroy(conn);
net->conn = NULL;
// XXX: notify user
}
/**
* Our irc_conn_callbacks list
*/
struct irc_conn_callbacks _conn_callbacks = {
.on_registered = &irc_net_conn_registered,
.on_error = &irc_net_conn_error,
.on_quit = &irc_net_conn_quit,
};
/**
* Propagate the command to the appropriate irc_chan based on the given name
*/
static void irc_net_propagate_chan (struct irc_net *net, const struct irc_line *line, const char *channel)
{
struct irc_chan *chan;
// look up channel
if ((chan = irc_net_get_chan(net, channel)) == NULL) {
log_warn("unknown channel: %s", channel);
return;
}
// propagate
irc_cmd_invoke(&chan->handlers, line);
}
/**
* Propagate line to irc_chan based on args[0]
*/
static void irc_net_on_chan0 (const struct irc_line *line, void *arg)
{
struct irc_net *net = arg;
irc_net_propagate_chan(net, line, line->args[0]);
}
/**
* :nm PRIVMSG <target> <message>
*
* Either propagate to channel if found, otherwise handle as a privmsg
*/
static void irc_net_on_PRIVMSG (const struct irc_line *line, void *arg)
{
struct irc_net *net = arg;
// XXX: does two lookups
if (irc_net_get_chan(net, line->args[0])) {
// short-circuit to on_chan0
irc_net_on_chan0(line, arg);
} else {
// XXX: callbacks for privmsgs
}
}
/**
* Our irc_cmd handler list
*/
static struct irc_cmd_handler _cmd_handlers[] = {
{ "JOIN", &irc_net_on_chan0 },
{ "PRIVMSG", &irc_net_on_PRIVMSG },
{ NULL, NULL }
};
err_t irc_net_create (struct irc_net **net_ptr, const struct irc_net_info *info, struct error_info *err)
{
struct irc_net *net;
struct sock_stream *sock = NULL;
// allocate
if ((net = calloc(1, sizeof(*net))) == NULL)
return SET_ERROR(err, ERR_CALLOC);
// initialize
TAILQ_INIT(&net->channels);
if (info->raw_sock) {
log_info("connected using raw socket: %p", info->raw_sock);
sock = info->raw_sock;
} else if (info->use_ssl) {
log_info("connecting to [%s]:%s using SSL", info->hostname, info->service);
// XXX: over-simplified blocking connect
if (sock_ssl_connect(&sock, info->hostname, info->service, err))
goto error;
} else {
log_info("connecting to [%s]:%s", info->hostname, info->service);
// XXX: over-simplified blocking connect
if (sock_tcp_connect(&sock, info->hostname, info->service, err))
goto error;
}
// create the irc connection state
if (irc_conn_create(&net->conn, sock, &_conn_callbacks, net, err))
goto error;
// add our command handlers
if ((ERROR_CODE(err) = irc_conn_add_cmd_handlers (net->conn, _cmd_handlers, net)))
goto error;
// register
log_info("connected, registering");
if ((ERROR_CODE(err) = irc_conn_register(net->conn, &info->register_info)))
goto error;
// ok
*net_ptr = net;
return SUCCESS;
error:
if (sock && !net->conn)
// we need to clean up the socket ourself
sock_stream_release(sock);
// cleanup main
irc_net_destroy(net);
return ERROR_CODE(err);
}
void irc_net_destroy (struct irc_net *net)
{
struct irc_chan *next = TAILQ_FIRST(&net->channels), *chan;
// our conn
if (net->conn)
irc_conn_destroy(net->conn);
// our channels
while (next) {
chan = next;
next = TAILQ_NEXT(chan, node);
irc_chan_destroy(chan);
}
// ourselves
free(net);
}
struct irc_chan* irc_net_add_chan (struct irc_net *net, const struct irc_chan_info *info)
{
struct irc_chan *chan;
struct error_info err;
// create the new irc_chan struct
if (irc_chan_create(&chan, net, info, &err))
// XXX: we lose error info
return NULL;
// add to network list
TAILQ_INSERT_TAIL(&net->channels, chan, node);
// currently connected?
if (net->conn && net->conn->registered) {
// then join
if ((ERROR_CODE(&err) = irc_chan_join(chan)))
// XXX
return NULL;
}
// ok, return
return chan;
}
struct irc_chan* irc_net_get_chan (struct irc_net *net, const char *channel)
{
struct irc_chan *chan = NULL;
// look for it...
TAILQ_FOREACH(chan, &net->channels, node) {
if (strcasecmp(chan->info.channel, channel) == 0)
// found it
return chan;
}
// no such channel
return NULL;
}
err_t irc_net_quit (struct irc_net *net, const char *message)
{
if (!net->conn)
return ERR_IRC_NET_QUIT_STATE;
// send the QUIT message, and then we can wait for the reply
return irc_conn_QUIT(net->conn, message);
}