#include "chanserv.h"
+#include "../core/hooks.h"
#include "../core/schedule.h"
#include "../core/config.h"
#include "../localuser/localuser.h"
#include "../nick/nick.h"
#include "../parser/parser.h"
#include "../lib/splitline.h"
+#include "../lib/irc_string.h"
#include <string.h>
#include <stdio.h>
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;
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;i<CHANNELHASHSIZE;i++) {
for (cip=chantable[i];cip;cip=cip->next) {
if (cip->channel && (rcp=cip->exts[chanservext]) && !CIsSuspended(rcp)) {
- if (CIsJoined(rcp))
- chanservjoinchan(cip->channel);
+ /* This will do timestamp faffing even if it won't actually join */
+ chanservjoinchan(cip->channel);
/* Do a check at some future time.. */
cs_schedupdate(cip, 1, 5);
rcp->status |= (QCSTAT_OPCHECK | QCSTAT_MODECHECK | QCSTAT_BANCHECK);
}
}
+ Error("chanserv",ERR_INFO,"Loaded and joined channels.");
+
if (chanserv_init_status == CS_INIT_NOUSER) {
/* If this is the first time, perform last init tasks */
chanserv_finalinit();
}
cmd=findcommandintree(csctcpcommands, cargv[0]+1, 1);
if (cmd) {
+ cmd->calls++;
rejoinline(cargv[1],cargc-1);
cmd->handler((void *)sender, cargc-1, &(cargv[1]));
}
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]);
break;
}
+ if ((cmd->level & QCMD_ACHIEVEMENTS) && !UIsDev(rup) &&
+ ((time(NULL) < ACHIEVEMENTS_START) ||
+ ((time(NULL) > ACHIEVEMENTS_END) && !UIsAchievements(rup)))) {
+ chanservstdmessage(sender, QM_UNKNOWNCMD, cargv[0]);
+ break;
+ }
+
+ if ((cmd->level & QCMD_TITLES) && !UIsDev(rup) &&
+ ((time(NULL) < ACHIEVEMENTS_START) ||
+ (time(NULL) > ACHIEVEMENTS_END))) {
+ chanservstdmessage(sender, QM_UNKNOWNCMD, cargv[0]);
+ break;
+ }
+
+ cmd->calls++;
+
if (cmd->maxparams < (cargc-1)) {
rejoinline(cargv[cmd->maxparams],cargc-(cmd->maxparams));
cargc=(cmd->maxparams)+1;
}
cmd->handler((void *)sender, cargc-1, &(cargv[1]));
+
+ triggerhook(HOOK_CHANSERV_CMD, sender);
}
break;
destroycommandtree(csctcpcommands);
}
-void chanservaddcommand(char *command, int flags, int maxparams, CommandHandler handler, char *description) {
+void chanservaddcommand(char *command, int flags, int maxparams, CommandHandler handler, char *description, const char *help) {
Command *newcmd;
cmdsummary *summary;
memset((void *)summary,0,sizeof(cmdsummary));
summary->def=getsstring(description, 250);
-
+ summary->defhelp=(char *)help; /* Assume that help is a constant */
+
newcmd->ext=(void *)summary;
loadcommandsummary(newcmd);
}
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 = 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);
}
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)) {
- localpartchannel(chanservnick, cp);
+ localpartchannel(chanservnick, cp, NULL);
}
-
- /* OK, we're going to join the channel. Since our timestamp must be less
- * than or equal to the one already there it should be OK to burst on.
- *
+
+ /* Right, we are definately going to either join the channel or at least
+ * burst some modes onto it.
+ *
* We will try and burst our view of the world; if the timestamps are
* actually equal this will be mostly ignored and we will have to fix it
- * up later */
+ * 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..
+ * 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
+ * bit rude if it's their channel, so if there is only one person on the
+ * channel and they have a right to be there, burst with default modes
+ * only to avoid them being netrider kicked.
+ */
+ if (cp->users->totalusers==1) {
+ for (i=0;i<cp->users->hashsize;i++) {
+ if (cp->users->content[i] != nouser) {
+ if ((np=getnickbynumeric(cp->users->content[i]&CU_NUMERICMASK)) &&
+ (rup=getreguserfromnick(np)) &&
+ (rcup=findreguseronchannel(rcp,rup)) &&
+ CUKnown(rcup)) {
+ /* OK, there was one user, and they are known on this channel.
+ * Don't burst with +i or +k */
+ themodes &= ~(CHANMODE_INVITEONLY | CHANMODE_KEY);
+ }
+ }
+ }
+ }
+
+ /* We should be on the channel - join with our nick */
if (!CIsSuspended(rcp) && CIsJoined(rcp) && !getnumerichandlefromchanhash(cp->users, chanservnick->numeric)) {
- localburstontochannel(cp, chanservnick, rcp->ltimestamp, rcp->forcemodes, rcp->limit, (rcp->key)?rcp->key->content:NULL);
+ /* If we pass the key parameter here but are not setting +k (see above)
+ * then localburstontochannel() will ignore the key */
+ localburstontochannel(cp, chanservnick, rcp->ltimestamp, themodes,
+ rcp->limit, (rcp->key)?rcp->key->content:NULL);
+ }
+
+ /* We're not joining the channel - send the burst with no nick */
+ if (!CIsSuspended(rcp) && !CIsJoined(rcp) && (cp->timestamp > rcp->ltimestamp)) {
+ localburstontochannel(cp, NULL, rcp->ltimestamp, themodes,
+ rcp->limit, (rcp->key)?rcp->key->content:NULL);
}
}
int notice;
reguser *rup;
int language;
- va_list va;
- char *message;
+ va_list va, va2;
+ char *message, *messageargs;
char *bp2,*bp;
int len;
} 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;
*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;
}
/* If we ran out of buffer, get out here */
- if (!*bp)
+ if (!*bp || (*bp=='\n' && oneline))
break;
bp2=buf2;
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;i<NICKHASHSIZE;i++)
- for (np=nicktable[i];np;np=np->next)
- if (IsOper(np))
- chanservsendmessage(np, "%s", buf);
+ for (i=0;i<NICKHASHSIZE;i++) {
+ for (np=nicktable[i];np;np=np->next) {
+ 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;
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 checkmasterpassword(reguser *rup, const char *pass) {
- if (!strncmp(rup->masterpass, pass, PASSLEN))
- return 1;
- return 0;
+ 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;
}
-int setmasterpassword(reguser *rup, const char *pass) {
- strncpy(rup->masterpass, pass, PASSLEN);
- rup->masterpass[PASSLEN]='\0';
- return 1;
+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';
+
+ return algorithm(usernamel, rup->password, cs_calcchallenge(entropy), response);
+}
+
+int checkhashpass(reguser *rup, const char *junk, const char *hash) {
+ char usernamel[NICKLEN+1], *dp, *up;
+
+ for(up=rup->username,dp=usernamel;*up;)
+ *dp++ = ToLower(*up++);
+ *dp = '\0';
+
+ return cs_checkhashpass(usernamel, rup->password, junk, hash);
}
int setpassword(reguser *rup, const char *pass) {
void cs_checknick(nick *np) {
activeuser* aup;
reguser *rup;
- nicklist *nlp;
char userhost[USERLEN+HOSTLEN+3];
if (!(aup=getactiveuserfromnick(np))) {
assert(getactiveuserfromnick(np));
- if (IsAccount(np)) {
- if ((rup=findreguserbynick(np->authname))!=NULL) {
- aup->rup=rup;
- nlp=getnicklist();
- nlp->np=np;
- nlp->next=rup->nicks;
- rup->nicks=nlp;
+ 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 {
- aup->rup=NULL;
/* Auto create user.. */
rup=getreguser();
rup->status=0;
- rup->ID=++lastuserID;
+ rup->ID=np->auth->userid;
+ if (rup->ID > lastuserID)
+ lastuserID=rup->ID;
strncpy(rup->username,np->authname,NICKLEN); rup->username[NICKLEN]='\0';
rup->created=time(NULL);
- rup->lastauth=time(NULL);
+ 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->masterpass[0]='\0';
rup->email=NULL;
+ rup->lastemail=NULL;
+ rup->localpart=NULL;
+ rup->domain=NULL;
rup->info=NULL;
sprintf(userhost,"%s@%s",np->ident,np->host->name->content);
rup->lastuserhost=getsstring(userhost,USERLEN+HOSTLEN+1);
rup->checkshd=NULL;
rup->stealcount=0;
rup->fakeuser=NULL;
- rup->nicks=getnicklist();
- rup->nicks->np=np;
- rup->nicks->next=NULL;
addregusertohash(rup);
csdb_createuser(rup);
- aup->rup=rup;
}
- } else {
- aup->rup=NULL;
}
cs_checknickbans(np);
}
}
+/* 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;
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;
}
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) {
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.. */
+ /* User is on channel.. */
+
+ /* 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 (CUHasOpPriv(rcup) && cs_ischannelactive(rcup->chan->index->channel, NULL)) {
+ /* 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) {
if (!IsService(np) && (CUIsDeny(rcup) || (CIsBitch(rcup->chan) && !CUHasOpPriv(rcup))))
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) {
} else {
/* Channel exists but user is not joined: invite if they are +j-b */
if (CUIsAutoInvite(rcup) && CUKnown(rcup) && !CUIsBanned(rcup)) {
- localinvite(chanservnick, rcup->chan->index->channel, np);
+ localinvite(chanservnick, rcup->chan->index, np);
}
}
- }
- }
+ } /* if (rcup->chan->index->channel) */ else {
+ /* Channel doesn't currently exist - send invite anyway for +j */
+ if (CUIsAutoInvite(rcup) && CUKnown(rcup) && !CUIsBanned(rcup)) {
+ localinvite(chanservnick, rcup->chan->index, np);
+ }
+ }
+ } /* for */
}
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 */
for (j=0;j<i;j++) {
if ((rcp=ca[j]->index->exts[chanservext]) && !CIsSuspended(rcp) &&
- CIsEnforce(rcp) && nickbanned(np, ca[j]))
+ CIsEnforce(rcp) && nickbanned(np, ca[j], 1))
localkickuser(chanservnick, ca[j], np, "Banned.");
}
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.");
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);
/* Only chanserv left in this channel */
if (now >= (rcp->lastpart + LINGERTIME)) {
/* Time to go */
- localpartchannel(chanservnick, cip->channel);
+ localpartchannel(chanservnick, cip->channel, "Empty Channel");
return;
} else {
if (!nextsched || nextsched > (rcp->lastpart + LINGERTIME))
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;
localsetmodeflush(&changes, 1);
}
-void cs_removechannel(regchan *rcp) {
+void cs_removechannel(regchan *rcp, char *reason) {
int i;
chanindex *cip;
regchanuser *rcup, *nrcup;
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);
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;i<REGCHANUSERHASHSIZE;i++) {
+ for (rcup=rcp->regusers[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;
/* 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;i<REGCHANUSERHASHSIZE;i++) {
- if (rcp->regusers[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);
removereguserfromhash(rup);
- if (rup->nicks) {
+ if ((anp=findauthname(rup->ID)) && anp->nicks) {
rup->status |= QUSTAT_DEAD;
} else {
freereguser(rup);
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);
if (!cip->channel)
return;
-
+
localsetmodeinit(&changes, cip->channel, chanservnick);
localdosetmode_ban(&changes, bantostring(rbp->cbp), MCB_ADD);
localsetmodeflush(&changes, 1);
for (i=0;(cip->channel) && i<cip->channel->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.");
}
if (!cip->channel)
return;
- if (nickbanned(np, cip->channel)) {
+ if (nickbanned(np, cip->channel, 1)) {
localkickuser(chanservnick, cip->channel, np, reason?reason:"Banned.");
return;
}
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:
+ * This function does the standard "nick or #user" lookup.
+ * If "sender" is not NULL, a suitable error message will
+ * be sent if the lookup fails.
+ * "sender" MUST be sent when a user is requesting a lookup
+ * as there is some policy code here.
+ */
+
+reguser *findreguser(nick *sender, const char *str) {
+ reguser *rup, *vrup = getreguserfromnick(sender);;
+ nick *np;
+
+ if (!str || !*str)
+ return NULL;
+
+ if (*str=='#') {
+ if (str[1]=='\0') {
+ if (sender)
+ chanservstdmessage(sender, QM_UNKNOWNUSER, str);
+ return NULL;
+ }
+ if (!(rup=findreguserbynick(str+1)) && sender)
+ chanservstdmessage(sender, QM_UNKNOWNUSER, str);
+ } else if (*str=='&' && vrup && UHasStaffPriv(vrup)) {
+ if (str[1]=='\0') {
+ if (sender)
+ chanservstdmessage(sender, QM_UNKNOWNUSER, str);
+ return NULL;
+ }
+ if (!(rup=findreguserbyID(atoi(str+1))) && sender)
+ chanservstdmessage(sender, QM_UNKNOWNUSER, str);
+ } else {
+ if (!(np=getnickbynick(str))) {
+ if (sender)
+ chanservstdmessage(sender, QM_UNKNOWNUSER, str);
+ return NULL;
+ }
+ if (!(rup=getreguserfromnick(np)) && sender)
+ chanservstdmessage(sender, QM_USERNOTAUTHED, str);
+ }
+
+ /* 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, int (*fn)(void *arg, struct chanban *ban), 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;
+}
+
+regchan *cs_addchan(chanindex *cip, nick *sender, reguser *addedby, reguser *founder, flag_t flags, short type) {
+ regchan *rcp;
+ regchanuser *rcup;
+ void *args[3];
+ int i;
+
+ if (cip->exts[chanservext])
+ return NULL;
+
+ /* Initialise the channel */
+ rcp=getregchan();
+
+ /* ID, index */
+ rcp->ID=++lastchannelID;
+ rcp->index=cip;
+ cip->exts[chanservext]=rcp;
+
+ rcp->chantype=type;
+ rcp->flags=flags;
+ rcp->status=0;
+ rcp->bans=NULL;
+ rcp->lastcountersync=0;
+
+ rcp->limit=0;
+ rcp->forcemodes=CHANMODE_DEFAULT;
+ rcp->denymodes=0;
+
+ if (CIsAutoLimit(rcp)) {
+ rcp->forcemodes |= CHANMODE_LIMIT;
+ }
+
+ rcp->autolimit=5;
+ rcp->banstyle=0;
+
+ rcp->created=rcp->lastactive=rcp->statsreset=rcp->ostatsreset=time(NULL);
+ rcp->banduration=0;
+ rcp->autoupdate=0;
+ rcp->lastbancheck=0;
+
+ /* Added by */
+ rcp->addedby=addedby->ID;
+
+ /* Founder */
+ rcp->founder=founder->ID;
+
+ /* Suspend by */
+ rcp->suspendby=0;
+ rcp->suspendtime=0;
+
+ rcp->totaljoins=rcp->tripjoins=rcp->otripjoins=rcp->maxusers=rcp->tripusers=rcp->otripusers=0;
+ rcp->welcome=rcp->topic=rcp->key=rcp->suspendreason=rcp->comment=NULL;
+
+ /* Users */
+ memset(rcp->regusers,0,REGCHANUSERHASHSIZE*sizeof(reguser *));
+
+ rcp->checksched=NULL;
+ rcp->ltimestamp=0;
+ for (i=0;i<CHANOPHISTORY;i++) {
+ rcp->chanopnicks[i][0]='\0';
+ rcp->chanopaccts[i]=0;
+ }
+ rcp->chanoppos=0;
+
+ /* Add new channel to db.. */
+ csdb_createchannel(rcp);
+
+ /* Add the founder as +ano */
+ rcup=getregchanuser();
+ rcup->chan=rcp;
+ rcup->user=founder;
+ rcup->flags=(QCUFLAG_OWNER | QCUFLAG_OP | QCUFLAG_AUTOOP);
+ rcup->usetime=0;
+ rcup->info=NULL;
+ rcup->changetime=time(NULL);
+
+ addregusertochannel(rcup);
+ csdb_createchanuser(rcup);
+ csdb_chanlevhistory_insert(rcp, sender, rcup->user, 0, rcup->flags);
+
+ args[0]=sender;
+ args[1]=rcup;
+ args[2]=(void *)0;
+
+ triggerhook(HOOK_CHANSERV_CHANLEVMOD, args);
+
+ /* If the channel exists, get the ball rolling */
+ if (cip->channel) {
+ chanservjoinchan(cip->channel);
+ rcp->status |= QCSTAT_MODECHECK | QCSTAT_OPCHECK | QCSTAT_BANCHECK;
+ cs_timerfunc(cip);
+ }
+
+ return rcp;
+}