/* I/O part of eggdrop: main server communication, startup, etc */ #include #include #include #include #include #include #include #include #include "eggdrop.h" #define msgrate 2 char version[81]="Eggdrop v0.9g (c)1994 Robey Pointer"; char ver[81]="eggdrop v0.9g"; #ifdef BSD char os[]="BSD"; #endif #ifdef LINUX char os[]="Linux"; /* also irix, but irix is just a clone... */ #endif #ifdef BSD386 char os[]="BSD/386"; #endif #ifdef HPUX char os[]="HP-UX"; #endif #ifdef AIX char os[]="AIX"; #endif /* socket that the server is on */ int serv=-1; /* run in the background? */ int backgrd=1; /* successful connect yet? */ int online=0; /* hide parameters in ps list? */ int hide_param=0; /* foreground: constantly display channel stats? */ int con_chan=0; /* foreground: use the terminal as a party line? */ int term_z=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=6667; /* name of the config file */ char configfile[121]="egg.config"; /* temporary thing for nick changes */ char newbotname[10]; /* nickname of the helpbot */ char helpbot[21]; /* socket of the helpbot, if used: */ int helpsock=-1; /* current position in server list: */ int curserv=0; /* current position in server list of helpbot: */ int helpserv=0; /* current helpbot server: */ char helpserver[121]; /* current helpbot server port: */ int helpserverport=6667; /* directory of help files (if used) */ char helpdir[121]; /* store startup parameters */ char **argv1; /* # of msgs per min that's a flood */ int flood_thr=5; /* # of publics per min that's a flood */ int flood_pub_thr=10; /* # of joins per min that's a flood */ int flood_join_thr=5; /* 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; /* show channel update line every 5 mins */ int chan_updates=1; /* 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]; /* helpbot has returned a ping recently */ int gothelpping=1; /* using bot in make-userfile mode? (first user to 'hello' becomes master) */ int make_userfile=0; extern char curchan[]; extern char botchan[]; extern char botname[]; extern char origbotname[]; extern int dcc_total; extern struct dcc_t dcc[]; extern int use_console; extern char dccdir[]; extern char dccin[]; extern int telnet_port; extern char admin[]; extern char gainops[]; extern char notefile[]; extern char newserver[]; extern int newserverport; extern int lastsock; extern int periodic_save; extern int enforce_bans; extern int chankey[]; extern char *logfile[]; extern int conmask; extern int perm_bans; int use_stderr=1; int dead_child=0; void fatal(s,recoverable) char *s; int recoverable; { int i; log(LOG_MISC,"* %s",s); if (use_stderr) tprintf(STDERR,"* %s\n",s); if (serv!=-1) { close(serv); shutdown(serv,2); } if (helpsock!=-1) { close(helpsock); shutdown(serv,2); } 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); } /* 433 : nickname in use */ /* change nicks till we're acceptable or we give up */ void got433(from,msg) char *from,*msg; { char c,*oknicks="^-_\\[]`",*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; } 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==NULL) fatal("COULDN'T FIND A SUITABLE NICKNAME.",1); botname[strlen(botname)-1]=*p; } log(LOG_MISC,"NICK IN USE: Trying '%s'",botname); } else { if (strlen(botname)==9) botname[strlen(botname)-1]='0'; else { botname[strlen(botname)+1]=0; botname[strlen(botname)]='0'; } log(LOG_MISC,"NICK IN USE: Trying '%s'",botname); } tprintf(serv,"NICK %s\n",botname); } 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,which,tochan) char *from; int which,tochan; { char *p; time_t t; char h[121]; int thr=flood_thr; char me[121]; context; strcpy(me,botuserhost); splitc(NULL,botuserhost,'@'); /* determine how many are necessary to make a flood */ if (((which==_PRIVMSG) || (which==_NOTICE)) && (tochan)) thr=flood_pub_thr; if (which==_JOIN) thr=flood_join_thr; p=strchr(from,'!'); if (p!=NULL) { p++; p=strchr(p,'@'); } if (p!=NULL) { p++; t=time(NULL); if (strcasecmp(p,me)==0) return 0; /* don't flood check self */ if (strcasecmp(lastmsghost[which],p)!=0) { /* new */ strcpy(lastmsghost[which],p); lastmsgtime[which]=t; lastmsgs[which]=0; } else { /* old */ if (lastmsgtime[which] < t-60) { lastmsgtime[which]=t; lastmsgs[which]=0; } else { lastmsgs[which]++; if (lastmsgs[which]>=thr) { /* FLOOD */ if (((which==_PRIVMSG) || (which==_NOTICE)) && (!tochan)) { /* private msg */ sprintf(h,"*!*@%s",p); lastmsgs[which]=0; lastmsgtime[which]=0; lastmsghost[which][0]=0; log(LOG_MISC,"Flood from @%s! Placing on ignore!",p); prog_ignore(h,time(NULL)); return 1; } else if (which==_CTCP) { /* ctcp flood! */ sprintf(h,"*!*@%s",p); lastmsgs[which]=0; lastmsgtime[which]=0; lastmsghost[which][0]=0; log(LOG_MISC, "CTCP flood from @%s! Placing on ignore and kicking!",p); prog_ignore(h,time(NULL)); p=strchr(from,'!'); if (p!=NULL) { *p=0; tprintf(serv,"KICK %s %s :CTCP flood\n",curchan,from); *p='!'; } return 1; } else if (which==_JOIN) { /* join flood */ char sx[21]; sprintf(h,"*!*@%s",p); strcpy(sx,"join flood"); if (!enforce_bans) kick_match_since(h,lastmsgtime[which]); lastmsgs[which]=0; lastmsgtime[which]=0; lastmsghost[which][0]=0; log(LOG_MISC|LOG_CHAN,"JOIN flood from @%s! Banning...",p); add_mode('+','b',h); prog_ban(h,time(NULL),sx); return 1; } else { /* flooding chan! either by public, notice, or nick */ lastmsgs[which]=0; lastmsgtime[which]=0; lastmsghost[which][0]=0; p=strchr(from,'!'); if (p!=NULL) { log(LOG_CHAN,"Channel flood from %s -- kicking",from); *p=0; tprintf(serv,"KICK %s %s :flood\n",curchan,from); *p='!'; return 1; } } } } } } return 0; } void got_bus() { log(LOG_MISC,"* Last context: %s/%d",cx_file,cx_line); fatal("BUS ERROR -- CRASHING!",1); } void got_segv() { log(LOG_MISC,"* Last context: %s/%d",cx_file,cx_line); fatal("SEGMENT VIOLATION -- CRASHING!",1); } void got_term() { log(LOG_MISC,"RECEIVED TERMINATE SIGNAL (IGNORING)"); if (periodic_save) write_userfile(); return; } void got_quit() { log(LOG_MISC,"RECEIVED QUIT SIGNAL (IGNORING)"); return; } void got_hup() { log(LOG_MISC,"RECEIVED HANGUP SIGNAL (IGNORING)"); if (periodic_save) write_userfile(); return; } void got_pipe() { context; } /* int i,ok=0; static time_t last=0L; static int recur=0,cnt=0; if (recur) fatal("* Recursive pipe fault!",0); recur=1; if (time(NULL)==last) { cnt++; if (cnt>5) fatal("* >5 pipe faults per second!",0); } else { last=time(NULL); cnt=0; } log(LOG_MISC,"* Pipe fault; recovering."); log(LOG_MISC,"Last context: %s/%d",cx_file,cx_line); if (fcntl(serv,F_GETFD,0)==-1) { log(LOG_MISC,"Server socket expired -- pfft"); close(serv); shutdown(serv,2); serv=(-1); } if (helpbot[0]) if (fcntl(helpsock,F_GETFD,0)==-1) { log(LOG_MISC,"Helpbot server expired -- pfft"); close(helpsock); shutdown(helpsock,2); helpsock=(-1); } for (i=0; i=0) && (dcc[i].type!=DCC_LOST)) if (fcntl(dcc[i].sock,F_GETFD,0)==-1) { log(LOG_MISC,"DCC socket %d expired -- pfft",dcc[i].sock); close(dcc[i].sock); shutdown(dcc[i].sock,2); dcc[i].sock=dcc[i].type; dcc[i].type=DCC_LOST; } recur=0; } */ void got_tterr() { log(LOG_MISC,"ATTEMPTED READ/WRITE TTY CONSOLE FROM BACKGROUND"); log(LOG_MISC,"Notify Robey of this error! It's a major goof!"); return; } void got_child() { context; dead_child++; } void got_usr1() { int x,i; log(LOG_MISC,"* USER1 SIGNAL: Debugging sockets"); x=creat("DEBUG",0644); if (x<0) { log(LOG_MISC,"* Failed to write DEBUG"); return; } tell_dcc(x); tprintf(x,"\n"); tprintf(x,"SERVER %s:%d, SOCK %d\n",botserver,botserverport,serv); if (helpbot[0]) tprintf(x,"HELPSERVER %s:%d, SOCK %d\n",helpserver,helpserverport, helpsock); if (fcntl(serv,F_GETFD,0)<0) { log(LOG_MISC,"* Server socket expired -- pfft"); close(serv); shutdown(serv,2); serv=(-1); } if (helpbot[0]) if (fcntl(helpsock,F_GETFD,0)<0) { log(LOG_MISC,"* Helpbot socket expired -- pfft"); close(helpsock); shutdown(helpsock,2); helpsock=(-1); } for (i=0; i %s:%d",helpserver,helpserverport); } if (helpbot[0]) { tprintf(helpsock,"USER %s 1 1 :%s\n",botuser,botrealname); tprintf(helpsock,"NICK %s\n",helpbot); tprintf(helpsock,"MODE %s +i\n",helpbot); } } main(argc,argv) int argc; char **argv; { int xx,i,j; char buf[512]; time_t now,then; struct tm *nowtm; char s[512],s1[141],from[121],code[41],msg[512],oldserv[121],oldserv1[121]; int timecnt=0,midnite=0,fivemin=0,hourly=0; int sockl[50]; FILE *f; struct sigaction sv; context; argv1=argv; /* in case ya wanna try rebooting */ if (argc>1) for (i=1; i0) { int n=dcc_total; dcc[n].addr=getmyip(); dcc[n].port=telnet_port; dcc[n].sock=i; dcc[n].type=DCC_TELNET; dcc[n].u.other=NULL; strcpy(dcc[n].nick,"TELNET"); getmyhostname(dcc[n].host); dcc_total++; } else telnet_port++; } if (i>0) log(LOG_MISC,"Listening at telnet port %d.",telnet_port); else { log(LOG_MISC,"Failed to find an open telnet port near %d.",telnet_port); telnet_port=0; } } /* 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; strcpy(dcc[n].nick,"HQ"); strcpy(dcc[n].host,"local console"); dcc_total++; tprintf(STDOUT,"\n### ENTERING DCC CHAT SIMULATION ###\n\n"); dcc_chatter(STDOUT); } /* now connect to a server: */ newserver[0]=0; newserverport=0; connect_server(); /* connect helpbot if applicable */ if (helpbot[0]) connect_helpbot_server(); if (helpbot[0]) log(LOG_MISC,"Helpbot '%s' online to %s:%d",helpbot,helpserver, helpserverport); then=time(NULL); online_since=time(NULL); auto_link_tandem(NULL); /* hurry and connect to tandem bots */ 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(); deq_helpmsg(); timecnt=0; } cnt++; if (cnt>=10) { /* every 10 seconds */ cnt=0; if ((con_chan) && (!backgrd)) { tprintf(STDOUT,"%c[2J%c[1;1H",27,27); tell_verbose_status(STDOUT,0); tell_mem_status_dcc(STDOUT); tprintf(STDOUT,"\n"); tell_verbose_chan_info(STDOUT); } #ifdef HUNT_ZOMBIES /* check for pending dead children */ if (!dead_child) { int pid; WAIT_T stat; context; pid=waitpid(0,&stat,WNOHANG); if (pid>0) got_dead_child2(pid,stat); } context; #endif } if (now-online_since == 60) { /* one minute after loading */ online=1; /* considered to be successfully online by now */ } else if (((int)(((float)(now-online_since))/60.0)*60)==(now-online_since)) { /* once a minute */ context; 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); } else if (newbotname[0]) { newbotname[0]=0; log(LOG_CHAN|LOG_MISC,"Regained nickname '%s'.",botname); } check_lonely_channel(); if (strcasecmp(botchan,curchan)!=0) { /* not on our channel... so re-join */ if (curchan[0]) tprintf(serv,"PART %s\n",curchan); tprintf(serv,"JOIN %s %s\n",botchan,chankey); } if (!gothelpping) { /* lost track of help bot : make it change servers */ tprintf(helpsock,"QUIT :zarrf!\n"); close(helpsock); helpsock=(-1); gothelpping=1; log(LOG_MISC,"Helpbot AWOL; moving it"); } else if (helpbot[0]) { /* ping it again */ tprintf(serv,"PRIVMSG %s :\001PING 69\001\n",helpbot); gothelpping=0; } check_expired_splits(); check_expired_dcc(); check_expired_ignores(); check_expired_bans(); check_expired_tbufs(); check_for_split(); /* are WE split? */ check_for_loops(); /* in the tandem chain */ auto_link_tandem(NULL); /* attempt autolinks */ check_expired_forks(); if (!perm_bans) check_expired_chanbans(); } } nowtm=localtime(&now); then=now; if (((int)(nowtm->tm_min/5)*5) == (nowtm->tm_min)) { /* 5 min */ if (!fivemin) { fivemin=1; if (chan_updates) log_chan(); /* send out ctcp awake to itself so server doesn't think it's idle */ mprintf(serv,"PRIVMSG %s :\001AWAKE\001\n",botname); } } else fivemin=0; if ((nowtm->tm_hour==0) && (nowtm->tm_min==0)) { /* at midnight */ if (!midnite) { midnite=1; log(LOG_MISC,"Switching logfiles..."); for (i=0; itm_min==0) { /* hourly! */ if (!hourly) { hourly=1; if (periodic_save) write_userfile(); } } else hourly=0; context; if (serv==-1) { clear_channel(); curchan[0]=0; connect_server(); } if ((helpbot[0]) && (helpsock==-1)) connect_helpbot_server(); while (dead_child) { got_dead_child(); dead_child--; } /* check for server or dcc activity */ j=0; context; for (i=0; i0) { /* non-error */ context; if (xx==helpsock) helpbot_activity(buf); else if (xx!=serv) dcc_activity(xx,buf,i); else { /* SERVER ACTIVITY */ context; strcpy(s,buf); parsemsg(buf,from,code,msg); fixfrom(from); if (strcmp(code,"PRIVMSG")==0) { if (!match_ignore(from)) gotmsg(from,msg); } else if (strcmp(code,"NOTICE")==0) { if (!match_ignore(from)) gotnotice(from,msg); } else if (strcmp(code,"JOIN")==0) gotjoin(from,msg); else if (strcmp(code,"PART")==0) gotpart(from,msg); else if (strcmp(code,"251")==0) got251(from,msg); else if (strcmp(code,"324")==0) got324(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,"433")==0) got433(from,msg); else if (strcmp(code,"473")==0) got473(from,msg); else if (strcmp(code,"474")==0) got474(from,msg); else if (strcmp(code,"QUIT")==0) gotquit(from,msg); else if (strcmp(code,"MODE")==0) gotmode(from,msg); else if (strcmp(code,"NICK")==0) { gotnick(from,msg); detect_flood(from,_NICK,1); } 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,"PING")==0) { fixcolon(msg); tprintf(serv,"PONG :%s\n",msg); } } flush_mode(); /* dump all mode changes */ } else if (xx==-1) { /* EOF from someone */ context; if (i==serv) { /* we lost this server, dammit */ log(LOG_MISC|LOG_CHAN,"Disconnected from %s",botserver); clear_channel(); curchan[0]=0; /* we're not on a channel any more */ close(serv); shutdown(serv,2); connect_server(); } else if (i==helpsock) { /* helpbot vanished */ close(helpsock); shutdown(helpsock,2); connect_helpbot_server(); } else if (i!=STDOUT) eof_dcc(i); else fatal("END OF FILE ON TERMINAL",1); } else if ((xx==-2) && (errno!=EINTR)) { /* select() error */ context; log(LOG_MISC,"* Socket error #%d; recovering.",errno); if (fcntl(serv,F_GETFD,0)==-1) { log(LOG_MISC,"Server socket expired -- pfft"); close(serv); shutdown(serv,2); serv=(-1); } if (helpbot[0]) if (fcntl(helpsock,F_GETFD,0)==-1) { log(LOG_MISC,"Helpbot server socket expired -- pfft"); close(helpsock); shutdown(helpsock,2); helpsock=(-1); } for (i=0; i