/* chanset.c -- handles: low-level channel and chanset manipulation channel pointers to the userrec cache check expired channel stuff dprintf'ized, 5feb96 multi-channel, 6feb96 */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include "eggdrop.h" #include "users.h" #include "chan.h" #include "proto.h" /* data for each channel */ struct chanset_t *chanset=NULL; extern int cx_line; extern char cx_file[]; extern char botname[]; extern char newbotname[]; extern int serv; extern int ban_time; /* memory expected to be used by this module */ int expmem_chan() { int tot=0; banlist *b; struct chanset_t *chan=chanset; while (chan!=NULL) { tot+=sizeof(struct chanset_t); tot+=strlen(chan->channel.key)+1; tot+=(sizeof(struct memstruct)*(chan->channel.members+1)); b=chan->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; } chan=chan->next; } return tot; } /* find a chanset by channel name */ struct chanset_t *findchan(name) char *name; { struct chanset_t *chan=chanset; while (chan!=NULL) { if (strcasecmp(chan->name,name)==0) return chan; chan=chan->next; } return NULL; } /* get pointer to new chanset */ struct chanset_t *newchanset() { struct chanset_t *c; c=(struct chanset_t *)nmalloc(sizeof(struct chanset_t)); return c; } /* add a chanset pointer to the list */ void addchanset(chan) struct chanset_t *chan; { struct chanset_t *c=chanset,*old=NULL; chan->next=NULL; while (c!=NULL) { old=c; c=c->next; } if (old!=NULL) old->next=chan; else chanset=chan; } /* destroy a chanset in the list */ /* does NOT free up memory associated with channel data inside the chanset! */ int killchanset(name) char *name; { struct chanset_t *c=chanset,*old=NULL; while (c!=NULL) { if (strcasecmp(c->name,name)==0) { if (old!=NULL) old->next=c->next; else chanset=c->next; nfree(c); return 1; } old=c; c=c->next; } return 0; } /* get channels list */ void getchanlist(s) char *s; { struct chanset_t *chan=chanset; s[0]=0; while (chan!=NULL) { if (s[0]) strcat(s," "); strcat(s,chan->name); chan=chan->next; } } /* set the key */ void set_key(chan,k) struct chanset_t *chan; char *k; { context; nfree(chan->channel.key); if (k==NULL) { chan->channel.key=(char *)nmalloc(1); chan->channel.key[0]=0; return; } chan->channel.key=(char *)nmalloc(strlen(k)+1); strcpy(chan->channel.key,k); } /* is this channel +s/+p? */ int channel_hidden(chan) struct chanset_t *chan; { return (chan->channel.mode&(CHANPRIV|CHANSEC)); } int hand_on_chan(chan,handle) struct chanset_t *chan; char *handle; { char s[UHOSTLEN],h[10]; memberlist *m=chan->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; } /* initialize out the channel record */ void init_channel(chan) struct chanset_t *chan; { context; chan->channel.maxmembers=(-1); chan->channel.mode=0; chan->channel.members=0; chan->channel.key=(char *)nmalloc(1); chan->channel.key[0]=0; chan->channel.ban=(banlist *)nmalloc(sizeof(banlist)); chan->channel.ban->ban=(char *)nmalloc(1); chan->channel.ban->ban[0]=0; chan->channel.ban->who=NULL; chan->channel.ban->next=NULL; chan->channel.member=(memberlist *)nmalloc(sizeof(memberlist)); chan->channel.member->nick[0]=0; chan->channel.member->next=NULL; } /* clear out channel data from memory */ void clear_channel(chan,reset) struct chanset_t *chan; int reset; { memberlist *m,*m1; banlist *b,*b1; context; nfree(chan->channel.key); m=chan->channel.member; while (m!=NULL) { m1=m->next; nfree(m); m=m1; } b=chan->channel.ban; while (b!=NULL) { b1=b->next; if (b->ban[0]) nfree(b->who); nfree(b->ban); nfree(b); b=b1; } if (reset) init_channel(chan); context; } /* reset all the channels, as if we just left a server or something */ void clear_channels() { struct chanset_t *chan; chan=chanset; while (chan!=NULL) { clear_channel(chan,1); chan->stat&=~(CHANPEND|CHANACTIVE); chan=chan->next; } } /* shortcut for get_user_by_host -- might have user record in one */ /* of the channel caches */ struct userrec *check_chanlist(host) char *host; { char nick[NICKLEN],uhost[UHOSTLEN]; memberlist *m; struct chanset_t *chan; chan=chanset; strcpy(uhost,host); splitnick(nick,uhost); while (chan!=NULL) { m=chan->channel.member; while (m->nick[0]) { if ((strcasecmp(uhost,m->userhost)==0) && (strcasecmp(nick,m->nick)==0)) return m->user; m=m->next; } chan=chan->next; } return NULL; } /* shortcut for get_user_by_handle -- might have user record in channels */ struct userrec *check_chanlist_hand(hand) char *hand; { struct chanset_t *chan=chanset; memberlist *m; while (chan!=NULL) { m=chan->channel.member; while (m->nick[0]) { if (m->user != NULL) if (strcasecmp(m->user->handle,hand)==0) return m->user; m=m->next; } chan=chan->next; } return NULL; } /* clear the user pointers in the chanlists */ /* (necessary when a hostmask is added/removed or a user is added) */ void clear_chanlist() { memberlist *m; struct chanset_t *chan=chanset; while (chan!=NULL) { m=chan->channel.member; while (m->nick[0]) { m->user=NULL; m=m->next; } chan=chan->next; } } /* if this user@host is in a channel, set it (it was null) */ void set_chanlist(host,rec) char *host; struct userrec *rec; { char nick[NICKLEN],uhost[UHOSTLEN]; memberlist *m; struct chanset_t *chan=chanset; strcpy(uhost,host); splitnick(nick,uhost); while (chan!=NULL) { m=chan->channel.member; while (m->nick[0]) { if ((strcasecmp(uhost,m->userhost)==0) && (strcasecmp(nick,m->nick)==0)) m->user=rec; m=m->next; } chan=chan->next; } } int active_channel(name) char *name; { struct chanset_t *chan; chan=findchan(name); if (chan==NULL) return 0; if (chan->stat&CHANACTIVE) return 1; return 0; } /* returns a pointer to a new channel member structure */ memberlist *newmember(chan) struct chanset_t *chan; { memberlist *x; x=chan->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; chan->channel.members++; return x; } /* adds a ban to the list */ void newban(chan,s,who) struct chanset_t *chan; char *s; char *who; { banlist *b; context; b=chan->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(chan,nick) struct chanset_t *chan; char *nick; { memberlist *x,*old; x=chan->channel.member; old=NULL; while ((x->nick[0]) && (strcasecmp(x->nick,nick)!=0)) { old=x; x=x->next; } if ((x->nick[0]==0) && (!(chan->channel.mode & CHANPEND))) { putlog(LOG_MISC,"*","(!) killmember(%s) -> nonexistent",nick); return 0; } if (old==NULL) chan->channel.member=x->next; else old->next=x->next; nfree(x); chan->channel.members--; return 1; } /* removes a ban from the list */ int killban(chan,s) struct chanset_t *chan; char *s; { banlist *b,*old; context; b=chan->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) chan->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(chan,nick) struct chanset_t *chan; char *nick; { memberlist *x; x=chan->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(chname,nick) char *nick,*chname; { struct chanset_t *chan; chan=findchan(chname); if (chan==NULL) return 0; return (ismember(chan,nick) != NULL); } /* am i a chanop? */ int me_op(chan) struct chanset_t *chan; { memberlist *mx=NULL; if (newbotname[0]) mx=ismember(chan,newbotname); if (mx==NULL) mx=ismember(chan,botname); if (mx==NULL) return 0; if (mx->flags&CHANOP) return 1; else return 0; } /* are there any ops on the channel? */ int any_ops(chan) struct chanset_t *chan; { memberlist *x=chan->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(chan,user) struct chanset_t *chan; char *user; { banlist *b; context; b=chan->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(chname,nick,host) char *chname,*nick,*host; { struct chanset_t *chan; memberlist *m; host[0]=0; chan=findchan(chname); if (chan==NULL) return; m=ismember(chan,nick); if (m==NULL) return; strcpy(host,m->userhost); } int is_split(chname,nick) char *chname,*nick; { memberlist *m; struct chanset_t *chan; chan=findchan(chname); if (chan==NULL) return 0; m=ismember(chan,nick); if (m==NULL) return 0; return (int)(m->split); } /* called only if dynamic-bans is on */ void check_expired_chanbans() { banlist *b; time_t now=time(NULL); struct chanset_t *chan; context; chan=chanset; while (chan!=NULL) { if ((chan->stat&CHAN_DYNAMICBANS) && (me_op(chan))) { b=chan->channel.ban; while (b->ban[0]) { if (now - b->timer > 60*ban_time) { putlog(LOG_MODES,chan->name,"(%s) Channel ban on %s expired.", chan->name,b->ban); add_mode(chan,'-','b',b->ban); } b=b->next; } } chan=chan->next; } context; } /* kick anyone off the channel who matches a ban */ void kick_match_ban(chan,ban) struct chanset_t *chan; char *ban; { memberlist *m; char s[UHOSTLEN]; context; if (!(chan->stat&CHAN_ENFORCEBANS)) return; m=chan->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",chan->name,m->nick); m=m->next; } } /* kick everyone on the channel with matching hostmask that joined since the time stamp */ void kick_match_since(chan,mask,timestamp) struct chanset_t *chan; char *mask; time_t timestamp; { char s[UHOSTLEN]; memberlist *m=chan->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",chan->name,m->nick); m=m->next; } } /* resets the bans on the channel */ void resetbans(chan) struct chanset_t *chan; { banlist *b=chan->channel.ban; context; if (!me_op(chan)) return; /* can't do it */ /* remove bans we didn't put there */ while (b->ban[0]) { if ((!equals_ban(b->ban)) && (!u_equals_ban(chan->bans,b->ban))) add_mode(chan,'-','b',b->ban); b=b->next; } context; /* make sure the intended bans are still there */ recheck_bans(chan); context; } /* remove any bogus bans */ void kill_bogus_bans(chan) struct chanset_t *chan; { banlist *b=chan->channel.ban; int bogus,i; context; if (!me_op(chan)) 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(chan,'-','b',b->ban); b=b->next; } } /* things to do when i just became a chanop: */ void recheck_channel(chan) struct chanset_t *chan; { memberlist *m; char s[UHOSTLEN],hand[10]; int atr; /* okay, sort through who needs to be deopped. */ context; m=chan->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 ((m->flags&CHANOP) && (strcasecmp(m->nick,botname)!=0) && ((atr & USER_DEOP) || ((chan->stat&CHAN_BITCH) && (!(atr & USER_OP))))) add_mode(chan,'-','o',m->nick); if ((!(m->flags&CHANOP)) && (atr & USER_OP) && (chan->stat&CHAN_OPONJOIN)) add_mode(chan,'+','o',m->nick); if ((chan->stat&CHAN_ENFORCEBANS) && (strcasecmp(m->nick,botname)!=0) && ((match_ban(s)) || (u_match_ban(chan->bans,s)))) refresh_ban_kick(chan,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", chan->name,m->nick); else tprintf(serv,"KICK %s %s :%s\n",chan->name,m->nick,s); } m=m->next; } recheck_bans(chan); context; recheck_chanmode(chan); context; } void newly_chanop(chan) struct chanset_t *chan; { recheck_channel(chan); /* find out the bans now */ tprintf(serv,"MODE %s +b\n",chan->name); } /* is user x a chanop? */ int member_op(chname,x) char *chname,*x; { memberlist *mx; struct chanset_t *chan; chan=findchan(chname); if (chan==NULL) return 0; mx=ismember(chan,x); if (mx==NULL) return 0; if (mx->flags&CHANOP) return 1; else return 0; } /* is user x a voice? (+v) */ int member_voice(chname,x) char *chname,*x; { memberlist *mx; struct chanset_t *chan; chan=findchan(chname); if (chan==NULL) return 0; mx=ismember(chan,x); if (mx==NULL) return 0; if (mx->flags&CHANVOICE) return 1; else return 0; } /* reset the channel information */ void reset_chan_info(chan) struct chanset_t *chan; { clear_channel(chan,1); chan->stat|=CHANPEND; chan->stat&=~CHANACTIVE; tprintf(serv,"WHO %s\n",chan->name); tprintf(serv,"MODE %s\n",chan->name); if (me_op(chan)) newly_chanop(chan); } /* 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(chan) struct chanset_t *chan; { memberlist *m; char s[UHOSTLEN]; int i=0; static int whined=0; context; m=chan->channel.member; if (chan->stat&CHANPEND) return; if ((chan->stat&CHANACTIVE) && (!me_op(chan))) { /* 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",chan->name); tprintf(serv,"JOIN %s %s\n",chan->name,chan->key_prot); putlog(LOG_MISC,"*","Trying to cycle %s to regain ops.",chan->name); whined=0; } else if (any_ops(chan)) { whined=0; if (chan->need_op[0]) do_tcl("need-op",chan->need_op); } else { /* other people here, but none are ops */ /* are there other bots? make them LEAVE. */ int ok=1; if (!whined) { putlog(LOG_MISC,"*","%s is active but has no ops :(",chan->name); whined=1; } m=chan->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=chan->channel.member; while (m->nick[0]) { mprintf(serv,"PRIVMSG %s :go\n",m->nick); m=m->next; } } } } } void check_lonely_channels() { struct chanset_t *chan; chan=chanset; while (chan!=NULL) { check_lonely_channel(chan); chan=chan->next; } } /* check for expired netsplit people */ void check_expired_splits() { memberlist *m; time_t now; char s[UHOSTLEN],hand[10]; struct chanset_t *chan; context; now=time(NULL); chan=chanset; while (chan!=NULL) { m=chan->channel.member; while (m->nick[0]) { if (m->split) { if (!(chan->stat&CHANACTIVE)) killmember(chan,m->nick); else if (now-(m->split) > WAIT_SPLIT) { sprintf(s,"%s!%s",m->nick,m->userhost); get_handle_by_host(hand,s); check_tcl_sign(m->nick,m->userhost,hand,chan->name, "lost in the netsplit"); putlog(LOG_JOIN,chan->name,"%s (%s) got lost in the net-split.", m->nick,m->userhost); killmember(chan,m->nick); } } m=m->next; } chan=chan->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; int ok=0; struct chanset_t *chan; context; chan=chanset; while (chan!=NULL) { if ((chan->stat&CHANACTIVE) && (chan->channel.members==1)) ok=1; chan=chan->next; } if (!ok) return; count++; if (count>=5) { mprintf(serv,"LUSERS\n"); count=0; } } /* called once a minute to kick idlers */ void check_idle_kick() { memberlist *m; time_t now; char s[UHOSTLEN]; int atr; struct chanset_t *chan; context; now=time(NULL); chan=chanset; while (chan!=NULL) { if ((chan->stat&CHANACTIVE) && (chan->idle_kick)) { m=chan->channel.member; while (m->nick[0]) { if ((now-(m->last) >= chan->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",chan->name,m->nick, chan->idle_kick); } m=m->next; } } chan=chan->next; } } void update_idle(chname,nick) char *chname,*nick; { memberlist *m; struct chanset_t *chan; chan=findchan(chname); if (chan==NULL) return; m=ismember(chan,nick); if (m==NULL) return; m->last=time(NULL); }