src/lua_irc.c
author Tero Marttila <terom@fixme.fi>
Thu, 28 May 2009 01:17:36 +0300
branchnew-lib-errors
changeset 219 cefec18b8268
parent 199 8eb839fbabba
permissions -rw-r--r--
some of the lib/transport stuff compiles
#include "lua_irc.h"
#include "lua_func.h"

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

static struct lua_type lua_chan_type = LUA_TYPE("evirc.chan");

/**
 * Create a lua_chan userdata from the given irc_chan and leave it on the stack, returning 1
 */
static int lua_chan_create (lua_State *L, struct irc_chan *chan)
{
    // create the new obj
    struct lua_chan *lua_chan = lua_type_create(L, &lua_chan_type, sizeof(*lua_chan));

    // initialize
    lua_chan->chan = chan;

    // ok
    return 1;
}

/**
 * Return the channel name as a lua string
 */
static struct lua_func lua_chan__tostring_func = LUA_FUNC(&lua_chan_type, "__tostring",
        "format using channel name",

        LUA_FUNC_ARG_END
    );

static int lua_chan__tostring (lua_State *L)
{
    struct lua_chan *lua_chan;

    lua_args_parse(L, &lua_chan__tostring_func, (void *) &lua_chan);
    
    lua_pushfstring(L, "<irc_chan %s>", irc_chan_name(lua_chan->chan));

    return 1;
}

/**
 * Send a PRIVMSG to the channel
 */
static struct lua_func lua_chan_say_func = LUA_FUNC(&lua_chan_type, "evirc.chan.say",
        "send a message to a channel",

        LUA_FUNC_ARG_STRING("message",      LUA_ARG_STRING_REQUIRED )
    );

static int lua_chan_say (lua_State *L)
{
    struct lua_chan *lua_chan;
    err_t err;
    const char *message;

    // parse args
    lua_args_parse(L, &lua_chan_say_func, (void *) &lua_chan,
            &message
        );

    // send
    if ((err = irc_chan_PRIVMSG(lua_chan->chan, message)))
        return luaL_error(L, "irc_chan_PRIVMSG: '%s': %s", message, error_name(err));
    
    // ok
    return 0;
}

static struct lua_method lua_chan_methods[] = LUA_METHODS(
        LUA_METHOD("__tostring",    lua_chan__tostring, &lua_chan__tostring_func    ),
        LUA_METHOD("say",           lua_chan_say,       &lua_chan_say_func          )
    );



static struct lua_type lua_net_type = LUA_TYPE("evirc.net");

/**
 * Create a lua_net userdata from the given irc_net and push it onto the stack, returning 1.
 */
static int lua_net_create (lua_State *L, struct irc_net *net)
{
    // create the new obj
    struct lua_net *lua_net = lua_type_create(L, &lua_net_type, sizeof(*lua_net));

    // initialize
    lua_net->net = net;

    // ok
    return 1;
}

/**
 * Return the network name as a lua string
 */
static struct lua_func lua_net__tostring_func = LUA_FUNC(&lua_net_type, "__tostring",
        "format using network name",

        LUA_FUNC_ARG_END
    );

static int lua_net__tostring (lua_State *L)
{
    struct lua_net *lua_net;

    lua_args_parse(L, &lua_net__tostring_func, (void *) &lua_net);
    
    lua_pushfstring(L, "<irc_net %s>", irc_net_name(lua_net->net));

    return 1;
}

/**
 * Join a new channel, returning the lua_chan
 */
static struct lua_func lua_net_join_func = LUA_FUNC(&lua_net_type, "join",
        "create a new channel and join it",

        LUA_FUNC_ARG_STRING("channel",      LUA_ARG_STRING_REQUIRED)
    );


static int lua_net_join (lua_State *L)
{
    struct lua_net *lua_net;
    struct irc_chan_info chan_info;
    struct irc_chan *chan;
    struct error_info err;
    
    // the channel name
    lua_args_parse(L, &lua_net_join_func, (void *) &lua_net,
            &chan_info.channel
        );
    
    // add it
    if (irc_net_add_chan(lua_net->net, &chan, &chan_info, &err))
        return luaL_error(L, "irc_net_add_chan: %s: %s", chan_info.channel, error_msg(&err));
    
    // return it
    return lua_chan_create(L, chan);    
}

/**
 * Look up a channel by name, returning the lua_chan
 */
static struct lua_func lua_net_get_chan_func = LUA_FUNC(&lua_net_type, "channel",
        "look up a channel by name",

        LUA_FUNC_ARG_STRING("channel",      LUA_ARG_STRING_REQUIRED)
    );

