add irc_client_defaults to apply default values for irc_client_add_net irc_net_info, implement --defaults cmd opt and lua_client_connect
authorTero Marttila <terom@fixme.fi>
Tue, 31 Mar 2009 22:09:53 +0300
changeset 98 f357f835f0d5
parent 97 d3bc82ee76cb
child 99 155a6c7d3886
add irc_client_defaults to apply default values for irc_client_add_net irc_net_info, implement --defaults cmd opt and lua_client_connect
src/error.h
src/irc_client.c
src/irc_client.h
src/irc_net.h
src/lua_objs.c
src/nexus.c
--- a/src/error.h	Tue Mar 31 20:57:07 2009 +0300
+++ b/src/error.h	Tue Mar 31 22:09:53 2009 +0300
@@ -81,6 +81,7 @@
 
     /** irc_net errors */
     _ERR_IRC_NET    = 0x000900,
+    ERR_IRC_NET_INFO,
     ERR_IRC_NET_STATE,
 
     /** @see module_error_code */
@@ -94,6 +95,8 @@
     _ERR_LUA        = 0x000c00,
     ERR_LUA_MEM,
     ERR_LUA_SYNTAX,
+    ERR_LUA_RUN,
+    ERR_LUA_ERR,
 
     /** irc_chan errors */
     _ERR_IRC_CHAN   = 0x000d00,
@@ -102,6 +105,7 @@
     /** General errors */
     _ERR_GENERAL    = 0xffff00,
     ERR_CMD_OPT,
+    ERR_UNKNOWN,
 
 };
 
--- a/src/irc_client.c	Tue Mar 31 20:57:07 2009 +0300
+++ b/src/irc_client.c	Tue Mar 31 22:09:53 2009 +0300
@@ -37,12 +37,42 @@
     free(client);
 }
 
