From a1479a87757fb5e9b48f3c8bb5dd9cc247406f1a Mon Sep 17 00:00:00 2001 From: "rob rob@31f1291d-b8d6-0310-a050-a5561fc1590b" Date: Mon, 19 Jul 2004 14:08:30 +0000 Subject: BUILD : 1.7.4 (264) BUGS : N/A NOTES : Switched to autoconf - try to commit part 1 git-svn-id: svn://svn.anope.org/anope/trunk@264 31f1291d-b8d6-0310-a050-a5561fc1590b git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@169 5417fbe8-f217-4b02-8779-1006273d7864 --- src/nickserv.c | 4169 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4169 insertions(+) create mode 100644 src/nickserv.c (limited to 'src/nickserv.c') diff --git a/src/nickserv.c b/src/nickserv.c new file mode 100644 index 000000000..ec10e4fe0 --- /dev/null +++ b/src/nickserv.c @@ -0,0 +1,4169 @@ +/* NickServ functions. + * + * (C) 2003 Anope Team + * Contact us at info@anope.org + * + * Please read COPYING and README for furhter 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 */ + +/*************************************************************************/ + +extern char *getvHost(char *nick); + +static int is_on_access(User * u, NickCore * nc); +void alpha_insert_alias(NickAlias * na); +void insert_core(NickCore * nc); +void insert_requestnick(NickRequest * nr); +static NickAlias *makenick(const char *nick); +static NickRequest *makerequest(const char *nick); +static NickAlias *makealias(const char *nick, NickCore * nc); +static void change_core_display(NickCore * nc, char *newdisplay); +static int group_identified(User * u, NickCore * nc); + +static void collide(NickAlias * na, int from_timeout); +static void release(NickAlias * na, int from_timeout); +static void add_ns_timeout(NickAlias * na, int type, time_t delay); +static void del_ns_timeout(NickAlias * na, int type); +int delnickrequest(NickRequest * nr); +NickRequest *findrequestnick(const char *nick); +static int do_sendregmail(User * u, NickRequest * nr); +static int do_setmodes(User * u); + +static int do_help(User * u); +static int do_register(User * u); +static int do_confirm(User * u); +static int do_group(User * u); +static int do_nickupdate(User * u); +static int do_identify(User * u); +static int do_logout(User * u); +static int do_drop(User * u); +static int do_set(User * u); +static int do_set_display(User * u, NickCore * nc, char *param); +static int do_set_password(User * u, NickCore * nc, char *param); +static int do_set_language(User * u, NickCore * nc, char *param); +static int do_set_url(User * u, NickCore * nc, char *param); +static int do_set_email(User * u, NickCore * nc, char *param); +static int do_set_greet(User * u, NickCore * nc, char *param); +static int do_set_icq(User * u, NickCore * nc, char *param); +static int do_set_kill(User * u, NickCore * nc, char *param); +static int do_set_secure(User * u, NickCore * nc, char *param); +static int do_set_private(User * u, NickCore * nc, char *param); +static int do_set_msg(User * u, NickCore * nc, char *param); +static int do_set_hide(User * u, NickCore * nc, char *param); +static int do_set_noexpire(User * u, NickAlias * nc, char *param); +static int do_access(User * u); +static int do_info(User * u); +static int do_list(User * u); +static int do_alist(User * u); +static int do_glist(User * u); +static int do_recover(User * u); +static int do_release(User * u); +static int do_ghost(User * u); +static int do_status(User * u); +static int do_getpass(User * u); +static int do_getemail(User * u); +static int do_sendpass(User * u); +static int do_forbid(User * u); +static int do_resend(User * u); + +/* Obsolete commands */ +static int do_link(User * u); +static int do_unlink(User * u); +static int do_listlinks(User * u); + + +/*************************************************************************/ +/* *INDENT-OFF* */ +void moduleAddNickServCmds(void) { + Command *c; + c = createCommand("HELP", do_help, NULL, -1, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("REGISTER", do_register, NULL, NICK_HELP_REGISTER, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("RESEND", do_resend, NULL, -1, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("CONFIRM", do_confirm, NULL, -1, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("GROUP", do_group, NULL, NICK_HELP_GROUP, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("UPDATE", do_nickupdate, NULL, NICK_HELP_UPDATE, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("ID", do_identify, NULL, NICK_HELP_IDENTIFY, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("IDENTIFY", do_identify, NULL, NICK_HELP_IDENTIFY, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SIDENTIFY",do_identify, NULL, -1, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("LOGOUT", do_logout, NULL, -1,NICK_HELP_LOGOUT, NICK_SERVADMIN_HELP_LOGOUT,NICK_SERVADMIN_HELP_LOGOUT, NICK_SERVADMIN_HELP_LOGOUT); addCoreCommand(NICKSERV,c); + c = createCommand("DROP", do_drop, NULL, -1,NICK_HELP_DROP, NICK_SERVADMIN_HELP_DROP,NICK_SERVADMIN_HELP_DROP, NICK_SERVADMIN_HELP_DROP); addCoreCommand(NICKSERV,c); + c = createCommand("LINK", do_link, NULL, -1, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("UNLINK", do_unlink, NULL, -1, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("LISTLINKS",do_listlinks,NULL, -1, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("ACCESS", do_access, NULL, NICK_HELP_ACCESS, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SET", do_set, NULL, NICK_HELP_SET,-1, NICK_SERVADMIN_HELP_SET,NICK_SERVADMIN_HELP_SET, NICK_SERVADMIN_HELP_SET); addCoreCommand(NICKSERV,c); + c = createCommand("SET DISPLAY", NULL, NULL, NICK_HELP_SET_DISPLAY, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SET PASSWORD", NULL, NULL, NICK_HELP_SET_PASSWORD, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SET URL", NULL, NULL, NICK_HELP_SET_URL, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SET EMAIL", NULL, NULL, NICK_HELP_SET_EMAIL, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SET ICQ", NULL, NULL, NICK_HELP_SET_ICQ, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SET GREET", NULL, NULL, NICK_HELP_SET_GREET, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SET KILL", NULL, NULL, NICK_HELP_SET_KILL, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SET SECURE", NULL, NULL, NICK_HELP_SET_SECURE, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SET PRIVATE", NULL, NULL, NICK_HELP_SET_PRIVATE, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SET MSG", NULL, NULL, NICK_HELP_SET_MSG, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SET HIDE", NULL, NULL, NICK_HELP_SET_HIDE, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SET NOEXPIRE", NULL, NULL, -1, -1,NICK_SERVADMIN_HELP_SET_NOEXPIRE,NICK_SERVADMIN_HELP_SET_NOEXPIRE,NICK_SERVADMIN_HELP_SET_NOEXPIRE); addCoreCommand(NICKSERV,c); + c = createCommand("RECOVER", do_recover, NULL, NICK_HELP_RECOVER, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("RELEASE", do_release, NULL, NICK_HELP_RELEASE, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("GHOST", do_ghost, NULL, NICK_HELP_GHOST, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("INFO", do_info, NULL, NICK_HELP_INFO,-1, NICK_HELP_INFO, NICK_SERVADMIN_HELP_INFO,NICK_SERVADMIN_HELP_INFO); addCoreCommand(NICKSERV,c); + c = createCommand("LIST", do_list, NULL, -1,NICK_HELP_LIST, NICK_SERVADMIN_HELP_LIST,NICK_SERVADMIN_HELP_LIST, NICK_SERVADMIN_HELP_LIST); addCoreCommand(NICKSERV,c); + c = createCommand("ALIST", do_alist, NULL, -1,NICK_HELP_ALIST, NICK_SERVADMIN_HELP_ALIST,NICK_SERVADMIN_HELP_ALIST, NICK_SERVADMIN_HELP_ALIST); addCoreCommand(NICKSERV,c); + c = createCommand("GLIST", do_glist, NULL, -1,NICK_HELP_GLIST, NICK_SERVADMIN_HELP_GLIST,NICK_SERVADMIN_HELP_GLIST, NICK_SERVADMIN_HELP_GLIST); addCoreCommand(NICKSERV,c); + c = createCommand("STATUS", do_status, NULL, NICK_HELP_STATUS, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("SENDPASS", do_sendpass, NULL, NICK_HELP_SENDPASS, -1,-1,-1,-1); addCoreCommand(NICKSERV,c); + c = createCommand("GETPASS", do_getpass, is_services_admin, -1,-1, NICK_SERVADMIN_HELP_GETPASS,NICK_SERVADMIN_HELP_GETPASS, NICK_SERVADMIN_HELP_GETPASS); addCoreCommand(NICKSERV,c); + c = createCommand("GETEMAIL", do_getemail, is_services_admin, -1,-1, NICK_SERVADMIN_HELP_GETEMAIL,NICK_SERVADMIN_HELP_GETEMAIL, NICK_SERVADMIN_HELP_GETEMAIL); addCoreCommand(NICKSERV,c); + c = createCommand("FORBID", do_forbid, is_services_admin, -1,-1, NICK_SERVADMIN_HELP_FORBID,NICK_SERVADMIN_HELP_FORBID, NICK_SERVADMIN_HELP_FORBID); addCoreCommand(NICKSERV,c); +} +/* *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 ). 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(buf, sizeof(buf) - (end - buf), "%sSecurity", + need_comma ? commastr : ""); + need_comma = 1; + } + if (na->nc->flags & NI_PRIVATE) { + end += snprintf(buf, sizeof(buf) - (end - buf), "%sPrivate", + need_comma ? commastr : ""); + need_comma = 1; + } + if (na->status & NS_NO_EXPIRE) { + end += snprintf(buf, sizeof(buf) - (end - buf), "%sNo Expire", + need_comma ? commastr : ""); + need_comma = 1; + } + printf(" Options: %s\n", *buf ? buf : "None"); + + } 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->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 = "\1"; + notice(s_NickServ, u->nick, "\1PING %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, m; + NickAlias *na, *na2, *next; + NickCore *nc; + int failed = 0; + + int16 tmp16; + int32 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; +#ifdef USE_ENCRYPTION + if (!(na->status & (NS_OLD_ENCRYPTEDPW | NS_VERBOTEN))) { + if (debug) + alog("debug: %s: encrypting password for `%s' on load", + s_NickServ, na->nick); + if (encrypt_in_place(bufp, PASSMAX) < 0) + fatal("%s: Can't encrypt `%s' nickname password!", + s_NickServ, na->nick); + na->status |= NS_OLD_ENCRYPTEDPW; + } +#else + if (na->status & NS_OLD_ENCRYPTEDPW) { + /* Bail: it makes no sense to continue with encrypted + * passwords, since we won't be able to verify them */ + fatal + ("%s: load database: password for %s encrypted but encryption disabled, aborting", + s_NickServ, na->nick); + } +#endif + 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) + nc->pass = sstrdup(bufp); + else + nc->pass = NULL; /* 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(&nc->memos.memocount, f)); + SAFE(read_int16(&nc->memos.memomax, f)); + 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)); + for (m = 0; m < MAX_CMD_HASH; m++) { + memos->moduleData[m] = 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; + int32 tmp32; + int failed = 0; + + 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)); + SAFE(read_string(&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, m; + NickAlias *na, **nalast, *naprev; + NickCore *nc, **nclast, *ncprev; + int failed = 0; + int16 tmp16; + int32 tmp32; + char *s; + + 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)); + SAFE(read_string(&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; +#ifdef USE_ENCRYPTION + if (nc->pass && !(nc->flags & NI_ENCRYPTEDPW)) { + if (debug) + alog("debug: %s: encrypting password for `%s' on load", + s_NickServ, nc->display); + if (encrypt_in_place(nc->pass, PASSMAX) < 0) + fatal("%s: Can't encrypt `%s' nickname password!", + s_NickServ, nc->display); + nc->flags |= NI_ENCRYPTEDPW; + } +#else + if (nc->flags & NI_ENCRYPTEDPW) { + /* Bail: it makes no sense to continue with encrypted + * passwords, since we won't be able to verify them */ + fatal + ("%s: load database: password for %s encrypted but encryption disabled, aborting", + s_NickServ, nc->display); + } +#endif + 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(&nc->memos.memocount, f)); + SAFE(read_int16(&nc->memos.memomax, f)); + 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)); + for (m = 0; m < MAX_CMD_HASH; m++) { + memos->moduleData[m] = 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) { \ + wallops(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_string(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_string(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; + + rdb_tag_table("anope_ns_core"); + rdb_tag_table("anope_ns_alias"); + rdb_scrub_table("anope_ms_info", "serv='NICK'"); + rdb_clear_table("anope_ns_access"); + + for (i = 0; i < 1024; i++) { + for (nc = nclists[i]; nc; nc = nc->next) { + rdb_save_ns_core(nc); + + } /* for (nc) */ + } /* for (i) */ + + for (i = 0; i < 1024; i++) { + for (na = nalists[i]; na; na = na->next) { + rdb_save_ns_alias(na); + + } /* for (na) */ + } /* for (i) */ + + rdb_scrub_table("anope_ns_core", "active='0'"); + rdb_scrub_table("anope_ns_alias", "active='0'"); + rdb_close(); +#endif +} + +void save_ns_req_rdb_dbase(void) +{ +#ifdef USE_RDB + int i; + NickRequest *nr; + + if (!rdb_open()) + return; + + rdb_tag_table("anope_ns_request"); + + for (i = 0; i < 1024; i++) { + for (nr = nrlists[i]; nr; nr = nr->next) { + rdb_save_ns_req(nr); + } + } + + rdb_scrub_table("anope_ns_request", "active='0'"); + 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; + } + + 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(GetIdent(u)) + strlen(GetHost(u)) + 2, 1); + sprintf(na->last_usermask, "%s@%s", GetIdent(u), GetHost(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) { +#ifdef HAS_SVSHOLD + if (UseSVSHOLD) { + send_cmd(ServerName, "SVSHOLD %s %d :%s", na->nick, + NSReleaseTimeout, + "Being held for registered user"); + } else { +#endif + NEWNICK(u->nick, NSEnforcerUser, NSEnforcerHost, + "Services Enforcer", "+", 0); + add_ns_timeout(na, TO_RELEASE, NSReleaseTimeout); +#ifdef HAS_SVSHOLD + } +#endif + 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) +{ + return u->na && (u->na->status & NS_IDENTIFIED); +} + +/*************************************************************************/ + +/* Return whether a user is recognized for their nickname. */ + +int nick_recognized(User * u) +{ + return u->na && (u->na->status & (NS_IDENTIFIED | NS_RECOGNIZED)); +} + +/*************************************************************************/ + +/* Returns whether a user is identified AND in the group nc */ + +static 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); + + 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))) { + alog("Expiring nickname %s (group: %s) (e-mail: %s)", + na->nick, na->nc->display, + (na->nc->email ? na->nc->email : "none")); + delnick(na); + } + } + } +} + +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) + 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; + + 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; + + 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. */ + +static int is_on_access(User * u, NickCore * nc) +{ + int i; + char *buf; +#ifdef HAS_VHOST + char *buf2 = NULL; +#endif + + if (nc->accesscount == 0) + return 0; + + buf = scalloc(strlen(u->username) + strlen(u->host) + 2, 1); + sprintf(buf, "%s@%s", u->username, u->host); +#ifdef HAS_VHOST + if (u->vhost) { + buf2 = scalloc(strlen(u->username) + strlen(u->vhost) + 2, 1); + sprintf(buf2, "%s@%s", u->username, u->vhost); + } +#endif + + for (i = 0; i < nc->accesscount; i++) { + if (match_wild_nocase(nc->access[i], buf) +#ifdef HAS_VHOST + || (u->vhost ? match_wild_nocase(nc->access[i], buf2) : 0) +#endif + ) { + free(buf); +#ifdef HAS_VHOST + free(buf2); +#endif + return 1; + } + } + free(buf); +#ifdef HAS_VHOST + free(buf2); +#endif + return 0; +} + +/*************************************************************************/ + +/* Insert a nick alias alphabetically into the database. */ + +void alpha_insert_alias(NickAlias * na) +{ + NickAlias *ptr, *prev; + char *nick = na->nick; + int 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 = 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); + + nr->prev = NULL; + nr->next = nrlists[index]; + if (nr->next) + nr->next->prev = nr; + nrlists[index] = nr; +} + +/*************************************************************************/ +/* Creates a new Nick Request */ +static NickRequest *makerequest(const char *nick) +{ + NickRequest *nr; + + nr = scalloc(1, sizeof(NickRequest)); + nr->nick = sstrdup(nick); + insert_requestnick(nr); + alog("%s: Nick %s has been requested", s_NickServ, nr->nick); + return nr; +} + + + +/* Creates a full new nick (alias + core) in NickServ database. */ + +static NickAlias *makenick(const char *nick) +{ + NickAlias *na; + NickCore *nc; + + /* First make the core */ + nc = scalloc(1, sizeof(NickCore)); + nc->display = sstrdup(nick); + slist_init(&nc->aliases); + insert_core(nc); + alog("%s: group %s has been created", s_NickServ, nc->display); + + /* Then make the alias */ + na = scalloc(1, sizeof(NickAlias)); + na->nick = sstrdup(nick); + na->nc = nc; + slist_add(&nc->aliases, na); + alpha_insert_alias(na); + return na; +} + +/*************************************************************************/ + +/* Creates a new alias in NickServ database. */ + +static NickAlias *makealias(const char *nick, NickCore * nc) +{ + NickAlias *na; + + /* Just need to make the alias */ + na = scalloc(1, sizeof(NickAlias)); + na->nick = sstrdup(nick); + na->nc = nc; + slist_add(&nc->aliases, na); + alpha_insert_alias(na); + return na; +} + +/*************************************************************************/ + +/* Sets nc->display to newdisplay. If newdisplay is NULL, it will change + * it to the first alias in the list. + */ + +static 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()) { + rdb_ns_set_display(newdisplay, 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]; +#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()) { + + snprintf(clause, sizeof(clause), "display='%s'", nc->display); + rdb_scrub_table("anope_ns_access", clause); + rdb_scrub_table("anope_ns_core", clause); + rdb_scrub_table("anope_cs_access", clause); + /* 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'", + nc->display); + rdb_scrub_table("anope_ms_info", clause); + rdb_close(); + } +#endif + + /* Now we can safely free it. */ + free(nc->display); + if (nc->pass) + free(nc->pass); + + 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) + moduleCleanStruct(nc->memos.memos[i].moduleData); + free(nc->memos.memos[i].text); + } + 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->password) + free(nr->password); + 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]; +#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; + +#ifndef IRC_PTLINK + change_user_mode(na->u, "-r+d", "1"); +#else + change_user_mode(na->u, "-r", NULL); +#endif + + + } + + 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()) { + + snprintf(clause, sizeof(clause), "nick='%s'", na->nick); + rdb_scrub_table("anope_ns_alias", clause); + rdb_close(); + } +#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 + */ + +static void collide(NickAlias * na, int from_timeout) +{ +#ifndef IRC_HYBRID + char guestnick[NICKMAX]; +#endif + + 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 + */ + +#ifdef IRC_HYBRID + kill_user(s_NickServ, na->nick, "Services nickname-enforcer kill"); +#else + snprintf(guestnick, sizeof(guestnick), "%s%d", NSGuestNickPrefix, + guestnum++); + notice_lang(s_NickServ, na->u, FORCENICKCHANGE_CHANGING, guestnick); + send_cmd(NULL, "SVSNICK %s %s :%ld", na->nick, guestnick, time(NULL)); + na->status |= NS_GUESTED; +#endif +} + +/*************************************************************************/ + +/* Release hold on a nick. */ + +static void release(NickAlias * na, int from_timeout) +{ + if (!from_timeout) + del_ns_timeout(na, TO_RELEASE); +#ifdef HAS_SVSHOLD + if (UseSVSHOLD) + send_cmd(ServerName, "SVSHOLD %s 0", na->nick); + else +#endif + send_cmd(na->nick, "QUIT"); + 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. */ + +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=%p (%s), delay=%ld", + type, na, na->nick, 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. */ + +static 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("%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 ***********************/ +/*************************************************************************/ + +/* Return a help message. */ + +static int do_help(User * u) +{ + char *cmd = strtok(NULL, ""); + + if (!cmd) { + notice_help(s_NickServ, u, NICK_HELP); + if (NSExpire >= 86400) + notice_help(s_NickServ, u, NICK_HELP_EXPIRES, + NSExpire / 86400); + if (is_services_oper(u)) + notice_help(s_NickServ, u, NICK_SERVADMIN_HELP); + moduleDisplayHelp(1, u); + } else if (stricmp(cmd, "SET LANGUAGE") == 0) { + int i; + notice_help(s_NickServ, u, NICK_HELP_SET_LANGUAGE); + for (i = 0; i < NUM_LANGS && langlist[i] >= 0; i++) + notice_user(s_NickServ, u, " %2d) %s", i + 1, + langnames[langlist[i]]); + } else { + mod_help_cmd(s_NickServ, u, NICKSERV, cmd); + } + return MOD_CONT; +} + +/*************************************************************************/ + +/* Register a nick. */ + +static int do_register(User * u) +{ + NickRequest *nr = NULL, *anr = NULL; + int prefixlen = strlen(NSGuestNickPrefix); + int nicklen = strlen(u->nick); + char *pass = strtok(NULL, " "); + char *email = strtok(NULL, " "); + char passcode[11]; + int idx, min = 1, max = 62; + int chars[] = + { ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', + 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', + 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + }; + + if (readonly) { + notice_lang(s_NickServ, u, NICK_REGISTRATION_DISABLED); + return MOD_CONT; + } + + if (checkDefCon(DEFCON_NO_NEW_NICKS)) { + notice_lang(s_NickServ, u, OPER_DEFCON_DENIED); + return MOD_CONT; + } + + if (!is_oper(u) && NickRegDelay + && ((time(NULL) - u->my_signon) < NickRegDelay)) { + notice_lang(s_NickServ, u, NICK_REG_DELAY, NickRegDelay); + return MOD_CONT; + } + + if ((anr = findrequestnick(u->nick))) { + notice_lang(s_NickServ, u, NICK_REQUESTED); + return MOD_CONT; + } + /* Prevent "Guest" nicks from being registered. -TheShadow */ + + /* Guest nick can now have a series of between 1 and 7 digits. + * --lara + */ + if (nicklen <= prefixlen + 7 && nicklen >= prefixlen + 1 && + stristr(u->nick, NSGuestNickPrefix) == u->nick && + strspn(u->nick + prefixlen, "1234567890") == nicklen - prefixlen) { + notice_lang(s_NickServ, u, NICK_CANNOT_BE_REGISTERED, u->nick); + return MOD_CONT; + } + + if (!pass || (NSForceEmail && !email)) { + syntax_error(s_NickServ, u, "REGISTER", +