X-Git-Url: https://jfr.im/git/irc/quakenet/newserv.git/blobdiff_plain/9b6ec52ca860565f0ae390675757f7226ce2e0c2..5ec20d224f8f4d79c9cb9e5b8546c4a638a35943:/chanserv/chanservuser.c?ds=sidebyside diff --git a/chanserv/chanservuser.c b/chanserv/chanservuser.c index 25f18b2e..c90c4ac3 100644 --- a/chanserv/chanservuser.c +++ b/chanserv/chanservuser.c @@ -31,7 +31,7 @@ CommandTree *csctcpcommands; void chanservuserhandler(nick *target, int message, void **params); void chanservreguser(void *arg) { - sstring *csnick,*csuser,*cshost,*csrealname; + sstring *csnick,*csuser,*cshost,*csrealname,*csaccount; chanindex *cip; regchan *rcp; int i; @@ -40,19 +40,21 @@ void chanservreguser(void *arg) { csuser=getcopyconfigitem("chanserv","user","TheQBot",USERLEN); cshost=getcopyconfigitem("chanserv","host","some.host",HOSTLEN); csrealname=getcopyconfigitem("chanserv","realname","ChannelService",REALLEN); + csaccount=getcopyconfigitem("chanserv","account",csnick&&csnick->content&&csnick->content[0]?csnick->content:"Q",ACCOUNTLEN); Error("chanserv",ERR_INFO,"Connecting %s...",csnick->content); chanservnick=registerlocaluser(csnick->content,csuser->content,cshost->content, - csrealname->content,NULL, - UMODE_INV|UMODE_SERVICE|UMODE_DEAF, + csrealname->content,csaccount->content, + UMODE_INV|UMODE_SERVICE|UMODE_DEAF|UMODE_OPER|UMODE_ACCOUNT, &chanservuserhandler); freesstring(csnick); freesstring(csuser); freesstring(cshost); freesstring(csrealname); - + freesstring(csaccount); + /* Now join channels */ for (i=0;inext) { @@ -241,14 +243,14 @@ void chanservjoinchan(channel *cp) { nick *np; reguser *rup; regchanuser *rcup; - flag_t themodes; + flag_t themodes = 0; /* Skip unregistered channels */ if (!(rcp=cp->index->exts[chanservext])) return; - /* Check for a new timestamp */ - if ((!rcp->ltimestamp) || (cp->timestamp < rcp->ltimestamp)) { + /* Check for a new timestamp. But don't do anything stupid if the incoming timestamp is 0 */ + if (cp->timestamp && ((!rcp->ltimestamp) || (cp->timestamp < rcp->ltimestamp))) { rcp->ltimestamp=cp->timestamp; csdb_updatechanneltimestamp(rcp); } @@ -273,8 +275,14 @@ void chanservjoinchan(channel *cp) { * up later. For modes we use the forced modes, plus the default channel * modes (unless any of those are explicitly denied) */ - /* By default, we set the forcemodes and the default modes, but never denymodes */ - themodes = (CHANMODE_DEFAULT | rcp->forcemodes) & ~rcp->denymodes; + /* By default, we set the forcemodes and the default modes, but never denymodes.. + * OK, actually we should only set the default modes if our timestamp is older.*/ + if (cp->timestamp > rcp->ltimestamp) + themodes |= CHANMODE_DEFAULT; + else + themodes=0; + + themodes = (themodes | rcp->forcemodes) & ~rcp->denymodes; /* Now, if someone has just created a channel and we are going to set +i * or +k on it, this will kick them off. This could be construed as a @@ -319,7 +327,7 @@ void chanservstdmessage(nick *np, int messageid, ... ) { reguser *rup; int language; va_list va, va2; - char *message; + char *message, *messageargs; char *bp2,*bp; int len; @@ -341,12 +349,14 @@ void chanservstdmessage(nick *np, int messageid, ... ) { } else if (csmessages[0][messageid]) { message=csmessages[0][messageid]->content; } else { - message=defaultmessages[messageid]; + message=defaultmessages[messageid*2]; } + messageargs=defaultmessages[messageid*2+1]; + va_start(va,messageid); va_copy(va2, va); - vsnprintf(buf,5000,message,va); + q9vsnprintf(buf,5000,message,messageargs,va); va_end(va); len=0; @@ -466,7 +476,7 @@ void chanservwallmessage(char *message, ... ) { void chanservkillstdmessage(nick *target, int messageid, ... ) { char buf[512]; int language; - char* message; + char *message, *messageargs; va_list va; reguser *rup; @@ -480,15 +490,19 @@ void chanservkillstdmessage(nick *target, int messageid, ... ) { else if (csmessages[0][messageid]) message=csmessages[0][messageid]->content; else - message=defaultmessages[messageid]; + message=defaultmessages[messageid*2]; + messageargs=defaultmessages[messageid*2+1]; va_start(va, messageid); - vsnprintf(buf, 511, message, va); + q9vsnprintf(buf, 511, message, messageargs, va); va_end(va); killuser(chanservnick, target, buf); } int checkpassword(reguser *rup, const char *pass) { + if (!(*rup->password)) + return 0; + if (!strncmp(rup->password, pass, PASSLEN)) return 1; return 0; @@ -497,6 +511,9 @@ int checkpassword(reguser *rup, const char *pass) { int checkresponse(reguser *rup, const unsigned char *entropy, const char *response, CRAlgorithm algorithm) { char usernamel[NICKLEN+1], *dp, *up; + if (!(*rup->password)) + return 0; + for(up=rup->username,dp=usernamel;*up;) *dp++ = ToLower(*up++); *dp = '\0'; @@ -535,10 +552,21 @@ void cs_checknick(nick *np) { if (IsAccount(np) && np->auth) { if (np->auth->exts[chanservaext]) { rup=getreguserfromnick(np); + /* safe? */ - if(rup && UHasSuspension(rup)) { - chanservkillstdmessage(np, QM_SUSPENDKILL); - return; + if(rup) { + if (UIsDelayedGline(rup)) { + /* delayed-gline - schedule the user's squelching */ + deleteschedule(NULL, &chanservdgline, (void*)rup); /* icky, but necessary unless we stick more stuff in reguser structure */ + scheduleoneshot(time(NULL)+rand()%900, &chanservdgline, (void*)rup); + } else if (UIsGline(rup)) { + /* instant-gline - lets be lazy and set a schedule expiring now :) */ + deleteschedule(NULL, &chanservdgline, (void*)rup); /* icky, but necessary unless we stick more stuff in reguser structure */ + scheduleoneshot(time(NULL), &chanservdgline, (void*)rup); + } else if(UHasSuspension(rup)) { + chanservkillstdmessage(np, QM_SUSPENDKILL); + return; + } } cs_doallautomodes(np); } else { @@ -550,15 +578,17 @@ void cs_checknick(nick *np) { lastuserID=rup->ID; strncpy(rup->username,np->authname,NICKLEN); rup->username[NICKLEN]='\0'; rup->created=time(NULL); - rup->lastauth=time(NULL); /* questionable */ + rup->lastauth=0; rup->lastemailchange=0; rup->flags=QUFLAG_NOTICE; rup->languageid=0; rup->suspendby=0; rup->suspendexp=0; + rup->suspendtime=0; rup->lockuntil=0; rup->password[0]='\0'; rup->email=NULL; + rup->lastemail=NULL; rup->localpart=NULL; rup->domain=NULL; rup->info=NULL; @@ -622,6 +652,9 @@ void cs_docheckchanmodes(channel *cp, modechanges *changes) { } } +/* Slightly misnamed function that checks each USER on the channel against channel + * settings. Possible actions are opping/deopping, voicing/devoicing, and banning + * for chanflag +k or chanlev flag +b */ void cs_docheckopvoice(channel *cp, modechanges *changes) { regchan *rcp; reguser *rup; @@ -640,7 +673,7 @@ void cs_docheckopvoice(channel *cp, modechanges *changes) { continue; if ((np=getnickbynumeric(cp->users->content[i]))==NULL) { - Error("chanserv",ERR_ERROR,"Found non-existent numeric %d on channel %s",cp->users->content[i], + Error("chanserv",ERR_ERROR,"Found non-existent numeric %lu on channel %s",cp->users->content[i], cp->index->name->content); continue; } @@ -650,23 +683,33 @@ void cs_docheckopvoice(channel *cp, modechanges *changes) { else rcup=NULL; - if (rcup && CUIsBanned(rcup) && !IsService(np)) { - cs_banuser(changes, cp->index, np, NULL); - continue; - } - - if (!IsService(np) && CIsKnownOnly(rcp) && !(rcup && CUKnown(rcup))) { - cs_banuser(changes, cp->index, np, "Authorised users only."); - continue; + /* Various things that might ban the user - don't apply these to +o, +k or +X users */ + if (!IsService(np) && !IsXOper(np) && !IsOper(np)) { + if (rcup && CUIsBanned(rcup)) { + cs_banuser(changes, cp->index, np, NULL); + continue; + } + + /* chanflag +k checks; kick them if they "obviously" can't rejoin without a ban */ + if (CIsKnownOnly(rcp) && !(rcup && CUKnown(rcup))) { + if (IsInviteOnly(cp) || (IsRegOnly(cp) && !IsAccount(np))) { + localkickuser(chanservnick,cp,np,"Authorised users only."); + } else { + cs_banuser(NULL, cp->index, np, "Authorised users only."); + } + continue; + } } - + if ((cp->users->content[i] & CUMODE_OP) && !IsService(np)) { if ((CIsBitch(rcp) && (!rcup || !CUHasOpPriv(rcup))) || (rcup && CUIsDeny(rcup))) localdosetmode_nick(changes, np, MC_DEOP); } else { - if (rcup && (CUIsProtect(rcup) || CIsProtect(rcp)) && CUIsOp(rcup) && !CUIsDeny(rcup)) + if (rcup && (CUIsProtect(rcup) || CIsProtect(rcp)) && CUIsOp(rcup) && !CUIsDeny(rcup)) { localdosetmode_nick(changes, np, MC_OP); + cs_logchanop(rcp, np->nick, rcup->user); + } } if (cp->users->content[i] & CUMODE_VOICE) { @@ -689,11 +732,26 @@ void cs_doallautomodes(nick *np) { return; for (rcup=rup->knownon;rcup;rcup=rcup->nextbyuser) { + /* Skip suspended channels */ + if (CIsSuspended(rcup->chan)) + continue; + if (rcup->chan->index->channel) { /* Channel exists */ if ((lp=getnumerichandlefromchanhash(rcup->chan->index->channel->users, np->numeric))) { /* User is on channel.. */ + if (CUKnown(rcup) && rcup->chan->index->channel->users->totalusers >= 3) { + /* This meets the channel use criteria, update. */ + rcup->chan->lastactive=time(NULL); + + /* Don't spam the DB though for channels with lots of joins */ + if (rcup->chan->lastcountersync < (time(NULL) - COUNTERSYNCINTERVAL)) { + csdb_updatechannelcounters(rcup->chan); + rcup->chan->lastcountersync=time(NULL); + } + } + /* Update last use time */ rcup->usetime=getnettime(); @@ -703,8 +761,10 @@ void cs_doallautomodes(nick *np) { localdosetmode_nick(&changes, np, MC_DEOP); } else { if (!CUIsDeny(rcup) && CUIsOp(rcup) && - (CUIsProtect(rcup) || CIsProtect(rcup->chan) || CUIsAutoOp(rcup))) + (CUIsProtect(rcup) || CIsProtect(rcup->chan) || CUIsAutoOp(rcup))) { localdosetmode_nick(&changes, np, MC_OP); + cs_logchanop(rcup->chan, np->nick, rup); + } } if (*lp & CUMODE_VOICE) { @@ -731,7 +791,7 @@ void cs_checknickbans(nick *np) { regchan *rcp; int i,j; - if (IsService(np)) + if (IsService(np) || IsOper(np) || IsXOper(np)) return; /* Avoid races: memcpy the channel array */ @@ -741,7 +801,7 @@ void cs_checknickbans(nick *np) { for (j=0;jindex->exts[chanservext]) && !CIsSuspended(rcp) && - CIsEnforce(rcp) && nickbanned(np, ca[j])) + CIsEnforce(rcp) && nickbanned_visible(np, ca[j])) localkickuser(chanservnick, ca[j], np, "Banned."); } @@ -772,18 +832,18 @@ void cs_checkbans(channel *cp) { continue; if ((np=getnickbynumeric(cp->users->content[i]))==NULL) { - Error("chanserv",ERR_ERROR,"Found numeric %d on channel %s who doesn't exist.", + Error("chanserv",ERR_ERROR,"Found numeric %lu on channel %s who doesn't exist.", cp->users->content[i], cp->index->name->content); continue; } - if (IsService(np)) + if (IsService(np) || IsOper(np) || IsXOper(np)) continue; for (rbp=rcp->bans;rbp;rbp=rbp->next) { if (((!rbp->expiry) || (rbp->expiry <= now)) && - nickmatchban(np, rbp->cbp)) { - if (!nickbanned(np, cp)) { + nickmatchban_visible(np, rbp->cbp)) { + if (!nickbanned_visible(np, cp)) { localdosetmode_ban(&changes, bantostring(rbp->cbp), MCB_ADD); } localkickuser(chanservnick,cp,np,rbp->reason?rbp->reason->content:"Banned."); @@ -796,7 +856,7 @@ void cs_checkbans(channel *cp) { if (CIsEnforce(rcp)) { for (cbp=cp->bans;cbp;cbp=cbp->next) { - if ((cbp->timeset>=rcp->lastbancheck) && nickmatchban(np, cbp)) + if ((cbp->timeset>=rcp->lastbancheck) && nickmatchban_visible(np, cbp)) localkickuser(chanservnick,cp,np,"Banned."); } rcp->lastbancheck=time(NULL); @@ -880,9 +940,14 @@ void cs_timerfunc(void *arg) { if (CIsAutoLimit(rcp)) { if (!rcp->limit || rcp->autoupdate <= now) { - /* Update limit.. */ - rcp->limit=cp->users->totalusers+rcp->autolimit; - rcp->status |= QCSTAT_MODECHECK; + /* Only update the limit if there are <= (autolimit/2) or + * >= (autolimit * 1.5) slots free */ + + if ((cp->users->totalusers >= (rcp->limit - rcp->autolimit/2)) || + (cp->users->totalusers <= (rcp->limit - (3 * rcp->autolimit)/2))) { + rcp->limit=cp->users->totalusers+rcp->autolimit; + rcp->status |= QCSTAT_MODECHECK; + } /* And set the schedule for the next update */ rcp->autoupdate = now + AUTOLIMIT_INTERVAL; @@ -1003,8 +1068,25 @@ void cs_removechannel(regchan *rcp) { releasechanindex(cip); } +/* Sender is who the DELCHAN is attributed to.. */ +int cs_removechannelifempty(nick *sender, regchan *rcp) { + unsigned int i; + regchanuser *rcup; + + for (i=0;iregusers[i];rcup;rcup=rcup->nextbychan) { + if (rcup->flags & ~(QCUFLAGS_PUNISH)) + return 0; + } + } + + cs_log(sender,"DELCHAN %s (Empty)",rcp->index->name->content); + cs_removechannel(rcp); + + return 1; +} + void cs_removeuser(reguser *rup) { - int i; regchanuser *rcup, *nrcup; regchan *rcp; struct authname *anp; @@ -1012,29 +1094,17 @@ void cs_removeuser(reguser *rup) { /* Remove the user from all its channels */ for (rcup=rup->knownon;rcup;rcup=nrcup) { nrcup=rcup->nextbyuser; - freesstring(rcup->info); rcp=rcup->chan; delreguserfromchannel(rcp, rup); - - for (i=0;iregusers[i]) - break; - } - - if (i==REGCHANUSERHASHSIZE) { - /* There are no users left on this channel! */ - cs_log(NULL, "DELCHAN %s (last user removed)",rcp->index->name->content); - cs_removechannel(rcp); - } - - freeregchanuser(rcup); + cs_removechannelifempty(NULL, rcp); } if(rup->domain) delreguserfrommaildomain(rup,rup->domain); freesstring(rup->localpart); freesstring(rup->email); + freesstring(rup->lastemail); freesstring(rup->lastuserhost); freesstring(rup->suspendreason); freesstring(rup->comment); @@ -1070,9 +1140,9 @@ int cs_bancheck(nick *np, channel *cp) { freesstring(rbp->reason); freechanban(rbp->cbp); freeregban(rbp); - } else if (nickmatchban(np,(*rbh)->cbp)) { + } else if (nickmatchban_visible(np,(*rbh)->cbp)) { /* This user matches this ban.. */ - if (!nickbanned(np,cp)) { + if (!nickbanned_visible(np,cp)) { /* Only bother putting the ban on the channel if they're not banned already */ /* (might be covered by this ban or a different one.. doesn't really matter */ localsetmodeinit(&changes, cp, chanservnick); @@ -1105,8 +1175,8 @@ void cs_setregban(chanindex *cip, regban *rbp) { for (i=0;(cip->channel) && ichannel->users->hashsize;i++) { if (cip->channel->users->content[i]!=nouser && (np=getnickbynumeric(cip->channel->users->content[i])) && - !IsService(np) && - nickmatchban(np, rbp->cbp)) + !IsService(np) && !IsOper(np) && !IsXOper(np) && + nickmatchban_visible(np, rbp->cbp)) localkickuser(chanservnick, cip->channel, np, rbp->reason ? rbp->reason->content : "Banned."); } @@ -1120,7 +1190,7 @@ void cs_banuser(modechanges *changes, chanindex *cip, nick *np, const char *reas if (!cip->channel) return; - if (nickbanned(np, cip->channel)) { + if (nickbanned_visible(np, cip->channel)) { localkickuser(chanservnick, cip->channel, np, reason?reason:"Banned."); return; } @@ -1157,7 +1227,7 @@ void cs_banuser(modechanges *changes, chanindex *cip, nick *np, const char *reas flag_t cs_sanitisechanlev(flag_t flags) { /* +m or +n cannot have any "punishment" flags */ if (flags & (QCUFLAG_MASTER | QCUFLAG_OWNER)) - flags &= ~(QCUFLAG_BANNED | QCUFLAG_QUIET | QCUFLAG_DENY); + flags &= ~(QCUFLAGS_PUNISH); /* +d can't be +o */ if (flags & QCUFLAG_DENY) @@ -1183,9 +1253,9 @@ flag_t cs_sanitisechanlev(flag_t flags) { if (!(flags & (QCUFLAG_VOICE | QCUFLAG_OP))) flags &= ~QCUFLAG_PROTECT; - /* The personal flags require one of +mnovk */ + /* The personal flags require one of +mnovk, as does +t */ if (!(flags & (QCUFLAG_OWNER | QCUFLAG_MASTER | QCUFLAG_OP | QCUFLAG_VOICE | QCUFLAG_KNOWN))) - flags &= ~QCUFLAGS_PERSONAL; + flags &= ~(QCUFLAGS_PERSONAL | QCUFLAG_TOPIC); return flags; } @@ -1243,8 +1313,10 @@ reguser *findreguser(nick *sender, const char *str) { /* * Unbans a mask from a channel, including permbans if user has correct privs. + * + * Return 0 if it works, 1 if it don't. */ -void cs_unbanfn(nick *sender, chanindex *cip, UnbanFN fn, void *arg, int removepermbans) { +int cs_unbanfn(nick *sender, chanindex *cip, UnbanFN fn, void *arg, int removepermbans, int abortonfailure) { regban **rbh, *rbp; chanban **cbh, *cbp; regchan *rcp; @@ -1266,6 +1338,7 @@ void cs_unbanfn(nick *sender, chanindex *cip, UnbanFN fn, void *arg, int removep rbh=&(rbp->next); } else if (!cs_checkaccess(sender, NULL, CA_MASTERPRIV, cip, NULL, 0, 1)) { chanservstdmessage(sender, QM_NOTREMOVEDPERMBAN, banstr, cip->name->content); + if (abortonfailure) return 1; /* Just give up... */ rbh=&(rbp->next); } else { chanservstdmessage(sender, QM_REMOVEDPERMBAN, banstr, cip->name->content); @@ -1299,4 +1372,14 @@ void cs_unbanfn(nick *sender, chanindex *cip, UnbanFN fn, void *arg, int removep } localsetmodeflush(&changes,1); } + + return 0; +} + +/* Add entry to channel op history when someone gets ops. */ +void cs_logchanop(regchan *rcp, char *nick, reguser *rup) { + strncpy(rcp->chanopnicks[rcp->chanoppos], nick, NICKLEN); + rcp->chanopnicks[rcp->chanoppos][NICKLEN]='\0'; + rcp->chanopaccts[rcp->chanoppos]=rup->ID; + rcp->chanoppos=(rcp->chanoppos+1)%CHANOPHISTORY; }