]> jfr.im git - irc/quakenet/newserv.git/blobdiff - chanserv/chancmds/chanlev.c
chanserv: Limit number of channels a user can have flags on.
[irc/quakenet/newserv.git] / chanserv / chancmds / chanlev.c
index 871ad4ed6dfebdc9ccd056644a20a2fb41d316b7..3a9e66782d715438e1f8914c659cbfd57fdc7c47 100644 (file)
@@ -90,18 +90,18 @@ int csc_dochanlev(void *source, int cargc, char **cargv) {
   nick *sender=source;
   chanindex *cip;
   regchan *rcp;
-  regchanuser *rcup, *rcuplist;
+  regchanuser *rcup, *trcup, *rcuplist;
   regchanuser **rusers;
   reguser *rup=getreguserfromnick(sender), *target;
-  char time1[15],time2[15];
+  char time1[TIMELEN],time2[TIMELEN];
   char flagbuf[30];
-  struct tm *tmp;
   flag_t flagmask, changemask, flags, oldflags;
   int showtimes=0;
   int donehead=0;
   int i,j;
   int newuser=0;
   int usercount;
+  void *args[3];
 
   if (cargc<1) {
     chanservstdmessage(sender, QM_NOTENOUGHPARAMS, "chanlev");
@@ -133,6 +133,8 @@ int csc_dochanlev(void *source, int cargc, char **cargv) {
   
   if (cargc==1) {
     /* One arg: list chanlev */
+    int ncnt=0,mcnt=0,ocnt=0,vcnt=0,kcnt=0,bcnt=0;
+    
     if (cs_privcheck(QPRIV_VIEWFULLCHANLEV, sender)) {
       reguser *founder=NULL, *addedby=NULL;
       addedby=findreguserbyID(rcp->addedby);
@@ -144,6 +146,28 @@ int csc_dochanlev(void *source, int cargc, char **cargv) {
       }
     }
 
+    if (CIsSuspended(rcp) && cs_privcheck(QPRIV_VIEWCHANSUSPENSION, sender)) {
+      char *bywhom;
+
+      if(cs_privcheck(QPRIV_VIEWSUSPENDEDBY, sender)) {
+        reguser *trup = findreguserbyID(rcp->suspendby);
+        if(trup) {
+          bywhom = trup->username;
+        } else {
+         bywhom = "(unknown)";
+        }
+      } else {
+        bywhom = "(hidden)";
+      }
+
+      chanservstdmessage(sender, QM_CHANLEV_SUSPENDREASON, rcp->suspendreason?rcp->suspendreason->content:"(no reason)");
+      chanservstdmessage(sender, QM_CHANLEV_SUSPENDBY, bywhom);
+      chanservstdmessage(sender, QM_CHANLEV_SUSPENDSINCE, rcp->suspendtime);
+    }
+
+    if (rcp->comment && (cs_privcheck(QPRIV_VIEWCOMMENTS, sender)))
+      chanservstdmessage(sender, QM_SHORT_COMMENT, rcp->comment->content);
+
     /* Count users */
     for (i=0,usercount=0;i<REGCHANUSERHASHSIZE;i++)
       for (rcuplist=rcp->regusers[i];rcuplist;rcuplist=rcuplist->nextbychan)
@@ -177,6 +201,16 @@ int csc_dochanlev(void *source, int cargc, char **cargv) {
         flags=rcuplist->flags & (flagmask | QCUFLAGS_PERSONAL);
       }
       
+      /* Do the count here; note that +n's aren't counted as +m (and so on).  We're not
+       * using the IsX() macros because the displayed count needs to match up with 
+       * the displayed flags... */
+      if (flags & QCUFLAG_OWNER) ncnt++; else
+      if (flags & QCUFLAG_MASTER) mcnt++; else
+      if (flags & QCUFLAG_OP) ocnt++; else
+      if (flags & QCUFLAG_VOICE) vcnt++; else
+      if (flags & QCUFLAG_KNOWN) kcnt++;
+      if (flags & QCUFLAG_BANNED) bcnt++;
+      
       if (!donehead) {
        chanservstdmessage(sender, QM_CHANLEVHEADER, cip->name->content);
        if (showtimes) 
@@ -190,14 +224,12 @@ int csc_dochanlev(void *source, int cargc, char **cargv) {
        if (!rcuplist->usetime) {
          strcpy(time1,"Never");
        } else {
-         tmp=localtime(&(rcuplist->usetime));
-         strftime(time1,15,"%d/%m/%y %H:%M",tmp);
+         q9strftime(time1,sizeof(time1),rcuplist->usetime);
        }
        if (!rcuplist->changetime) {
          strcpy(time2, "Unknown");
        } else {
-         tmp=localtime(&(rcuplist->changetime));
-         strftime(time2,15,"%d/%m/%y %H:%M",tmp);
+         q9strftime(time2,sizeof(time2),rcuplist->changetime);
        }
        chanservsendmessage(sender, " %-15s %-13s %-14s  %-14s  %s", rcuplist->user->username, 
                            printflags(flags, rcuflags), time1, time2, rcuplist->info?rcuplist->info->content:"");
@@ -207,9 +239,12 @@ int csc_dochanlev(void *source, int cargc, char **cargv) {
     
     if (donehead) {
       chanservstdmessage(sender, QM_ENDOFLIST);
+      chanservstdmessage(sender, QM_CHANLEVSUMMARY, j, ncnt, mcnt, ocnt, vcnt, kcnt, bcnt);
     } else {
       chanservstdmessage(sender, QM_NOUSERSONCHANLEV, cip->name->content);
     }
+    
+    triggerhook(HOOK_CHANSERV_CHANLEVDUMP, sender);
 
     free(rusers);
   } else {
@@ -231,6 +266,27 @@ int csc_dochanlev(void *source, int cargc, char **cargv) {
       }
       
       if (!rcuplist) {
+        /* new user, we could store a count instead... that's probably better... */
+        unsigned int chanlevcount, channelcount;
+
+        for (chanlevcount=i=0;i<REGCHANUSERHASHSIZE;i++)
+          for (rcuplist=rcp->regusers[i];rcuplist;rcuplist=rcuplist->nextbychan)
+            chanlevcount++;
+
+        if(chanlevcount >= MAXCHANLEVS) {
+          chanservstdmessage(sender, QM_TOOMANYCHANLEVS);
+          return CMD_ERROR;
+        }
+
+        channelcount=0;
+        for (trcup=target->knownon;trcup;trcup=trcup->nextbyuser)
+          channelcount++;
+
+        if(channelcount >= MAXCHANNELS) {
+          chanservstdmessage(sender, QM_TOOMANYCHANNELS);
+          return CMD_ERROR;
+        } 
+
        rcuplist=getregchanuser();
        rcuplist->user=target;
        rcuplist->chan=rcp;
@@ -249,15 +305,14 @@ int csc_dochanlev(void *source, int cargc, char **cargv) {
        
        /* Everyone can change their own flags (except +dqb), and control (and see) personal flags */
        if (rcup==rcuplist) {
-         changemask = (rcup->flags | QCUFLAGS_PERSONAL) & 
-                     ~(QCUFLAG_BANNED | QCUFLAG_DENY | QCUFLAG_QUIET);
+         changemask = (rcup->flags | QCUFLAGS_PERSONAL) & ~(QCUFLAGS_PUNISH);
          flagmask |= QCUFLAGS_PERSONAL;
        }
        
        /* Masters are allowed to manipulate +ovagtbqdpk */
        if (CUHasMasterPriv(rcup))
          changemask |= ( QCUFLAG_KNOWN | QCUFLAG_OP | QCUFLAG_VOICE | QCUFLAG_AUTOOP | QCUFLAG_AUTOVOICE | 
-                         QCUFLAG_TOPIC | QCUFLAG_BANNED | QCUFLAG_QUIET | QCUFLAG_DENY | QCUFLAG_PROTECT);
+                         QCUFLAG_TOPIC | QCUFLAG_PROTECT | QCUFLAGS_PUNISH);
        
        /* Owners are allowed to manipulate +ms as well.
         * We allow +n to be given initially, but we check later to see if the flag has been added.
@@ -270,99 +325,91 @@ int csc_dochanlev(void *source, int cargc, char **cargv) {
       oldflags=rcuplist->flags;
       if (setflags(&(rcuplist->flags), changemask, cargv[2], rcuflags, REJECT_UNKNOWN | REJECT_DISALLOWED)) {
        chanservstdmessage(sender, QM_INVALIDCHANLEVCHANGE);
+        if (newuser)
+          freeregchanuser(rcuplist);
        return CMD_ERROR;
       }
 
-      /* check to see if +n has been given */
-      if (!(oldflags & QCUFLAG_OWNER) && (rcuplist->flags & QCUFLAG_OWNER)) {
+      /* check to see if +n has been given.  Opers can bypass this. */
+      if (!cs_privcheck(QPRIV_CHANGECHANLEV, sender) && !(oldflags & QCUFLAG_OWNER) && (rcuplist->flags & QCUFLAG_OWNER)) {
         rcuplist->flags=oldflags;
        chanservstdmessage(sender, QM_USEGIVEOWNER);
+        if (newuser)
+          freeregchanuser(rcuplist);
        return CMD_ERROR;
       }
 
-      /* Now fix up some "impossible" combinations.. */
-      /* +m can't be any of +qdb */
-      if (CUHasMasterPriv(rcuplist))
-       rcuplist->flags &= ~(QCUFLAG_BANNED | QCUFLAG_QUIET | QCUFLAG_DENY);
-      
-      /* +d can't be +o */
-      if (CUIsDeny(rcuplist))
-       rcuplist->flags &= ~QCUFLAG_OP;
-
-      /* +q can't be +v */
-      if (CUIsQuiet(rcuplist))
-       rcuplist->flags &= ~QCUFLAG_VOICE;
-
-      /* -o or +p can't be +a */
-      if (!CUIsOp(rcuplist) || CUIsProtect(rcuplist))
-       rcuplist->flags &= ~QCUFLAG_AUTOOP;
-      
-      /* +a or -v or +p can't be +g */
-      if (!CUIsVoice(rcuplist) || CUIsAutoOp(rcuplist) || CUIsProtect(rcuplist))
-       rcuplist->flags &= ~QCUFLAG_AUTOVOICE;
-
-      /* and -ov can't be +p */
-      if (!CUIsOp(rcuplist) && !CUIsVoice(rcuplist)) 
-       rcuplist->flags &= ~QCUFLAG_PROTECT;
-
-      /* Unknown users aren't allowed personal flags */
-      if (!CUKnown(rcuplist))
-        rcuplist->flags &= ~QCUFLAGS_PERSONAL;
+      /* Fix up impossible combinations */
+      rcuplist->flags = cs_sanitisechanlev(rcuplist->flags);
 
       /* Check if anything "significant" has changed */
       if ((oldflags ^ rcuplist->flags) & (QCUFLAG_OWNER | QCUFLAG_MASTER | QCUFLAG_OP))
        rcuplist->changetime=time(NULL);
 
       if(rcuplist->flags == oldflags) {
-        chanservstdmessage(sender, QM_DONE);
+        chanservstdmessage(sender, QM_CHANLEVNOCHANGE);
+        if (newuser)
+          freeregchanuser(rcuplist);
         return CMD_OK;
       }
 
-
       strcpy(flagbuf,printflags(oldflags,rcuflags));
       cs_log(sender,"CHANLEV %s #%s %s (%s -> %s)",cip->name->content,rcuplist->user->username,cargv[2],
             flagbuf,printflags(rcuplist->flags,rcuflags));
       csdb_chanlevhistory_insert(rcp, sender, rcuplist->user, oldflags, rcuplist->flags);
 
+      /* The user has to be on the relevant chanlev list before we trigger the hook.
+       * So that enlisting has been hoisted to here. */
+      if (newuser && rcuplist->flags) {
+        addregusertochannel(rcuplist);
+      }
+
+      args[0]=sender;
+      args[1]=rcuplist;
+      args[2]=(void *)oldflags;
+
+      triggerhook(HOOK_CHANSERV_CHANLEVMOD, args);
+
       /* Now see what we do next */
       if (rcuplist->flags) {
        /* User still valid: update or create */
        if (newuser) {
-         addregusertochannel(rcuplist);
          csdb_createchanuser(rcuplist);
        } else {
          csdb_updatechanuser(rcuplist);
        }
+       chanservstdmessage(sender, QM_CHANLEVCHANGED, cargv[1], cip->name->content, 
+                           printflags(rcuplist->flags & flagmask, rcuflags));
       } else {
        /* User has no flags: delete */
        if (!newuser) {
+         chanservstdmessage(sender, QM_CHANLEVREMOVED, cargv[1], cip->name->content);
          csdb_deletechanuser(rcuplist);
          delreguserfromchannel(rcp, target);
        }
        freeregchanuser(rcuplist);
        rcuplist=NULL;
-        for (i=0;i<REGCHANUSERHASHSIZE;i++)
-          if (rcp->regusers[i])
-            break;
-        if (i==REGCHANUSERHASHSIZE) {
-         cs_log(sender,"DELCHAN %s (Cleared chanlev)",cip->name->content);
-          cs_removechannel(rcp);       
-       }
+       if (cs_removechannelifempty(sender, rcp)) {
+         chanservstdmessage(sender, QM_CHANLEVEMPTIEDCHANNEL);
+          return CMD_OK;
+        }
       }
-
-      /* Say we've done it */
-      chanservstdmessage(sender, QM_DONE);
+      
+      /* Update the channel if needed */
       rcp->status |= QCSTAT_OPCHECK;
       cs_timerfunc(cip);
-    }
-    
-    if (rcuplist && (rcuplist->flags & flagmask)) {
-      chanservstdmessage(sender, QM_CHANUSERFLAGS, cargv[1], cip->name->content, 
-                        printflags(rcuplist->flags & flagmask, rcuflags));
     } else {
-      chanservstdmessage(sender, QM_CHANUSERUNKNOWN, cargv[1], cip->name->content);
+      if (rcuplist == rcup)
+        flagmask |= QCUFLAGS_PERSONAL;
+      if (rcuplist && (rcuplist->flags & flagmask)) {
+        chanservstdmessage(sender, QM_CHANUSERFLAGS, cargv[1], cip->name->content, 
+                           printflags(rcuplist->flags & flagmask, rcuflags));
+      } else {
+        chanservstdmessage(sender, QM_CHANUSERUNKNOWN, cargv[1], cip->name->content);
+      }
     }
   }
+
   
   return CMD_OK;
 }