]> jfr.im git - irc/quakenet/newserv.git/blobdiff - chanserv/usercmds/cleanupdb.c
CHANSERV: only update channels as active if they have at least one known op, and...
[irc/quakenet/newserv.git] / chanserv / usercmds / cleanupdb.c
index 70785719ed97a57fb6cdb4f3b4a011d33da1b4a5..0dfb3e1052cc4b7e45cd2ba71618beaf18e66651 100644 (file)
@@ -8,8 +8,7 @@
  * CMDFUNC: csu_docleanupdb
  * CMDPROTO: int csu_docleanupdb(void *source, int cargc, char **cargv);
  * CMDHELP: Usage: cleanupdb
- * CMDHELP: Removes unused and never used accounts that exceed the idleness
- * CMDHELP: thresholds.
+ * CMDHELP: Cleans up inactive accounts, unused accounts and inactive channels.
  */
 
 #include "../chanserv.h"
 #include <stdio.h>
 #include <string.h>
 
-int csu_docleanupdb(void *source, int cargc, char **cargv) {
-  nick *sender=source;
-  reguser *vrup, *srup;
-  authname *anp;
-  int i;
-  time_t t;
-  long to_age, unused_age;
-  int expired = 0, unauthed = 0;
+unsigned int cleanupdb_active;
 
+void csu_docleanupdb_real(DBConn *dbconn, void *arg) {
+  nick *sender=getnickbynumeric((unsigned long)arg);
+  reguser *vrup, *srup, *founder;
+  regchanuser *rcup, *nrcup;
+  authname *anp;
+  int i,j;
+  time_t t, to_age, unused_age, maxchan_age, authhistory_age;
+  int expired = 0, unauthed = 0, chansvaped = 0;
+  chanindex *cip, *ncip;
+  regchan *rcp;
+  DBResult *pgres;
+  unsigned int themarker;
+  unsigned int id;
+  
   t = time(NULL);
-  to_age = t - (80 * 3600 * 24);  
-  unused_age = t - (10 * 3600 * 24);
+  to_age = t - (CLEANUP_ACCOUNT_INACTIVE * 3600 * 24);  
+  unused_age = t - (CLEANUP_ACCOUNT_UNUSED * 3600 * 24);
+  maxchan_age = t - (CLEANUP_CHANNEL_INACTIVE * 3600 * 24);
+  authhistory_age = t - (CLEANUP_AUTHHISTORY * 3600 * 24);
+
+  themarker=nextauthnamemarker();
+  
+  if (!dbconn) {
+    if (sender)
+      chanservsendmessage(sender, "No DB connection, aborting cleanup.");
+    goto out;
+  }
+
+  pgres=dbgetresult(dbconn);
+  
+  if (!dbquerysuccessful(pgres)) {
+    Error("chanserv", ERR_ERROR, "Error loading cleanupdb data.");
+    if (sender) 
+      chanservsendmessage(sender, "DB error, aborting cleanup.");
+    goto out;
+  }
+  
+  while (dbfetchrow(pgres)) {
+    id=strtoul(dbgetvalue(pgres, 0), NULL, 10);
+    anp=findauthname(id);
+    if (anp)
+      anp->marker=themarker;
+  }
+  
+  dbclear(pgres);
 
+  cs_log(sender, "CLEANUPDB started");
+
+  if (sender)
+    chanservsendmessage(sender, "Scanning regusers...");
   for (i=0;i<REGUSERHASHSIZE;i++) {
     for (vrup=regusernicktable[i]; vrup; vrup=srup) {
       srup=vrup->nextbyname;
       if (!(anp=findauthname(vrup->ID)))
         continue; /* should maybe raise hell instead */
-        
-      if(!anp->nicks && !UHasHelperPriv(vrup) && !UIsCleanupExempt(vrup)) {
+
+      /* If this user has the right marker, this means the authtracker data
+       * indicates that they have been active recently */
+      if (anp->marker == themarker)
+        continue;
+
+      if(!anp->nicks && !UHasStaffPriv(vrup) && !UIsCleanupExempt(vrup)) {
         if(vrup->lastauth && (vrup->lastauth < to_age)) {
           expired++;
+          cs_log(sender, "CLEANUPDB inactive user %s %u", vrup->username, vrup->ID);
         } else if(!vrup->lastauth && (vrup->created < unused_age)) {
           unauthed++;
+          cs_log(sender, "CLEANUPDB unused user %s %u", vrup->username, vrup->ID);
         } else {
           continue;
         }
@@ -49,7 +94,92 @@ int csu_docleanupdb(void *source, int cargc, char **cargv) {
       }
     }
   }
+
+  if (sender)
+    chanservsendmessage(sender, "Scanning chanindicies...");
+    
+  for (i=0;i<CHANNELHASHSIZE;i++) {
+    for (cip=chantable[i];cip;cip=ncip) {
+      ncip=cip->next;
+      if (!(rcp=cip->exts[chanservext]))
+        continue;
+
+      /* there's a bug here... if no joins or modes are done within the threshold
+       * and someone leaves just before the cleanup then the channel will be nuked.
+       */
+
+      /* this is one possible soln but relies on cleanupdb being run more frequently than
+       * the threshold:
+       */ 
+      if(cip->channel && cs_ischannelactive(cip->channel, rcp)) {
+        rcp->lastactive = t;
+        if (rcp->lastcountersync < (t - COUNTERSYNCINTERVAL)) {
+          csdb_updatechannelcounters(rcp);
+          rcp->lastcountersync=t;
+        }
+      }
+
+      if(rcp->lastactive < maxchan_age) {
+        /* don't remove channels with the original founder as an oper */
+        founder=findreguserbyID(rcp->founder);
+        if(founder && UHasOperPriv(founder))
+          continue;
+
+        cs_log(sender, "CLEANUPDB inactive channel %s", cip->name?cip->name->content:"??");
+        cs_removechannel(rcp, "Channel deleted due to lack of activity.");
+        chansvaped++;
+      }
+      
+      /* Get rid of any dead chanlev entries */
+      for (j=0;j<REGCHANUSERHASHSIZE;j++) {
+        for (rcup=rcp->regusers[j];rcup;rcup=nrcup) {
+          nrcup=rcup->nextbychan;
+          
+          if (!rcup->flags) {
+            if (sender)
+              chanservsendmessage(sender, "Removing user %s from channel %s (no flags)",rcup->user->username,rcp->index->name->content);
+            csdb_deletechanuser(rcup);
+            delreguserfromchannel(rcp, rcup->user);
+          }
+        }
+      }
+    }
+  }
+  
+  if (sender)
+    chanservsendmessage(sender, "Starting history database cleanup (will run in background).");
+    
+  csdb_cleanuphistories(authhistory_age);
+  
+  cs_log(sender, "CLEANUPDB complete %d inactive accounts %d unused accounts %d channels", expired, unauthed, chansvaped);
+  if (sender)
+    chanservsendmessage(sender, "Cleanup complete, %d accounts inactive for %d days, %d accounts weren't used within %d days, %d channels were inactive for %d days.", expired, CLEANUP_ACCOUNT_INACTIVE, unauthed, CLEANUP_ACCOUNT_UNUSED, chansvaped, CLEANUP_CHANNEL_INACTIVE);
+
+out:
+  cleanupdb_active=0;
+}
+
+int csu_docleanupdb(void *source, int cargc, char **cargv) {
+  nick *sender=source;
+  unsigned int to_age;
+  
+  to_age = time(NULL) - (CLEANUP_ACCOUNT_INACTIVE * 3600 * 24);
+  
+  if (cleanupdb_active) {
+    chanservsendmessage(sender, "Cleanup already in progress.\n");
+    return CMD_ERROR;
+  }
+
+  /* This query returns a single column containing the userids of all users
+   * who have active sessions now, or sessions which ended in the last
+   * CLEANUP_ACCOUNT_INACTIVE days.  */
+  q9u_asyncquery(csu_docleanupdb_real, (void *)sender->numeric,
+    "SELECT userID from chanserv.authhistory WHERE disconnecttime=0 OR disconnecttime > %d GROUP BY userID", to_age);
+  
+  chanservsendmessage(sender, "Retrieving auth history data, cleanup will proceed when done.");
+  
+  cleanupdb_active=1;
   
-  chanservsendmessage(sender, "Cleanup complete, %d unused for 80 days, %d didn't auth within 10 days.", expired, unauthed);
   return CMD_OK;
 }
+