Implement WHO for the admin network.
[jelmer/ctrlproxy.git] / src / admin.c
index 2f3dc01b006c3f710f6d3f85f5ded2541b1d1e1d..29831cc74c9b77d79ca9919d305eaf407c95ea01 100644 (file)
 #include "internals.h"
 #include <string.h>
 #include "admin.h"
+#include "help.h"
+#include "irc.h"
+
+help_t *help;
 
 #define ADMIN_CHANNEL "#ctrlproxy"
 
-static GList *commands = NULL;
-static guint longest_command = 0;
+GList *admin_commands = NULL;
+guint longest_command = 0;
+
+static void privmsg_admin_out(admin_handle h, const char *data)
+{
+       struct client *c = h->client;
+       char *nick = c->nick;
+       char *hostmask;
+
+       hostmask = g_strdup_printf("ctrlproxy!ctrlproxy@%s", c->network->info.name);
+       if (c->network->state) nick = c->network->state->me.nick;
+       client_send_args_ex(c, hostmask, "NOTICE", nick, data, NULL);
+
+       g_free(hostmask);
+}
+
+static void network_admin_out(admin_handle h, const char *data)
+{
+       struct client *c = h->client;
+       char *hostmask;
+
+       hostmask = g_strdup_printf("ctrlproxy!ctrlproxy@%s", c->network->info.name);
+       virtual_network_recv_args(c->network, hostmask, "PRIVMSG", ADMIN_CHANNEL, 
+                                                         data, NULL);
+
+       g_free(hostmask);
+}
+
+static void cmd_help(admin_handle h, char **args, void *userdata)
+{
+       const char *s;
+
+       s = help_get(help, args[1] != NULL?args[1]:"index");
+
+       if (s == NULL) {
+               if (args[1] == NULL)
+                       admin_out(h, "Sorry, help not available");
+               else
+                       admin_out(h, "Sorry, no help for %s available", args[1]);
+               return;
+       }
+
+       while (strncmp(s, "%\n", 2) != 0) {
+               char *tmp;
+               admin_out(h, "%s", tmp = g_strndup(s, strchr(s, '\n')-s));
+               g_free(tmp);
+                       
+               s = strchr(s, '\n')+1;
+       }
+}
 
-void admin_out(struct client *c, const char *fmt, ...)
+struct client *admin_get_client(admin_handle h)
+{
+       return h->client;
+}
+
+struct global *admin_get_global(admin_handle h)
+{
+       return h->global;
+}
+
+struct network *admin_get_network(admin_handle h)
+{
+       return h->network;
+}
+
+void admin_out(admin_handle h, const char *fmt, ...)
 {
        va_list ap;
        char *msg;
-       char *hostmask;
        va_start(ap, fmt);
        msg = g_strdup_vprintf(fmt, ap);
        va_end(ap);
 
-       hostmask = g_strdup_printf("ctrlproxy!ctrlproxy@%s", c->network->name);
-       if (c->network->config->type == NETWORK_VIRTUAL && 
-               !strcmp(c->network->connection.data.virtual.ops->name, "admin")) {
-               virtual_network_recv_args(c->network, hostmask, "PRIVMSG", ADMIN_CHANNEL, msg, NULL);
-
-       } else {
-               char *nick = c->nick;
-               if (c->network->state) nick = c->network->state->me.nick;
-               client_send_args_ex(c, hostmask, "NOTICE", nick, msg, NULL);
-       }
-       g_free(hostmask);
+       h->send_fn(h, msg);
 
        g_free(msg);
 }
 
-static void add_network (struct client *c, char **args, void *userdata)
+static void add_network (admin_handle h, char **args, void *userdata)
 {
        struct network_config *nc;
 
        if (args[1] == NULL) {
-               admin_out(c, "No name specified");
+               admin_out(h, "No name specified");
+               return;
+       }
+
+       if (find_network(admin_get_global(h), args[1]) != NULL) {
+               admin_out(h, "Network with name `%s' already exists", args[1]);
                return;
        }
 
-       nc = network_config_init(c->network->global->config);
+       nc = network_config_init(admin_get_global(h)->config);
        g_free(nc->name); nc->name = g_strdup(args[1]);
-       load_network(c->network->global, nc);
+       load_network(admin_get_global(h), nc);
 
-       admin_out(c, "Network `%s' added. Use ADDSERVER to add a server to this network.", args[1]);
+       admin_out(h, "Network `%s' added. Use ADDSERVER to add a server to this network.", args[1]);
 }
 
