summaryrefslogtreecommitdiff
path: root/src/nickserv.c
diff options
context:
space:
mode:
authorsjaz <sjaz@5417fbe8-f217-4b02-8779-1006273d7864>2009-01-01 12:00:20 +0000
committersjaz <sjaz@5417fbe8-f217-4b02-8779-1006273d7864>2009-01-01 12:00:20 +0000
commitc777c8d9aa7cd5c2e9a399727a7fa9985a77fb1c (patch)
tree9e996ae4a1bbb833cec036c5cd4d87a590149e85 /src/nickserv.c
Anope Stable Branch
git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/stable@1902 5417fbe8-f217-4b02-8779-1006273d7864
Diffstat (limited to 'src/nickserv.c')
-rw-r--r--src/nickserv.c1995
1 files changed, 1995 insertions, 0 deletions
diff --git a/src/nickserv.c b/src/nickserv.c
new file mode 100644
index 000000000..a14877b8b
--- /dev/null
+++ b/src/nickserv.c
@@ -0,0 +1,1995 @@
+
+/* NickServ functions.
+ *
+ * (C) 2003-2008 Anope Team
+ * Contact us at info@anope.org
+ *
+ * Please read COPYING and README for further details.
+ *
+ * Based on the original code of Epona by Lara.
+ * Based on the original code of Services by Andy Church.
+ *
+ * $Id$
+ *
+ */
+
+#include "services.h"
+#include "pseudo.h"
+
+/*************************************************************************/
+
+#define HASH(nick) ((tolower((nick)[0])&31)<<5 | (tolower((nick)[1])&31))
+
+NickAlias *nalists[1024];
+NickCore *nclists[1024];
+NickRequest *nrlists[1024];
+
+unsigned int guestnum; /* Current guest number */
+
+#define TO_COLLIDE 0 /* Collide the user with this nick */
+#define TO_RELEASE 1 /* Release a collided nick */
+
+/*************************************************************************/
+
+static void add_ns_timeout(NickAlias * na, int type, time_t delay);
+
+/*************************************************************************/
+/* *INDENT-OFF* */
+void moduleAddNickServCmds(void) {
+ modules_core_init(NickServCoreNumber, NickServCoreModules);
+}
+/* *INDENT-ON* */
+/*************************************************************************/
+
+/* Display total number of registered nicks and info about each; or, if
+ * a specific nick is given, display information about that nick (like
+ * /msg NickServ INFO <nick>). If count_only != 0, then only display the
+ * number of registered nicks (the nick parameter is ignored).
+ */
+
+void listnicks(int count_only, const char *nick)
+{
+ int count = 0;
+ NickAlias *na;
+ int i;
+ char *end;
+
+ if (count_only) {
+
+ for (i = 0; i < 1024; i++) {
+ for (na = nalists[i]; na; na = na->next)
+ count++;
+ }
+ printf("%d nicknames registered.\n", count);
+
+ } else if (nick) {
+
+ struct tm *tm;
+ char buf[512];
+ static const char commastr[] = ", ";
+ int need_comma = 0;
+
+ if (!(na = findnick(nick))) {
+ printf("%s not registered.\n", nick);
+ return;
+ } else if (na->status & NS_VERBOTEN) {
+ printf("%s is FORBIDden.\n", nick);
+ return;
+ }
+ printf("%s is %s\n", nick, na->last_realname);
+ printf("Last seen address: %s\n", na->last_usermask);
+ tm = localtime(&na->time_registered);
+ strftime(buf, sizeof(buf),
+ getstring(NULL, STRFTIME_DATE_TIME_FORMAT), tm);
+ printf(" Time registered: %s\n", buf);
+ tm = localtime(&na->last_seen);
+ strftime(buf, sizeof(buf),
+ getstring(NULL, STRFTIME_DATE_TIME_FORMAT), tm);
+ printf(" Last seen time: %s\n", buf);
+ if (na->nc->url)
+ printf(" URL: %s\n", na->nc->url);
+ if (na->nc->email)
+ printf(" E-mail address: %s\n", na->nc->email);
+ if (na->nc->icq)
+ printf(" ICQ #: %d\n", na->nc->icq);
+ if (na->nc->greet)
+ printf(" Greet: %s\n", na->nc->greet);
+ *buf = 0;
+ end = buf;
+ if (na->nc->flags & NI_KILLPROTECT) {
+ end +=
+ snprintf(end, sizeof(buf) - (end - buf),
+ "Kill protection");
+ need_comma = 1;
+ }
+ if (na->nc->flags & NI_SECURE) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%sSecurity",
+ need_comma ? commastr : "");
+ need_comma = 1;
+ }
+ if (na->nc->flags & NI_PRIVATE) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%sPrivate",
+ need_comma ? commastr : "");
+ need_comma = 1;
+ }
+ if (na->status & NS_NO_EXPIRE) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%sNo Expire",
+ need_comma ? commastr : "");
+ need_comma = 1;
+ }
+ printf(" Options: %s\n", *buf ? buf : "None");
+
+ if (na->nc->flags & NI_SUSPENDED) {
+ if (na->last_quit) {
+ printf
+ ("This nickname is currently suspended, reason: %s\n",
+ na->last_quit);
+ } else {
+ printf("This nickname is currently suspended.\n");
+ }
+ }
+
+
+ } else {
+
+ for (i = 0; i < 1024; i++) {
+ for (na = nalists[i]; na; na = na->next) {
+ printf(" %s %-20s %s\n",
+ na->status & NS_NO_EXPIRE ? "!" : " ",
+ na->nick, na->status & NS_VERBOTEN ?
+ "Disallowed (FORBID)" : (na->nc->
+ flags & NI_SUSPENDED ?
+ "Disallowed (SUSPENDED)" :
+ na->last_usermask));
+ count++;
+ }
+ }
+ printf("%d nicknames registered.\n", count);
+
+ }
+}
+
+/*************************************************************************/
+
+/* Return information on memory use. Assumes pointers are valid. */
+
+void get_aliases_stats(long *nrec, long *memuse)
+{
+ long count = 0, mem = 0;
+ int i;
+ NickAlias *na;
+
+ for (i = 0; i < 1024; i++) {
+ for (na = nalists[i]; na; na = na->next) {
+ count++;
+ mem += sizeof(*na);
+ if (na->nick)
+ mem += strlen(na->nick) + 1;
+ if (na->last_usermask)
+ mem += strlen(na->last_usermask) + 1;
+ if (na->last_realname)
+ mem += strlen(na->last_realname) + 1;
+ if (na->last_quit)
+ mem += strlen(na->last_quit) + 1;
+ }
+ }
+ *nrec = count;
+ *memuse = mem;
+}
+
+/*************************************************************************/
+
+/* Return information on memory use. Assumes pointers are valid. */
+
+void get_core_stats(long *nrec, long *memuse)
+{
+ long count = 0, mem = 0;
+ int i, j;
+ NickCore *nc;
+ char **accptr;
+
+ for (i = 0; i < 1024; i++) {
+ for (nc = nclists[i]; nc; nc = nc->next) {
+ count++;
+ mem += sizeof(*nc);
+
+ if (nc->display)
+ mem += strlen(nc->display) + 1;
+ if (nc->pass)
+ mem += strlen(nc->pass) + 1;
+
+ if (nc->url)
+ mem += strlen(nc->url) + 1;
+ if (nc->email)
+ mem += strlen(nc->email) + 1;
+ if (nc->greet)
+ mem += strlen(nc->greet) + 1;
+
+ mem += sizeof(char *) * nc->accesscount;
+ for (accptr = nc->access, j = 0; j < nc->accesscount;
+ accptr++, j++) {
+ if (*accptr)
+ mem += strlen(*accptr) + 1;
+ }
+
+ mem += nc->memos.memocount * sizeof(Memo);
+ for (j = 0; j < nc->memos.memocount; j++) {
+ if (nc->memos.memos[j].text)
+ mem += strlen(nc->memos.memos[j].text) + 1;
+ }
+
+ mem += sizeof(void *) * nc->aliases.count;
+ }
+ }
+ *nrec = count;
+ *memuse = mem;
+}
+
+/*************************************************************************/
+/*************************************************************************/
+
+/* NickServ initialization. */
+
+void ns_init(void)
+{
+ moduleAddNickServCmds();
+ guestnum = time(NULL);
+ while (guestnum > 9999999)
+ guestnum -= 10000000;
+}
+
+/*************************************************************************/
+
+/* Main NickServ routine. */
+
+void nickserv(User * u, char *buf)
+{
+ char *cmd, *s;
+
+ cmd = strtok(buf, " ");
+
+ if (!cmd) {
+ return;
+ } else if (stricmp(cmd, "\1PING") == 0) {
+ if (!(s = strtok(NULL, ""))) {
+ s = "";
+ }
+ anope_cmd_ctcp(s_NickServ, u->nick, "PING %s", s);
+ } else if (skeleton) {
+ notice_lang(s_NickServ, u, SERVICE_OFFLINE, s_NickServ);
+ } else {
+ mod_run_cmd(s_NickServ, u, NICKSERV, cmd);
+ }
+
+}
+
+/*************************************************************************/
+
+/* Load/save data files. */
+
+
+#define SAFE(x) do { \
+ if ((x) < 0) { \
+ if (!forceload) \
+ fatal("Read error on %s", NickDBName); \
+ failed = 1; \
+ break; \
+ } \
+} while (0)
+
+/* Loads NickServ database versions 5 to 11 (<= 4 is not supported) */
+
+void load_old_ns_dbase(void)
+{
+ dbFILE *f;
+ int ver, i, j, c;
+ NickAlias *na, *na2, *next;
+ NickCore *nc;
+ int failed = 0;
+
+ uint16 tmp16;
+ uint32 tmp32;
+
+ char bufn[NICKMAX], bufp[PASSMAX];
+ char *email, *greet, *url, *forbidby, *forbidreason;
+ uint32 icq;
+
+ if (!(f = open_db(s_NickServ, NickDBName, "r", NICK_VERSION)))
+ return;
+
+ ver = get_file_version(f);
+ if (ver <= 4) {
+ fatal("Unsupported version number (%d) on %s", ver, NickDBName);
+ close_db(f);
+ return;
+ }
+
+ for (i = 0; i < 256 && !failed; i++) {
+ while ((c = getc_db(f)) == 1) {
+ if (c != 1)
+ fatal("Invalid format in %s", NickDBName);
+
+ na = scalloc(sizeof(NickAlias), 1);
+
+ SAFE(read_buffer(bufn, f));
+ na->nick = sstrdup(bufn);
+ SAFE(read_buffer(bufp, f)); /* Will be used later if needed */
+
+ SAFE(read_string(&url, f));
+ SAFE(read_string(&email, f));
+ if (ver >= 10)
+ SAFE(read_int32(&icq, f));
+ else
+ icq = 0;
+ if (ver >= 9)
+ SAFE(read_string(&greet, f));
+ else
+ greet = NULL;
+
+ SAFE(read_string(&na->last_usermask, f));
+ SAFE(read_string(&na->last_realname, f));
+ SAFE(read_string(&na->last_quit, f));
+
+ SAFE(read_int32(&tmp32, f));
+ na->time_registered = tmp32;
+ SAFE(read_int32(&tmp32, f));
+ na->last_seen = tmp32;
+
+ SAFE(read_int16(&na->status, f));
+ na->status &= ~NS_TEMPORARY;
+
+ if (ver >= 9) {
+ SAFE(read_string(&forbidby, f));
+ SAFE(read_string(&forbidreason, f));
+ /* Cleanup */
+ if (forbidby && *forbidby == '@') {
+ free(forbidby);
+ forbidby = NULL;
+ }
+ if (forbidreason && *forbidreason == 0) {
+ free(forbidreason);
+ forbidreason = NULL;
+ }
+ } else {
+ forbidby = NULL;
+ forbidreason = NULL;
+ }
+
+ if (na->status & NS_VERBOTEN) {
+ if (na->last_usermask)
+ free(na->last_usermask);
+ if (na->last_realname)
+ free(na->last_realname);
+
+ na->last_usermask = forbidby;
+ na->last_realname = forbidreason;
+ } else {
+ if (!na->last_usermask)
+ na->last_usermask = sstrdup("");
+ if (!na->last_realname)
+ na->last_realname = sstrdup("");
+ }
+
+ /* Store the reference for later resolving */
+ SAFE(read_string((char **) &na->nc, f));
+ SAFE(read_int16(&tmp16, f)); /* Was linkcount */
+
+ if (na->nc) {
+ SAFE(read_int16(&tmp16, f)); /* Was channelcount */
+ } else {
+ /* This nick was a master nick, so it also has all the
+ * core info! =)
+ */
+ nc = scalloc(1, sizeof(NickCore));
+ slist_init(&nc->aliases);
+
+ /* The initial display is what used to be the master nick */
+ nc->display = sstrdup(na->nick);
+
+ /* We grabbed info before; fill the appropriate fields now */
+ if (*bufp)
+ memcpy(nc->pass, bufp, PASSMAX);
+ else
+ memset(nc->pass, 0, PASSMAX); /* Which may be the case for forbidden nicks .. */
+
+ nc->email = email;
+ nc->greet = greet;
+ nc->icq = icq;
+ nc->url = url;
+
+ /* We check whether the e-mail is valid because it was not tested
+ * in older versions.
+ */
+ if (ver <= 10 && nc->email && !MailValidate(nc->email)) {
+ free(nc->email);
+ nc->email = NULL;
+ }
+
+ SAFE(read_int32(&nc->flags, f));
+ if (!NSAllowKillImmed)
+ nc->flags &= ~NI_KILL_IMMED;
+
+ /* Status flags cleanup */
+ if (na->status & NS_OLD_ENCRYPTEDPW) {
+ nc->flags |= NI_ENCRYPTEDPW;
+ na->status &= ~NS_OLD_ENCRYPTEDPW;
+ }
+
+ /* Add services opers and admins to the appropriate list, but
+ only if the database version is equal to or more than 10. */
+ if (ver >= 10) {
+ if (nc->flags & NI_SERVICES_ADMIN)
+ slist_add(&servadmins, nc);
+ if (nc->flags & NI_SERVICES_OPER)
+ slist_add(&servopers, nc);
+ }
+
+ /* Add the Services root flag if needed. */
+ if (nc)
+ for (j = 0; j < RootNumber; j++)
+ if (!stricmp(ServicesRoots[j], na->nick))
+ nc->flags |= NI_SERVICES_ROOT;
+
+ SAFE(read_int16(&nc->accesscount, f));
+ if (nc->accesscount) {
+ char **access;
+ access = scalloc(sizeof(char *) * nc->accesscount, 1);
+ nc->access = access;
+ for (j = 0; j < nc->accesscount; j++, access++)
+ SAFE(read_string(access, f));
+ }
+
+ SAFE(read_int16(&tmp16, f));
+ nc->memos.memocount = (int16) tmp16;
+ SAFE(read_int16(&tmp16, f));
+ nc->memos.memomax = (int16) tmp16;
+ if (nc->memos.memocount) {
+ Memo *memos;
+ memos = scalloc(sizeof(Memo) * nc->memos.memocount, 1);
+ nc->memos.memos = memos;
+
+ for (j = 0; j < nc->memos.memocount; j++, memos++) {
+ SAFE(read_int32(&memos->number, f));
+ SAFE(read_int16(&memos->flags, f));
+ SAFE(read_int32(&tmp32, f));
+ memos->time = tmp32;
+ SAFE(read_buffer(memos->sender, f));
+ SAFE(read_string(&memos->text, f));
+ memos->moduleData = NULL;
+ }
+ }
+
+ /* We read the channel count, but don't take care of it.
+ load_cs_dbase will regenerate it correctly. */
+ SAFE(read_int16(&tmp16, f));
+ SAFE(read_int16(&nc->channelmax, f));
+ if (ver == 5)
+ nc->channelmax = CSMaxReg;
+
+ SAFE(read_int16(&nc->language, f));
+
+ if (ver >= 11 && ver < 13) {
+ char *s;
+
+ SAFE(read_int16(&tmp16, f));
+ SAFE(read_int32(&tmp32, f));
+ SAFE(read_int16(&tmp16, f));
+ SAFE(read_string(&s, f));
+ }
+
+ /* Set us as being a master nick; fill the nc field also.
+ The NS_MASTER flag will not be cleared in this function. */
+ na->status |= NS_MASTER;
+ na->nc = nc;
+ slist_add(&nc->aliases, na);
+
+ /* Insert our new core in the core list */
+ insert_core(nc);
+ }
+
+ alpha_insert_alias(na);
+
+ } /* while (getc_db(f) != 0) */
+ } /* for (i) */
+
+ /* Now resolve what were called links */
+ for (i = 0; i < 1024; i++) {
+ for (na = nalists[i]; na; na = next) {
+ next = na->next;
+
+ /* Master nicks are already resolved */
+ if (na->status & NS_MASTER)
+ continue;
+
+ na2 = na;
+ /* While the reference resolves and it's not a master nick */
+ while ((na2 = findnick((char *) na2->nc))
+ && !(na2->status & NS_MASTER));
+
+ /* It didn't resolve. This is problematic since there is no core. :/
+ We delete the nick. */
+ if (!na2) {
+ alog("%s: while loading database: %s was linked to inexistant %s", s_NickServ, na->nick, (char *) na->nc);
+ delnick(na);
+ continue;
+ }
+
+ /* OK we have information on the core. We mark the current alias
+ as a master nick because it now contains a valid core. */
+ na->nc = na2->nc;
+ na->status |= NS_MASTER;
+ slist_add(&na->nc->aliases, na);
+ }
+ }
+
+ close_db(f);
+}
+
+void load_ns_req_db(void)
+{
+ dbFILE *f;
+ int i, c, ver;
+ NickRequest *nr;
+ uint32 tmp32;
+ int failed = 0, len;
+ char *pass;
+
+ if (!(f = open_db(s_NickServ, PreNickDBName, "r", PRE_NICK_VERSION)))
+ return;
+ ver = get_file_version(f);
+ for (i = 0; i < 1024 && !failed; i++) {
+ while ((c = getc_db(f)) == 1) {
+ if (c != 1)
+ fatal("Invalid format in %s", PreNickDBName);
+ nr = scalloc(1, sizeof(NickRequest));
+ SAFE(read_string(&nr->nick, f));
+ SAFE(read_string(&nr->passcode, f));
+ if (ver < 2) {
+ SAFE(read_string(&pass, f));
+ len = strlen(pass);
+ enc_encrypt(pass, len, nr->password, PASSMAX);
+ memset(pass, 0, len);
+ free(pass);
+ } else
+ SAFE(read_buffer(nr->password, f));
+ SAFE(read_string(&nr->email, f));
+ SAFE(read_int32(&tmp32, f));
+ nr->requested = tmp32;
+ insert_requestnick(nr);
+ }
+ }
+ close_db(f);
+}
+
+void load_ns_dbase(void)
+{
+ dbFILE *f;
+ int ver, i, j, c;
+ NickAlias *na, **nalast, *naprev;
+ NickCore *nc, **nclast, *ncprev;
+ int failed = 0;
+ uint16 tmp16;
+ uint32 tmp32;
+ char *s, *pass;
+
+ if (!(f = open_db(s_NickServ, NickDBName, "r", NICK_VERSION)))
+ return;
+
+ ver = get_file_version(f);
+
+ if (ver <= 11) {
+ close_db(f);
+ load_old_ns_dbase();
+ return;
+ }
+
+ /* First we load nick cores */
+ for (i = 0; i < 1024 && !failed; i++) {
+ nclast = &nclists[i];
+ ncprev = NULL;
+
+ while ((c = getc_db(f)) == 1) {
+ if (c != 1)
+ fatal("Invalid format in %s", NickDBName);
+
+ nc = scalloc(1, sizeof(NickCore));
+ *nclast = nc;
+ nclast = &nc->next;
+ nc->prev = ncprev;
+ ncprev = nc;
+
+ slist_init(&nc->aliases);
+
+ SAFE(read_string(&nc->display, f));
+ if (ver < 14) {
+ SAFE(read_string(&pass, f));
+ if (pass) {
+ memset(nc->pass, 0, PASSMAX);
+ memcpy(nc->pass, pass, strlen(pass));
+ } else
+ memset(nc->pass, 0, PASSMAX);
+ } else
+ SAFE(read_buffer(nc->pass, f));
+
+ SAFE(read_string(&nc->email, f));
+ SAFE(read_string(&nc->greet, f));
+ SAFE(read_int32(&nc->icq, f));
+ SAFE(read_string(&nc->url, f));
+
+ SAFE(read_int32(&nc->flags, f));
+ if (!NSAllowKillImmed)
+ nc->flags &= ~NI_KILL_IMMED;
+ SAFE(read_int16(&nc->language, f));
+
+ /* Add services opers and admins to the appropriate list, but
+ only if the database version is more than 10. */
+ if (nc->flags & NI_SERVICES_ADMIN)
+ slist_add(&servadmins, nc);
+ if (nc->flags & NI_SERVICES_OPER)
+ slist_add(&servopers, nc);
+
+ SAFE(read_int16(&nc->accesscount, f));
+ if (nc->accesscount) {
+ char **access;
+ access = scalloc(sizeof(char *) * nc->accesscount, 1);
+ nc->access = access;
+ for (j = 0; j < nc->accesscount; j++, access++)
+ SAFE(read_string(access, f));
+ }
+
+ SAFE(read_int16(&tmp16, f));
+ nc->memos.memocount = (int16) tmp16;
+ SAFE(read_int16(&tmp16, f));
+ nc->memos.memomax = (int16) tmp16;
+ if (nc->memos.memocount) {
+ Memo *memos;
+ memos = scalloc(sizeof(Memo) * nc->memos.memocount, 1);
+ nc->memos.memos = memos;
+ for (j = 0; j < nc->memos.memocount; j++, memos++) {
+ SAFE(read_int32(&memos->number, f));
+ SAFE(read_int16(&memos->flags, f));
+ SAFE(read_int32(&tmp32, f));
+ memos->time = tmp32;
+ SAFE(read_buffer(memos->sender, f));
+ SAFE(read_string(&memos->text, f));
+ memos->moduleData = NULL;
+ }
+ }
+
+ SAFE(read_int16(&nc->channelcount, f));
+ SAFE(read_int16(&tmp16, f));
+ nc->channelmax = CSMaxReg;
+
+ if (ver < 13) {
+ /* Used to be dead authentication system */
+ SAFE(read_int16(&tmp16, f));
+ SAFE(read_int32(&tmp32, f));
+ SAFE(read_int16(&tmp16, f));
+ SAFE(read_string(&s, f));
+ }
+
+ } /* while (getc_db(f) != 0) */
+ *nclast = NULL;
+ } /* for (i) */
+
+ for (i = 0; i < 1024 && !failed; i++) {
+ nalast = &nalists[i];
+ naprev = NULL;
+ while ((c = getc_db(f)) == 1) {
+ if (c != 1)
+ fatal("Invalid format in %s", NickDBName);
+
+ na = scalloc(1, sizeof(NickAlias));
+
+ SAFE(read_string(&na->nick, f));
+
+ SAFE(read_string(&na->last_usermask, f));
+ SAFE(read_string(&na->last_realname, f));
+ SAFE(read_string(&na->last_quit, f));
+
+ SAFE(read_int32(&tmp32, f));
+ na->time_registered = tmp32;
+ SAFE(read_int32(&tmp32, f));
+ na->last_seen = tmp32;
+ SAFE(read_int16(&na->status, f));
+ na->status &= ~NS_TEMPORARY;
+
+ SAFE(read_string(&s, f));
+ na->nc = findcore(s);
+ free(s);
+
+ slist_add(&na->nc->aliases, na);
+
+ if (!(na->status & NS_VERBOTEN)) {
+ if (!na->last_usermask)
+ na->last_usermask = sstrdup("");
+ if (!na->last_realname)
+ na->last_realname = sstrdup("");
+ }
+
+ na->nc->flags &= ~NI_SERVICES_ROOT;
+
+ *nalast = na;
+ nalast = &na->next;
+ na->prev = naprev;
+ naprev = na;
+
+ } /* while (getc_db(f) != 0) */
+
+ *nalast = NULL;
+ } /* for (i) */
+
+ close_db(f);
+
+ for (i = 0; i < 1024; i++) {
+ NickAlias *next;
+
+ for (na = nalists[i]; na; na = next) {
+ next = na->next;
+ /* We check for coreless nicks (although it should never happen) */
+ if (!na->nc) {
+ alog("%s: while loading database: %s has no core! We delete it.", s_NickServ, na->nick);
+ delnick(na);
+ continue;
+ }
+
+ /* Add the Services root flag if needed. */
+ for (j = 0; j < RootNumber; j++)
+ if (!stricmp(ServicesRoots[j], na->nick))
+ na->nc->flags |= NI_SERVICES_ROOT;
+ }
+ }
+}
+
+#undef SAFE
+
+/*************************************************************************/
+
+#define SAFE(x) do { \
+ if ((x) < 0) { \
+ restore_db(f); \
+ log_perror("Write error on %s", NickDBName); \
+ if (time(NULL) - lastwarn > WarningTimeout) { \
+ anope_cmd_global(NULL, "Write error on %s: %s", NickDBName, \
+ strerror(errno)); \
+ lastwarn = time(NULL); \
+ } \
+ return; \
+ } \
+} while (0)
+
+
+
+void save_ns_dbase(void)
+{
+ dbFILE *f;
+ int i, j;
+ NickAlias *na;
+ NickCore *nc;
+ char **access;
+ Memo *memos;
+ static time_t lastwarn = 0;
+
+ if (!(f = open_db(s_NickServ, NickDBName, "w", NICK_VERSION)))
+ return;
+
+ for (i = 0; i < 1024; i++) {
+ for (nc = nclists[i]; nc; nc = nc->next) {
+ SAFE(write_int8(1, f));
+
+ SAFE(write_string(nc->display, f));
+ SAFE(write_buffer(nc->pass, f));
+
+ SAFE(write_string(nc->email, f));
+ SAFE(write_string(nc->greet, f));
+ SAFE(write_int32(nc->icq, f));
+ SAFE(write_string(nc->url, f));
+
+ SAFE(write_int32(nc->flags, f));
+ SAFE(write_int16(nc->language, f));
+
+ SAFE(write_int16(nc->accesscount, f));
+ for (j = 0, access = nc->access; j < nc->accesscount;
+ j++, access++)
+ SAFE(write_string(*access, f));
+
+ SAFE(write_int16(nc->memos.memocount, f));
+ SAFE(write_int16(nc->memos.memomax, f));
+ memos = nc->memos.memos;
+ for (j = 0; j < nc->memos.memocount; j++, memos++) {
+ SAFE(write_int32(memos->number, f));
+ SAFE(write_int16(memos->flags, f));
+ SAFE(write_int32(memos->time, f));
+ SAFE(write_buffer(memos->sender, f));
+ SAFE(write_string(memos->text, f));
+ }
+
+ SAFE(write_int16(nc->channelcount, f));
+ SAFE(write_int16(nc->channelmax, f));
+
+ } /* for (nc) */
+
+ SAFE(write_int8(0, f));
+
+ } /* for (i) */
+
+ for (i = 0; i < 1024; i++) {
+ for (na = nalists[i]; na; na = na->next) {
+ SAFE(write_int8(1, f));
+
+ SAFE(write_string(na->nick, f));
+
+ SAFE(write_string(na->last_usermask, f));
+ SAFE(write_string(na->last_realname, f));
+ SAFE(write_string(na->last_quit, f));
+
+ SAFE(write_int32(na->time_registered, f));
+ SAFE(write_int32(na->last_seen, f));
+
+ SAFE(write_int16(na->status, f));
+
+ SAFE(write_string(na->nc->display, f));
+
+ } /* for (na) */
+ SAFE(write_int8(0, f));
+ } /* for (i) */
+
+ close_db(f);
+
+}
+
+void save_ns_req_dbase(void)
+{
+ dbFILE *f;
+ int i;
+ NickRequest *nr;
+ static time_t lastwarn = 0;
+
+ if (!(f = open_db(s_NickServ, PreNickDBName, "w", PRE_NICK_VERSION)))
+ return;
+
+ for (i = 0; i < 1024; i++) {
+ for (nr = nrlists[i]; nr; nr = nr->next) {
+ SAFE(write_int8(1, f));
+ SAFE(write_string(nr->nick, f));
+ SAFE(write_string(nr->passcode, f));
+ SAFE(write_buffer(nr->password, f));
+ SAFE(write_string(nr->email, f));
+ SAFE(write_int32(nr->requested, f));
+ SAFE(write_int8(0, f));
+ }
+ }
+ close_db(f);
+
+}
+
+#undef SAFE
+
+void save_ns_rdb_dbase(void)
+{
+#ifdef USE_RDB
+ int i;
+ NickAlias *na;
+ NickCore *nc;
+
+ if (!rdb_open())
+ return;
+
+ if (rdb_tag_table("anope_ns_core") == 0) {
+ alog("Unable to tag 'anope_ns_core' - NickServ RDB save failed.");
+ rdb_close();
+ return;
+ }
+ if (rdb_tag_table("anope_ns_alias") == 0) {
+ alog("Unable to tag 'anope_ns_alias' - NickServ RDB save failed.");
+ rdb_close();
+ return;
+ }
+ if (rdb_tag_table("anope_ns_access") == 0) {
+ alog("Unable to tag 'anope_ns_access' - NickServ RDB save failed.");
+ rdb_close();
+ return;
+ }
+ if (rdb_tag_table_where("anope_ms_info", "serv='NICK'") == 0) {
+ alog("Unable to tag 'anope_ms_info' - NickServ RDB save failed.");
+ rdb_close();
+ return;
+ }
+
+ for (i = 0; i < 1024; i++) {
+ for (nc = nclists[i]; nc; nc = nc->next) {
+ if (rdb_save_ns_core(nc) == 0) {
+ alog("Unable to save NickCore for '%s' - NickServ RDB save failed.", nc->display);
+ rdb_close();
+ return;
+ }
+ } /* for (nc) */
+ } /* for (i) */
+
+ for (i = 0; i < 1024; i++) {
+ for (na = nalists[i]; na; na = na->next) {
+ if (rdb_save_ns_alias(na) == 0) {
+ alog("Unable to save NickAlias for '%s' - NickServ RDB save failed.", na->nick);
+ rdb_close();
+ return;
+ }
+ } /* for (na) */
+ } /* for (i) */
+
+ if (rdb_clean_table("anope_ns_core") == 0) {
+ alog("Unable to clean table 'anope_ns_core' - NickServ RDB save failed.");
+ rdb_close();
+ return;
+ }
+ if (rdb_clean_table("anope_ns_alias") == 0) {
+ alog("Unable to clean table 'anope_ns_alias' - NickServ RDB save failed.");
+ rdb_close();
+ return;
+ }
+ if (rdb_clean_table("anope_ns_access") == 0) {
+ alog("Unable to clean table 'anope_ns_access' - NickServ RDB save failed.");
+ rdb_close();
+ return;
+ }
+ if (rdb_clean_table_where("anope_ms_info", "serv='NICK'") == 0)
+ alog("Unable to clean table 'anope_ms_info' - NickServ RDB save failed.");
+
+ rdb_close();
+#endif
+}
+
+void save_ns_req_rdb_dbase(void)
+{
+#ifdef USE_RDB
+ int i;
+ NickRequest *nr;
+
+ if (!rdb_open())
+ return;
+
+ if (rdb_tag_table("anope_ns_request") == 0) {
+ alog("Unable to tag table 'anope_ns_request' - NickServ Request RDB save failed.");
+ rdb_close();
+ return;
+ }
+
+ for (i = 0; i < 1024; i++) {
+ for (nr = nrlists[i]; nr; nr = nr->next) {
+ if (rdb_save_ns_req(nr) == 0) {
+ /* Something went wrong - abort saving */
+ alog("Unable to save NickRequest (nick '%s') - NickServ Request RDB save failed.", nr->nick);
+ rdb_close();
+ return;
+ }
+ }
+ }
+
+ if (rdb_clean_table("anope_ns_request") == 0)
+ alog("Unable to clean table 'anope_ns_request' - NickServ Request RDB save failed.");
+
+ rdb_close();
+#endif
+
+}
+
+/*************************************************************************/
+
+/* Check whether a user is on the access list of the nick they're using If
+ * not, send warnings as appropriate. If so (and not NI_SECURE), update
+ * last seen info.
+ * Return 1 if the user is valid and recognized, 0 otherwise (note
+ * that this means an NI_SECURE nick will return 0 from here).
+ * If the user's nick is not registered, 0 is returned.
+ */
+
+int validate_user(User * u)
+{
+ NickAlias *na;
+ NickRequest *nr;
+
+ int on_access;
+
+ if ((nr = findrequestnick(u->nick))) {
+ notice_lang(s_NickServ, u, NICK_IS_PREREG);
+ }
+
+ if (!(na = u->na))
+ return 0;
+
+ if (na->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_MAY_NOT_BE_USED);
+ collide(na, 0);
+ return 0;
+ }
+
+ if (na->nc->flags & NI_SUSPENDED) {
+ notice_lang(s_NickServ, u, NICK_X_SUSPENDED, u->nick);
+ collide(na, 0);
+ return 0;
+ }
+
+ on_access = is_on_access(u, na->nc);
+ if (on_access)
+ na->status |= NS_ON_ACCESS;
+
+ if (!(na->nc->flags & NI_SECURE) && on_access) {
+ na->status |= NS_RECOGNIZED;
+ na->last_seen = time(NULL);
+ if (na->last_usermask)
+ free(na->last_usermask);
+ na->last_usermask =
+ scalloc(strlen(common_get_vident(u)) +
+ strlen(common_get_vhost(u)) + 2, 1);
+ sprintf(na->last_usermask, "%s@%s", common_get_vident(u),
+ common_get_vhost(u));
+ if (na->last_realname)
+ free(na->last_realname);
+ na->last_realname = sstrdup(u->realname);
+ return 1;
+ }
+
+ if (on_access || !(na->nc->flags & NI_KILL_IMMED)) {
+ if (na->nc->flags & NI_SECURE)
+ notice_lang(s_NickServ, u, NICK_IS_SECURE, s_NickServ);
+ else
+ notice_lang(s_NickServ, u, NICK_IS_REGISTERED, s_NickServ);
+ }
+
+ if ((na->nc->flags & NI_KILLPROTECT) && !on_access) {
+ if (na->nc->flags & NI_KILL_IMMED) {
+ notice_lang(s_NickServ, u, FORCENICKCHANGE_NOW);
+ collide(na, 0);
+ } else if (na->nc->flags & NI_KILL_QUICK) {
+ notice_lang(s_NickServ, u, FORCENICKCHANGE_IN_20_SECONDS);
+ add_ns_timeout(na, TO_COLLIDE, 20);
+ } else {
+ notice_lang(s_NickServ, u, FORCENICKCHANGE_IN_1_MINUTE);
+ add_ns_timeout(na, TO_COLLIDE, 60);
+ }
+ }
+
+ return 0;
+}
+
+/*************************************************************************/
+
+/* Cancel validation flags for a nick (i.e. when the user with that nick
+ * signs off or changes nicks). Also cancels any impending collide. */
+
+void cancel_user(User * u)
+{
+ NickAlias *na = u->na;
+
+ if (na) {
+ if (na->status & NS_GUESTED) {
+ if (ircd->svshold) {
+ if (UseSVSHOLD) {
+ anope_cmd_svshold(na->nick);
+ } else {
+ if (ircd->svsnick) {
+ anope_cmd_guest_nick(u->nick, NSEnforcerUser,
+ NSEnforcerHost,
+ "Services Enforcer", "+");
+ add_ns_timeout(na, TO_RELEASE, NSReleaseTimeout);
+ } else {
+ anope_cmd_svskill(s_NickServ, u->nick,
+ "Killing to enforce nick");
+ }
+ }
+ } else {
+ if (ircd->svsnick) {
+ anope_cmd_guest_nick(u->nick, NSEnforcerUser,
+ NSEnforcerHost,
+ "Services Enforcer", "+");
+ add_ns_timeout(na, TO_RELEASE, NSReleaseTimeout);
+ } else {
+ anope_cmd_svskill(s_NickServ, u->nick,
+ "Killing to enforce nick");
+ }
+ }
+ na->status &= ~NS_TEMPORARY;
+ na->status |= NS_KILL_HELD;
+ } else {
+ na->status &= ~NS_TEMPORARY;
+ }
+ del_ns_timeout(na, TO_COLLIDE);
+ }
+}
+
+/*************************************************************************/
+
+/* Return whether a user has identified for their nickname. */
+
+int nick_identified(User * u)
+{
+ if (u) {
+ if (u->na) {
+ if (u->na->status) {
+ return (u->na->status & NS_IDENTIFIED);
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/*************************************************************************/
+
+/* Return whether a user is recognized for their nickname. */
+
+int nick_recognized(User * u)
+{
+ if (u) {
+ if (u->na) {
+ if (u->na->status) {
+ return (u->na->status & (NS_IDENTIFIED | NS_RECOGNIZED));
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/*************************************************************************/
+
+/* Returns whether a user is identified AND in the group nc */
+
+int group_identified(User * u, NickCore * nc)
+{
+ return nick_identified(u) && u->na->nc == nc;
+}
+
+/*************************************************************************/
+
+/* Remove all nicks which have expired. Also update last-seen time for all
+ * nicks.
+ */
+
+void expire_nicks()
+{
+ int i;
+ NickAlias *na, *next;
+ time_t now = time(NULL);
+ char *tmpnick;
+
+ for (i = 0; i < 1024; i++) {
+ for (na = nalists[i]; na; na = next) {
+ next = na->next;
+
+ if (na->u
+ && ((na->nc->flags & NI_SECURE) ? nick_identified(na->u) :
+ nick_recognized(na->u))) {
+ if (debug >= 2)
+ alog("debug: NickServ: updating last seen time for %s",
+ na->nick);
+ na->last_seen = now;
+ continue;
+ }
+
+ if (NSExpire && now - na->last_seen >= NSExpire
+ && !(na->status & (NS_VERBOTEN | NS_NO_EXPIRE))
+ && !(na->nc->flags & (NI_SUSPENDED))) {
+ alog("Expiring nickname %s (group: %s) (e-mail: %s)",
+ na->nick, na->nc->display,
+ (na->nc->email ? na->nc->email : "none"));
+ tmpnick = sstrdup(na->nick);
+ delnick(na);
+ send_event(EVENT_NICK_EXPIRE, 1, tmpnick);
+ free(tmpnick);
+ }
+ }
+ }
+}
+
+void expire_requests()
+{
+ int i;
+ NickRequest *nr, *next;
+ time_t now = time(NULL);
+ for (i = 0; i < 1024; i++) {
+ for (nr = nrlists[i]; nr; nr = next) {
+ next = nr->next;
+ if (NSRExpire && now - nr->requested >= NSRExpire) {
+ alog("Request for nick %s expiring", nr->nick);
+ delnickrequest(nr);
+ }
+ }
+ }
+}
+
+/*************************************************************************/
+/*************************************************************************/
+/* Return the NickRequest structire for the given nick, or NULL */
+
+NickRequest *findrequestnick(const char *nick)
+{
+ NickRequest *nr;
+
+ if (!*nick || !nick) {
+ if (debug) {
+ alog("debug: findrequestnick() called with NULL values");
+ }
+ return NULL;
+ }
+
+ for (nr = nrlists[HASH(nick)]; nr; nr = nr->next) {
+ if (stricmp(nr->nick, nick) == 0)
+ return nr;
+ }
+ return NULL;
+}
+
+/* Return the NickAlias structure for the given nick, or NULL if the nick
+ * isn't registered. */
+
+NickAlias *findnick(const char *nick)
+{
+ NickAlias *na;
+
+ if (!nick || !*nick) {
+ if (debug) {
+ alog("debug: findnick() called with NULL values");
+ }
+ return NULL;
+ }
+
+ for (na = nalists[HASH(nick)]; na; na = na->next) {
+ if (stricmp(na->nick, nick) == 0)
+ return na;
+ }
+
+ return NULL;
+}
+
+/*************************************************************************/
+
+/* Return the NickCore structure for the given nick, or NULL if the core
+ * doesn't exist. */
+
+NickCore *findcore(const char *nick)
+{
+ NickCore *nc;
+
+ if (!nick || !*nick) {
+ if (debug) {
+ alog("debug: findcore() called with NULL values");
+ }
+ return NULL;
+ }
+
+ for (nc = nclists[HASH(nick)]; nc; nc = nc->next) {
+ if (stricmp(nc->display, nick) == 0)
+ return nc;
+ }
+
+ return NULL;
+}
+
+/*************************************************************************/
+/*********************** NickServ private routines ***********************/
+/*************************************************************************/
+
+/* Is the given user's address on the given nick's access list? Return 1
+ * if so, 0 if not. */
+
+int is_on_access(User * u, NickCore * nc)
+{
+ int i;
+ char *buf;
+ char *buf2 = NULL;
+
+ if (nc->accesscount == 0)
+ return 0;
+
+ buf = scalloc(strlen(u->username) + strlen(u->host) + 2, 1);
+ sprintf(buf, "%s@%s", u->username, u->host);
+ if (ircd->vhost) {
+ if (u->vhost) {
+ buf2 = scalloc(strlen(u->username) + strlen(u->vhost) + 2, 1);
+ sprintf(buf2, "%s@%s", u->username, u->vhost);
+ }
+ }
+
+ for (i = 0; i < nc->accesscount; i++) {
+ if (match_wild_nocase(nc->access[i], buf)
+ || (ircd->vhost ? match_wild_nocase(nc->access[i], buf2) : 0)) {
+ free(buf);
+ if (ircd->vhost) {
+ if (u->vhost) {
+ free(buf2);
+ }
+ }
+ return 1;
+ }
+ }
+ free(buf);
+ if (ircd->vhost) {
+ free(buf2);
+ }
+ return 0;
+}
+
+/*************************************************************************/
+
+/* Insert a nick alias alphabetically into the database. */
+
+void alpha_insert_alias(NickAlias * na)
+{
+ NickAlias *ptr, *prev;
+ char *nick;
+ int index;
+
+ if (!na) {
+ if (debug) {
+ alog("debug: alpha_insert_alias called with NULL values");
+ }
+ return;
+ }
+
+ nick = na->nick;
+ index = HASH(nick);
+
+ for (prev = NULL, ptr = nalists[index];
+ ptr && stricmp(ptr->nick, nick) < 0; prev = ptr, ptr = ptr->next);
+ na->prev = prev;
+ na->next = ptr;
+ if (!prev)
+ nalists[index] = na;
+ else
+ prev->next = na;
+ if (ptr)
+ ptr->prev = na;
+}
+
+/*************************************************************************/
+
+/* Insert a nick core into the database. */
+
+void insert_core(NickCore * nc)
+{
+ int index;
+
+ if (!nc) {
+ if (debug) {
+ alog("debug: insert_core called with NULL values");
+ }
+ return;
+ }
+
+ index = HASH(nc->display);
+
+ nc->prev = NULL;
+ nc->next = nclists[index];
+ if (nc->next)
+ nc->next->prev = nc;
+ nclists[index] = nc;
+}
+
+/*************************************************************************/
+void insert_requestnick(NickRequest * nr)
+{
+ int index = HASH(nr->nick);
+ if (!nr) {
+ if (debug) {
+ alog("debug: insert_requestnick called with NULL values");
+ }
+ return;
+ }
+
+
+ nr->prev = NULL;
+ nr->next = nrlists[index];
+ if (nr->next)
+ nr->next->prev = nr;
+ nrlists[index] = nr;
+}
+
+/*************************************************************************/
+
+/* Sets nc->display to newdisplay. If newdisplay is NULL, it will change
+ * it to the first alias in the list.
+ */
+
+void change_core_display(NickCore * nc, char *newdisplay)
+{
+ if (!newdisplay) {
+ NickAlias *na;
+
+ if (nc->aliases.count <= 0)
+ return;
+
+ na = nc->aliases.list[0];
+ newdisplay = na->nick;
+ }
+
+ /* Log ... */
+ alog("%s: changing %s nickname group display to %s", s_NickServ,
+ nc->display, newdisplay);
+
+#ifdef USE_RDB
+ /* Reflect this change in the database right away. This
+ * ensures that we know how to deal with this "new" nick
+ * on the next /OS UPDATE might need it on /NS DROP too...
+ */
+ if (rdb_open()) {
+ if (rdb_ns_set_display(newdisplay, nc->display) == 0) {
+ alog("Unable to update display for %s - Nick Display RDB update failed.", nc->display);
+ }
+ rdb_close();
+ }
+#endif
+
+ /* Remove the core from the list */
+ if (nc->next)
+ nc->next->prev = nc->prev;
+ if (nc->prev)
+ nc->prev->next = nc->next;
+ else
+ nclists[HASH(nc->display)] = nc->next;
+
+ free(nc->display);
+ nc->display = sstrdup(newdisplay);
+ insert_core(nc);
+
+}
+
+/*************************************************************************/
+
+/* Deletes the core. This must be called only when there is no more
+ * aliases for it, because no cleanup is done.
+ * This function removes all references to the core as well.
+ */
+
+static int delcore(NickCore * nc)
+{
+ int i;
+#ifdef USE_RDB
+ static char clause[128];
+ char *q_display;
+#endif
+ /* (Hopefully complete) cleanup */
+ cs_remove_nick(nc);
+ os_remove_nick(nc);
+
+ /* Remove the core from the list */
+ if (nc->next)
+ nc->next->prev = nc->prev;
+ if (nc->prev)
+ nc->prev->next = nc->next;
+ else
+ nclists[HASH(nc->display)] = nc->next;
+
+ /* Log .. */
+ alog("%s: deleting nickname group %s", s_NickServ, nc->display);
+
+#ifdef USE_RDB
+ /* Reflect this change in the database right away. */
+ if (rdb_open()) {
+ q_display = rdb_quote(nc->display);
+ snprintf(clause, sizeof(clause), "display='%s'", q_display);
+ if (rdb_scrub_table("anope_ns_access", clause) == 0)
+ alog("Unable to scrub table 'anope_ns_access' - RDB update failed.");
+ else if (rdb_scrub_table("anope_ns_core", clause) == 0)
+ alog("Unable to scrub table 'anope_ns_core' - RDB update failed.");
+ else if (rdb_scrub_table("anope_cs_access", clause) == 0)
+ alog("Unable to scrub table 'anope_cs_access' - RDB update failed.");
+ else {
+ /* I'm unsure how to clean up the OS ADMIN/OPER list on the db */
+ /* I wish the "display" primary key would be the same on all tables */
+ snprintf(clause, sizeof(clause),
+ "receiver='%s' AND serv='NICK'", q_display);
+ if (rdb_scrub_table("anope_ms_info", clause) == 0)
+ alog("Unable to scrub table 'anope_ms_info' - RDB update failed.");
+ }
+ rdb_close();
+ free(q_display);
+ }
+#endif
+
+ /* Now we can safely free it. */
+ free(nc->display);
+
+ if (nc->email)
+ free(nc->email);
+ if (nc->greet)
+ free(nc->greet);
+ if (nc->url)
+ free(nc->url);
+
+ if (nc->access) {
+ for (i = 0; i < nc->accesscount; i++) {
+ if (nc->access[i])
+ free(nc->access[i]);
+ }
+ free(nc->access);
+ }
+
+ if (nc->memos.memos) {
+ for (i = 0; i < nc->memos.memocount; i++) {
+ if (nc->memos.memos[i].text)
+ free(nc->memos.memos[i].text);
+ moduleCleanStruct(&nc->memos.memos[i].moduleData);
+ }
+ free(nc->memos.memos);
+ }
+
+ moduleCleanStruct(&nc->moduleData);
+
+ free(nc);
+
+ return 1;
+}
+
+
+/*************************************************************************/
+int delnickrequest(NickRequest * nr)
+{
+ if (nr) {
+ nrlists[HASH(nr->nick)] = nr->next;
+ if (nr->nick)
+ free(nr->nick);
+ if (nr->passcode)
+ free(nr->passcode);
+ if (nr->email)
+ free(nr->email);
+ free(nr);
+ }
+
+ return 0;
+}
+
+/*************************************************************************/
+
+/* Deletes an alias. The core will also be deleted if it has no more
+ * nicks attached to it. Easy but powerful.
+ * Well, we must also take care that the nick being deleted is not
+ * the core display, and if so, change it to the next alias in the list,
+ * otherwise weird things will happen.
+ * Returns 1 on success, 0 otherwise.
+ */
+
+int delnick(NickAlias * na)
+{
+#ifdef USE_RDB
+ static char clause[128];
+ char *q_nick;
+#endif
+ /* First thing to do: remove any timeout belonging to the nick we're deleting */
+ clean_ns_timeouts(na);
+
+ /* Second thing to do: look for an user using the alias
+ * being deleted, and make appropriate changes */
+
+ if (na->u) {
+ na->u->na = NULL;
+
+ if (ircd->modeonunreg)
+ common_svsmode(na->u, ircd->modeonunreg, "1");
+
+ }
+
+ delHostCore(na->nick); /* delete any vHost's for this nick */
+
+ /* Accept nicks that have no core, because of database load functions */
+ if (na->nc) {
+ /* Next: see if our core is still useful. */
+ slist_remove(&na->nc->aliases, na);
+ if (na->nc->aliases.count == 0) {
+ if (!delcore(na->nc))
+ return 0;
+ na->nc = NULL;
+ } else {
+ /* Display updating stuff */
+ if (!stricmp(na->nick, na->nc->display))
+ change_core_display(na->nc, NULL);
+ }
+ }
+
+ /* Remove us from the aliases list */
+ if (na->next)
+ na->next->prev = na->prev;
+ if (na->prev)
+ na->prev->next = na->next;
+ else
+ nalists[HASH(na->nick)] = na->next;
+
+#ifdef USE_RDB
+ /* Reflect this change in the database right away. */
+ if (rdb_open()) {
+ q_nick = rdb_quote(na->nick);
+ snprintf(clause, sizeof(clause), "nick='%s'", q_nick);
+ if (rdb_scrub_table("anope_ns_alias", clause) == 0)
+ alog("Unable to scrub table 'anope_ns_alias' - RDB update failed");
+ rdb_close();
+ free(q_nick);
+ }
+#endif
+
+ free(na->nick);
+ if (na->last_usermask)
+ free(na->last_usermask);
+ if (na->last_realname)
+ free(na->last_realname);
+ if (na->last_quit)
+ free(na->last_quit);
+
+ moduleCleanStruct(&na->moduleData);
+
+ free(na);
+
+
+ return 1;
+}
+
+/*************************************************************************/
+/*************************************************************************/
+
+/* Collide a nick.
+ *
+ * When connected to a network using DALnet servers, version 4.4.15 and above,
+ * Services is now able to force a nick change instead of killing the user.
+ * The new nick takes the form "Guest######". If a nick change is forced, we
+ * do not introduce the enforcer nick until the user's nick actually changes.
+ * This is watched for and done in cancel_user(). -TheShadow
+ */
+
+void collide(NickAlias * na, int from_timeout)
+{
+ char guestnick[NICKMAX];
+
+ if (!from_timeout)
+ del_ns_timeout(na, TO_COLLIDE);
+
+ /* Old system was unsure since there can be more than one collide
+ * per second. So let use another safer method.
+ * --lara
+ */
+ /* So you should check the length of NSGuestNickPrefix, eh Lara?
+ * --Certus
+ */
+
+ if (ircd->svsnick) {
+ /* We need to make sure the guestnick is free -- heinz */
+ do {
+ snprintf(guestnick, sizeof(guestnick), "%s%d",
+ NSGuestNickPrefix, getrandom16());
+ } while (finduser(guestnick));
+ notice_lang(s_NickServ, na->u, FORCENICKCHANGE_CHANGING,
+ guestnick);
+ anope_cmd_svsnick(na->nick, guestnick, time(NULL));
+ na->status |= NS_GUESTED;
+ } else {
+ kill_user(s_NickServ, na->nick, "Services nickname-enforcer kill");
+ }
+}
+
+/*************************************************************************/
+
+/* Release hold on a nick. */
+
+void release(NickAlias * na, int from_timeout)
+{
+ if (!from_timeout)
+ del_ns_timeout(na, TO_RELEASE);
+ if (ircd->svshold) {
+ if (UseSVSHOLD) {
+ anope_cmd_release_svshold(na->nick);
+ } else {
+ anope_cmd_quit(na->nick, NULL);
+ }
+ } else {
+ anope_cmd_quit(na->nick, NULL);
+ }
+ na->status &= ~NS_KILL_HELD;
+}
+
+/*************************************************************************/
+/*************************************************************************/
+
+static struct my_timeout {
+ struct my_timeout *next, *prev;
+ NickAlias *na;
+ Timeout *to;
+ int type;
+} *my_timeouts;
+
+/*************************************************************************/
+
+/* Remove a collide/release timeout from our private list. */
+
+static void rem_ns_timeout(NickAlias * na, int type)
+{
+ struct my_timeout *t, *t2;
+
+ t = my_timeouts;
+ while (t) {
+ if (t->na == na && t->type == type) {
+ t2 = t->next;
+ if (t->next)
+ t->next->prev = t->prev;
+ if (t->prev)
+ t->prev->next = t->next;
+ else
+ my_timeouts = t->next;
+ free(t);
+ t = t2;
+ } else {
+ t = t->next;
+ }
+ }
+}
+
+/*************************************************************************/
+
+/* Collide a nick on timeout. */
+
+static void timeout_collide(Timeout * t)
+{
+ NickAlias *na = t->data;
+
+ rem_ns_timeout(na, TO_COLLIDE);
+ /* If they identified or don't exist anymore, don't kill them. */
+ if ((na->status & NS_IDENTIFIED) || !na->u
+ || na->u->my_signon > t->settime)
+ return;
+ /* The RELEASE timeout will always add to the beginning of the
+ * list, so we won't see it. Which is fine because it can't be
+ * triggered yet anyway. */
+ collide(na, 1);
+}
+
+/*************************************************************************/
+
+/* Release a nick on timeout. */
+
+static void timeout_release(Timeout * t)
+{
+ NickAlias *na = t->data;
+
+ rem_ns_timeout(na, TO_RELEASE);
+ release(na, 1);
+}
+
+/*************************************************************************/
+
+/* Add a collide/release timeout. */
+
+static void add_ns_timeout(NickAlias * na, int type, time_t delay)
+{
+ Timeout *to;
+ struct my_timeout *t;
+ void (*timeout_routine) (Timeout *);
+
+ if (type == TO_COLLIDE)
+ timeout_routine = timeout_collide;
+ else if (type == TO_RELEASE)
+ timeout_routine = timeout_release;
+ else {
+ alog("NickServ: unknown timeout type %d! na=0x%p (%s), delay=%ld",
+ type, (void *) na, na->nick, (long int) delay);
+ return;
+ }
+
+ to = add_timeout(delay, timeout_routine, 0);
+ to->data = na;
+
+ t = scalloc(sizeof(struct my_timeout), 1);
+ t->na = na;
+ t->to = to;
+ t->type = type;
+
+ t->prev = NULL;
+ t->next = my_timeouts;
+ my_timeouts = t;
+ /* Andy Church should stop coding while being drunk.
+ * Here's the two lines he forgot that produced the timed_update evil bug
+ * and a *big* memory leak.
+ */
+ if (t->next)
+ t->next->prev = t;
+}
+
+/*************************************************************************/
+
+/* Delete a collide/release timeout. */
+
+void del_ns_timeout(NickAlias * na, int type)
+{
+ struct my_timeout *t, *t2;
+
+ t = my_timeouts;
+ while (t) {
+ if (t->na == na && t->type == type) {
+ t2 = t->next;
+ if (t->next)
+ t->next->prev = t->prev;
+ if (t->prev)
+ t->prev->next = t->next;
+ else
+ my_timeouts = t->next;
+ del_timeout(t->to);
+ free(t);
+ t = t2;
+ } else {
+ t = t->next;
+ }
+ }
+}
+
+/*************************************************************************/
+
+/* Deletes all timeouts belonging to a given nick.
+ * This should only be called before nick deletion.
+ */
+
+void clean_ns_timeouts(NickAlias * na)
+{
+ struct my_timeout *t, *next;
+
+ for (t = my_timeouts; t; t = next) {
+ next = t->next;
+ if (t->na == na) {
+ if (debug)
+ alog("debug: %s deleting timeout type %d from %s",
+ s_NickServ, t->type, t->na->nick);
+ /* If the timeout has the TO_RELEASE type, we should release the user */
+ if (t->type == TO_RELEASE)
+ release(na, 1);
+ if (t->next)
+ t->next->prev = t->prev;
+ if (t->prev)
+ t->prev->next = t->next;
+ else
+ my_timeouts = t->next;
+ del_timeout(t->to);
+ free(t);
+ }
+ }
+}
+
+/*************************************************************************/
+/*********************** NickServ command routines ***********************/
+/*************************************************************************/
+
+
+/* We don't use this function but we keep it for module coders -certus */
+int should_mode_change(int16 status, int16 mode)
+{
+ switch (mode) {
+ case CUS_OP:
+ if (status & CUS_OP) {
+ return 0;
+ }
+ break;
+ case CUS_VOICE:
+ if (status & CUS_OP) {
+ return 0;
+ }
+ if (status & CUS_HALFOP) {
+ return 0;
+ }
+ if (status & CUS_VOICE) {
+ return 0;
+ }
+ return 1;
+ break;
+ case CUS_HALFOP:
+ if (status & CUS_OP) {
+ return 0;
+ }
+ if (status & CUS_HALFOP) {
+ return 0;
+ }
+ return 1;
+ break;
+ case CUS_OWNER:
+ if (ircd->owner) {
+ if (status & CUS_OWNER) {
+ return 0;
+ }
+ }
+ break;
+ case CUS_PROTECT:
+ if (ircd->protect) {
+ if (status & CUS_PROTECT) {
+ return 0;
+ }
+ }
+ break;
+ }
+ return 1;
+}
+
+/*************************************************************************/
+
+int do_setmodes(User * u)
+{
+ struct u_chanlist *uc;
+ Channel *c;
+
+ /* Walk users current channels */
+ for (uc = u->chans; uc; uc = uc->next) {
+ if ((c = uc->chan))
+ chan_set_correct_modes(u, c, 1);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+/*
+ * Nick tracking
+ */
+
+/**
+ * Start Nick tracking and store the nick core display under the user struct.
+ * @param u The user to track nicks for
+ **/
+void nsStartNickTracking(User * u)
+{
+ NickCore *nc;
+
+ /* We only track identified users */
+ if (nick_identified(u)) {
+ nc = u->na->nc;
+
+ /* Release memory if needed */
+ if (u->nickTrack)
+ free(u->nickTrack);
+
+ /* Copy the nick core displayed nick to
+ the user structure for further checks */
+ u->nickTrack = sstrdup(nc->display);
+ }
+}
+
+/**
+ * Stop Nick tracking and remove the nick core display under the user struct.
+ * @param u The user to stop tracking for
+ **/
+void nsStopNickTracking(User * u)
+{
+ /* Simple enough. If its there, release it */
+ if (u->nickTrack) {
+ free(u->nickTrack);
+ u->nickTrack = NULL;
+ }
+}
+
+/**
+ * Boolean function to check if the user requesting a nick has the tracking
+ * signature of that core in its structure.
+ * @param u The user whom to check tracking for
+ **/
+int nsCheckNickTracking(User * u)
+{
+ NickCore *nc;
+ NickAlias *na;
+ char *nick;
+
+ /* No nick alias or nick return false by default */
+ if ((!(na = u->na)) || (!(nick = na->nick))) {
+ return 0;
+ }
+
+ /* nick is forbidden best return 0 */
+ if (na->status & NS_VERBOTEN) {
+ return 0;
+ }
+
+ /* Get the core for the requested nick */
+ nc = na->nc;
+
+ /* If the core and the tracking displayed nick are there,
+ * and they match, return true
+ */
+ if (nc && u->nickTrack && (strcmp(nc->display, u->nickTrack) == 0))
+ return 1;
+ else
+ return 0;
+}