static int lua_net_get_chan (lua_State *L)
{
    struct lua_net *lua_net;
    struct irc_chan *chan;
    const char *channel;

    // parse args
    lua_args_parse(L, &lua_net_get_chan_func, (void *) &lua_net,
            &channel
        );
    
    // lookup the irc_chan
    if ((chan = irc_net_get_chan(lua_net->net, channel)) == NULL)
        return luaL_error(L, "irc_net_get_chan: no such channel: %s", channel);

    // wrap it
    return lua_chan_create(L, chan);
}

static struct lua_func lua_net_channels_func = LUA_FUNC(&lua_net_type, "channels",
        "Return an iterater over the network's channels",
        
        LUA_FUNC_ARG_END
    );

static int lua_net_channels_iter (lua_State *L)
{
    int nargs = lua_gettop(L);
    struct lua_net *lua_net = lua_arg_obj(L, nargs, 1, &lua_net_type, false);
    struct lua_chan *lua_chan = lua_arg_obj(L, nargs, 2, &lua_chan_type, true);
    struct irc_chan *chan_next;

    // get next item from current
    if (lua_chan)
        chan_next = TAILQ_NEXT(lua_chan->chan, net_channels);
    else
        chan_next = TAILQ_FIRST(&lua_net->net->channels);

    // push return value - next item
    if (chan_next)
        lua_chan_create(L, chan_next);
    else
        lua_pushnil(L);

    return 1;
}

static int lua_net_channels (lua_State *L)
{
    struct lua_net *lua_net;

    // parse args
    lua_args_parse(L, &lua_net_channels_func, (void *) &lua_net);

    // push iter func
    lua_pushcfunction(L, lua_net_channels_iter);

    // push invariant state - the lua_net
    lua_pushvalue(L, 1);

    // return iter three-tuple
    return 2;
}

static struct lua_method lua_net_methods[] = LUA_METHODS(
        LUA_METHOD("__tostring",    lua_net__tostring,  &lua_net__tostring_func ),
        LUA_METHOD("join",          lua_net_join,       &lua_net_join_func      ),
        LUA_METHOD("channel",       lua_net_get_chan,   &lua_net_get_chan_func  ),
        LUA_METHOD("channels",      lua_net_channels,   &lua_net_channels_func  )
    );




static struct lua_type lua_client_type = LUA_TYPE("evirc.client");


static struct lua_func lua_client_set_defaults_func = LUA_FUNC(&lua_client_type, "set_defaults", 
        "set the default settings to use for evirc.client.connect",

        LUA_FUNC_ARG_STRING("nickname",     LUA_ARG_STRING_REQUIRED ),
        LUA_FUNC_ARG_STRING("username",     LUA_ARG_STRING_REQUIRED ),
        LUA_FUNC_ARG_STRING("realname",     LUA_ARG_STRING_REQUIRED ),
        LUA_FUNC_ARG_STRING("service",      IRC_PORT                ),
        LUA_FUNC_ARG_STRING("service_ssl",  IRC_SSL_PORT            )
    );

static int lua_client_set_defaults (lua_State *L)
{
    struct lua_client *lua_client;
    const char *nickname, *username, *realname, *service, *service_ssl;

    // parse args
    lua_args_parse(L, &lua_client_set_defaults_func, (void *) &lua_client,
            &nickname, &username, &realname, &service, &service_ssl
        );

    // set
    struct irc_client_defaults defaults = {
        .register_info = {
            .nickname   = nickname,
            .username   = username,
            .realname   = realname
        },
        .service        = service,
        .service_ssl    = service_ssl
    };

    // invoke
    // XXX: needs to be copied
    irc_client_set_defaults(lua_client->client, &defaults);

    // ok
    return 0;
}

static struct lua_func lua_client_connect_func = LUA_FUNC(&lua_client_type, "connect",
        "Create and return a new IRC network",

        LUA_FUNC_ARG_STRING("network",      LUA_ARG_STRING_REQUIRED ),
        LUA_FUNC_ARG_STRING("hostname",     LUA_ARG_STRING_REQUIRED ),
        LUA_FUNC_ARG_STRING("service",      NULL                    ),
        LUA_FUNC_ARG_BOOL(  "ssl",          false                   ),
        LUA_FUNC_ARG_STRING("ssl_cafile",   NULL                    ),
        LUA_FUNC_ARG_BOOL(  "ssl_verify",   false                   ),
        LUA_FUNC_ARG_STRING("ssl_cert",     NULL                    ),
        LUA_FUNC_ARG_STRING("ssl_pkey",     NULL                    )
);

