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