mirror of
https://github.com/EnergyMech/energymech.git
synced 2025-12-29 16:14:43 +00:00
1745 lines
34 KiB
C
1745 lines
34 KiB
C
/*
|
|
|
|
EnergyMech, IRC bot software
|
|
Parts Copyright (c) 1997-2018 proton
|
|
|
|
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.
|
|
|
|
*/
|
|
#define CORE_C
|
|
#include "config.h"
|
|
|
|
#include "defines.h"
|
|
#include "structs.h"
|
|
#include "global.h"
|
|
#include "h.h"
|
|
#include "text.h"
|
|
#include "mcmd.h"
|
|
#include "settings.h"
|
|
|
|
#ifdef IDWRAP
|
|
|
|
void unlink_identfile(void)
|
|
{
|
|
if (current->identfile)
|
|
{
|
|
#ifdef DEBUG
|
|
debug("(unlink_identfile) unlink(%s)\n",current->identfile);
|
|
#endif /* DEBUG */
|
|
unlink(current->identfile);
|
|
Free((char**)¤t->identfile);
|
|
}
|
|
}
|
|
|
|
#endif /* IDWRAP */
|
|
|
|
int conf_callback(char *line)
|
|
{
|
|
|
|
if (line && *line == COMMENT_CHAR)
|
|
return(FALSE);
|
|
|
|
fix_config_line(line);
|
|
|
|
on_msg((char*)CoreUser.name,current->nick,line);
|
|
return(FALSE);
|
|
}
|
|
|
|
void readcfgfile(void)
|
|
{
|
|
Mech *bot;
|
|
int oc,in;
|
|
|
|
oc = TRUE;
|
|
in = -1;
|
|
|
|
#ifdef SESSION
|
|
if (!stringcmp(CFGFILE,configfile))
|
|
{
|
|
if ((in = open(SESSIONFILE,O_RDONLY)) >= 0)
|
|
{
|
|
to_file(1,"init: Restoring previously saved session...\n");
|
|
oc = FALSE;
|
|
}
|
|
}
|
|
#endif /* SESSION */
|
|
|
|
if (oc)
|
|
{
|
|
if ((in = open(configfile,O_RDONLY)) < 0)
|
|
{
|
|
to_file(1,"init: Couldn't open the file %s\n",configfile);
|
|
mechexit(1,exit);
|
|
}
|
|
}
|
|
|
|
current = add_bot(0,"(conf)");
|
|
|
|
*CurrentNick = 0;
|
|
CurrentShit = NULL;
|
|
CurrentChan = NULL;
|
|
CurrentDCC = (Client*)&CoreClient;
|
|
CurrentUser = (User*)&CoreUser;
|
|
|
|
readline(in,&conf_callback); /* readline closes in */
|
|
|
|
CurrentDCC = NULL;
|
|
|
|
if (!current->guid)
|
|
{
|
|
to_file(1,"init: Error: No bots in the configfile\n");
|
|
mechexit(1,exit);
|
|
}
|
|
|
|
if ((current) && (current->chanlist == NULL))
|
|
to_file(1,"%s %s will not join any channels\n",ERR_INIT,current->nick);
|
|
|
|
oc = 0;
|
|
#ifdef DEBUG
|
|
in = dodebug;
|
|
dodebug = 0;
|
|
#endif /* DEBUG */
|
|
to_file(1,"init: Mech(s) added [ ");
|
|
for(bot=botlist;bot;bot=bot->next)
|
|
{
|
|
if (oc > 30)
|
|
{
|
|
to_file(1,", ...");
|
|
break;
|
|
}
|
|
to_file(1,"%s%s",(oc > 0) ? ", " : "",bot->nick);
|
|
oc += strlen(bot->nick);
|
|
}
|
|
to_file(1," ]\n");
|
|
#ifdef DEBUG
|
|
dodebug = in;
|
|
#endif /* DEBUG */
|
|
}
|
|
|
|
#ifdef SESSION
|
|
|
|
int write_session(void)
|
|
{
|
|
#ifdef BOTNET
|
|
NetCfg *cfg;
|
|
#endif /* BOTNET */
|
|
#ifdef RAWDNS
|
|
Strp *p;
|
|
#endif /* RAWDNS */
|
|
#ifdef ALIAS
|
|
Alias *alias;
|
|
#endif /* ALIAS */
|
|
Server *sp;
|
|
Spy *spy;
|
|
Chan *chan;
|
|
Mech *bot;
|
|
UniVar *varval;
|
|
int j,sf;
|
|
|
|
if ((sf = open(SESSIONFILE,O_WRONLY|O_CREAT|O_TRUNC,NEWFILEMODE)) < 0)
|
|
return(FALSE);
|
|
|
|
#ifdef RAWDNS
|
|
for(p=dnsrootfiles;p;p=p->next)
|
|
to_file(sf,"dnsroot %s\n",p->p);
|
|
for(j=0;j<MAX_NAMESERVERS;j++)
|
|
if (ia_ns[j].s_addr > 0)
|
|
to_file(sf,"dnsserver %s\n",inet_ntoa(ia_ns[j]));
|
|
#endif /* RAWDNS */
|
|
|
|
to_file(sf,"set ctimeout %i\n",ctimeout);
|
|
for(sp=serverlist;sp;sp=sp->next)
|
|
{
|
|
to_file(sf,"server %s %i %s\n",sp->name,(sp->port) ? sp->port : 6667,
|
|
(sp->pass[0]) ? sp->pass : "");
|
|
}
|
|
|
|
#ifdef BOTNET
|
|
if (linkpass)
|
|
to_file(sf,"set linkpass %s\n",linkpass);
|
|
if (linkport)
|
|
to_file(sf,"set linkport %i\n",linkport);
|
|
if (autolink)
|
|
to_file(sf,"set autolink 1\n");
|
|
for(cfg=netcfglist;cfg;cfg=cfg->next)
|
|
{
|
|
to_file(sf,"link %i %s",cfg->guid,(cfg->pass) ? cfg->pass : MATCH_ALL);
|
|
if (cfg->host)
|
|
to_file(sf," %s %i",cfg->host,cfg->port);
|
|
to_file(sf,"\n");
|
|
}
|
|
#endif /* BOTNET */
|
|
|
|
#ifdef BOUNCE
|
|
if (bounce_port)
|
|
to_file(sf,"set bncport %i\n",bounce_port);
|
|
#endif /* BOUNCE */
|
|
|
|
#ifdef WEB
|
|
if (webport)
|
|
to_file(sf,"set webport %i\n",webport);
|
|
#endif /* WEB */
|
|
|
|
#ifdef UPTIME
|
|
if (uptimehost && stringcasecmp(uptimehost,defaultuptimehost))
|
|
to_file(sf,"set uphost %s\n",uptimehost);
|
|
if (uptimeport)
|
|
to_file(sf,"set upport %i\n",uptimeport);
|
|
if (uptimenick)
|
|
to_file(sf,"set upnick %s\n",uptimenick);
|
|
#endif /* UPTIME */
|
|
|
|
#ifdef TRIVIA
|
|
if (triv_qfile)
|
|
to_file(sf,"set qfile %s\n",triv_qfile);
|
|
to_file(sf,"set qdelay %i\n",triv_qdelay);
|
|
to_file(sf,"set qchar %c\n",triv_qchar);
|
|
#endif /* TRIVIA */
|
|
|
|
#ifdef SEEN
|
|
if (seenfile)
|
|
to_file(sf,"set seenfile %s\n",seenfile);
|
|
#endif /* SEEN */
|
|
|
|
#ifdef DYNCMD
|
|
/*
|
|
* because of "chaccess XXX disable" its best to save chaccess last
|
|
*/
|
|
for(j=0;mcmd[j].name;j++)
|
|
{
|
|
if (acmd[j] != mcmd[j].defaultaccess)
|
|
{
|
|
if (acmd[j] == 250)
|
|
to_file(sf,"chaccess %s disable\n",mcmd[j].name);
|
|
else
|
|
to_file(sf,"chaccess %s %i\n",mcmd[j].name,(int)acmd[j]);
|
|
}
|
|
}
|
|
#endif /* DYNCMD */
|
|
|
|
#ifdef ALIAS
|
|
for(alias=aliaslist;alias;alias=alias->next)
|
|
{
|
|
to_file(sf,"alias %s %s\n",alias->alias,alias->format);
|
|
}
|
|
#endif /* ALIAS */
|
|
|
|
for(bot=botlist;bot;bot=bot->next)
|
|
{
|
|
to_file(sf,"nick %i %s\n",bot->guid,bot->wantnick);
|
|
/*
|
|
* current->setting contains channel defaults and global vars
|
|
*/
|
|
for(j=0;VarName[j].name;j++)
|
|
{
|
|
if (IsProc(j))
|
|
continue;
|
|
varval = &bot->setting[j];
|
|
if (IsChar(j))
|
|
{
|
|
if (VarName[j].v.num != varval->char_var)
|
|
to_file(sf,"set %s %c\n",VarName[j].name,varval->char_var);
|
|
}
|
|
else
|
|
if (IsNum(j))
|
|
{
|
|
if (VarName[j].v.num != varval->int_var)
|
|
to_file(sf,"set %s %i\n",VarName[j].name,varval->int_var);
|
|
}
|
|
else
|
|
if (IsStr(j))
|
|
{
|
|
/*
|
|
* There are no default string settings
|
|
*/
|
|
if (varval->str_var)
|
|
to_file(sf,"set %s %s\n",VarName[j].name,varval->str_var);
|
|
}
|
|
}
|
|
for(chan=bot->chanlist;chan;chan=chan->next)
|
|
{
|
|
if (!chan->active && !chan->rejoin)
|
|
continue;
|
|
to_file(sf,"join %s %s\n",chan->name,(chan->key) ? chan->key : "");
|
|
/*
|
|
* using CHANSET_SIZE: only the first settings contain stuff
|
|
*/
|
|
for(j=0;j<CHANSET_SIZE;j++)
|
|
{
|
|
varval = &chan->setting[j];
|
|
if (IsNum(j))
|
|
{
|
|
if (VarName[j].v.num != varval->int_var)
|
|
to_file(sf,"set %s %i\n",VarName[j].name,varval->int_var);
|
|
}
|
|
else
|
|
if (IsStr(j))
|
|
{
|
|
/*
|
|
* There are no default string settings
|
|
*/
|
|
if (varval->str_var)
|
|
to_file(sf,"set %s %s\n",VarName[j].name,varval->str_var);
|
|
}
|
|
}
|
|
}
|
|
// SPY files
|
|
for(spy=bot->spylist;spy;spy=spy->next)
|
|
{
|
|
if (spy->t_dest == SPY_FILE)
|
|
{
|
|
if (spy->src && spy->dest)
|
|
to_file(sf,"spy %s > %s\n",spy->src,spy->dest);
|
|
#ifdef DEBUG
|
|
else
|
|
debug("spy to session fail\n");
|
|
#endif /* DEBUG */
|
|
}
|
|
}
|
|
}
|
|
|
|
close(sf);
|
|
return(TRUE);
|
|
}
|
|
|
|
#endif /* SESSION */
|
|
|
|
/*
|
|
* Bot nicking, adding and killing
|
|
*/
|
|
|
|
void setbotnick(Mech *bot, char *nick)
|
|
{
|
|
/*
|
|
* if its exactly the same we dont need to change it
|
|
*/
|
|
if (!stringcmp(bot->nick,nick))
|
|
return;
|
|
|
|
Free((char**)&bot->nick);
|
|
set_mallocdoer(setbotnick);
|
|
bot->nick = stringdup(nick);
|
|
#ifdef BOTNET
|
|
botnet_refreshbotinfo();
|
|
#endif /* BOTNET */
|
|
}
|
|
|
|
Mech *add_bot(int guid, char *nick)
|
|
{
|
|
Mech *bot;
|
|
|
|
set_mallocdoer(add_bot);
|
|
bot = (Mech*)Calloc(sizeof(Mech));
|
|
bot->connect = CN_NOSOCK;
|
|
bot->sock = -1;
|
|
bot->guid = guid;
|
|
set_mallocdoer(add_bot);
|
|
bot->nick = stringdup(nick);
|
|
set_mallocdoer(add_bot);
|
|
bot->wantnick = stringdup(nick);
|
|
set_binarydefault(bot->setting);
|
|
bot->next = botlist;
|
|
botlist = bot;
|
|
|
|
#ifndef I_HAVE_A_LEGITIMATE_NEED_FOR_MORE_THAN_4_BOTS
|
|
spawning_lamer++;
|
|
#endif /* I_HAVE_A_LEGITIMATE_NEED_FOR_MORE_THAN_4_BOTS */
|
|
|
|
return(bot);
|
|
}
|
|
|
|
void signoff(char *from, char *reason)
|
|
{
|
|
Mech **pp;
|
|
char *fname;
|
|
int i;
|
|
|
|
if (from)
|
|
{
|
|
to_user(from,"Killing mech: %s",current->nick);
|
|
to_user(from,"Saving the lists...");
|
|
}
|
|
fname = current->setting[STR_USERFILE].str_var;
|
|
#ifdef DEBUG
|
|
debug("(signoff) Saving lists...\n");
|
|
#endif /* DEBUG */
|
|
if (!write_userlist(fname) && from)
|
|
{
|
|
to_user(from,(fname) ? ERR_NOSAVE : ERR_NOUSERFILENAME,fname);
|
|
}
|
|
#ifdef SEEN
|
|
if (seenfile && !write_seenlist() && from)
|
|
{
|
|
to_user(from,TEXT_SEENNOSAVE,seenfile);
|
|
}
|
|
#endif /* SEEN */
|
|
#ifdef NOTIFY
|
|
if (current->notifylist)
|
|
write_notifylog();
|
|
#endif /* NOTIFY */
|
|
if (from)
|
|
{
|
|
to_user(from,TEXT_SHUTDOWNCOMPLETE);
|
|
}
|
|
|
|
while(current->chanlist)
|
|
remove_chan(current->chanlist);
|
|
|
|
while(current->clientlist)
|
|
delete_client(current->clientlist);
|
|
|
|
while(current->authlist)
|
|
remove_auth(current->authlist);
|
|
|
|
purge_shitlist();
|
|
purge_kicklist();
|
|
|
|
#ifdef NOTIFY
|
|
purge_notify();
|
|
#endif /* NOTIFY */
|
|
|
|
if (current->sock != -1)
|
|
{
|
|
#ifdef IDWRAP
|
|
unlink_identfile();
|
|
#endif /* IDWRAP */
|
|
if (!reason)
|
|
reason = randstring(SIGNOFFSFILE);
|
|
to_server("QUIT :%s\n",(reason) ? reason : "");
|
|
killsock(current->sock);
|
|
current->sock = -1;
|
|
}
|
|
|
|
/*
|
|
* release string var memory
|
|
*/
|
|
delete_vars(current->setting,(SIZE_VARS-1));
|
|
|
|
#ifdef DEBUG
|
|
debug("(signoff) Removing userlist...\n");
|
|
#endif /* DEBUG */
|
|
while(current->userlist)
|
|
remove_user(current->userlist);
|
|
|
|
#ifdef DEBUG
|
|
debug("(signoff) Removing lastcmd list...\n");
|
|
#endif /* DEBUG */
|
|
for(i=0;i<LASTCMDSIZE;i++)
|
|
{
|
|
Free(¤t->lastcmds[i]);
|
|
}
|
|
|
|
/*
|
|
* These 2 are used by do_die() to pass reason and doer.
|
|
*/
|
|
Free((char**)¤t->signoff);
|
|
Free((char**)¤t->from);
|
|
|
|
#ifdef DEBUG
|
|
debug("(signoff) Unlinking bot record from linked list...\n");
|
|
#endif /* DEBUG */
|
|
|
|
pp = &botlist;
|
|
while(*pp)
|
|
{
|
|
if (*pp == current)
|
|
{
|
|
*pp = current->next;
|
|
break;
|
|
}
|
|
}
|
|
Free((char**)¤t);
|
|
|
|
/*
|
|
* get a new current bot, or exit
|
|
*/
|
|
if ((current = botlist) == NULL)
|
|
{
|
|
#if defined(BOUNCE) && defined(IDWRAP)
|
|
bounce_cleanup();
|
|
#endif /* BOUNCE && IDWRAP */
|
|
|
|
#ifdef TRIVIA
|
|
write_triviascore();
|
|
#endif /* TRIVIA */
|
|
|
|
#ifdef UPTIME
|
|
uptime_death(UPTIME_GENERICDEATH);
|
|
#endif /* UPTIME */
|
|
|
|
while(killsock(-2))
|
|
/* killsock() sleeps 1 second in select() */
|
|
;
|
|
|
|
mechexit(0,exit);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
debug("(signoff) All done.\n");
|
|
#endif /* DEBUG */
|
|
}
|
|
|
|
void kill_all_bots(char *reason)
|
|
{
|
|
while(TRUE)
|
|
{
|
|
current = botlist;
|
|
signoff(NULL,reason);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Server lists, connects, etc...
|
|
*/
|
|
Server *add_server(char *host, int port, char *pass)
|
|
{
|
|
Server *sp,**pp;
|
|
|
|
pp = &serverlist;
|
|
while(*pp)
|
|
{
|
|
sp = *pp;
|
|
if ((!port || sp->port == port) && (!stringcasecmp(host,sp->name) || !stringcasecmp(host,sp->realname)))
|
|
return(sp);
|
|
pp = &sp->next;
|
|
}
|
|
set_mallocdoer(add_server);
|
|
*pp = sp = (Server*)Calloc(sizeof(Server));
|
|
sp->ident = serverident++;
|
|
stringcpy_n(sp->name,host,NAMELEN);
|
|
if (pass && *pass)
|
|
stringcpy_n(sp->pass,pass,PASSLEN);
|
|
sp->port = (port) ? port : DEFAULT_IRC_PORT;
|
|
if (currentservergroup)
|
|
sp->servergroup = currentservergroup->servergroup;
|
|
return(sp);
|
|
}
|
|
|
|
ServerGroup *getservergroup(const char *name)
|
|
{
|
|
ServerGroup *sg;
|
|
|
|
for(sg=servergrouplist;sg;sg=sg->next)
|
|
{
|
|
if (!stringcasecmp(sg->name,name))
|
|
return(sg);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
ServerGroup *getservergroupid(int id)
|
|
{
|
|
ServerGroup *sg;
|
|
|
|
for(sg=servergrouplist;sg;sg=sg->next)
|
|
{
|
|
if (sg->servergroup == id)
|
|
return(sg);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
Server *find_server(int id)
|
|
{
|
|
Server *sp;
|
|
|
|
for(sp=serverlist;sp;sp=sp->next)
|
|
if (sp->ident == id)
|
|
return(sp);
|
|
return(NULL);
|
|
}
|
|
|
|
int try_server(Server *sp, char *hostname)
|
|
{
|
|
#ifdef RAWDNS
|
|
char temphost[NAMEBUF];
|
|
char *host;
|
|
uint32_t ip;
|
|
#endif /* RAWDNS */
|
|
|
|
if (!hostname)
|
|
{
|
|
send_spy(SPYSTR_STATUS,"Trying new server: %s:%i",(*sp->realname) ? sp->realname : sp->name,sp->port);
|
|
hostname = sp->name;
|
|
}
|
|
|
|
sp->lastattempt = now;
|
|
sp->usenum++;
|
|
|
|
#ifdef RAWDNS
|
|
if ((host = poll_rawdns(hostname)))
|
|
{
|
|
#ifdef DEBUG
|
|
debug("(try_server) rawdns: %s ==> %s\n",sp->name,host);
|
|
#endif /* DEBUG */
|
|
stringcpy(temphost,host);
|
|
hostname = temphost;
|
|
}
|
|
else
|
|
if ((ip = inet_addr(hostname)) == -1)
|
|
{
|
|
current->server = sp->ident;
|
|
current->connect = CN_DNSLOOKUP;
|
|
current->conntry = now;
|
|
rawdns(hostname);
|
|
return(0);
|
|
}
|
|
#endif /* RAWDNS */
|
|
current->server = sp->ident;
|
|
if ((current->sock = SockConnect(hostname,sp->port,TRUE)) < 0)
|
|
{
|
|
sp->err = SP_ERRCONN;
|
|
return(-1);
|
|
}
|
|
current->away = FALSE;
|
|
current->connect = CN_TRYING;
|
|
current->activity = current->conntry = now;
|
|
*current->modes = 0;
|
|
return(current->sock);
|
|
}
|
|
|
|
void connect_to_server(void)
|
|
{
|
|
ServerGroup *sg;
|
|
Server *sp,*sptry;
|
|
Chan *chan;
|
|
char *s;
|
|
int sgi;
|
|
|
|
/*
|
|
* This should prevent the bot from chewing up too
|
|
* much CPU when it fails to connect to ANYWHERE
|
|
*/
|
|
current->conntry = now;
|
|
|
|
/*
|
|
* Is this the proper action if there is no serverlist?
|
|
*/
|
|
if (!serverlist)
|
|
return;
|
|
|
|
if (current->chanlist)
|
|
{
|
|
#ifdef DEBUG
|
|
if (current->connect != CN_SPINNING) debug("[CtS] Setting rejoin- and synced-status for all channels\n");
|
|
#endif /* DEBUG */
|
|
current->rejoin = TRUE;
|
|
for(chan=current->chanlist;chan;chan=chan->next)
|
|
{
|
|
if (chan->active)
|
|
{
|
|
chan->active = FALSE;
|
|
chan->rejoin = TRUE;
|
|
}
|
|
chan->sync = TRUE;
|
|
chan->bot_is_op = FALSE;
|
|
#ifdef STATS
|
|
if (chan->stats)
|
|
chan->stats->flags |= CSTAT_PARTIAL;
|
|
#endif /* STATS */
|
|
}
|
|
}
|
|
|
|
if (current->nextserver)
|
|
{
|
|
sp = find_server(current->nextserver);
|
|
current->nextserver = 0;
|
|
if (sp && (try_server(sp,NULL) >= 0))
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* The purpose of this kludge is to find the least used server
|
|
* July 7th: added logic for servergroup
|
|
*/
|
|
sptry = NULL;
|
|
if ((s = current->setting[STR_SERVERGROUP].str_var))
|
|
{
|
|
if ((sg = getservergroup(s)))
|
|
sgi = sg->servergroup;
|
|
#ifdef DEBUG
|
|
if (sg)
|
|
debug("[CtS] trying servergroup \"%s\" (%i)\n",s,sg->servergroup);
|
|
else
|
|
debug("[CtS] trying servergroup \"%s\" (not found)\n",s);
|
|
#endif /* DEBUG */
|
|
}
|
|
else
|
|
{
|
|
sgi = 0;
|
|
}
|
|
for(sp=serverlist;sp;sp=sp->next)
|
|
{
|
|
if ((sgi == 0 || sp->servergroup == sgi || sp->servergroup == 0) && sp->lastattempt != now)
|
|
{
|
|
if ((!sptry) || (sp->usenum < sptry->usenum))
|
|
{
|
|
if (sp->err == 0)
|
|
sptry = sp;
|
|
else
|
|
if (
|
|
(sp->err == SP_THROTTLED && (sp->lastattempt + 45) < now) || /* retry throttled after 45 seconds */
|
|
(sp->err == SP_KLINED && (sp->lastattempt + 86400) < now) /* retry Klined after a day */
|
|
)
|
|
sptry = sp;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Connect...
|
|
*/
|
|
if (sptry)
|
|
try_server(sptry,NULL);
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
const char *errtxt;
|
|
|
|
if (current->connect != CN_SPINNING)
|
|
{
|
|
debug("[CtS] Serverlist Exhausted:\n");
|
|
for(sp=serverlist;sp;sp=sp->next)
|
|
{
|
|
errtxt = (const char *[]){"No error","SP_NOAUTH","SP_KLINED","SP_FULLCLASS",
|
|
"SP_TIMEOUT","SP_ERRCONN","SP_DIFFPORT","SP_NO_DNS","SP_THROTTLED"}[sp->err];
|
|
debug("[CtS] (%i) %s[%i]: %s(%i) / servergroup %i\n",sp->ident,(sp->realname[0]) ? sp->realname : sp->name,
|
|
sp->port,errtxt,sp->err,sp->servergroup);
|
|
}
|
|
debug("[CtS] Server connection is spinning...\n");
|
|
}
|
|
current->connect = CN_SPINNING;
|
|
#endif /* DEBUG */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* === according to rfc1459 ===
|
|
* Command: USER
|
|
* Parameters: <username> <hostname> <servername> <realname>
|
|
*/
|
|
void register_with_server(void)
|
|
{
|
|
Server *sp;
|
|
char *ident,*ircname;
|
|
int sendpass;
|
|
|
|
#ifdef IRCD_EXTENSIONS
|
|
current->ircx_flags = 0;
|
|
#endif /* IRCD_EXTENSIONS */
|
|
sp = find_server(current->server);
|
|
ident = current->setting[STR_IDENT].str_var;
|
|
ircname = current->setting[STR_IRCNAME].str_var;
|
|
sendpass = (sp && *sp->pass);
|
|
to_server((sendpass) ? "PASS :%s\nNICK %s\nUSER %s " MECHUSERLOGIN " 0 :%s\n" :
|
|
"%sNICK %s\nUSER %s " MECHUSERLOGIN " 0 :%s\n",
|
|
(sendpass) ? sp->pass : "",
|
|
current->wantnick,
|
|
(ident) ? ident : BOTLOGIN,
|
|
(ircname) ? ircname : VERSION);
|
|
current->connect = CN_CONNECTED;
|
|
current->conntry = now;
|
|
}
|
|
|
|
/*
|
|
* scripting stuff
|
|
*/
|
|
|
|
#ifdef SCRIPTING
|
|
|
|
int sub_compile_timer(int limit, uint32_t *flags1, uint32_t *flags2, char *args)
|
|
{
|
|
char *s,*dash;
|
|
uint32_t f;
|
|
int n,hi,lo;
|
|
|
|
*flags1 = 0;
|
|
if (flags2) *flags2 = 0;
|
|
|
|
if (!args || !*args)
|
|
return -1;
|
|
|
|
if (args[0] == '*' && args[1] == 0)
|
|
{
|
|
*flags1 = -1; /* 0-29 = 1 */
|
|
if (flags2) *flags2 = -1; /* 30-59 = 1 */
|
|
return 0;
|
|
}
|
|
|
|
/* "n,n,n-m,n-m" */
|
|
for(s=args;*s;s++)
|
|
if (*s == ',')
|
|
*s = ' ';
|
|
|
|
/* "n n n-m n-m" */
|
|
do
|
|
{
|
|
s = chop(&args);
|
|
if (s && *s)
|
|
{
|
|
if ((dash = STRCHR(s,'-')))
|
|
{
|
|
*(dash++) = 0;
|
|
if (!*dash)
|
|
return -1;
|
|
|
|
lo = asc2int(s);
|
|
hi = asc2int(dash);
|
|
|
|
if (lo < 0 || lo > limit || hi < 0 || hi > limit)
|
|
return -1;
|
|
for(n=lo;n<=hi;n++)
|
|
{
|
|
if (n >= 30)
|
|
{
|
|
f = (1 << (n - 30));
|
|
if (!flags2)
|
|
return -1;
|
|
*flags2 |= f;
|
|
}
|
|
else
|
|
{
|
|
f = (1 << n);
|
|
*flags1 |= f;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
n = asc2int(s);
|
|
if (n < 0 || n > limit)
|
|
return -1;
|
|
if (n >= 30)
|
|
{
|
|
f = (1 << (n - 30));
|
|
if (!flags2)
|
|
return -1;
|
|
*flags2 |= f;
|
|
}
|
|
else
|
|
{
|
|
f = (1 << n);
|
|
*flags1 |= f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while(s);
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
|
|
turn this:
|
|
"* * * *"
|
|
"0-59 0-59 0-23 0-6"
|
|
0,1,2,3,...,58,59 = all seconds
|
|
0,1,2,3,...,58,59 = all minutes
|
|
0,1,2,3,...,22,23 = all hours
|
|
0,1,2,3,4,5,6 = all weekdays
|
|
|
|
into this:
|
|
|
|
typedef struct
|
|
{
|
|
time_t last;
|
|
time_t next;
|
|
uint32_t second1:30;
|
|
uint32_t second2:30;
|
|
uint32_t minute1:30;
|
|
uint32_t minute2:30;
|
|
uint32_t hour:24;
|
|
uint32_t weekday:7;
|
|
|
|
} HookTimer;
|
|
|
|
//using that struct, calculate when the next time will be
|
|
//start by determining what the time is now
|
|
|
|
thistime = now;
|
|
|
|
//which second is it
|
|
thissecond = thistime % 60;
|
|
|
|
if (ht->second1 == 0x3FFFFFFF && ht->second2 == 0x3FFFFFFF)
|
|
{
|
|
// dont add waiting period to get to the proper second
|
|
}
|
|
|
|
//which minute is it
|
|
thistime = (thistime - thissecond) / 60;
|
|
thisminute = thistime % 60;
|
|
if (ht->minute1 == 0x3FFFFFFF && ht->minute2 == 0x3FFFFFFF)
|
|
{
|
|
// dont add waiting period to get to the proper minute
|
|
}
|
|
|
|
//which hour is it
|
|
thistime = (thistime - thisminute) / 60;
|
|
thishour = thistime % 24;
|
|
if (ht->hour == 0xFFFFFF)
|
|
{
|
|
// dont add waiting period to get to the proper hour
|
|
}
|
|
|
|
//which weekday is it
|
|
thistime = (thistime - thishour) / 60; //thistime is now = day since epoch
|
|
if (ht->weekday == 0x7F) // every day
|
|
{
|
|
// dont add waiting period to get to the correct day
|
|
}
|
|
|
|
#endif /* 0 */
|
|
|
|
/*
|
|
* return -1 on failure
|
|
*/
|
|
int compile_timer(HookTimer *timer, char *rest)
|
|
{
|
|
char backup[strlen(rest)+1];
|
|
char *sec,*min,*hour,*day;
|
|
|
|
stringcpy(backup,rest);
|
|
rest = backup;
|
|
#ifdef DEBUG
|
|
debug("(compile_timer) rest = %s\n",nullstr(rest));
|
|
#endif /* DEBUG */
|
|
|
|
sec = chop(&rest);
|
|
min = chop(&rest);
|
|
hour = chop(&rest);
|
|
day = chop(&rest);
|
|
|
|
if (sub_compile_timer(59,&timer->second1,&timer->second2,sec) < 0)
|
|
return -1;
|
|
if (sub_compile_timer(59,&timer->minute1,&timer->minute2,min) < 0)
|
|
return -1;
|
|
if (sub_compile_timer(23,&timer->hour,NULL,hour) < 0)
|
|
return -1;
|
|
if (sub_compile_timer(6,&timer->weekday,NULL,day) < 0)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
#endif /* SCRIPTING */
|
|
|
|
/*
|
|
* Periodic updates
|
|
*/
|
|
|
|
void update(SequenceTime *this)
|
|
{
|
|
Server *sp;
|
|
Chan *chan;
|
|
char *temp,*chantemp;
|
|
int tt,th;
|
|
int x,n;
|
|
|
|
tt = now / 600; /* current 10-minute period */
|
|
th = tt / 6; /* current hour */
|
|
|
|
#ifdef DEBUG
|
|
x = 0;
|
|
if (tt != this->tenminute)
|
|
{
|
|
debug("(update) running: ten minute updates [%i]",tt);
|
|
x++;
|
|
}
|
|
if (th != this->hour)
|
|
{
|
|
debug("%shour updates [%i]",(x) ? ", " : "(update) running: ",th);
|
|
x++;
|
|
}
|
|
if (x)
|
|
debug("\n");
|
|
#endif /* DEBUG */
|
|
|
|
short_tv &= ~TV_REJOIN;
|
|
for(current=botlist;current;current=current->next)
|
|
{
|
|
if (current->reset || current->connect != CN_ONLINE)
|
|
continue;
|
|
|
|
if (current->rejoin)
|
|
{
|
|
if ((now - current->lastrejoin) > REJOIN_DELAY)
|
|
{
|
|
current->rejoin = FALSE;
|
|
current->lastrejoin = now;
|
|
}
|
|
short_tv |= TV_REJOIN;
|
|
}
|
|
|
|
#ifdef NOTIFY
|
|
send_ison();
|
|
#endif /* NOTIFY */
|
|
|
|
for(chan=current->chanlist;chan;chan=chan->next)
|
|
{
|
|
if (!chan->active)
|
|
{
|
|
if (!current->rejoin && chan->rejoin)
|
|
{
|
|
/*
|
|
* join_channel() will set current->rejoin to TRUE
|
|
*/
|
|
join_channel(chan->name,chan->key);
|
|
}
|
|
}
|
|
|
|
if (tt != this->tenminute)
|
|
{
|
|
chan->last10 = chan->this10;
|
|
chan->this10 = 0;
|
|
}
|
|
if (th != this->hour)
|
|
{
|
|
chan->last60 = chan->this60;
|
|
chan->this60 = 0;
|
|
#ifdef STATS
|
|
if ((temp = chan->setting[STR_STATS].str_var))
|
|
stats_loghour(chan,temp,this->hour);
|
|
#endif /* STATS */
|
|
}
|
|
#ifdef DYNAMODE
|
|
if (chan->bot_is_op && chan->sync && chan->setting[STR_DYNLIMIT].str_var)
|
|
check_dynamode(chan);
|
|
#endif /* DYNAMODE */
|
|
}
|
|
if ((now - current->lastreset) > RESETINTERVAL)
|
|
{
|
|
current->lastreset = now;
|
|
if (stringcmp(current->nick,current->wantnick))
|
|
to_server("NICK %s\n",current->wantnick);
|
|
check_idlekick();
|
|
if ((x = current->setting[INT_AAWAY].int_var) && current->away == FALSE)
|
|
{
|
|
if ((now - current->activity) > (x * 60))
|
|
{
|
|
temp = randstring(AWAYFILE);
|
|
to_server(AWAYFORM,(temp && *temp) ? temp : "auto-away",time2away(now));
|
|
current->away = TRUE;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* 10 minute update.
|
|
*/
|
|
if (tt != this->tenminute)
|
|
{
|
|
/*
|
|
* use `x' to count channels for the status line
|
|
*/
|
|
x = 0;
|
|
for(chan=current->chanlist;chan;chan=chan->next)
|
|
{
|
|
#ifdef BOTNET
|
|
if (chan->bot_is_op == FALSE)
|
|
{
|
|
botnet_relay(NULL,"CO%i %s\n",current->guid,chan->name);
|
|
}
|
|
#endif /* BOTNET */
|
|
if ((n = chan->setting[INT_AUB].int_var))
|
|
{
|
|
channel_massunban(chan,MATCH_ALL,60*n);
|
|
}
|
|
x++;
|
|
}
|
|
temp = TEXT_NOTINSERVLIST;
|
|
if ((sp = find_server(current->server)))
|
|
{
|
|
sprintf(gsockdata,"%s:%i",(*sp->realname) ? sp->realname : sp->name,sp->port);
|
|
temp = gsockdata;
|
|
}
|
|
chantemp = (current->activechan) ? current->activechan->name : TEXT_NONE;
|
|
send_spy(SPYSTR_STATUS,"C:%s AC:%i CS:%s",chantemp,x,temp);
|
|
}
|
|
/*
|
|
* Hourly update.
|
|
*/
|
|
if (th != this->hour)
|
|
{
|
|
if (current->userlist && current->ul_save)
|
|
{
|
|
temp = current->setting[STR_USERFILE].str_var;
|
|
if (!write_userlist(temp))
|
|
send_spy(SPYSTR_STATUS,(temp) ? ERR_NOSAVE : ERR_NOUSERFILENAME,temp);
|
|
else
|
|
send_spy(SPYSTR_STATUS,TEXT_LISTSAVED,temp);
|
|
}
|
|
#ifdef SEEN
|
|
if (seenfile && !write_seenlist())
|
|
{
|
|
send_spy(SPYSTR_STATUS,TEXT_SEENNOSAVE,seenfile);
|
|
}
|
|
#endif /* SEEN */
|
|
#ifdef NOTIFY
|
|
if (current->notifylist)
|
|
write_notifylog();
|
|
#endif /* NOTIFY */
|
|
}
|
|
}
|
|
|
|
#ifdef SESSION
|
|
if (th != this->hour)
|
|
{
|
|
if (!write_session())
|
|
send_global(SPYSTR_STATUS,"Session could not be saved to file %s",SESSIONFILE);
|
|
}
|
|
#endif /* SESSION */
|
|
|
|
this->tenminute = tt;
|
|
this->hour = th;
|
|
}
|
|
|
|
/*
|
|
* Read data from server socket
|
|
*/
|
|
void parse_server_input(void)
|
|
{
|
|
#ifdef WINGATE
|
|
Server *sp;
|
|
#endif /* WINGATE */
|
|
char linebuf[MSGLEN];
|
|
char *res;
|
|
|
|
if (FD_ISSET(current->sock,&write_fds))
|
|
{
|
|
setbotnick(current,current->wantnick);
|
|
#ifdef DEBUG
|
|
debug("[PSI] {%i} connection established (%s) [ASYNC]\n",
|
|
current->sock,current->wantnick);
|
|
#endif /* DEBUG */
|
|
#ifdef WINGATE
|
|
if ((current->vhost_type & VH_WINGATE_BOTH) == VH_WINGATE)
|
|
{
|
|
sp = find_server(current->server);
|
|
if (!sp)
|
|
{
|
|
#ifdef IDWRAP
|
|
unlink_identfile();
|
|
#endif /* IDWRAP */
|
|
close(current->sock);
|
|
current->sock = -1;
|
|
return;
|
|
}
|
|
#ifdef DEBUG
|
|
debug("[PSI] Connecting via WinGate proxy...\n");
|
|
#endif /* DEBUG */
|
|
to_server("%s %i\n",sp->name,sp->port);
|
|
/*
|
|
* If we fail here, we'd better drop the WinGate
|
|
* and retry the SAME server again
|
|
*/
|
|
if (current->sock == -1)
|
|
{
|
|
current->nextserver = sp->ident;
|
|
current->vhost_type |= VH_WINGATE_FAIL;
|
|
}
|
|
current->connect = CN_WINGATEWAIT;
|
|
current->conntry = now;
|
|
return;
|
|
}
|
|
#endif /* WINGATE */
|
|
/*
|
|
* send NICK, USER and maybe PASS
|
|
*/
|
|
register_with_server();
|
|
#ifdef IDWRAP
|
|
if (current->sock == -1)
|
|
unlink_identfile();
|
|
#endif /* IDWRAP */
|
|
return;
|
|
}
|
|
if (FD_ISSET(current->sock,&read_fds))
|
|
{
|
|
get_line:
|
|
res = sockread(current->sock,current->sockdata,linebuf);
|
|
if (res)
|
|
{
|
|
#ifdef WINGATE
|
|
if (current->connect == CN_WINGATEWAIT)
|
|
{
|
|
if (!matches("Connecting to host *Connected",linebuf))
|
|
{
|
|
#ifdef DEBUG
|
|
debug("[PSI] WinGate proxy active\n");
|
|
#endif /* DEBUG */
|
|
register_with_server();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
#ifdef IDWRAP
|
|
unlink_identfile();
|
|
#endif /* IDWRAP */
|
|
close(current->sock);
|
|
current->sock = -1;
|
|
return;
|
|
}
|
|
}
|
|
#endif /* WINGATE */
|
|
parseline(linebuf);
|
|
goto get_line;
|
|
}
|
|
switch(errno)
|
|
{
|
|
case EINTR:
|
|
case EAGAIN:
|
|
break;
|
|
default:
|
|
#ifdef DEBUG
|
|
debug("[PSI] {%i} errno = %i; closing server socket\n",current->sock,errno);
|
|
#endif /* DEBUG */
|
|
*current->sockdata = 0;
|
|
#ifdef IDWRAP
|
|
unlink_identfile();
|
|
#endif /* IDWRAP */
|
|
close(current->sock);
|
|
current->sock = -1;
|
|
current->connect = CN_NOSOCK;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*
|
|
* commands associated with core.c
|
|
*
|
|
*/
|
|
|
|
void do_version(COMMAND_ARGS)
|
|
{
|
|
to_user_q(from,"%s %s",BOTCLASS,VERSION);
|
|
}
|
|
|
|
void do_core(COMMAND_ARGS)
|
|
{
|
|
char tmp[MSGLEN]; /* big buffers at the top */
|
|
Server *sp;
|
|
Chan *chan;
|
|
User *user;
|
|
char *pt;
|
|
int i,u,su,bu;
|
|
|
|
u = su = bu = 0;
|
|
for(user=current->userlist;user;user=user->next)
|
|
{
|
|
u++;
|
|
if (user->x.x.access == OWNERLEVEL)
|
|
su++;
|
|
if (user->x.x.access == BOTLEVEL)
|
|
bu++;
|
|
}
|
|
|
|
i = stringcmp(current->nick,current->wantnick);
|
|
if (i)
|
|
table_buffer(TEXT_CURRNICKWANT,current->nick,current->wantnick,current->guid);
|
|
else
|
|
table_buffer(TEXT_CURRNICKHAS,current->nick,current->guid);
|
|
table_buffer(TEXT_USERLISTSTATS,u,su,EXTRA_CHAR(su),bu,EXTRA_CHAR(bu));
|
|
|
|
pt = tmp;
|
|
*pt = i = 0;
|
|
for(chan=current->chanlist;chan;chan=chan->next)
|
|
{
|
|
if (*tmp && ((pt - tmp) + strlen(chan->name) > 57))
|
|
{
|
|
table_buffer((i) ? TEXT_MOREACTIVECHANS : TEXT_ACTIVECHANS,tmp);
|
|
pt = tmp;
|
|
i++;
|
|
}
|
|
if (chan->bot_is_op)
|
|
*(pt++) = '@';
|
|
if (chan == current->activechan)
|
|
{
|
|
*(pt++) = '\037';
|
|
pt = stringcpy(pt,chan->name);
|
|
*(pt++) = '\037';
|
|
}
|
|
else
|
|
{
|
|
pt = stringcpy(pt,chan->name);
|
|
}
|
|
if (chan->next)
|
|
*(pt++) = ' ';
|
|
*pt = 0;
|
|
}
|
|
table_buffer((i) ? TEXT_MOREACTIVECHANS : TEXT_ACTIVECHANS,(*tmp) ? tmp : TEXT_NONE);
|
|
|
|
if (current->setting[STR_VIRTUAL].str_var)
|
|
{
|
|
if ((current->vhost_type & VH_IPALIAS_FAIL) == 0)
|
|
pt = "";
|
|
else
|
|
pt = TEXT_VHINACTIVE;
|
|
table_buffer(TEXT_VIRTHOST,current->setting[STR_VIRTUAL].str_var,pt);
|
|
}
|
|
#ifdef WINGATE
|
|
if (current->setting[STR_WINGATE].str_var && current->setting[INT_WINGPORT].int_var)
|
|
{
|
|
if ((current->vhost_type & VH_WINGATE_FAIL) == 0)
|
|
pt = "";
|
|
else
|
|
pt = TEXT_VHINACTIVE;
|
|
table_buffer(TEXT_VIRTHOSTWINGATE,current->setting[STR_WINGATE].str_var,
|
|
current->setting[INT_WINGPORT].int_var,pt);
|
|
}
|
|
#endif /* WINGATE */
|
|
sp = find_server(current->server);
|
|
if (sp)
|
|
table_buffer(TEXT_CURRSERVER,
|
|
(sp->realname[0]) ? sp->realname : sp->name,sp->port);
|
|
else
|
|
table_buffer(TEXT_CURRSERVERNOT);
|
|
table_buffer(TEXT_SERVERONTIME,idle2str(now - current->ontime,FALSE));
|
|
table_buffer(TEXT_BOTMODES,(*current->modes) ? current->modes : TEXT_NONE);
|
|
table_buffer(TEXT_CURRENTTIME,time2str(now));
|
|
table_buffer(TEXT_BOTSTARTED,time2str(uptime));
|
|
table_buffer(TEXT_BOTUPTIME,idle2str(now - uptime,FALSE));
|
|
table_buffer(TEXT_BOTVERSION,VERSION,SRCDATE);
|
|
table_buffer(TEXT_BOTFEATURES,__mx_opts);
|
|
table_send(from,2);
|
|
}
|
|
|
|
void do_die(COMMAND_ARGS)
|
|
{
|
|
/*
|
|
* on_msg checks GAXS
|
|
*/
|
|
char *reason;
|
|
|
|
#ifdef SESSION
|
|
write_session();
|
|
#endif /* SESSION */
|
|
|
|
if (!rest || !*rest)
|
|
{
|
|
if ((reason = randstring(SIGNOFFSFILE)) == NULL)
|
|
reason = "I'll get you for this!!!";
|
|
}
|
|
else
|
|
{
|
|
reason = rest;
|
|
if (!uptime)
|
|
{
|
|
to_file(1,"init: %s\n",rest);
|
|
_exit(0);
|
|
}
|
|
}
|
|
|
|
set_mallocdoer(do_die);
|
|
current->signoff = stringdup(reason);
|
|
set_mallocdoer(do_die);
|
|
current->from = stringdup(from);
|
|
|
|
current->connect = CN_BOTDIE;
|
|
}
|
|
|
|
void do_shutdown(COMMAND_ARGS)
|
|
{
|
|
send_global(SPYSTR_STATUS,TEXT_SHUTDOWNBY,nickcpy(NULL,from));
|
|
|
|
#ifdef SESSION
|
|
write_session();
|
|
#endif /* SESSION */
|
|
|
|
kill_all_bots(NULL);
|
|
/* NOT REACHED */
|
|
}
|
|
|
|
void do_servergroup(COMMAND_ARGS)
|
|
{
|
|
ServerGroup *sg,*new,**sgp;
|
|
char *name;
|
|
|
|
name = chop(&rest);
|
|
|
|
/*
|
|
* no args, list servergroups
|
|
*/
|
|
if (!name)
|
|
{
|
|
table_buffer(str_underline("id") "\t" str_underline("name"));
|
|
for(sg=servergrouplist;sg;sg=sg->next)
|
|
{
|
|
table_buffer("%i\t%s%s",sg->servergroup,sg->name,(sg == currentservergroup) ? " (current)" : "");
|
|
}
|
|
table_send(from,2);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* find pre-existing severgroup by the same name (case-insensitive)
|
|
*/
|
|
sg = getservergroup(name);
|
|
if (!sg)
|
|
{
|
|
#ifdef DEBUG
|
|
debug("(do_servergroup) creating new servergroup: %s\n",name);
|
|
#endif /* DEBUG */
|
|
set_mallocdoer(do_servergroup);
|
|
new = (ServerGroup*)Calloc(sizeof(ServerGroup) + strlen(name));
|
|
servergroupid++;
|
|
new->servergroup = servergroupid;
|
|
stringcpy(new->name,name);
|
|
sgp = &servergrouplist;
|
|
while(*sgp)
|
|
sgp = &(*sgp)->next;
|
|
sg = *sgp = new;
|
|
#ifdef DEBUG
|
|
{
|
|
ServerGroup *g;
|
|
|
|
for (g=servergrouplist;g;g=g->next)
|
|
{
|
|
debug("(do_servergroup) %s (%i)\n",g->name,g->servergroup);
|
|
}
|
|
}
|
|
#endif /* DEBUG */
|
|
}
|
|
currentservergroup = sg;
|
|
#ifdef DEBUG
|
|
debug("(do_servergroup) current servergroup set to \"%s\" (%i)\n",sg->name,sg->servergroup);
|
|
#endif /* DEBUG */
|
|
}
|
|
|
|
void do_server(COMMAND_ARGS)
|
|
{
|
|
ServerGroup *sg;
|
|
Server *sp,*dp,**spp;
|
|
char *server,*aport,*pass;
|
|
char addc,*last,*quitmsg = TEXT_TRYNEWSERVER;
|
|
int n,iport,sgi;
|
|
|
|
if (CurrentCmd->name == C_NEXTSERVER)
|
|
{
|
|
quitmsg = TEXT_SWITCHSERVER;
|
|
to_user(from,FMT_PLAIN,quitmsg);
|
|
goto do_server_jump;
|
|
}
|
|
server = chop(&rest);
|
|
|
|
/*
|
|
* no args, list all known servers
|
|
*/
|
|
if (!server)
|
|
{
|
|
if (partyline_only_command(from))
|
|
return;
|
|
if (servergrouplist->next)
|
|
table_buffer(str_underline("server") "\t" str_underline("last connect") "\t" str_underline("group"));
|
|
else
|
|
table_buffer(str_underline("server") "\t" str_underline("last connect"));
|
|
sgi = -1;
|
|
for(sp=serverlist;sp;sp=sp->next)
|
|
{
|
|
if (sp->lastconnect)
|
|
last = idle2str(now - sp->lastconnect,FALSE);
|
|
else
|
|
{
|
|
switch(sp->err)
|
|
{
|
|
case SP_NOAUTH:
|
|
last = TEXT_SP_NOAUTH;
|
|
break;
|
|
case SP_KLINED:
|
|
last = TEXT_SP_KLINED;
|
|
break;
|
|
case SP_FULLCLASS:
|
|
last = TEXT_SP_FULLCLASS;
|
|
break;
|
|
case SP_TIMEOUT:
|
|
last = TEXT_SP_TIMEOUT;
|
|
break;
|
|
case SP_ERRCONN:
|
|
last = TEXT_SP_ERRCONN;
|
|
break;
|
|
case SP_DIFFPORT:
|
|
last = TEXT_SP_DIFFPORT;
|
|
break;
|
|
case SP_NO_DNS:
|
|
last = TEXT_SP_NO_DNS;
|
|
break;
|
|
default:
|
|
last = TEXT_NEVER;
|
|
}
|
|
}
|
|
if (servergrouplist->next)
|
|
{
|
|
if (sgi != sp->servergroup)
|
|
{
|
|
sg = getservergroupid(sp->servergroup);
|
|
if (sg)
|
|
sgi = sg->servergroup;
|
|
}
|
|
table_buffer("%s:%i\t%s%s%s\t%s",(*sp->realname) ? sp->realname : sp->name,sp->port,
|
|
last,(sp->lastconnect) ? TEXT_AGO : "",(sp->ident == current->server) ? TEXT_CURRENT : "",
|
|
(sg) ? sg->name : "(unknown)");
|
|
}
|
|
else
|
|
table_buffer("%s:%i\t%s%s%s",(*sp->realname) ? sp->realname : sp->name,sp->port,
|
|
last,(sp->lastconnect) ? TEXT_AGO : "",(sp->ident == current->server) ? TEXT_CURRENT : "");
|
|
}
|
|
table_send(from,2);
|
|
return;
|
|
}
|
|
|
|
addc = *server;
|
|
if (addc == '-' || addc == '+')
|
|
{
|
|
server++;
|
|
if (!*server)
|
|
{
|
|
usage(from);
|
|
return;
|
|
}
|
|
}
|
|
if (strlen(server) >= MAXHOSTLEN)
|
|
{
|
|
to_user(from,TEXT_NAMETOOLONG);
|
|
return;
|
|
}
|
|
|
|
aport = chop(&rest);
|
|
pass = chop(&rest);
|
|
iport = asc2int(aport);
|
|
|
|
if (aport && *aport == COMMENT_CHAR)
|
|
{
|
|
aport = pass = NULL;
|
|
}
|
|
else
|
|
if (pass && *pass == COMMENT_CHAR)
|
|
{
|
|
pass = NULL;
|
|
}
|
|
|
|
if (aport && (errno || iport < 1 || iport > 65534))
|
|
{
|
|
usage(from);
|
|
return;
|
|
}
|
|
if (!aport)
|
|
{
|
|
iport = 0;
|
|
}
|
|
|
|
if (addc == '-')
|
|
{
|
|
if (!serverlist)
|
|
{
|
|
to_user(from,TEXT_EMPTYSERVLIST);
|
|
return;
|
|
}
|
|
n = 0;
|
|
dp = NULL;
|
|
for(sp=serverlist;sp;sp=sp->next)
|
|
{
|
|
if ((!stringcasecmp(server,sp->name)) || (!stringcasecmp(server,sp->realname)))
|
|
{
|
|
if (!iport || (iport && sp->port == iport))
|
|
{
|
|
dp = sp;
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
switch(n)
|
|
{
|
|
case 0:
|
|
to_user(from,(iport) ? TEXT_NOSERVMATCHP : TEXT_NOSERVMATCH,server,iport);
|
|
break;
|
|
case 1:
|
|
to_user(from,TEXT_SERVERDELETED,server,dp->port);
|
|
for(spp=&serverlist;*spp;spp=&(*spp)->next)
|
|
{
|
|
if (*spp == dp)
|
|
{
|
|
*spp = dp->next;
|
|
Free((void*)&dp);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
to_user(from,TEXT_MANYSERVMATCH,server);
|
|
}
|
|
return;
|
|
}
|
|
sp = add_server(server,iport,pass);
|
|
if (!sp)
|
|
{
|
|
to_user(from,"Problem adding server: %s",server);
|
|
return;
|
|
}
|
|
if (addc || from == CoreUser.name)
|
|
return;
|
|
|
|
current->nextserver = sp->ident;
|
|
to_user(from,"Trying new server: %s on port %i",server,iport);
|
|
do_server_jump:
|
|
switch(current->connect)
|
|
{
|
|
case CN_CONNECTED:
|
|
case CN_ONLINE:
|
|
to_server("QUIT :%s\n",quitmsg);
|
|
killsock(current->sock);
|
|
break;
|
|
default:
|
|
if (current->sock != -1)
|
|
close(current->sock);
|
|
}
|
|
current->sock = -1;
|
|
}
|
|
|
|
void do_cserv(COMMAND_ARGS)
|
|
{
|
|
Server *sp;
|
|
|
|
if ((sp = find_server(current->server)))
|
|
to_user(from,TEXT_CSERV,(*sp->realname) ? sp->realname : sp->name,sp->port);
|
|
else
|
|
to_user(from,TEXT_CSERVNOT);
|
|
}
|
|
|
|
void do_away(COMMAND_ARGS)
|
|
{
|
|
if (!rest || !*rest)
|
|
{
|
|
to_server("AWAY\n");
|
|
to_user(from,TEXT_NOLONGERAWAY);
|
|
current->away = FALSE;
|
|
current->activity = now;
|
|
return;
|
|
}
|
|
to_server(AWAYFORM,rest,time2away(now));
|
|
to_user(from,TEXT_NOWSETAWAY);
|
|
current->away = TRUE;
|
|
}
|
|
|
|
void do_do(COMMAND_ARGS)
|
|
{
|
|
/*
|
|
* on_msg checks: CARGS + GAXS
|
|
*/
|
|
to_server(FMT_PLAINLINE,rest);
|
|
}
|
|
|
|
void do_nick(COMMAND_ARGS)
|
|
{
|
|
/*
|
|
* on_msg checks: GAXS + CARGS
|
|
*/
|
|
Mech *backup;
|
|
char *nick;
|
|
int guid;
|
|
|
|
nick = chop(&rest);
|
|
if (!nick || !*nick)
|
|
{
|
|
usage(from); /* usage for CurrentCmd->name */
|
|
return;
|
|
}
|
|
guid = asc2int(nick);
|
|
backup = current;
|
|
if (!errno)
|
|
{
|
|
nick = chop(&rest);
|
|
for(current=botlist;current;current=current->next)
|
|
{
|
|
if (current->guid == guid)
|
|
break;
|
|
if (current->guid == 0)
|
|
break;
|
|
}
|
|
}
|
|
if (!is_nick(nick))
|
|
{
|
|
current = backup;
|
|
to_user(from,ERR_NICK,nick);
|
|
return;
|
|
}
|
|
if (!current)
|
|
{
|
|
#ifdef DEBUG
|
|
debug("(do_nick) Adding new bot: guid = %i; nick = %s\n",guid,nick);
|
|
#endif /* DEBUG */
|
|
current = add_bot(guid,nick);
|
|
if (!sigmaster)
|
|
sigmaster = guid;
|
|
if (from == CoreUser.name)
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (current->guid == 0)
|
|
{
|
|
Free((char**)¤t->nick);
|
|
set_mallocdoer(do_nick);
|
|
current->nick = stringdup(nick);
|
|
current->guid = guid;
|
|
}
|
|
Free((char**)¤t->wantnick);
|
|
set_mallocdoer(do_nick);
|
|
current->wantnick = stringdup(nick);
|
|
to_server("NICK %s\n",current->wantnick);
|
|
}
|
|
current = backup;
|
|
}
|
|
|
|
void do_time(COMMAND_ARGS)
|
|
{
|
|
to_user_q(from,"Current time: %s",time2away(now));
|
|
}
|
|
|
|
void do_upontime(COMMAND_ARGS)
|
|
{
|
|
to_user_q(from,CurrentCmd->cmdarg,
|
|
idle2str(now - ((CurrentCmd->name == C_UPTIME) ? uptime : current->ontime),FALSE));
|
|
}
|
|
|
|
void do_msg(COMMAND_ARGS)
|
|
{
|
|
/*
|
|
* on_msg checks: CARGS
|
|
*/
|
|
Client *client;
|
|
|
|
to = chop(&rest);
|
|
if (ischannel(to))
|
|
{
|
|
/*
|
|
* need to check channel access
|
|
*/
|
|
if (get_authaccess(from,to) < cmdaccess)
|
|
return;
|
|
}
|
|
|
|
if (*to == '=')
|
|
{
|
|
to++;
|
|
if ((client = find_client(to)) == NULL)
|
|
{
|
|
to_user(from,"I have no DCC connection to %s",to);
|
|
return;
|
|
}
|
|
}
|
|
CoreChan.name = to;
|
|
CurrentChan = (Chan*)&CoreChan;
|
|
to_user_q(to,FMT_PLAIN,rest);
|
|
to_user(from,"Message sent to %s",to);
|
|
}
|