X-Git-Url: https://jfr.im/git/irc/quakenet/newserv.git/blobdiff_plain/4630ab32fe5498fda5616f059d6a4a11b8f82c88..ff4d31c7eb83d5395bd669c01fd281c8acdf54fc:/chanserv/chanservuser.c diff --git a/chanserv/chanservuser.c b/chanserv/chanservuser.c index bbd7390d..8e6a2ef5 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) { @@ -109,6 +111,7 @@ void chanservuserhandler(nick *target, int message, void **params) { } cmd=findcommandintree(csctcpcommands, cargv[0]+1, 1); if (cmd) { + cmd->calls++; rejoinline(cargv[1],cargc-1); cmd->handler((void *)sender, cargc-1, &(cargv[1])); } @@ -141,6 +144,12 @@ void chanservuserhandler(nick *target, int message, void **params) { break; } + if ((cmd->level & QCMD_STAFF) && + (!(rup=getreguserfromnick(sender)) || !UHasStaffPriv(rup))) { + chanservstdmessage(sender, QM_NOACCESS, cargv[0]); + break; + } + if ((cmd->level & QCMD_HELPER) && (!(rup=getreguserfromnick(sender)) || !UHasHelperPriv(rup))) { chanservstdmessage(sender, QM_NOACCESS, cargv[0]); @@ -165,6 +174,8 @@ void chanservuserhandler(nick *target, int message, void **params) { break; } + cmd->calls++; + if (cmd->maxparams < (cargc-1)) { rejoinline(cargv[cmd->maxparams],cargc-(cmd->maxparams)); cargc=(cmd->maxparams)+1; @@ -235,20 +246,32 @@ void chanservremovecommand(char *command, CommandHandler handler) { deletecommandfromtree(cscommands, command, handler); } +void chanservpartchan(channel *cp, char *reason) { + /* Sanity check that we exist and are on the channel. + * + * Note that we don't do any of the other usual sanity checks here; if + * this channel is unregged or suspended or whatever then all the more + * reason to get Q off it ASAP! */ + if (!chanservnick || !cp || !cp->users || !getnumerichandlefromchanhash(cp->users, chanservnick->numeric)) + return; + + localpartchannel(chanservnick, cp, reason); +} + void chanservjoinchan(channel *cp) { regchan *rcp; unsigned int i; 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); } @@ -257,12 +280,10 @@ void chanservjoinchan(channel *cp) { if (!chanservnick) return; + /* In gerenal this function shouldn't be used any more as a reason to get + * Q to leave, but if it should be leaving then just part with no reason. */ if ((CIsSuspended(rcp) || !CIsJoined(rcp)) && getnumerichandlefromchanhash(cp->users, chanservnick->numeric)) { - if(rcp->suspendreason) { - localpartchannel(chanservnick, cp, rcp->suspendreason->content); - } else { - localpartchannel(chanservnick, cp, NULL); - } + localpartchannel(chanservnick, cp, NULL); } /* Right, we are definately going to either join the channel or at least @@ -273,8 +294,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 @@ -318,8 +345,8 @@ void chanservstdmessage(nick *np, int messageid, ... ) { int notice; reguser *rup; int language; - va_list va; - char *message; + va_list va, va2; + char *message, *messageargs; char *bp2,*bp; int len; @@ -341,11 +368,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); - vsnprintf(buf,5000,message,va); + va_copy(va2, va); + q9vsnprintf(buf,5000,message,messageargs,va); va_end(va); len=0; @@ -378,9 +408,18 @@ void chanservstdmessage(nick *np, int messageid, ... ) { *bp2++=*bp; } } + + /* Special case: If it's a "not enough parameters" message, show the first line of help */ + if (messageid==QM_NOTENOUGHPARAMS) { + char *command=va_arg(va2, char *); + cs_sendhelp(np, command, 1); + chanservstdmessage(np, QM_TYPEHELPFORHELP, command); + } + + va_end(va2); } -void chanservsendmessage(nick *np, char *message, ... ) { +void chanservsendmessage_real(nick *np, int oneline, char *message, ... ) { char buf[5010]; /* Very large buffer.. */ char buf2[512], *bp, *bp2; int notice; @@ -424,7 +463,7 @@ void chanservsendmessage(nick *np, char *message, ... ) { } /* If we ran out of buffer, get out here */ - if (!*bp) + if (!*bp || (*bp=='\n' && oneline)) break; bp2=buf2; @@ -441,22 +480,29 @@ void chanservwallmessage(char *message, ... ) { va_list va; nick *np; unsigned int i=0; - + va_start(va,message); vsnprintf(buf,5000,message,va); va_end(va); /* Scan for users */ - for (i=0;inext) - if (IsOper(np)) - chanservsendmessage(np, "%s", buf); + for (i=0;inext) { + if (!IsOper(np)) /* optimisation, if VIEWWALLMESSAGE changes change this */ + continue; + + if (!cs_privcheck(QPRIV_VIEWWALLMESSAGE, np)) + continue; + + chanservsendmessage(np, "%s", buf); + } + } } void chanservkillstdmessage(nick *target, int messageid, ... ) { char buf[512]; int language; - char* message; + char *message, *messageargs; va_list va; reguser *rup; @@ -470,15 +516,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); + killuser(chanservnick, target, "%s", buf); } int checkpassword(reguser *rup, const char *pass) { + if (!(*rup->password)) + return 0; + if (!strncmp(rup->password, pass, PASSLEN)) return 1; return 0; @@ -487,6 +537,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'; @@ -524,6 +577,23 @@ void cs_checknick(nick *np) { if (IsAccount(np) && np->auth) { if (np->auth->exts[chanservaext]) { + rup=getreguserfromnick(np); + + /* safe? */ + 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 { /* Auto create user.. */ @@ -534,14 +604,18 @@ 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->lastpasschange=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; @@ -605,6 +679,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; @@ -623,7 +700,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; } @@ -633,23 +710,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) { @@ -672,13 +759,34 @@ 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.. */ - /* Update last use time */ + /* Update last use time. Do early in case of ban. */ rcup->usetime=getnettime(); + + if (CUIsBanned(rcup)) { + cs_banuser(NULL, rcup->chan->index, np, NULL); + continue; + } + + 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); + } + } + localsetmodeinit(&changes, rcup->chan->index->channel, chanservnick); if (*lp & CUMODE_OP) { @@ -686,8 +794,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) { @@ -714,7 +824,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 */ @@ -724,7 +834,7 @@ void cs_checknickbans(nick *np) { for (j=0;jindex->exts[chanservext]) && !CIsSuspended(rcp) && - CIsEnforce(rcp) && nickbanned(np, ca[j])) + CIsEnforce(rcp) && nickbanned(np, ca[j], 1)) localkickuser(chanservnick, ca[j], np, "Banned."); } @@ -755,18 +865,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(np, rbp->cbp, 1)) { + if (!nickbanned(np, cp, 1)) { localdosetmode_ban(&changes, bantostring(rbp->cbp), MCB_ADD); } localkickuser(chanservnick,cp,np,rbp->reason?rbp->reason->content:"Banned."); @@ -779,7 +889,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(np, cbp, 1)) localkickuser(chanservnick,cp,np,"Banned."); } rcp->lastbancheck=time(NULL); @@ -863,9 +973,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; @@ -939,7 +1054,7 @@ void cs_timerfunc(void *arg) { localsetmodeflush(&changes, 1); } -void cs_removechannel(regchan *rcp) { +void cs_removechannel(regchan *rcp, char *reason) { int i; chanindex *cip; regchanuser *rcup, *nrcup; @@ -968,8 +1083,7 @@ void cs_removechannel(regchan *rcp) { deleteschedule(rcp->checksched, cs_timerfunc, rcp->index); if (cip->channel) { - rcp->flags=QCFLAG_SUSPENDED; - chanservjoinchan(cip->channel); /* Force off the channel */ + chanservpartchan(cip->channel, reason); } csdb_deletechannel(rcp); @@ -986,8 +1100,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, "Last user removed - channel deleted."); + + return 1; +} + void cs_removeuser(reguser *rup) { - int i; regchanuser *rcup, *nrcup; regchan *rcp; struct authname *anp; @@ -995,29 +1126,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); @@ -1053,9 +1172,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(np,(*rbh)->cbp,1)) { /* This user matches this ban.. */ - if (!nickbanned(np,cp)) { + if (!nickbanned(np,cp,1)) { /* 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); @@ -1088,8 +1207,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(np, rbp->cbp, 1)) localkickuser(chanservnick, cip->channel, np, rbp->reason ? rbp->reason->content : "Banned."); } @@ -1103,7 +1222,7 @@ void cs_banuser(modechanges *changes, chanindex *cip, nick *np, const char *reas if (!cip->channel) return; - if (nickbanned(np, cip->channel)) { + if (nickbanned(np, cip->channel, 1)) { localkickuser(chanservnick, cip->channel, np, reason?reason:"Banned."); return; } @@ -1131,6 +1250,47 @@ void cs_banuser(modechanges *changes, chanindex *cip, nick *np, const char *reas localkickuser(chanservnick, cip->channel, np, reason?reason:"Banned."); } + +/* + * cs_sanitisechanlev: Removes impossible combinations from chanlev flags. + * chanservuser.c is probably not the right file for this, but nowhere better + * presented itself... + */ +flag_t cs_sanitisechanlev(flag_t flags) { + /* +m or +n cannot have any "punishment" flags */ + if (flags & (QCUFLAG_MASTER | QCUFLAG_OWNER)) + flags &= ~(QCUFLAGS_PUNISH); + + /* +d can't be +o */ + if (flags & QCUFLAG_DENY) + flags &= ~QCUFLAG_OP; + + /* +q can't be +v */ + if (flags & QCUFLAG_QUIET) + flags &= ~QCUFLAG_VOICE; + + /* +p trumps +a and +g */ + if (flags & QCUFLAG_PROTECT) + flags &= ~(QCUFLAG_AUTOOP | QCUFLAG_AUTOVOICE); + + /* -o can't be +a */ + if (!(flags & QCUFLAG_OP)) + flags &= ~QCUFLAG_AUTOOP; + + /* +a or -v can't be +g. +a implies +o at this stage (see above) */ + if (!(flags & QCUFLAG_VOICE) || (flags & QCUFLAG_AUTOOP)) + flags &= ~QCUFLAG_AUTOVOICE; + + /* +p requires +o or +v */ + if (!(flags & (QCUFLAG_VOICE | QCUFLAG_OP))) + flags &= ~QCUFLAG_PROTECT; + + /* 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 | QCUFLAG_TOPIC); + + return flags; +} /* * findreguser: @@ -1156,7 +1316,7 @@ reguser *findreguser(nick *sender, const char *str) { } if (!(rup=findreguserbynick(str+1)) && sender) chanservstdmessage(sender, QM_UNKNOWNUSER, str); - } else if (*str=='&' && vrup && UHasHelperPriv(vrup)) { + } else if (*str=='&' && vrup && UHasStaffPriv(vrup)) { if (str[1]=='\0') { if (sender) chanservstdmessage(sender, QM_UNKNOWNUSER, str); @@ -1174,10 +1334,93 @@ reguser *findreguser(nick *sender, const char *str) { chanservstdmessage(sender, QM_USERNOTAUTHED, str); } - if (rup && (UIsSuspended(rup) || (rup->status & QUSTAT_DEAD))) { + /* removed the suspended check from here, I don't see the point... */ + if (rup && (rup->status & QUSTAT_DEAD)) { chanservstdmessage(sender, QM_USERHASBADAUTH, rup->username); return NULL; } return rup; } + +/* + * Unbans a mask from a channel, including permbans if user has correct privs. + * + * Return 0 if it works, 1 if it don't. + */ +int cs_unbanfn(nick *sender, chanindex *cip, UnbanFN fn, void *arg, int removepermbans, int abortonfailure) { + regban **rbh, *rbp; + chanban **cbh, *cbp; + regchan *rcp; + modechanges changes; + char *banstr; + + rcp=cip->exts[chanservext]; + + if (cip->channel) + localsetmodeinit(&changes, cip->channel, chanservnick); + + for (rbh=&(rcp->bans); *rbh; ) { + rbp=*rbh; + if (fn(arg, rbp->cbp)) { + banstr=bantostring(rbp->cbp); + /* Check perms and remove */ + if(!removepermbans) { + chanservstdmessage(sender, QM_WARNNOTREMOVEDPERMBAN, banstr, cip->name->content); + 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); + if (cip->channel) + localdosetmode_ban(&changes, banstr, MCB_DEL); + /* Remove from database */ + csdb_deleteban(rbp); + /* Remove from list */ + (*rbh)=rbp->next; + /* Free ban/string and update setby refcount, and free actual regban */ + freesstring(rbp->reason); + freechanban(rbp->cbp); + freeregban(rbp); + } + } else { + rbh=&(rbp->next); + } + } + + if (cip->channel) { + for (cbh=&(cip->channel->bans); *cbh; ) { + cbp=*cbh; + if (fn(arg, cbp)) { + /* Remove */ + banstr=bantostring(cbp); + chanservstdmessage(sender, QM_REMOVEDCHANBAN, banstr, cip->name->content); + localdosetmode_ban(&changes, banstr, MCB_DEL); + } else { + cbh=&(cbp->next); + } + } + 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; +} + +int checkreason(nick *np, char *reason) { + if((strlen(reason) < MIN_REASONLEN) || !strchr(reason, ' ')) { + chanservstdmessage(np, QM_REASONREQUIRED); + return 0; + } + + return 1; +}