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