-static void del_network (struct client *c, char **args, void *userdata)
+static void del_network (admin_handle h, char **args, void *userdata)
 {
        struct network *n;
 
        if (args[1] == NULL) {
-               admin_out(c, "Not enough parameters");
+               admin_out(h, "Not enough parameters");
                return;
        }
 
-       n = find_network(c->network->global, args[1]);
+       n = find_network(admin_get_global(h), args[1]);
        if (n == NULL) {
-               admin_out(c, "No such network `%s'", args[1]);
+               admin_out(h, "No such network `%s'", args[1]);
                return;
        }
 
        disconnect_network(n);
 
-       admin_out(c, "Network `%s' deleted", args[1]);
+       admin_out(h, "Network `%s' deleted", args[1]);
 }
 
-static void add_server (struct client *c, char **args, void *userdata)
+static void add_server (admin_handle h, char **args, void *userdata)
 {
        struct network *n;
        struct tcp_server_config *s;
        char *t;
 
        if(!args[1] || !args[2]) {
-               admin_out(c, "Not enough parameters");
+               admin_out(h, "Not enough parameters");
                return;
        }
 
-       n = find_network(c->network->global, args[1]);
+       n = find_network(admin_get_global(h), args[1]);
 
        if (!n) {
-               admin_out(c, "No such network '%s'", args[1]);
+               admin_out(h, "No such network '%s'", args[1]);
                return;
        }
 
        if (n->config->type != NETWORK_TCP) {
-               admin_out(c, "Not a TCP/IP network!");
+               admin_out(h, "Not a TCP/IP network!");
                return;
        }
 
@@ -125,245 +186,250 @@ static void add_server (struct client *c, char **args, void *userdata)
 
        n->config->type_settings.tcp_servers = g_list_append(n->config->type_settings.tcp_servers, s);
 
-       admin_out(c, "Server added to `%s'", args[1]);
+       admin_out(h, "Server added to `%s'", args[1]);
 }
 
-static void com_connect_network (struct client *c, char **args, void *userdata)
+static void com_connect_network (admin_handle h, char **args, void *userdata)
 {
        struct network *s;
        if(!args[1]) {
-                admin_out(c, "No network specified");
+                admin_out(h, "No network specified");
                 return;
        }
 
-       s = find_network(c->network->global, args[1]);
+       s = find_network(admin_get_global(h), args[1]);
 
        if (!s) {
-               admin_out(c, "No such network `%s'", args[1]);
+               admin_out(h, "No such network `%s'", args[1]);
                return;
        }
 
        switch (s->connection.state) {
                case NETWORK_CONNECTION_STATE_NOT_CONNECTED:
-                       admin_out(c, "Connecting to `%s'", args[1]);
+                       admin_out(h, "Connecting to `%s'", args[1]);
                        connect_network(s);
                        break;
                case NETWORK_CONNECTION_STATE_RECONNECT_PENDING:
-                       admin_out(c, "Forcing reconnect to `%s'", args[1]);
+                       admin_out(h, "Forcing reconnect to `%s'", args[1]);
                        disconnect_network(s);
                        network_select_next_server(s);
                        connect_network(s);
                        break;
                case NETWORK_CONNECTION_STATE_LOGIN_SENT:
-                       admin_out(c, "Connect to `%s' already in progress", args[1]);
+                       admin_out(h, "Connect to `%s' already in progress", args[1]);
                        break;
                case NETWORK_CONNECTION_STATE_MOTD_RECVD:
-                       admin_out(c, "Already connected to `%s'", args[1]);
+                       admin_out(h, "Already connected to `%s'", args[1]);
                        break;
        }
 }
 
