]> jfr.im git - irc/quakenet/newserv.git/blame - a4stats/a4stats_db.c
A4STATS: remove E style escapes and switch to createtable for indices
[irc/quakenet/newserv.git] / a4stats / a4stats_db.c
CommitLineData
e7e7312d 1#define _GNU_SOURCE
d5c004ba
GB
2#include <stdio.h>
3#include <stdarg.h>
e7e7312d 4#include <string.h>
d5c004ba
GB
5#include "../lib/version.h"
6#include "../dbapi2/dbapi2.h"
7#include "../core/error.h"
8#include "../core/hooks.h"
9#include "../core/schedule.h"
10#include "../control/control.h"
11#include "../irc/irc.h"
12#include "../lua/lua.h"
13
5905e45f 14#define CLEANUP_KEEP 10 /* keep this many topics and kicks per channel around */
ef90464f 15#define CLEANUP_INTERVAL 86400 /* db cleanup interval (in seconds) */
82645134 16#define CLEANUP_INACTIVE_DAYS 30 /* disable channels where nothing happened for this many days */
5af6ffcf 17#define CLEANUP_DELETE_DAYS 5 /* delete data for channels that have been disabled for this many days */
5905e45f 18
7004a170 19#define A4STATS_DB_TOLOWER(x) "translate(lower(" x "), '[]\\~', '{}|^')"
a0b4a0cf
TS
20#define A4STATS_DB_EQ_NOCASE(x, y) A4STATS_DB_TOLOWER(x) " = " A4STATS_DB_TOLOWER(y)
21
d5c004ba
GB
22MODULE_VERSION("");
23
24DBAPIConn *a4statsdb;
25
26static int a4stats_connectdb(void) {
27 if(!a4statsdb) {
cf5ac373 28 a4statsdb = dbapi2open("pqsql", "a4stats");
d5c004ba
GB
29 if(!a4statsdb) {
30 Error("a4stats", ERR_WARNING, "Unable to connect to db -- not loaded.");
31 return 0;
32 }
33 }
34
35 a4statsdb->createtable(a4statsdb, NULL, NULL,
53c0b4a2 36 "CREATE TABLE ? (id SERIAL PRIMARY KEY, name VARCHAR(256) UNIQUE, timestamp INT DEFAULT 0, active INT DEFAULT 1, deleted INT DEFAULT 0, privacy INT DEFAULT 1, "
92898969
GB
37 "h0 INT DEFAULT 0, h1 INT DEFAULT 0, h2 INT DEFAULT 0, h3 INT DEFAULT 0, h4 INT DEFAULT 0, h5 INT DEFAULT 0, "
38 "h6 INT DEFAULT 0, h7 INT DEFAULT 0, h8 INT DEFAULT 0, h9 INT DEFAULT 0, h10 INT DEFAULT 0, h11 INT DEFAULT 0, "
39 "h12 INT DEFAULT 0, h13 INT DEFAULT 0, h14 INT DEFAULT 0, h15 INT DEFAULT 0, h16 INT DEFAULT 0, h17 INT DEFAULT 0, "
40 "h18 INT DEFAULT 0, h19 INT DEFAULT 0, h20 INT DEFAULT 0, h21 INT DEFAULT 0, h22 INT DEFAULT 0, h23 INT DEFAULT 0)", "T", "channels");
d5c004ba 41
f79faea1 42 a4statsdb->createtable(a4statsdb, NULL, NULL,
5af6ffcf
TS
43 "CREATE TABLE ? (channelid INT, kicker VARCHAR(128), kickerid INT, victim VARCHAR(128), victimid INT, timestamp INT, reason VARCHAR(256),"
44 "FOREIGN KEY (channelid) REFERENCES ? (id) ON DELETE CASCADE)", "TT", "kicks", "channels");
f79faea1 45
7004a170
CP
46 a4statsdb->createtable(a4statsdb, NULL, NULL, "CREATE INDEX kicks_channelid_index ON ? (channelid)", "T", "kicks");
47 a4statsdb->createtable(a4statsdb, NULL, NULL, "CREATE INDEX kicks_timestamp_index ON ? (timestamp)", "T", "kicks");
d5c004ba
GB
48
49 a4statsdb->createtable(a4statsdb, NULL, NULL,
5af6ffcf
TS
50 "CREATE TABLE ? (channelid INT, setby VARCHAR(128), setbyid INT, timestamp INT, topic VARCHAR(512),"
51 "FOREIGN KEY (channelid) REFERENCES ? (id) ON DELETE CASCADE)", "TT", "topics", "channels");
d5c004ba 52
7004a170 53 a4statsdb->createtable(a4statsdb, NULL, NULL, "CREATE INDEX topics_channelid_index ON ? (channelid)", "T", "topics");
d5c004ba
GB
54
55 a4statsdb->createtable(a4statsdb, NULL, NULL,
cf5ac373 56 "CREATE TABLE ? (channelid INT, account VARCHAR(128), accountid INT, seen INT DEFAULT 0, rating INT DEFAULT 0, lines INT DEFAULT 0, chars INT DEFAULT 0, words INT DEFAULT 0, "
f79faea1
GB
57 "h0 INT DEFAULT 0, h1 INT DEFAULT 0, h2 INT DEFAULT 0, h3 INT DEFAULT 0, h4 INT DEFAULT 0, h5 INT DEFAULT 0, "
58 "h6 INT DEFAULT 0, h7 INT DEFAULT 0, h8 INT DEFAULT 0, h9 INT DEFAULT 0, h10 INT DEFAULT 0, h11 INT DEFAULT 0, "
59 "h12 INT DEFAULT 0, h13 INT DEFAULT 0, h14 INT DEFAULT 0, h15 INT DEFAULT 0, h16 INT DEFAULT 0, h17 INT DEFAULT 0, "
60 "h18 INT DEFAULT 0, h19 INT DEFAULT 0, h20 INT DEFAULT 0, h21 INT DEFAULT 0, h22 INT DEFAULT 0, h23 INT DEFAULT 0, "
e0311e96 61 "last VARCHAR(512), quote VARCHAR(512), quotereset INT DEFAULT 0, mood_happy INT DEFAULT 0, mood_sad INT DEFAULT 0, questions INT DEFAULT 0, yelling INT DEFAULT 0, caps INT DEFAULT 0, "
d5c004ba 62 "slaps INT DEFAULT 0, slapped INT DEFAULT 0, highlights INT DEFAULT 0, kicks INT DEFAULT 0, kicked INT DEFAULT 0, ops INT DEFAULT 0, deops INT DEFAULT 0, actions INT DEFAULT 0, skitzo INT DEFAULT 0, foul INT DEFAULT 0, "
5af6ffcf 63 "firstseen INT DEFAULT 0, curnick VARCHAR(16), FOREIGN KEY (channelid) REFERENCES ? (id) ON DELETE CASCADE)", "TT", "users", "channels");
d5c004ba 64
7004a170
CP
65 a4statsdb->createtable(a4statsdb, NULL, NULL, "CREATE INDEX users_account_index ON ? (account)", "T", "users");
66 a4statsdb->createtable(a4statsdb, NULL, NULL, "CREATE INDEX users_accountid_index ON ? (accountid)", "T", "users");
67 a4statsdb->createtable(a4statsdb, NULL, NULL, "CREATE INDEX users_channelid_index ON ? (channelid)", "T", "users");
68 a4statsdb->createtable(a4statsdb, NULL, NULL, "CREATE UNIQUE INDEX users_channelid_account_accountid_index ON ? (channelid, account, accountid)", "T", "users");
69 a4statsdb->createtable(a4statsdb, NULL, NULL, "CREATE INDEX users_channelid_lines_index ON ? (channelid, lines)", "T", "users");
d5c004ba 70
28d6a377 71 a4statsdb->createtable(a4statsdb, NULL, NULL,
5af6ffcf
TS
72 "CREATE TABLE ? (channelid INT, first VARCHAR(128), firstid INT, second VARCHAR(128), secondid INT, seen INT, score INT DEFAULT 1,"
73 "FOREIGN KEY (channelid) REFERENCES ? (id) ON DELETE CASCADE)", "TT", "relations", "channels");
28d6a377 74
7004a170
CP
75 a4statsdb->createtable(a4statsdb, NULL, NULL, "CREATE INDEX relations_channelid_index ON ? (channelid)", "T", "relations");
76 a4statsdb->createtable(a4statsdb, NULL, NULL, "CREATE INDEX relations_score_index ON ? (score)", "T", "relations");
8d6e0508
GB
77
78
d5c004ba
GB
79 return 1;
80}
81
82static void a4stats_closedb(void) {
83 if(!a4statsdb)
84 return;
85
86 a4statsdb->close(a4statsdb);
87 a4statsdb = NULL;
88}
89
90static int a4stats_lua_escape_string(lua_State *ps) {
91 const char *input;
92 char *buf, *buf2;
93 size_t len, i, o;
94
95 if (!lua_isstring(ps, 1))
96 LUA_RETURN(ps, LUA_FAIL);
97
98 input = lua_tostring(ps, 1);
99 len = strlen(input);
100
101 buf = malloc(len * 2 + 1);
102
103 if (!buf)
104 LUA_RETURN(ps, LUA_FAIL);
105
106 a4statsdb->escapestring(a4statsdb, buf, input, len);
107
108 buf2 = malloc(len * 4 + 1);
109
110 if (!buf2) {
111 free(buf);
112 LUA_RETURN(ps, LUA_FAIL);
113 }
114
115 /* escape "?" */
116 o = 0;
117 for (i = 0; buf[i]; i++) {
118 if (buf[i] == '?') {
119 buf2[i + o] = '\\';
120 o++;
121 }
122
123 buf2[i + o] = buf[i];
124 }
125 buf2[i + o] = '\0';
126
127 free(buf);
128
129 lua_pushstring(ps, buf2);
130
131 free(buf2);
132
133 return 1;
134}
135
f79faea1
GB
136typedef struct db_callback_info {
137 struct db_callback_info *next;
d5c004ba
GB
138
139 lua_State *interp;
140 char callback[64];
141 int uarg_index;
f79faea1
GB
142} db_callback_info;
143
144static db_callback_info *dci_head;
145
146static void a4stats_delete_dci(db_callback_info *dci) {
5905e45f 147 db_callback_info **pnext;
d5c004ba 148
f79faea1
GB
149 for (pnext = &dci_head; *pnext; pnext = &((*pnext)->next)) {
150 if (*pnext == dci) {
151 *pnext = dci->next;
152 break;
153 }
154 }
155
156 free(dci);
157}
d5c004ba 158
d5c004ba
GB
159typedef struct user_update_info {
160 int stage;
161 char *update;
f79faea1 162 unsigned long channelid;
d5c004ba
GB
163 char *account;
164 unsigned long accountid;
165} user_update_info;
166
167static void a4stats_update_user_cb(const struct DBAPIResult *result, void *uarg) {
168 user_update_info *uui = uarg;
169
170 uui->stage++;
171
e6a4eec1 172 if (uui->stage == 1 || (result != NULL && uui->stage == 3))
f79faea1 173 a4statsdb->query(a4statsdb, a4stats_update_user_cb, uui, uui->update, "TUUs", "users", uui->channelid, uui->accountid, uui->account);
d5c004ba 174 else {
e6a4eec1 175 if (result == NULL || result->affected > 0 || uui->stage == 4) {
424b0e83 176 if (result == NULL || (result->affected == 0 && uui->stage == 4))
d5c004ba
GB
177 Error("a4stats", ERR_WARNING, "Unable to update user.");
178
179 free(uui->update);
d5c004ba
GB
180 free(uui->account);
181 free(uui);
316ff6c6 182 goto a4_uuc_return;
d5c004ba
GB
183 }
184
f79faea1 185 a4statsdb->query(a4statsdb, a4stats_update_user_cb, uui, "INSERT INTO ? (channelid, account, accountid, firstseen) VALUES (?, ?, ?, ?)", "TUsUt", "users", uui->channelid, uui->account, uui->accountid, time(NULL));
d5c004ba 186 }
316ff6c6
TS
187
188a4_uuc_return:
189 if (result)
190 result->clear(result);
d5c004ba
GB
191}
192
193static int a4stats_lua_update_user(lua_State *ps) {
f79faea1
GB
194 const char *account;
195 unsigned long channelid, accountid;
d5c004ba
GB
196 char query[4096];
197 int first = 1;
198 user_update_info *uui;
199
f79faea1 200 if (!lua_isnumber(ps, 1) || !lua_isstring(ps, 2) || !lua_isnumber(ps, 3))
d5c004ba
GB
201 LUA_RETURN(ps, LUA_FAIL);
202
f79faea1 203 channelid = lua_tonumber(ps, 1);
d5c004ba
GB
204 account = lua_tostring(ps, 2);
205 accountid = lua_tonumber(ps, 3);
206
207 strcpy(query, "UPDATE ? SET ");
208
209 lua_pushvalue(ps, 4);
210 lua_pushnil(ps);
211
212 while (lua_next(ps, -2)) {
213 const char *value = lua_tostring(ps, -1);
214
215 if (first)
216 first = 0;
217 else
218 strcat(query, ", ");
219
220 strcat(query, value);
221
222 lua_pop(ps, 1);
223 }
224
225 lua_pop(ps, 1);
226
f79faea1 227 strcat(query, " WHERE channelid = ? AND (accountid != 0 AND accountid = ? OR accountid = 0 AND account = ?)");
d5c004ba
GB
228
229 uui = malloc(sizeof(*uui));
230 uui->stage = 0;
231 uui->update = strdup(query);
f79faea1 232 uui->channelid = channelid;
d5c004ba
GB
233 uui->account = strdup(account);
234 uui->accountid = accountid;
235
236 a4stats_update_user_cb(NULL, uui);
237
238 LUA_RETURN(ps, LUA_OK);
239}
240
28d6a377
GB
241typedef struct relation_update_info {
242 int stage;
243 unsigned long channelid;
244 char *first;
245 unsigned long firstid;
246 char *second;
247 unsigned long secondid;
248} relation_update_info;
249
250static void a4stats_update_relation_cb(const struct DBAPIResult *result, void *uarg) {
251 relation_update_info *rui = uarg;
252
253 rui->stage++;
254
255 if (rui->stage == 1) {
256 a4statsdb->query(a4statsdb, a4stats_update_relation_cb, rui, "UPDATE ? SET score = score + 1, seen = ? "
257 "WHERE channelid = ? AND first = ? AND firstid = ? AND second = ? AND secondid = ?",
258 "TtUsUsU", "relations", time(NULL), rui->channelid, rui->first, rui->firstid, rui->second, rui->secondid);
316ff6c6 259 goto a4_urc_return;
28d6a377
GB
260 } else if (rui->stage == 2 && result && result->affected == 0) {
261 a4statsdb->query(a4statsdb, a4stats_update_relation_cb, rui, "INSERT INTO ? (channelid, first, firstid, second, secondid, seen) VALUES (?, ?, ?, ?, ?, ?)",
262 "TUsUsUt", "relations", rui->channelid, rui->first, rui->firstid, rui->second, rui->secondid, time(NULL));
316ff6c6 263 goto a4_urc_return;
28d6a377
GB
264 }
265
266 if (!result || result->affected == 0)
267 Error("a4stats", ERR_WARNING, "Unable to update relation.");
268
269 free(rui->first);
270 free(rui->second);
271 free(rui);
316ff6c6
TS
272
273a4_urc_return:
274 if (result)
275 result->clear(result);
28d6a377
GB
276}
277
278static int a4stats_lua_update_relation(lua_State *ps) {
279 const char *user1, *user2;
280 unsigned long channelid, user1id, user2id;
281 relation_update_info *rui;
282
283 if (!lua_isnumber(ps, 1) || !lua_isstring(ps, 2) || !lua_isnumber(ps, 3) || !lua_isstring(ps, 4) || !lua_isnumber(ps, 5))
284 LUA_RETURN(ps, LUA_FAIL);
285
286 channelid = lua_tonumber(ps, 1);
287 user1 = lua_tostring(ps, 2);
288 user1id = lua_tonumber(ps, 3);
289 user2 = lua_tostring(ps, 4);
290 user2id = lua_tonumber(ps, 5);
291
292 rui = malloc(sizeof(*rui));
293 rui->stage = 0;
294 rui->channelid = channelid;
295
296 if (user1id < user2id || (user1id == user2id && strcmp(user1, user2) <= 0)) {
297 rui->first = strdup(user1);
298 rui->firstid = user1id;
299 rui->second = strdup(user2);
300 rui->secondid = user2id;
301 } else {
302 rui->first = strdup(user2);
303 rui->firstid = user2id;
304 rui->second = strdup(user1);
305 rui->secondid = user1id;
306 }
307
308 a4stats_update_relation_cb(NULL, rui);
309
310 LUA_RETURN(ps, LUA_OK);
311}
312
92898969
GB
313static int a4stats_lua_add_line(lua_State *ps) {
314 char query[256];
315 const char *channel;
316 int hour;
317
318 if (!lua_isstring(ps, 1) || !lua_isnumber(ps, 2))
319 LUA_RETURN(ps, LUA_FAIL);
320
321 channel = lua_tostring(ps, 1);
322 hour = lua_tonumber(ps, 2);
323
a0b4a0cf 324 snprintf(query, sizeof(query), "UPDATE ? SET h%d = h%d + 1 WHERE " A4STATS_DB_EQ_NOCASE("name", "?"), hour, hour);
92898969
GB
325
326 a4statsdb->squery(a4statsdb, query, "Ts", "channels", channel);
327
328 LUA_RETURN(ps, LUA_OK);
329}
330
5af6ffcf
TS
331static struct {
332 time_t start;
82645134
TS
333 unsigned long pending;
334 unsigned long topicskicks;
335 unsigned long disabled;
5af6ffcf 336 unsigned long deleted;
82645134
TS
337} cleanupdata;
338
5af6ffcf
TS
339static void a4stats_cleanupdb_got_result(void) {
340 if (!--cleanupdata.pending) {
23c149d9 341 controlwall(NO_OPER, NL_CLEANUP, "CLEANUPA4STATS: Deleted %lu old topics and kicks. Disabled %lu inactive channels. Deleted data for %lu channels.",
5af6ffcf
TS
342 cleanupdata.topicskicks, cleanupdata.disabled, cleanupdata.deleted);
343 cleanupdata.start = 0;
344 }
82645134
TS
345}
346
5af6ffcf
TS
347static void a4stats_cleanupdb_cb_countrows(const struct DBAPIResult *result, void *arg) {
348 unsigned long *counter = arg;
82645134 349
8a12ea21 350 if (result) {
5af6ffcf 351 *counter += result->affected;
8a12ea21
TS
352 result->clear(result);
353 }
82645134 354
5af6ffcf 355 a4stats_cleanupdb_got_result();
5905e45f
TS
356}
357
5af6ffcf 358static void a4stats_cleanupdb_cb_active(const struct DBAPIResult *result, void *null) {
82645134
TS
359 unsigned long channelid;
360 time_t seen;
8a12ea21
TS
361
362 if (!result)
363 return;
364
365 if (result->success) {
5905e45f
TS
366 while (result->next(result)) {
367 channelid = strtoul(result->get(result, 0), NULL, 10);
82645134
TS
368 seen = (time_t)strtoul(result->get(result, 2), NULL, 10);
369 /* use channel enabling timestamp if there was never any event */
370 if (!seen)
371 seen = (time_t)strtoul(result->get(result, 1), NULL, 10);
372
5af6ffcf
TS
373 if (seen < cleanupdata.start - CLEANUP_INACTIVE_DAYS * 86400) {
374 /* disable inactive channels */
375 cleanupdata.pending++;
376 a4statsdb->query(a4statsdb, a4stats_cleanupdb_cb_countrows, &cleanupdata.disabled,
82645134 377 "UPDATE ? SET active = 0, deleted = ? WHERE id = ? AND active = 1",
5af6ffcf 378 "TtU", "channels", cleanupdata.start, channelid);
82645134
TS
379 } else {
380 /* cleanup old kicks/topics */
5af6ffcf
TS
381 cleanupdata.pending++;
382 a4statsdb->query(a4statsdb, a4stats_cleanupdb_cb_countrows, &cleanupdata.topicskicks,
82645134
TS
383 "DELETE FROM ? WHERE channelid = ? AND timestamp <= "
384 "(SELECT timestamp FROM ? WHERE channelid = ? ORDER BY timestamp DESC OFFSET ? LIMIT 1)",
385 "TUTUU", "kicks", channelid, "kicks", channelid, (unsigned long)CLEANUP_KEEP);
5af6ffcf
TS
386 cleanupdata.pending++;
387 a4statsdb->query(a4statsdb, a4stats_cleanupdb_cb_countrows, &cleanupdata.topicskicks,
82645134
TS
388 "DELETE FROM ? WHERE channelid = ? AND timestamp <= "
389 "(SELECT timestamp FROM ? WHERE channelid = ? ORDER BY timestamp DESC OFFSET ? LIMIT 1)",
390 "TUTUU", "topics", channelid, "topics", channelid, (unsigned long)CLEANUP_KEEP);
391 }
5905e45f
TS
392 }
393 }
8a12ea21 394 result->clear(result);
5905e45f
TS
395}
396
397static void a4stats_cleanupdb(void *null) {
5af6ffcf 398 controlwall(NO_OPER, NL_CLEANUP, "Starting a4stats_db cleanup.");
82645134 399
5af6ffcf
TS
400 if (cleanupdata.start != 0) {
401 controlwall(NO_OPER, NL_CLEANUP, "a4stats cleanup already in progress.");
402 return;
403 }
5905e45f 404
5af6ffcf
TS
405 cleanupdata.start = time(NULL);
406 cleanupdata.pending = 0;
407 cleanupdata.topicskicks = 0;
408 cleanupdata.disabled = 0;
409 cleanupdata.deleted = 0;
410
411 a4statsdb->query(a4statsdb, a4stats_cleanupdb_cb_active, NULL,
82645134
TS
412 "SELECT id, timestamp, MAX(users.seen) FROM ? LEFT JOIN ? AS users ON id = users.channelid WHERE active = 1 GROUP BY id",
413 "TT", "channels", "users");
5af6ffcf
TS
414 cleanupdata.pending++;
415 a4statsdb->query(a4statsdb, a4stats_cleanupdb_cb_countrows, &cleanupdata.deleted,
416 "DELETE FROM ? WHERE active = 0 AND deleted < ?", "Tt", "channels", (time_t)(cleanupdata.start - CLEANUP_DELETE_DAYS * 84600));
5905e45f
TS
417}
418
f79faea1
GB
419static void a4stats_fetch_channels_cb(const struct DBAPIResult *result, void *uarg) {
420 db_callback_info *dci = uarg;
421 unsigned long channelid;
422 int active;
423 char *channel;
424
425 if (result) {
426 if (result->success) {
427 while (result->next(result)) {
428 channelid = strtoul(result->get(result, 0), NULL, 10);
429 channel = result->get(result, 1);
430 active = atoi(result->get(result, 2));
431
432 if (dci->interp)
cf5ac373 433 lua_vpcall(dci->interp, dci->callback, "lsiR", channelid, channel, active, dci->uarg_index);
f79faea1
GB
434 }
435 }
436
437 result->clear(result);
438 }
439
440 if (dci->interp)
441 luaL_unref(dci->interp, LUA_REGISTRYINDEX, dci->uarg_index);
442
443 a4stats_delete_dci(dci);
444}
445
446static int a4stats_lua_fetch_channels(lua_State *ps) {
447 const char *callback;
448 db_callback_info *dci;
449
450 if (!lua_isstring(ps, 1))
451 LUA_RETURN(ps, LUA_FAIL);
452
453 callback = lua_tostring(ps, 1);
454
455 dci = malloc(sizeof(*dci));
456 dci->interp = ps;
457
458 strncpy(dci->callback, callback, sizeof(dci->callback));
459 dci->callback[sizeof(dci->callback) - 1] = '\0';
460
461 lua_pushvalue(ps, 2);
462 dci->uarg_index = luaL_ref(ps, LUA_REGISTRYINDEX);
463
464 a4statsdb->query(a4statsdb, a4stats_fetch_channels_cb, dci, "SELECT id, name, active FROM ?", "T", "channels");
465
466 LUA_RETURN(ps, LUA_OK);
467}
468
5bf3ab9d 469static int a4stats_lua_enable_channel(lua_State *ps) {
f79faea1
GB
470 if (!lua_isstring(ps, 1))
471 LUA_RETURN(ps, LUA_FAIL);
472
899f2ee4 473 a4statsdb->squery(a4statsdb, "INSERT INTO ? (name, timestamp) VALUES (?, ?)", "Tst", "channels", lua_tostring(ps, 1), time(NULL));
a0b4a0cf 474 a4statsdb->squery(a4statsdb, "UPDATE ? SET active = 1, deleted = 0 WHERE " A4STATS_DB_EQ_NOCASE("name", "?"), "Ts", "channels", lua_tostring(ps, 1));
5bf3ab9d
GB
475
476 LUA_RETURN(ps, LUA_OK);
477}
478
479static int a4stats_lua_disable_channel(lua_State *ps) {
480 if (!lua_isstring(ps, 1))
481 LUA_RETURN(ps, LUA_FAIL);
482
a0b4a0cf 483 a4statsdb->squery(a4statsdb, "UPDATE ? SET active = 0, deleted = ? WHERE " A4STATS_DB_EQ_NOCASE("name", "?"), "Tts", "channels", time(NULL), lua_tostring(ps, 1));
f79faea1
GB
484
485 LUA_RETURN(ps, LUA_OK);
486}
487
1b086b45
TS
488static int a4stats_lua_set_privacy(lua_State *ps) {
489 const char *channel;
490 unsigned long privacy;
491
492 if (!lua_isstring(ps, 1) || !lua_isnumber(ps, 2))
493 LUA_RETURN(ps, LUA_FAIL);
494
495 channel = lua_tostring(ps, 1);
496 privacy = lua_tonumber(ps, 2);
497
a0b4a0cf 498 a4statsdb->squery(a4statsdb, "UPDATE ? SET privacy = ? WHERE " A4STATS_DB_EQ_NOCASE("name", "?"), "TUs", "channels", privacy, channel);
1b086b45
TS
499 LUA_RETURN(ps, LUA_OK);
500}
501
d5c004ba 502static int a4stats_lua_add_kick(lua_State *ps) {
f79faea1
GB
503 unsigned long channelid, kickerid, victimid;
504 const char *kicker, *victim, *reason;
505
506 if (!lua_isnumber(ps, 1) || !lua_isstring(ps, 2) || !lua_isnumber(ps, 3) || !lua_isstring(ps, 4) || !lua_isnumber(ps, 5) || !lua_isstring(ps, 6))
d5c004ba
GB
507 LUA_RETURN(ps, LUA_FAIL);
508
f79faea1
GB
509 channelid = lua_tonumber(ps, 1);
510 kicker = lua_tostring(ps, 2);
511 kickerid = lua_tonumber(ps, 3);
512 victim = lua_tostring(ps, 4);
513 victimid = lua_tonumber(ps, 5);
514 reason = lua_tostring(ps, 6);
515
516 a4statsdb->squery(a4statsdb, "INSERT INTO ? (channelid, kicker, kickerid, victim, victimid, timestamp, reason) VALUES (?, ?, ?, ?, ?, ?, ?)", "TUsUsUts",
517 "kicks", channelid, kicker, kickerid, victim, victimid, time(NULL), reason);
d5c004ba
GB
518
519 LUA_RETURN(ps, LUA_OK);
520}
521
522static int a4stats_lua_add_topic(lua_State *ps) {
f79faea1
GB
523 unsigned long channelid, setbyid;
524 const char *topic, *setby;
525
526 if (!lua_isnumber(ps, 1) || !lua_isstring(ps, 2) || !lua_isstring(ps, 3) || !lua_isnumber(ps, 4))
d5c004ba
GB
527 LUA_RETURN(ps, LUA_FAIL);
528
f79faea1
GB
529 channelid = lua_tonumber(ps, 1);
530 topic = lua_tostring(ps, 2);
531 setby = lua_tostring(ps, 3);
532 setbyid = lua_tonumber(ps, 4);
533
534 a4statsdb->squery(a4statsdb, "INSERT INTO ? (channelid, topic, timestamp, setby, setbyid) VALUES (?, ?, ?, ?, ?)", "TUstsU",
535 "topics", channelid, topic, time(NULL), setby, setbyid);
d5c004ba
GB
536
537 LUA_RETURN(ps, LUA_OK);
538}
539
65c41787
TS
540static int a4stats_lua_db_begin(lua_State *ps) {
541 a4statsdb->query(a4statsdb, NULL, NULL, "BEGIN TRANSACTION", "");
542 LUA_RETURN(ps, LUA_OK);
543}
544
545static int a4stats_lua_db_commit(lua_State *ps) {
546 a4statsdb->query(a4statsdb, NULL, NULL, "COMMIT TRANSACTION", "");
547 LUA_RETURN(ps, LUA_OK);
548}
549
d5c004ba
GB
550static void a4stats_hook_loadscript(int hooknum, void *arg) {
551 void **args = arg;
552 lua_State *l = args[1];
553
5bf3ab9d
GB
554 lua_register(l, "a4_enable_channel", a4stats_lua_enable_channel);
555 lua_register(l, "a4_disable_channel", a4stats_lua_disable_channel);
1b086b45 556 lua_register(l, "a4_set_privacy", a4stats_lua_set_privacy);
f79faea1 557 lua_register(l, "a4_fetch_channels", a4stats_lua_fetch_channels);
d5c004ba
GB
558 lua_register(l, "a4_add_kick", a4stats_lua_add_kick);
559 lua_register(l, "a4_add_topic", a4stats_lua_add_topic);
92898969 560 lua_register(l, "a4_add_line", a4stats_lua_add_line);
d5c004ba 561 lua_register(l, "a4_update_user", a4stats_lua_update_user);
28d6a377 562 lua_register(l, "a4_update_relation", a4stats_lua_update_relation);
d5c004ba 563 lua_register(l, "a4_escape_string", a4stats_lua_escape_string);
65c41787
TS
564 lua_register(l, "a4_db_begin", a4stats_lua_db_begin);
565 lua_register(l, "a4_db_commit", a4stats_lua_db_commit);
d5c004ba
GB
566}
567
568#define lua_unregister(L, n) (lua_pushnil(L), lua_setglobal(L, n))
569
570static void a4stats_hook_unloadscript(int hooknum, void *arg) {
f79faea1 571 db_callback_info **pnext, *dci;
899f2ee4 572 lua_State *l = arg;
d5c004ba 573
f79faea1
GB
574 for (pnext = &dci_head; *pnext; pnext = &((*pnext)->next)) {
575 dci = *pnext;
899f2ee4 576 if (dci->interp == l) {
f79faea1
GB
577 *pnext = dci->next;
578 free(dci);
d5c004ba
GB
579 }
580 }
d5c004ba
GB
581}
582
583void _init(void) {
584 lua_list *l;
585 void *args[2];
586
587 a4stats_connectdb();
588
589 registerhook(HOOK_LUA_LOADSCRIPT, a4stats_hook_loadscript);
590 registerhook(HOOK_LUA_UNLOADSCRIPT, a4stats_hook_unloadscript);
5905e45f 591 schedulerecurring(time(NULL), 0, CLEANUP_INTERVAL, a4stats_cleanupdb, NULL);
d5c004ba
GB
592
593 args[0] = NULL;
594 for (l = lua_head; l;l = l->next) {
595 args[1] = l->l;
596 a4stats_hook_loadscript(HOOK_LUA_LOADSCRIPT, args);
597 }
598}
599
600void _fini(void) {
f79faea1
GB
601 lua_list *l;
602
5905e45f 603 deleteschedule(NULL, a4stats_cleanupdb, NULL);
d5c004ba
GB
604 a4stats_closedb();
605
f79faea1 606 for (l = lua_head; l;l = l->next) {
064db467 607 a4stats_hook_unloadscript(HOOK_LUA_UNLOADSCRIPT, l->l);
899f2ee4
GB
608
609 lua_unregister(l->l, "a4_enable_channel");
610 lua_unregister(l->l, "a4_disable_channel");
1b086b45 611 lua_unregister(l->l, "a4_set_privacy");
899f2ee4
GB
612 lua_unregister(l->l, "a4_fetch_channels");
613 lua_unregister(l->l, "a4_add_kick");
614 lua_unregister(l->l, "a4_add_topic");
92898969 615 lua_unregister(l->l, "a4_add_line");
899f2ee4
GB
616 lua_unregister(l->l, "a4_fetch_user");
617 lua_unregister(l->l, "a4_update_user");
28d6a377 618 lua_unregister(l->l, "a4_update_relation");
899f2ee4 619 lua_unregister(l->l, "a4_escape_string");
65c41787
TS
620 lua_unregister(l->l, "a4_db_begin");
621 lua_unregister(l->l, "a4_db_commit");
f79faea1
GB
622 }
623
d5c004ba
GB
624 deregisterhook(HOOK_LUA_LOADSCRIPT, a4stats_hook_loadscript);
625 deregisterhook(HOOK_LUA_UNLOADSCRIPT, a4stats_hook_unloadscript);
626}
627