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