/*
* commands.cpp
*
* (C) 2001-2002 Murat Deligonul
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "autoconf.h"
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <sys/resource.h>
#include "commands.h"
#include "server.h"
#include "dynbuff.h"
#include "general.h"
#include "conn.h"
#include "messages.h"
#include "ircaddr.h"
#include "debug.h"
/*******************************************************
* Stupid hacks to work around GCC 2.95.x compiler bugs
*******************************************************
* It chokes if it encounters to pointer-to-member function inside
* a struct, so if we detect buggy GCC, we just store function
* pointers in array, and replace relevant struct element with an
* array index */
#ifdef H
#undef H
#endif
#ifdef GCC_COMPILER_BUG
#warning Buggy GCC 2.95.x detected -- trying to work around ...
#define H(x,y) (x)
const cmd_handler_t handlers[] = {
&conn::do_registration_cmd, /* 0 */
&conn::do_login_cmd,
&conn::do_conn_cmd,
&conn::do_misc_cmd,
&conn::do_ident_cmd,
&conn::do_motd_cmd, /* 5 */
&conn::do_help_cmd,
&conn::do_vhost_cmd,
&conn::do_vhosts_cmd,
&conn::do_detach_cmd,
&conn::do_reattach_cmd, /* 10 */
&conn::do_disconnect_cmd,
&conn::do_log_cmd,
&conn::do_sessions_cmd,
&conn::do_traffic_cmd,
&conn::do_set_cmd, /* 15 */
&conn::do_echo_cmd,
&conn::do_save_cmd,
&conn::do_allowed_cmd,
&conn::do_debug_cmd,
&conn::do_version_cmd, /* 20 */
&conn::do_about_cmd,
&conn::do_ezb_cmd,
&conn::do_quit_cmd,
&conn::do_privmsg_cmd,
&conn::do_status_cmd, /* 25 */
&conn::do_rehash_cmd,
&conn::do_write_cmd,
&conn::do_hash_cmd,
&conn::do_adminmisc_cmd,
&conn::do_whois_cmd, /* 30 */
&conn::do_trace_cmd,
&conn::do_reload_cmd,
#ifdef __DEBUG__
&conn::do_dccsend_cmd, /* 33 */
#else
0, /* 33 */
#endif
0,
0, /* 35 -- reserved for future use ???*/
0,
0,
0,
0,
&conn::do_privmsg_incoming_cmd, /* 40 */
&conn::do_nick_incoming_cmd,
&conn::do_mode_incoming_cmd,
&conn::do_join_incoming_cmd,
&conn::do_part_incoming_cmd,
&conn::do_kick_incoming_cmd, /* 45 */
&conn::do_topic_incoming_cmd,
&conn::do_notice_incoming_cmd,
&conn::do_quit_incoming_cmd,
&conn::do_servinfo_cmd,
&conn::do_pong_incoming_cmd, /* 50 */
&conn::do_ping_incoming_cmd,
&conn::do_error_incoming_cmd
};
#else
#define H(x,y) (y)
#endif
/*
* The NDMs are No Direct Match (when Connected).
* 'ezb' command is directly matched when connected.
* the other commands are not.
*
* PPE = hack to make sure Ping/pong/error don't match
* on initial command scan
*/
const struct cmd command_table[] = {
/* str int-id req_flag bad_flag handler help */
/* -------------------------------------------------------------------------------------------------------------------------------------------*/
{ "USER", CMD_USER, 0, NDM | USERED | CONNECTING, H(0, &conn::do_registration_cmd), __HELP_ENTRY(user)},
{ "NICK", CMD_NICK, 0, NDM | CONNECTING, H(0, &conn::do_registration_cmd), __HELP_ENTRY(nick)},
{ "PASS", CMD_PASS, 0, NDM | PWED | CONNECTING, H(1, &conn::do_login_cmd), __HELP_ENTRY(pass)},
{ "LOGIN", CMD_LOGIN, 0, NDM | PWED | CONNECTING, H(1, &conn::do_login_cmd), __HELP_ENTRY(login)},
{ "CONN", CMD_CONN, REGISTERED, BOUNCED | CONNECTING, H(2, &conn::do_conn_cmd), __HELP_ENTRY(conn)},
{ "CANCEL" , CMD_CANCEL, REGISTERED | CONNECTING, 0, H(3, &conn::do_misc_cmd), __HELP_ENTRY(cancel)},
{ "IDENT", CMD_IDENT, REGISTERED, NDM | CONNECTING, H(4, &conn::do_ident_cmd), __HELP_ENTRY(ident)},
{ "MOTD", CMD_MOTD, REGISTERED, NDM | CONNECTING, H(5, &conn::do_motd_cmd), __HELP_ENTRY(motd)},
{ "HELP", CMD_HELP, REGISTERED, NDM | CONNECTING, H(6, &conn::do_help_cmd), __HELP_ENTRY(help)},
{ "INTERFACE", CMD_INTERFACE, REGISTERED, BOUNCED | CONNECTING, H(7, &conn::do_vhost_cmd), __HELP_ENTRY(vhost)},
{ "VHOST", CMD_INTERFACE, REGISTERED, BOUNCED | CONNECTING, H(7, &conn::do_vhost_cmd), __HELP_ENTRY(vhost)},
{ "VHOSTS", CMD_VHOSTS, REGISTERED, NDM | CONNECTING, H(8, &conn::do_vhosts_cmd), __HELP_ENTRY(vhosts)},
{ "DETACH", CMD_DETACH, REGISTERED, NDM | DETACHED | CONNECTING,H(9, &conn::do_detach_cmd), __HELP_ENTRY(detach)},
{ "REATTACH", CMD_REATTACH, REGISTERED, NDM | CONNECTING, H(10, &conn::do_reattach_cmd), __HELP_ENTRY(reattach)},
{ "ATTACH", CMD_REATTACH, REGISTERED, NDM | CONNECTING, H(10, &conn::do_reattach_cmd), __HELP_ENTRY(reattach)},
{ "DISCONNECT", CMD_DISCONNECT, REGISTERED | BOUNCED, NDM | CONNECTING, H(11, &conn::do_disconnect_cmd), __HELP_ENTRY(disconnect)},
{ "LOG", CMD_LOG, REGISTERED, NDM | CONNECTING, H(12, &conn::do_log_cmd), __HELP_ENTRY(log)},
{ "SESSIONS", CMD_SESSIONS, REGISTERED, CONNECTING, H(13, &conn::do_sessions_cmd), __HELP_ENTRY(sessions)},
{ "TRAFFIC", CMD_TRAFFIC, REGISTERED, NDM | CONNECTING, H(14, &conn::do_traffic_cmd), __HELP_ENTRY(traffic)},
{ "SET", CMD_SET, REGISTERED, NDM | CONNECTING, H(15, &conn::do_set_cmd), __HELP_ENTRY(set)},
{ "ECHO", CMD_ECHO, REGISTERED, NDM, H(16, &conn::do_echo_cmd), __HELP_ENTRY(echo)},
{ "SAVE", CMD_SAVE, REGISTERED, NDM | CONNECTING, H(17, &conn::do_save_cmd), __HELP_ENTRY(save)},
{ "ALLOWED", CMD_ALLOWED, REGISTERED, NDM | CONNECTING, H(18, &conn::do_allowed_cmd), __HELP_ENTRY(allowed)},
{ "DEBUG", CMD_DEBUG, REGISTERED, NDM, H(19, &conn::do_debug_cmd), __HELP_ENTRY(debug)},
{ "VERSION", CMD_VERSION, REGISTERED, NDM | CONNECTING, H(20, &conn::do_version_cmd), __HELP_ENTRY(version)},
{ "ABOUT", CMD_ABOUT, REGISTERED, NDM | CONNECTING, H(21, &conn::do_about_cmd), __HELP_ENTRY(about)},
{ "EZBOUNCE", CMD_EZBOUNCE, REGISTERED, CONNECTING, H(22, &conn::do_ezb_cmd), __HELP_ENTRY(ezbounce)},
{ "EZB", CMD_EZBOUNCE, REGISTERED, CONNECTING, H(22, &conn::do_ezb_cmd), __HELP_ENTRY(ezbounce)},
{ "QUIT", CMD_QUIT, REGISTERED | BOUNCED, CONNECTING, H(23, &conn::do_quit_cmd), __HELP_ENTRY(quit)},
{ "PRIVMSG", CMD_PRIVMSG, REGISTERED | BOUNCED, CONNECTING, H(24, &conn::do_privmsg_cmd), NULL},
{ "STATUS", CMD_STATUS, REGISTERED | ADMIN, NDM | CONNECTING, H(25, &conn::do_status_cmd), __HELP_ENTRY(status)},
{ "REHASH", CMD_REHASH, REGISTERED | ADMIN, NDM | CONNECTING, H(26, &conn::do_rehash_cmd), __HELP_ENTRY(rehash)},
{ "WRITE", CMD_WRITE, REGISTERED | ADMIN, NDM | CONNECTING, H(27, &conn::do_write_cmd), __HELP_ENTRY(write)},
{ "HASH", CMD_HASH, REGISTERED | ADMIN, NDM | CONNECTING, H(28, &conn::do_hash_cmd), __HELP_ENTRY(hash)},
{ "KILL", CMD_KILL, REGISTERED | ADMIN, NDM | CONNECTING, H(29, &conn::do_adminmisc_cmd), __HELP_ENTRY(kill)},
{ "DIE" , CMD_DIE, REGISTERED | ADMIN, NDM | CONNECTING, H(29, &conn::do_adminmisc_cmd), __HELP_ENTRY(die)},
{ "DIENOW", CMD_DIENOW, REGISTERED | ADMIN, NDM | CONNECTING, H(29, &conn::do_adminmisc_cmd), __HELP_ENTRY(dienow)},
{ "WHOIS", CMD_WHOIS, REGISTERED | ADMIN, NDM | CONNECTING, H(30, &conn::do_whois_cmd), __HELP_ENTRY(whois)},
{ "TRACE", CMD_TRACE, REGISTERED | ADMIN, NDM | CONNECTING, H(31, &conn::do_trace_cmd), __HELP_ENTRY(trace)},
{ "RELOAD", CMD_RELOAD, REGISTERED | ADMIN, NDM | CONNECTING, H(32, &conn::do_reload_cmd), __HELP_ENTRY(reload)},
#ifdef __DEBUG__
{ "CHATSEND", CMD_CHATSEND, REGISTERED | ADMIN, NDM | CONNECTING, H(33, &conn::do_dccsend_cmd), NULL},
{ "DCCSEND", CMD_DCCSEND, REGISTERED | ADMIN, NDM | CONNECTING, H(33, &conn::do_dccsend_cmd), NULL},
{ "ZOMBIFY", CMD_ZOMBIFY, REGISTERED, NDM, H(34, &conn::do_misc_cmd), NULL},
#endif
};
/* table for incoming traps */
const struct cmd incoming_command_table[] = {
{"PRIVMSG", INCOMING_PRIVMSG, 0, 0, H(40, &conn::do_privmsg_incoming_cmd)},
{"NICK", INCOMING_NICK, 0, 0, H(41, &conn::do_nick_incoming_cmd)},
{"MODE", INCOMING_MODE, DETACHED, 0, H(42, &conn::do_mode_incoming_cmd)},
{"JOIN", INCOMING_JOIN, 0, 0, H(43, &conn::do_join_incoming_cmd)},
{"PART", INCOMING_PART, 0, 0, H(44, &conn::do_part_incoming_cmd)},
{"KICK", INCOMING_KICK, 0, 0, H(45, &conn::do_kick_incoming_cmd)},
{"TOPIC", INCOMING_TOPIC, DETACHED, 0, H(46, &conn::do_topic_incoming_cmd)},
{"NOTICE", INCOMING_NOTICE, DETACHED, 0, H(47, &conn::do_notice_incoming_cmd)},
{"QUIT", INCOMING_QUIT, DETACHED, 0, H(48, &conn::do_quit_incoming_cmd)},
{"003", INCOMING_003, 0, GOTSERVINFO, H(49, &conn::do_servinfo_cmd)},
{"004", INCOMING_004, 0, GOTSERVINFO, H(49, &conn::do_servinfo_cmd)},
{"005", INCOMING_005, 0, GOTSERVINFO3, H(49, &conn::do_servinfo_cmd)},
{"PONG", INCOMING_PONG, 0, 0, H(50, &conn::do_pong_incoming_cmd)},
{"PING", INCOMING_PING, PPE, 0, H(51, &conn::do_ping_incoming_cmd)},
{"ERROR", INCOMING_ERROR, DETACHED | PPE, 0, H(52, &conn::do_error_incoming_cmd)},
};
int conn::init_command_hash(void)
{
int size = sizeof(command_table) / sizeof(struct cmd);
int i;
for (i = 0; i < size; ++i)
cmdhash.insert(&command_table[i]);
size = sizeof(incoming_command_table) / sizeof(struct cmd);
for (i = 0; i < size; ++i)
incoming_hash.insert(&incoming_command_table[i]);
return 1;
}
int htbl::insert(const struct cmd * c)
{
int idx = hash(c->msg) % buckets;
list_add(&table[idx], (void *) c);
return 1;
}
/* note: str must be capitalized */
const struct cmd * htbl::lookup(const char * str, int flags)
{
int idx = hash(str) % (buckets);
struct hashlist * l = &table[idx];
struct node * n;
const struct cmd * c;
lookups ++;
for (n = l->head; n; n = n->next)
{
c = (const struct cmd *) n->data;
/* No required flags for this command or we meet all required flags */
if (((flags & c->req_flags) == c->req_flags) || (!c->req_flags))
if (!strcmp(c->msg, str))
/* Check that we don't have any of the bad flags */
if (hits++, !c->bad_flags ||
!((flags & c->bad_flags) & c->bad_flags))
/* Found it */
return c;
}
return 0;
}
/*
* Show some info on 'c' */
void conn::show_whois(conn * c) const
{
char timebuff[10];
char flagbuff[10];
c->mkstat(flagbuff);
duration(ircproxy_time() - c->connect_time, 0, timebuff, sizeof(timebuff));
cprintf("---> Connection ID %d\r\n",c->id);
cprintf(" Online: %s\r\n",timebuff);
cprintf(" From: %s\r\n", inet_ntoa(c->client_saddr.sin_addr));
cprintf(" IRC Nick: %s\r\n", c->uinfo.irc->nick);
if (c->server)
cprintf(" Connected to: %s:%d (running %s)\r\n", inet_ntoa(c->serv_saddr.sin_addr), c->uinfo.port, c->uinfo.serverversion);
cprintf(" Flags: %s (0x%X)\r\n", flagbuff, c->stat);
cprintf("<--- [end of info]\r\n");
}
/* Command functions below.
* Type: int conn::do_XXX_cmd(int type, int argc, char * argv[])
* .. or CMDFUNC(XXX)
*
* Return:
* 0 - Unhandled, relay to IRC server
* 1 - Handled properly
* -1 - Handled, but destroy object immediately
*/
/* int */ CMDFUNC(registration) /* (int type, int argc, char ** argv) */
{
switch (type)
{
case CMD_USER:
/* Check for non-blank non-whitespace valid arguments */
if (!argc)
return 1;
uinfo.usercmd = my_strdup(argv[0]);
setf(USERED);
if (checkf(USERED) && checkf(NICKED))
goto success;
return 1;
case CMD_NICK:
{
if (!argc)
return 1;
if (strlen(argv[1]) > NICKNAME_LENGTH)
argv[1][NICKNAME_LENGTH] = 0;
/* First time */
delete[] uinfo.irc->nick;
uinfo.irc->nick = my_strdup(argv[1]);
/* If we all these set, then merely exit with updated nickname information */
if (checkf(USERED) && checkf(NICKED) && checkf(PWED))
{
client->printf(":%s!unknown@%s NICK :%s\r\n", uinfo.irc->nick, inet_ntoa(client_saddr.sin_addr), argv[1]);
printlog("NICK CHANGE: %s --> %s\n", addr(), argv[1]);
return 1;
}
setf(NICKED);
if (checkf(USERED) && checkf(NICKED))
goto success;
return 1;
}
}
return 1;
success:
/* Got password? */
if (!checkf(PWED))
cprintf("[awaiting login/pass command]\r\n");
/* Yes, we have password */
else if (checkf(PWED) && checkf(NICKED) && checkf(USERED))
on_client_connect(0);
return 1;
}
CMDFUNC(login)
{
char login[50], pw[50];
userdef * u = 0;
int as_ptr = 3;
if (!argc)
return 1;
safe_strcpy(login, argv[1], sizeof(login));
if (type == CMD_PASS)
{
if (gettok(login, pw, sizeof(pw), ':',2))
{
*strchr(login, ':') = 0;
as_ptr = 2;
} else {
strcpy(pw, login);
strcpy(login, "default");
as_ptr = 2;
}
} else {
if (argc < 2)
return 1;
safe_strcpy(pw, argv[2], sizeof(pw));
}
u = userdef::find(users, login);
if (!u)
{
cprintf("Invalid username: `%s'\r\n", login);
return 1;
}
char * host = inet_ntoa(client_saddr.sin_addr);
unsigned short port =ntohs(client_saddr.sin_port);
switch (u->add(this, &rulesets, pw, host, port) )
{
case 1:
/* ->add() gave us rulesets, now check them all */
char reason[100];
switch (ruleset::list_is_allowed_from(&rulesets,
host, port, reason, sizeof(reason)))
{
case 0:
u->remove(this);
goto no_authorization;
case -1:
cprintf(msg_banned, reason, reason);
printlog("Connection DENIED: (banned) from %s on port %d\n", host, port);
u->remove(this);
on_client_disconnect(0);
return -1;
}
/* Ready to go */
user = u;
config = new user_options(&user->cfg);
config->clear(OPT_PASSWORD);
/* for admins, just wipe out the list. they can connect wherever the
* hell they want, and don't need to bother with limits */
if (config->checkf(OPT_USER_IS_ADMIN))
rulesets.clear();
else
ruleset::list_register_from(&rulesets, host, port);
setf(PWED | FROM_RULESETS_REGISTERED);
/* Auto server: */
if (argv[as_ptr])
config->set(OPT_AUTOSERVER, argv[as_ptr]);
if (checkf(USERED) && checkf(NICKED))
on_client_connect(0);
return 1;
case 0:
cprintf("LOGIN: Incorrect password for `%s'\r\n", login);
printlog("Incorrect password from %s\n", addr());
if (++failed_passwords >= pcfg.max_failed_passwords
&& pcfg.max_failed_passwords)
{
cprintf(msg_too_many_failures);
printlog("... disconnected client for giving too many incorrect passwords!\n");
on_client_disconnect(0);
return -1;
}
break;
case -1:
no_authorization:
on_client_disconnect(0);
cprintf("No authorization\r\n");
printlog("DENIED: Connection from %s: No authorization\n", addr());
return -1;
}
return 0;
}
/* the /conn command */
CMDFUNC(conn)
{
char port[6] = "", * pass = 0;
int tmp,i=1,inssl=0;
u_short p;
if (!argc)
{
cprintf(msg_not_enuff_args, "CONN");
return 1;
}
if (strcasecmp("-ssl",argv[i]) == 0) {
if (argc<2) {
cprintf(msg_not_enuff_args, "CONN");
return 1;
}
inssl=1;
i++;
}
if (gettok(argv[i], port, sizeof(port), ':', 2))
*strchr(argv[i],':') = 0;
p = (strcmp(port, "") == 0) ? 6667: (u_short) atoi(port);
pass = argv[i+1];
/* Let's see if we CAN connect to this place */
if (!can_connect(argv[i], p))
{
printlog("Connection attempt DENIED: %s to %s:%d\n",
addr(), server, p);
return 1;
}
/* we can now try to connect.. */
uinfo.server = my_strdup(argv[i]);
printlog("Connection attempt: %s to %s:%d\n",
addr(), argv[i], p);
if ((tmp = do_connect(argv[i], p,pass,inssl)) == 1)
{
in_addr in = { config->get(PREF_VHOST) };
cprintf("[\002connecting to\002]: %s:%d\r\n", argv[i], p);
cprintf("[\002vhost\002]: %s\r\n", inet_ntoa(in));
cprintf("Use `/quote cancel' to bail out\r\n");
}
else
{
const char *err;
if (tmp == -1)
err = "unknown host";
else
err = strerror(errno);
cprintf(msg_conn_failed, argv[i], err);
printlog("Connection attempt FAILED: %s to %s:%d (failed prematurely): %s\n", addr(),
argv[i], p, err);
}
return 1;
}
/* ezb help system */
CMDFUNC(help)
{
if (!argc)
{
cprintf_multiline(__HELP_ENTRY(command_list));
return 1;
}
ToUpper(argv[1]);
const struct cmd * c = cmdhash.lookup(argv[1], 0);
if (!c || !c->help)
cprintf("HELP: no help found for command %s\n", argv[1]);
else
cprintf_multiline(c->help);
return 1;
}
/* VHOST & INTERFACE commands */
CMDFUNC(vhost)
{
if (!config->checkf(OPT_ENABLE_VHOST_COMMAND))
{
cprintf("This command has been disabled here.\r\n");
return 1;
}
if (!argc)
{
/*
* No arguments given: set vhost to default
* interface
*/
config->set(PREF_VHOST, user->cfg.get(PREF_VHOST));
cprintf("VHOST: Set vhost to default interface\r\n");
return 1;
}
/*
* Arguments given: check if the vhost exists in the table
* and then set it if so
* (ignore whats on the table for admin)
*/
if (!checkf(ADMIN))
{
list_iterator<char> vi(vhosts);
while (vi.has_next())
{
char * r = vi.next();
if (!strcasecmp(r, "all") ||
!strcasecmp(r, argv[1]))
goto found;
}
if (user->vhosts)
{
list_iterator<char> vi2(user->vhosts);
while (vi2.has_next())
{
char * r = vi2.next();
if (!strcasecmp(r, "all") ||
!strcasecmp(r, argv[1]))
goto found;
}
}
cprintf("Host is on not on my vhost list.\r\n");
return 1;
}
found:
/* Resolve interface and fill it into client->iface */
struct in_addr in;
switch (fill_in_addr(argv[1], &in))
{
case 0:
cprintf(msg_interface_failed, strerror(errno));
break;
case 1:
config->set(PREF_VHOST, in.s_addr);
cprintf(msg_interface_set, argv[1], inet_ntoa(in));
break;
case -1:
cprintf(msg_interface_failed, "unknown host\n");
default:
break;
}
return 1;
}
CMDFUNC(vhosts)
{
if (!config->checkf(OPT_ENABLE_VHOST_COMMAND))
{
cprintf("This command has been disabled here.\r\n");
return 1;
}
cprintf("Available virtual hosts:\r\n");
/* BLAH! duplicated code */
list_iterator<char> vi(vhosts);
int n = 0;
while (vi.has_next())
cprintf("%d) %s=\r\n", ++n, vi.next());
if (user->vhosts)
{
list_iterator<char> vi2(user->vhosts);
while (vi2.has_next())
cprintf("%d) %s\r\n", ++n, vi2.next());
}
cprintf("End of list.\r\n");
return 1;
}
CMDFUNC(motd)
{
if (pcfg.motdfile)
show_motd();
else
cprintf("There is no MOTD.\r\n");
return 1;
}
CMDFUNC(disconnect)
{
on_server_disconnect();
cprintf("Ok, broke your connection to the irc server.\r\n");
printlog("DISCONNECT: %s from IRC server by his request.\n",
addr());
return 1;
}
/*
* Set a fake ident. Fake idents require MDIDENTD to be running
*/
CMDFUNC(ident)
{
if (!config->checkf(OPT_ENABLE_FAKE_IDENTS))
{
cprintf("Sorry, fake idents have been disabled here.\r\n");
return 1;
}
if (argc)
{
config->set(PREF_FAKE_IDENT, argv[1]);
cprintf("Ok, set your fake ident to %s\r\n", argv[1]);
} else
cprintf(msg_not_enuff_args, "IDENT");
return 1;
}
CMDFUNC(quit)
{
if (config->decide(PREF_AUTO_DETACH))
{
do_auto_detach();
return 1;
}
return 0;
}
/* other commands */
CMDFUNC(misc)
{
switch (type)
{
case CMD_CANCEL:
on_server_connect(0x29A);
#ifdef __DEBUG__
case CMD_ZOMBIFY:
die();
#endif
}
return 1;
}
/**
** The gigantor LOG command
**/
CMDFUNC(log)
{
if (!config->checkf(OPT_LOG_OPTIONS))
{
cprintf("Detached-logging has been disabled here.\r\n");
return 1;
}
char *cmd = argv[1];
char buff[20];
/* Status Command */
if (!argc)
{
logfile::intflags_to_char(config->get(OPT_LOG_OPTIONS), buff);
cprintf("Your current log options are: %s\r\n", buff);
cprintf("Use /quote [ezb] LOG HELP for description of options\r\n");
cprintf("Use /quote [ezb] LOG LIST to list any log files ready for sending\r\n");
}
else if (strcasecmp(cmd, "LIST") == 0)
{
// if (loglist && !loglist->size())
// {
// delete loglist;
// loglist = 0;
// }
if ((!loglist) || (loglist && !loglist->size()))
cprintf("LOG LIST: List is empty\r\n");
else
goto list_logs;
}
else if (strcasecmp(cmd, "SEND") == 0 || strcasecmp(cmd,"VIEW") == 0)
{
bool view = 0;
if (strcasecmp(cmd,"VIEW") == 0)
view = 1;
if (argc < 2)
{
cprintf("LOG SEND/VIEW: Specify log file # or 'all'\r\n");
return 1;
}
if (!loglist || !loglist->size())
{
cprintf("LOG SEND/VIEW: There are no log files to send. Try '/quote [ezb] log find'\r\n");
return 1;
}
int idx = (unsigned int) atoi(argv[2]);
int num2send = 1;
if (strcasecmp(argv[2], "all") == 0)
{
cprintf("LOG SEND/VIEW: All requested: I will only send two at a time...\r\n");
num2send = 2;
idx = 1;
}
else if (!idx || idx > loglist->size())
{
cprintf("LOG: Log idx %d out of range: try <1-%d>\r\n", idx, loglist->size());
return 1;
}
char * file = 0;
int x = 0;
do {
if (idx > loglist->size())
break;
file = loglist->get(idx - 1);
if (logfile::is_locked(file))
{
cprintf("LOG: Unable to send log file %d: logfile is locked\r\n", idx);
continue;
}
dcc * d = dcc_send_file(file, logfile::fixup_logname(file), NULL, 1, view);
if (!d)
{
cprintf("LOG: Unable to send log file %d: %s\r\n", idx + 1, strerror(errno));
continue;
}
logfile::lock(file);
if (!view)
{
printlog("LOG SEND: Sent logfile %s to %s\n", file, addr());
cprintf("LOG SEND: Sent %s...\r\n", file);
}
else
{
printlog("LOG VIEW: Sent (as DCC CHAT) logfile %s to %s\n", file, addr());
cprintf("LOG VIEW: Sent %s as DCC CHAT\r\n", file);
}
x++;
} while (idx++, --num2send != 0);
cprintf("Sent %d log files...\r\n", x);
}
else if (strcasecmp(cmd, "SET") == 0)
{
if (argc < 2)
{
cprintf(msg_not_enuff_args, "LOG SET");
return 1;
}
if (!config->checkf(OPT_LOG_OPTIONS))
{
cprintf("LOG SET: Error: Detached-Logging is disabled here.\r\n");
return 1;
}
/* Set the log options. Reverse lookup the log options if the
* user stuck any invalid ones in there */
int tmp = logfile::charflags_to_int(argv[2]);
if (tmp == logfile::LOG_NONE)
{
cprintf("LOG SET: Ok, disabling logging for this session\r\n");
config->set(OPT_LOG_OPTIONS, logfile::LOG_NONE);
return 1;
}
if (!(tmp & logfile::LOG_ALL))
{
cprintf("LOG SET: Error: you did not specify a A,C,P option, assuming you want to log everything...\r\n");
tmp |= logfile::LOG_ALL;
}
/* Enforce config file restrictions */
if (!checkf(ADMIN))
{
if (!config->checkf(OPT_ENABLE_PRIVATE_LOGGING) && (tmp & logfile::LOG_PRIVATE))
{
cprintf("LOG SET: Error: logging of private messages is disabled here.\r\n");
tmp &= ~logfile::LOG_PRIVATE;
}
if (!config->checkf(OPT_ENABLE_PUBLIC_LOGGING) && (tmp & logfile::LOG_PUBLIC))
{
cprintf("LOG SET: Error: logging of public messages is disabled here.\r\n");
tmp &= ~logfile::LOG_PUBLIC;
}
if (!config->checkf(OPT_ENABLE_SEPERATE_LOGGING) && (tmp & logfile::LOG_SEPERATE))
{
cprintf("LOG SET: Error: logging to seperate files is disabled here.\r\n");
tmp &= ~logfile::LOG_SEPERATE;
}
}
config->set(OPT_LOG_OPTIONS, tmp);
logfile::intflags_to_char(tmp, buff);
cprintf("Set your log options to: %s\r\n", buff);
cprintf("Use /quote HELP LOG for description of options\r\n");
}
/* Look up old log files */
else if (strcasecmp(cmd, "FIND") == 0)
{
int num;
if (argc < 2 && checkf(DEFAULT_USER))
{
cprintf("Usage: /quote [ezb] LOG FIND <password for detached session>\r\n");
return 1;
}
cprintf("LOG FIND: Searching for old log files of user `%s' w/ password `%s'\r\n",
user->name, argv[2]);
/* List is destroyed each and every time */
if (loglist)
destroy_list(loglist, 1);
delete loglist;
loglist = new list<char>;
num = logfile::find_log_files(pcfg.logdir, user->name,
argv[2], -1, loglist, 10);
if (num)
{
cprintf("Found %d matching log files: \r\n", num);
list_logs:
list_iterator<char> li(loglist);
int i = 1;
while (li.has_next())
cprintf(" %d. %s\r\n", i++, li.next());
cprintf("---\r\n");
cprintf("Use:\r\n");
cprintf(" ----> /quote [ezb] log SEND <#> to retrieve a log file.\r\n");
cprintf(" ----> /quote [ezb] log VIEW <#> to view a log file thru DCC CHAT\r\n");
return 1;
}
// delete loglist;
// loglist = 0;
cprintf("LOG FIND: Couldn't find anything\r\n");
cprintf("LOG FIND: Be sure to check your logfile send list (with /quote [ezb] LOG LIST)\r\n");
}
else if (strcasecmp(cmd, "HELP") == 0)
cprintf_multiline(__HELP_ENTRY(log));
return 1;
}
/* ezb command
* bump up the pointer and call the lookup function
* again. we do a few checks on the result afterwards.. */
CMDFUNC(ezb)
{
const struct cmd * c;
int r = 0;
extern int mk_ppchar(char * string, char *(*buff)[MAX_PPCHAR_ARGS]);
if (!argc)
{
cprintf("EZB: usage `/quote ezb <command> [args]'\r\n");
return 1;
}
ToUpper(argv[1]);
c = cmdhash.lookup(argv[1], stat);
if (!c)
{
cprintf("EZB: bad or unknown command\r\n");
return 1;
}
else {
/* Special commands we should be aware of.. prevent double ezbounce command
* and don't engage the privmsg handler */
switch (c->id)
{
case CMD_EZBOUNCE:
case CMD_PRIVMSG:
cprintf("EZB: Hey! Cannot use %s command with 'ezb' command!\r\n", c->msg);
return 1;
case CMD_QUIT:
/* EZB QUIT: Force disconnection from server */
/* When auto-detach enabled, use this to send quit
to IRC server */
server->printf("QUIT :%s\r\n", gettok_ptr(argv[0], ' ', 2));
return 1;
}
/* just call the handler then */
/* but first assemble a new argv[] table */
char *__argv[MAX_PPCHAR_ARGS];
memset(__argv, 0, sizeof (__argv));
argc = mk_ppchar(no_leading(argv[0] + strlen(argv[1])),
&__argv);
DEBUG(" ###### HANDLER: %s(%d, %d, %s)\n", c->msg, c->id, argc, __argv[0]);
#ifdef GCC_COMPILER_BUG
r = (this->*(handlers[c->idx]))(c->id, argc, __argv);
#else
r = (this->*(c->handler))(c->id, argc, __argv);
#endif
delete[] __argv[0];
return r;
}
return 1;
}
/*
* Outgoing PRIVMSG handler -- Current does:
* Outgoing DCC proxying (and that's it) */
CMDFUNC(privmsg)
{
if (config->decide(PREF_DCC_OUT))
{
/* If we get here, the message is in the form
* PRIVMSG target :[CTCP] xxxx
*/
if (argc < 2 || argv[2][1] != '\001')
return 0;
char * args = gettok_ptr(argv[0], ' ', 3);
return do_ctcp(0, &argv[2][2], uinfo.irc->nick, argv[1], args);
}
return 0;
}
/************************
* ADMIN commands below
************************/
/*
* Dump information about the proxy
*/
CMDFUNC(status)
{
char timebuff[100];
duration(ircproxy_time() - start_time, 1, timebuff, sizeof(timebuff));
cprintf(msg_status_uptime, timestamp(), timebuff);
#ifdef HAVE_GETRUSAGE
{
struct rusage ru;
char buff[100];
getrusage(RUSAGE_SELF, &ru);
time_t mins = (int) ((ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) / 60);
time_t sex = (int) ((ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) - (mins * 60));
sprintf(buff, "%02d:%02d", (int) mins, (int) sex);
cprintf(msg_status_cputime, buff);
}
#endif
cprintf(msg_status_connections, num_active);
cprintf("Active DCCs: %d\r\n", dcc_list.size());
client->printf(msg_status_listheader, uinfo.irc->nick);
list_iterator<conn> iter(ircproxy_conn_list());
while (iter.has_next())
{
conn * c = iter.next();
char str_stat[9] = "?";
c->mkstat(str_stat);
duration(ircproxy_time() - c->connect_time, 0, timebuff, sizeof(timebuff));
/* ":" "info!ezb" " NOTICE %s :ID TIME NICK FROM TO STAT VHOST\n"; */
client->printf(":%s NOTICE %s :%-6d", EZBOUNCE_HEADER, uinfo.irc->nick,
c->id);
client->printf("%-6s",timebuff);
client->printf("%-10s ", (c->user ? strtrunc(c->user->name, 10) : "(none)"));
client->printf("%-15s ",inet_ntoa(c->client_saddr.sin_addr));
client->printf("%-15s ",(c->server) ?
inet_ntoa(c->serv_saddr.sin_addr) :
"(n/a)");
client->printf("%-9s\r\n", str_stat);
client->flushO();
}
return 1;
}
CMDFUNC(rehash)
{
printlog("%s is rehashing proxy configuration file..\n", addr());
switch (ircproxy_rehash())
{
case 1:
printlog("Rehash successful\n");
cprintf("REHASH successful\r\n");
set_dns_timeout(pcfg.max_dns_wait_time);
break;
case 0:
printlog("Rehash failed -- errors loading file: %s\n", strerror(errno));
cprintf("REHASH failed: %s\r\n", strerror(errno));
break;
default:
printlog("Rehash failed.\n");
cprintf("REHASH failed -- check log file for details\r\n");
break;
}
return 1;
}
/* Dump hashing stats */
CMDFUNC(hash)
{
struct __htbl::hash_stat_t ht;
extern struct __htbl::hash_stat_t cfght;
memset(&ht, 0, sizeof(ht));
if (!argc)
{
cprintf("Usage: HASH <1,2, or 3>\n");
return 1;
}
switch (atoi(argv[1]))
{
case 1:
cprintf("User Command Parser stats ...\r\n");
cmdhash.stat(&ht);
break;
case 2:
cprintf("Incoming Command Parser stats ...\r\n");
incoming_hash.stat(&ht);
break;
case 3:
cprintf("Config File Parser stats ...\r\n");
memcpy(&ht, &cfght, sizeof(ht));
break;
default:
cprintf("HASH: Unknown option... Use 1-3\r\n");
return 1;
}
cprintf(" Buckets: %d\n", ht.buckets);
cprintf(" In Use: %d (%.1f%%)\n", ht.size, (float) ht.size / ht.buckets * 100);
cprintf(" [empty]: %d (%.1f%%)\n", ht.sizes[0], (float) ht.sizes[0] / ht.buckets * 100);
cprintf(" [1]: %d (%.1f%%)\n", ht.sizes[1], (float) ht.sizes[1] / ht.buckets * 100);
cprintf(" [2]: %d (%.1f%%)\n", ht.sizes[2], (float) ht.sizes[2] / ht.buckets * 100);
cprintf(" [3]: %d (%.1f%%)\n", ht.sizes[3], (float) ht.sizes[3] / ht.buckets * 100);
cprintf(" [4+]: %d (%.1f%%)\n", ht.sizes[4], (float) ht.sizes[4] / ht.buckets * 100);
cprintf(" Lookups: %d\n", ht.lookups);
cprintf(" Hits: %d (%.1f%%)\n", ht.hits,float(ht.hits) / ht.lookups * 100);
return 1;
}
/*
* Send a message to a single user or everybody
* using the proxy. Unfortunately there is no
* way for the users to respond back ;)
*/
CMDFUNC(write)
{
if (argc < 2)
{
cprintf("Usage: WRITE <userid/ALL> <message>\n");
return 1;
}
if (strcasecmp(argv[1], "ALL") == 0)
{
char buff[72];
sprintf(buff, "*** GLOBAL MESSAGE FROM %s ***", uinfo.irc->nick);
broadcast(ircproxy_conn_list(), buff);
broadcast(ircproxy_conn_list(), argv[0] + strlen(argv[1])+ 1);
}
else {
conn * c = NULL;
if ((isdigit(argv[1][0])) && (c = lookup(ircproxy_conn_list(), atoi(&argv[1][0]))) && (c->client))
c->cprintf("Message from %s: %s\n", uinfo.irc->nick, argv[0] + strlen(argv[1])+ 1);
else if (c && !c->client)
cprintf("WRITE: Can't deliver message: user is detached or dead.\r\n");
else
cprintf("Can't find user by that id\n");
}
return 1;
}
/* The other admin commands that no one really
* cares about */
CMDFUNC(adminmisc)
{
switch (type)
{
case CMD_DIENOW:
case CMD_DIE:
if (argc)
{
cprintf(type == CMD_DIENOW ? msg_dieing_now : msg_dieing);
ircproxy_die((type == CMD_DIENOW), argv[0]);
}
else
cprintf(msg_not_enuff_args, "DIE/DIENOW");
return 1;
case CMD_KILL:
{
conn *victim;
unsigned i;
if (argc < 2)
return cprintf(msg_not_enuff_args,"KILL"), 1;
i = (unsigned int) atoi(argv[1]);
if (i == id)
{
printlog("%s tried to kill himself\n", addr());
cprintf("Suicides not allowed.\r\n");
return 1;
}
if ((victim = conn::lookup(ircproxy_conn_list(), i)))
{
const char * reason2 = argv[0] + strlen(argv[1]) + 1;
if (victim->dead())
{
cprintf("Cannot kill: already dead\r\n");
return 1;
}
victim->kill(uinfo.irc->nick, reason2);
cprintf(msg_killed, i, victim->uinfo.irc->nick);
printlog("KILLED: %s killed by %s: %s\n",
victim->addr(), addr(), reason2);
}
else
cprintf(msg_cant_kill, i);
}
}
return 1;
}
/*
* Sessions [username] -- list all detached connections for
* a particular user */
CMDFUNC(sessions)
{
userdef * u;
int idx = 0;
bool hShown = 0;
/* Argument specified ... show sessions for a user.. only
* if we are admin though */
if (argc)
{
if (checkf(ADMIN))
{
u = userdef::find(users, argv[1]);
if (!u)
{
cprintf("SESSIONS: unknown user `%s'\n", argv[1]);
return 1;
}
}
else {
cprintf("SESSIONS: you must be admin to view sessions for other users\n");
return 1;
}
} else
u = this->user;
list_iterator<conn> i(u->conns);
conn * c = 0;
while (i.has_next())
{
c = i.next();
if (c->checkf(DETACHED))
{
char timebuff[15];
if (!hShown)
{
cprintf("Current detached sessions for user %s:\n", u->name);
cprintf("ID IRC NICK TO TIME\n");
hShown = 1;
}
duration(ircproxy_time() - c->detach_time, 0, timebuff, sizeof(timebuff));
cprintf("%-3d %-20s %-20s %s\n", ++idx, c->uinfo.irc->nick, c->uinfo.server, timebuff);
}
}
if (!hShown)
cprintf("No detached sessions found for %s\n", u->name);
return 1;
}
CMDFUNC(detach)
{
/* We'll check for this here, to be more user-friendly */
if (!config->checkf(OPT_ENABLE_DETACH_COMMAND))
{
cprintf("Sorry, the Detach command has been disabled here\r\n");
return 1;
}
if (!checkf(BOUNCED))
{
cprintf("DETACH: you must be connected to an IRC server first ...\r\n");
return 1;
}
if (!argc)
{
if (checkf(DEFAULT_USER))
{
cprintf("DETACH: you must supply a password\r\n");
return 1;
}
}
if (!detach(argv[1]))
cprintf("Detach failed!\r\n");
return 1;
}
CMDFUNC(reattach)
{
/* We'll check for this here, to be more user-friendly */
if (checkf(BOUNCED))
{
cprintf("REATTACH: You must NOT be connected "
"to an irc server to use this command\r\n");
return 1;
}
if (!config->checkf(OPT_ENABLE_DETACH_COMMAND))
{
cprintf("Sorry, the detach/reattach commands have been disabled here\r\n");
return 1;
}
if (checkf(DEFAULT_USER) && argc < 2)
{
cprintf("REATTACH: default user must supply a password\r\n");
return 1;
}
int ctr = 0;
userdef * u;
char * pw;
int __id;
#if 0
if (!isdigit(argv[1][0]) && argc >= 2)
{
if (!checkf(ADMIN))
{
cprintf("Must be admin to reattach to different user\r\n");
return 1;
}
/* Assume it's 'reattach user id [pass]' */
u = userdef::find(users, argv[1]);
if (!u)
{
cprintf("REATTACH: Unknown user `%s'\n", argv[1]);
return 1;
}
cprintf("REATTACH: Ok, trying user `%s'\n", argv[1]);
__id = atoi(argv[2]);
pw = argv[3];
}
else
#endif
{ /* No user name specified. Go straight for the id */
__id = argc ? atoi(argv[1]) : 1;
pw = argv[2];
u = user;
}
/* Find it in the list */
list_iterator<conn> i(u->conns);
while (i.has_next())
{
conn * c = i.next();
if (c->checkf(DETACHED))
{
if (++ctr == __id)
{
if (!reattach(pw, c))
cprintf("Reattach to id '%d' failed\r\n", __id);
return 1;
}
}
}
cprintf("REATTACH: Could not find id %d: try `/quote sessions' to list current detached sessions\r\n", __id);
return 1;
}
CMDFUNC(traffic)
{
cprintf("ezbounce traffic stats @ %s\n", timestamp());
cprintf("clients ---> ezbounce : %.2f kB\r\n", (float)bytes_fromc / 1024);
cprintf(" ezbounce ---> IRC : %.2f kB\r\n", (float)bytes_tos / 1024);
cprintf(" ezbounce <--- IRC : %.2f kB\r\n", (float)bytes_froms / 1024);
cprintf("clients <--- ezbounce : %.2f kB\r\n", (float)bytes_toc / 1024);
return 1;
}
CMDFUNC(whois)
{
userdef * u;
if (!argc)
{
cprintf("WHOIS: must specify connection id or user name\r\n");
return 1;
}
u = userdef::find(users, argv[1]);
if (!u)
{
conn * c = conn::lookup(ircproxy_conn_list(), atoi(argv[1]));
if (c)
{
cprintf("Connection '%d' is user '%s'\r\n", c->id, c->user->name);
show_whois(c);
return 1;
}
else {
cprintf("WHOIS: not found: %s\r\n", argv[1]);
return 1;
}
}
cprintf("User info for %s\r\n", argv[1]);
cprintf("---> Active connections: %d\r\n", u->conns->size());
list_iterator<conn> i(u->conns);
while (i.has_next())
show_whois(i.next());
return 1;
}
CMDFUNC(set)
{
if (!argc)
{
struct in_addr addr = { (unsigned long) config->get(PREF_VHOST) };
cprintf("Your Current Preferences:\r\n");
cprintf(" VHost: %s\r\n", inet_ntoa(addr));
cprintf(" Auto-Server: %s\r\n", config->get(OPT_AUTOSERVER, 0));
cprintf(" Fake Ident: %s\r\n", config->get(PREF_FAKE_IDENT, 0));
cprintf(" Auto-Detach: %s\r\n", config->checkp(PREF_AUTO_DETACH) ? "on" : "off");
cprintf(" Proxy DCC-in: %s\r\n", config->checkp(PREF_DCC_IN) ? "on" : "off");
cprintf(" Proxy DCC-out: %s\r\n", config->checkp(PREF_DCC_OUT) ? "on" : "off");
cprintf(" [flags prefs]: %d %d\r\n", config->getf(), config->getp());
return 1;
}
int cmd = user_options::lookup_cmd(argv[1]);
switch (cmd)
{
case PREF_AUTO_PASS:
case OPT_AUTOSERVER:
case PREF_FAKE_IDENT:
config->set(cmd, argv[2]);
cprintf("Set %s to: %s\r\n", argv[1], argv[2]);
break;
case PREF_DCC_IN:
case PREF_DCC_OUT:
case PREF_AUTO_DETACH:
if (argc < 2)
cprintf("%s is currently set to: %s\r\n", argv[1], config->checkf(cmd) ? "ON" : "OFF");
else if (strcasecmp(argv[2],"ON") == 0 || atoi(argv[2]))
{
config->setp(cmd);
cprintf("Set %s to ON\n", argv[1]);
if (!config->checkf(cmd))
cprintf("NOTE: The proxy configuration does not allow you to use this feature..\r\n");
} else {
config->clearp(cmd);
cprintf("Set %s to OFF\r\n", argv[1]);
}
break;
case PREF_VHOST:
{
char *__argv[] = { argv[2], argv[2], 0 };
/* call VHOST command */
do_vhost_cmd(CMD_CONN, argc - 1, __argv);
break;
}
case PREF_LOG:
cprintf("SET: use /quote [ezb] LOG command to manage logging settings\r\n");
break;
default:
cprintf("SET: Unknown variable `%s'\r\n", argv[1]);
}
return 1;
}
CMDFUNC(allowed)
{
userdef * user = this->user;
user_options * config = this->config;
if (argc && isadmin())
{
user = userdef::find(users, argv[1]);
if (!user)
{
cprintf("ALLOWED: invalid user %s\n", argv[1]);
return 1;
}
config = &user->cfg;
}
cprintf("Configuration for user %s: \n", user->name);
cprintf(" 'vhost' command: %s\r\n", config->checkf(OPT_ENABLE_VHOST_COMMAND) ? "yes" : "NO");
cprintf(" Fake Ident services: %s\r\n", config->checkf(OPT_ENABLE_FAKE_IDENTS) ? "yes" : "NO");
cprintf(" Drop on disconnect: %s\r\n", config->checkf(OPT_DROP_ON_DISCONNECT) ? "yes" : "NO");
// cprintf(" Max Idle Time: FIXME!!!\r\n");
cprintf(" Logging: %s\r\n", config->checkf(OPT_LOG_OPTIONS) ? "yes" : "NO");
cprintf(" ---> Log file size limit: %d\r\n", 0);
cprintf(" 'detach' command: %s\r\n", config->checkf(OPT_ENABLE_DETACH_COMMAND) ? "yes" : "NO");
cprintf(" ---> automatic detach feature: %s\r\n", config->checkf(OPT_ENABLE_AUTO_DETACH) ? "yes" : "NO");
cprintf(" DCC Proxying\r\n");
cprintf(" ---> Incoming: %s\r\n", config->checkf(OPT_ENABLE_INCOMING_DCC_PROXYING) ? "yes" : "no");
cprintf(" ---> Outgoing: %s\r\n", config->checkf(OPT_ENABLE_OUTGOING_DCC_PROXYING) ? "yes" : "no");
return 1;
}
CMDFUNC(echo)
{
if (argc)
client->printf(":%s\r\n", argv[0]);
return 1;
}
CMDFUNC(reload)
{
if (!pcfg.userfile)
cprintf("Can't reload user preferences: user file wasn't set in the config\r\n");
else {
printlog("%s is reloading user preferences ...\n", addr());
cprintf("Reloading User Preferences ...\r\n");
if (ircproxy_load_prefs(::users, pcfg.userfile) < 1)
cprintf(" .... failed\r\n");
else
cprintf(" .... success\r\n");
}
return 1;
}
CMDFUNC(save)
{
if (checkf(DEFAULT_USER))
{
cprintf("Sorry, can't save preferences for default user\r\n");
return 1;
}
cprintf("Saving preferences for user `%s'... \r\n", user->name);
/* blah -- copy() will wipe out user record's password field,
* because config->password == null -- so save it */
char * pass = my_strdup(user->cfg.get(OPT_PASSWORD, 0));
user->cfg.copy(config);
user->cfg.set(OPT_PASSWORD, pass);
delete[] pass;
cprintf(" .... Success\r\n");
if (isadmin())
{
if (!pcfg.userfile)
{
cprintf("Can't save options to disk: no user file was set in the config\r\n");
return 1;
}
cprintf("Saving to disk ...\r\n");
if (ircproxy_save_prefs(users, pcfg.userfile) < 1)
cprintf(" .... Failed: %s\r\n", strerror(errno));
else
cprintf(" .... Success\r\n");
}
return 1;
}
/*
* This thing is spiffy */
CMDFUNC(trace)
{
char addrs[4][23] /* 4 to hold 255.255.255.255:65535 */ =
{"client is detached",
"",
"",
" not connected"};
struct sockaddr_in out_addr;
socklen_t st = sizeof(out_addr);
conn * c = this;
if (argc && isadmin())
{
c = conn::lookup(ircproxy_conn_list(), atoi(argv[1]));
if (!c)
{
cprintf("TRACE: can't find user id %s\n", argv[1]);
return 1;
}
if (c->dead())
{
cprintf("TRACE: can't trace id %s: zombie connection\n", argv[1]);
return 1;
}
}
cprintf("Showing network info for connection id %d (%s)\r\n", c->id, c->user->name);
if (c->client)
{
sprintf(addrs[0], "%s:%u", inet_ntoa(c->client_saddr.sin_addr), ntohs(c->client_saddr.sin_port));
sprintf(addrs[1], "%s:%u", inet_ntoa(c->local_saddr.sin_addr), ntohs(c->local_saddr.sin_port));
}
if (c->server)
{
getsockname(c->server->fd, (sockaddr *) &out_addr, &st);
sprintf(addrs[2], "%s:%u", inet_ntoa(out_addr.sin_addr), ntohs(out_addr.sin_port));
sprintf(addrs[3], "%s:%u", inet_ntoa(c->serv_saddr.sin_addr), ntohs(c->serv_saddr.sin_port));
}
cprintf(" ________________________\r\n");
cprintf(" [%-22s]\r\n", (c->client) ? c->user->name : "this");
cprintf(" [%-22s]\r\n", addrs[0]);
cprintf(" \\______________________/\r\n");
cprintf(" |\r\n");
cprintf(" | /----------------------\\\r\n");
cprintf(" \\----->[%22s]\r\n", addrs[1]);
cprintf(" [\002%22s\002]\r\n", "ezbounce");
cprintf(" [%22s]\r\n", EZBOUNCE_VERSION);
if (server)
cprintf(" [%22s]\r\n", addrs[2]);
cprintf(" \\----------------------/\r\n");
cprintf(" |\r\n");
cprintf(" |\r\n");
cprintf(" \\----->[%22s]\r\n", addrs[3]);
if (server)
{
cprintf(" [%22s]\r\n", c->uinfo.irc->nick);
cprintf(" [ ]\r\n");
cprintf(" [%22s]\r\n", c->uinfo.server);
cprintf(" [%22s]\r\n", c->uinfo.serverversion);
}
return 1;
}
CMDFUNC(about)
{
struct utsname uts;
uname(&uts);
cprintf("This is \002" EZBOUNCE_VERSION "\002 [built " __DATE__ " " __TIME__ "]\r\n");
cprintf("Running on: %s %s\r\n", uts.sysname, uts.release);
cprintf(" Visit the ezbounce web site at http://druglord.freelsd.org/ezbounce/\r\n");
return 1;
}
CMDFUNC(version)
{
cprintf(EZBOUNCE_VERSION "\r\n");
return 1;
}
CMDFUNC(debug)
{
cprintf("DEBUG: not compiled with debugging info\r\n");
return 1;
}
#ifdef __DEBUG__
CMDFUNC(dccsend)
{
#if 0
if (argc < 1)
return 1;
if (type == CMD_DCCSEND)
if (!dcc_send_file(argv[1]))
cprintf("Unable to send :(\r\n");
if (type == CMD_CHATSEND)
if (!dcc_send_file(argv[1], 0, 0, 0, 1))
cprintf("Unable to send :-P\r\n");
#else
cprintf("Feature disabled\r\n");
#endif
return 1;
}
#endif
/*********************************************************
* INCOMING command handlers
* Notes:
* Return values and their meanings are the same
* Return values are meaningless when detached
* 'argv[0]' argument to the function contains ENTIRE line,
* so that the token before the command may be read
*********************************************************/
/*
* Gathers misc. info about the server during connect, so we can
* dump it back during reattach .. */
CMDFUNC(servinfo)
{
char * tmp = 0;
switch (type)
{
case INCOMING_003:
/* Here we find out when the server was created,
* as well as the server's true name,
* and, we might as well get a positive identification on our
* nickname */
/**** example line:
* :irc.Prison.NET 003 druglord_ :This server was created Oct 23 2001 02:34:33
*/
if (strcasecmp(argv[3], uinfo.irc->nick))
{
printlog("NICK CHANGE: %s ----> %s\n", addr(), argv[3]);
delete[] uinfo.irc->nick;
uinfo.irc->nick = my_strdup(argv[3]);
delete[] uinfo.fulladdr;
uinfo.fulladdr = new char[10 + strlen(uinfo.irc->nick) + 18];
sprintf(uinfo.fulladdr, "%lu:%s@%s", id, uinfo.irc->nick, inet_ntoa(client_saddr.sin_addr));
}
delete[] uinfo.servercreated;
delete[] uinfo.server;
tmp = gettok_ptr(argv[0], ' ', 8);
uinfo.server = my_strdup(no_leading(argv[1]));
uinfo.servercreated = my_strdup(tmp);
break;
case INCOMING_004:
/* Here we obtain the server version
* and the crap it sends right afterward... i want to keep them
* seperate because we will use the version string in 'trace' */
/**** example line:
* :irc.arcti.ca 004 _druglord irc.arcti.ca 2.8/hybrid-6.2 oOiwszcrkfydnxb biklmnopstve */
delete[] uinfo.serverversion;
delete[] uinfo.servermodes;
tmp = gettok_ptr(argv[0],' ', 6);
uinfo.serverversion = my_strdup(argv[5]);
uinfo.servermodes = my_strdup(tmp);
setf(GOTSERVINFO);
break;
case INCOMING_005:
/* sample line:
* :elysium.ga.us.dal.net 005 druglord NOQUIT WATCH=128 SAFELIST MODES=6 MAXCHANNELS=10
* MAXBANS=100 NICKLEN=30 TOPICLEN=307 KICKLEN=307 CHANTYPES=&# PREFIX=(ov)@+ NETWORK=DALnet
* SILENCE=10 :are available on this server
*
* -- so just copy all from 4th token */
if (!checkf(GOTSERVINFO2))
{
delete[] uinfo.server005_1;
tmp = gettok_ptr(argv[0], ' ', 4);
uinfo.server005_1 = my_strdup(tmp);
setf(GOTSERVINFO2);
} else if (!checkf(GOTSERVINFO3)) {
delete[] uinfo.server005_2;
tmp = gettok_ptr(argv[0], ' ', 4);
uinfo.server005_2 = my_strdup(tmp);
setf(GOTSERVINFO3);
}
break;
}
return 0;
}
CMDFUNC(privmsg_incoming)
{
/* What all we need to do:
* -- Handle CTCP DCC (only when !detached and DCC_IN)
* -- Handle CTCP PING (only when detached)
* -- Handle all CTCP (only when detached)
* -- Handle all Other (only when detached) */
/* Ensure that we really need to handle this.. */
if (!checkf(DETACHED) && !config->decide(PREF_DCC_IN))
return 0;
char * ctcp = 0;
char * ctcp_args = 0;
ircaddr_simple ia(argv[1]);
if (argc > 3 && argv[4][1] == '\001')
{
ctcp = &argv[4][2];
if (argc > 4)
ctcp_args = gettok_ptr(argv[0], ' ', 5);
}
/* The hook for Incoming DCC Proxying */
if (!checkf(DETACHED) && config->decide(PREF_DCC_IN) && ctcp_args)
/* Possibly a DCC -- have do_ctcp handle it (will relay too) */
return do_ctcp(true, ctcp, no_leading(argv[1]), no_leading(argv[3]), ctcp_args);
/* The hook for CTCP responses when detached
* :nick!source@address PRIVMSG target :\001[CTCP] args... */
if (checkf(DETACHED) && ctcp_args)
{
/* Check for and reply to CTCP PINGs -- only once per 5 secs */
if (ircproxy_time() - last_recved <= 5
|| strcmp(ctcp, "PING") != 0)
return 0;
last_recved = ircproxy_time();
/* And reply -- note, no trailing \001, we just use the one that
* was sent in the original ping */
server->printf("NOTICE %s :\001PING %s\r\n", ia.nick, ctcp_args);
return 1;
}
/* Finally, logging */
if (log)
{
if (ctcp)
log->write((ischan(argv[3]) ? logfile::EVENT_PUBLIC : logfile::EVENT_PRIVATE) |
logfile::EVENT_CTCP, &ia, argv[3], ctcp_args, ctcp);
else
log->write((ischan(argv[3]) ? logfile::EVENT_PUBLIC : logfile::EVENT_PRIVATE),
&ia, argv[3], no_leading(gettok_ptr(argv[0], ' ', 4)), 0);
}
return 0;
}
CMDFUNC(nick_incoming)
{
ircaddr_simple ia(argv[1]);
/*
* Do the logging stuff first, cause we fuck with the ia object
* in the nick change handler below */
if (log)
{
log->write(logfile::EVENT_NICK | logfile::EVENT_PUBLIC,
&ia, no_leading(argv[3]), NULL, NULL);
}
if (strcasecmp(ia.nick, uinfo.irc->nick) == 0
&& argc > 2)
{
printlog("NICK CHANGE: %s ----> %s\n", addr(), argv[3]+1);
if (!uinfo.irc->host)
{
/* If we don't know our host, we do now */
uinfo.irc->host = my_strdup(ia.host);
uinfo.irc->ident = my_strdup(ia.ident);
}
delete[] uinfo.irc->nick;
uinfo.irc->nick = my_strdup(argv[3]+1);
uinfo.fulladdr = new char[10 + strlen(uinfo.irc->nick) + 18];
sprintf(uinfo.fulladdr, "%lu:%s@%s", id, uinfo.irc->nick, inet_ntoa(client_saddr.sin_addr));
}
return 0;
}
CMDFUNC(mode_incoming)
{
ircaddr_simple ia(argv[1]);
if (log)
if (ischan(argv[3]))
log->write(logfile::EVENT_MODE | logfile::EVENT_PUBLIC,
&ia, argv[3],
no_leading(gettok_ptr(argv[0], ' ', 4)), NULL);
return 0;
}
CMDFUNC(join_incoming)
{
/* See if we joined, in that case update the IRC server */
ircaddr_simple ia(argv[1]);
if (!strcasecmp(ia.nick, uinfo.irc->nick))
{
uinfo.channels.add(my_strdup(no_leading(argv[3])));
DEBUG("Adding %s to channel list!\n", argv[3]);
/* If we don't know our hostname... we do now..
* update accordingly */
if (!uinfo.irc->host)
{
delete uinfo.irc;
uinfo.irc = new ircaddr(ia);
DEBUG("Found out address for %s (%s@%s)\n", uinfo.irc->nick, uinfo.irc->ident, uinfo.irc->host);
}
}
if (log)
{
log->write(logfile::EVENT_JOIN | logfile::EVENT_PUBLIC,
&ia, no_leading(argv[3]), NULL, NULL);
}
return 0;
}
CMDFUNC(part_incoming)
{
ircaddr_simple ia(argv[1]);
if (!strcasecmp(ia.nick, uinfo.irc->nick))
{
DEBUG("Removing %s from channel list\n", no_leading(argv[3]));
strlist_remove(&uinfo.channels, no_leading(argv[3]));
}
if (log)
{
log->write(logfile::EVENT_PART | logfile::EVENT_PUBLIC,
&ia, no_leading(argv[3]),
no_leading(gettok_ptr(argv[0], ' ', 4)), NULL);
}
return 0;
}
CMDFUNC(kick_incoming)
{
if (!strcasecmp(argv[4], uinfo.irc->nick))
{
DEBUG("Removing %s from channel list\n", argv[3]);
strlist_remove(&uinfo.channels, argv[3]);
if (checkf(DETACHED))
server->printf("JOIN :%s\r\n", argv[3]);
}
if (log)
{
ircaddr_simple ia(argv[1]);
log->write(logfile::EVENT_KICK | logfile::EVENT_PUBLIC,
&ia, argv[3],
no_leading(gettok_ptr(argv[0], ' ', 5)), argv[4]);
}
return 0;
}
/*
* Handle notices -- so far just used for logging
* :nick!user@host NOTICE <target> :<message> */
CMDFUNC(notice_incoming)
{
if (log)
{
ircaddr_simple ia(argv[1]);
log->write( logfile::EVENT_NOTICE | ischan(argv[3]) ? logfile::EVENT_PUBLIC : logfile::EVENT_PRIVATE,
&ia, argv[3],
no_leading(gettok_ptr(argv[0], ' ', 4)), NULL);
}
return 0;
}
CMDFUNC(topic_incoming)
{
if (log)
{
ircaddr_simple ia(argv[1]);
log->write( logfile::EVENT_TOPIC | logfile::EVENT_PUBLIC,
&ia, argv[3],
no_leading(gettok_ptr(argv[0], ' ', 4)),
NULL);
}
return 0;
}
CMDFUNC(quit_incoming)
{
if (log)
{
ircaddr_simple ia(argv[1]);
log->write( logfile::EVENT_QUIT | logfile::EVENT_PUBLIC,
&ia, NULL,
no_leading(gettok_ptr(argv[0], ' ', 3)),
NULL);
}
return 0;
}
/* Handles incoming server pings -- example:
* PING :irc.lagged.org */
CMDFUNC(ping_incoming)
{
char * args = gettok_ptr(argv[0], ' ', 2);
server->printf("PONG %s\r\n", args);
DEBUG("Replying to server ping: %s\n", args);
return 0;
}
/* if/when we start pinging the IRC server, this will be
* the handler */
CMDFUNC(pong_incoming)
{
DEBUG("Got a server PONG: %s\n", argv[1]);
return 0;
}
/* get ERROR strings when we are detached and stuff
*/
CMDFUNC(error_incoming)
{
if (log)
{
log->dump(argv[0]);
log->dump("\n");
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1