/* main.c -- handles: changing nicknames when the desired nick is in use flood detection signal handling telnet code translation command line arguments connecting to a server (bot and helpbot) interpreting server responses (main loop) dprintf'ized, 15nov95 */ /* This file is part of the eggdrop source code copyright (c) 1997 Robey Pointer and is distributed according to the GNU general public license. 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. The author (Robey Pointer) can be reached at: robey@netcom.com */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #ifdef STOP_UAC /* osf/1 complains a lot */ # include # define UAC_NOPRINT 0x00000001 /* Don't report unaligned fixups */ #endif /* some systems have a working sys/wait.h even though configure will */ /* decide it's not bsd compatable. oh well. */ #include "../lush.h" #include "eggdrop.h" #include "chan.h" #include "proto.h" /* number of seconds to wait between transmitting queued lines to the server */ /* lower this value at your own risk. ircd is known to start flood control */ /* at 512 bytes/2 seconds */ #define msgrate 2 /* solaris needs this */ #define _POSIX_SOURCE 1 /* Version info is no longer done here. Search down for "PATCH INFO GOES HERE" and add a line like this: PATCH("snarf"); If the old version info was "eggdrop v1.0z" it will now appear as "eggdrop v1.0z+snarf". The info returned by Tcl $version will change from "1.0z 1260" to "1.0z+snarf 1261 snarf". Each new PATCH line will increment the patch level (ie, 1260) and replace the version name. But all patch names will be appended to $version (so Tcl scripts can easily figure out what patches are loaded). For example, if you have three PATCH lines: PATCH("p1"); PATCH("snarf"); PATCH("p2"); Only the last one will appear in version info ("eggdrop v1.0z+p2") but $version will have everything ("1.0z+p2 1263 p1 snarf p2"). Please use the PATCH macro instead of directly altering the version string from now on (it makes it much easier to maintain patches). Also please read the README file regarding your rights to distribute modified versions of this bot. Note: Loading more than 10 patches could make your patch level "roll over" to the next release! So try not to do that. :) */ char egg_version[161]="1.0s"; int egg_numver=1190; /* socket that the server is on */ int serv=(-1); /* run in the background? */ #ifdef __CYGWIN32__ int backgrd=0; #else int backgrd=1; #endif /* successful connect yet? */ int online=0; /* foreground: constantly display channel stats? */ int con_chan=0; /* foreground: use the terminal as a party line? */ int term_z=0; /* trying to connect to a server right now? */ time_t trying_server=0L; /* how lagged (in seconds) is the server? */ int server_lag=0; /* bot's username */ char botuser[21]; /* bot's real name field */ char botrealname[121]; /* our current host */ char bothost[121]; /* our server */ char botserver[121]; /* port # to connect to */ int botserverport=DEFAULT_PORT; /* name of the config file */ char configfile[121]="egg.config"; /* possible alternate nickname to use */ char altnick[10]=""; /* temporary thing for nick changes */ char newbotname[10]; /* current position in server list: */ int curserv=0; /* directory of help files (if used) */ char helpdir[121]; /* directory for text files that get dumped */ char textdir[121]=""; /* MSG flood */ int flood_thr=5; int flood_time=60; /* PUBLIC flood */ int flood_pub_thr=10; int flood_pub_time=60; /* JOIN flood */ int flood_join_thr=5; int flood_join_time=60; /* CTCP flood */ int flood_ctcp_thr=3; int flood_ctcp_time=60; /* what, if anything, to send to the server on connection */ char initserver[121]; /* never erase logfiles, no matter how old they are? */ int keep_all_logs=0; /* context storage for fatal crashes */ char cx_file[30]; int cx_line; /* unix-time that the bot loaded up */ time_t online_since; /* bot's user@host (refreshed whenever the bot joins a channel) */ /* may not be correct user@host BUT it's how the server sees it */ char botuserhost[121]; /* using bot in make-userfile mode? (first user to 'hello' becomes master) */ int make_userfile=0; /* never give up when connecting to servers? */ int never_give_up=0; /* permanent owner(s) of the bot */ char owner[121]=""; /* keep trying to regain my intended nickname? */ int keepnick=1; /* set when i send a CTCP AWAKE to myself, cleared when i get it */ int waiting_for_awake=0; /* name of the file for the pid to be stored in */ char pid_file[40]; /* how many minutes past the hour to save the userfile? */ int save_users_at=0; /* how many minutes past the hour to notify users of notes? */ int notify_users_at=0; /* when (military time) to switch logfiles */ int switch_logfiles_at=300; /* version info (long form) */ char version[81]; /* version info (short form) */ char ver[41]; /* patch info */ char egg_xtra[121]; /* server connection time */ time_t server_online=0L; /* static buffer used by various functions */ char SBUF[1024]; extern char botname[]; extern char origbotname[]; extern int dcc_total; extern struct dcc_t dcc[]; extern char dccdir[]; extern char dccin[]; extern int telnet_port; extern char admin[]; extern char notefile[]; extern char newserver[]; extern char newserverpass[]; extern int newserverport; extern int lastsock; extern char *logfile[]; extern char *logchan[]; extern int conmask; extern struct userrec *userlist; extern int cache_hit,cache_miss; extern char userfile[]; extern struct chanset_t *chanset; extern int ban_time; extern int ignore_time; /* send stuff to stderr instead of logfiles? */ int use_stderr=1; void fatal(s,recoverable) char *s; int recoverable; { int i; putlog(LOG_MISC,"*","* %s",s); if (serv>=0) killsock(serv); for (i=0; i (raw stuff from server), pull off who it's from & the code */ void parsemsg(in,from,code,params) char *in,*from,*code,*params; { char *p; from[0]=0; if (in[0]==':') { strcpy(in,&in[1]); p=strchr(in,' '); if (p==NULL) { from[0]=params[0]=0; strcpy(code,in); return; } strcpy(params,p+1); *p=0; strcpy(from,in); *p=' '; p++; strcpy(in,p); } p=strchr(in,' '); if (p==NULL) { strcpy(code,in); params[0]=0; return; } *p=0; strcpy(code,in); *p=' '; strcpy(params,p+1); } /* ping from server */ void gotpong(from,msg) char *from,*msg; { context; split(NULL,msg); fixcolon(msg); /* scrap server name */ waiting_for_awake=0; server_lag=time(NULL)-my_atoul(msg); if (server_lag > 99999) { /* bogus */ server_lag=(-1); } } /* 432 : bad nickname */ void got432(from,msg) char *from,*msg; { context; putlog(LOG_MISC,"*","Server says my nickname is invalid."); /* make random nick. */ makepass(botname); newbotname[0]=0; tprintf(serv,"NICK %s\n",botname); } /* 433 : nickname in use */ /* change nicks till we're acceptable or we give up */ void got433(from,msg) char *from,*msg; { char c,*oknicks="^-_\\[]`zqxjyc",*p; context; /* could be futile attempt to regain nick: */ if (newbotname[0]) { tprintf(serv,"NICK %s\n",newbotname); strcpy(botname,newbotname); newbotname[0]=0; return; } /* alternate nickname defined? */ if ((altnick[0]) && (strcasecmp(altnick,botname)!=0)) { strcpy(botname,altnick); } /* if alt nickname failed, drop thru to here */ else { c=botname[strlen(botname)-1]; p=strchr(oknicks,c); if (((c>='0') && (c<='9')) || (p!=NULL)) { if (p==NULL) { if (c=='9') botname[strlen(botname)-1]=oknicks[0]; else botname[strlen(botname)-1]=c+1; } else { p++; if (!*p) fatal("COULDN'T FIND A SUITABLE NICKNAME.",1); botname[strlen(botname)-1]=(*p); } } else { if (strlen(botname)==9) botname[strlen(botname)-1]='0'; else { botname[strlen(botname)+1]=0; botname[strlen(botname)]='0'; } } } putlog(LOG_MISC,"*","NICK IN USE: Trying '%s'",botname); tprintf(serv,"NICK %s\n",botname); } /* 437 : nickname juped (Euronet) */ void got437(from,msg) char *from,*msg; { char s[512]; struct chanset_t *chan; context; split(NULL,msg); split(s,msg); if (strchr("#&+",s[0]) != NULL) { chan=findchan(s); if (chan!=NULL) { if (chan->stat&CHANACTIVE) { putlog(LOG_MISC,"*","Can't change nickname on %s. Is my nickname banned?",s); got433(from,NULL); } else { putlog(LOG_MISC,"*","Channel %s is juped. :(",s); } } } else { putlog(LOG_MISC,"*","Nickname has been juped."); got433(from,NULL); } } int lastmsgs[5]={ 0,0,0,0,0 }; char lastmsghost[5][81]={ "","","","","" }; time_t lastmsgtime[5]={ 0L,0L,0L,0L,0L }; /* do on NICK, PRIVMSG, and NOTICE -- and JOIN */ int detect_flood(from,chan,which,tochan) char *from; int which,tochan; struct chanset_t *chan; { char *p; time_t t; char h[UHOSTLEN],floodnick[NICKLEN],handle[10],ftype[10]; int thr=0,lapse=0; memberlist *m; if (get_attr_host(from)&(USER_BOT|USER_MASTER|USER_FRIEND)) return 0; /* determine how many are necessary to make a flood */ switch(which) { case FLOOD_PRIVMSG: case FLOOD_NOTICE: if (tochan) { thr=flood_pub_thr; lapse=flood_pub_time; strcpy(ftype,"pub"); } else { thr=flood_thr; lapse=flood_time; strcpy(ftype,"msg"); } break; case FLOOD_JOIN: case FLOOD_NICK: thr=flood_join_thr; lapse=flood_join_time; if (which==FLOOD_JOIN) strcpy(ftype,"join"); else strcpy(ftype,"nick"); break; case FLOOD_CTCP: thr=flood_ctcp_thr; lapse=flood_ctcp_time; strcpy(ftype,"ctcp"); break; } if (thr==0) return 0; /* no flood protection */ /* okay, make sure i'm not flood-checking myself */ strcpy(h,from); splitnick(floodnick,h); if (newbotname[0]) { if (strcasecmp(floodnick,newbotname)==0) return 0; } else if (strcasecmp(floodnick,botname)==0) return 0; if (strcasecmp(h,botuserhost)==0) return 0; /* my user@host (?) */ p=strchr(from,'!'); if (p!=NULL) { p++; p=strchr(p,'@'); } if (p!=NULL) { p++; t=time(NULL); if (strcasecmp(lastmsghost[which],p)!=0) { /* new */ strcpy(lastmsghost[which],p); lastmsgtime[which]=t; lastmsgs[which]=0; return 0; } } if (p==NULL) return 0; /* uh... whatever. */ if (lastmsgtime[which] < t-lapse) { /* flood timer expired, reset it */ lastmsgtime[which]=t; lastmsgs[which]=0; return 0; } lastmsgs[which]++; if (lastmsgs[which]>=thr) { /* FLOOD */ /* reset counters */ lastmsgs[which]=0; lastmsgtime[which]=0; lastmsghost[which][0]=0; get_handle_by_host(handle,from); if (tochan) { if (check_tcl_flud(floodnick,from,handle,ftype,chan->name)) return 0; } else { if (check_tcl_flud(floodnick,from,handle,ftype,"*")) return 0; } if (((which==FLOOD_PRIVMSG) || (which==FLOOD_NOTICE)) && (!tochan)) { /* private msg */ sprintf(h,"*!*@%s",p); putlog(LOG_MISC,"*","Flood from @%s! Placing on ignore!",p); addignore(h,origbotname,"msg/notice flood",time(NULL)+(60*ignore_time)); return 1; } else if ((which==FLOOD_CTCP) && (!tochan)) { /* ctcp flood (off-chan) */ sprintf(h,"*!*@%s",p); putlog(LOG_MISC,"*","CTCP flood from @%s! Placing on ignore!",p); addignore(h,origbotname,"ctcp flood",time(NULL)+(60*ignore_time)); return 1; } else if (which==FLOOD_JOIN) { /* join flood */ char sx[21]; sprintf(h,"*!*@%s",p); strcpy(sx,"join flood"); if (!isbanned(chan,h)) { add_mode(chan,'+','b',h); flush_mode(chan,QUICK); } if ((match_ban(from)) || (u_match_ban(chan->bans,from))) return 1; /* already banned */ putlog(LOG_MISC|LOG_JOIN,chan->name,"Join flood from @%s! Banning.",p); u_addban(chan->bans,h,origbotname,sx,time(NULL)+(60*ban_time)); if (!(chan->stat&CHAN_ENFORCEBANS)) kick_match_since(chan,h,lastmsgtime[which]); return 1; } else if (which==FLOOD_NICK) { /* nick flood */ char sx[21]; struct chanset_t *ch; p=strchr(from,'!'); if (p!=NULL) { *p=0; sprintf(h,"*!%s",p+1); strcpy(sx,"nick flood"); ch=chanset; while (ch!=NULL) { m=ismember(ch,from); if (m!=NULL) { if (!(m->flags & SENTKICK)) { mprintf(serv,"KICK %s %s :nick flood\n",ch->name,from); add_mode(ch,'+','b',h); m->flags |= SENTKICK; } } ch=ch->next; } addban(h,origbotname,sx,time(NULL)+(60*ban_time)); *p='!'; } } else { /* flooding chan! either by public or notice */ p=strchr(from,'!'); if (p!=NULL) { putlog(LOG_MODES,chan->name,"Channel flood from %s -- kicking", from); *p=0; mprintf(serv,"KICK %s %s :flood\n",chan->name,from); *p='!'; return 1; } } } return 0; } void write_debug() { int x; time_t now=time(NULL); char s[80]; putlog(LOG_MISC,"*","* Last context: %s/%d",cx_file,cx_line); x=creat("DEBUG",0644); setsock(x,SOCK_NONSOCK); if (x<0) { putlog(LOG_MISC,"*","* Failed to write DEBUG"); } else { strcpy(s,ctime(&now)); tprintf(x,"Debug (%s) written %s",ver,s); tprintf(x,"Context: %s/%d\n\n",cx_file,cx_line); tell_dcc(-x); tprintf(x,"\n"); debug_mem_to_dcc(-x); close(x); putlog(LOG_MISC,"*","* Wrote DEBUG"); } } void got_bus(z) int z; { write_debug(); fatal("BUS ERROR -- CRASHING!",1); } void got_segv(z) int z; { write_debug(); fatal("SEGMENT VIOLATION -- CRASHING!",1); } void got_fpe(z) int z; { write_debug(); fatal("FLOATING POINT ERROR -- CRASHING!",1); } void got_term(z) int z; { #ifdef DIE_ON_TERMHUP write_userfile(); tprintf(serv,"QUIT :terminate signal\n"); fatal("TERMINATE SIGNAL -- SIGNING OFF"); #else putlog(LOG_MISC,"*","RECEIVED TERMINATE SIGNAL (IGNORING)"); write_userfile(); return; #endif } void got_quit(z) int z; { putlog(LOG_MISC,"*","RECEIVED QUIT SIGNAL (IGNORING)"); return; } void got_hup(z) int z; { #ifdef DIE_ON_TERMHUP write_userfile(); tprintf(serv,"QUIT :hangup signal\n"); fatal("HANGUP SIGNAL -- SIGNING OFF"); #else putlog(LOG_MISC,"*","Received HUP signal: rehashing..."); rehash(); return; #endif } void got_alarm(z) int z; { /* connection to a server was ended prematurely */ return; } void got_usr1(z) int z; { int i; putlog(LOG_MISC,"*","* USER1 SIGNAL: Debugging sockets"); write_debug(); if (fcntl(serv,F_GETFD,0)<0) { putlog(LOG_MISC,"*","* Server socket expired -- pfft"); killsock(serv); serv=(-1); } for (i=0; i= 251) && (*(p+1) <= 254)) { mark=3; if (!*(p+2)) mark=2; /* bogus */ } strcpy((char *)p,(char *)(p+mark)); } } } void strip_telnet(sock,buf,len) int sock,*len; char *buf; { unsigned char *p=(unsigned char *)buf; int mark; while (*p != 0) { while ((*p != 255) && (*p != 0)) p++; /* search for IAC */ if (*p == 255) { p++; mark=2; if (!*p) mark=1; /* bogus */ if ((*p >= 251) && (*p <= 254)) { mark=3; if (!*(p+1)) mark=2; /* bogus */ } if (*p == 251) { /* WILL X -> response: DONT X */ /* except WILL ECHO which we just smile and ignore */ if (!(*(p+1) == 1)) { write(sock,"\377\376",2); write(sock,p+1,1); } } if (*p == 253) { /* DO X -> response: WONT X */ /* except DO ECHO which we just smile and ignore */ if (!(*(p+1) == 1)) { write(sock,"\377\374",2); write(sock,p+1,1); } } if (*p == 246) { /* "are you there?" */ /* response is: "hell yes!" */ write(sock,"\r\nHell, yes!\r\n",14); } /* anything else can probably be ignored */ p--; strcpy((char *)p,(char *)(p+mark)); /* wipe code from buffer */ *len = *len - mark; } } } void change_telnet_port() { int i,j,idx=(-1); /* find old entry if it exists */ for (i=0; i= MAXDCC) { putlog(LOG_MISC,"*","No more DCC slots available: can't open telnet port"); return; } idx=dcc_total; dcc[idx].addr=getmyip(); dcc[idx].type=DCC_TELNET; dcc[idx].u.other=NULL; strcpy(dcc[idx].nick,"(telnet)"); getmyhostname(dcc[idx].host); dcc_total++; } else { /* already an entry */ killsock(dcc[idx].sock); if (!telnet_port) { lostdcc(idx); putlog(LOG_MISC,"*","No longer listening at a telnet port."); return; } } j=telnet_port+20; i=(-1); while ((telnet_port=0) { dcc[idx].port=telnet_port; dcc[idx].sock=i; putlog(LOG_MISC,"*","Listening at telnet port %d",telnet_port); return; } putlog(LOG_MISC,"*","Couldn't find telnet port near %d.",telnet_port); telnet_port=0; lostdcc(idx); } void do_arg(s) char *s; { int i; if (s[0]=='-') for (i=1; i < strlen(s); i++) { if (s[i]=='n') backgrd=0; if (s[i]=='c') { con_chan=1; term_z=0; } if (s[i]=='t') { con_chan=0; term_z=1; } if (s[i]=='m') make_userfile=1; if (s[i]=='v') { char x[256],z[81]; strcpy(x,egg_version); nsplit(z,x); nsplit(z,x); printf("%s\n",version); if (x[0]) printf(" (patches: %s)\n",x); exit(0); } if (s[i]=='h') { printf("\n%s\n\n",version); printf("Command line arguments:\n"); printf(" -h help\n"); printf(" -v print version and exit\n"); printf(" -n don't go into the background\n"); printf(" -c (with -n) display channel stats every 10 seconds\n"); printf(" -t (with -n) use terminal to simulate dcc-chat\n"); printf(" -m userfile creation mode\n"); printf(" optional config filename (default 'egg.config')\n"); printf("\n"); exit(0); } } else strcpy(configfile,s); } /* hook up to a server */ /* works a little differently now... async i/o is your friend */ void connect_server() { char s[121],pass[121]; static int oldserv=(-1); struct chanset_t *chan; context; waiting_for_awake=0; trying_server=time(NULL); empty_msgq(); if ((oldserv<0) || (never_give_up)) oldserv=curserv; /* start up the counter (always reset it if "never-give-up" is on) */ if (newserverport) { /* jump to specified server */ curserv=(-1); /* reset server list */ strcpy(botserver,newserver); botserverport=newserverport; strcpy(pass,newserverpass); newserver[0]=0; newserverport=0; newserverpass[0]=0; oldserv=(-1); } next_server(&curserv,botserver,&botserverport,pass); putlog(LOG_SERV,"*","Trying server %s:%d",botserver, botserverport); serv=open_telnet(botserver,botserverport); if (serv<0) { if (serv==(-2)) strcpy(s,"DNS lookup failed"); else neterror(s); putlog(LOG_SERV,"*","Failed connect to %s (%s)",botserver,s); if ((oldserv==curserv) && !(never_give_up)) fatal("NO SERVERS WILL ACCEPT MY CONNECTION.",1); } else { /* queue standard login */ tprintf(serv,"NICK %s\n",botname); if (pass[0]) tprintf(serv,"PASS %s\n",pass); tprintf(serv,"USER %s %s %s :%s\n",botuser,bothost,botserver,botrealname); chan=chanset; while (chan!=NULL) { mprintf(serv,"JOIN %s %s\n",chan->name,chan->key_prot); chan->stat&=~(CHANACTIVE|CHANPEND); chan=chan->next; } /* wait for async result now */ } } int main(argc,argv) int argc; char **argv; { int xx,i; char buf[520]; time_t now,then; struct tm *nowtm; FILE *f; char s[520],s1[UHOSTLEN],hand[10]; struct sigaction sv; int modecnt=0; int timecnt=0,midnite=0,fivemin=0,hourly=0,hourli=0,miltime,switched=0, lastmin=99,k,l; #ifndef NO_IRC char from[121],code[520],msg[520]; struct chanset_t *chan; int j; memberlist *m; #endif context; /* --- PATCH INFO GOES HERE --- */ /* --- END OF PATCH INFO --- */ /* version info! */ sprintf(ver,"eggdrop v%s",egg_version); sprintf(version,"Eggdrop v%s (c)1997 Robey Pointer",egg_version); /* now add on the patchlevel (for Tcl) */ sprintf(&egg_version[strlen(egg_version)]," %u",egg_numver); #ifdef NO_IRC strcat(egg_version," NO_IRC"); #endif strcat(egg_version,egg_xtra); context; #ifdef STOP_UAC { int nvpair[2]; nvpair[0]=SSIN_UACPROC; nvpair[1]=UAC_NOPRINT; setsysinfo(SSI_NVPAIRS, (char *) nvpair, 1, NULL, 0); } #endif if (argc>1) for (i=1; itm_min; srandom((unsigned short int)time(NULL)); init_mem(); init_misc(); init_bots(); init_net(); init_blowfish(); init_tcl(); chanprog(); context; cache_miss=0; cache_hit=0; #ifdef NO_IRC strcat(ver," [limbo]"); #endif getmyhostname(bothost); sprintf(botuserhost,"%s@%s",botuser,bothost); /* wishful thinking */ get_first_server(); sprintf(pid_file,"pid.%s",origbotname); /* check for pre-existing eggdrop! */ f=fopen(pid_file,"r"); if (f!=NULL) { fgets(s,10,f); xx=atoi(s); kill(xx,SIGCHLD); /* meaningless kill to determine if pid is used */ if (errno!=ESRCH) { printf("I detect %s already running from this directory.\n",origbotname); printf("If this is incorrect, erase the '%s' file.\n\n",pid_file); exit(1); } } i=count_users(userlist); use_stderr=0; #ifndef NO_IRC j=0; chan=chanset; while (chan!=NULL) { j++; chan=chan->next; } #endif strcpy(s,ctime(&now)); s[strlen(s)-1]=0; strcpy(&s[11],&s[20]); putlog(LOG_ALL,"*",""); putlog(LOG_ALL,"*","--- Loading %s (%s)",ver,s); #ifdef NO_IRC putlog(LOG_ALL,"*","=== %s: in limbo, %d users.",botname,i); use_stderr=1; printf("%s: in limbo, %d users.\n",botname,i); #else putlog(LOG_ALL,"*","=== %s: %d channel%s, %d users.",botname,j,j==1?"":"s", i); use_stderr=1; printf("%s: %d channel%s, %d users.\n",botname,j,j==1?"":"s",i); #endif /* move into background? */ if (backgrd) { xx=fork(); if (xx==-1) fatal("CANNOT FORK PROCESS.",0); if (xx!=0) { FILE *fp; printf("Launched into the background (pid: %d)\n\n",xx); /* write pid to file */ unlink(pid_file); fp=fopen(pid_file,"w"); if (fp!=NULL) { fprintf(fp,"%u\n",xx); fclose(fp); } else printf("* Warning! Could not write %s file!\n",pid_file); #if HAVE_SETPGID setpgid(xx,xx); #endif exit(0); } } use_stderr=0; /* stop writing to stderr now */ if (backgrd) { /* ok, try to disassociate from controlling terminal */ /* (finger cross) */ #if HAVE_SETPGID setpgid(0,0); #endif /* close out stdin/out/err */ freopen("/dev/null","r",stdin); freopen("/dev/null","w",stdout); freopen("/dev/null","w",stderr); /* tcl wants those file handles kept open */ /* close(0); close(1); close(2); */ } /* terminal emulating dcc chat */ if ((!backgrd) && (term_z)) { int n=dcc_total; dcc[n].addr=getmyip(); dcc[n].port=0; dcc[n].sock=STDOUT; dcc[n].type=DCC_CHAT; set_chat(n); dcc[n].u.chat->away=NULL; dcc[n].u.chat->status=STAT_MASTER; dcc[n].u.chat->timer=time(NULL); dcc[n].u.chat->msgs_per_sec=0; dcc[n].u.chat->con_flags=conmask; dcc[n].u.chat->channel=0; strcpy(dcc[n].nick,"HQ"); strcpy(dcc[n].host,"local console"); setsock(STDOUT,0); /* entry in net table */ dprintf(n,"\n### ENTERING DCC CHAT SIMULATION ###\n\n"); dcc_total++; dcc_chatter(n); } debug0("main: initialization done, server connect"); #ifndef NO_IRC /* now connect to a server: */ newserver[0]=0; newserverport=0; connect_server(); #else /* initialize irc things so they won't bother us */ serv=(-1); dccdir[0]=0; strcpy(botname,origbotname); #endif then=time(NULL); online_since=time(NULL); autolink_cycle(NULL); /* hurry and connect to tandem bots */ debug0("main: entering loop"); while(1) { context; now=time(NULL); if (now!=then) { /* once a second */ static int cnt=0; context; timecnt++; /* time to dequeue a msg? */ if (timecnt==msgrate) { deq_msg(); timecnt=0; } check_utimers(); /* secondly timers */ cnt++; if (cnt>=10) { /* every 10 seconds */ cnt=0; if ((con_chan) && (!backgrd)) { tprintf(STDOUT,"\033[2J\033[1;1H"); tell_verbose_status(DP_STDOUT,0); tell_mem_status_dcc(DP_STDOUT); } } if ((!online) && (now-online_since >= 60)) online=1; } nowtm=localtime(&now); then=now; if ((online) && (nowtm->tm_min != lastmin)) { /* once a minute */ context; lastmin=(lastmin+1)%60; #ifndef NO_IRC check_lonely_channels(); if (keepnick) { if (strcmp(botname,origbotname)!=0) { /* if i'm not using my intended nickname, try to recapture it */ strcpy(newbotname,botname); strcpy(botname,origbotname); tprintf(serv,"NICK %s\n",botname); waiting_for_awake=0; } else if (newbotname[0]) { newbotname[0]=0; waiting_for_awake=0; putlog(LOG_MISC,"*","Regained nickname '%s'. (?)",botname); } } check_idle_kick(); /* join any channels that aren't active or pending */ chan=chanset; while (chan!=NULL) { if (!(chan->stat&(CHANACTIVE|CHANPEND))) mprintf(serv,"JOIN %s\n",chan->name); chan=chan->next; } check_expired_splits(); check_expired_ignores(); check_expired_bans(); check_for_split(); /* am *I* split? */ check_expired_chanbans(); #endif /* ifndef NO_IRC */ check_expired_dcc(); check_expired_tbufs(); autolink_cycle(NULL); /* attempt autolinks */ check_timers(); /* in case for some reason more than 1 min has passed: */ i=0; while (nowtm->tm_min!=lastmin) { /* timer drift, dammit */ check_timers(); i++; lastmin=(lastmin+1)%60; } if (i>1) putlog(LOG_MISC,"*","(!) timer drift -- spun %d minutes",i); } context; if (((int)(nowtm->tm_min/5)*5) == (nowtm->tm_min)) { /* 5 min */ if (!fivemin) { fivemin=1; #ifndef NO_IRC log_chans(); #ifdef CHECK_STONED if (waiting_for_awake) { /* uh oh! never got pong from last time, five minutes ago! */ /* server is probably stoned */ killsock(serv); serv=(-1); /* will reconnect about 50 lines down */ putlog(LOG_SERV,"*","Server got stoned; jumping..."); } else { /* check for server being stoned */ if ((serv>=0) && !trying_server) { tprintf(serv,"PING :%lu\n",(unsigned long)time(NULL)); waiting_for_awake=1; } } #endif /* CHECK_STONED */ #endif /* !NO_IRC */ check_botnet_pings(); } } else fivemin=0; context; miltime=(nowtm->tm_hour*100)+(nowtm->tm_min); if (miltime==0) { /* at midnight */ if (!midnite) { midnite=1; strcpy(s,ctime(&now)); s[strlen(s)-1]=0; strcpy(&s[11],&s[20]); putlog(LOG_MISC,"*","--- %s",s); } } else midnite=0; if (miltime==switch_logfiles_at) { if (!switched) { switched=1; putlog(LOG_MISC,"*","Backing up user file..."); strcpy(s,userfile); strcat(s,"~bak"); copyfile(userfile,s); expire_notes(); if (!keep_all_logs) { putlog(LOG_MISC,"*","Switching logfiles..."); for (i=0; itm_min==save_users_at) { /* hourly! */ if (!hourly) { hourly=1; write_userfile(); } } else hourly=0; if (nowtm->tm_min==notify_users_at) { /* hourli! */ context; if (!hourli) { hourli=1; #ifndef NO_IRC chan=chanset; while (chan!=NULL) { m=chan->channel.member; while (m->nick[0]) { sprintf(s1,"%s!%s",m->nick,m->userhost); get_handle_by_host(hand,s1); k=num_notes(hand); for (l=0; lnick,k,k==1?"":"s",botname); hprintf(serv,"NOTICE %s :For a list, /MSG %s NOTES [pass] INDEX\n",m->nick,botname); } m=m->next; } chan=chan->next; } #endif for (l=0; l0 && dcc[l].type==DCC_CHAT) { dprintf(l,"### You have %d note%s waiting.\n",k,k==1?"":"s"); dprintf(l,"### Use '.notes read' to read them.\n"); } } } } else hourli=0; #ifdef MODERN_TCL /* now Tcl has an event loop too, whee */ /* Tcl_DoOneEvent(TCL_DONT_WAIT); -breaks a lot */ #endif context; #ifndef NO_IRC if (serv<0) { clear_channels(); connect_server(); } context; #endif /* clean up sockets that were just left for dead */ for (i=0; i=0) { /* non-error */ context; #ifdef NO_IRC dcc_activity(xx,buf,i); #else if (xx!=serv) dcc_activity(xx,buf,i); else { /* SERVER ACTIVITY */ context; if (trying_server) { putlog(LOG_SERV,"*","Connected to %s",botserver); server_online=time(NULL); trying_server=0L; waiting_for_awake=0; } strcpy(s,buf); parsemsg(buf,from,code,msg); fixfrom(from); #ifdef USE_CONSOLE_R if ((strcmp(code,"PRIVMSG")==0) || (strcmp(code,"NOTICE")==0)) { if (!match_ignore(from)) { putlog(LOG_RAW,"*","[@] %s",s); check_tcl_raw(s); } } else { putlog(LOG_RAW,"*","[@] %s",s); check_tcl_raw(s); } #endif if (strcmp(code,"PRIVMSG")==0) gotmsg(from,msg,match_ignore(from)); else if (strcmp(code,"NOTICE")==0) gotnotice(from,msg,match_ignore(from)); else if (strcmp(code,"MODE")==0) gotmode(from,msg); else if (strcmp(code,"JOIN")==0) gotjoin(from,msg); else if (strcmp(code,"PART")==0) gotpart(from,msg); else if (strcmp(code,"ERROR")==0) goterror(from,msg); else if (strcmp(code,"PONG")==0) gotpong(from,msg); else if (strcmp(code,"WALLOPS")==0) gotwall(from,msg); else if (strcmp(code,"001")==0) got001(from,msg); else if (strcmp(code,"251")==0) got251(from,msg); else if (strcmp(code,"315")==0) got315(from,msg); else if (strcmp(code,"324")==0) got324(from,msg); else if (strcmp(code,"331")==0) got331(from,msg); else if (strcmp(code,"332")==0) got332(from,msg); else if (strcmp(code,"352")==0) got352(from,msg); else if (strcmp(code,"367")==0) got367(from,msg); else if (strcmp(code,"368")==0) got368(from,msg); else if (strcmp(code,"405")==0) got405(from,msg); else if (strcmp(code,"432")==0) got432(from,msg); else if (strcmp(code,"433")==0) got433(from,msg); else if (strcmp(code,"437")==0) got437(from,msg); else if (strcmp(code,"471")==0) got471(from,msg); else if (strcmp(code,"473")==0) got473(from,msg); else if (strcmp(code,"474")==0) got474(from,msg); else if (strcmp(code,"475")==0) got475(from,msg); else if (strcmp(code,"QUIT")==0) gotquit(from,msg); else if (strcmp(code,"NICK")==0) { detect_flood(from,NULL,FLOOD_NICK,0); gotnick(from,msg); } else if (strcmp(code,"KICK")==0) gotkick(from,msg); else if (strcmp(code,"INVITE")==0) gotinvite(from,msg); else if ((strcmp(code,"375")==0) || (strcmp(code,"376")==0) || (strcmp(code,"372")==0)) ; /* ignore motd */ else if (strcmp(code,"TOPIC")==0) gottopic(from,msg); else if (strcmp(code,"PING")==0) { fixcolon(msg); tprintf(serv,"PONG :%s\n",msg); } } /* in periods of high traffic (sockgets always returns info), only */ /* flush the stacked modes every 5th time -- in calmer times (when */ /* sockgets sometimes spends a whole second with no input) dump out */ /* any pending modes */ if (modecnt==4) flush_modes(); /* dump all mode changes */ modecnt=(modecnt+1)%5; #endif /* NO_IRC */ } else if (xx==-1) { /* EOF from someone */ context; #ifdef NO_IRC if ((i!=STDOUT) || backgrd) eof_dcc(i); else fatal("EOF ON TERMINAL",1); #else if (i==serv) { /* we lost this server, dammit */ putlog(LOG_SERV,"*","Disconnected from %s",botserver); clear_channels(); /* we're not on any channels any more */ killsock(serv); connect_server(); } else if ((i!=STDOUT) || backgrd) eof_dcc(i); else fatal("END OF FILE ON TERMINAL",1); #endif } else if ((xx==-2) && (errno!=EINTR)) { /* select() error */ context; putlog(LOG_MISC,"*","* Socket error #%d; recovering.",errno); #ifndef NO_IRC if (fcntl(serv,F_GETFD,0)==(-1)) { putlog(LOG_MISC,"*","Server socket expired -- pfft"); killsock(serv); serv=(-1); } #endif for (i=0; i