static int lua_client_connect (lua_State *L)
{
    struct lua_client *lua_client;
    struct irc_net_info net_info;
    const char *ssl_cafile = NULL, *ssl_cert = NULL, *ssl_pkey = NULL;
    bool use_ssl = false, ssl_verify = false;
    struct irc_net *net;
    struct error_info err;

    // init net_info
    memset(&net_info, 0, sizeof(net_info));

    // parse args
    lua_args_parse(L, &lua_client_connect_func, (void *) &lua_client,
        &net_info.network, &net_info.hostname, &net_info.service,
        &use_ssl, &ssl_cafile, &ssl_verify, &ssl_cert, &ssl_pkey
    );

    // SSL?
    if (use_ssl || ssl_cafile || ssl_verify || ssl_cert || ssl_pkey) {
        // verify
        if ((ssl_cert || ssl_pkey) && !(ssl_cert && ssl_pkey))
            return luaL_error(L, "must give both ssl_cert and ssl_pkey");
        
        // create
        if (ssl_client_cred_create(&net_info.ssl_cred, ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, &err))
            return luaL_error(L, "sock_ssl_client_cred_create(ssl_cafile=%s, ssl_verify=%b, ssl_cert=%s, ssl_pkey=%s): %s", 
                    ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, error_msg(&err)
                );
    }

    // create it
    if (irc_client_add_net(lua_client->client, &net, &net_info, &err))
        return luaL_error(L, "irc_client_add_net: %s/%s: %s", net_info.network, net_info.hostname, error_msg(&err));

    // wrap it
    return lua_net_create(L, net);
}

static struct lua_func lua_client_get_network_func = LUA_FUNC(&lua_client_type, "network",
        "Lookup an existing network by name",

        LUA_FUNC_ARG_STRING("network",  LUA_ARG_STRING_REQUIRED)
    );

static int lua_client_get_network (lua_State *L)
{
    struct lua_client *lua_client;
    struct irc_net *net;
    const char *network;

    // parse args
    lua_args_parse(L, &lua_client_get_network_func, (void *) &lua_client,
            &network
        );
    
    // lookup the irc_net
    if ((net = irc_client_get_net(lua_client->client, network)) == NULL)
        return luaL_error(L, "irc_client_get_net: no such network: %s", network);

    // wrap it
    return lua_net_create(L, net);
}

static struct lua_func lua_client_networks_func = LUA_FUNC(&lua_client_type, "channels",
        "Return an iterator over the client's networks",

        LUA_FUNC_ARG_END
    );

static int lua_client_networks_iter (lua_State *L)
{
    int nargs = lua_gettop(L);
    struct lua_client *lua_client = lua_arg_obj(L, nargs, 1, &lua_client_type, false);
    struct lua_net *lua_net = lua_arg_obj(L, nargs, 2, &lua_net_type, true);
    struct irc_net *net_next;

    if (lua_net)
        // return next
        net_next = TAILQ_NEXT(lua_net->net, client_networks);
    else
        // return first
        net_next = TAILQ_FIRST(&lua_client->client->networks);
    
    // build and return next value
    if (net_next)
        lua_net_create(L, net_next);
    else
        lua_pushnil(L);
    
    return 1;
}

static int lua_client_networks (lua_State *L)
{
    struct lua_client *lua_client;

    // parse args
    lua_args_parse(L, &lua_client_networks_func, (void *) &lua_client);

    // push iter func
    lua_pushcfunction(L, lua_client_networks_iter);

    // push invariant state - the lua_client
    lua_pushvalue(L, 1);

    // return three-tuple
    return 2;
}

static struct lua_func lua_client_quit_func = LUA_FUNC(&lua_client_type, "quit",
        "Disconnect from all networks",

        LUA_FUNC_ARG_STRING("message",  "Bye")
    );

static int lua_client_quit (lua_State *L)
{
    struct lua_client *lua_client;
    err_t err;
    const char *message;

    // parse args
    lua_args_parse(L, &lua_client_quit_func, (void *) &lua_client,
            &message
        );

    // execute
    if ((err = irc_client_quit(lua_client->client, message)))
        return luaL_error(L, "irc_client_quit: %s", error_name(err));

    // ok
    return 0;
}

static struct lua_method lua_client_methods[] = LUA_METHODS(
        LUA_METHOD("set_defaults",  lua_client_set_defaults,    &lua_client_set_defaults_func   ),
        LUA_METHOD("connect",       lua_client_connect,         &lua_client_connect_func        ),
        LUA_METHOD("network",       lua_client_get_network,     &lua_client_get_network_func    ),
        LUA_METHOD("networks",      lua_client_networks,        &lua_client_networks_func       ),
        LUA_METHOD("quit",          lua_client_quit,            &lua_client_quit_func           )
    );

void lua_irc_init (struct nexus_lua *lua)
{
    // register types
    lua_type_register(lua->st, &lua_chan_type, lua_chan_methods);
    lua_type_register(lua->st, &lua_net_type, lua_net_methods);

    // register the global "client" object
    struct lua_client *lua_client = lua_type_register_global(lua->st, &lua_client_type, lua_client_methods, "client", sizeof(*lua_client));
    
    // initialize it
    lua_client->client = lua->nexus->client;
}