/* 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 */ #if HAVE_CONFIG_H #include #endif #include #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) 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 cx_file[]; extern int cx_line; extern char origbotname[]; extern char SBUF[]; 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; /* is this nick!user@host being ignored? */ int match_ignore(uhost) 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; } /* returns 1 if temporary ban, 2 if permban, 0 if not a ban at all */ int u_equals_ban(u,uhost) 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) { if (s[0]=='0') return 1; else return 2; } q=q->next; } return 0; /* not equal */ } int equals_ban(uhost) 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(u,uhost) 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(uhost) 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(chan,user,nick) char *user,*nick; struct chanset_t *chan; { 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; static char lastban[161]=""; static time_t lastbantime=(unsigned long)0L; int cycle=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)) { /* check if this is repetitive */ if ((strcasecmp(host,lastban)==0) && (lastbantime >= time(NULL)-60)) { /* ban was made within last 60 seconds -- may not have propagated */ /* yet -- or, could be a desync, which can't be solved from here */ } else { strcpy(lastban,host); lastbantime=time(NULL); 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; } 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:+%lu:%lu:%s",host,new_expire?"+":"", expire_time,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(u,who) struct userrec *u; char *who; { int i,j; struct eggqueue *q; char s[256],host[UHOSTLEN]; context; 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(who) 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 (u->host==NULL) deluser(BAN_NAME); return i; } int delignore(ign) 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 */ void u_addban(u,ban,from,note,expire_time) struct userrec *u; char *ban,*from,*note; time_t expire_time; { char s[UHOSTLEN],host[UHOSTLEN],*p,oldnote[256]; time_t t,now=time(NULL); context; 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 */ /* new format: */ sprintf(s,"%s:+%lu:+%lu:%lu:%s:",host,expire_time,now,now,from); 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 (strcasecmp(u->handle,BAN_NAME)==0) shareout("+ban %s +%lu %s %s\n",host,t,from,oldnote); else shareout("+banchan %s +%lu %s %s %s\n",host,t,u->info,from,oldnote); } strcpy(note,oldnote); } void addban(ban,from,note,expire_time) char *ban,*from,*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); } u_addban(u,ban,from,note,expire_time); } void addignore(ign,from,mnote,expire_time) char *ign,*from,*mnote; time_t expire_time; { struct userrec *u; char s[UHOSTLEN],oldnote[256],*p,note[81]; time_t t,now; context; 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(host,from,note) char *host,*from,*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; 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 (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 (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(idx,show_inact,match) int idx,show_inact; char *match; { struct userrec *u; struct eggqueue *q; int k=1,cycle; char s[256],hst[UHOSTLEN],from[81],note[121]; struct chanset_t *chan; 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(idx,match) 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; context; 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); 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; context; 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; } } /* see if there are any bots that need to be linked */ /* priority is: +sh (highest), +h, +a (lowest) */ /* only link ONE +h bot at a time */ /* only link ONE +a bot at a time, and only if the +h are unreachable, and then keep trying for the +h bot(s) */ void autolink_cycle(start) char *start; { struct userrec *u=userlist,*autc=NULL; static int cycle=0; int got_hub=0,got_alt=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; } } /* check for possibility that share-bot IS linked in, but not to me. */ /* if +sh, drop that uplink, so i can link directly on the next round. */ /* (if +sa, don't bother.) */ if ((!linked) && (in_chain(u->handle)) && (u->flags&BOT_SHARE) && (u->flags&BOT_HUB) && (strcasecmp(origbotname,u->handle)!=0)) { int q=nextbot(u->handle),qatr=get_attr_handle(dcc[q].nick); debug2("autolink: want +sh bot %s, unlinking %s",u->handle, dcc[q].nick); putlog(LOG_BOTS,"*","Restructure to get sharebot %s",u->handle); if ((qatr & (BOT_HUB|BOT_SHARE)) == (BOT_HUB|BOT_SHARE)) { /* ok, i can't be linked to both as-is, so make the other one move */ tandout("chat %s Bringing sharebot %s to me...\n",origbotname, u->handle); chatout("*** Bringing sharebot %s to me...\n",u->handle); tprintf(dcc[q].sock,"unlink %s %s %s\n",origbotname, lastbot(u->handle),u->handle); } else { /* disconnect from botnet and reconnect there */ tprintf(dcc[q].sock,"bye\n"); tandout_but(q,"unlinked %s\n",dcc[q].nick); tandout_but(q,"chat %s Disconnected %s (moving to %s)\n", origbotname,dcc[q].nick,u->handle); chatout("*** Disconnected %s (moving to %s)\n",dcc[q].nick, u->handle); cancel_user_xfer(q); rembot(dcc[q].nick,dcc[q].nick); unvia(q,dcc[q].nick); killsock(dcc[q].sock); lostdcc(q); } /* once unlinking is done, i'll try to autoconnect later */ } /* is this a HUB we have linked? */ if ((u->flags&BOT_HUB) && (linked)) { got_hub=1; if ((autc!=NULL) && (autc->flags&BOT_HUB) && !(autc->flags&BOT_SHARE)) { /* great... already have a hub bot in line to be linked */ /* start over from there */ debug2("autolink: wanted hub %s, but %s is connected",autc->handle, u->handle); u=autc; linked=1; autc=NULL; } } /* is this an ALT we have linked? */ if ((u->flags&BOT_ALT) && (linked)) got_alt=1; /* don't link anything if we have our +h bot */ if ((got_hub) && !(u->flags&BOT_SHARE)) linked=1; /* if we've got an alt linked, don't link another one */ if ((got_alt) && (u->flags&BOT_ALT) && !(u->flags&BOT_SHARE)) linked=1; if ((!linked) && (u->flags&BOT_ALT) && (in_chain(u->handle))) linked=1; /* don't try hubs on 2nd cycle */ if ((u->flags&BOT_HUB) && (cycle==1)) linked=1; if ((!linked) && (ready)) { debug3("autolink: considering link to %s (+%s%c)",u->handle, (u->flags&BOT_SHARE)?"s":"", (u->flags&BOT_HUB)?'h':((u->flags&BOT_ALT)?'a':'?')); if (autc==NULL) autc=u; else if ((u->flags&BOT_HUB) && !(autc->flags&BOT_HUB) && (cycle==0)) autc=u; /* hub priority over alt (except 2nd cycle) */ else if ((u->flags&BOT_SHARE) && !(autc->flags&BOT_SHARE)) autc=u; /* share bots get priority */ if (autc==u) debug1("autolink: decided on %s",u->handle); } /* 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 +a bot, must be in 2nd cycle */ if (u->flags&BOT_ALT) { debug0("autolink: leap to cycle 2 (continuation)"); cycle=1; } } } 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) && (cycle==0) && (!got_hub)) { /* end of 1st run of userlist -- no succeses */ /* try again, this time go for +a */ cycle++; u=userlist; debug0("autolink: cycle 2 (no +h, looking for +a)"); } } if ((got_hub) && (cycle==0)) { autc=NULL; debug0("autolink: have a hub, cycle 1 -- no auto-link"); } if (((got_hub) || (got_alt)) && (cycle==1)) { autc=NULL; debug0("autolink: have hub/alt, cycle 2 -- no auto-link"); } if (autc!=NULL) { debug1("autolink: trying %s",autc->handle); botlink("",-1,autc->handle); /* try autoconnect */ } else debug0("autolink: done trying"); } /* erase old user list, switch to new one */ void finish_share(idx) 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; } reaffirm_owners(); /* make sure my owners are +n */ unlink(dcc[idx].u.xfer->filename); /* done with you! */ putlog(LOG_MISC,"*","Userlist transfer complete; switched over."); clear_userlist(userlist); userlist=u; clear_chanlist(); lastuser=banu=ignu=NULL; } /* begin the user transfer process */ void start_sending_users(idx) int idx; { struct userrec *u; char s[161]; int i; struct eggqueue *q; context; sprintf(s,".share.user%lu",time(NULL)); u=dup_userlist(0); /* only non-bots */ write_tmp_userfile(s,u); clear_userlist(u); 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",ntohl(getmyip()),dcc[i].port, dcc[i].u.xfer->length); /* 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) { /* 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); } u=u->next; } /* wish could unlink the file here to avoid possibly leaving it lying */ /* around, but that messes up NFS clients. */ } /* update a user's last signon, by host */ void update_laston(host) char *host; { struct userrec *u; u=get_user_by_host(host); if (u==NULL) return; u->laston=time(NULL); } /* return laston time */ void get_handle_laston(nick,n) char *nick; time_t *n; { struct userrec *u; u=get_user_by_handle(userlist,nick); if (u==NULL) *n=0L; else *n = u->laston; } void set_handle_laston(nick,n) char *nick; time_t n; { struct userrec *u; u=get_user_by_handle(userlist,nick); if (u==NULL) return; u->laston=n; } void set_handle_laston2(bu,nick,n) struct userrec *bu; char *nick; time_t n; { struct userrec *u; u=get_user_by_handle(bu,nick); if (u==NULL) return; u->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(chan) struct chanset_t *chan; { struct userrec *u; struct eggqueue *q; char s[256],host[UHOSTLEN]; int i; context; 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(chan,who,nick) char *who,*nick; struct chanset_t *chan; { char s[121]; if (get_attr_handle(who) & USER_BOT) return; get_handle_info(who,s); if (s[0]=='@') strcpy(s,&s[1]); if (s[0]) mprintf(serv,"PRIVMSG %s :[%s] %s\n",chan->name,nick,s); } void tell_user(idx,u,master) int idx; struct userrec *u; int master; { char s[81],s1[81]; struct eggqueue *q; time_t now; int n; 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); if (u->laston == 0L) strcpy(s1,"never"); else { now=time(NULL)-(u->laston); strcpy(s1,ctime(&(u->laston))); 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%-25s%5d %s\n",u->handle,u->pass[0]=='-'?"no":"yes", s,n,s1); s[0]=0; q=u->host; while (q!=NULL) { if (!s[0]) sprintf(s," %s",q->item); else { if (strlen(s)+strlen(q->item)+2>60) { 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 ((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); } } /* show user by ident */ void tell_user_ident(idx,id,master) 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 FLAGS NOTES 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(idx,mtch,start,limit,master) int idx; char *mtch; int start,limit,master; { struct userrec *u=userlist; int fnd=0,cnt; struct eggqueue *q; char s[UHOSTLEN]; dprintf(idx,"*** Matching '%s':\n",mtch); cnt=0; dprintf(idx,"HANDLE PASS FLAGS NOTES LAST\n"); if (start>1) dprintf(idx,"(skipping first %d)\n",start-1); while (u!=NULL) { if (mtch[0]=='+') { fnd=str2flags(&mtch[1]); if (((u->flags&fnd)==fnd) && (fnd!=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 */ int readuserfile(file,ret) 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; char ignored[512]; 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]>'2') fatal("Don't understand userfile encoding!"); 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); } } } 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) { if (lasthand[0]) { set_handle_info(bu,lasthand,s); } } else if (strcasecmp(code,".")==0) { if (lasthand[0]) { add_handle_xtra(bu,lasthand,s); } } else if (strcasecmp(code,"!")==0) { /* ! #chan laston flags */ /* not implemented yet */ putlog(LOG_MISC,"*","* Channel-specific user data for '%s'! %s", lasthand,"(not supported yet)"); } 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 (convpw) { nsplit(host,s); rmspace(host); rmspace(s); /* unused */ } nsplit(pass,s); rmspace(pass); rmspace(s); nsplit(attr,s); rmspace(attr); rmspace(s); if ((!s[0]) || (!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 (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); set_handle_laston2(bu,code,atol(s)); } } } } } noshare=0; context; fclose(f); (*ret)=bu; if (ignored[0]) { putlog(LOG_MISC,"*","Ignored bans for channel(s): %s",ignored); } return 1; }