]> jfr.im git - irc/quakenet/newserv.git/blob - noperserv/noperserv_fakeuser.c
use dbapi2
[irc/quakenet/newserv.git] / noperserv / noperserv_fakeuser.c
1 /*
2 * NOperserv Fakeuser module
3 *
4 * Allows fakeusers to be added so as to block nicks, for example.
5 *
6 * Copyright (c) Tim Gordon 2006.
7 */
8
9 #include "../core/schedule.h"
10 #include "../localuser/localuser.h"
11 #include "../localuser/localuserchannel.h"
12 #include "../irc/irc_config.h"
13 #include "../lib/irc_string.h"
14 #include "../control/control.h"
15 #include "../channel/channel.h"
16 #include "../dbapi2/dbapi2.h"
17 #include "../lib/strlfunc.h"
18 #include "../lib/version.h"
19 #include <string.h>
20 #include <stdlib.h>
21
22 MODULE_VERSION("");
23
24 /* #define SIT_CHANNEL "#qnet.fakeusers" */
25 #define KILL_WAIT 10
26 #define KILL_TIME 60
27
28 typedef struct fakeuser {
29 nick *user;
30 time_t lastkill;
31 struct fakeuser *next;
32 } fakeuser;
33
34 typedef struct user_details {
35 char nick[NICKLEN + 1];
36 char ident[USERLEN + 1];
37 char host[HOSTLEN + 1];
38 char realname[REALLEN + 1];
39 time_t lastkill;
40 struct user_details *next;
41 void *schedule;
42 } user_details;
43
44 fakeuser *fakeuserlist = NULL;
45 user_details *killeduserlist = NULL;
46 int fakeusercount = 0;
47 int killedusercount = 0;
48
49 void fakeuser_cleanup();
50 int fakeuser_loaddb();
51 void fakeusers_load(const DBAPIResult *res, void *arg);
52 void fakeuser_handler(nick *user, int command, void **params);
53 int fakeadd(void *sender, int cargc, char **cargv);
54 int fakelist(void *sender, int cargc, char **cargv);
55 int fakekill(void *sender, int cargc, char **cargv);
56 void reconnectfake(void *details);
57
58 int db_loaded = 0;
59 static DBAPIConn *nofudb;
60
61 void _init() {
62 if (!fakeuser_loaddb())
63 {
64 Error("noperserv_fakeuser", ERR_FATAL, "Cannot load database");
65 return;
66 }
67 registercontrolhelpcmd("fakeuser", NO_OPER, 4, &fakeadd, "Usage: FAKEUSER nick <ident> <host> <realname>\nCreates a fake user.");
68 registercontrolhelpcmd("fakelist", NO_OPER, 0, &fakelist, "Usage: FAKELIST\nLists all fake users.");
69 registercontrolhelpcmd("fakekill", NO_OPER, 2, &fakekill, "Usage: FAKEKILL nick <reason>\nRemoves a fake user");
70 }
71
72 void _fini() {
73 fakeuser_cleanup();
74 deregistercontrolcmd("fakeuser", &fakeadd);
75 deregistercontrolcmd("fakelist", &fakelist);
76 deregistercontrolcmd("fakekill", &fakekill);
77 }
78
79 void fakeuser_cleanup()
80 {
81 fakeuser *fake;
82 user_details *killed;
83 void *next;
84 for (fake = fakeuserlist; fake; fake = next)
85 {
86 deregisterlocaluser(fake->user, "Signing off");
87 next = fake->next;
88 free(fake);
89 }
90 for (killed = killeduserlist; killed; killed = next)
91 {
92 deleteschedule(killed->schedule, &reconnectfake, killed);
93 next = killed->next;
94 free(killed);
95 }
96 fakeusercount = 0;
97 killedusercount = 0;
98 fakeuserlist = NULL;
99 killeduserlist = NULL;
100 }
101
102 int fakeuser_loaddb()
103 {
104 if (!nofudb) {
105 nofudb = dbapi2open(DBAPI2_DEFAULT, "noperservfakeuser");
106 if(!nofudb) {
107 Error("fakeuser", ERR_STOP, "Could not connect to database.");
108 return 0;
109 }
110 }
111
112 db_loaded = 1;
113
114 nofudb->createtable(nofudb, NULL, NULL,
115 "CREATE TABLE ? ("
116 "nick VARCHAR(?) NOT NULL,"
117 "ident VARCHAR(?) NOT NULL,"
118 "host VARCHAR(?) NOT NULL,"
119 "realname VARCHAR(?) NOT NULL,"
120 "PRIMARY KEY (nick))", "Tdddd", "fakeusers", NICKLEN, USERLEN, HOSTLEN, REALLEN);
121
122 nofudb->query(nofudb, fakeusers_load, NULL,
123 "SELECT nick, ident, host, realname FROM ?", "T", "fakeusers");
124
125 return 1;
126 }
127
128 void fakeusers_load(const DBAPIResult *res, void *arg) {
129 user_details details;
130
131 if(!res)
132 return;
133
134 if(!res->success) {
135 Error("fakeuser", ERR_ERROR, "Error loading fakeuser list.");
136 res->clear(res);
137 return;
138 }
139
140 details.lastkill = 0;
141 details.schedule = NULL;
142 while(res->next(res)) {
143 strlcpy(details.nick, res->get(res, 0), NICKLEN + 1);
144 strlcpy(details.ident, res->get(res, 1), USERLEN + 1);
145 strlcpy(details.host, res->get(res, 2), HOSTLEN + 1);
146 strlcpy(details.realname, res->get(res, 3), REALLEN + 1);
147 reconnectfake(&details);
148 }
149
150 res->clear(res);
151 }
152
153 user_details *getdetails(nick *user)
154 {
155 user_details *details;
156 details = malloc(sizeof(user_details));
157 if (!details)
158 return NULL;
159 strlcpy(details->nick, user->nick, NICKLEN + 1);
160 strlcpy(details->ident, user->ident, USERLEN + 1);
161 strlcpy(details->host, user->host->name->content, HOSTLEN + 1);
162 strlcpy(details->realname, user->realname->name->content, REALLEN + 1);
163 details->schedule = NULL;
164 details->lastkill = 0;
165 return details;
166 }
167
168 #define ERR_EXISTS 1
169 #define ERR_MEM 2
170 #define ERR_WONTKILL 3
171 int err_code;
172
173 nick *register_fakeuser(user_details *details)
174 {
175 nick *user;
176 if ((user = getnickbynick(details->nick)) && (IsOper(user) || IsService(user) || IsXOper(user))) {
177 err_code = ERR_WONTKILL;
178 return NULL;
179 }
180
181 err_code = ERR_MEM;
182 return registerlocaluser(details->nick, details->ident, details->host, details->realname,
183 NULL, UMODE_INV | UMODE_DEAF, &fakeuser_handler);
184 }
185
186 fakeuser *ll_add(user_details *details)
187 // Adds to the (sorted) linked list
188 {
189 fakeuser *fake, *newfake;
190 int cmp;
191
192 if (fakeuserlist)
193 {
194 cmp = ircd_strcmp(details->nick, fakeuserlist->user->nick);
195 if (!cmp)
196 {
197 err_code = ERR_EXISTS;
198 return NULL;
199 }
200 }
201 else
202 cmp = -1;
203 if (cmp < 0)
204 {
205 newfake = malloc(sizeof(fakeuser));
206 if (!newfake)
207 {
208 err_code = ERR_MEM;
209 return NULL;
210 }
211 newfake->user = register_fakeuser(details);
212 if (!newfake->user)
213 {
214 free(newfake);
215 return NULL; //errcode already set by register_fakeuser
216 }
217 newfake->lastkill = details->lastkill;
218 newfake->next = fakeuserlist;
219 fakeuserlist = newfake;
220 fakeusercount++;
221 return newfake;
222 }
223 for (fake = fakeuserlist; fake->next; fake = fake->next)
224 {
225 cmp = ircd_strcmp(details->nick, fake->next->user->nick);
226 if (!cmp)
227 {
228 err_code = ERR_EXISTS;
229 return NULL;
230 }
231 if (cmp < 0)
232 {
233 newfake = malloc(sizeof(fakeuser));
234 if (!newfake)
235 {
236 err_code = ERR_MEM;
237 return NULL;
238 }
239 newfake->user = register_fakeuser(details);
240 if (!newfake->user)
241 {
242 free(newfake);
243 return NULL; //errcode already set by register_fakeuser
244 }
245 newfake->lastkill = details->lastkill;
246 newfake->next = fake->next;
247 fake->next = newfake;
248 fakeusercount++;
249 return newfake;
250 }
251 }
252 newfake = malloc(sizeof(fakeuser));
253 if (!newfake)
254 {
255 err_code = ERR_MEM;
256 return NULL;
257 }
258 newfake->user = register_fakeuser(details);
259 if (!newfake->user)
260 {
261 free(newfake);
262 return NULL; //errcode already set by register_fakeuser
263 }
264 newfake->lastkill = details->lastkill;
265 newfake->next = NULL;
266 fake->next = newfake;
267 fakeusercount++;
268 return newfake;
269 }
270
271 void kll_add(user_details *details)
272 //Adds to the (sorted) linked list of killed users
273 {
274 int cmp;
275 user_details *killed;
276
277 if (killeduserlist)
278 cmp = ircd_strcmp(details->nick, killeduserlist->nick);
279 else
280 cmp = -1;
281 if (cmp < 0) //Add to the start of the list
282 {
283 details->next = killeduserlist;
284 killeduserlist = details;
285 killedusercount++;
286 return;
287 }
288 for (killed = killeduserlist; killed->next; killed = killed->next)
289 {
290 cmp = ircd_strcmp(details->nick, killed->next->nick);
291 if (cmp < 0)
292 {
293 details->next = killed->next;
294 killed->next = details;
295 killedusercount++;
296 return;
297 }
298 }
299 details->next = NULL;
300 killed->next = details;
301 killedusercount++;
302 return;
303 }
304
305 fakeuser *ll_remove(char *nickname)
306 //Removes from the linked list
307 {
308 fakeuser *fake, *rmfake;
309 int cmp;
310
311 if (!fakeuserlist)
312 return NULL;
313 cmp = ircd_strcmp(nickname, fakeuserlist->user->nick);
314 if (cmp < 0)
315 return NULL;
316 if (!cmp)
317 {
318 rmfake = fakeuserlist;
319 fakeuserlist = fakeuserlist->next;
320 fakeusercount--;
321 rmfake->next = NULL;
322 return rmfake;
323 }
324 for (fake = fakeuserlist; fake->next; fake = fake->next)
325 {
326 rmfake = fake->next;
327 cmp = ircd_strcmp(nickname, rmfake->user->nick);
328 if (cmp < 0)
329 return NULL;
330 if (!cmp)
331 {
332 fake->next = rmfake->next;
333 fakeusercount--;
334 rmfake->next = NULL;
335 return rmfake;
336 }
337 }
338 return NULL;
339 }
340
341 user_details *kll_remove(char *nickname) //Removes from the killed user linked list
342 {
343 user_details *details, *rmdetails;
344 int cmp;
345
346 if (!killeduserlist)
347 return NULL;
348 cmp = ircd_strcmp(nickname, killeduserlist->nick);
349 if (cmp < 0)
350 return NULL;
351 if (!cmp)
352 {
353 rmdetails = killeduserlist;
354 killeduserlist = killeduserlist->next;
355 rmdetails->next = NULL;
356 killedusercount--;
357 return rmdetails;
358 }
359 for (details = killeduserlist; details->next; details = details->next)
360 {
361 rmdetails = details->next;
362 cmp = ircd_strcmp(nickname, rmdetails->nick);
363 if (cmp < 0)
364 return NULL;
365 if (!cmp)
366 {
367 details->next = rmdetails->next;
368 rmdetails->next = NULL;
369 killedusercount--;
370 return rmdetails;
371 }
372 }
373 return NULL;
374 }
375
376 void fakeuser_handler(nick *user, int command, void **params)
377 {
378 if (command == LU_KILLED)
379 {
380 user_details *details;
381 fakeuser *item;
382 time_t timenow = time(NULL);
383
384 details = getdetails(user);
385 item = ll_remove(details->nick);
386 if (!item)
387 {
388 controlwall(NO_OPER, NL_FAKEUSERS, "Error: A fakeuser was killed, but wasn't found in the list");
389 Error("noperserv_fakeuser", ERR_ERROR, "A fakeuser was killed, but wasn't found in the list");
390 return;
391 }
392
393 details->lastkill = item->lastkill;
394 free(item);
395
396 if (timenow - details->lastkill < KILL_TIME) {
397 controlwall(NO_OPER, NL_FAKEUSERS, "Fake user %s!%s@%s (%s) KILL'ed twice under in %d seconds. Removing.", details->nick, details->ident, details->host, details->realname, KILL_TIME);
398 nofudb->squery(nofudb, "DELETE FROM ? WHERE nick = ?", "Ts", "fakeusers", details->nick);
399 return;
400 }
401 details->lastkill = timenow;
402
403 /*
404 controlwalls(NO_OPER, NL_FAKEUSERS, "Fake user %s!%s@%s (%s) was KILL'ed", details->nick,
405 details->ident, details->host, details->realname);
406 */
407 details->schedule = scheduleoneshot(time(NULL) + KILL_WAIT, &reconnectfake, details);
408 if (!details->schedule)
409 {
410 Error("noperserv_fakeuser", ERR_ERROR, "Couldn't reschedule fakeuser for reconnect");
411 free(details);
412 return;
413 }
414 kll_add(details);
415 }
416 }
417
418 //Usage: FAKEUSER nick <ident> <host> <realname>
419 int fakeadd(void *sender, int cargc, char **cargv)
420 {
421 user_details details, *killed;
422 nick *newnick;
423 fakeuser *newfake;
424
425 if (cargc == 0)
426 return CMD_USAGE;
427 strlcpy(details.nick, cargv[0], NICKLEN + 1);
428 if (cargc < 4)
429 strlcpy(details.realname, cargv[0], REALLEN + 1);
430 else
431 strlcpy(details.realname, cargv[3], REALLEN + 1);
432 if (cargc < 3)
433 {
434 strlcpy(details.host, cargv[0], NICKLEN + 1);
435 strlcat(details.host, ".fakeusers.quakenet.org", HOSTLEN + 1);
436 }
437 else
438 strlcpy(details.host, cargv[2], HOSTLEN + 1);
439 if (cargc < 2)
440 strlcpy(details.ident, cargv[0], USERLEN + 1);
441 else
442 strlcpy(details.ident, cargv[1], USERLEN + 1);
443
444 details.lastkill = 0;
445
446 newfake = ll_add(&details);
447 if (!newfake)
448 {
449 if (err_code == ERR_EXISTS)
450 controlreply(sender, "Error: fakeuser already exists");
451 else if (err_code == ERR_MEM)
452 {
453 controlreply(sender, "Error: memory error!!");
454 Error("noperserv_fakeuser", ERR_STOP, "malloc error");
455 }
456 else if (err_code == ERR_WONTKILL)
457 controlreply(sender, "Nick already exists and I won't kill it");
458 else
459 {
460 controlreply(sender, "Unknown error when adding fakeuser");
461 Error("noperserv_fakeuser", ERR_ERROR, "Unknown error when adding fakeuser");
462 }
463 return CMD_ERROR;
464 }
465 newnick = newfake->user;
466 if ((killed = kll_remove(newnick->nick))) //Is this nickname scheduled to reconnect?
467 {
468 deleteschedule(killed->schedule, &reconnectfake, killed);
469 free(killed);
470 nofudb->squery(nofudb, "UPDATE ? SET ident=?, host=?, realname=? WHERE nick = ?", "Tssss", "fakeusers", newnick->ident, newnick->host->name->content, newnick->realname->name->content, newnick->nick);
471 }
472 else
473 nofudb->squery(nofudb, "INSERT INTO ? (nick, ident, host, realname) VALUES (?,?,?,?)", "Tssss", "fakeusers", newnick->nick, newnick->ident, newnick->host->name->content, newnick->realname->name->content);
474 controlreply(sender, "Added fake user %s", newnick->nick);
475 controlwall(NO_OPER, NL_FAKEUSERS, "Fake user %s!%s@%s (%s) added by %s/%s", newnick->nick, newnick->ident,
476 newnick->host->name->content, newnick->realname->name->content, ((nick*)sender)->nick, ((nick*)sender)->authname);
477 #ifdef SIT_CHANNEL
478 {
479 channel *sitchannel;
480 if (!(sitchannel = findchannel(SIT_CHANNEL)))
481 sitchannel = createchannel(SIT_CHANNEL);
482 localjoinchannel(newnick, sitchannel);
483 }
484 #endif
485 return CMD_OK;
486 }
487
488 //Usage: FAKELIST
489 int fakelist(void *sender, int cargc, char **cargv)
490 {
491 fakeuser *fake;
492 if (fakeusercount == 1)
493 controlreply(sender, "1 fakeuser is currently connected");
494 else
495 controlreply(sender, "%d fakeusers are currently connected", fakeusercount);
496 for(fake = fakeuserlist; fake; fake = fake->next)
497 controlreply(sender, "%s!%s@%s (%s)", fake->user->nick, fake->user->ident,
498 fake->user->host->name->content, fake->user->realname->name->content);
499 if (killeduserlist)
500 {
501 user_details *k_fake;
502 if (fakeusercount == 1)
503 controlreply(sender, "1 fakeuser is currently waiting to reconnect");
504 else
505 controlreply(sender, "%d fakeusers are currently waiting to reconnect", killedusercount);
506 for(k_fake = killeduserlist; k_fake; k_fake = k_fake->next)
507 controlreply(sender, "%s!%s@%s (%s)", k_fake->nick, k_fake->ident, k_fake->host, k_fake->realname);
508 }
509 return CMD_OK;
510 }
511
512 //Usage: FAKEKILL nick <reason>
513 int fakekill(void *sender, int cargc, char **cargv)
514 {
515 fakeuser *fake;
516 user_details *k_fake;
517 if (cargc == 0)
518 return CMD_USAGE;
519 if ((fake = ll_remove(cargv[0])))
520 {
521 nofudb->squery(nofudb, "DELETE FROM ? WHERE nick = ?", "Ts", "fakeusers", fake->user->nick);
522 controlreply(sender, "Killed fake user %s", fake->user->nick);
523 controlwall(NO_OPER, NL_FAKEUSERS, "Fake user %s!%s@%s (%s) removed by %s/%s", fake->user->nick, fake->user->ident,
524 fake->user->host->name->content, fake->user->realname->name->content, ((nick*)sender)->nick, ((nick*)sender)->authname);
525 if (cargc > 1)
526 deregisterlocaluser(fake->user, cargv[1]);
527 else
528 deregisterlocaluser(fake->user, "Signing off");
529 free(fake);
530 return CMD_OK;
531 }
532 if ((k_fake = kll_remove(cargv[0]))) //Fakeuser might be waiting to rejoin
533 {
534 nofudb->squery(nofudb, "DELETE FROM ? WHERE nick = ?", "Ts", "fakeusers", k_fake->nick);
535 controlreply(sender, "Killed fake user %s", k_fake->nick);
536 controlwall(NO_OPER, NL_FAKEUSERS, "Fake user %s!%s@%s (%s) removed by %s/%s", k_fake->nick,
537 k_fake->ident, k_fake->host, k_fake->realname, ((nick*)sender)->nick, ((nick*)sender)->authname);
538 free(k_fake);
539 return CMD_OK;
540 }
541 controlreply(sender, "No fakeuser with that nick exists");
542 return CMD_ERROR;
543 }
544
545 void reconnectfake(void *details)
546 //Called after the timeout period has expired since a fakeuser was killed, or on load
547 {
548 fakeuser *fake;
549 user_details *u_details = details;
550 if (u_details->schedule)
551 kll_remove(u_details->nick); //Fakeuser was /kill'd
552 fake = ll_add(u_details);
553 if (u_details->schedule)
554 free(u_details);
555 if (!fake)
556 return;
557 #ifdef SIT_CHANNEL
558 {
559 channel *sitchannel;
560 if (!(sitchannel = findchannel(SIT_CHANNEL)))
561 sitchannel = createchannel(SIT_CHANNEL);
562 localjoinchannel(fake->user, sitchannel);
563 }
564 #endif
565 }