src/irc_net.c
author Tero Marttila <terom@fixme.fi>
Thu, 12 Mar 2009 22:50:08 +0200
changeset 45 71e65564afd2
parent 44 6bd70113e1ed
child 46 0c13bca53ae1
permissions -rw-r--r--
remove irc_chan.state, modify irc_chan_callbacks.on_msg to take a irc_nm, improve error handling a bit further (up to irc_net now)
#include "irc_net.h"
#include "log.h"

#include <stdlib.h>
#include <string.h>

static void irc_net_conn_registered (struct irc_conn *conn, void *arg)
{
    struct irc_net *net = arg;
    struct irc_chan *chan = NULL;
    err_t err;

    (void) conn;

    // join our channels
    TAILQ_FOREACH(chan, &net->channels, node) {
        if ((err = irc_chan_join(chan)))
            // XXX: fuck...
            FATAL_ERR(err, "irc_chan_join failed");
    }
}

static void irc_net_conn_error (struct irc_conn *conn, struct error_info *err, void *arg)
{
    struct irc_net *net = arg;
    
    // log an error
    log_err_info(err, "irc_conn failed");

    // destroy and set NULL
    irc_conn_destroy(conn);
    net->conn = NULL;

    // XXX: reconnect?
}

/**
 * Our irc_conn_callbacks list
 */
struct irc_conn_callbacks _conn_callbacks = {
    .on_registered  = &irc_net_conn_registered,
    .on_error       = &irc_net_conn_error,
};

/**
 * 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:
    // cleanup
    if (net->conn)
        // irc_conn takes care of the socket as well
        irc_conn_destroy(net->conn);
    
    else if (sock)
        // we need to clean up the socket ourself
        sock_stream_release(sock);
    
    // release our state
    free(net);

    return ERROR_CODE(err);
}

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;

}