/* users.c -- handles: testing and enforcing bans and ignores adding and removing bans and ignores listing bans and ignores auto-linking bots sending and receiving a userfile from a bot listing users ('.whois' and '.match') reading the user file dprintf'ized, 9nov95 */ /* This file is part of the eggdrop source code copyright (c) 1997 Robey Pointer and is distributed according to the GNU general public license. For full details, read the top of 'main.c' or the file called COPYING that was distributed with this code. */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include "eggdrop.h" #include "users.h" #include "chan.h" #include "proto.h" /* bans: ::[+:]:: expire-time: timestamp when the ban was made, or 0 for permanent (if it starts with '+': when the ban will expire) (if it ends with '*', it's "sticky" -- not dynamic) time-added: when the ban was first created last-active: last time ban was enforced user: who placed the ban ignores: :+:[::] time-added: when the ignore was created user: who placed the ignore */ extern char botname[]; extern char botuser[]; extern char botuserhost[]; extern int serv; extern struct dcc_t dcc[]; extern int dcc_total; extern int noshare; extern struct userrec *userlist,*lastuser,*banu,*ignu; extern char origbotname[]; extern char botnetnick[]; extern struct chanset_t *chanset; /* where the user records are stored */ char userfile[121]=""; /* how many minutes will bans last? */ int ban_time=60; /* how many minutes will ignores last? */ int ignore_time=10; /* Total number of global bans */ int gban_total=0; /* is this nick!user@host being ignored? */ int match_ignore(char *uhost) { struct userrec *u; struct eggqueue *q; char host[UHOSTLEN],s[161]; u=get_user_by_handle(userlist,IGNORE_NAME); if (u==NULL) return 0; q=u->host; while (q!=NULL) { strcpy(s,q->item); splitc(host,s,':'); if (wild_match(host,uhost)) return 1; q=q->next; } return 0; } /* is this ban sticky? */ int u_sticky_ban(struct userrec *u,char *uhost) { struct eggqueue *q; char host[UHOSTLEN],s[256]; q=u->host; while (q!=NULL) { strcpy(s,q->item); splitc(host,s,':'); if (strcasecmp(host,uhost)==0) { splitc(host,s,':'); if (strchr(host,'*')==NULL) return 0; else return 1; } q=q->next; } return 0; } /* set sticky attribute for a ban */ int u_setsticky_ban(struct userrec *u,char *uhost,int sticky) { struct eggqueue *q; char host[UHOSTLEN],s[256],s1[256],*p; int j,k; j=k=atoi(uhost); if (!j) j=(-1); q=u->host; while (q!=NULL) { strcpy(s,q->item); splitc(host,s,':'); if ((j>=0) && (strcmp(q->item,"none")!=0)) j--; if ((j==0) || (strcasecmp(host,uhost)==0)) { strcpy(uhost,host); splitc(host,s,':'); p=strchr(host,'*'); if ((p==NULL) && (sticky)) strcat(host,"*"); if ((p!=NULL) && (!sticky)) strcpy(p,p+1); sprintf(s1,"%s:%s:%s",uhost,host,s); chg_q(q,s1); return 1; } q=q->next; } if (j>=0) return j-k; else return 0; } /* returns 1 if temporary ban, 2 if permban, 0 if not a ban at all */ int u_equals_ban(struct userrec *u,char *uhost) { struct eggqueue *q; char host[UHOSTLEN],s[256],*p; q=u->host; while (q!=NULL) { strcpy(s,q->item); splitc(host,s,':'); if (strcasecmp(host,uhost)==0) { p=s; if (*p=='+') p++; if (atoi(p)==0) return 2; else return 1; } q=q->next; } return 0; /* not equal */ } int sticky_ban(char *uhost) { struct userrec *u; u=get_user_by_handle(userlist,BAN_NAME); if (u==NULL) return 0; return u_sticky_ban(u,uhost); } int setsticky_ban(char *uhost,int par) { struct userrec *u; u=get_user_by_handle(userlist,BAN_NAME); if (u==NULL) return 0; return u_setsticky_ban(u,uhost,par); } int equals_ban(char *uhost) { struct userrec *u; u=get_user_by_handle(userlist,BAN_NAME); if (u==NULL) return 0; return u_equals_ban(u,uhost); } int equals_ignore(uhost) char *uhost; { struct userrec *u; struct eggqueue *q; char host[UHOSTLEN],s[256]; u=get_user_by_handle(userlist,IGNORE_NAME); if (u==NULL) return 0; q=u->host; while (q!=NULL) { strcpy(s,q->item); splitc(host,s,':'); if (strcasecmp(host,uhost)==0) { if (s[0]=='0') return 1; else return 2; } q=q->next; } return 0; /* not equal */ } int u_match_ban(struct userrec *u,char *uhost) { struct eggqueue *q; char host[UHOSTLEN],s[256]; q=u->host; while (q!=NULL) { strcpy(s,q->item); splitc(host,s,':'); if (wild_match(host,uhost)) return 1; q=q->next; } return 0; } int match_ban(char *uhost) { struct userrec *u; u=get_user_by_handle(userlist,BAN_NAME); if (u==NULL) return 0; return u_match_ban(u,uhost); } /* if any bans match this wildcard expression, refresh them on the channel */ void refresh_ban_kick(struct chanset_t *chan,char *user,char *nick) { struct userrec *u; struct eggqueue *q; char host[UHOSTLEN],s[256],ts[21],s1[161],*p,new_expire=0; time_t expire_time,time_added=(time_t)0L,last_active=(time_t)0L; int cycle=0,sticky=0; u=get_user_by_handle(userlist,BAN_NAME); while (u!=NULL) { q=u->host; while ((q!=NULL) && (strcmp(q->item,"none")!=0)) { strcpy(s,q->item); splitc(host,s,':'); if (wild_match(host,user)) { /* if this ban was placed in the last 60 seconds, it may not have */ /* propagated yet -- or it could be a desync, which can't be solved */ /* from here. :( */ if (q->stamp < time(NULL)-60) { if (member_op(chan->name,nick)) add_mode(chan,'-','o',nick); /* guess it can't hurt */ add_mode(chan,'+','b',host); flush_mode(chan,QUICK); /* do it IMMEDIATELY */ splitc(ts,s,':'); if (ts[0]=='+') { strcpy(ts,&ts[1]); new_expire=1; if (strchr(ts,'*')!=NULL) sticky=1; } expire_time=(time_t)atol(ts); if (s[0]=='+') { /* strip off new timestamps */ strcpy(s,&s[1]); splitc(ts,s,':'); time_added=(time_t)atol(ts); splitc(ts,s,':'); last_active=(time_t)atol(ts); /* (update last-active timestamp) */ sprintf(s1,"%s:%s%lu%s:+%lu:%lu:%s",host,new_expire?"+":"", expire_time,sticky?"*":"",time_added,time(NULL),s); chg_q(q,s1); } /* split off nick */ splitc(s1,s,':'); if (s[0]) { /* ban reason stored */ p=strchr(s,'~'); while (p!=NULL) { *p=' '; p=strchr(s,'~'); } p=strchr(s,'`'); while (p!=NULL) { *p=','; p=strchr(s,'`'); } mprintf(serv,"KICK %s %s :banned: %s\n",chan->name,nick,s); } else mprintf(serv,"KICK %s %s :You are banned.\n",chan->name,nick); } } q=q->next; } cycle++; if (cycle==1) u=chan->bans; else u=NULL; } } int u_delban(struct userrec *u,char *who) { int i,j; struct eggqueue *q; char s[256],host[UHOSTLEN]; i=0; if (atoi(who)) { j=atoi(who); q=u->host; while ((j>0) && (q!=NULL)) { if (strcmp(q->item,"none")!=0) j--; if (j>0) q=q->next; } if (q!=NULL) { strcpy(s,q->item); splitc(who,s,':'); if (!who[0]) strcpy(who,s); u->host=del_q(q->item,u->host,&i); } else return j-atoi(who); } else { /* find matching host, if there is one */ q=u->host; while ((q!=NULL) && (!i)) { strcpy(s,q->item); splitc(host,s,':'); if (!host[0]) strcpy(host,s); if (strcasecmp(who,host)==0) u->host=del_q(q->item,u->host,&i); q=q->next; } } if (i) { if (!noshare) { /* distribute chan bans differently */ if (strcasecmp(u->handle,BAN_NAME)==0) shareout("-ban %s\n",who); else shareout("-banchan %s %s\n",u->info,who); } } return i; } int delban(char *who) { struct userrec *u; int i; u=get_user_by_handle(userlist,BAN_NAME); if (u==NULL) return 0; i=u_delban(u,who); if (i>0) gban_total--; if (u->host==NULL) deluser(BAN_NAME); return i; } int delignore(char *ign) { struct userrec *u; int i,j; struct eggqueue *q; char s[161],host[UHOSTLEN]; context; u=get_user_by_handle(userlist,IGNORE_NAME); i=0; if (u==NULL) return 0; if (atoi(ign)) { j=atoi(ign)-1; q=u->host; while (j>0) { if (q!=NULL) q=q->next; j--; } if (q!=NULL) { strcpy(s,q->item); splitc(ign,s,':'); u->host=del_q(q->item,u->host,&i); } } else { /* find the matching host, if there is one */ q=u->host; while ((q!=NULL) && (!i)) { strcpy(s,q->item); splitc(host,s,':'); context; if (strcasecmp(ign,host)==0) u->host=del_q(q->item,u->host,&i); q=q->next; } } if (i) { if (u->host==NULL) deluser(IGNORE_NAME); if (!noshare) shareout("-ignore %s\n",ign); } return i; } /* new method of creating bans */ /* if first char of note is '*' it's a sticky ban */ void u_addban(struct userrec *u,char *ban,char *from,char *note, time_t expire_time) { char s[UHOSTLEN],host[UHOSTLEN],*p,oldnote[256]; time_t t,now=time(NULL); int sticky=0; strcpy(host,ban); /* choke check: fix broken bans (must have '!' and '@') */ if ((strchr(host,'!')==NULL) && (strchr(host,'@')==NULL)) strcat(host,"!*@*"); else if (strchr(host,'@')==NULL) strcat(host,"@*"); else if (strchr(host,'!')==NULL) { p=strchr(host,'@'); strcpy(s,p); *p=0; strcat(host,"!*"); strcat(host,s); } sprintf(s,"%s!%s",botname,botuserhost); if (wild_match(host,s)) { putlog(LOG_MISC,"*","Wanted to ban myself: deflected."); return; } if (u_equals_ban(u,host)) u_delban(u,host); /* remove old ban */ /* it shouldn't expire and be sticky also */ if (expire_time!=0L && note[0]=='*') strcpy(note,¬e[1]); /* new format: */ sprintf(s,"%s:+%lu%s:+%lu:%lu:%s:",host,expire_time,note[0]=='*'?"*":"", now,now,from); if (note[0]=='*') { strcpy(note,¬e[1]); sticky=1; } if (note[0]) { strcpy(oldnote,note); /* remove spaces & commas */ p=strchr(note,' '); while (p!=NULL) { *p='~'; p=strchr(note,' '); } p=strchr(note,','); while (p!=NULL) { *p='`'; p=strchr(note,','); } strcat(s,note); } else oldnote[0]=0; t=0L; if (expire_time!=0L) { t=(expire_time-now); if (t==0) t=1; } u->host=add_q(s,u->host); if (!noshare) { if (sticky) { strcpy(¬e[1],oldnote); note[0]='*'; } else strcpy(note,oldnote); if (strcasecmp(u->handle,BAN_NAME)==0) shareout("+ban %s +%lu %s %s\n",host,t,from,note); else shareout("+banchan %s +%lu %s %s %s\n",host,t,u->info,from,note); } strcpy(note,oldnote); } void addban(char *ban,char *from,char *note,time_t expire_time) { struct userrec *u; u=get_user_by_handle(userlist,BAN_NAME); if (u==NULL) { userlist=adduser(userlist,BAN_NAME,"none","-",0); u=get_user_by_handle(userlist,BAN_NAME); } gban_total++; u_addban(u,ban,from,note,expire_time); } void addignore(char *ign,char *from,char *mnote,time_t expire_time) { struct userrec *u; char s[UHOSTLEN],oldnote[256],*p,note[81]; time_t t,now; now=time(NULL); strcpy(note,mnote); if (equals_ignore(ign)) delignore(ign); /* remove old ban */ u=get_user_by_handle(userlist,IGNORE_NAME); sprintf(s,"%s:+%lu:%s:%lu:",ign,expire_time,from,now); if (note[0]) { strcpy(oldnote,note); /* remove spaces & commas */ p=strchr(note,' '); while (p!=NULL) { *p='~'; p=strchr(note,' '); } p=strchr(note,','); while (p!=NULL) { *p='`'; p=strchr(note,','); } strcat(s,note); } else oldnote[0]=0; t=0L; if (expire_time!=0L) { t=(expire_time-now); if (t==0) t=1; } if (u==NULL) userlist=adduser(userlist,IGNORE_NAME,s,"-",0); else u->host=add_q(s,u->host); if (!noshare) shareout("+ignore %s +%lu %s %s\n",ign,t,from,oldnote); strcpy(note,oldnote); } /* grabs and translates the note from a ban (in host form) */ void getbannote(char *host,char *from,char *note) { char *p; /* scratch off ban and timestamps */ splitc(NULL,host,':'); splitc(NULL,host,':'); if (host[0]=='+') { splitc(NULL,host,':'); splitc(NULL,host,':'); } if (host[0]) { splitc(from,host,':'); /* fix spaces & commas */ p=strchr(host,'~'); while (p!=NULL) { *p=' '; p=strchr(host,'~'); } p=strchr(host,'`'); while (p!=NULL) { *p=','; p=strchr(host,'`'); } } else from[0]=0; strcpy(note,host); } /* grabs and translates the note from an ignore (in host form) */ void getignorenote(host,from,note) char *host,*from,*note; { char *p; /* scratch off ignore and timestamp */ splitc(NULL,host,':'); splitc(NULL,host,':'); splitc(from,host,':'); if (!from[0]) { strcpy(from,host); host[0]=0;} /* old */ if (host[0]) { splitc(NULL,host,':'); /* another timestamp */ /* fix spaces & commas */ p=strchr(host,'~'); while (p!=NULL) { *p=' '; p=strchr(host,'~'); } p=strchr(host,'`'); while (p!=NULL) { *p=','; p=strchr(host,'`'); } } strcpy(note,host); } /* take host entry from ban list and display it ban-style */ void display_ban(idx,number,host,chan,show_inact) int idx,number,show_inact; char *host; struct chanset_t *chan; { char ban[UHOSTLEN],ts[21],note[121],dates[81],from[81],s[41],*p; time_t expire_time,time_added,last_active,now; int sticky=0; now=time(NULL); /* split off ban and expire-time */ splitc(ban,host,':'); splitc(ts,host,':'); if (ts[0]=='+') { /* new format */ strcpy(ts,&ts[1]); expire_time=(time_t)atol(ts); } else { /* old format (ban originate time) */ expire_time=(time_t)atol(ts); if (expire_time!=0L) expire_time+=(60*ban_time); } if (strchr(ts,'*')!=NULL) sticky=1; if (host[0]=='+') { /* extended format */ strcpy(host,&host[1]); splitc(ts,host,':'); time_added=(time_t)atol(ts); splitc(ts,host,':'); last_active=(time_t)atol(ts); daysago(now,time_added,note); sprintf(dates,"Created %s",note); if (time_added < last_active) { strcat(dates,", last used "); daysago(now,last_active,note); strcat(dates,note); } } else { time_added=(time_t)0L; last_active=(time_t)0L; dates[0]=0; } splitc(from,host,':'); strcpy(note,host); if (expire_time==0) strcpy(s,"(perm)"); else { char s1[41]; days(expire_time,now,s1); sprintf(s,"(expires %s)",s1); } if (sticky) strcat(s," (sticky)"); if (note[0]) { /* fix spaces & commas */ p=strchr(note,'~'); while (p!=NULL) { *p=' '; p=strchr(note,'~'); } p=strchr(note,'`'); while (p!=NULL) { *p=','; p=strchr(note,'`'); } } if (note[0]==' ') strcpy(note,¬e[1]); if ((chan==NULL) || (isbanned(chan,ban))) { if (number>=0) { dprintf(idx," [%3d] %s %s\n",number,ban,s); dprintf(idx," %s: %s\n",from,note); if (dates[0]) dprintf(idx," %s\n",dates); } else { dprintf(idx,"BAN: %s %s\n",ban,s); dprintf(idx," %s: %s\n",from,note); if (dates[0]) dprintf(idx," %s\n",dates); } } else if (show_inact) { if (number>=0) { dprintf(idx,"! [%3d] %s %s\n",number,ban,s); dprintf(idx," %s: %s\n",from,note); if (dates[0]) dprintf(idx," %s\n",dates); } else { dprintf(idx,"BAN (inactive): %s %s\n",ban,s); dprintf(idx," %s: %s\n",from,note); if (dates[0]) dprintf(idx," %s\n",dates); } } } /* take host entry from ignore list and display it ignore-style */ void display_ignore(idx,number,host) int idx,number; char *host; { char ign[UHOSTLEN],ts[21],note[121],dates[81],from[81],s[41],*p; time_t expire_time,time_added,now; now=time(NULL); /* split off host and expire-time */ splitc(ign,host,':'); splitc(ts,host,':'); if (ts[0]=='+') { /* new format */ strcpy(ts,&ts[1]); expire_time=(time_t)atol(ts); } else { /* old format (originate time) */ expire_time=(time_t)atol(ts); if (expire_time!=0L) expire_time+=(60*ban_time); } splitc(from,host,':'); if (!from[0]) { strcpy(from,host); host[0]=0; } /* old */ if (host[0]) { /* extended format */ splitc(ts,host,':'); time_added=(time_t)atol(ts); daysago(now,time_added,note); sprintf(dates,"Started %s",note); } else { time_added=(time_t)0L; dates[0]=0; } strcpy(note,host); if (expire_time==0) strcpy(s,"(perm)"); else { char s1[41]; days(expire_time,now,s1); sprintf(s,"(expires %s)",s1); } if (note[0]) { /* fix spaces & commas */ p=strchr(note,'~'); while (p!=NULL) { *p=' '; p=strchr(note,'~'); } p=strchr(note,'`'); while (p!=NULL) { *p=','; p=strchr(note,'`'); } } if (number>=0) { dprintf(idx," [%3d] %s %s\n",number,ign,s); if (note[0]) dprintf(idx," %s: %s\n",from,note); else dprintf(idx," placed by %s\n",from); if (dates[0]) dprintf(idx," %s\n",dates); } else { dprintf(idx,"IGNORE: %s %s\n",ign,s); if (note[0]) dprintf(idx," %s: %s\n",from,note); else dprintf(idx," placed by %s\n",from); if (dates[0]) dprintf(idx," %s\n",dates); } } void tell_bans(int idx,int show_inact,char *match) { struct userrec *u; struct eggqueue *q; int k=1,cycle; char s[256],hst[UHOSTLEN],from[81],note[121],chname[512]; struct chanset_t *chan=NULL; /* was channel given? */ if (match[0]) { nsplit(chname,match); if ((chname[0]=='#') || (chname[0]=='+') || (chname[0]=='&')) { chan=findchan(chname); if (chan==NULL) { dprintf(idx,"No such channel defined.\n"); return; } } else strcpy(match,chname); } if (chan==NULL) chan=findchan(dcc[idx].u.chat->con_chan); if (chan==NULL) chan=chanset; /* pick arbitrary channel to view */ if (chan==NULL) return; /* i give up then. */ if (show_inact) dprintf(idx,"Global bans: (! = not active on %s)\n",chan->name); else dprintf(idx,"Global bans:\n"); u=get_user_by_handle(userlist,BAN_NAME); cycle=0; if (u==NULL) { u=chan->bans; cycle++; } /* skip to next cycle */ while (u!=NULL) { if (cycle==1) { if (show_inact) dprintf(idx,"Channel bans for %s: (! = not active, * = not placed by bot)\n", chan->name); else dprintf(idx,"Channel bans for %s: (* = not placed by bot)\n", chan->name); } q=u->host; while ((q!=NULL) && (strcasecmp(q->item,"none")!=0)) { strcpy(s,q->item); getbannote(s,from,note); strcpy(s,q->item); splitc(hst,s,':'); strcpy(s,q->item); if (match[0]) { if ((wild_match(match,hst)) || (wild_match(match,note)) || (wild_match(match,from))) display_ban(idx,k,s,chan,1); k++; } else display_ban(idx,k++,s,chan,show_inact); q=q->next; } if (cycle==0) { u=chan->bans; cycle++; } else u=NULL; } tell_chanbans(chan,idx,k,match); if ((!show_inact) && (!match[0])) dprintf(idx,"Use '.bans all' to see the total list.\n"); } /* list the ignores and how long they've been active */ void tell_ignores(int idx,char *match) { struct userrec *u; struct eggqueue *q; int k=1; char s[256],hst[UHOSTLEN],from[81],note[121]; u=get_user_by_handle(userlist,IGNORE_NAME); if (u==NULL) { dprintf(idx,"No ignores.\n"); return; } q=u->host; if (q==NULL) dprintf(idx,"No ignores.\n"); dprintf(idx,"Currently ignoring:\n"); while ((q!=NULL) && (strcasecmp(q->item,"none")!=0)) { strcpy(s,q->item); getignorenote(s,from,note); strcpy(s,q->item); splitc(hst,s,':'); strcpy(s,q->item); if (match[0]) { if ((wild_match(match,hst)) || (wild_match(match,note)) || (wild_match(match,from))) display_ignore(idx,k,s); k++; } else display_ignore(idx,k++,s); q=q->next; } } /* check for expired timed-ignores */ void check_expired_ignores() { struct userrec *u; struct eggqueue *q; char s[UHOSTLEN],host[UHOSTLEN]; time_t now,expire_time; u=get_user_by_handle(userlist,IGNORE_NAME); now=time(NULL); if (u==NULL) return; q=u->host; if (q==NULL) return; while (q!=NULL) { strcpy(s,q->item); splitc(host,s,':'); if (s[0]=='+') { /* new-style */ strcpy(s,&s[1]); expire_time=(time_t)atol(s); } else { expire_time=(time_t)atol(s); if (expire_time!=0L) expire_time+=(60*ignore_time); } if ((expire_time!=0L) && (now>=expire_time)) { /* expired */ putlog(LOG_MISC,"*","No longer ignoring %s (expired)",host); delignore(host); u=get_user_by_handle(userlist,IGNORE_NAME); #ifdef SILENCE char *p; /* possibly an ircu silence was added for this user */ p=strchr(host,'!'); if (p==NULL) p=host; else p++; mprintf(serv,"SILENCE -%s\n",p); #endif if (u!=NULL) q=u->host; /* start over, check for more */ } if ((u!=NULL) && (q!=NULL)) q=q->next; else q=NULL; } } /* check for expired timed-bans */ void check_expired_bans() { struct userrec *u; struct eggqueue *q; struct chanset_t *chan; char s[256],host[UHOSTLEN],note[81]; time_t now,ti; int expired; now=time(NULL); u=get_user_by_handle(userlist,BAN_NAME); if (u==NULL) q=NULL; else q=u->host; while ((q!=NULL) && (strcmp(q->item,"none")!=0)) { strcpy(s,q->item); splitc(host,s,':'); splitc(note,s,':'); if (note[0]=='+') { /* new style */ strcpy(note,¬e[1]); ti=(time_t)atol(note); expired=((ti!=0L) && (ti<=now)); /* new style */ } else { ti=(time_t)atol(note); expired=((ti!=0L) && (now-ti>=60*ban_time)); } if (expired) { putlog(LOG_MISC,"*","No longer banning %s (expired)",host); chan=chanset; while (chan!=NULL) { add_mode(chan,'-','b',host); chan=chan->next; } delban(host); u=get_user_by_handle(userlist,BAN_NAME); if (u!=NULL) q=u->host; /* start over, check for more */ else q=NULL; } else q=q->next; } /* check for specific channel-domain bans expiring */ chan=chanset; while (chan!=NULL) { q=chan->bans->host; while ((q!=NULL) && (strcmp(q->item,"none")!=0)) { strcpy(s,q->item); splitc(host,s,':'); splitc(note,s,':'); if (note[0]=='+') { /* new style */ strcpy(note,¬e[1]); ti=(time_t)atol(note); expired=((ti!=0L) && (ti<=now)); /* new style */ } else { ti=(time_t)atol(note); expired=((ti!=0L) && (now-ti>=60*ban_time)); } if (expired) { putlog(LOG_MISC,chan->name,"No longer banning %s on %s (expired)", host,chan->name); add_mode(chan,'-','b',host); u_delban(chan->bans,host); q=chan->bans->host; } else q=q->next; } chan=chan->next; } } /* erase old user list, switch to new one */ void finish_share(int idx) { struct userrec *u; int i,j=0; for (i=0; istatus&=~STAT_GETTING; /* copy the bots over */ u=dup_userlist(1); /* read the rest in */ if (!readuserfile(dcc[idx].u.xfer->filename,&u)) { putlog(LOG_MISC,"*","CAN'T READ NEW USERFILE"); return; } unlink(dcc[idx].u.xfer->filename); /* done with you! */ putlog(LOG_MISC,"*","Userlist transfer complete; switched over."); clear_userlist(userlist); userlist=u; reaffirm_owners(); /* make sure my owners are +n */ clear_chanlist(); lastuser=banu=ignu=NULL; } /* begin the user transfer process */ void start_sending_users(int idx) { struct userrec *u; char s[161]; int i; struct eggqueue *q; struct chanuserrec *ch; sprintf(s,".share.user%lu",time(NULL)); debug0("ufsend: copying userlist"); u=dup_userlist(0); /* only non-bots */ debug0("ufsend: writing temporary userfile"); write_tmp_userfile(s,u); clear_userlist(u); debug0("ufsend: starting file transfer socket"); i=raw_dcc_send(s,"*users","(users)",s); if (i>0) { /* abort */ unlink(s); tprintf(dcc[idx].sock,"error Can't send userfile to you (internal error)\n"); dcc[idx].u.bot->status&=~STAT_SHARE; return; } dcc[idx].u.bot->status|=STAT_SENDING; i=dcc_total-1; strcpy(dcc[i].host,dcc[idx].nick); /* store bot's nick */ tprintf(dcc[idx].sock,"ufsend %lu %d %lu\n",iptolong(getmyip()),dcc[i].port, dcc[i].u.xfer->length); debug0("ufsend: queueing bot info"); /* start up a tbuf to queue outgoing changes for this bot until the */ /* userlist is done transferring */ new_tbuf(dcc[idx].nick); /* immediately, queue bot hostmasks & addresses (jump-start) */ u=userlist; while (u!=NULL) { if ((u->flags & USER_BOT) && !(u->flags & USER_UNSHARED)) { /* send hostmasks */ q=u->host; while (q!=NULL) { if (strcmp(q->item,"none")!=0) { sprintf(s,"+bothost %s %s\n",u->handle,q->item); q_tbuf(dcc[idx].nick,s); } q=q->next; } /* send address */ sprintf(s,"chaddr %s %s\n",u->handle,u->info); q_tbuf(dcc[idx].nick,s); /* send user-flags */ sprintf(s,"chattr %s %d\n",u->handle,(u->flags&BOT_MASK)); q_tbuf(dcc[idx].nick,s); ch=u->chanrec; while (ch) { if (ch->flags) { sprintf(s,"chattr %s %d %s\n",u->handle,ch->flags,ch->channel); q_tbuf(dcc[idx].nick,s); } ch=ch->next; } } u=u->next; } /* wish could unlink the file here to avoid possibly leaving it lying */ /* around, but that messes up NFS clients. */ debug0("ufsend: waiting for connect"); } /* update a user's last signon, by host */ void update_laston(char *chan,char *host) { struct userrec *u; struct chanuserrec *ch; u=get_user_by_host(host); if (u==NULL) return; touch_laston(u,chan,time(NULL)); ch=get_chanrec(u,chan); if (ch==NULL) return; ch->laston=time(NULL); } /* return laston time */ void get_handle_laston(char *chan,char *nick,time_t *n) { struct userrec *u; struct chanuserrec *ch; u=get_user_by_handle(userlist,nick); if (u==NULL) *n=0L; else if (chan[0]=='*') { if (u->laston > 0) *n=u->laston; else { *n=0L; ch=u->chanrec; while (ch!=NULL) { if (ch->laston > *n) *n=ch->laston; ch=ch->next; } } } else { ch=get_chanrec(u,chan); if (ch==NULL) *n=0L; else *n=ch->laston; } } void get_handle_chanlaston(char *nick,char *chan) { time_t n; struct userrec *u;struct chanuserrec *ch; char *cht; u=get_user_by_handle(userlist,nick); if (u==NULL) chan[0]=0; else if (u->laston>0) strcpy(chan,u->lastonchan); else { n=0L; cht=0; ch=u->chanrec; while (ch!=NULL) { if (ch->laston > n) { n=ch->laston; cht=ch->channel; } ch=ch->next; } if (cht) strcpy(chan,cht); else chan[0]=0; } } void set_handle_laston(char *chan,char *nick,time_t n) { struct userrec *u; struct chanuserrec *ch; u=get_user_by_handle(userlist,nick); if (u==NULL) return; touch_laston(u,chan,n); ch=get_chanrec(u,chan); if (ch==NULL) return; ch->laston=n; } /* since i was getting a ban list, i assume i'm chop */ /* recheck_bans makes sure that all who are 'banned' on the userlist are actually in fact banned on the channel */ void recheck_bans(struct chanset_t *chan) { struct userrec *u; struct eggqueue *q; char s[256],host[UHOSTLEN]; int i; if (chan->stat&CHAN_DYNAMICBANS) return; for (i=0; i<2; i++) { if (i==0) u=get_user_by_handle(userlist,BAN_NAME); else u=chan->bans; if (u!=NULL) { q=u->host; while ((q!=NULL) && (strcmp(q->item,"none")!=0)) { strcpy(s,q->item); splitc(host,s,':'); if (!host[0]) strcpy(host,s); if (!isbanned(chan,host)) add_mode(chan,'+','b',host); q=q->next; } } } } /* find info line for a user and display it if there is one */ void showinfo(struct chanset_t *chan,char *who,char *nick) { char s[121],s1[121]; if (get_attr_handle(who) & USER_BOT) return; get_handle_info(who,s); get_handle_chaninfo(who,chan->name,s1); /* locked info line overides non-locked channel specific info line */ if (s1[0] && (s[0]!='@' || s1[0]=='@')) strcpy(s,s1); if (s[0]=='@') strcpy(s,&s[1]); if (s[0]) mprintf(serv,"PRIVMSG %s :[%s] %s\n",chan->name,nick,s); } void tell_file_stats(int idx,char *hand) { struct userrec *u; float fr=(-1.0),kr=(-1.0); u=get_user_by_handle(userlist,hand); if (u==NULL) return; dprintf(idx," uploads: %4u / %6luk\n",u->uploads,u->upload_k); dprintf(idx,"downloads: %4u / %6luk\n",u->dnloads,u->dnload_k); if (u->uploads) fr=((float)u->dnloads / (float)u->uploads); if (u->upload_k) kr=((float)u->dnload_k / (float)u->upload_k); if (fr<0.0) dprintf(idx,"(infinite file leech)\n"); else dprintf(idx,"leech ratio (files): %6.2f\n",fr); if (kr<0.0) dprintf(idx,"(infinite size leech)\n"); else dprintf(idx,"leech ratio (size) : %6.2f\n",kr); } void tell_user(idx,u,master) int idx; struct userrec *u; int master; { char s[81],s1[81]; time_t now; int n; time_t t; struct eggqueue *q; struct chanuserrec *ch; if (strcmp(u->handle,BAN_NAME)==0) return; if (strcmp(u->handle,IGNORE_NAME)==0) return; flags2str(u->flags,s); n=num_notes(u->handle); get_handle_laston(u->handle,"*",&t); if (t==0L) strcpy(s1,"never"); else { now=time(NULL)-t;strcpy(s1,ctime(&t)); if (now>86400) { s1[7]=0; strcpy(&s1[11],&s1[4]); strcpy(s1,&s1[8]); } else { s1[16]=0; strcpy(s1,&s1[11]); } } dprintf(idx,"%-10s%-5s%5d %-25s %s\n",u->handle,u->pass[0]=='-'?"no":"yes", n,s,s1); /* channel flags? */ ch=u->chanrec; while (ch!=NULL) { if (ch->laston==0L) strcpy(s1,"never"); else { now=time(NULL)-(ch->laston); strcpy(s1,ctime(&(ch->laston))); if (now>86400) { s1[7]=0; strcpy(&s1[11],&s1[4]); strcpy(s1,&s1[8]); } else { s1[16]=0; strcpy(s1,&s1[11]); } } chflags2str(ch->flags,s); dprintf(idx," %-18s %-25s %s\n",ch->channel,s,s1); if (ch->info!=NULL) dprintf(idx," INFO: %s\n",ch->info); ch=ch->next; } s[0]=0; q=u->host; strcpy(s," HOSTS: "); while (q!=NULL) { if (strcmp(s," HOSTS: ")==0) strcat(s,q->item); else if (!s[0]) sprintf(s," %s",q->item); else { if (strlen(s)+strlen(q->item)+2>65) { dprintf(idx,"%s\n",s); sprintf(s," %s",q->item); } else { strcat(s,", "); strcat(s,q->item); } } q=q->next; } if (s[0]) dprintf(idx,"%s\n",s); if ((u->uploads) || (u->dnloads)) dprintf(idx," FILES: %u download%s (%luk), %u upload%s (%luk)\n", u->dnloads,(u->dnloads==1)?"":"s",u->dnload_k, u->uploads,(u->uploads==1)?"":"s",u->upload_k); if ((master) && (u->comment!=NULL)) dprintf(idx," COMMENT: %s\n",u->comment); if (u->email!=NULL) dprintf(idx," EMAIL: %s\n",u->email); if (u->flags&USER_BOT) { if (u->info!=NULL) dprintf(idx," ADDRESS: %s\n",u->info); } else if (u->info!=NULL) dprintf(idx," INFO: %s\n",u->info); /* user-defined extra fields */ if (u->xtra!=NULL) tcl_tell_whois(idx,u->xtra); } /* show user by ident */ void tell_user_ident(int idx,char *id,int master) { struct userrec *u; u=get_user_by_handle(userlist,id); if (u==NULL) u=get_user_by_host(id); if (u==NULL) { dprintf(idx,"Can't find anyone matching that.\n"); return; } dprintf(idx,"HANDLE PASS NOTES FLAGS LAST\n"); tell_user(idx,u,master); } /* match string: wildcard to match nickname or hostmasks */ /* +attr to find all with attr */ void tell_users_match(int idx,char *mtch,int start,int limit,int master, char *chname) { struct userrec *u=userlist; int fnd=0,cnt,not=0,fl; struct eggqueue *q; char s[UHOSTLEN],*t; struct chanuserrec *ch; dprintf(idx,"*** Matching '%s':\n",mtch); cnt=0; dprintf(idx,"HANDLE PASS NOTES FLAGS LAST\n"); if (start>1) dprintf(idx,"(skipping first %d)\n",start-1); t=mtch; while ((t!=NULL) && ((*t=='+') || (*t=='-'))) { char c=0,*tt; tt=strpbrk(t+1,"+-"); if (tt!=NULL) { c=*tt; *tt=0; } if (*t=='+') { if (chname[0]) fnd|=str2chflags(t+1); else fnd|=str2flags(t+1); } else { if (chname[0]) not|=str2chflags(t+1); else not|=str2flags(t+1); } if (tt!=NULL) { *tt=c; } t=tt; } while (u!=NULL) { if ((mtch[0]=='+') || (mtch[0]=='-')) { if (chname[0]) { ch=get_chanrec(u,chname); if (ch==NULL) fl=0; else fl=ch->flags; } else fl=u->flags; if (((fl&fnd)==fnd) && !(fl¬) && ((fnd!=0) || (not!=0))) { cnt++; if ((cnt<=limit) && (cnt>=start)) tell_user(idx,u,master); if (cnt==limit+1) dprintf(idx,"(more than %d matches; list truncated)\n",limit); } } else if (wild_match(mtch,u->handle)) { cnt++; if ((cnt<=limit) && (cnt>=start)) tell_user(idx,u,master); if (cnt==limit+1) dprintf(idx,"(more than %d matches; list truncated)\n",limit); } else { fnd=0; q=u->host; while (q!=NULL) { if ((wild_match(mtch,q->item)) && (!fnd)) { cnt++; fnd=1; if ((cnt<=limit) && (cnt>=start)) { if (strcmp(u->handle,BAN_NAME)==0) { strcpy(s,q->item); fnd=0; display_ban(idx,-1,s,NULL,1); } else if (strcmp(u->handle,IGNORE_NAME)==0) { strcpy(s,q->item); fnd=0; display_ignore(idx,-1,s); } else tell_user(idx,u,master); } if (cnt==limit+1) dprintf(idx,"(more than %d matches; list truncated)\n",limit); } q=q->next; } } u=u->next; } dprintf(idx,"--- Found %d match%s.\n",cnt,cnt==1?"":"es"); } /* tagged lines in the user file: # (comment) ; (comment) - hostmask(s) + email * dcc directory = comment : info line . xtra (Tcl) ! channel-specific | global laston */ int readuserfile(char *file,struct userrec **ret) { char *p,s[181],lasthand[181],host[181],attr[181],pass[181],code[181]; FILE *f; unsigned int flags; struct userrec *bu; int convpw=0,convch=0; char s1[181],ignored[512]; int firstxtra=0; context; bu=(*ret); ignored[0]=0; if (bu==userlist) { clear_chanlist(); lastuser=banu=ignu=NULL; } lasthand[0]=0; f=fopen(file,"r"); if (f==NULL) return 0; noshare=1; context; /* read opening comment */ fgets(s,180,f); if (s[1]<'2') { convpw=1; putlog(LOG_MISC,"*","* Old userfile (unencrypted passwords)"); putlog(LOG_MISC,"*","* Encrypting as I load ..."); } if (s[1]<'3') { convch=1; putlog(LOG_MISC,"*","* Old userfile (single-channel records)"); putlog(LOG_MISC,"*","* Converting as I load ..."); } if (s[1]>'3') fatal("Don't understand userfile encoding!",1); gban_total=0; while (!feof(f)) { fgets(s,180,f); if (!feof(f)) { rmspace(s); if ((s[0]!='#') && (s[0]!=';') && (s[0])) { nsplit(code,s); rmspace(code); rmspace(s); if (strcasecmp(code,"-")==0) { if (lasthand[0]) { p=strchr(s,','); while (p!=NULL) { splitc(code,s,','); rmspace(code); rmspace(s); if (code[0]) addhost_by_handle2(bu,lasthand,code); p=strchr(s,','); } /* channel bans are never stacked with , */ if (s[0]) { if ((lasthand[0]=='#') || (lasthand[0]=='+')) restore_chanban(lasthand,s); else { addhost_by_handle2(bu,lasthand,s); if (strcmp(lasthand,"*ban")==0) gban_total++; } } } } else if (strcasecmp(code,"+")==0) { if (lasthand[0]) set_handle_email(bu,lasthand,s); } else if (strcasecmp(code,"*")==0) { if (lasthand[0]) set_handle_dccdir(bu,lasthand,s); } else if (strcasecmp(code,"=")==0) { if (lasthand[0]) set_handle_comment(bu,lasthand,s); } else if (strcasecmp(code,":")==0) { /* global (default) info line */ if (lasthand[0]) set_handle_info(bu,lasthand,s); } else if (strcasecmp(code,".")==0) { if (lasthand[0]) { if (!firstxtra++) set_handle_xtra(bu,lasthand,s); else add_handle_xtra(bu,lasthand,s); } } else if (strcasecmp(code,"!")==0) { /* ! #chan laston flags [info] */ char chname[181],st[181],fl[181]; int flags; time_t last; nsplit(chname,s); rmspace(chname); rmspace(s); nsplit(st,s); rmspace(chname); rmspace(s); nsplit(fl,s); rmspace(chname); rmspace(s); flags=str2chflags(fl); last=(time_t)atol(st); if (defined_channel(chname)) add_chanrec_by_handle(bu,lasthand,chname,flags,last); if (s[0]) set_handle_chaninfo(bu,lasthand,chname,s); } else if (strncmp(code,"::",2)==0) { /* channel-specific bans */ strcpy(lasthand,&code[2]); if (!defined_channel(lasthand)) { strcat(ignored,lasthand); strcat(ignored," "); lasthand[0]=0; } } else if (strncmp(code,"!!",2)==0) { /* global laston time & channel */ if (lasthand[0]) { char lt[181]; nsplit (lt,s); rmspace(lt); touch_laston_handle(bu,lasthand,s,atoi(lt)); } } else { if (convpw) { nsplit(host,s); rmspace(host); rmspace(s); /* unused */ } nsplit(pass,s); rmspace(pass); rmspace(s); nsplit(attr,s); rmspace(attr); rmspace(s); firstxtra=0; if ((!attr[0]) || (!pass[0]) || ((!host[0]) && convpw)) { putlog(LOG_MISC,"*","* Corrupt user record '%s'!",code); lasthand[0]=0; } else if (is_user2(bu,code)) { putlog(LOG_MISC,"*","* Duplicate user record '%s'!",code); lasthand[0]=0; } else { flags=str2flags(attr); strcpy(lasthand,code); if (convch) { /* okay, some fiddling for older userfiles: */ if (strchr(attr,'o')!=NULL) flags|=USER_GLOBAL; /* +g */ } if (convpw) { if (strcasecmp(host,"$placeholder$")==0) host[0]=0; if (strcasecmp(host,"none")==0) host[0]=0; } else host[0]=0; if (strlen(code)>9) code[9]=0; if (strlen(pass)>20) { putlog(LOG_MISC,"*","* Corrupted password for %s; reset.", code); strcpy(pass,"-"); } if (convpw) { if (strcmp(pass,"nopass")==0) strcpy(pass,"-"); else if (!(flags & USER_BOT)) encrypt_pass(pass,pass); } bu=adduser(bu,code,host,pass,flags); if (convch) { /* for old user files: */ if (strchr(attr,'f')!=NULL) change_chanflags(bu,code,"*",CHANUSER_FRIEND,0); if (strchr(attr,'k')!=NULL) change_chanflags(bu,code,"*",CHANUSER_KICK,0); if (strchr(attr,'d')!=NULL) change_chanflags(bu,code,"*",CHANUSER_DEOP,0); } /* if s starts with '/' it's got file info */ if (s[0]=='/') { unsigned int up,dn; unsigned long upk,dnk; strcpy(s,&s[1]); nsplit(s1,s); up=atoi(s1); nsplit(s1,s); upk=atoi(s1); nsplit(s1,s); dn=atoi(s1); nsplit(s1,s); dnk=atoi(s1); set_handle_uploads(userlist,code,up,upk); set_handle_dnloads(userlist,code,dn,dnk); } } } } } } noshare=0; context; fclose(f); (*ret)=bu; if (ignored[0]) { putlog(LOG_MISC,"*","Ignored bans for channel(s): %s",ignored); } return 1; } /* New methodology - cycle through list 3 times */ /* 1st time scan for +sh bots and link if none connected */ /* 2nd time scan for +h bots */ /* 3rd time scan for +a/+h bots */ void autolink_cycle(char *start) { struct userrec *u=userlist,*autc=NULL; static int cycle=0; int got_hub=0,got_alt=0,got_shared=0,linked,ready=0,i; context; /* don't start a new cycle if some links are still pending */ if (start==NULL) { for (i=0; itype==DCC_BOT) return; } } debug1("autolink: begin at: %s",start==NULL?"(null)":start); if (start==NULL) { ready=1; cycle=0; } /* new run through the user list */ while (u!=NULL) { if ((flags_eq(USER_BOT|BOT_HUB,u->flags)) || (flags_eq(USER_BOT|BOT_ALT,u->flags))) { linked=0; for (i=0; ihandle)==0) { if (dcc[i].type==DCC_BOT) linked=1; if (dcc[i].type==DCC_BOT_NEW) linked=1; if (dcc[i].type==DCC_FORK) if (dcc[i].u.fork->type==DCC_BOT) linked=1; } } if (flags_eq(BOT_HUB|BOT_SHARE,u->flags)) { if (linked) got_shared=1; else if ((cycle==0) && ready && !autc) autc=u; } else if (u->flags&BOT_HUB && cycle > 0) { if (linked) got_hub=1; else if ((cycle==1) && ready && !autc) autc=u; } else if (u->flags&BOT_ALT && cycle==2) { if (linked) got_alt=1; else if (!in_chain(u->handle) && ready && !autc) autc=u; } /* did we make it where we're supposed to start? yay! */ if (!ready) if (strcasecmp(u->handle,start)==0) { ready=1; autc=NULL; /* if starting point is a +h bot, must be in 2nd cycle */ if ((u->flags&BOT_HUB)&&(!(u->flags&BOT_SHARE))) { debug0("autolink: leap to cycle 2 (continuation)"); cycle=1; } /* if starting point is a +a bot, must be in 3rd cycle */ if (u->flags&BOT_ALT) { debug0("autolink: leap to cycle 2 (continuation)"); cycle=2; } } } if ((u->flags&USER_BOT) && (u->flags&BOT_REJECT)) if (in_chain(u->handle)) { /* get rid of nasty reject bot */ reject_bot(u->handle); } u=u->next; if ((u==NULL) && (autc==NULL)) { if ((cycle==0) && (!got_shared)) { debug0("autolink: cycle 2 (no +sh, looking for +h)"); cycle++; u=userlist; } else if ((cycle==1) && (!(got_shared||got_hub))) { cycle++; u=userlist; debug0("autolink: cycle 3 (no +h, looking for +a)"); } } } if ((got_shared) && (cycle==0)) { autc=NULL; debug0("autolink: have a sharehub, cycle 1 -- no auto-link"); } if ((got_shared || got_hub) && (cycle==1)) { autc=NULL; debug0("autolink: have a hub, cycle 2 -- no auto-link"); } if ((got_shared || got_hub || got_alt) && (cycle==2)) { autc=NULL; debug0("autolink: have hub/alt, cycle 3 -- no auto-link"); } if (autc!=NULL) { debug1("autolink: trying %s",autc->handle); botlink("",-1,autc->handle); /* try autoconnect */ } else debug0("autolink: done trying"); } /* returns 1 if Global Ban, 0 otherwise */ int is_global_ban(char *ban) { int i,j;struct userrec *u;struct eggqueue *q; char host[UHOSTLEN],s[256]; context; j=atoi(ban); if (j>0) { if (j<=gban_total) return 1; else return 0; } else { u=get_user_by_handle(userlist,BAN_NAME); if (u==NULL) return 0; q=u->host;i=1;while ((q!=NULL) && (i<=gban_total)) { strcpy(s,q->item); splitc(host,s,':'); if (strcasecmp(ban,host)==0) return 1; q=q->next;i++; } return 0; /* Ban not in banlist */ } }