/* chan.c -- handles: almost everything to do with channel manipulation telling channel status 'who' response user kickban, op, deop idle kicking dprintf'ized, 27oct95 */ #define _CHAN #if HAVE_CONFIG_H #include #endif #include #include #include #include #include "eggdrop.h" #if HAVE_GETRUSAGE #include # if HAVE_SYS_RUSAGE_H # include # endif #endif #include "users.h" #include "chan.h" #include "proto.h" extern char botuserhost[]; extern char botuser[]; extern char version[]; extern int serv; extern int memused; extern char botchan[]; extern char botserver[]; extern time_t online_since; extern int backgrd; extern int con_chan; extern int term_z; extern int botserverport; extern char helpbot[]; extern char helpserver[]; extern int helpserverport; extern int learn_users; extern int helpsock; extern int mtot,htot; extern int dcc_total; extern struct dcc_t dcc[]; extern int use_info; extern int share_users; extern int passive; extern char cx_file[]; extern int cx_line; extern char ver[]; extern int ban_time; extern struct userrec *userlist; extern int cache_hit, cache_miss; extern int default_flags; extern int clearbans; /* bot's nickname */ char botname[10]; /* bot's intended nickname (might have had to switch if nick was in use) */ char origbotname[10]; /* +o to oplisted people when they join? */ int op_on_join=1; /* be vengeful when people are bad? */ int revenge=1; /* if # servers on our side of a split falls below this, jump servers */ /* 0 = don't use this */ int min_servs=0; /* show joins/parts/quits in log? */ int log_joins=1; /* admin info */ char admin[121]; #ifdef TCL /* script to run to get ops */ char need_op[121]; /* script to run to get invite */ char need_invite[121]; #else /* something we can dump to server to get ops? */ char gainops[121]; #endif /* desired channel modes (future use) */ int desired_mode=0; /* use greetings in userfile? */ int greet=1; /* enforce bans by kicking banned people, etc? */ int enforce_bans=1; /* strict hostname matching (don't strip ~) */ int strict_host=0; /* expected channel key */ char chankey[121]=""; /* if 0, all bans are temporary */ int perm_bans=1; /* bitch mode: ONLY +o get ops */ int bitch=0; /* idle kick after how many minutes? */ int idle_kick=0; struct chan_t channel; char curchan[81]=""; char botchan[81]=""; int members=0; int me_op(); int any_ops(); /* set the key */ void set_key(k) char *k; { context; nfree(channel.key); if (k==NULL) { channel.key=(char *)nmalloc(1); channel.key[0]=0; return; } channel.key=(char *)nmalloc(strlen(k)+1); strcpy(channel.key,k); } /* memory expected to be used by this module */ int expmem_chan() { int tot; banlist *b; tot=strlen(channel.key)+1+(sizeof(struct memstruct)*(members+1)); b=channel.ban; while (b!=NULL) { tot+=strlen(b->ban)+1; if (b->ban[0]) tot+=strlen(b->who)+1; tot+=sizeof(struct banstruct); b=b->next; } return tot; } /* is channel +s or +p? */ int channel_hidden() { return (channel.mode&(CHANPRIV|CHANSEC)); } /* dump status info out to dcc */ void tell_verbose_status(idx,showchan) int idx,showchan; { char s[81],s1[121],s2[81]; int i; time_t now,hr,min; #if HAVE_GETRUSAGE struct rusage ru; #endif context; s[0]='+'; i=1; if (channel.mode&CHANINV) s[i++]='i'; if (channel.mode&CHANPRIV) s[i++]='p'; if (channel.mode&CHANSEC) s[i++]='s'; if (channel.mode&CHANMODER) s[i++]='m'; if (channel.mode&CHANTOPIC) s[i++]='t'; if (channel.mode&CHANNOMSG) s[i++]='n'; if (channel.mode&CHANANON) s[i++]='a'; if (channel.key[0]) s[i++]='k'; if (channel.maxmembers>-1) s[i++]='l'; s[i]=0; if (channel.key[0]) sprintf(&s[strlen(s)]," %s",channel.key); if (channel.maxmembers>-1) sprintf(&s[strlen(s)]," %d",channel.maxmembers); #ifdef TCL dprintf(idx,"I am %s, running %s\n",botname,ver); #else dprintf(idx,"I am %s, running %s (non-Tcl)\n",botname,ver); #endif if (admin[0]) dprintf(idx,"Admin: %s\n",admin); if (curchan[0]) { if (channel.mode&CHANPEND) sprintf(s1,"Processing channel %s",curchan); else sprintf(s1,"Channel %s",curchan); } else sprintf(s1,"Desiring channel %s",botchan); if (!me_op()) { dprintf(idx,"-> I'm not a chanop!\n"); if (any_ops()) { #ifdef TCL if (need_op[0]) do_tcl("need-op",need_op); #else if (gainops[0]) tprintf(serv,"%s\n",gainops); #endif } } if (showchan) { int nn; i=count_users(userlist); dprintf(idx,"%s, %d member%s, %d user%s, mode %s (mem: %uk)\n",s1,members, members==1?"":"s",i,i==1?"":"s",s,(int)(memused/1024)); for (i=0; istatus&STAT_GETTING) { nn=count_users(dcc[i].u.tand->user); dprintf(idx,"Downloading userlist from %s (got %d so far)\n", dcc[i].nick,nn); } if (dcc[i].u.tand->status&STAT_SENDING) { nn=count_users(dcc[i].u.tand->user); dprintf(idx,"Sending userlist to %s (%d to go)\n",dcc[i].nick,nn); } } } dprintf(idx,"Server %s:%d\n",botserver,botserverport); if (helpbot[0]) { dprintf(idx,"Helpbot '%s' on %s:%d\n",helpbot,helpserver,helpserverport); if (htot) dprintf(idx,"Helpbot's queue is %d%% full.\n", (int)((float)(htot*100.0)/(float)MAXQMSG)); } if (mtot) dprintf(idx,"Server queue is %d%% full.\n", (int)((float)(mtot*100.0)/(float)MAXQMSG)); now=time(NULL); now-=online_since; s[0]=0; if (now>86400) { /* days */ sprintf(s,"%d day",(int)(now/86400)); if ((int)(now/86400) >= 2) strcat(s,"s"); strcat(s,", "); now-=(((int)(now/86400))*86400); } hr=(time_t)((int)now/3600); now-=(hr*3600); min=(time_t)((int)now/60); sprintf(&s[strlen(s)],"%02d:%02d",(int)hr,(int)min); s1[0]=0; if (backgrd) strcpy(s1,"background"); else { if (term_z) strcpy(s1,"terminal mode"); else if (con_chan) strcpy(s1,"status mode"); else strcpy(s1,"logdump mode"); } #if HAVE_GETRUSAGE getrusage(RUSAGE_SELF,&ru); hr=(int)((ru.ru_utime.tv_sec+ru.ru_stime.tv_sec)/60); min=(int)((ru.ru_utime.tv_sec+ru.ru_stime.tv_sec)-(hr*60)); sprintf(s2,"CPU %02d:%02d",(int)hr,(int)min); /* actally min/sec */ #else sprintf(s2,"CPU ???"); #endif dprintf(idx,"Online for %s (%s) %s cache hit %4.1f%%\n",s,s1,s2, 100.0*((float)cache_hit)/((float)(cache_hit+cache_miss))); s1[0]=0; if (op_on_join) strcat(s1,"auto-op, "); if ((bitch) && (revenge)) strcat(s1,"vengeful bitch, "); else if (bitch) strcat(s1,"bitch, "); else if (revenge) strcat(s1,"revenge, "); if ((use_info) && (greet)) strcat(s1,"greet, "); if (learn_users) strcat(s1,"learn users, "); if (share_users) { if (passive) strcat(s1,"passive share, "); else strcat(s1,"aggressive share, "); } if (s1[0]) s1[strlen(s1)-2]=0; else strcpy(s1,"none"); dprintf(idx,"Mode(s): %s.\n",s1); } /* dump channel info out to dcc */ void tell_verbose_chan_info(idx) int idx; { char handle[20],s[81],s1[121]; int i,atr; memberlist *m; time_t now; context; s[0]='+'; i=1; now=time(NULL); if (channel.mode&CHANINV) s[i++]='i'; if (channel.mode&CHANPRIV) s[i++]='p'; if (channel.mode&CHANSEC) s[i++]='s'; if (channel.mode&CHANMODER) s[i++]='m'; if (channel.mode&CHANTOPIC) s[i++]='t'; if (channel.mode&CHANNOMSG) s[i++]='n'; if (channel.mode&CHANANON) s[i++]='a'; if (channel.key[0]) s[i++]='k'; if (channel.maxmembers>-1) s[i++]='l'; s[i]=0; if (channel.key[0]) sprintf(&s[strlen(s)]," %s",channel.key); if (channel.maxmembers>-1) sprintf(&s[strlen(s)]," %d",channel.maxmembers); if (curchan[0]) { if (channel.mode&CHANPEND) sprintf(s1,"Processing channel %s",curchan); else sprintf(s1,"Channel %s",curchan); } else sprintf(s1,"Desiring channel %s",botchan); i=count_users(userlist); dprintf(idx,"%s, %d member%s, %d user%s, mode %s: (mem: %uk)\n",s1,members, members==1?"":"s",i,i==1?"":"s",s,(int)(memused/1024)); m=channel.member; i=0; if (curchan[0]) { dprintf(idx,"(I = idle, M = master, O = op, D = deop)\n"); dprintf(idx," NICKNAME HANDLE JOINED STAT USER@HOST\n"); while (m->nick[0]) { if (m->joined > 0) { strcpy(s,ctime(&(m->joined))); if ((time(NULL)-(m->joined)) > 86400) { strcpy(s1,&s[4]); strcpy(s,&s[8]); strcpy(&s[2],s1); s[5]=0; } else { strcpy(s,&s[11]); s[5]=0; } } else strcpy(s,"dunno"); if (m->user==NULL) { sprintf(s1,"%s!%s",m->nick,m->userhost); get_handle_by_host(handle,s1); /* <- automagically updates m->user */ } if (m->user==NULL) atr=0; else { strcpy(handle,m->user->handle); atr=m->user->flags; } if (m->split > 0) dprintf(idx,"%2d. %c%-9s %-9s (%s) <- netsplit, %lus\n",++i,m->flags& CHANOP?'@':(m->flags&CHANVOICE?'+':' '),m->nick,handle,s, now-(m->split)); else if (strcmp(m->nick,botname)==0) dprintf(idx,"%2d. %c%-9s %-9s (%s) <- it's me!\n",++i,m->flags&CHANOP? '@':(m->flags&CHANVOICE?'+':' '),m->nick,handle,s); else dprintf(idx,"%2d. %c%-9s %-9s (%s) %c%c%c%c %s\n",++i,m->flags&CHANOP? '@':(m->flags&CHANVOICE?'+':' '),m->nick,handle,s, ((now-(m->last) > 900) && (strcmp(m->nick,botname)!=0))? 'I':' ',(atr&USER_MASTER)?'M':' ',(atr&USER_OP)?'O':' ', (atr&USER_DEOP)?'D':' ',m->userhost); if (m->flags&FAKEOP) dprintf(idx," (FAKE CHANOP GIVEN BY SERVER)\n"); if (m->flags&SENTOP) dprintf(idx," (pending +o -- I'm lagged)\n"); if (m->flags&SENTDEOP) dprintf(idx," (pending -o -- I'm lagged)\n"); m=m->next; } } dprintf(idx,"End of channel info.\n"); } int hand_on_chan(handle) char *handle; { char s[161],h[21]; memberlist *m=channel.member; while (m->nick[0]) { sprintf(s,"%s!%s",m->nick,m->userhost); get_handle_by_host(h,s); if (strcasecmp(h,handle)==0) return 1; m=m->next; } return 0; } void tell_chanbans(idx,k,match) int idx,k; char *match; { banlist *b=channel.ban; char s[121],s1[81],fill[121]; time_t now; int min,sec; context; now=time(NULL); if (curchan[0]) { while (b->ban[0]) { if (!equals_ban(b->ban)) { strcpy(s,b->who); splitnick(s1,s); if (s1[0]) sprintf(fill,"%s (%s!%s)",b->ban,s1,s); else if (strcasecmp(s,"existent")==0) sprintf(fill,"%s (%s)",b->ban,s); else sprintf(fill,"%s (server %s)",b->ban,s); if (b->timer != 0) { min=(now - b->timer)/60; sec=(now - b->timer)-(min*60); sprintf(s," (for %02d:%02d)",min,sec); strcat(fill,s); } if ((!match[0]) || (wild_match(match,b->ban))) dprintf(idx,"* [%2d] %s\n",k,fill); k++; } b=b->next; } } if (k==1) dprintf(idx,"(There are no bans, permanent or otherwise.)\n"); } int kill_chanban(idx,k,which) int idx,k,which; { banlist *b=channel.ban; context; if (k==0) k=1; while (b->ban[0]) { if (!equals_ban(b->ban)) { if (k==which) { add_mode('-','b',b->ban); dprintf(idx,"Removed ban on '%s'.\n",b->ban); } k++; } b=b->next; } if (k<=which) { dprintf(idx,"No such ban.\n"); return 0; } else return 1; } int kill_chanban_name(idx,which) int idx; char *which; { banlist *b=channel.ban; context; while (b->ban[0]) { if (strcasecmp(b->ban,which)==0) { add_mode('-','b',b->ban); dprintf(idx,"Removed ban on '%s'.\n",b->ban); return 1; } b=b->next; } dprintf(idx,"No such ban.\n"); return 0; } /* log the channel members */ void log_chan() { banlist *b; memberlist *m; char s1[121]; int chops=0,masters=0,ops=0,deops=0,bans=0,atr; context; if ((curchan[0]) && !(channel.mode&CHANPEND)) { m=channel.member; while (m->nick[0]) { sprintf(s1,"%s!%s",m->nick,m->userhost); atr=get_attr_host(s1); if (m->flags&CHANOP) chops++; if (atr & USER_MASTER) masters++; if (atr & USER_OP) ops++; if (atr & USER_DEOP) deops++; m=m->next; } b=channel.ban; while (b->ban[0]) { bans++; b=b->next; } log(LOG_MISC,"%2d member%s, %2d chop%s, %2do %2dm %2dd, %2d ban%s", members,members==1?" ":"s",chops,chops==1?" ":"s",ops,masters,deops, bans,bans==1?" ":"s"); } } /* dump channel info out to nick */ void tell_chan_info(nick) char *nick; { char s[81],s1[121]; int i,chops=0,masters=0,ops=0,deops=0; memberlist *m; banlist *b; int atr; context; s[0]='+'; i=1; if (channel.mode&CHANINV) s[i++]='i'; if (channel.mode&CHANPRIV) s[i++]='p'; if (channel.mode&CHANSEC) s[i++]='s'; if (channel.mode&CHANMODER) s[i++]='m'; if (channel.mode&CHANTOPIC) s[i++]='t'; if (channel.mode&CHANNOMSG) s[i++]='n'; if (channel.mode&CHANANON) s[i++]='a'; if (channel.key[0]) s[i++]='k'; if (channel.maxmembers>-1) s[i++]='l'; s[i]=0; if (channel.key[0]) sprintf(&s[strlen(s)]," %s",channel.key); if (channel.maxmembers>-1) sprintf(&s[strlen(s)]," %d",channel.maxmembers); mprintf(serv,"NOTICE %s :I am %s, running %s.\n",nick,botname,version); if (curchan[0]) { if (channel.mode&CHANPEND) sprintf(s1,"Processing channel %s",curchan); else sprintf(s1,"Channel %s",curchan); } else sprintf(s1,"Desiring channel %s",botchan); mprintf(serv,"NOTICE %s :%s, %d members, mode %s (mem: %u)\n",nick,s1, members,s,memused); m=channel.member; i=0; while (m->nick[0]) { sprintf(s1,"%s!%s",m->nick,m->userhost); atr=get_attr_host(s1); if (m->flags&CHANOP) chops++; if (atr & USER_MASTER) masters++; if (atr & USER_OP) ops++; if (atr & USER_DEOP) deops++; m=m->next; } sprintf(s1,"NOTICE %s :%d chops, %d masterlist, %d oplist, %d deoplist,", nick,chops,masters,ops,deops); b=channel.ban; while (b->ban[0]) { i++; b=b->next; } mprintf(serv,"%s %d bans.\n",s1,i); } /* initialize out the channel record */ void init_channel() { context; channel.maxmembers=(-1); channel.mode=0; members=0; channel.key=(char *)nmalloc(1); channel.key[0]=0; channel.ban=(banlist *)nmalloc(sizeof(banlist)); channel.ban->ban=(char *)nmalloc(1); channel.ban->ban[0]=0; channel.ban->who=NULL; channel.ban->next=NULL; channel.member=(memberlist *)nmalloc(sizeof(memberlist)); channel.member->nick[0]=0; channel.member->next=NULL; } /* clear out channel data from memory */ void clear_channel() { memberlist *m,*m1; banlist *b,*b1; context; nfree(channel.key); m=channel.member; while (m!=NULL) { m1=m->next; nfree(m); m=m1; } b=channel.ban; while (b!=NULL) { b1=b->next; if (b->ban[0]) nfree(b->who); nfree(b->ban); nfree(b); b=b1; } init_channel(); } /* shortcut for get_user_by_host -- might have user record in channel */ struct userrec *check_chanlist(host) char *host; { char nick[31],uhost[121]; memberlist *m=channel.member; strcpy(uhost,host); splitnick(nick,uhost); while (m->nick[0]) { if ((strcasecmp(uhost,m->userhost)==0) && (strcasecmp(nick,m->nick)==0)) return m->user; m=m->next; } return NULL; } /* shortcut for get_user_by_handle -- might have user record in channel */ struct userrec *check_chanlist_hand(hand) char *hand; { memberlist *m=channel.member; while (m->nick[0]) { if (m->user != NULL) if (strcasecmp(m->user->handle,hand)==0) return m->user; m=m->next; } return NULL; } /* clear the user pointers in the chanlist */ /* (necessary when a hostmask is added/removed or a user is added) */ void clear_chanlist() { memberlist *m=channel.member; while (m->nick[0]) { m->user=NULL; m=m->next; } } /* if this user@host is in the channel, set it (it was null) */ void set_chanlist(host,rec) char *host; struct userrec *rec; { char nick[31],uhost[120]; memberlist *m=channel.member; strcpy(uhost,host); splitnick(nick,uhost); while (m->nick[0]) { if ((strcasecmp(uhost,m->userhost)==0) && (strcasecmp(nick,m->nick)==0)) m->user=rec; m=m->next; } } /* returns a pointer to a new channel member structure */ memberlist *newmember() { memberlist *x; x=channel.member; while (x->nick[0]) x=x->next; x->next=(memberlist *)nmalloc(sizeof(memberlist)); x->next->next=NULL; x->next->nick[0]=0; x->next->split=0L; x->next->last=0L; members++; return x; } /* adds a ban to the list */ void newban(s,who) char *s; char *who; { banlist *b; context; b=channel.ban; while ((b->ban[0]) && (strcasecmp(b->ban,s)!=0)) b=b->next; if (b->ban[0]) return; /* already existant ban */ b->next=(banlist *)nmalloc(sizeof(banlist)); b->next->next=NULL; b->next->ban=(char *)nmalloc(1); b->next->ban[0]=0; nfree(b->ban); b->ban=(char *)nmalloc(strlen(s)+1); strcpy(b->ban,s); b->who=(char *)nmalloc(strlen(who)+1); strcpy(b->who,who); b->timer=time(NULL); } /* removes a nick from the channel member list (returns 1 if successful) */ int killmember(nick) char *nick; { memberlist *x,*old; x=channel.member; old=NULL; while ((x->nick[0]) && (strcasecmp(x->nick,nick)!=0)) { old=x; x=x->next; } if ((x->nick[0]==0) && (!(channel.mode & CHANPEND))) { log(LOG_MISC,"(!) killmember(%s) -> nonexistent",nick); return 0; } if (old==NULL) channel.member=x->next; else old->next=x->next; nfree(x); members--; return 1; } /* removes a ban from the list */ int killban(s) char *s; { banlist *b,*old; context; b=channel.ban; old=NULL; while ((b->ban[0]) && (strcasecmp(b->ban,s)!=0)) { old=b; b=b->next; } if (b->ban[0]==0) return 0; if (old==NULL) channel.ban=b->next; else old->next=b->next; nfree(b->ban); nfree(b->who); nfree(b); return 1; } /* returns memberfields if the nick is in the member list */ memberlist *ismember(nick) char *nick; { memberlist *x; x=channel.member; while ((x->nick[0]) && (strcasecmp(x->nick,nick)!=0)) x=x->next; if (x->nick[0]==0) return NULL; return x; } /* boolean form for other modules to use */ int ischanmember(nick) char *nick; { return (ismember(nick) != NULL); } /* are there any ops on the channel? */ int any_ops() { memberlist *x=channel.member; while ((x->nick[0]) && (!(x->flags&CHANOP))) x=x->next; if (x->nick[0]==0) return 0; return 1; } /* returns true if this is one of the channel bans */ int isbanned(user) char *user; { banlist *b; context; b=channel.ban; while ((b->ban[0]) && (strcasecmp(b->ban,user)!=0)) b=b->next; if (b->ban[0]==0) return 0; return 1; } void getchanhost(nick,host) char *nick,*host; { memberlist *m; host[0]=0; m=ismember(nick); if (m==NULL) return; strcpy(host,m->userhost); } int is_split(nick) char *nick; { memberlist *m; m=ismember(nick); if (m==NULL) return 0; return (int)(m->split); } /* called only if perm-bans is off */ void check_expired_chanbans() { banlist *b; time_t now=time(NULL); if (!me_op()) return; /* can't do this if not op! */ context; b=channel.ban; while (b->ban[0]) { if (now - b->timer > 60*ban_time) { log(LOG_MISC,"Channel ban on %s expired.",b->ban); add_mode('-','b',b->ban); } b=b->next; } context; } /* kick anyone off the channel who matches a ban */ void kick_match_ban(ban) char *ban; { memberlist *m; char s[121]; context; if (!enforce_bans) return; m=channel.member; while (m->nick[0]) { sprintf(s,"%s!%s",m->nick,m->userhost); if ((wild_match(ban,s)) && (strcasecmp(m->nick,botname)!=0)) mprintf(serv,"KICK %s %s :banned\n",curchan,m->nick); m=m->next; } } /* kick everyone on the channel with matching hostmask that joined since the time stamp */ void kick_match_since(mask,timestamp) char *mask; time_t timestamp; { char s[121]; memberlist *m=channel.member; context; while (m->nick[0]) { sprintf(s,"%s!%s",m->nick,m->userhost); if ((wild_match(mask,s)) && (m->joined >= timestamp)) tprintf(serv,"KICK %s %s :lemmingbot\n",curchan,m->nick); m=m->next; } } /* resets the bans on the channel */ void resetbans() { banlist *b=channel.ban; context; if (!me_op()) return; /* can't do it */ /* remove bans we didn't put there */ while (b->ban[0]) { if (!equals_ban(b->ban)) add_mode('-','b',b->ban); b=b->next; } /* make sure the intended bans are still there */ recheck_bans(); context; } /* remove any bogus bans */ void kill_bogus_bans() { banlist *b=channel.ban; int bogus,i; context; if (!me_op()) return; while (b->ban[0]) { bogus=0; for (i=0; iban); i++) if ((b->ban[i]<32) || (b->ban[i]>126)) bogus=1; if (bogus) add_mode('-','b',b->ban); b=b->next; } } /* things to do when i just became a chanop: */ void recheck_channel() { memberlist *m; char s[121],hand[41]; int atr; /* okay, sort through who needs to be deopped. */ context; m=channel.member; while (m->nick[0]) { sprintf(s,"%s!%s",m->nick,m->userhost); atr=get_attr_host(s); get_handle_by_host(hand,s); if ((atr & USER_OP) && (atr & USER_DEOP)) log(LOG_MISC,"User is +o and +d (please fix): %s",s); else { if ((m->flags&CHANOP) && (strcasecmp(m->nick,botname)!=0) && ((atr & USER_DEOP) || ((bitch) && (!(atr & USER_OP))))) add_mode('-','o',m->nick); if ((!(m->flags&CHANOP)) && (atr & USER_OP) && (op_on_join)) add_mode('+','o',m->nick); } if ((enforce_bans) && (strcasecmp(m->nick,botname)!=0) && (match_ban(s))) refresh_ban_kick(s,m->nick); /* ^ will use the ban comment */ else if ((atr & USER_KICK) && (strcasecmp(m->nick,botname)!=0)) { get_handle_comment(hand,s); if (!s[0]) tprintf(serv,"KICK %s %s :...and thank you for playing.\n",curchan, m->nick); else tprintf(serv,"KICK %s %s :%s\n",curchan,m->nick,s); } m=m->next; } recheck_bans(); context; recheck_chanmode(); context; } void newly_chanop() { recheck_channel(); /* find out the bans now */ tprintf(serv,"MODE %s +b\n",curchan); } /* am i a chanop? */ int me_op() { memberlist *mx; mx=ismember(botname); if (mx==NULL) return 0; if (mx->flags&CHANOP) return 1; else return 0; } /* is user x a chanop? */ int member_op(x) char *x; { memberlist *mx; mx=ismember(x); if (mx==NULL) return 0; if (mx->flags&CHANOP) return 1; else return 0; } /* is user x a voice? (+v) */ int member_voice(x) char *x; { memberlist *mx; mx=ismember(x); if (mx==NULL) return 0; if (mx->flags&CHANVOICE) return 1; else return 0; } /* reset the channel information */ void reset_chan_info() { clear_channel(); channel.mode|=CHANPEND; tprintf(serv,"WHO %s\n",curchan); tprintf(serv,"MODE %s\n",curchan); if (me_op()) newly_chanop(); } /* if i'm the only person on the channel, and i'm not op'd, might as well leave and rejoin */ /* if i'm NOT the only person on the channel, but i'm still not op'd, demand ops */ void check_lonely_channel() { memberlist *m=channel.member; char s[121]; int i=0; static int whined=0; context; if (channel.mode&CHANPEND) return; if ((curchan[0]) && (!me_op())) { /* count non-split channel members */ while (m->nick[0]) { if (m->split == 0L) i++; m=m->next; } if (i==1) { tprintf(serv,"PART %s\n",curchan); tprintf(serv,"JOIN %s %s\n",botchan,chankey); log(LOG_MISC,"Attempting to regain chanop by recreating the channel."); whined=0; } else if (any_ops()) { whined=0; #ifdef TCL if (need_op[0]) do_tcl("need-op",need_op); #else if (gainops[0]) tprintf(serv,"%s\n",gainops); #endif } else { /* other people here, but none are ops */ /* are there other bots? make them LEAVE. */ int ok=1; if (!whined) { log(LOG_MISC,"Active channel, no ops :("); whined=1; } m=channel.member; while (m->nick[0]) { sprintf(s,"%s!%s",m->nick,m->userhost); if ((strcasecmp(m->nick,botname)!=0) && (!(get_attr_host(s) & USER_BOT))) ok=0; m=m->next; } if (ok) { /* ALL bots! make them LEAVE!!! */ m=channel.member; while (m->nick[0]) { mprintf(serv,"PRIVMSG %s :go\n",m->nick); m=m->next; } } } } } /* got 324: mode status */ void got324(from,msg) char *from,*msg; { int i=1; char *p,*q; context; split(NULL,msg); split(NULL,msg); /* get rid of to & chan fields */ while (msg[i]!=0) { context; if (msg[i]=='i') channel.mode |= CHANINV; if (msg[i]=='p') channel.mode |= CHANPRIV; if (msg[i]=='s') channel.mode |= CHANSEC; if (msg[i]=='m') channel.mode |= CHANMODER; if (msg[i]=='t') channel.mode |= CHANTOPIC; if (msg[i]=='n') channel.mode |= CHANNOMSG; if (msg[i]=='a') channel.mode |= CHANANON; if (msg[i]=='k') { context; p=strchr(msg,' '); p++; q=strchr(p,' '); if (q!=NULL) { *q=0; channel.key=(char *)nmalloc(strlen(p)+1); strcpy(channel.key,p); strcpy(p,q+1); } else { channel.key=(char *)nmalloc(strlen(p)+1); strcpy(channel.key,p); *p=0; } } if (msg[i]=='l') { context; p=strchr(msg,' '); p++; q=strchr(p,' '); if (q!=NULL) { *q=0; channel.maxmembers=atoi(p); strcpy(p,q+1); } else { channel.maxmembers=atoi(p); *p=0; } } i++; } context; } /* got a 352: who info! */ void got352(from,msg) char *from,*msg; { char userhost[121],nick[10]; char s[121]; memberlist *m; int waschanop,atr; context; split(NULL,msg); split(s,msg); from[0]=from[0]; if (strcasecmp(s,curchan)!=0) return; /* they're not on MY channel... */ split(userhost,msg); strcat(userhost,"@"); split(&userhost[strlen(userhost)],msg); split(NULL,msg); split(nick,msg); sprintf(s,"%s!%s",nick,userhost); fixfrom(s); splitnick(nick,s); strcpy(userhost,s); split(s,msg); m=ismember(nick); if (m==NULL) { m=newmember(); m->joined=m->split=0L; m->flags=0; m->last=time(NULL); } strcpy(m->nick,nick); strcpy(m->userhost,userhost); m->user=NULL; if (strcasecmp(nick,botname)==0) strcpy(botuserhost,userhost); waschanop=me_op(); if (strchr(s,'@')!=NULL) m->flags |= CHANOP; else m->flags &= ~CHANOP; if (strchr(s,'+')!=NULL) m->flags |= CHANVOICE; else m->flags &= ~CHANVOICE; if ((strcasecmp(nick,botname)==0) && (!waschanop) && (me_op())) newly_chanop(); if ((strcasecmp(nick,botname)==0) && (any_ops()) && (!me_op())) { #ifdef TCL if (need_op[0]) do_tcl("need-op",need_op); #else if (gainops[0]) tprintf(serv,"%s\n",gainops); #endif } sprintf(s,"%s!%s",nick,userhost); fixfrom(s); m->user=get_user_by_host(s); atr=get_attr_host(s); if ((m->flags&CHANOP) && (atr & USER_DEOP) && (me_op()) && (strcasecmp(nick,botname)!=0)) add_mode('-','o',nick); if ((match_ban(s)) && (strcasecmp(nick,botname)!=0) && (me_op()) && (enforce_bans)) tprintf(serv,"KICK %s %s :banned\n",curchan,nick); else if ((atr & USER_KICK) && (strcasecmp(nick,botname)!=0) && (me_op())) { char s1[121],hand[40]; get_handle_by_host(hand,s); get_handle_comment(hand,s1); if (s1[0]) tprintf(serv,"KICK %s %s :%s\n",curchan,nick,s1); else tprintf(serv,"KICK %s %s :...and thank you for playing.\n",curchan,nick); } } /* got 315: end of who */ void got315(from,msg) char *from,*msg; { channel.mode&=~CHANPEND; /* finished getting who list, can now be considered officially ON CHANNEL */ } /* got 367: ban info */ void got367(from,msg) char *from,*msg; { char s[121],who[81],*p; context; split(NULL,msg); split(NULL,msg); from[0]=from[0]; p=strchr(msg,' '); if (p!=NULL) { strcpy(s,msg); split(msg,s); split(who,s); newban(msg,who); } else newban(msg,"existant"); sprintf(s,"%s!%s",botname,botuserhost); if ((wild_match(msg,s)) && (me_op())) add_mode('-','b',msg); if ((get_attr_host(msg) & USER_OP) && (me_op())) add_mode('-','b',msg); /* these will be flushed by 368: end of ban info (in chanprog.c) */ } /* got 368: end of ban list */ void got368(from,msg) char *from,*msg; { /* ok, now add bans that i want, which aren't set yet */ context; if (clearbans) resetbans(); else { kill_bogus_bans(); recheck_bans(); } /* if i sent a mode -b on myself (deban) in got367, either */ /* resetbans() or recheck_bans() will flush that */ } /* got 251: lusers */ /* Yebor :there are 2258 users and 805 invisible on 127 servers */ void got251(from,msg) char *from,*msg; { int i; char servs[20]; split(NULL,msg); fixcolon(msg); from[0]=from[0]; for (i=0; i<8; i++) split(NULL,msg); split(servs,msg); if (strncmp(msg,"servers",7)!=0) return; /* was invalid format */ i=atoi(servs); if (min_servs==0) return; /* no minimum limit on servers */ if (ilast=time(NULL); #ifdef TCL { char s[121]; sprintf(s,"%s!%s",nick,from); get_handle_by_host(handle,s); check_tcl_topc(nick,from,handle,msg); } #endif } /* 331: no current topic for this channel */ void got331(from,msg) char *from,*msg; { #ifdef TCL check_tcl_topc("*","*","*",""); #endif } /* 332: topic on a channel i've just joined */ /* 332 Topic for #turtle: topic goes here */ void got332(from,msg) char *from,*msg; { context; split(NULL,msg); split(NULL,msg);/* split(NULL,msg); */ #ifdef TCL check_tcl_topc("*","*","*",msg); #endif } /* join */ void gotjoin(from,chan) char *from,*chan; { char s[121],handle[20],nick[10]; memberlist *m; time_t tt,tnow; int i,j; context; detect_flood(from,_JOIN,1); /* grab last time joined before we updated it */ get_handle_by_host(handle,from); get_handle_laston(handle,&tt); tnow=time(NULL); splitnick(nick,from); fixcolon(chan); m=ismember(nick); if (m!=NULL) { /* already on channel?!? */ if (m->split==0) killmember(nick); else if (strcasecmp(m->userhost,from)!=0) killmember(nick); else { m->split=0; m->last=time(NULL); set_handle_laston(handle,tnow); if ((m->flags&CHANOP) && (me_op()) && (op_on_join)) add_mode('+','o',nick); log(LOG_CHAN,"%s (%s) returned to %s.",nick,from,chan); return; } } m=newmember(); m->joined=tnow; m->split=0L; m->flags=0; m->last=time(NULL); strcpy(m->nick,nick); strcpy(m->userhost,from); sprintf(s,"%s!%s",nick,from); m->user=NULL; m->user=get_user_by_host(s); #ifdef TCL check_tcl_join(nick,from,handle); #endif set_handle_laston(handle,tnow); if (strcasecmp(nick,botname)==0) { /* it was me joining! */ log(LOG_CHAN|LOG_MISC,"%s joined %s.",nick,chan); strcpy(curchan,chan); channel.mode|=CHANPEND; tprintf(serv,"MODE %s\n",chan); tprintf(serv,"TOPIC %s\n",chan); tprintf(serv,"WHO %s\n",chan); /* can't get ban list till i'm opped :( */ } else { int atr; char s1[121]; log(LOG_CHAN,"%s (%s) joined %s.",nick,from,chan); atr=get_attr_host(s); for (i=0; i WAIT_INFO)) showinfo(handle,nick); i=num_notes(handle); for (j=0; jlast=time(NULL); #ifdef TCL get_handle_by_host(hand,s); check_tcl_kick(whodid,from,hand,nick,msg); #endif atr=get_attr_host(s); /* check for masskick */ if (!(atr & (USER_FRIEND|USER_MASTER)) && (strcasecmp(botname,whodid)!=0) && (strcasecmp(kicknick,whodid)==0)) { time_t tt=time(NULL); if (tt-kicktime <= 10) { kicks++; if (kicks>=3) { /* masskick */ tprintf(serv,"KICK %s %s :mass kick, go sit in a corner\n",curchan, whodid); kicks=0; kicknick[0]=0; kicktime=0L; } } else { kicks=1; kicktime=time(NULL); } } else { strcpy(kicknick,whodid); kicktime=time(NULL); kicks=1; } /* kicking an oplisted person? KICK THEM. */ m=ismember(nick); if (m!=NULL) { sprintf(s1,"%s!%s",m->nick,m->userhost); atr1=get_attr_host(s1); update_laston(s1); /* revenge on, kicker was not bot, kicker was not kickee, kickee was oplisted, and kicker was not (whew!) */ if ((atr1 & USER_OP) && (revenge) && (strcasecmp(whodid,botname)!=0) && (!(atr & USER_OP)) && (strcasecmp(whodid,nick)!=0)) tprintf(serv,"KICK %s %s :don't kick my friends, bud\n",curchan,whodid); log(LOG_MODES,"%s kicked by %s: %s",s1,s,msg); } if (strcasecmp(nick,botname)==0) { curchan[0]=0; tprintf(serv,"JOIN %s %s\n",botchan,chankey); clear_channel(); if ((revenge) && !(atr & (USER_MASTER|USER_FRIEND))) { take_revenge(s,"kicked me off the channel"); /* ^put the kicker on the deop list : revenge */ } } else killmember(nick); if (strcasecmp(nick,botname)!=0) check_lonely_channel(); } /* nick change */ void gotnick(from,msg) char *from, *msg; { char nick[31],s[121],hand[21]; memberlist *m; context; fixfrom(from); strcpy(s,from); splitnick(nick,s); fixcolon(msg); m=ismember(nick); if (m==NULL) return; /* confused server. */ m->last=time(NULL); #ifdef TCL get_handle_by_host(hand,from); check_tcl_nick(nick,s,hand,msg); #endif log(LOG_CHAN,"Nick change: %s -> %s",m->nick,msg); if (strcasecmp(m->nick,botname)==0) strcpy(botname,msg); strcpy(m->nick,msg); clear_chanlist(); /* cache is meaningless now */ } /* signoff, similar to part */ void gotquit(from,msg) char *from,*msg; { char nick[31],oldfrom[121]; memberlist *m; char *p; context; strcpy(oldfrom,from); splitnick(nick,from); fixcolon(msg); /* Fred1: instead of expensive wild_match on signoff, quicker method */ /* determine if signoff string matches "%.% %.%", and only one space */ p=strchr(msg,' '); if ((p!=NULL) && (p==strrchr(msg,' '))) { char *z1,*z2; *p=0; z1=strchr(p+1,'.'); z2=strchr(msg,'.'); if ((z1!=NULL) && (z2!=NULL) && (*(z1+1)!=0) && (z1-1 != p) && (z2+1 != p) && (z2 != msg)) { /* server split, or else it looked like it anyway */ /* (no harm in assuming) */ m=ismember(nick); if (m!=NULL) { m->split=time(NULL); log(LOG_CHAN,"%s (%s) got netsplit.",nick,from); return; } } else *p=' '; } #ifdef TCL { char s[121],hand[21]; sprintf(s,"%s!%s",nick,from); get_handle_by_host(hand,s); check_tcl_sign(nick,from,hand,msg); } #endif update_laston(from); killmember(nick); log(LOG_CHAN,"%s (%s) left irc: %s",nick,from,msg); check_lonely_channel(); } /* msg to xx everyone on the channel's info */ void show_all_info(who) char *who; { memberlist *m; char s[120],nick[31],also[512]; int atr; context; also[0]=0; m=channel.member; while (m->nick[0]) { sprintf(s,"%s!%s",m->nick,m->userhost); get_handle_by_host(nick,s); get_handle_info(nick,s); atr=get_attr_handle(nick); if (atr & USER_BOT) s[0]=0; if (s[0]=='@') strcpy(s,&s[1]); if (s[0]) hprintf(helpsock,"NOTICE %s :[%9s] %s\n",who,m->nick,s); else { if (strcasecmp(m->nick,botname)==0) hprintf(helpsock,"NOTICE %s :[%9s] <-- I'm the bot, of course.\n", who,m->nick); else if (atr & USER_BOT) { if (atr & BOT_SHARE) hprintf(helpsock,"NOTICE %s :[%9s] <-- a twin of me\n",who,m->nick); else hprintf(helpsock,"NOTICE %s :[%9s] <-- another bot\n",who,m->nick); } else { strcat(also,m->nick); strcat(also,", "); } } m=m->next; } if (also[0]) { also[strlen(also)-2]=0; hprintf(helpsock,"NOTICE %s :No info: %s\n",who,also); } } /* request from a user to kickban (over dcc) */ void user_kickban(idx,nick) int idx; char *nick; { memberlist *m; char s[181],s1[181],note[121],*p,*pp; int atr,i; context; p=NULL; if (strchr(nick,' ')!=NULL) { split(s,nick); sprintf(note,"%s: %s",dcc[idx].nick,nick); note[70]=0; strcpy(nick,s); p=strchr(note,' ')+1; } else sprintf(note,"requested by %s",dcc[idx].nick); if (curchan[0]==0) { dprintf(idx,"I'm not on a channel!\n"); return; } if (strcasecmp(nick,botname)==0) { dprintf(idx,"You're trying to pull a Run?\n"); return; } m=ismember(nick); if (m==NULL) { dprintf(idx,"Not on channel: %s\n",nick); return; } sprintf(s,"%s!%s",m->nick,m->userhost); atr=get_attr_host(s); if (atr & USER_OP) { dprintf(idx,"%s is a legal op.\n",nick); return; } if (atr & USER_MASTER) { dprintf(idx,"%s is a bot master.\n",nick); return; } strcpy(s,m->userhost); maskhost(s,s1); /* detect long username (10-char username can't add the '*') */ i=0; pp=m->userhost; while ((*pp) && (*pp!='@')) { pp++; i++; } if (i>9) strcpy(s,s1); else sprintf(s,"*!*%s",&s1[2]); /* gotta add that extra '*' */ if (m->flags&CHANOP) add_mode('-','o',m->nick); add_mode('+','b',s); flush_mode(); if (p!=NULL) tprintf(serv,"KICK %s %s :request: %s\n",curchan,m->nick,p); tprintf(serv,"KICK %s %s :requested\n",curchan,m->nick); prog_ban(s,time(NULL),note); dprintf(idx,"Okay, done.\n"); } /* check for expired netsplit people */ void check_expired_splits() { memberlist *m; time_t now; context; now=time(NULL); m=channel.member; while (m->nick[0]) { if (m->split) { if (curchan[0]==0) killmember(m->nick); else if (now-(m->split) > WAIT_SPLIT) { #ifdef TCL { char s[121],hand[21]; sprintf(s,"%s!%s",m->nick,m->userhost); get_handle_by_host(hand,s); check_tcl_sign(m->nick,m->userhost,hand,"lost in the netsplit"); } #endif log(LOG_CHAN,"%s (%s) got lost in the net-split.",m->nick, m->userhost); killmember(m->nick); } } m=m->next; } } void check_for_split() { /* called once a minute... but if we're the only one on the channel, we only wanna send out "lusers" once every 5 mins */ static count=4; context; if (channel.mode&CHANPEND) return; if ((curchan[0]) && (members==1)) { count++; if (count>=5) { mprintf(serv,"LUSERS\n"); count=0; } } } /* called once a minute to kick idlers */ void check_idle_kick() { memberlist *m=channel.member; time_t now; char s[121]; int atr; if (!idle_kick) return; if (!curchan[0]) return; context; now=time(NULL); while (m->nick[0]) { if ((now-(m->last) >= idle_kick*60) && (strcmp(m->nick,botname)!=0)) { sprintf(s,"%s!%s",m->nick,m->userhost); atr=get_attr_host(s); if (!(atr & (USER_MASTER|USER_BOT|USER_FRIEND|USER_OP))) mprintf(serv,"KICK %s %s :idle %d min\n",curchan,m->nick,idle_kick); } m=m->next; } } /* add a user who's on the channel */ int add_chan_user(nick,idx) char *nick; int idx; { memberlist *m; char s[121],s1[121]; context; if (curchan[0]==0) { dprintf(idx,"I'm not on a channel!\n"); return 0; } m=ismember(nick); if (m==NULL) { dprintf(idx,"%s is not on the channel.\n",nick); return 0; } sprintf(s,"%s!%s",m->nick,m->userhost); get_handle_by_host(s1,s); if (s1[0]!='*') { dprintf(idx,"%s is already known as %s.\n",nick,s1); return 0; } maskhost(s,s1); if (!is_user(nick)) { dprintf(idx,"Added [%s]%s with no password.\n",m->nick,s1); userlist=adduser(userlist,m->nick,s1,"nopass",default_flags); return 1; } else { char h[21]; get_handle_by_host(h,s); if (strcmp(h,"*")!=0) { dprintf(idx,"This user already matches for %s!\n",h); return 0; } dprintf(idx,"Added hostmask %s to %s.\n",s1,m->nick); addhost_by_handle(m->nick,s1); return 1; } } /* op/deop on the fly per master's request */ void give_op(nick,idx) char *nick; int idx; { memberlist *m; char s[121]; int atr; context; if (curchan[0]==0) { dprintf(idx,"I'm not on a channel!\n"); return; } m=ismember(nick); if (m==NULL) { dprintf(idx,"%s is not on the channel.\n",nick); return; } sprintf(s,"%s!%s",m->nick,m->userhost); atr=get_attr_host(s); if (atr & USER_DEOP) { dprintf(idx,"%s is currently being auto-deopped.\n",m->nick); return; } if ((bitch) && (!(atr & USER_OP))) { dprintf(idx,"%s is not a registered op.\n",m->nick); return; } add_mode('+','o',nick); dprintf(idx,"Gave op to %s\n",nick); } void give_deop(nick,idx) char *nick; int idx; { memberlist *m; char s[121]; context; if (curchan[0]==0) { dprintf(idx,"I'm not on a channel!\n"); return; } m=ismember(nick); if (m==NULL) { dprintf(idx,"%s is not on the channel.\n",nick); return; } sprintf(s,"%s!%s",m->nick,m->userhost); if (get_attr_host(s) & USER_OP) { dprintf(idx,"%s is currently being auto-opped.\n",m->nick); return; } add_mode('-','o',nick); dprintf(idx,"Took op from %s\n",nick); } void update_idle(nick) char *nick; { memberlist *m; m=ismember(nick); if (m==NULL) return; m->last=time(NULL); }