#include "internals.h"
#include <string.h>
#include "admin.h"
+#include "help.h"
+#include "irc.h"
-#define ADMIN_CHANNEL "#ctrlproxy"
+help_t *help;
-static GList *commands = NULL;
-static guint longest_command = 0;
+#define ADMIN_CHANNEL "#ctrlproxy"
-struct admin_handle
-{
- struct global *global;
- struct client *client;
- struct network *network;
- void *user_data;
- void (*send_fn) (struct admin_handle *, const char *data);
-};
+GList *admin_commands = NULL;
+guint longest_command = 0;
static void privmsg_admin_out(admin_handle h, const char *data)
{
char *nick = c->nick;
char *hostmask;
- hostmask = g_strdup_printf("ctrlproxy!ctrlproxy@%s", c->network->name);
+ 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);
struct client *c = h->client;
char *hostmask;
- hostmask = g_strdup_printf("ctrlproxy!ctrlproxy@%s", c->network->name);
+ 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;
+ }
+}
+
struct client *admin_get_client(admin_handle h)
{
return h->client;
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(admin_get_global(h)->config);
g_free(nc->name); nc->name = g_strdup(args[1]);
load_network(admin_get_global(h), nc);
n = find_network(admin_get_global(h), args[1]);
} else {
n = admin_get_network(h);
- name = n->name;
+ name = n->info.name;
}
if(!n) {
admin_out(h, "%s: Not connected", name);
admin_out(h, "Configuration saved in %s", adm_dir);
}
-static void help (admin_handle h, char **args, void *userdata)
-{
- GList *gl = commands;
- char *tmp;
- char **details;
- int i;
- if(args[1]) {
- admin_out(h, "Details for command %s:", args[1]);
- } else {
- admin_out(h, "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(h, details[i]);
- }
- return;
- } else {
- admin_out(h, "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(h, tmp);
- g_free(tmp);
- } else {
- admin_out(h, cmd->name);
- }
- }
- gl = gl->next;
- }
- if(args[1]) {
- admin_out(h, "Unknown command");
- }
-}
static void list_networks(admin_handle h, char **args, void *userdata)
{
switch (n->connection.state) {
case NETWORK_CONNECTION_STATE_NOT_CONNECTED:
- admin_out(h, ("%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(h, ("%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(h, ("%s: connected"), n->name);
+ admin_out(h, "%s: connected", n->info.name);
break;
}
}
}
if (!n->state) {
- admin_out(h, "Network '%s' not connected", n->name);
+ admin_out(h, "Network '%s' not connected", n->info.name);
return;
}
}
if(!args[1]) {
- admin_out(h, "Sending backlog for network '%s'", n->name);
+ admin_out(h, "Sending backlog for network '%s'", n->info.name);
- linestack_send(n->linestack, lm, NULL, admin_get_client(h));
+ 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, n, linestack_get_marker(n->linestack));
g_hash_table_replace(markers, n, linestack_get_marker(n->linestack));
}
+static void cmd_log_level(admin_handle h, char **args, void *userdata)
+{
+ 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)
{
- GError *error = NULL;
struct client *c;
- c = admin_get_client(h);
-
if (args[1] == NULL) {
admin_out(h, "No charset specified");
return;
}
+ c = admin_get_client(h);
+
if (!client_set_charset(c, args[1])) {
- admin_out(h, "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;
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(admin_handle h, const char *cmd)
+gboolean process_cmd(admin_handle h, const char *cmd)
{
char **args = NULL;
GList *gl;
args = g_strsplit(cmd, " ", 0);
+ if (!args[0]) {
+ admin_out(h, "Please specify a command. Use the 'help' command to get a list of available commands");
+ return TRUE;
+ }
+
/* 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(h, args, cmd->userdata);
tmp = g_strdup_printf("%s %s", oldtmp, l->args[i]);
g_free(oldtmp);
}
- l->is_private = 1;
ah.send_fn = privmsg_admin_out;
ah.client = c;
char *hostmask;
char *nicks;
- hostmask = g_strdup_printf("ctrlproxy!ctrlproxy@%s", n->name);
+ 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_args(n, NULL, "332", n->config->nick, ADMIN_CHANNEL,
+ 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);
- virtual_network_recv_args(n, NULL, "353", n->config->nick, "=", ADMIN_CHANNEL, nicks, NULL);
+ virtual_network_recv_response(n, RPL_NAMREPLY, "=", ADMIN_CHANNEL, nicks, NULL);
g_free(nicks);
- virtual_network_recv_args(n, NULL, "366", n->config->nick, ADMIN_CHANNEL, "End of /NAMES list.", NULL);
+ virtual_network_recv_response(n, RPL_ENDOFNAMES, ADMIN_CHANNEL, "End of /NAMES list.", NULL);
g_free(hostmask);
static gboolean admin_to_server (struct network *n, struct client *c, const struct line *l)
{
- struct admin_handle ah;
+ if (!g_strcasecmp(l->args[0], "PRIVMSG") ||
+ !g_strcasecmp(l->args[0], "NOTICE")) {
+ struct admin_handle ah;
- if (g_strcasecmp(l->args[0], "PRIVMSG") &&
- g_strcasecmp(l->args[0], "NOTICE")) {
- log_global(LOG_TRACE, "Unhandled command `%s' to admin network",
- l->args[0]);
- return TRUE;
- }
+ 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;
+ }
- 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);
+ 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;
+ }
+
+ 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));
+ }
+ }
- 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_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);
+ }
- ah.send_fn = network_admin_out;
- ah.user_data = NULL;
- ah.client = c;
- ah.network = n;
- ah.global = n->global;
+ virtual_network_recv_response(n, RPL_ENDOFWHO, l->args[1], "End of /WHO list.", NULL);
- return process_cmd(&ah, l->args[2]);
+ 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;
+ }
}
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;
tmp = g_strdup_printf("%s%s%s%s%s%s",
data,
n?" (":"",
- n?n->name:"",
+ n?n->info.name:"",
c?"/":"",
c?c->description:"",
n?")":"");
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);
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 }
};