]> jfr.im git - irc/quakenet/newserv.git/blobdiff - chanserv/chanservuser.c
Added suspendtime fields to users and channels.
[irc/quakenet/newserv.git] / chanserv / chanservuser.c
index d4a05e85f667f05649939fbb8ff92ebec4a203e4..fbf6f5274048eeae5478884bf9373cf3a388de90 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "chanserv.h"
 
+#include "../core/hooks.h"
 #include "../core/schedule.h"
 #include "../core/config.h"
 #include "../localuser/localuser.h"
@@ -15,6 +16,7 @@
 #include "../nick/nick.h"
 #include "../parser/parser.h"
 #include "../lib/splitline.h"
+#include "../lib/irc_string.h"
 
 #include <string.h>
 #include <stdio.h>
@@ -187,7 +189,7 @@ void chanservcommandclose() {
   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;
 
@@ -197,7 +199,8 @@ void chanservaddcommand(char *command, int flags, int maxparams, CommandHandler
   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);
 }
@@ -234,7 +237,12 @@ void chanservremovecommand(char *command, CommandHandler handler) {
 
 void chanservjoinchan(channel *cp) {
   regchan *rcp;
-  
+  unsigned int i;
+  nick *np;
+  reguser *rup;
+  regchanuser *rcup;
+  flag_t themodes;
+
   /* Skip unregistered channels */
   if (!(rcp=cp->index->exts[chanservext]))
     return;
@@ -256,28 +264,52 @@ void chanservjoinchan(channel *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 */
+  themodes = (CHANMODE_DEFAULT | 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);
   }
-  
-  /* Maybe we're not joining, but the timestamp is wrong.  Fix that too. 
-   * 
-   * XXX: Actually, let's not do this until we've considered the obvious
-   * problem that this will make it impossible to re-establish a +k/+i
-   * enforced channel which has become empty...
-   */ 
-/*
+
+  /* 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, rcp->forcemodes, rcp->limit, (rcp->key)?rcp->key->content:NULL);
+    localburstontochannel(cp, NULL, rcp->ltimestamp, themodes,
+                          rcp->limit, (rcp->key)?rcp->key->content:NULL);
   }
- */
 }
 
 void chanservstdmessage(nick *np, int messageid, ... ) {
@@ -286,7 +318,7 @@ void chanservstdmessage(nick *np, int messageid, ... ) {
   int notice;
   reguser *rup;
   int language;
-  va_list va;
+  va_list va, va2;
   char *message;
   char *bp2,*bp;
   int len;
@@ -313,6 +345,7 @@ void chanservstdmessage(nick *np, int messageid, ... ) {
   }
 
   va_start(va,messageid);
+  va_copy(va2, va);
   vsnprintf(buf,5000,message,va);
   va_end(va);
 
@@ -346,9 +379,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;
@@ -392,7 +434,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;
@@ -446,22 +488,30 @@ void chanservkillstdmessage(nick *target, int messageid, ... ) {
   killuser(chanservnick, target, buf);
 }
 
-int checkmasterpassword(reguser *rup, const char *pass) {
-  if (!strncmp(rup->masterpass, pass, PASSLEN))
-    return 1;
-  return 0;
-}
-
 int checkpassword(reguser *rup, const char *pass) {
   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;
+
+  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) {
@@ -473,7 +523,6 @@ 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))) {
@@ -483,30 +532,35 @@ void cs_checknick(nick *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 && 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=time(NULL); /* questionable */
       rup->lastemailchange=0;
       rup->flags=QUFLAG_NOTICE;
       rup->languageid=0;
       rup->suspendby=0;
+      rup->suspendexp=0;
+      rup->suspendtime=0;
       rup->password[0]='\0';
-      rup->masterpass[0]='\0';
       rup->email=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);
@@ -516,16 +570,10 @@ void cs_checknick(nick *np) {
       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);
@@ -644,8 +692,11 @@ void cs_doallautomodes(nick *np) {
     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 */
+        rcup->usetime=getnettime();
+
        localsetmodeinit(&changes, rcup->chan->index->channel, chanservnick);
        if (*lp & CUMODE_OP) {
          if (!IsService(np) && (CUIsDeny(rcup) || (CIsBitch(rcup->chan) && !CUHasOpPriv(rcup))))
@@ -956,6 +1007,7 @@ 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) {
@@ -979,6 +1031,9 @@ void cs_removeuser(reguser *rup) {
     freeregchanuser(rcup);
   }
 
+  if(rup->domain)
+    delreguserfrommaildomain(rup,rup->domain);
+  freesstring(rup->localpart);
   freesstring(rup->email);
   freesstring(rup->lastuserhost);
   freesstring(rup->suspendreason);
@@ -989,7 +1044,7 @@ void cs_removeuser(reguser *rup) {
 
   removereguserfromhash(rup);
   
-  if (rup->nicks) {
+  if ((anp=findauthname(rup->ID)) && anp->nicks) {
     rup->status |= QUSTAT_DEAD;
   } else {
     freereguser(rup);
@@ -1093,16 +1148,59 @@ 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 &= ~(QCUFLAG_BANNED | QCUFLAG_QUIET | QCUFLAG_DENY);
+  
+  /* +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 */
+  if (!(flags & (QCUFLAG_OWNER | QCUFLAG_MASTER | QCUFLAG_OP | QCUFLAG_VOICE | QCUFLAG_KNOWN)))
+    flags &= ~QCUFLAGS_PERSONAL;
+  
+  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;
+  reguser *rup, *vrup = getreguserfromnick(sender);;
   nick *np;
 
   if (!str || !*str)
@@ -1116,6 +1214,14 @@ reguser *findreguser(nick *sender, const char *str) {
     }
     if (!(rup=findreguserbynick(str+1)) && sender)
       chanservstdmessage(sender, QM_UNKNOWNUSER, str);
+  } else if (*str=='&' && vrup && UHasHelperPriv(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)
@@ -1126,10 +1232,71 @@ 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.
+ */
+void cs_unbanfn(nick *sender, chanindex *cip, UnbanFN fn, void *arg, int removepermbans) {
+  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);
+        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);
+  }
+}