/* mode.c -- handles: queueing and flushing mode changes made by the bot channel mode changes and the bot's reaction to them setting and getting the current wanted channel modes dprintf'ized, 12dec95 multi-channel, 6feb96 */ #define _MODE #if HAVE_CONFIG_H #include #endif #include #include #include #include "eggdrop.h" #include "users.h" #include "chan.h" #include "proto.h" /* reversing this mode? */ int reversing=0; extern int serv; extern char botname[]; extern char botuserhost[]; extern char botuser[]; extern int revenge; extern char cx_file[]; extern int cx_line; extern char origbotname[]; extern char SBUF[]; extern struct chanset_t *chanset; #define PLUS 1 #define MINUS 2 #define CHOP 4 #define BAN 8 #define VOICE 16 void flush_mode(chan) struct chanset_t *chan; { char *p,*post; int i,ok=0; p=SBUF; post=&SBUF[512]; *p=0; *post=0; if (chan->pls[0]) *p++='+'; for (i=0; ipls); i++) *p++=chan->pls[i]; if (chan->mns[0]) *p++='-'; for (i=0; imns); i++) *p++=chan->mns[i]; chan->pls[0]=0; chan->mns[0]=0; ok=0; /* +k or +l ? */ if (chan->key[0]) { if (!ok) { *p++='+'; ok=1; } *p++='k'; strcat(post,chan->key); strcat(post," "); } if (chan->limit != -1) { if (!ok) { *p++='+'; ok=1; } *p++='l'; sprintf(&post[strlen(post)],"%d ",chan->limit); } chan->limit=(-1); chan->key[0]=0; for (i=0; i<3; i++) if (chan->cmode[i].type&PLUS) { if (!ok) { *p++='+'; ok=1; } *p++=(chan->cmode[i].type&BAN?'b':(chan->cmode[i].type&CHOP?'o':'v')); strcat(post,chan->cmode[i].op); strcat(post," "); nfree(chan->cmode[i].op); chan->cmode[i].op=NULL; } ok=0; /* -k ? */ if (chan->rmkey[0]) { if (!ok) { *p++='-'; ok=1; } *p++='k'; strcat(post,chan->rmkey); strcat(post," "); } chan->rmkey[0]=0; for (i=0; i<3; i++) if (chan->cmode[i].type&MINUS) { if (!ok) { *p++='-'; ok=1; } *p++=(chan->cmode[i].type&BAN?'b':(chan->cmode[i].type&CHOP?'o':'v')); strcat(post,chan->cmode[i].op); strcat(post," "); nfree(chan->cmode[i].op); chan->cmode[i].op=NULL; } *p=0; for (i=0; i<3; i++) chan->cmode[i].type=0; if (post[strlen(post)-1]==' ') post[strlen(post)-1]=0; if (post[0]) { strcat(SBUF," "); strcat(SBUF,post); } if (SBUF[0]) tprintf(serv,"MODE %s %s\n",chan->name,SBUF); } /* for EVERY channel */ void flush_modes() { struct chanset_t *chan; chan=chanset; while (chan!=NULL) { flush_mode(chan); chan=chan->next; } } /* queue a channel mode change */ void add_mode(chan,plus,mode,op) struct chanset_t *chan; char plus,mode,*op; { int i,type,ok; char s[21]; if ((mode=='o') || (mode=='b') || (mode=='v')) { type=(plus=='+'?PLUS:MINUS)|(mode=='o'?CHOP:(mode=='b'?BAN:VOICE)); /* op-type mode change */ for (i=0; i<3; i++) if ((chan->cmode[i].type==type) && (chan->cmode[i].op!=NULL) && (strcasecmp(chan->cmode[i].op,op)==0)) return; /* already in there :- duplicate */ ok=0; /* add mode to buffer */ for (i=0; i<3; i++) if ((chan->cmode[i].type==0) && (!ok)) { chan->cmode[i].type=type; chan->cmode[i].op=(char *)nmalloc(strlen(op)+1); strcpy(chan->cmode[i].op,op); ok=1; } ok=0; /* check for full buffer */ for (i=0; i<3; i++) if (chan->cmode[i].type==0) ok=1; if (!ok) flush_mode(chan); /* full buffer! flush modes */ return; } /* +k ? store key */ if ((plus=='+') && (mode=='k')) { strcpy(chan->key,op); return; } /* -k ? store removed key */ if ((plus=='-') && (mode=='k')) { strcpy(chan->rmkey,op); return; } /* +l ? store limit */ if ((plus=='+') && (mode=='l')) { chan->limit=atoi(op); return; } /* typical mode changes */ if (plus=='+') strcpy(s,chan->pls); else strcpy(s,chan->mns); if (strchr(s,mode)!=NULL) return; /* duplicate */ if (plus=='+') { chan->pls[strlen(chan->pls)+1]=0; chan->pls[strlen(chan->pls)]=mode; } else { chan->mns[strlen(chan->mns)+1]=0; chan->mns[strlen(chan->mns)]=mode; } } /**********************************************************************/ /* horrible code to parse mode changes */ /* no, it's not horrible, it just looks that way */ void got_key(chan,nick,from,key,atr) struct chanset_t *chan; char *nick,*from,*key; int atr; { int bogus=0,i; context; set_key(chan,key); for (i=0; i126)) bogus=1; if ((bogus) && (strcasecmp(nick,botname)!=0)) { putlog(LOG_MODES,chan->name,"Bogus channel key on %s!",chan->name); tprintf(serv,"KICK %s %s :bogus channel key\n",chan->name,nick); } if ((reversing) || (bogus) || ((chan->mode_mns_prot&CHANKEY) && (!(atr&USER_MASTER)))) add_mode(chan,'-','k',key); } void got_op(chan,nick,from,who,atr1) struct chanset_t *chan; char *nick,*from,*who; int atr1; { memberlist *m; char s[UHOSTLEN]; int atr; context; m=ismember(chan,who); if (m==NULL) { putlog(LOG_MISC,chan->name,"* Mode change on %s for nonexistant %s!", chan->name,who); tprintf(serv,"WHO %s\n",who); return; } sprintf(s,"%s!%s",m->nick,m->userhost); atr=get_attr_host(s); if ((!me_op(chan)) && (strcasecmp(who,botname)==0)) newly_chanop(chan); else if ((me_op(chan)) && (strcasecmp(who,botname)!=0) && (!(m->flags & SENTDEOP))) { if ((chan->stat&CHAN_BITCH) && (!(atr & USER_OP)) && (!(atr1 & USER_MASTER))) { add_mode(chan,'-','o',who); m->flags|=SENTDEOP; } else if ((atr&USER_DEOP) && (!(atr&USER_OP)) && (!(atr1&USER_MASTER))) { add_mode(chan,'-','o',who); m->flags|=SENTDEOP; } else if (reversing) { add_mode(chan,'-','o',who); m->flags|=SENTDEOP; } } else if ((reversing) && (!(m->flags & SENTDEOP)) && (strcasecmp(who,botname)!=0)) { add_mode(chan,'-','o',who); m->flags|=SENTDEOP; } if (nick[0]==0) { /* server op! */ if ((!(atr & (USER_OP|USER_FRIEND|USER_MASTER))) && (!(m->flags&CHANOP)) && (!(m->flags&SENTDEOP)) && (me_op(chan)) && (chan->stat&CHAN_STOPNETHACK)) { add_mode(chan,'-','o',who); m->flags|=(FAKEOP|SENTDEOP); } } else m->flags&=~FAKEOP; m->flags|=CHANOP; m->flags&=~SENTOP; } void got_deop(chan,nick,from,who,atr1) struct chanset_t *chan; char *nick,*from,*who; int atr1; { memberlist *m; char s[UHOSTLEN],s1[UHOSTLEN],s2[UHOSTLEN]; int atr; context; m=ismember(chan,who); if (m==NULL) { putlog(LOG_MISC,chan->name,"* Mode change on %s for nonexistant %s!", chan->name,who); tprintf(serv,"WHO %s\n",who); return; } sprintf(s,"%s!%s",m->nick,m->userhost); fixfrom(s); sprintf(s1,"%s!%s",nick,from); atr=get_attr_host(s); if ((me_op(chan)) && (atr & (USER_OP|USER_FRIEND)) && (!(atr & USER_DEOP)) && (strcasecmp(nick,botname)!=0) && (strcasecmp(who,nick)!=0) && (!(m->flags & SENTOP)) && (chan->stat&CHAN_PROTECTOPS) && (m->flags & CHANOP) && (strcasecmp(who,botname)!=0)) { /* added 25mar96, robey */ /* deop'd someone on my oplist */ if (!(atr1 & USER_MASTER)) { /* op her */ add_mode(chan,'+','o',who); m->flags|=SENTOP; } else if (reversing) { add_mode(chan,'+','o',who); m->flags|=SENTOP; } if (!(atr1 & (USER_MASTER|USER_FRIEND|USER_OP)) && (revenge)) { if (!nick[0]) putlog(LOG_MODES,chan->name,"TS resync (%s): %s deopped by %s", chan->name,who,from); else { sprintf(s2,"deopped %s",s); take_revenge(chan,s1,s2); /* punish bad guy */ } } } /* check for mass deop */ if (!(atr1 & (USER_MASTER|USER_FRIEND|USER_BOT)) && (nick[0]) && (strcasecmp(nick,botname)!=0)) { if (strcasecmp(nick,chan->deopnick)==0) { time_t tx=time(NULL); if (tx - chan->deoptime <= 10) { chan->deops++; if (chan->deops >= 3) { putlog(LOG_MODES,chan->name,"Mass deop on %s by %s",chan->name,s1); tprintf(serv,"KICK %s %s :mass deop, go sit in a corner\n", chan->name,nick); chan->deopnick[0]=0; chan->deoptime=0L; chan->deops=0; } } else { chan->deoptime=time(NULL); chan->deops=1; } } else { strcpy(chan->deopnick,nick); chan->deoptime=time(NULL); chan->deops=1; } } /* having op hides your +v status -- so now that someone's lost ops, check to see if they have +v */ if (!(m->flags&CHANVOICE)) tprintf(serv,"WHO %s\n",m->nick); if ((strcasecmp(who,botname)==0) && (revenge)) { /* deopped ME! take revenge */ if ((!(atr1 & (USER_MASTER|USER_FRIEND))) && (nick[0])) take_revenge(chan,s1,"deopped me"); if (!nick[0]) putlog(LOG_MODES,chan->name,"TS resync deopped me on %s :(",chan->name); if (chan->need_op[0]) do_tcl("need-op",chan->need_op); } m->flags&=~(FAKEOP|CHANOP|SENTDEOP); } void got_ban(chan,nick,from,who,atr) struct chanset_t *chan; char *nick,*from,*who; int atr; { char me[UHOSTLEN],s[UHOSTLEN],s1[UHOSTLEN]; int check,i,bogus; memberlist *m; context; sprintf(me,"%s!%s",botname,botuserhost); sprintf(s,"%s!%s",nick,from); newban(chan,who,s); bogus=0; check=1; if (strcasecmp(nick,botname)!=0) { /* it's not my ban */ if ((chan->stat&CHAN_NOUSERBANS) && (!(atr&USER_BOT)) && (!(atr&USER_MASTER))) { /* mprintf(serv,"PRIVMSG %s :You may not place bans directly.\n",nick); */ add_mode(chan,'-','b',who); return; } for (i=0; i126)) bogus=1; if (bogus) { if (atr & (USER_MASTER|USER_FRIEND)) { /* fix their bogus ban */ int ok=0; strcpy(s1,who); for (i=0; i126)) s1[i]='?'; if ((s1[i]!='?') && (s1[i]!='*') && (s1[i]!='!') && (s1[i]!='@')) ok=1; } add_mode(chan,'-','b',who); flush_mode(chan); /* only re-add it if it has something besides wildcards */ if (ok) add_mode(chan,'+','b',s1); } else { add_mode(chan,'-','b',who); tprintf(serv,"KICK %s %s :bogus ban\n",chan->name,nick); } return; } /* does this remotely match against any of our hostmasks? */ /* just an off-chance... */ if (get_attr_host(who) & (USER_OP|USER_FRIEND|USER_MASTER)) { if (!(atr&USER_MASTER)) { add_mode(chan,'-','b',who); check=0; } if (get_attr_host(who) & USER_MASTER) check=0; } else if ((wild_match(who,me)) && (me_op(chan))) add_mode(chan,'-','b',who); /* ^ don't really feel like being banned today, thank you! */ else { /* banning an oplisted person who's on the channel? */ m=chan->channel.member; while (m->nick[0]) { sprintf(s1,"%s!%s",m->nick,m->userhost); if (wild_match(who,s1)) { int tatr=get_attr_host(s1); if (tatr & (USER_OP|USER_FRIEND|USER_MASTER)) { /* remove ban on +o/f/m user, unless placed by another +m */ if (!(atr&USER_MASTER)) { add_mode(chan,'-','b',who); check=0; } if (tatr&USER_MASTER) check=0; } } m=m->next; } if (check) kick_match_ban(chan,who); } } else kick_match_ban(chan,who); /* is it a server ban from nowhere? */ /* uncomment the following line to remove all server bans */ /* if ((!nick[0]) && (!equals_ban(who)) && (check)) add_mode(chan,'-','b',who); else */ if (reversing) add_mode(chan,'-','b',who); } void got_unban(chan,nick,from,who,atr) struct chanset_t *chan; char *nick,*from,*who; int atr; { int i,bogus; killban(chan,who); bogus=0; for (i=0; i126)) bogus=1; if ((bogus) && (strcasecmp(nick,botname)!=0) && (!isbanned(chan,who)) && !(atr & (USER_OP|USER_FRIEND|USER_MASTER))) { tprintf(serv,"KICK %s %s :bogus ban\n",chan->name,nick); return; } if (((equals_ban(who)) || (u_equals_ban(chan->bans,who))) && (me_op(chan)) && (!(chan->stat&CHAN_DYNAMICBANS))) { /* that's a permban! */ if ((atr & (USER_BOT|BOT_SHARE)) == (USER_BOT|BOT_SHARE)) { /* sharebot -- do nothing */ } else if (atr & (USER_MASTER|USER_OP)) { hprintf(serv,"NOTICE %s :That's in my permban list. You need %s\n", nick,"to use '-ban' in dcc chat if you want it gone for good."); } else add_mode(chan,'+','b',who); } if (reversing) add_mode(chan,'+','b',who); } #define modechg(x,y) { \ char ms[3]; \ ms[0]=(pos==1)?'+':'-'; ms[1]=y; ms[2]=0; \ check_tcl_mode(nick,from,hand,chan->name,ms); \ if (pos==1) chan->channel.mode|=(x); else chan->channel.mode&=~(x); \ if ((((pos==1)&&(chan->mode_mns_prot&(x))) || \ ((pos==-1)&&(chan->mode_pls_prot&(x)))) && (!(atr&USER_MASTER))) \ add_mode(chan,(pos==1)?'-':'+',y,""); \ else if ((reversing) && ((pos==1)||(chan->mode_pls_prot&(x))) && \ ((pos==-1)||(chan->mode_mns_prot&(x)))) \ add_mode(chan,(pos==1)?'-':'+',y,""); \ } /* a pain in the ass: mode changes */ void gotmode(from,msg) char *from,*msg; { char nick[NICKLEN],hand[10],ch[UHOSTLEN],op[UHOSTLEN],chg[81]; char s[UHOSTLEN],ms[UHOSTLEN]; int pos=0,i,atr; memberlist *m; struct chanset_t *chan; context; split(ch,msg); nsplit(chg,msg); reversing=0; /* discard usermode changes, and +channels don't have modes: */ if ((ch[0]!='#') && (ch[0]!='&')) return; chan=findchan(ch); if (chan==NULL) { putlog(LOG_MISC,"*","Oops, someone joined me to %s ... leaving ...",ch); tprintf(serv,"PART %s\n",ch); return; } if (chan->stat & CHANPEND) return; /* not yet! */ putlog(LOG_MODES,chan->name,"%s: mode change '%s %s' by %s",ch,chg,msg,from); atr=get_attr_host(from); get_handle_by_host(hand,from); splitnick(nick,from); i=0; m=ismember(chan,nick); if (m!=NULL) if ((m->flags&FAKEOP) && (me_op(chan))) { putlog(LOG_MODES,ch,"Mode change by fake op on %s! Reversing...",ch); tprintf(serv,"KICK %s %s :abusing ill-gained server ops\n",ch,nick); reversing=1; } while (chg[i]!=0) { if (chg[i]=='+') pos=1; if (chg[i]=='-') pos=(-1); if (chg[i]=='i') modechg(CHANINV,'i'); if (chg[i]=='p') modechg(CHANPRIV,'p'); if (chg[i]=='s') modechg(CHANSEC,'s'); if (chg[i]=='m') modechg(CHANMODER,'m'); if (chg[i]=='t') modechg(CHANTOPIC,'t'); if (chg[i]=='n') modechg(CHANNOMSG,'n'); if (chg[i]=='a') modechg(CHANANON,'a'); if (chg[i]=='l') { if (pos==-1) { check_tcl_mode(nick,from,hand,chan->name,"-l"); if ((reversing) && (chan->channel.maxmembers!=(-1))) { sprintf(s,"%d",chan->channel.maxmembers); add_mode(chan,'+','l',s); } else if ((chan->limit_prot!=(-1)) && (!(atr&USER_MASTER))) { sprintf(s,"%d",chan->limit_prot); add_mode(chan,'+','l',s); } chan->channel.maxmembers=(-1); } else { nsplit(op,msg); chan->channel.maxmembers=atoi(op); sprintf(ms,"+l %d",chan->channel.maxmembers); check_tcl_mode(nick,from,hand,chan->name,ms); if ((reversing) || ((chan->mode_mns_prot&CHANLIMIT) && !(atr&USER_MASTER))) { if (chan->channel.maxmembers==0) add_mode(chan,'+','l',"23"); add_mode(chan,'-','l',""); } if ((chan->limit_prot != chan->channel.maxmembers) && !(atr&USER_MASTER)) { sprintf(s,"%d",chan->limit_prot); add_mode(chan,'+','l',s); } } } if (chg[i]=='k') { nsplit(op,msg); sprintf(ms,"%ck %s",(pos==1)?'+':'-',op); check_tcl_mode(nick,from,hand,chan->name,ms); if (pos==1) got_key(chan,nick,from,op,atr); else { if ((reversing) && (chan->channel.key[0])) add_mode(chan,'+','k',chan->channel.key); else if ((chan->key_prot[0]) && (!(atr&USER_MASTER))) add_mode(chan,'+','k',chan->key_prot); set_key(chan,NULL); } } if (chg[i]=='o') { nsplit(op,msg); sprintf(ms,"%co %s",(pos==1)?'+':'-',op); check_tcl_mode(nick,from,hand,chan->name,ms); if (pos==1) got_op(chan,nick,from,op,atr); else got_deop(chan,nick,from,op,atr); } if (chg[i]=='v') { nsplit(op,msg); m=ismember(chan,op); if (m==NULL) { putlog(LOG_MISC,chan->name,"* Mode change on %s for nonexistant %s!", chan->name,op); tprintf(serv,"WHO %s\n",op); } else { sprintf(ms,"%cv %s",(pos==1)?'+':'-',op); check_tcl_mode(nick,from,hand,chan->name,ms); if (pos==1) { m->flags|=CHANVOICE; if (reversing) add_mode(chan,'-','v',op); } else { m->flags&=~CHANVOICE; if (reversing) add_mode(chan,'+','v',op); } } } if (chg[i]=='b') { nsplit(op,msg); sprintf(ms,"%cb %s",(pos==1)?'+':'-',op); check_tcl_mode(nick,from,hand,chan->name,ms); if (pos==1) got_ban(chan,nick,from,op,atr); else got_unban(chan,nick,from,op,atr); } i++; } } void recheck_chanmode(chan) struct chanset_t *chan; { int i,chk; char plus,s[81]; context; for (i=0; i<2; i++) { if (i==0) { chk=chan->mode_pls_prot; plus='+'; } else { chk=chan->mode_mns_prot; plus='-'; } if (chk&CHANINV) add_mode(chan,plus,'i',""); if (chk&CHANPRIV) add_mode(chan,plus,'p',""); if (chk&CHANSEC) add_mode(chan,plus,'s',""); if (chk&CHANMODER) add_mode(chan,plus,'m',""); if (chk&CHANTOPIC) add_mode(chan,plus,'t',""); if (chk&CHANNOMSG) add_mode(chan,plus,'n',""); if (chk&CHANANON) add_mode(chan,plus,'a',""); if (chk&CHANLIMIT) { if (chan->channel.maxmembers == 0) add_mode(chan,'+','l',"23"); add_mode(chan,'-','l',""); } if (chk&CHANKEY) add_mode(chan,'-','k',chan->channel.key); } if (chan->limit_prot != chan->channel.maxmembers) { sprintf(s,"%d",chan->limit_prot); add_mode(chan,'+','l',s); } if (chan->key_prot[0]) { if (chan->channel.key[0]) if (strcasecmp(chan->channel.key,chan->key_prot)!=0) add_mode(chan,'-','k',chan->channel.key); add_mode(chan,'+','k',chan->key_prot); } } /* interpret configfile setting for modes to protect */ #define protmode(x) { \ chan->mode_pls_prot&=(~(x)); chan->mode_mns_prot&=(~(x)); \ if (pos==1) chan->mode_pls_prot|=(x); else chan->mode_mns_prot|=(x); \ } void set_mode_protect(chan,set) struct chanset_t *chan; char *set; { int i,pos=1; char s[121],s1[121]; context; nsplit(s,set); /* clear old modes */ chan->mode_mns_prot=chan->mode_pls_prot=0; chan->limit_prot=(-1); chan->key_prot[0]=0; for (i=0; imode_mns_prot&=(~CHANLIMIT); chan->limit_prot=(-1); if (pos==-1) chan->mode_mns_prot|=CHANLIMIT; else { nsplit(s1,set); if (s1[0]) chan->limit_prot=atoi(s1); } } if (s[i]=='k') { chan->mode_mns_prot&=(~CHANKEY); chan->key_prot[0]=0; if (pos==-1) chan->mode_mns_prot|=CHANKEY; else { nsplit(s1,set); if (s1[0]) strcpy(chan->key_prot,s1); } } } if (chan->stat&CHANACTIVE) recheck_chanmode(chan); } void get_mode_protect(chan,s) struct chanset_t *chan; char *s; { char *p=s,s1[121]; int ok=0,i,tst; s1[0]=0; for (i=0; i<2; i++) { ok=0; if (i==0) { tst=chan->mode_pls_prot; if ((tst) || (chan->limit_prot!=(-1)) || (chan->key_prot[0])) *p++='+'; if (chan->limit_prot!=(-1)) { *p++='l'; sprintf(&s1[strlen(s1)],"%d ",chan->limit_prot); } if (chan->key_prot[0]) { *p++='k'; sprintf(&s1[strlen(s1)],"%s ",chan->key_prot); } } else { tst=chan->mode_mns_prot; if (tst) *p++='-'; } if (tst&CHANINV) *p++='i'; if (tst&CHANPRIV) *p++='p'; if (tst&CHANSEC) *p++='s'; if (tst&CHANMODER) *p++='m'; if (tst&CHANTOPIC) *p++='t'; if (tst&CHANNOMSG) *p++='n'; if (tst&CHANLIMIT) *p++='l'; if (tst&CHANKEY) *p++='k'; if (tst&CHANANON) *p++='a'; } *p=0; if (s1[0]) { s1[strlen(s1)-1]=0; strcat(s," "); strcat(s,s1); } }