]> jfr.im git - irc/quakenet/newserv.git/commitdiff
Add fakeuser.
authorChris Porter <redacted>
Thu, 23 Feb 2006 13:26:00 +0000 (13:26 +0000)
committerChris Porter <redacted>
Thu, 23 Feb 2006 13:26:00 +0000 (13:26 +0000)
noperserv/noperserv_fakeuser.c [new file with mode: 0644]

diff --git a/noperserv/noperserv_fakeuser.c b/noperserv/noperserv_fakeuser.c
new file mode 100644 (file)
index 0000000..77776f8
--- /dev/null
@@ -0,0 +1,554 @@
+/*
+ *  NOperserv Fakeuser module
+ *
+ *  Allows fakeusers to be added so as to block nicks, for example.
+ *
+ *  Copyright (c) Tim Gordon 2006.
+ */
+
+#include "../core/schedule.h"
+#include "../localuser/localuser.h"
+#include "../localuser/localuserchannel.h"
+#include "../irc/irc_config.h"
+#include "../lib/irc_string.h"
+#include "../control/control.h"
+#include "../channel/channel.h"
+#include "../pqsql/pqsql.h"
+#include "../lib/strlfunc.h"
+#include <string.h>
+#include <stdlib.h>
+#include <libpq-fe.h>
+
+/* #define SIT_CHANNEL "#qnet.fakeusers" */
+#define KILL_WAIT 10
+#define KILL_TIME 60
+
+typedef struct fakeuser {
+  nick *user;
+  time_t lastkill;
+  struct fakeuser *next;
+} fakeuser;
+
+typedef struct user_details {
+  char nick[NICKLEN + 1];
+  char ident[USERLEN + 1];
+  char host[HOSTLEN + 1];
+  char realname[REALLEN + 1];
+  time_t lastkill;
+  struct user_details *next;
+  void *schedule;
+} user_details;
+
+fakeuser *fakeuserlist = NULL;
+user_details *killeduserlist = NULL;
+int fakeusercount = 0;
+int killedusercount = 0;
+
+void fakeuser_cleanup();
+int fakeuser_loaddb();
+void fakeusers_load(PGconn *dbconn, void *tag);
+void fakeuser_handler(nick *user, int command, void **params);
+int fakeadd(void *sender, int cargc, char **cargv);
+int fakelist(void *sender, int cargc, char **cargv);
+int fakekill(void *sender, int cargc, char **cargv);
+void reconnectfake(void *details);
+
+void _init() {
+  if (!fakeuser_loaddb())
+  {
+    Error("noperserv_fakeuser", ERR_ERROR, "Cannot load database");
+    return;
+  }
+  registercontrolhelpcmd("fakeuser", NO_OPER, 4, &fakeadd, "Usage: FAKEUSER nick <ident> <host> <realname>\nCreates a fake user.");
+  registercontrolhelpcmd("fakelist", NO_OPER, 0, &fakelist, "Usage: FAKELIST\nLists all fake users.");
+  registercontrolhelpcmd("fakekill", NO_OPER, 2, &fakekill, "Usage: FAKEKILL nick <reason>\nRemoves a fake user");
+}
+
+void _fini() {
+  fakeuser_cleanup();
+  deregistercontrolcmd("fakeuser", &fakeadd);
+  deregistercontrolcmd("fakelist", &fakelist);
+  deregistercontrolcmd("fakekill", &fakekill);
+}
+
+void fakeuser_cleanup()
+{
+  fakeuser *fake;
+  user_details *killed;
+  void *next;
+  for (fake = fakeuserlist; fake; fake = next)
+  {
+    deregisterlocaluser(fake->user, "Signing off");
+    next = fake->next;
+    free(fake);
+  }
+  for (killed = killeduserlist; killed; killed = next)
+  {
+    deleteschedule(killed->schedule, &reconnectfake, killed);
+    next = killed->next;
+    free(killed);
+  }
+  fakeusercount = 0;
+  fakeuserlist = NULL;
+  killeduserlist = NULL;
+}
+
+int fakeuser_loaddb()
+{
+  if (!pqconnected())
+    return 0;
+  fakeuser_cleanup();
+  pqcreatequery("CREATE TABLE noperserv.fakeusers ("
+    "nick      VARCHAR(%d)  NOT NULL,"
+    "ident     VARCHAR(%d)  NOT NULL,"
+    "host      VARCHAR(%d)  NOT NULL,"
+    "realname  VARCHAR(%d)  NOT NULL,"
+    "PRIMARY KEY (nick))", NICKLEN, USERLEN, HOSTLEN, REALLEN);
+  pqasyncquery(&fakeusers_load, NULL, "SELECT nick, ident, host, realname FROM noperserv.fakeusers");
+  return 1;
+}
+
+void fakeusers_load(PGconn *dbconn, void *tag)
+{
+  PGresult *pgres = PQgetResult(dbconn);
+  user_details details;
+  int i, rowcount;
+
+  if (PQresultStatus(pgres) != PGRES_TUPLES_OK) {
+    Error("noperserv_fakeuser", ERR_ERROR, "Error loading fakeuser list (%d)", PQresultStatus(pgres));
+    return;
+  }
+  details.schedule = NULL;
+  rowcount = PQntuples(pgres);
+  Error("noperserv_fakeuser", ERR_INFO, "Loading %d users", rowcount);
+
+  details.lastkill = 0;
+
+  for (i = 0; i < rowcount; i++)
+  {
+    strlcpy(details.nick, PQgetvalue(pgres, i, 0), NICKLEN + 1);
+    strlcpy(details.ident, PQgetvalue(pgres, i, 1), USERLEN + 1);
+    strlcpy(details.host, PQgetvalue(pgres, i, 2), HOSTLEN + 1);
+    strlcpy(details.realname, PQgetvalue(pgres, i, 3), REALLEN + 1);
+    reconnectfake(&details);
+  }
+}
+
+user_details *getdetails(nick *user)
+{
+  user_details *details;
+  details = malloc(sizeof(user_details));
+  if (!details)
+    return NULL;
+  details->schedule = NULL;
+  strlcpy(details->nick, user->nick, NICKLEN + 1);
+  strlcpy(details->ident, user->ident, USERLEN + 1);
+  strlcpy(details->host, user->host->name->content, HOSTLEN + 1);
+  strlcpy(details->realname, user->realname->name->content, REALLEN + 1);
+  details->lastkill = 0;
+  return details;
+}
+
+#define ERR_EXISTS 1
+#define ERR_MEM 2
+#define ERR_WONTKILL 3
+int err_code;
+
+nick *register_fakeuser(user_details *details)
+{
+  nick *user;
+  if((user = getnickbynick(details->nick)) && (IsOper(user) || IsService(user) || IsXOper(user))) {
+    err_code = ERR_WONTKILL;
+    return NULL;
+  }
+
+  err_code = ERR_MEM;
+  return registerlocaluser(details->nick, details->ident, details->host, details->realname,
+    NULL, UMODE_INV | UMODE_DEAF, &fakeuser_handler);
+}
+
+fakeuser *ll_add(user_details *details) //Adds to the (sorted) linked list
+{
+  fakeuser *fake, *newfake;
+  int cmp;
+
+  if (fakeuserlist)
+  {
+    cmp = ircd_strcmp(details->nick, fakeuserlist->user->nick);
+    if (!cmp)
+    {
+      err_code = ERR_EXISTS;
+      return NULL;
+    }
+  }
+  else
+    cmp = -1;
+  if (cmp < 0)
+  {
+    newfake = malloc(sizeof(fakeuser));
+    if (!newfake)
+    {
+      err_code = ERR_MEM;
+      return NULL;
+    }
+
+    newfake->user = register_fakeuser(details);
+    if (!newfake->user)
+    {
+      free(newfake);
+      /* errcode already set by register_fakeuser */
+      return NULL;
+    }
+    newfake->lastkill = details->lastkill;
+    newfake->next = fakeuserlist;
+    fakeuserlist = newfake;
+    fakeusercount++;
+    return newfake;
+  }
+  for (fake = fakeuserlist; fake->next; fake = fake->next)
+  {
+    cmp = ircd_strcmp(details->nick, fake->next->user->nick);
+    if (!cmp)
+    {
+      err_code = ERR_EXISTS;
+      return NULL;
+    }
+    if (cmp < 0)
+    {
+      newfake = malloc(sizeof(fakeuser));
+      if (!newfake)
+      {
+        err_code = ERR_MEM;
+        return NULL;
+      }
+      newfake->user = register_fakeuser(details);
+      if (!newfake->user)
+      {
+        free(newfake);
+        /* errcode already set by register_fakeuser */
+        return NULL;
+      }
+      newfake->lastkill = details->lastkill;
+      newfake->next = fake->next;
+      fake->next = newfake;
+      fakeusercount++;
+      return newfake;
+    }
+  }
+  newfake = malloc(sizeof(fakeuser));
+  if (!newfake)
+  {
+    err_code = ERR_MEM;
+    return NULL;
+  }
+  newfake->user = register_fakeuser(details);
+  if (!newfake->user)
+  {
+    free(newfake);
+    /* errcode already set by register_fakeuser */
+    return NULL;
+  }
+  newfake->lastkill = details->lastkill;
+  newfake->next = NULL;
+  fake->next = newfake;
+  fakeusercount++;
+  return newfake;
+}
+
+void kll_add(user_details *details) //Adds to the (sorted) linked list of killed users
+{
+  int cmp;
+  user_details *killed;
+
+  if (killeduserlist)
+    cmp = ircd_strcmp(details->nick, killeduserlist->nick);
+  else
+    cmp = -1;
+  if (cmp < 0)  //Add to the start of the list
+  {
+    details->next = killeduserlist;
+    killeduserlist = details;
+    killedusercount++;
+    return;
+  }
+  for (killed = killeduserlist; killed->next; killed = killed->next)
+  {
+    cmp = ircd_strcmp(details->nick, killed->next->nick);
+    if (cmp < 0)
+    {
+      details->next = killed->next;
+      killed->next = details;
+      killedusercount++;
+      return;
+    }
+  }
+  details->next = NULL;
+  killed->next = details;
+  killedusercount++;
+  return;
+}
+
+fakeuser *ll_remove(char *nickname) //Removes from the linked list
+{
+  fakeuser *fake, *rmfake;
+  int cmp;
+
+  if (!fakeuserlist)
+    return NULL;
+  cmp = ircd_strcmp(nickname, fakeuserlist->user->nick);
+  if (cmp < 0)
+    return NULL;
+  if (!cmp)
+  {
+    rmfake = fakeuserlist;
+    fakeuserlist = fakeuserlist->next;
+    fakeusercount--;
+    rmfake->next = NULL;
+    return rmfake;
+  }
+  for (fake = fakeuserlist; fake->next; fake = fake->next)
+  {
+    rmfake = fake->next;
+    cmp = ircd_strcmp(nickname, rmfake->user->nick);
+    if (cmp < 0)
+      return NULL;
+    if (!cmp)
+    {
+      fake->next = rmfake->next;
+      fakeusercount--;
+      rmfake->next = NULL;
+      return rmfake;
+    }
+  }
+  return NULL;
+}
+
+user_details *kll_remove(char *nickname) //Removes from the killed user linked list
+{
+  user_details *details, *rmdetails;
+  int cmp;
+
+  if (!killeduserlist)
+    return NULL;
+  cmp = ircd_strcmp(nickname, killeduserlist->nick);
+  if (cmp < 0)
+    return NULL;
+  if (!cmp)
+  {
+    rmdetails = killeduserlist;
+    killeduserlist = killeduserlist->next;
+    rmdetails->next = NULL;
+    killedusercount--;
+    return rmdetails;
+  }
+  for (details = killeduserlist; details->next; details = details->next)
+  {
+    rmdetails = details->next;
+    cmp = ircd_strcmp(nickname, rmdetails->nick);
+    if (cmp < 0)
+      return NULL;
+    if (!cmp)
+    {
+      details->next = rmdetails->next;
+      rmdetails->next = NULL;
+      killedusercount--;
+      return rmdetails;
+    }
+  }
+  return NULL;
+}
+
+void fakeuser_handler(nick *user, int command, void **params)
+{
+  if (command == LU_KILLED)
+  {
+    user_details *details;
+    fakeuser *item;
+    time_t timenow = time(NULL);
+
+    details = getdetails(user);
+    item = ll_remove(details->nick);
+    if(!item)
+      return;
+
+    details->lastkill = item->lastkill;
+    free(item);
+
+    if(timenow - item->lastkill < KILL_TIME) {
+      char escnick[NICKLEN * 2 + 1];
+      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);
+      item = ll_remove(details->nick);
+      free(item);
+
+      PQescapeString(escnick, details->nick, strlen(details->nick));
+      pqquery("DELETE FROM noperserv.fakeusers WHERE nick = '%s'", escnick);
+      return;
+    }
+    details->lastkill = timenow;
+
+    /*
+    controlwalls(NO_OPER, NL_FAKEUSERS, "Fake user %s!%s@%s (%s) was KILL'ed", details->nick,
+                details->ident, details->host, details->realname);
+    */
+    details->schedule = scheduleoneshot(time(NULL) + KILL_WAIT, &reconnectfake, details);
+    if (!details->schedule)
+    {
+      Error("noperserv_fakeuser", ERR_ERROR, "Couldn't reschedule fakeuser for reconnect");
+      free(details);
+      return;
+    }
+    kll_add(details);
+  }
+}
+
+//Usage: FAKEUSER nick <ident> <host> <realname>
+int fakeadd(void *sender, int cargc, char **cargv)
+{
+  user_details details, *killed;
+  char escnick[NICKLEN * 2 + 1], escident[USERLEN * 2 + 1], eschost[HOSTLEN * 2 + 1], escreal[REALLEN * 2 + 1];
+  nick *newnick;
+  fakeuser *newfake;
+
+  if (cargc == 0)
+    return CMD_USAGE;
+  strlcpy(details.nick, cargv[0], NICKLEN + 1);
+  if (cargc < 4)
+    strlcpy(details.realname, cargv[0], REALLEN + 1);
+  else
+    strlcpy(details.realname, cargv[3], REALLEN + 1);
+  if (cargc < 3)
+  {
+    strlcpy(details.host, cargv[0], NICKLEN + 1);
+    strlcat(details.host, ".fakeusers.quakenet.org", HOSTLEN + 1);
+  }
+  else
+    strlcpy(details.host, cargv[2], HOSTLEN + 1);
+  if (cargc < 2)
+    strlcpy(details.ident, cargv[0], USERLEN + 1);
+  else
+    strlcpy(details.ident, cargv[1], USERLEN + 1);
+
+  details.lastkill = 0;
+
+  newfake = ll_add(&details);
+  if (!newfake)
+  {
+    if (err_code == ERR_EXISTS)
+      controlreply(sender, "Error: fakeuser already exists");
+    else if (err_code == ERR_MEM)
+      controlreply(sender, "Error: memory error!!");
+    else if (err_code == ERR_WONTKILL)
+      controlreply(sender, "Nick already exists and I won't kill it");
+    else
+      controlreply(sender, "Unknown error when adding fakeuser");
+    return CMD_ERROR;
+  }
+  newnick = newfake->user;
+  PQescapeString(escnick, newnick->nick, strlen(newnick->nick));
+  PQescapeString(escident, newnick->ident, strlen(newnick->ident));
+  PQescapeString(eschost, newnick->host->name->content, newnick->host->name->length);
+  PQescapeString(escreal, newnick->realname->name->content, newnick->realname->name->length);  
+  if ((killed = kll_remove(newnick->nick)))  //Is this nickname scheduled to reconnect?
+  {
+    deleteschedule(killed->schedule, &reconnectfake, killed);
+    free(killed);
+    pqquery("UPDATE noperserv.fakeusers SET ident = '%s', host = '%s', realname = '%s' WHERE nick = '%s'", 
+             escident, eschost, escreal, escnick);
+  }
+  else
+    pqquery("INSERT INTO noperserv.fakeusers (nick, ident, host, realname) VALUES ('%s', '%s', '%s', '%s')",
+            escnick, escident, eschost, escreal);
+  controlreply(sender, "Added fake user %s", newnick->nick);
+  controlwall(NO_OPER, NL_FAKEUSERS, "Fake user %s!%s@%s (%s) added by %s/%s", newnick->nick, newnick->ident,
+              newnick->host->name->content, newnick->realname->name->content, ((nick*)sender)->nick, ((nick*)sender)->authname);
+#ifdef SIT_CHANNEL
+  {
+    channel *sitchannel;
+    if (!(sitchannel = findchannel(SIT_CHANNEL)))
+      sitchannel = createchannel(SIT_CHANNEL);
+    localjoinchannel(newnick, sitchannel);
+  }
+#endif
+  return CMD_OK;
+}
+
+//Usage: FAKELIST
+int fakelist(void *sender, int cargc, char **cargv)
+{
+  fakeuser *fake;
+  if (fakeusercount == 1)
+    controlreply(sender, "1 fakeuser is currently connected");
+  else
+    controlreply(sender, "%d fakeusers are currently connected", fakeusercount);
+  for(fake = fakeuserlist; fake; fake = fake->next)
+    controlreply(sender, "%s!%s@%s (%s)", fake->user->nick, fake->user->ident,
+                 fake->user->host->name->content, fake->user->realname->name->content);
+  if (killeduserlist)
+  {
+    user_details *k_fake;
+    if (fakeusercount == 1)
+      controlreply(sender, "1 fakeuser is currently waiting to reconnect");
+    else
+      controlreply(sender, "%d fakeusers are currently waiting to reconnect", killedusercount);
+      for(k_fake = killeduserlist; k_fake; k_fake = k_fake->next)
+        controlreply(sender, "%s!%s@%s (%s)", k_fake->nick, k_fake->ident, k_fake->host, k_fake->realname);
+  }
+  return CMD_OK;
+}
+
+//Usage: FAKEKILL nick <reason>
+int fakekill(void *sender, int cargc, char **cargv)
+{
+  fakeuser *fake;
+  user_details *k_fake;
+  char escnick[NICKLEN * 2 + 1];
+  if (cargc == 0)
+    return CMD_USAGE;
+  if ((fake = ll_remove(cargv[0])))
+  {
+    PQescapeString(escnick, fake->user->nick, strlen(fake->user->nick));
+    pqquery("DELETE FROM noperserv.fakeusers WHERE nick = '%s'", escnick);
+    controlreply(sender, "Killed fake user %s", fake->user->nick);
+    controlwall(NO_OPER, NL_FAKEUSERS, "Fake user %s!%s@%s (%s) removed by %s/%s", fake->user->nick, fake->user->ident,
+                fake->user->host->name->content, fake->user->realname->name->content, ((nick*)sender)->nick, ((nick*)sender)->authname);
+    if (cargc > 1)
+      deregisterlocaluser(fake->user, cargv[1]);
+    else
+      deregisterlocaluser(fake->user, "Signing off");
+    free(fake);
+    return CMD_OK;
+  }
+  if ((k_fake = kll_remove(cargv[0])))  //Fakeuser might be waiting to rejoin
+  {
+    PQescapeString(escnick, k_fake->nick, strlen(k_fake->nick));
+    pqquery("DELETE FROM noperserv.fakeusers WHERE nick = '%s'", escnick);
+    controlreply(sender, "Killed fake user %s", k_fake->nick);
+    controlwall(NO_OPER, NL_FAKEUSERS, "Fake user %s!%s@%s (%s) removed by %s/%s", k_fake->nick,
+                k_fake->ident, k_fake->host, k_fake->realname, ((nick*)sender)->nick, ((nick*)sender)->authname);
+    free(k_fake);
+    return CMD_OK;
+  }
+  controlreply(sender, "No fakeuser with that nick exists");
+  return CMD_ERROR;
+}
+
+void reconnectfake(void *details)
+{
+  fakeuser *fake;
+  user_details *u_details = details;
+  if (u_details->schedule)
+    kll_remove(u_details->nick); //Fakeuser was /kill'd
+  fake = ll_add(u_details);
+  if (u_details->schedule)
+    free(u_details);
+  if (!fake)
+    return;
+#ifdef SIT_CHANNEL
+  {
+    channel *sitchannel;
+    if (!(sitchannel = findchannel(SIT_CHANNEL)))
+      sitchannel = createchannel(SIT_CHANNEL);
+    localjoinchannel(fake->user, sitchannel);
+  }
+#endif
+}