* 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;
}
}
}
}
+
+ 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;
}
+