-static void com_disconnect_network (struct client *c, char **args, void *userdata)
+static void com_disconnect_network (admin_handle h, char **args, void *userdata)
 {
        struct network *n;
-       if(!args[1])n = c->network;
-       else {
-               n = find_network(c->network->global, args[1]);
+
+       n = admin_get_network(h);
+
+       if (args[1] != NULL) {
+               n = find_network(admin_get_global(h), args[1]);
                if(!n) {
-                       admin_out(c, "Can't find active network with that name");
+                       admin_out(h, "Can't find active network with that name");
                        return;
                }
        }
 
        if (n->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED) {
-               admin_out(c, "Already disconnected from `%s'", args[1]);
+               admin_out(h, "Already disconnected from `%s'", args[1]);
        } else {
-               admin_out(c, "Disconnecting from `%s'", args[1]);
+               admin_out(h, "Disconnecting from `%s'", args[1]);
                disconnect_network(n);
        }
 }
 
-static void com_next_server (struct client *c, char **args, void *userdata) {
+static void com_next_server (admin_handle h, char **args, void *userdata) 
+{
        struct network *n;
        const char *name;
+
+
        if(args[1] != NULL) {
                name = args[1];
-               n = find_network(c->network->global, args[1]);
+               n = find_network(admin_get_global(h), args[1]);
        } else {
-               n = c->network;
-               name = n->name;
+               n = admin_get_network(h);
+               name = n->info.name;
        }
        if(!n) {
-               admin_out(c, "%s: Not connected", name);
+               admin_out(h, "%s: Not connected", name);
        } else {
-               admin_out(c, "%s: Reconnecting", name);
+               admin_out(h, "%s: Reconnecting", name);
                disconnect_network(n);
                network_select_next_server(n);
                connect_network(n);
        }
 }
 
-static void com_save_config (struct client *c, char **args, void *userdata)
+static void com_save_config (admin_handle h, char **args, void *userdata)
 { 
        const char *adm_dir;
-       global_update_config(c->network->global);
-       adm_dir = args[1]?args[1]:c->network->global->config->config_dir; 
-       save_configuration(c->network->global->config, adm_dir);
-       admin_out(c, "Configuration saved in %s", adm_dir);
+       global_update_config(admin_get_global(h));
+       adm_dir = args[1]?args[1]:admin_get_global(h)->config->config_dir; 
+       save_configuration(admin_get_global(h)->config, adm_dir);
+       admin_out(h, "Configuration saved in %s", adm_dir);
 }
 
-static void help (struct client *c, char **args, void *userdata)
-{
-       GList *gl = commands;
-       char *tmp;
-       char **details;
-       int i;
 
-       if(args[1]) {
-               admin_out(c, "Details for command %s:", args[1]);
-       } else {
-               admin_out(c, "The following commands are available:");
-       }
-       while(gl) {
-               struct admin_command *cmd = (struct admin_command *)gl->data;
-               if(args[1]) {
-                       if(!g_strcasecmp(args[1], cmd->name)) {
-                               if(cmd->help_details != NULL) {
-                                       details = g_strsplit(cmd->help_details, "\n", 0);
-                                       for(i = 0; details[i] != NULL; i++) {
-                                               admin_out(c, details[i]);
-                                       }
-                                       return;
-                               } else {
-                                       admin_out(c, "Sorry, no help for %s available", args[1]);
-                               }
-                       }
-               } else {
-                       if(cmd->help != NULL) {
-                               tmp = g_strdup_printf("%s%s     %s",cmd->name,g_strnfill(longest_command - strlen(cmd->name),' '),cmd->help);
-                               admin_out(c, tmp);
-                               g_free(tmp);
-                       } else {
-                               admin_out(c, cmd->name);
-                       }
-               }
-               gl = gl->next;
-       }
-       if(args[1]) {
-               admin_out(c, "Unknown command");
-       }
-}
 
-static void list_networks(struct client *c, char **args, void *userdata)
+static void list_networks(admin_handle h, char **args, void *userdata)
 {
        GList *gl;
-       for (gl = c->network->global->networks; gl; gl = gl->next) {
+       for (gl = admin_get_global(h)->networks; gl; gl = gl->next) {
                struct network *n = gl->data;
 
                switch (n->connection.state) {
                case NETWORK_CONNECTION_STATE_NOT_CONNECTED:
-                       admin_out(c, ("%s: Not connected"), n->name);
+                       if (n->connection.data.tcp.last_disconnect_reason)
+                               admin_out(h, "%s: Not connected: %s", n->info.name, 
+                                                 n->connection.data.tcp.last_disconnect_reason);
+                       else
+                               admin_out(h, "%s: Not connected", n->info.name);
                        break;
                case NETWORK_CONNECTION_STATE_RECONNECT_PENDING:
-                       admin_out(c, ("%s: Reconnecting"), n->name);
+                       admin_out(h, "%s: Reconnecting", n->info.name);
                        break;
                case NETWORK_CONNECTION_STATE_LOGIN_SENT:
                case NETWORK_CONNECTION_STATE_MOTD_RECVD:
-                       admin_out(c, ("%s: connected"), n->name);
+                       admin_out(h, "%s: connected", n->info.name);
                        break;
                }
        }
 }
 
-static void detach_client(struct client *c, char **args, void *userdata)
+static void detach_client(admin_handle h, char **args, void *userdata)
 {
+       struct client *c = admin_get_client(h);
+
        disconnect_client(c, "Client exiting");
 }
 
-static void dump_joined_channels(struct client *c, char **args, void *userdata)
+static void dump_joined_channels(admin_handle h, char **args, void *userdata)
 {
        struct network *n;
        GList *gl;
 
        if (args[1] != NULL) {
-               n = find_network(c->network->global, args[1]);
+               n = find_network(admin_get_global(h), args[1]);
                if(n == NULL) {
-                       admin_out(c, "Can't find network '%s'", args[1]);
+                       admin_out(h, "Can't find network '%s'", args[1]);
                        return;
                }
        } else {
-               n = c->network;
+               n = admin_get_network(h);
        }
 
        if (!n->state) {
-               admin_out(c, "Network '%s' not connected", n->name);
+               admin_out(h, "Network '%s' not connected", n->info.name);
                return;
        }
 
        for (gl = n->state->channels; gl; gl = gl->next) {
                struct channel_state *ch = (struct channel_state *)gl->data;
-               admin_out(c, "%s", ch->name);
+               admin_out(h, "%s", ch->name);
        }
 }
 
 #ifdef DEBUG
-static void do_abort(struct client *c, char **args, void *userdata)
+static void do_abort(admin_handle h, char **args, void *userdata)
 {
        abort();
 }
 #endif
 
-static void handle_die(struct client *c, char **args, void *userdata)
+static void handle_die(admin_handle h, char **args, void *userdata)
 {
        exit(0);
 }
 
 static GHashTable *markers = NULL;
 
-static void repl_command(struct client *c, char **args, void *userdata)
+static void repl_command(admin_handle h, char **args, void *userdata)
 {
-       struct linestack_marker *lm = g_hash_table_lookup(markers, c->network);
+       struct linestack_marker *lm;
+       struct network *n;
+       
+       n = admin_get_network(h);
+
+       lm = g_hash_table_lookup(markers, n);
 
-       if (c->network->linestack == NULL) {
-               admin_out(c, "No backlog available. Perhaps the connection to the network is down?");
+       if (n->linestack == NULL) {
+               admin_out(h, "No backlog available. Perhaps the connection to the network is down?");
                return;
        }
 
        if(!args[1]) {
-               admin_out(c, "Sending backlog for network '%s'", c->network->name);
+               admin_out(h, "Sending backlog for network '%s'", n->info.name);
 
-               linestack_send(c->network->linestack, lm, NULL, c);
+               if (n->global->config->report_time)
+                       linestack_send_timed(n->linestack, lm, NULL, admin_get_client(h));
+               else
+                       linestack_send(n->linestack, lm, NULL, admin_get_client(h));
 
-               g_hash_table_replace(markers, c->network, linestack_get_marker(c->network->linestack));
+               g_hash_table_replace(markers, n, linestack_get_marker(n->linestack));
 
                return;
        } 
 
        /* Backlog for specific nick/channel */
-       admin_out(c, "Sending backlog for channel %s", args[1]);
+       admin_out(h, "Sending backlog for channel %s", args[1]);
 
-       if (c->network->global->config->report_time)
-               linestack_send_object_timed(c->network->linestack, args[1], lm, NULL, c);
+       if (n->global->config->report_time)
+               linestack_send_object_timed(n->linestack, args[1], lm, NULL, 
+                                                                       admin_get_client(h));
        else
-               linestack_send_object(c->network->linestack, args[1], lm, NULL, c);
+               linestack_send_object(n->linestack, args[1], lm, NULL, 
+                                                         admin_get_client(h));
 
-       g_hash_table_replace(markers, c->network, linestack_get_marker(c->network->linestack));
+       g_hash_table_replace(markers, n, linestack_get_marker(n->linestack));
 }
 
-static void handle_charset(struct client *c, char **args, void *userdata)
+static void cmd_log_level(admin_handle h, char **args, void *userdata)
 {
-       GError *error = NULL;
+       extern enum log_level current_log_level;
+       
+       if (args[1] == NULL) 
+               admin_out(h, "Current log level: %d", current_log_level);
+       else {
+               int x = atoi(args[1]);
+               if (x < 0 || x > 5) 
+                       admin_out(h, "Invalid log level %d", x);
+               else { 
+                       current_log_level = x;
+                       admin_out(h, "Log level changed to %d", x);
+               }
+       }
+}
+
+static void handle_charset(admin_handle h, char **args, void *userdata)
+{
+       struct client *c;
 
        if (args[1] == NULL) {
-               admin_out(c, "No charset specified");
+               admin_out(h, "No charset specified");
                return;
        }
 
+       c = admin_get_client(h);
+
        if (!client_set_charset(c, args[1])) {
-               admin_out(c, "Error setting charset: %s", error->message);
+               admin_out(h, "Error setting charset: %s", args[1]);
        }
 }
 
+static void cmd_echo(admin_handle h, char **args, void *userdata)
+{
+       admin_out(h, "%s", args[1]);
+}
+
 static gint cmp_cmd(gconstpointer a, gconstpointer b)
 {
        const struct admin_command *cmda = a, *cmdb = b;
@@ -373,131 +439,213 @@ static gint cmp_cmd(gconstpointer a, gconstpointer b)
 
 void register_admin_command(const struct admin_command *cmd)
 {
-       commands = g_list_insert_sorted(commands, g_memdup(cmd, sizeof(*cmd)), cmp_cmd);
+       admin_commands = g_list_insert_sorted(admin_commands, g_memdup(cmd, sizeof(*cmd)), cmp_cmd);
        if (strlen(cmd->name) > longest_command) longest_command = strlen(cmd->name);
 }
 
 void unregister_admin_command(const struct admin_command *cmd)
 {
-       commands = g_list_remove(commands, cmd);
+       admin_commands = g_list_remove(admin_commands, cmd);
 }
 
-static gboolean process_cmd(struct client *c, const struct line *l, int cmdoffset)
+gboolean process_cmd(admin_handle h, const char *cmd)
 {
-       char *tmp, **args = NULL;
-       int i;
+       char **args = NULL;
        GList *gl;
 
-       if(!l->args[cmdoffset]) {
-               admin_out(c, "Please specify a command. Use the 'help' command to get a list of available commands");
+       if (!cmd) {
+               admin_out(h, "Please specify a command. Use the 'help' command to get a list of available commands");
                return TRUE;
        }
 
-       tmp = g_strdup(l->args[cmdoffset]);
+       args = g_strsplit(cmd, " ", 0);
 
-       if(l->args[cmdoffset+1]) {
-               /* Add everything after l->args[cmdoffset] to tmp */
-               for(i = cmdoffset+1; l->args[i]; i++) {
-                       char *oldtmp = tmp;
-                       tmp = g_strdup_printf("%s %s", oldtmp, l->args[i]);
-                       g_free(oldtmp);
-               }
+       if (!args[0]) {
+               admin_out(h, "Please specify a command. Use the 'help' command to get a list of available commands");
+               return TRUE;
        }
 
-       args = g_strsplit(tmp, " ", 0);
-
        /* Ok, arguments are processed now. Execute the corresponding command */
-       for (gl = commands; gl; gl = gl->next) {
+       for (gl = admin_commands; gl; gl = gl->next) {
                struct admin_command *cmd = (struct admin_command *)gl->data;
                if(!g_strcasecmp(cmd->name, args[0])) {
-                       cmd->handler(c, args, cmd->userdata);
+                       cmd->handler(h, args, cmd->userdata);
                        g_strfreev(args);
-                       g_free(tmp);
                        return TRUE;
                }
        }
 
-       admin_out(c, ("Can't find command '%s'. Type 'help' for a list of available commands. "), args[0]);
+       admin_out(h, "Can't find command '%s'. Type 'help' for a list of available commands. ", args[0]);
 
        g_strfreev(args);
-       g_free(tmp);
 
        return TRUE;
 }
 
 gboolean admin_process_command(struct client *c, struct line *l, int cmdoffset)
 {
-       l->is_private = 1;
-       return process_cmd(c, l, cmdoffset);
+       int i;
+       char *tmp = g_strdup(l->args[cmdoffset]);
+       gboolean ret;
+       struct admin_handle ah;
+
+       /* Add everything after l->args[cmdoffset] to tmp */
+       for(i = cmdoffset+1; l->args[i]; i++) {
+               char *oldtmp = tmp;
+               tmp = g_strdup_printf("%s %s", oldtmp, l->args[i]);
+               g_free(oldtmp);
+       }
+
+       ah.send_fn = privmsg_admin_out;
+       ah.client = c;
+       ah.network = c->network;
+       ah.global = c->network->global;
+       ret = process_cmd(&ah, tmp);
+
+       g_free(tmp);
+
+       return ret;
 }
 
 static gboolean admin_net_init(struct network *n)
 {
-       struct channel_state *cs = g_new0(struct channel_state, 1);
-       struct channel_nick *user_nick = g_new0(struct channel_nick, 1);
-       struct channel_nick *admin_nick = g_new0(struct channel_nick, 1);
-       struct network_nick *admin_nnick = g_new0(struct network_nick, 1);
        char *hostmask;
-       
-       /* Channel */
-       cs->name = g_strdup(ADMIN_CHANNEL);
-       cs->topic = g_strdup("CtrlProxy administration channel | Type `help' for more information");
-       cs->network = n->state;
-
-       /* The users' user */
-       user_nick->global_nick = &n->state->me;
-       user_nick->channel = cs;
-       user_nick->mode = ' ';
-
-       cs->nicks = g_list_append(cs->nicks, user_nick);
-       n->state->me.channel_nicks = g_list_append(n->state->me.channel_nicks, user_nick);
-
-       /* CtrlProxy administrator */
-       /* global */
-       hostmask = g_strdup_printf("ctrlproxy!ctrlproxy@%s", n->name);
-       network_nick_set_hostmask(admin_nnick, hostmask);
-       g_free(hostmask);
-
-       admin_nnick->fullname = g_strdup("CtrlProxy Admin Tool");
-       admin_nnick->channel_nicks = g_list_append(admin_nnick->channel_nicks, admin_nick);
+       char *nicks;
 
-       n->state->nicks = g_list_append(n->state->nicks, admin_nnick);
+       hostmask = g_strdup_printf("ctrlproxy!ctrlproxy@%s", n->info.name);
+       
+       virtual_network_recv_args(n, n->state->me.hostmask, "JOIN", ADMIN_CHANNEL, NULL);
+       virtual_network_recv_response(n, RPL_TOPIC, ADMIN_CHANNEL, 
+               "CtrlProxy administration channel | Type `help' for more information",
+                                                         NULL);
+       nicks = g_strdup_printf("@ctrlproxy %s", n->config->nick);
 
-       /* channel */
-       admin_nick->global_nick = admin_nnick;
-       admin_nick->channel = cs;
-       admin_nick->mode = '@';
+       virtual_network_recv_response(n, RPL_NAMREPLY, "=", ADMIN_CHANNEL, nicks, NULL);
+       g_free(nicks);
+       virtual_network_recv_response(n, RPL_ENDOFNAMES, ADMIN_CHANNEL, "End of /NAMES list.", NULL);
 
-       cs->nicks = g_list_append(cs->nicks, admin_nick);
-       
-       n->state->channels = g_list_append(n->state->channels, cs);
+       g_free(hostmask);
 
        return TRUE;
 }
 
 static gboolean admin_to_server (struct network *n, struct client *c, const struct line *l)
 {
-       if (g_strcasecmp(l->args[0], "PRIVMSG") && g_strcasecmp(l->args[0], "NOTICE"))
+       if (!g_strcasecmp(l->args[0], "PRIVMSG") ||
+               !g_strcasecmp(l->args[0], "NOTICE")) {
+               struct admin_handle ah;
+
+               if (g_strcasecmp(l->args[0], n->state->me.nick) == 0) {
+                       virtual_network_recv_args(n, n->state->me.hostmask, l->args[0], l->args[1], NULL);
+                       return TRUE;
+               }
+
+               if (g_strcasecmp(l->args[1], ADMIN_CHANNEL) && 
+                       g_strcasecmp(l->args[1], "ctrlproxy")) {
+                       virtual_network_recv_response(n, ERR_NOSUCHNICK, l->args[1], "No such nick/channel", NULL);
+                       return TRUE;
+               }
+
+               ah.send_fn = network_admin_out;
+               ah.user_data = NULL;
+               ah.client = c;
+               ah.network = n;
+               ah.global = n->global;
+
+               return process_cmd(&ah, l->args[2]);
+       } else if (!g_strcasecmp(l->args[0], "ISON")) {
+               int i;
+               char *tmp;
+               GList *gl = NULL;
+
+               if (l->args[1] == NULL) {
+                       virtual_network_recv_response(n, ERR_NEEDMOREPARAMS, l->args[0], "Not enough params", NULL);
+                       return TRUE;
+               }
+
+               for (i = 1; l->args[i]; i++) {
+                       if (!g_strcasecmp(l->args[i], "ctrlproxy") ||
+                               !g_strcasecmp(l->args[i], n->state->me.nick)) {
+                               gl = g_list_append(gl, l->args[i]);
+                       }
+               }
+               virtual_network_recv_response(n, RPL_ISON, tmp = list_make_string(gl), NULL);
+               g_free(tmp);
+               g_list_free(gl);
                return TRUE;
+       } else if (!g_strcasecmp(l->args[0], "USERHOST")) {
+               GList *gl = NULL;
+               char *tmp;
+               int i;
+
+               if (l->args[1] == NULL) {
+                       virtual_network_recv_response(n, ERR_NEEDMOREPARAMS, l->args[0], "Not enough params", NULL);
+                       return TRUE;
+               }
 
-       if (g_strcasecmp(l->args[0], n->state->me.nick) == 0) {
-               virtual_network_recv_args(c->network, n->state->me.hostmask, l->args[0], l->args[1], NULL);
+               for (i = 1; l->args[i]; i++) {
+                       if (!g_strcasecmp(l->args[i], "ctrlproxy")) {
+                               gl = g_list_append(gl, g_strdup_printf("%s=+%s", l->args[i], get_my_hostname()));
+                       }
+                       if (!g_strcasecmp(l->args[i], n->state->me.nick)) {
+                               gl = g_list_append(gl, g_strdup_printf("%s=+%s", l->args[i], n->state->me.hostname));
+                       }
+               }
+
+               virtual_network_recv_response(n, RPL_ISON, tmp = list_make_string(gl), NULL);
+               g_free(tmp);
+               while (gl) {
+                       g_free(gl->data);
+                       gl = g_list_remove(gl, gl->data);
+               }
                return TRUE;
-       }
+       } else if (!g_strcasecmp(l->args[0], "QUIT")) {
+               return TRUE;
+       } else if (!g_strcasecmp(l->args[0], "MODE")) {
+               /* FIXME: Do something here ? */
+               return TRUE;
+       } else if (!g_strcasecmp(l->args[0], "WHO")) {
+               if (!strcmp(l->args[1], ADMIN_CHANNEL) || 
+                       !strcmp(l->args[1], "ctrlproxy")) {
+                       virtual_network_recv_response(n, RPL_WHOREPLY, ADMIN_CHANNEL, 
+                                                                         "ctrlproxy",
+                                                                         get_my_hostname(),
+                                                                         get_my_hostname(),
+                                                                         "ctrlproxy",
+                                                                         "H",
+                                                                         "0 CtrlProxy user",
+                                                                         NULL);
+               }
+               if (!strcmp(l->args[1], ADMIN_CHANNEL) ||
+                       !strcmp(l->args[1], n->state->me.nick)) {
+                       char *fullname = g_strdup_printf("0 %s", n->state->me.fullname);
+                       virtual_network_recv_response(n, RPL_WHOREPLY, ADMIN_CHANNEL, 
+                                                                         n->state->me.username,
+                                                                         n->state->me.hostname,
+                                                                         get_my_hostname(),
+                                                                         n->state->me.nick,
+                                                                         "H",
+                                                                         fullname,
+                                                                         NULL);
+                       g_free(fullname);
+               }
 
-       if (g_strcasecmp(l->args[1], ADMIN_CHANNEL) && 
-               g_strcasecmp(l->args[1], "ctrlproxy")) {
-               virtual_network_recv_args(c->network, NULL, "401", l->args[1], "No such nick/channel", NULL);
+               virtual_network_recv_response(n, RPL_ENDOFWHO, l->args[1], "End of /WHO list.", NULL);
+
+               return TRUE;
+       } else {
+               virtual_network_recv_response(n, ERR_UNKNOWNCOMMAND, l->args[0], "Unknown command", NULL);
+               log_global(LOG_TRACE, "Unhandled command `%s' to admin network", 
+                                  l->args[0]);
                return TRUE;
        }
-
-       return process_cmd(c, l, 2);
 }
 
 struct virtual_network_ops admin_network = {
        "admin", admin_net_init, admin_to_server, NULL
 };
 
+
 void admin_log(enum log_level level, const struct network *n, const struct client *c, const char *data)
 {
        extern struct global *my_global;
@@ -521,7 +669,7 @@ void admin_log(enum log_level level, const struct network *n, const struct clien
        tmp = g_strdup_printf("%s%s%s%s%s%s", 
                                                  data, 
                                                  n?" (":"",
-                                                 n?n->name:"", 
+                                                 n?n->info.name:"", 
                                                  c?"/":"",
                                                  c?c->description:"",
                                                  n?")":"");
@@ -532,7 +680,7 @@ void admin_log(enum log_level level, const struct network *n, const struct clien
                if (network->connection.data.virtual.ops != &admin_network)
                        continue;
 
-               hostmask = g_strdup_printf("ctrlproxy!ctrlproxy@%s", network->name);
+               hostmask = g_strdup_printf("ctrlproxy!ctrlproxy@%s", network->info.name);
                l = irc_parse_line_args(hostmask, "PRIVMSG", ADMIN_CHANNEL, tmp, NULL); 
                g_free(hostmask);
                
@@ -546,25 +694,25 @@ void admin_log(enum log_level level, const struct network *n, const struct clien
        entered = FALSE;
 }
 
-
-
 const static struct admin_command builtin_commands[] = {
-       { "ADDNETWORK", add_network, "<name>", "Add new network with specified name" },
-       { "ADDSERVER", add_server, "<network> <host>[:<port>] [<password>]", "Add server to network" },
-       { "BACKLOG", repl_command, "[channel]", "Send backlogs for this network or a channel, if specified" },
-       { "CONNECT", com_connect_network, "<network>", "Connect to specified network. Forces reconnect when waiting." },
-       { "DELNETWORK", del_network, "<network>", "Remove specified network" },
-       { "NEXTSERVER", com_next_server, "[network]", "Disconnect and use to the next server in the list" },
-       { "CHARSET", handle_charset, "<charset>", "Change client charset" },
-       { "DIE", handle_die, "", "Exit ctrlproxy" },
-       { "DISCONNECT", com_disconnect_network, "<network>", "Disconnect specified network" },
-       { "LISTNETWORKS", list_networks, "", "List current networks and their status" },
-       { "SAVECONFIG", com_save_config, "<name>", "Save current XML configuration to specified file" },
-       { "DETACH", detach_client, "", "Detach current client" },
-       { "HELP", help, "[command]", "This help command" },
-       { "DUMPJOINEDCHANNELS", dump_joined_channels, "[network]", NULL, NULL },
+       { "ADDNETWORK", add_network },
+       { "ADDSERVER", add_server },
+       { "BACKLOG", repl_command },
+       { "CONNECT", com_connect_network },
+       { "DELNETWORK", del_network },
+       { "ECHO", cmd_echo },
+       { "LOG_LEVEL", cmd_log_level },
+       { "NEXTSERVER", com_next_server },
+       { "CHARSET", handle_charset },
+       { "DIE", handle_die },
+       { "DISCONNECT", com_disconnect_network },
+       { "LISTNETWORKS", list_networks },
+       { "SAVECONFIG", com_save_config },
+       { "DETACH", detach_client },
+       { "HELP", cmd_help },
+       { "DUMPJOINEDCHANNELS", dump_joined_channels },
 #ifdef DEBUG
-       { "ABORT", do_abort, "", NULL, NULL },
+       { "ABORT", do_abort },
 #endif
        { NULL }
 };