]> jfr.im git - irc/quakenet/newserv.git/blobdiff - chanserv/chanservuser.c
CHANSERV: Add support to count calls, and "showcommands -v" to show the
[irc/quakenet/newserv.git] / chanserv / chanservuser.c
index 9f0628c84c778b4c10e51cf9b51a15f879998984..00d780b54985ed4f49bc3dc66ac5d94473f3d683 100644 (file)
@@ -111,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]));
       }      
@@ -143,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]);
@@ -167,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;
@@ -237,6 +246,18 @@ 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;
@@ -259,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
@@ -461,16 +480,23 @@ 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;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, ... ) {
@@ -500,6 +526,9 @@ void chanservkillstdmessage(nick *target, int messageid, ... ) {
 }
 
 int checkpassword(reguser *rup, const char *pass) {
+  if (!(*rup->password))
+    return 0;
+
   if (!strncmp(rup->password, pass, PASSLEN))
     return 1;
   return 0;
@@ -508,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';
@@ -546,10 +578,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 {
@@ -563,6 +606,7 @@ void cs_checknick(nick *np) {
       rup->created=time(NULL);
       rup->lastauth=0;
       rup->lastemailchange=0;
+      rup->lastpasschange=0;
       rup->flags=QUFLAG_NOTICE;
       rup->languageid=0;
       rup->suspendby=0;
@@ -635,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;
@@ -653,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;
     }
@@ -663,16 +710,24 @@ 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)))
@@ -704,11 +759,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();
 
@@ -748,7 +818,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 */
@@ -758,7 +828,7 @@ void cs_checknickbans(nick *np) {
 
   for (j=0;j<i;j++) {
     if ((rcp=ca[j]->index->exts[chanservext]) && !CIsSuspended(rcp) && 
-       CIsEnforce(rcp) && nickbanned(np, ca[j]))
+       CIsEnforce(rcp) && nickbanned_visible(np, ca[j]))
       localkickuser(chanservnick, ca[j], np, "Banned.");
   }
 
@@ -789,18 +859,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.");
@@ -813,7 +883,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);
@@ -897,9 +967,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;
@@ -973,7 +1048,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;
@@ -1002,8 +1077,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);
@@ -1033,7 +1107,7 @@ int cs_removechannelifempty(nick *sender, regchan *rcp) {
   }
   
   cs_log(sender,"DELCHAN %s (Empty)",rcp->index->name->content);
-  cs_removechannel(rcp);
+  cs_removechannel(rcp, "Last user removed - channel deleted.");
   
   return 1;
 }
@@ -1092,9 +1166,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);
@@ -1127,8 +1201,8 @@ void cs_setregban(chanindex *cip, regban *rbp) {
   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_visible(np, rbp->cbp))
       localkickuser(chanservnick, cip->channel, np, rbp->reason ? rbp->reason->content : "Banned.");
   }
 
@@ -1142,7 +1216,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;
   }
@@ -1236,7 +1310,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);
@@ -1335,3 +1409,12 @@ void cs_logchanop(regchan *rcp, char *nick, reguser *rup) {
   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;
+}