-err_t irc_client_add_net (struct irc_client *client, struct irc_net **net_ptr, struct irc_net_info *net_info, struct error_info *err)
+void irc_client_set_defaults (struct irc_client *client, const struct irc_client_defaults *defaults)
+{
+    client->defaults = *defaults;
+}
+
+#define APPLY_REGISTER_INFO_DEFAULT(out, value, def, _field_) ( \
+        ((out)->register_info._field_ = (    \
+            (value)->register_info._field_ ? (value)->register_info._field_ : (  \
+                (def)->register_info._field_ \
+            )   \
+        )) == NULL   \
+    )
+
+err_t irc_client_add_net (struct irc_client *client, struct irc_net **net_ptr, const struct irc_net_info *_net_info, struct error_info *err)
 {
     struct irc_net *net;
+    struct irc_net_info net_info = {
+        .use_ssl    = _net_info->use_ssl,
+    };
+
+    // combine _net_info and defaults to get net_info
+    if (
+            _net_info->raw_sock
+        ||  (net_info.network = _net_info->network) == NULL
+        ||  (net_info.hostname = _net_info->hostname) == NULL
+        ||  (net_info.service = (_net_info->service ? _net_info->service : (
+                    _net_info->use_ssl ? client->defaults.service_ssl : client->defaults.service
+            ))) == NULL
+        ||  APPLY_REGISTER_INFO_DEFAULT(&net_info, _net_info, &client->defaults, nickname)
+        ||  APPLY_REGISTER_INFO_DEFAULT(&net_info, _net_info, &client->defaults, username)
+        ||  APPLY_REGISTER_INFO_DEFAULT(&net_info, _net_info, &client->defaults, realname)
+    )
+        return SET_ERROR(err, ERR_IRC_NET_INFO);
 
     // create the new irc_chan struct
-    if (irc_net_create(&net, net_info, err))
+    if (irc_net_create(&net, &net_info, err))
         return ERROR_CODE(err);
     
     // add to network list
--- a/src/irc_client.h	Tue Mar 31 20:57:07 2009 +0300
+++ b/src/irc_client.h	Tue Mar 31 22:09:53 2009 +0300
@@ -10,9 +10,26 @@
 #include <sys/queue.h>
 
 /**
+ * Defaults for irc_client operation, mostly add_net
+ */
+struct irc_client_defaults {
+    /** Default nickname/username/etc to use */
+    struct irc_conn_register_info register_info;
+
+    /** Default service (port) to use for non-SSL connections */
+    const char *service;
+
+    /** Default service (port) to use for SSL connections */
+    const char *service_ssl;
+};
+
+/**
  * High-level IRC client state, this just means a set of named irc_net's and some common behaviour.
  */
 struct irc_client {
+    /** Default parameters */
+    struct irc_client_defaults defaults;
+
     /** Our set of configured IRC networks */
     TAILQ_HEAD(irc_client_networks, irc_net) networks;
 
@@ -34,14 +51,27 @@
 void irc_client_destroy (struct irc_client *client);
 
 /**
- * Add a new IRC network
+ * Set the default parameters to use for e.g. add_net.
+ *
+ * The irc_client_defaults struct itself is copied, but not its contents.
+ *
+ * @param client the IRC client
+ * @param defaults the defaults to use, not copied
+ */
+void irc_client_set_defaults (struct irc_client *client, const struct irc_client_defaults *defaults);
+
+/**
+ * Add a new IRC network, using any default values set using irc_client_set_defaults() to replace NULL values in the
+ * given \a net_info.
+ *
+ * Use of raw_socket is not supported.
  *
  * @param client the irc_client state
  * @param net_ptr used to return the new irc_net if not NULL
  * @param net_info info required to identify and connect to the network
  * @param err returned error info
  */
-err_t irc_client_add_net (struct irc_client *client, struct irc_net **net_ptr, struct irc_net_info *net_info, struct error_info *err);
+err_t irc_client_add_net (struct irc_client *client, struct irc_net **net_ptr, const struct irc_net_info *net_info, struct error_info *err);
 
 /**
  * Get a pre-existing IRC network by name
--- a/src/irc_net.h	Tue Mar 31 20:57:07 2009 +0300
+++ b/src/irc_net.h	Tue Mar 31 22:09:53 2009 +0300
@@ -76,6 +76,8 @@
  *
  * Errors are returned via *err, also returning the error code.
  *
+ * The irc_net_info struct itself is copied, but the strings contained therein are not...
+ *
  * @param net the new irc_net struct is returned via this pointer
  * @param info network information, used to connect and register
  * @param err return error info
--- a/src/lua_objs.c	Tue Mar 31 20:57:07 2009 +0300
+++ b/src/lua_objs.c	Tue Mar 31 22:09:53 2009 +0300
@@ -1,5 +1,7 @@
 #include "lua_objs.h"
 
+#include <string.h>
+
 #include <lua5.1/lua.h>
 #include <lua5.1/lualib.h>
 #include <lua5.1/lauxlib.h>
@@ -218,20 +220,31 @@
     struct irc_client *client;
 };
 
-static int lua_client_quit (lua_State *L)
+static int lua_client_connect (lua_State *L)
 {
     struct lua_client *lua_client = lua_obj_get_obj(L, __func__, "evirc.client");
-    err_t err;
-   
-    // the message
-    const char *message = luaL_checkstring(L, 2);
+    struct irc_net_info net_info;
+    struct irc_net *net;
+    struct error_info err;
 
-    // execute
-    if ((err = irc_client_quit(lua_client->client, message)))
-        return luaL_error(L, "irc_client_quit: %s", error_name(err));
+    // init net_info
+    memset(&net_info, 0, sizeof(net_info));
 
-    // ok
-    return 0;
+    // the network name
+    net_info.network = luaL_checkstring(L, 2);
+    
+    // the hostname
+    net_info.hostname = luaL_checkstring(L, 3);
+
+    // service remains default
+    net_info.service = "6667";
+
+    // 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 int lua_client_get_network (lua_State *L)
@@ -250,9 +263,26 @@
     return lua_net_create(L, net);
 }
 
+static int lua_client_quit (lua_State *L)
+{
+    struct lua_client *lua_client = lua_obj_get_obj(L, __func__, "evirc.client");
+    err_t err;
+   
+    // the message
+    const char *message = luaL_checkstring(L, 2);
+
+    // 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 const struct luaL_Reg lua_client_methods[] = {
+    {   "connect",      &lua_client_connect     },
+    {   "network",      &lua_client_get_network },
     {   "quit",         &lua_client_quit        },
-    {   "network",      &lua_client_get_network },
     {   NULL,           NULL                    }
 };
 
@@ -263,7 +293,6 @@
 static void lua_client_init (lua_State *L, struct irc_client *client)
 {
     // allocate the global "client" object
-    // XXX: mem errors?
     struct lua_client *lua_client = lua_newuserdata(L, sizeof(*lua_client));
     
     // create the type metatable
@@ -279,12 +308,35 @@
     lua_setglobal(L, "client");
 }
 
-err_t lua_objs_init (lua_State *L, struct nexus *nexus)
+/**
+ * Set up the lua state in protected mode
+ */
+static int _lua_objs_init (lua_State *L)
 {
+    struct nexus *nexus;
+    
+    // read the nexus off the stack
+    if ((nexus = lua_touserdata(L, 1)) == NULL)
+        luaL_error(L, "lua_touserdata: NULL");
+
     // init the various bits
     lua_client_init(L, nexus->client);
     lua_net_init(L);
     lua_chan_init(L);
 
-    return SUCCESS;
+    // nothing
+    return 0;
 }
+
+err_t lua_objs_init (lua_State *L, struct nexus *nexus)
+{
+    // call in protected mode
+    switch (lua_cpcall(L, &_lua_objs_init, nexus)) {
+        case 0:             return SUCCESS;
+        case LUA_ERRRUN:    return ERR_LUA_RUN;
+        case LUA_ERRMEM:    return ERR_LUA_MEM;
+        case LUA_ERRERR:    return ERR_LUA_ERR;
+        default:            return ERR_UNKNOWN;
+    }
+}
+
--- a/src/nexus.c	Tue Mar 31 20:57:07 2009 +0300
+++ b/src/nexus.c	Tue Mar 31 22:09:53 2009 +0300
@@ -18,6 +18,7 @@
  */
 enum option_code {
     OPT_HELP            = 'h',
+    OPT_DEFAULTS        = 'D',
     OPT_NETWORK         = 'n',
     OPT_CHANNEL         = 'c',
     OPT_MODULE          = 'm',
@@ -35,6 +36,7 @@
  */
 static struct option options[] = {
     {"help",            0,  NULL,   OPT_HELP        },
+    {"defaults",        1,  NULL,   OPT_DEFAULTS    },
     {"network",         1,  NULL,   OPT_NETWORK     },
     {"channel",         1,  NULL,   OPT_CHANNEL     },
     {"module",          1,  NULL,   OPT_MODULE      },
@@ -54,6 +56,7 @@
     printf("Usage: %s [OPTIONS]\n", exe);
     printf("\n");
     printf(" --help / -h            display this message\n");
+    printf(" --defaults / -D        set the IRC client default info using '<nickname>:<username>:<realname>'\n");
     printf(" --network / -n         add an IRC network using '<name>:<hostname>[:<port>[:ssl]]' format\n");
     printf(" --channel / -c         add an IRC channel using '<network>:<channel>' format\n");
     printf(" --module / -m          add a module using '<name>:<path>' format\n");
@@ -87,21 +90,49 @@
 }
 
 /**
+ * Parse and apply a --defaults option, setting the resulting defaults to our irc_client
+ */
+static err_t apply_defaults (struct nexus *nexus, char *opt, struct error_info *err)
+{
+    struct irc_client_defaults defaults = {
+        .register_info      = { NULL, NULL, NULL },
+        .service            = DEFAULT_PORT,
+        .service_ssl        = DEFAULT_PORT_SSL,
+    };
+
+    // parse the required fields
+    if ((defaults.register_info.nickname = strsep(&opt, ":")) == NULL)
+        RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <nickname> field for --defaults");
+    
+    if ((defaults.register_info.username = strsep(&opt, ":")) == NULL)
+        RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <username> field for --defaults");
+    
+    if ((defaults.register_info.realname = strsep(&opt, ":")) == NULL)
+        RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <realname> field for --defaults");
+
+    // trailing garbage?
+    if (opt)
+        RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "trailing values for --channel");
+    
+    // apply them
+    log_info("using default nick/user/real-name: %s/%s/%s", 
+            defaults.register_info.nickname, defaults.register_info.username, defaults.register_info.realname);
+
+    irc_client_set_defaults(nexus->client, &defaults);
+
+    // ok
+    return SET_ERROR(err, SUCCESS);
+}
+
+/**
  * Parse and apply a --network option, adding the given network to our irc_client.
  */
 static err_t apply_network (struct nexus *nexus, char *opt, struct error_info *err)
 {
-    struct irc_net_info info = {
-        .network            = NULL,
-        .hostname           = NULL,
-        .service            = DEFAULT_PORT,
-        .use_ssl            = false,
-        .register_info      = {
-            .nickname       = "SpBotDev",
-            .username       = "spbot-dev",
-            .realname       = "SpBot (development version)"
-        }
-    };
+    struct irc_net_info info;
+
+    // init to zero
+    memset(&info, 0, sizeof(info));
 
     // parse the required fields
     if ((info.network = strsep(&opt, ":")) == NULL)
@@ -280,6 +311,12 @@
                     return ERROR_CODE(err);
 
                 break;
+            
+            case OPT_DEFAULTS:
+                if (apply_defaults(nexus, optarg, err))
+                    return ERROR_CODE(err);
+
+                break;
 
             case OPT_CHANNEL:
                 if (apply_channel(nexus, optarg, err))