+static struct {
+ time_t start;
+ unsigned long pending;
+ unsigned long topicskicks;
+ unsigned long disabled;
+ unsigned long deleted;
+} cleanupdata;
+
+static void a4stats_cleanupdb_got_result(void) {
+ if (!--cleanupdata.pending) {
+ controlwall(NO_OPER, NL_CLEANUP, "CLEANUPA4STATS: Deleted %lu old topics and kicks. Disabled %lu inactive channels. Deleted data for %lu channels.",
+ cleanupdata.topicskicks, cleanupdata.disabled, cleanupdata.deleted);
+ cleanupdata.start = 0;
+ }
+}
+
+static void a4stats_cleanupdb_cb_countrows(const struct DBAPIResult *result, void *arg) {
+ unsigned long *counter = arg;
+
+ if (result) {
+ *counter += result->affected;
+ result->clear(result);
+ }
+
+ a4stats_cleanupdb_got_result();
+}
+
+static void a4stats_cleanupdb_cb_active(const struct DBAPIResult *result, void *null) {
+ unsigned long channelid;
+ time_t seen;
+
+ if (!result)
+ return;
+
+ if (result->success) {
+ while (result->next(result)) {
+ channelid = strtoul(result->get(result, 0), NULL, 10);
+ seen = (time_t)strtoul(result->get(result, 2), NULL, 10);
+ /* use channel enabling timestamp if there was never any event */
+ if (!seen)
+ seen = (time_t)strtoul(result->get(result, 1), NULL, 10);
+
+ if (seen < cleanupdata.start - CLEANUP_INACTIVE_DAYS * 86400) {
+ /* disable inactive channels */
+ cleanupdata.pending++;
+ a4statsdb->query(a4statsdb, a4stats_cleanupdb_cb_countrows, &cleanupdata.disabled,
+ "UPDATE ? SET active = 0, deleted = ? WHERE id = ? AND active = 1",
+ "TtU", "channels", cleanupdata.start, channelid);
+ } else {
+ /* cleanup old kicks/topics */
+ cleanupdata.pending++;
+ a4statsdb->query(a4statsdb, a4stats_cleanupdb_cb_countrows, &cleanupdata.topicskicks,
+ "DELETE FROM ? WHERE channelid = ? AND timestamp <= "
+ "(SELECT timestamp FROM ? WHERE channelid = ? ORDER BY timestamp DESC OFFSET ? LIMIT 1)",
+ "TUTUU", "kicks", channelid, "kicks", channelid, (unsigned long)CLEANUP_KEEP);
+ cleanupdata.pending++;
+ a4statsdb->query(a4statsdb, a4stats_cleanupdb_cb_countrows, &cleanupdata.topicskicks,
+ "DELETE FROM ? WHERE channelid = ? AND timestamp <= "
+ "(SELECT timestamp FROM ? WHERE channelid = ? ORDER BY timestamp DESC OFFSET ? LIMIT 1)",
+ "TUTUU", "topics", channelid, "topics", channelid, (unsigned long)CLEANUP_KEEP);
+ }
+ }
+ }
+ result->clear(result);
+}
+
+static void a4stats_cleanupdb(void *null) {
+ controlwall(NO_OPER, NL_CLEANUP, "Starting a4stats_db cleanup.");
+
+ if (cleanupdata.start != 0) {
+ controlwall(NO_OPER, NL_CLEANUP, "a4stats cleanup already in progress.");
+ return;
+ }
+
+ cleanupdata.start = time(NULL);
+ cleanupdata.pending = 0;
+ cleanupdata.topicskicks = 0;
+ cleanupdata.disabled = 0;
+ cleanupdata.deleted = 0;
+
+ a4statsdb->query(a4statsdb, a4stats_cleanupdb_cb_active, NULL,
+ "SELECT id, timestamp, MAX(users.seen) FROM ? LEFT JOIN ? AS users ON id = users.channelid WHERE active = 1 GROUP BY id",
+ "TT", "channels", "users");
+ cleanupdata.pending++;
+ a4statsdb->query(a4statsdb, a4stats_cleanupdb_cb_countrows, &cleanupdata.deleted,
+ "DELETE FROM ? WHERE active = 0 AND deleted < ?", "Tt", "channels", (time_t)(cleanupdata.start - CLEANUP_DELETE_DAYS * 84600));
+}
+