summaryrefslogtreecommitdiff
path: root/nickserv.c
diff options
context:
space:
mode:
Diffstat (limited to 'nickserv.c')
-rw-r--r--nickserv.c4010
1 files changed, 4010 insertions, 0 deletions
diff --git a/nickserv.c b/nickserv.c
new file mode 100644
index 000000000..f6365ab93
--- /dev/null
+++ b/nickserv.c
@@ -0,0 +1,4010 @@
+/* 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: nickserv.c,v 1.93 2004/03/13 03:25:59 dane Exp $
+ *
+ */
+
+#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];
+
+static 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);
+static void alpha_insert_alias(NickAlias * na);
+static void insert_core(NickCore * nc);
+static 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 <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(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;
+ 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));
+ }
+ }
+
+ /* 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;
+ 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));
+ }
+ }
+
+ 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) {
+ NEWNICK(u->nick, NSEnforcerUser, NSEnforcerHost,
+ "Services Enforcer", "+", 0);
+ add_ns_timeout(na, TO_RELEASE, NSReleaseTimeout);
+ 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. */
+
+static 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. */
+
+static 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;
+}
+
+/*************************************************************************/
+static 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)
+ free(nc->memos.memos[i].text);
+ }
+ free(nc->memos.memos);
+ }
+
+ 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);
+
+ 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)
+{
+ 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
+ */
+
+ snprintf(guestnick, sizeof(guestnick), "%s%d", NSGuestNickPrefix,
+ guestnum++);
+#ifdef IRC_HYBRID
+ kill_user(s_NickServ, na->nick, "Services nickname-enforcer kill");
+#else
+ 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);
+ 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 ((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",
+ NICK_REGISTER_SYNTAX_EMAIL);
+ } else if (time(NULL) < u->lastnickreg + NSRegDelay) {
+ notice_lang(s_NickServ, u, NICK_REG_PLEASE_WAIT, NSRegDelay);
+ } else if (u->na) { /* i.e. there's already such a nick regged */
+ if (u->na->status & NS_VERBOTEN) {
+ alog("%s: %s@%s tried to register FORBIDden nick %s",
+ s_NickServ, u->username, GetHost(u), u->nick);
+ notice_lang(s_NickServ, u, NICK_CANNOT_BE_REGISTERED, u->nick);
+ } else {
+ notice_lang(s_NickServ, u, NICK_ALREADY_REGISTERED, u->nick);
+ }
+ } else if (stricmp(u->nick, pass) == 0
+ || (StrictPasswords && strlen(pass) < 5)) {
+ notice_lang(s_NickServ, u, MORE_OBSCURE_PASSWORD);
+ } else if (email && !MailValidate(email)) {
+ notice_lang(s_NickServ, u, MAIL_X_INVALID, email);
+ } else {
+#ifdef USE_ENCRYPTION
+ if (strlen(pass) > PASSMAX) {
+ pass[PASSMAX] = 0;
+ notice_lang(s_NickServ, u, PASSWORD_TRUNCATED, PASSMAX);
+ }
+#else
+ if (strlen(pass) > PASSMAX - 1) { /* -1 for null byte */
+ pass[PASSMAX] = 0;
+ notice_lang(s_NickServ, u, PASSWORD_TRUNCATED, PASSMAX - 1);
+ }
+#endif
+ srand((unsigned) time(NULL));
+ for (idx = 0; idx < 9; idx++) {
+ passcode[idx] =
+ chars[(1 +
+ (int) (((float) (max - min)) * rand() /
+ (RAND_MAX + 1.0)) + min)];
+ } passcode[idx] = '\0';
+ nr = makerequest(u->nick);
+ nr->passcode = sstrdup(passcode);
+ nr->password = sstrdup(pass);
+ if (email) {
+ nr->email = sstrdup(email);
+ }
+ nr->requested = time(NULL);
+ if (NSEmailReg) {
+ if (do_sendregmail(u, nr) == 0) {
+ notice_lang(s_NickServ, u, NICK_ENTER_REG_CODE, email,
+ s_NickServ);
+ alog("%s: sent registration verification code to %s",
+ s_NickServ, nr->email);
+ } else {
+ alog("%s: Unable to send registration verification mail",
+ s_NickServ);
+ notice_lang(s_NickServ, u, NICK_REG_UNABLE);
+ delnickrequest(nr); /* Delete the NickRequest if we couldnt send the mail */
+ return MOD_CONT;
+ }
+ } else {
+ do_confirm(u);
+ }
+
+ }
+ return MOD_CONT;
+}
+
+
+static int do_resend(User * u)
+{
+ NickRequest *nr = NULL;
+ if (NSEmailReg) {
+ if ((nr = findrequestnick(u->nick))) {
+ if (do_sendregmail(u, nr) == 0) {
+ notice_lang(s_NickServ, u, NICK_REG_RESENT, nr->email);
+ alog("%s: re-sent registration verification code for %s to %s", s_NickServ, nr->nick, nr->email);
+ } else {
+ alog("%s: Unable to re-send registration verification mail for %s", s_NickServ, nr->nick);
+ return MOD_CONT;
+ }
+ }
+ }
+ return MOD_CONT;
+}
+
+static int do_sendregmail(User * u, NickRequest * nr)
+{
+ MailInfo *mail = NULL;
+ char buf[BUFSIZE];
+
+ if (!(nr || u)) {
+ return -1;
+ }
+ snprintf(buf, sizeof(buf), getstring2(NULL, NICK_REG_MAIL_SUBJECT),
+ nr->nick);
+ mail = MailRegBegin(u, nr, buf, s_NickServ);
+ if (!mail) {
+ return -1;
+ }
+ fprintf(mail->pipe, getstring2(NULL, NICK_REG_MAIL_HEAD));
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring2(NULL, NICK_REG_MAIL_LINE_1), nr->nick);
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring2(NULL, NICK_REG_MAIL_LINE_2), s_NickServ,
+ nr->passcode);
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring2(NULL, NICK_REG_MAIL_LINE_3));
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring2(NULL, NICK_REG_MAIL_LINE_4));
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring2(NULL, NICK_REG_MAIL_LINE_5),
+ NetworkName);
+ fprintf(mail->pipe, "\n.\n");
+ MailEnd(mail);
+ return 0;
+}
+
+static int do_confirm(User * u)
+{
+
+ NickRequest *nr = NULL;
+ NickAlias *na = NULL;
+ char *passcode = strtok(NULL, " ");
+ char *pass = NULL;
+ char *email = NULL;
+ int forced = 0;
+ User *utmp = NULL;
+#ifdef USE_ENCRYPTION
+ int len;
+#endif
+ nr = findrequestnick(u->nick);
+
+ if (NSEmailReg) {
+ if (!passcode) {
+ notice_lang(s_NickServ, u, NICK_CONFIRM_INVALID);
+ return MOD_CONT;
+ }
+
+ if (!nr) {
+ if (is_services_admin(u)) {
+/* If an admin, thier nick is obviously already regged, so look at the passcode to get the nick
+ of the user they are trying to validate, and push that user through regardless of passcode */
+ nr = findrequestnick(passcode);
+ if (nr) {
+ utmp = finduser(passcode);
+ if (utmp) {
+ sprintf(passcode,
+ "FORCE_ACTIVATION_DUE_TO_OPER_CONFIRM %s",
+ nr->passcode);
+ passcode = strtok(passcode, " ");
+ notice_lang(s_NickServ, u, NICK_FORCE_REG,
+ nr->nick);
+ do_confirm(utmp);
+ return MOD_CONT;
+ } else {
+ passcode = sstrdup(nr->passcode);
+ forced = 1;
+ }
+ } else {
+ notice_lang(s_NickServ, u, NICK_CONFIRM_NOT_FOUND,
+ s_NickServ);
+ return MOD_CONT;
+ }
+ } else {
+ notice_lang(s_NickServ, u, NICK_CONFIRM_NOT_FOUND,
+ s_NickServ);
+ return MOD_CONT;
+ }
+ }
+
+ if (stricmp(nr->passcode, passcode) != 0) {
+ notice_lang(s_NickServ, u, NICK_CONFIRM_INVALID);
+ return MOD_CONT;
+ }
+ }
+
+ if (!nr) {
+ notice_lang(s_NickServ, u, NICK_REGISTRATION_FAILED);
+ return MOD_CONT;
+ }
+ pass = sstrdup(nr->password);
+
+ if (nr->email) {
+ email = sstrdup(nr->email);
+ }
+ na = makenick(nr->nick);
+
+ if (na) {
+ int i;
+#if !defined(IRC_PTLINK)
+ char tsbuf[16];
+#endif
+
+#ifdef USE_ENCRYPTION
+ len = strlen(pass);
+ na->nc->pass = smalloc(PASSMAX);
+ if (encrypt(pass, len, na->nc->pass, PASSMAX) < 0) {
+ memset(pass, 0, strlen(pass));
+ alog("%s: Failed to encrypt password for %s (register)",
+ s_NickServ, nr->nick);
+ notice_lang(s_NickServ, u, NICK_REGISTRATION_FAILED);
+ return MOD_CONT;
+ }
+ memset(pass, 0, strlen(pass));
+ na->status = NS_IDENTIFIED | NS_RECOGNIZED;
+ na->nc->flags |= NI_ENCRYPTEDPW;
+#else
+ na->nc->pass = sstrdup(pass);
+ na->status = NS_IDENTIFIED | NS_RECOGNIZED;
+#endif
+ na->nc->flags |= NSDefFlags;
+ for (i = 0; i < RootNumber; i++) {
+ if (!stricmp(ServicesRoots[i], nr->nick)) {
+ na->nc->flags |= NI_SERVICES_ROOT;
+ break;
+ }
+ }
+ na->nc->memos.memomax = MSMaxMemos;
+ na->nc->channelmax = CSMaxReg;
+ if (forced == 1) {
+ na->last_usermask = sstrdup("*@*");
+ na->last_realname = sstrdup("unknown");
+ } else {
+ na->last_usermask =
+ scalloc(strlen(GetIdent(u)) + strlen(GetHost(u)) + 2, 1);
+ sprintf(na->last_usermask, "%s@%s", GetIdent(u), GetHost(u));
+ na->last_realname = sstrdup(u->realname);
+ }
+ na->time_registered = na->last_seen = time(NULL);
+ na->nc->accesscount = 1;
+ na->nc->access = scalloc(sizeof(char *), 1);
+ na->nc->access[0] = create_mask(u);
+ na->nc->language = NSDefLanguage;
+ if (email)
+ na->nc->email = sstrdup(email);
+ if (forced != 1) {
+ u->na = na;
+ na->u = u;
+ alog("%s: '%s' registered by %s@%s (e-mail: %s)", s_NickServ,
+ u->nick, u->username, GetHost(u),
+ (email ? email : "none"));
+ notice_lang(s_NickServ, u, NICK_REGISTERED, u->nick,
+ na->nc->access[0]);
+#ifndef USE_ENCRYPTION
+ notice_lang(s_NickServ, u, NICK_PASSWORD_IS, na->nc->pass);
+#endif
+ u->lastnickreg = time(NULL);
+#if !defined(IRC_PTLINK)
+ snprintf(tsbuf, sizeof(tsbuf), "%lu", u->timestamp);
+ change_user_mode(u, "+rd", tsbuf);
+#else
+ change_user_mode(u, "+r", NULL);
+#endif
+
+ } else {
+ notice_lang(s_NickServ, u, NICK_FORCE_REG, nr->nick);
+ }
+ delnickrequest(nr); /* remove the nick request */
+ } else {
+ alog("%s: makenick(%s) failed", s_NickServ, u->nick);
+ notice_lang(s_NickServ, u, NICK_REGISTRATION_FAILED);
+ }
+
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+/* Register a nick in a specified group. */
+
+static int do_group(User * u)
+{
+ NickAlias *na, *target;
+ char *nick = strtok(NULL, " ");
+ char *pass = strtok(NULL, " ");
+ int i;
+#if !defined(IRC_PTLINK)
+ char tsbuf[16];
+#endif
+
+ if (NSEmailReg && (findrequestnick(u->nick))) {
+ notice_lang(s_NickServ, u, NICK_REQUESTED);
+ return MOD_CONT;
+ }
+
+ if (readonly) {
+ notice_lang(s_NickServ, u, NICK_GROUP_DISABLED);
+ return MOD_CONT;
+ }
+ if (checkDefCon(DEFCON_NO_NEW_NICKS)) {
+ notice_lang(s_NickServ, u, OPER_DEFCON_DENIED);
+ return MOD_CONT;
+ }
+
+ if (!nick || !pass) {
+ syntax_error(s_NickServ, u, "GROUP", NICK_GROUP_SYNTAX);
+ } else if (time(NULL) < u->lastnickreg + NSRegDelay) {
+ notice_lang(s_NickServ, u, NICK_GROUP_PLEASE_WAIT, NSRegDelay);
+ } else if (u->na && (u->na->status & NS_VERBOTEN)) {
+ alog("%s: %s@%s tried to use GROUP from FORBIDden nick %s",
+ s_NickServ, u->username, GetHost(u), u->nick);
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, u->nick);
+ } else if (u->na && NSNoGroupChange) {
+ notice_lang(s_NickServ, u, NICK_GROUP_CHANGE_DISABLED, s_NickServ);
+ } else if (u->na && !nick_identified(u)) {
+ notice_lang(s_NickServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
+ } else if (!(target = findnick(nick))) {
+ notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ } else if (target->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, nick);
+ } else if (u->na && target->nc == u->na->nc) {
+ notice_lang(s_NickServ, u, NICK_GROUP_SAME, target->nick);
+ } else if (NSMaxAliases && (target->nc->aliases.count >= NSMaxAliases)
+ && !nick_is_services_admin(target->nc)) {
+ notice_lang(s_NickServ, u, NICK_GROUP_TOO_MANY, target->nick,
+ s_NickServ, s_NickServ);
+ } else if (check_password(pass, target->nc->pass) != 1) {
+ alog("%s: Failed GROUP for %s!%s@%s (invalid password)",
+ s_NickServ, u->nick, u->username, GetHost(u));
+ notice_lang(s_NickServ, u, PASSWORD_INCORRECT);
+ bad_password(u);
+ } else {
+ /* If the nick is already registered, drop it.
+ * If not, check that it is valid.
+ */
+ if (u->na) {
+ delnick(u->na);
+ } else {
+ int prefixlen = strlen(NSGuestNickPrefix);
+ int nicklen = strlen(u->nick);
+
+ 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;
+ }
+ }
+ na = makealias(u->nick, target->nc);
+
+ if (na) {
+ na->last_usermask =
+ scalloc(strlen(GetIdent(u)) + strlen(GetHost(u)) + 2, 1);
+ sprintf(na->last_usermask, "%s@%s", GetIdent(u), GetHost(u));
+ na->last_realname = sstrdup(u->realname);
+ na->time_registered = na->last_seen = time(NULL);
+ na->status = NS_IDENTIFIED | NS_RECOGNIZED;
+
+ if (!(na->nc->flags & NI_SERVICES_ROOT)) {
+ for (i = 0; i < RootNumber; i++) {
+ if (!stricmp(ServicesRoots[i], u->nick)) {
+ na->nc->flags |= NI_SERVICES_ROOT;
+ break;
+ }
+ }
+ }
+
+ u->na = na;
+ na->u = u;
+
+#ifdef USE_RDB
+ /* Is this really needed? Since this is a new alias it will get
+ * its unique id on the next update, since it was previously
+ * deleted by delnick. Must observe...
+ */
+ if (rdb_open()) {
+ rdb_save_ns_alias(na);
+ rdb_close();
+ }
+#endif
+ alog("%s: %s!%s@%s makes %s join group of %s (%s) (e-mail: %s)", s_NickServ, u->nick, u->username, GetHost(u), u->nick, target->nick, target->nc->display, (target->nc->email ? target->nc->email : "none"));
+ notice_lang(s_NickServ, u, NICK_GROUP_JOINED, target->nick);
+
+ u->lastnickreg = time(NULL);
+#if !defined(IRC_PTLINK)
+ snprintf(tsbuf, sizeof(tsbuf), "%lu", u->timestamp);
+ change_user_mode(u, "+rd", tsbuf);
+#else
+ change_user_mode(u, "+r", NULL);
+#endif
+
+ check_memos(u);
+ } else {
+ alog("%s: makealias(%s) failed", s_NickServ, u->nick);
+ notice_lang(s_NickServ, u, NICK_GROUP_FAILED);
+ }
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_nickupdate(User * u)
+{
+ NickAlias *na;
+
+ if (!nick_identified(u)) {
+ notice_lang(s_NickServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
+ } else {
+ na = u->na;
+ if (NSModeOnID)
+ do_setmodes(u);
+ check_memos(u);
+ if (na->last_realname)
+ free(na->last_realname);
+ na->last_realname = sstrdup(u->realname);
+ na->status |= NS_IDENTIFIED;
+ na->last_seen = time(NULL);
+ do_on_id(u);
+ notice_lang(s_NickServ, u, NICK_UPDATE_SUCCESS, s_NickServ);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_identify(User * u)
+{
+ char *pass = strtok(NULL, " ");
+ NickAlias *na;
+ NickRequest *nr;
+ int res;
+#if !defined(IRC_PTLINK)
+ char tsbuf[16];
+#endif
+
+ if (!pass) {
+ syntax_error(s_NickServ, u, "IDENTIFY", NICK_IDENTIFY_SYNTAX);
+ } else if (!(na = u->na)) {
+ if ((nr = findrequestnick(u->nick))) {
+ notice_lang(s_NickServ, u, NICK_IS_PREREG);
+ } else {
+ notice_lang(s_NickServ, u, NICK_NOT_REGISTERED);
+ }
+ } else if (na->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ } else if (!(res = check_password(pass, na->nc->pass))) {
+ alog("%s: Failed IDENTIFY for %s!%s@%s", s_NickServ, u->nick,
+ u->username, GetHost(u));
+ notice_lang(s_NickServ, u, PASSWORD_INCORRECT);
+ bad_password(u);
+ } else if (res == -1) {
+ notice_lang(s_NickServ, u, NICK_IDENTIFY_FAILED);
+ } else if (nick_identified(u)) {
+ notice_lang(s_NickServ, u, NICK_ALREADY_IDENTIFIED);
+ } else {
+ if (!(na->status & NS_IDENTIFIED) && !(na->status & NS_RECOGNIZED)) {
+ 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);
+ }
+
+ na->status |= NS_IDENTIFIED;
+ na->last_seen = time(NULL);
+
+#ifndef IRC_PTLINK
+ snprintf(tsbuf, sizeof(tsbuf), "%lu", u->timestamp);
+ change_user_mode(u, "+rd", tsbuf);
+#else
+ change_user_mode(u, "+r", "");
+#endif /* IRC_PTLINK */
+
+
+ alog("%s: %s!%s@%s identified for nick %s", s_NickServ, u->nick,
+ u->username, GetHost(u), u->nick);
+ notice_lang(s_NickServ, u, NICK_IDENTIFY_SUCCEEDED);
+ do_on_id(u);
+ if (NSModeOnID) {
+ do_setmodes(u);
+ }
+
+ if (NSForceEmail && u->na && !u->na->nc->email) {
+ notice_lang(s_NickServ, u, NICK_IDENTIFY_EMAIL_REQUIRED);
+ notice_help(s_NickServ, u, NICK_IDENTIFY_EMAIL_HOWTO);
+ }
+
+ if (!(na->status & NS_RECOGNIZED))
+ check_memos(u);
+ }
+ return MOD_CONT;
+}
+
+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;
+ }
+#ifdef HAS_HALFOP
+ if (status & CUS_HALFOP) {
+ return 0;
+ }
+#endif
+ if (status & CUS_VOICE) {
+ return 0;
+ }
+ return 1;
+ break;
+#ifdef HAS_HALFOP
+
+
+ case CUS_HALFOP:
+ if (status & CUS_OP) {
+ return 0;
+ }
+ if (status & CUS_HALFOP) {
+ return 0;
+ }
+ return 1;
+ break;
+#endif
+#ifdef IRC_UNREAL
+ case CUS_OWNER:
+ if (status & CUS_OWNER) {
+ return 0;
+ }
+ break;
+ case CUS_PROTECT:
+ if (status & CUS_OWNER) {
+ return 0;
+ }
+ if (status & CUS_PROTECT) {
+ return 0;
+ }
+ break;
+#endif
+#ifdef IRC_VIAGRA
+ case CUS_OWNER:
+ if (status & CUS_OWNER) {
+ return 0;
+ }
+ break;
+ case CUS_PROTECT:
+ if (status & CUS_OWNER) {
+ return 0;
+ }
+ if (status & CUS_PROTECT) {
+ return 0;
+ }
+ break;
+#endif
+#ifdef IRC_ULTIMATE3
+ case CUS_PROTECT:
+ if (status & CUS_PROTECT) {
+ return 0;
+ }
+ break;
+#endif
+ }
+ return 1;
+}
+
+static int do_setmodes(User * u)
+{
+ struct u_chanlist *uc;
+ Channel *c;
+ char *chan;
+
+ /* Walk users current channels */
+ for (uc = u->chans; uc; uc = uc->next) {
+ if ((c = uc->chan)) {
+ chan = c->name;
+#if defined(IRC_UNREAL) || defined(IRC_VIAGRA)
+ if (should_mode_change(uc->status, CUS_OWNER)
+ && check_should_owner(u, chan)) {
+ chan_set_user_status(c, u, CUS_OWNER);
+ } else
+#endif
+#if defined(IRC_UNREAL) || defined(IRC_VIAGRA) || defined(IRC_ULTIMATE3)
+ if (should_mode_change(uc->status, CUS_PROTECT)
+ && check_should_protect(u, chan)) {
+ chan_set_user_status(c, u, CUS_PROTECT);
+ } else
+#endif
+ if (should_mode_change(uc->status, CUS_OP)
+ && check_should_op(u, chan)) {
+ chan_set_user_status(c, u, CUS_OP);
+ } else
+#ifdef HAS_HALFOP
+ if (should_mode_change(uc->status, CUS_HALFOP)
+ && check_should_halfop(u, chan)) {
+ chan_set_user_status(c, u, CUS_HALFOP);
+ } else
+#endif
+ if (should_mode_change(uc->status, CUS_VOICE)
+ && check_should_voice(u, chan)) {
+ chan_set_user_status(c, u, CUS_VOICE);
+ }
+ }
+ }
+ return MOD_CONT;
+}
+
+
+/*************************************************************************/
+
+static int do_logout(User * u)
+{
+ char *nick = strtok(NULL, " ");
+ char *param = strtok(NULL, " ");
+ User *u2;
+
+ if (!is_services_admin(u) && nick) {
+ syntax_error(s_NickServ, u, "LOGOUT", NICK_LOGOUT_SYNTAX);
+ } else if (!(u2 = (nick ? finduser(nick) : u))) {
+ notice_lang(s_NickServ, u, NICK_X_NOT_IN_USE, nick);
+ } else if (!u2->na) {
+ if (nick)
+ notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ else
+ notice_lang(s_NickServ, u, NICK_NOT_REGISTERED);
+ } else if (u2->na->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, u2->na->nick);
+ } else if (!nick && !nick_identified(u)) {
+ notice_lang(s_NickServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
+ } else if (nick && is_services_admin(u2)) {
+ notice_lang(s_NickServ, u, NICK_LOGOUT_SERVICESADMIN, nick);
+ } else {
+ if (nick && param && !stricmp(param, "REVALIDATE")) {
+ cancel_user(u2);
+ validate_user(u2);
+ } else {
+ u2->na->status &= ~(NS_IDENTIFIED | NS_RECOGNIZED);
+ }
+
+ change_user_mode(u2, "-r+d", "1");
+
+ u->isSuperAdmin = 0; /* Dont let people logout and remain a SuperAdmin */
+ alog("%s: %s!%s@%s logged out nickname %s", s_NickServ, u->nick,
+ u->username, GetHost(u), u2->nick);
+
+ if (nick)
+ notice_lang(s_NickServ, u, NICK_LOGOUT_X_SUCCEEDED, nick);
+ else
+ notice_lang(s_NickServ, u, NICK_LOGOUT_SUCCEEDED);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_drop(User * u)
+{
+ char *nick = strtok(NULL, " ");
+ NickAlias *na;
+ NickRequest *nr = NULL;
+ int is_servadmin = is_services_admin(u);
+ int is_mine; /* Does the nick being dropped belong to the user that is dropping? */
+
+ if (readonly && !is_servadmin) {
+ notice_lang(s_NickServ, u, NICK_DROP_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (!(na = (nick ? findnick(nick) : u->na))) {
+ if (nick) {
+ if ((nr = findrequestnick(nick)) && is_servadmin) {
+ if (readonly)
+ notice_lang(s_NickServ, u, READ_ONLY_MODE);
+ if (WallDrop)
+ wallops(s_NickServ, "\2%s\2 used DROP on \2%s\2",
+ u->nick, nick);
+ alog("%s: %s!%s@%s dropped nickname %s (e-mail: %s)",
+ s_NickServ, u->nick, u->username, GetHost(u),
+ nr->nick, nr->email);
+ delnickrequest(nr);
+ notice_lang(s_NickServ, u, NICK_X_DROPPED, nick);
+ } else {
+ notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ }
+ } else
+ notice_lang(s_NickServ, u, NICK_NOT_REGISTERED);
+ return MOD_CONT;
+ }
+
+ is_mine = (u->na && (u->na->nc == na->nc));
+
+ if (is_mine && !nick_identified(u)) {
+ notice_lang(s_NickServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
+ } else if (!is_mine && !is_servadmin) {
+ notice_lang(s_NickServ, u, ACCESS_DENIED);
+ } else if (NSSecureAdmins && !is_mine && nick_is_services_admin(na->nc)
+ && !is_services_root(u)) {
+ notice_lang(s_NickServ, u, PERMISSION_DENIED);
+ } else {
+ if (readonly)
+ notice_lang(s_NickServ, u, READ_ONLY_MODE);
+
+ alog("%s: %s!%s@%s dropped nickname %s (group %s) (e-mail: %s)",
+ s_NickServ, u->nick, u->username, GetHost(u), na->nick,
+ na->nc->display, (na->nc->email ? na->nc->email : "none"));
+ delnick(na);
+
+ if (!is_mine) {
+ if (WallDrop)
+ wallops(s_NickServ, "\2%s\2 used DROP on \2%s\2", u->nick,
+ nick);
+ notice_lang(s_NickServ, u, NICK_X_DROPPED, nick);
+ } else {
+ if (nick)
+ notice_lang(s_NickServ, u, NICK_X_DROPPED, nick);
+ else
+ notice_lang(s_NickServ, u, NICK_DROPPED);
+ }
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set(User * u)
+{
+ char *cmd = strtok(NULL, " ");
+ char *param = strtok(NULL, " ");
+
+ NickAlias *na;
+ int is_servadmin = is_services_admin(u);
+ int set_nick = 0;
+
+ if (readonly) {
+ notice_lang(s_NickServ, u, NICK_SET_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (is_servadmin && cmd && (na = findnick(cmd))) {
+ cmd = param;
+ param = strtok(NULL, " ");
+ set_nick = 1;
+ } else {
+ na = u->na;
+ }
+
+ if (!param
+ && (!cmd
+ || (stricmp(cmd, "URL") != 0 && stricmp(cmd, "EMAIL") != 0
+ && stricmp(cmd, "GREET") != 0
+ && stricmp(cmd, "ICQ") != 0))) {
+ if (is_servadmin) {
+ syntax_error(s_NickServ, u, "SET", NICK_SET_SERVADMIN_SYNTAX);
+ } else {
+ syntax_error(s_NickServ, u, "SET", NICK_SET_SYNTAX);
+ }
+ } else if (!na) {
+ notice_lang(s_NickServ, u, NICK_NOT_REGISTERED);
+ } else if (na->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ } else if (!is_servadmin && !nick_identified(u)) {
+ notice_lang(s_NickServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
+ } else if (stricmp(cmd, "DISPLAY") == 0) {
+ do_set_display(u, na->nc, param);
+ } else if (stricmp(cmd, "PASSWORD") == 0) {
+ do_set_password(u, na->nc, param);
+ } else if (stricmp(cmd, "LANGUAGE") == 0) {
+ do_set_language(u, na->nc, param);
+ } else if (stricmp(cmd, "URL") == 0) {
+ do_set_url(u, na->nc, param);
+ } else if (stricmp(cmd, "EMAIL") == 0) {
+ do_set_email(u, na->nc, param);
+ } else if (stricmp(cmd, "ICQ") == 0) {
+ do_set_icq(u, na->nc, param);
+ } else if (stricmp(cmd, "GREET") == 0) {
+ do_set_greet(u, na->nc, param);
+ } else if (stricmp(cmd, "KILL") == 0) {
+ do_set_kill(u, na->nc, param);
+ } else if (stricmp(cmd, "SECURE") == 0) {
+ do_set_secure(u, na->nc, param);
+ } else if (stricmp(cmd, "PRIVATE") == 0) {
+ do_set_private(u, na->nc, param);
+ } else if (stricmp(cmd, "MSG") == 0) {
+ do_set_msg(u, na->nc, param);
+ } else if (stricmp(cmd, "HIDE") == 0) {
+ do_set_hide(u, na->nc, param);
+ } else if (stricmp(cmd, "NOEXPIRE") == 0) {
+ do_set_noexpire(u, na, param);
+ } else {
+ if (is_servadmin)
+ notice_lang(s_NickServ, u, NICK_SET_UNKNOWN_OPTION_OR_BAD_NICK,
+ cmd);
+ else
+ notice_lang(s_NickServ, u, NICK_SET_UNKNOWN_OPTION, cmd);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_display(User * u, NickCore * nc, char *param)
+{
+ int i;
+ NickAlias *na;
+
+ /* First check whether param is a valid nick of the group */
+ for (i = 0; i < nc->aliases.count; i++) {
+ na = nc->aliases.list[i];
+ if (!stricmp(na->nick, param)) {
+ param = na->nick; /* Because case may differ */
+ break;
+ }
+ }
+
+ if (i == nc->aliases.count) {
+ notice_lang(s_NickServ, u, NICK_SET_DISPLAY_INVALID);
+ return MOD_CONT;
+ }
+
+ change_core_display(nc, param);
+ notice_lang(s_NickServ, u, NICK_SET_DISPLAY_CHANGED, nc->display);
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_password(User * u, NickCore * nc, char *param)
+{
+ int len = strlen(param);
+
+ if (NSSecureAdmins && u->na->nc != nc && nick_is_services_admin(nc)
+ && !is_services_root(u)) {
+ notice_lang(s_NickServ, u, PERMISSION_DENIED);
+ return MOD_CONT;
+ } else if (stricmp(nc->display, param) == 0
+ || (StrictPasswords && len < 5)) {
+ notice_lang(s_NickServ, u, MORE_OBSCURE_PASSWORD);
+ return MOD_CONT;
+ }
+
+ if (nc->pass)
+ free(nc->pass);
+
+#ifdef USE_ENCRYPTION
+ nc->pass = smalloc(PASSMAX);
+
+ if (encrypt(param, len, nc->pass, PASSMAX) < 0) {
+ memset(param, 0, len);
+ alog("%s: Failed to encrypt password for %s (set)", s_NickServ,
+ nc->display);
+ notice_lang(s_NickServ, u, NICK_SET_PASSWORD_FAILED);
+ return MOD_CONT;
+ }
+
+ memset(param, 0, len);
+ notice_lang(s_NickServ, u, NICK_SET_PASSWORD_CHANGED);
+#else
+ nc->pass = sstrdup(param);
+ notice_lang(s_NickServ, u, NICK_SET_PASSWORD_CHANGED_TO, nc->pass);
+#endif
+
+ if (u->na && u->na->nc != nc && is_services_admin(u)) {
+ alog("%s: %s!%s@%s used SET PASSWORD as Services admin on %s (e-mail: %s)", s_NickServ, u->nick, u->username, GetHost(u), nc->display, (nc->email ? nc->email : "none"));
+ if (WallSetpass)
+ wallops(s_NickServ,
+ "\2%s\2 used SET PASSWORD as Services admin on \2%s\2",
+ u->nick, nc->display);
+ } else {
+ alog("%s: %s!%s@%s (e-mail: %s) changed its password.", s_NickServ,
+ u->nick, u->username, GetHost(u),
+ (nc->email ? nc->email : "none"));
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_language(User * u, NickCore * nc, char *param)
+{
+ int langnum;
+
+ if (param[strspn(param, "0123456789")] != 0) { /* i.e. not a number */
+ syntax_error(s_NickServ, u, "SET LANGUAGE",
+ NICK_SET_LANGUAGE_SYNTAX);
+ return MOD_CONT;
+ }
+ langnum = atoi(param) - 1;
+ if (langnum < 0 || langnum >= NUM_LANGS || langlist[langnum] < 0) {
+ notice_lang(s_NickServ, u, NICK_SET_LANGUAGE_UNKNOWN, langnum + 1,
+ s_NickServ);
+ return MOD_CONT;
+ }
+ nc->language = langlist[langnum];
+ notice_lang(s_NickServ, u, NICK_SET_LANGUAGE_CHANGED);
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_url(User * u, NickCore * nc, char *param)
+{
+ if (nc->url)
+ free(nc->url);
+
+ if (param) {
+ nc->url = sstrdup(param);
+ notice_lang(s_NickServ, u, NICK_SET_URL_CHANGED, param);
+ } else {
+ nc->url = NULL;
+ notice_lang(s_NickServ, u, NICK_SET_URL_UNSET);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_email(User * u, NickCore * nc, char *param)
+{
+ if (!param && NSForceEmail) {
+ notice_lang(s_NickServ, u, NICK_SET_EMAIL_UNSET_IMPOSSIBLE);
+ return MOD_CONT;
+ } else if (param && !MailValidate(param)) {
+ notice_lang(s_NickServ, u, MAIL_X_INVALID, param);
+ return MOD_CONT;
+ }
+
+ alog("%s: %s!%s@%s (e-mail: %s) changed its e-mail to %s.", s_NickServ,
+ u->nick, u->username, GetHost(u),
+ (nc->email ? nc->email : "none"), (param ? param : "none"));
+
+ if (nc->email)
+ free(nc->email);
+
+ if (param) {
+ nc->email = sstrdup(param);
+ notice_lang(s_NickServ, u, NICK_SET_EMAIL_CHANGED, param);
+ } else {
+ nc->email = NULL;
+ notice_lang(s_NickServ, u, NICK_SET_EMAIL_UNSET);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_icq(User * u, NickCore * nc, char *param)
+{
+ if (param) {
+ int32 tmp = atol(param);
+ if (!tmp) {
+ notice_lang(s_NickServ, u, NICK_SET_ICQ_INVALID, param);
+ } else {
+ nc->icq = tmp;
+ notice_lang(s_NickServ, u, NICK_SET_ICQ_CHANGED, param);
+ }
+ } else {
+ nc->icq = 0;
+ notice_lang(s_NickServ, u, NICK_SET_ICQ_UNSET);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_greet(User * u, NickCore * nc, char *param)
+{
+ if (nc->greet)
+ free(nc->greet);
+
+ if (param) {
+ char buf[BUFSIZE];
+ char *end = strtok(NULL, "");
+
+ snprintf(buf, sizeof(buf), "%s%s%s", param, (end ? " " : ""),
+ (end ? end : ""));
+
+ nc->greet = sstrdup(buf);
+ notice_lang(s_NickServ, u, NICK_SET_GREET_CHANGED, buf);
+ } else {
+ nc->greet = NULL;
+ notice_lang(s_NickServ, u, NICK_SET_GREET_UNSET);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_kill(User * u, NickCore * nc, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ nc->flags |= NI_KILLPROTECT;
+ nc->flags &= ~(NI_KILL_QUICK | NI_KILL_IMMED);
+ notice_lang(s_NickServ, u, NICK_SET_KILL_ON);
+ } else if (stricmp(param, "QUICK") == 0) {
+ nc->flags |= NI_KILLPROTECT | NI_KILL_QUICK;
+ nc->flags &= ~NI_KILL_IMMED;
+ notice_lang(s_NickServ, u, NICK_SET_KILL_QUICK);
+ } else if (stricmp(param, "IMMED") == 0) {
+ if (NSAllowKillImmed) {
+ nc->flags |= NI_KILLPROTECT | NI_KILL_IMMED;
+ nc->flags &= ~NI_KILL_QUICK;
+ notice_lang(s_NickServ, u, NICK_SET_KILL_IMMED);
+ } else {
+ notice_lang(s_NickServ, u, NICK_SET_KILL_IMMED_DISABLED);
+ }
+ } else if (stricmp(param, "OFF") == 0) {
+ nc->flags &= ~(NI_KILLPROTECT | NI_KILL_QUICK | NI_KILL_IMMED);
+ notice_lang(s_NickServ, u, NICK_SET_KILL_OFF);
+ } else {
+ syntax_error(s_NickServ, u, "SET KILL",
+ NSAllowKillImmed ? NICK_SET_KILL_IMMED_SYNTAX :
+ NICK_SET_KILL_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_secure(User * u, NickCore * nc, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ nc->flags |= NI_SECURE;
+ notice_lang(s_NickServ, u, NICK_SET_SECURE_ON);
+ } else if (stricmp(param, "OFF") == 0) {
+ nc->flags &= ~NI_SECURE;
+ notice_lang(s_NickServ, u, NICK_SET_SECURE_OFF);
+ } else {
+ syntax_error(s_NickServ, u, "SET SECURE", NICK_SET_SECURE_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_private(User * u, NickCore * nc, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ nc->flags |= NI_PRIVATE;
+ notice_lang(s_NickServ, u, NICK_SET_PRIVATE_ON);
+ } else if (stricmp(param, "OFF") == 0) {
+ nc->flags &= ~NI_PRIVATE;
+ notice_lang(s_NickServ, u, NICK_SET_PRIVATE_OFF);
+ } else {
+ syntax_error(s_NickServ, u, "SET PRIVATE",
+ NICK_SET_PRIVATE_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_msg(User * u, NickCore * nc, char *param)
+{
+ if (!UsePrivmsg) {
+ notice_lang(s_NickServ, u, NICK_SET_OPTION_DISABLED, "MSG");
+ return MOD_CONT;
+ }
+
+ if (stricmp(param, "ON") == 0) {
+ nc->flags |= NI_MSG;
+ notice_lang(s_NickServ, u, NICK_SET_MSG_ON);
+ } else if (stricmp(param, "OFF") == 0) {
+ nc->flags &= ~NI_MSG;
+ notice_lang(s_NickServ, u, NICK_SET_MSG_OFF);
+ } else {
+ syntax_error(s_NickServ, u, "SET MSG", NICK_SET_MSG_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_hide(User * u, NickCore * nc, char *param)
+{
+ int flag, onmsg, offmsg;
+
+ if (stricmp(param, "EMAIL") == 0) {
+ flag = NI_HIDE_EMAIL;
+ onmsg = NICK_SET_HIDE_EMAIL_ON;
+ offmsg = NICK_SET_HIDE_EMAIL_OFF;
+ } else if (stricmp(param, "USERMASK") == 0) {
+ flag = NI_HIDE_MASK;
+ onmsg = NICK_SET_HIDE_MASK_ON;
+ offmsg = NICK_SET_HIDE_MASK_OFF;
+ } else if (stricmp(param, "QUIT") == 0) {
+ flag = NI_HIDE_QUIT;
+ onmsg = NICK_SET_HIDE_QUIT_ON;
+ offmsg = NICK_SET_HIDE_QUIT_OFF;
+ } else {
+ syntax_error(s_NickServ, u, "SET HIDE", NICK_SET_HIDE_SYNTAX);
+ return MOD_CONT;
+ }
+
+ param = strtok(NULL, " ");
+ if (!param) {
+ syntax_error(s_NickServ, u, "SET HIDE", NICK_SET_HIDE_SYNTAX);
+ } else if (stricmp(param, "ON") == 0) {
+ nc->flags |= flag;
+ notice_lang(s_NickServ, u, onmsg, s_NickServ);
+ } else if (stricmp(param, "OFF") == 0) {
+ nc->flags &= ~flag;
+ notice_lang(s_NickServ, u, offmsg, s_NickServ);
+ } else {
+ syntax_error(s_NickServ, u, "SET HIDE", NICK_SET_HIDE_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_noexpire(User * u, NickAlias * na, char *param)
+{
+ if (!is_services_admin(u)) {
+ notice_lang(s_NickServ, u, PERMISSION_DENIED);
+ return MOD_CONT;
+ }
+ if (!param) {
+ syntax_error(s_NickServ, u, "SET NOEXPIRE",
+ NICK_SET_NOEXPIRE_SYNTAX);
+ return MOD_CONT;
+ }
+ if (stricmp(param, "ON") == 0) {
+ na->status |= NS_NO_EXPIRE;
+ notice_lang(s_NickServ, u, NICK_SET_NOEXPIRE_ON, na->nick);
+ } else if (stricmp(param, "OFF") == 0) {
+ na->status &= ~NS_NO_EXPIRE;
+ notice_lang(s_NickServ, u, NICK_SET_NOEXPIRE_OFF, na->nick);
+ } else {
+ syntax_error(s_NickServ, u, "SET NOEXPIRE",
+ NICK_SET_NOEXPIRE_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_link(User * u)
+{
+ notice_lang(s_NickServ, u, OBSOLETE_COMMAND, "GROUP");
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_unlink(User * u)
+{
+ notice_lang(s_NickServ, u, OBSOLETE_COMMAND, "DROP");
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_listlinks(User * u)
+{
+ notice_lang(s_NickServ, u, OBSOLETE_COMMAND, "GLIST");
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_access(User * u)
+{
+ char *cmd = strtok(NULL, " ");
+ char *mask = strtok(NULL, " ");
+ NickAlias *na;
+ int i;
+ char **access;
+
+ if (cmd && stricmp(cmd, "LIST") == 0 && mask && is_services_admin(u)
+ && (na = findnick(mask))) {
+
+ if (na->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ return MOD_CONT;
+ }
+
+ notice_lang(s_NickServ, u, NICK_ACCESS_LIST_X, mask);
+ mask = strtok(NULL, " ");
+ for (access = na->nc->access, i = 0; i < na->nc->accesscount;
+ access++, i++) {
+ if (mask && !match_wild(mask, *access))
+ continue;
+ notice_user(s_NickServ, u, " %s", *access);
+ }
+
+ } else if (!cmd || ((stricmp(cmd, "LIST") == 0) ? !!mask : !mask)) {
+ syntax_error(s_NickServ, u, "ACCESS", NICK_ACCESS_SYNTAX);
+
+ } else if (mask && !strchr(mask, '@')) {
+ notice_lang(s_NickServ, u, BAD_USERHOST_MASK);
+ notice_lang(s_NickServ, u, MORE_INFO, s_NickServ, "ACCESS");
+
+ } else if (!(na = u->na)) {
+ notice_lang(s_NickServ, u, NICK_NOT_REGISTERED);
+
+ } else if (na->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+
+ } else if (!nick_identified(u)) {
+ notice_lang(s_NickServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
+
+ } else if (stricmp(cmd, "ADD") == 0) {
+ if (na->nc->accesscount >= NSAccessMax) {
+ notice_lang(s_NickServ, u, NICK_ACCESS_REACHED_LIMIT,
+ NSAccessMax);
+ return MOD_CONT;
+ }
+
+ for (access = na->nc->access, i = 0; i < na->nc->accesscount;
+ access++, i++) {
+ if (strcmp(*access, mask) == 0) {
+ notice_lang(s_NickServ, u, NICK_ACCESS_ALREADY_PRESENT,
+ *access);
+ return MOD_CONT;
+ }
+ }
+
+ na->nc->accesscount++;
+ na->nc->access =
+ srealloc(na->nc->access, sizeof(char *) * na->nc->accesscount);
+ na->nc->access[na->nc->accesscount - 1] = sstrdup(mask);
+ notice_lang(s_NickServ, u, NICK_ACCESS_ADDED, mask);
+
+ } else if (stricmp(cmd, "DEL") == 0) {
+
+ for (access = na->nc->access, i = 0; i < na->nc->accesscount;
+ access++, i++) {
+ if (stricmp(*access, mask) == 0)
+ break;
+ }
+ if (i == na->nc->accesscount) {
+ notice_lang(s_NickServ, u, NICK_ACCESS_NOT_FOUND, mask);
+ return MOD_CONT;
+ }
+
+ notice_lang(s_NickServ, u, NICK_ACCESS_DELETED, *access);
+ free(*access);
+ na->nc->accesscount--;
+ if (i < na->nc->accesscount) /* if it wasn't the last entry... */
+ memmove(access, access + 1,
+ (na->nc->accesscount - i) * sizeof(char *));
+ if (na->nc->accesscount) /* if there are any entries left... */
+ na->nc->access =
+ srealloc(na->nc->access,
+ na->nc->accesscount * sizeof(char *));
+ else {
+ free(na->nc->access);
+ na->nc->access = NULL;
+ }
+ } else if (stricmp(cmd, "LIST") == 0) {
+ notice_lang(s_NickServ, u, NICK_ACCESS_LIST);
+ for (access = na->nc->access, i = 0; i < na->nc->accesscount;
+ access++, i++) {
+ if (mask && !match_wild(mask, *access))
+ continue;
+ notice_user(s_NickServ, u, " %s", *access);
+ }
+ } else {
+ syntax_error(s_NickServ, u, "ACCESS", NICK_ACCESS_SYNTAX);
+
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+/* Show hidden info to nick owners and sadmins when the "ALL" parameter is
+ * supplied. If a nick is online, the "Last seen address" changes to "Is
+ * online from".
+ * Syntax: INFO <nick> {ALL}
+ * -TheShadow (13 Mar 1999)
+ */
+
+static int do_info(User * u)
+{
+ char *nick = strtok(NULL, " ");
+ char *param = strtok(NULL, " ");
+
+ NickAlias *na;
+ NickRequest *nr = NULL;
+ int is_servadmin = is_services_admin(u);
+
+#ifdef HAS_VHOST
+ char *vHost;
+#endif
+
+ if (!nick) {
+ syntax_error(s_NickServ, u, "INFO", NICK_INFO_SYNTAX);
+ } else if (!(na = findnick(nick))) {
+ if ((nr = findrequestnick(nick))) {
+ notice_lang(s_NickServ, u, NICK_IS_PREREG);
+ if (param && stricmp(param, "ALL") == 0 && is_servadmin) {
+ notice_lang(s_NickServ, u, NICK_INFO_EMAIL, nr->email);
+ } else {
+ if (is_servadmin) {
+ notice_lang(s_NickServ, u, NICK_INFO_FOR_MORE,
+ s_NickServ, nr->nick);
+ }
+ }
+ } else {
+ notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ }
+ } else if (na->status & NS_VERBOTEN) {
+ if (is_oper(u) && na->last_usermask)
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN_OPER, nick,
+ na->last_usermask,
+ (na->last_realname ? na->
+ last_realname : getstring(u->na, NO_REASON)));
+ else
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, nick);
+ } else {
+ struct tm *tm;
+ char buf[BUFSIZE], *end;
+ const char *commastr = getstring(u->na, COMMA_SPACE);
+ int need_comma = 0;
+ int nick_online = 0;
+ int show_hidden = 0;
+
+ /* Is the real owner of the nick we're looking up online? -TheShadow */
+ if (na->status & (NS_RECOGNIZED | NS_IDENTIFIED))
+ nick_online = 1;
+
+ /* Only show hidden fields to owner and sadmins and only when the ALL
+ * parameter is used. -TheShadow */
+ if (param && stricmp(param, "ALL") == 0 && u->na
+ && ((nick_identified(u) && (na->nc == u->na->nc))
+ || is_servadmin))
+ show_hidden = 1;
+
+ notice_lang(s_NickServ, u, NICK_INFO_REALNAME, na->nick,
+ na->last_realname);
+
+ if (nick_is_services_admin(na->nc)) /* This will also include the services root(s) */
+ notice_lang(s_NickServ, u, NICK_INFO_SERVICES_ADMIN, na->nick);
+ else if (nick_is_services_oper(na->nc))
+ notice_lang(s_NickServ, u, NICK_INFO_SERVICES_OPER, na->nick);
+
+
+ if (nick_online) {
+ if (show_hidden || !(na->nc->flags & NI_HIDE_MASK))
+ notice_lang(s_NickServ, u, NICK_INFO_ADDRESS_ONLINE,
+ na->last_usermask);
+ else
+ notice_lang(s_NickServ, u, NICK_INFO_ADDRESS_ONLINE_NOHOST,
+ na->nick);
+ } else {
+ if (show_hidden || !(na->nc->flags & NI_HIDE_MASK))
+ notice_lang(s_NickServ, u, NICK_INFO_ADDRESS,
+ na->last_usermask);
+ }
+
+ tm = localtime(&na->time_registered);
+ strftime_lang(buf, sizeof(buf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ notice_lang(s_NickServ, u, NICK_INFO_TIME_REGGED, buf);
+
+ if (!nick_online) {
+ tm = localtime(&na->last_seen);
+ strftime_lang(buf, sizeof(buf), u, STRFTIME_DATE_TIME_FORMAT,
+ tm);
+ notice_lang(s_NickServ, u, NICK_INFO_LAST_SEEN, buf);
+ }
+
+ if (na->last_quit
+ && (show_hidden || !(na->nc->flags & NI_HIDE_QUIT)))
+ notice_lang(s_NickServ, u, NICK_INFO_LAST_QUIT, na->last_quit);
+
+ if (na->nc->url)
+ notice_lang(s_NickServ, u, NICK_INFO_URL, na->nc->url);
+ if (na->nc->email
+ && (show_hidden || !(na->nc->flags & NI_HIDE_EMAIL)))
+ notice_lang(s_NickServ, u, NICK_INFO_EMAIL, na->nc->email);
+ if (na->nc->icq)
+ notice_lang(s_NickServ, u, NICK_INFO_ICQ, na->nc->icq);
+
+ if (show_hidden) {
+#ifdef HAS_VHOST
+ if (s_HostServ) {
+ if (getvHost(na->nick) != NULL) {
+ vHost = smalloc(strlen(getvHost(na->nick)) + 2);
+ bzero(vHost, sizeof(vHost));
+ snprintf(vHost, strlen(getvHost(na->nick)) + 2, "%s",
+ getvHost(na->nick));
+ notice_lang(s_NickServ, u, NICK_INFO_VHOST, vHost);
+ free(vHost);
+ }
+ }
+#endif
+ if (na->nc->greet)
+ notice_lang(s_NickServ, u, NICK_INFO_GREET, na->nc->greet);
+
+ *buf = 0;
+ end = buf;
+
+ if (na->nc->flags & NI_KILLPROTECT) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s",
+ getstring(u->na, NICK_INFO_OPT_KILL));
+ need_comma = 1;
+ }
+ if (na->nc->flags & NI_SECURE) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
+ need_comma ? commastr : "",
+ getstring(u->na, NICK_INFO_OPT_SECURE));
+ need_comma = 1;
+ }
+ if (na->nc->flags & NI_PRIVATE) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
+ need_comma ? commastr : "",
+ getstring(u->na, NICK_INFO_OPT_PRIVATE));
+ need_comma = 1;
+ }
+ if (na->nc->flags & NI_MSG) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
+ need_comma ? commastr : "",
+ getstring(u->na, NICK_INFO_OPT_MSG));
+ need_comma = 1;
+ }
+
+ notice_lang(s_NickServ, u, NICK_INFO_OPTIONS,
+ *buf ? buf : getstring(u->na, NICK_INFO_OPT_NONE));
+
+ if (na->status & NS_NO_EXPIRE)
+ notice_lang(s_NickServ, u, NICK_INFO_NO_EXPIRE);
+ }
+
+ if (!show_hidden
+ && ((u->na && (na->nc == u->na->nc) && nick_identified(u))
+ || is_servadmin))
+ notice_lang(s_NickServ, u, NICK_INFO_FOR_MORE, s_NickServ,
+ na->nick);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+/* SADMINS can search for nicks based on their NS_VERBOTEN and NS_NO_EXPIRE
+ * status. The keywords FORBIDDEN and NOEXPIRE represent these two states
+ * respectively. These keywords should be included after the search pattern.
+ * Multiple keywords are accepted and should be separated by spaces. Only one
+ * of the keywords needs to match a nick's state for the nick to be displayed.
+ * Forbidden nicks can be identified by "[Forbidden]" appearing in the last
+ * seen address field. Nicks with NOEXPIRE set are preceeded by a "!". Only
+ * SADMINS will be shown forbidden nicks and the "!" indicator.
+ * Syntax for sadmins: LIST pattern [FORBIDDEN] [NOEXPIRE]
+ * -TheShadow
+ */
+
+static int do_list(User * u)
+{
+ char *pattern = strtok(NULL, " ");
+ char *keyword;
+ NickAlias *na;
+ NickCore *mync;
+ int nnicks, i;
+ char buf[BUFSIZE];
+ int is_servadmin = is_services_admin(u);
+ int16 matchflags = 0;
+ NickRequest *nr = NULL;
+ int nronly = 0;
+ char noexpire_char = ' ';
+ int count = 0, from = 0, to = 0;
+ char *tmp = NULL;
+ char *s = NULL;
+
+ if (NSListOpersOnly && !(is_oper(u))) {
+ notice_lang(s_NickServ, u, PERMISSION_DENIED);
+ return MOD_CONT;
+ }
+
+ if (!pattern) {
+ syntax_error(s_NickServ, u, "LIST",
+ is_servadmin ? NICK_LIST_SERVADMIN_SYNTAX :
+ NICK_LIST_SYNTAX);
+ } else {
+
+ if (pattern) {
+ if (pattern[0] == '#') {
+ tmp = myStrGetOnlyToken((pattern + 1), '-', 0); /* Read FROM out */
+ if (!tmp) {
+ return MOD_CONT;
+ }
+ for (s = tmp; *s; s++) {
+ if (!isdigit(*s)) {
+ return MOD_CONT;
+ }
+ }
+ from = atoi(tmp);
+ tmp = myStrGetTokenRemainder(pattern, '-', 1); /* Read TO out */
+ if (!tmp) {
+ return MOD_CONT;
+ }
+ for (s = tmp; *s; s++) {
+ if (!isdigit(*s)) {
+ return MOD_CONT;
+ }
+ }
+ to = atoi(tmp);
+ pattern = sstrdup("*");
+ }
+ }
+
+ nnicks = 0;
+
+ while (is_servadmin && (keyword = strtok(NULL, " "))) {
+ if (stricmp(keyword, "FORBIDDEN") == 0)
+ matchflags |= NS_VERBOTEN;
+ if (stricmp(keyword, "NOEXPIRE") == 0)
+ matchflags |= NS_NO_EXPIRE;
+ if (stricmp(keyword, "UNCONFIRMED") == 0)
+ nronly = 1;
+ }
+
+ mync = (nick_identified(u) ? u->na->nc : NULL);
+
+ notice_lang(s_NickServ, u, NICK_LIST_HEADER, pattern);
+ if (nronly != 1) {
+ for (i = 0; i < 1024; i++) {
+ for (na = nalists[i]; na; na = na->next) {
+ /* Don't show private and forbidden nicks to non-services admins. */
+ if ((na->status & NS_VERBOTEN) && !is_servadmin)
+ continue;
+ if ((na->nc->flags & NI_PRIVATE) && !is_servadmin
+ && na->nc != mync)
+ continue;
+ if ((matchflags != 0) && !(na->status & matchflags))
+ continue;
+
+ /* We no longer compare the pattern against the output buffer.
+ * Instead we build a nice nick!user@host buffer to compare.
+ * The output is then generated separately. -TheShadow */
+ snprintf(buf, sizeof(buf), "%s!%s", na->nick,
+ (na->last_usermask
+ && !(na->status & NS_VERBOTEN)) ? na->
+ last_usermask : "*@*");
+ if (stricmp(pattern, na->nick) == 0
+ || match_wild_nocase(pattern, buf)) {
+
+ if ((((count + 1 >= from) && (count + 1 <= to))
+ || ((from == 0) && (to == 0)))
+ && (++nnicks <= NSListMax)) {
+ if (is_servadmin
+ && (na->status & NS_NO_EXPIRE))
+ noexpire_char = '!';
+ else {
+ noexpire_char = ' ';
+ }
+ if ((na->nc->flags & NI_HIDE_MASK)
+ && !is_servadmin && na->nc != mync) {
+ snprintf(buf, sizeof(buf),
+ "%-20s [Hostname Hidden]",
+ na->nick);
+ } else if (na->status & NS_VERBOTEN) {
+ snprintf(buf, sizeof(buf),
+ "%-20s [Forbidden]", na->nick);
+ } else {
+ snprintf(buf, sizeof(buf), "%-20s %s",
+ na->nick, na->last_usermask);
+ }
+ notice_user(s_NickServ, u, " %c%s",
+ noexpire_char, buf);
+ }
+ count++;
+ }
+ }
+ }
+ }
+
+ if (nronly == 1 || (is_servadmin && matchflags == 0)) {
+ noexpire_char = ' ';
+ for (i = 0; i < 1024; i++) {
+ for (nr = nrlists[i]; nr; nr = nr->next) {
+ snprintf(buf, sizeof(buf), "%s!*@*", nr->nick);
+ if (stricmp(pattern, nr->nick) == 0
+ || match_wild_nocase(pattern, buf)) {
+ if (++nnicks <= NSListMax) {
+ snprintf(buf, sizeof(buf),
+ "%-20s [UNCONFIRMED]", nr->nick);
+ notice_user(s_NickServ, u, " %c%s",
+ noexpire_char, buf);
+ }
+ }
+ }
+ }
+ }
+ notice_lang(s_NickServ, u, NICK_LIST_RESULTS,
+ nnicks > NSListMax ? NSListMax : nnicks, nnicks);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_glist(User * u)
+{
+ char *nick = strtok(NULL, " ");
+
+ NickAlias *na, *na2;
+ int i;
+
+ if ((nick ? !is_services_admin(u) : !nick_identified(u))) {
+ notice_lang(s_NickServ, u, ACCESS_DENIED);
+ } else if ((!nick ? !(na = u->na) : !(na = findnick(nick)))) {
+ notice_lang(s_NickServ, u,
+ (!nick ? NICK_NOT_REGISTERED : NICK_X_NOT_REGISTERED),
+ nick);
+ } else if (na->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ } else {
+ notice_lang(s_NickServ, u,
+ nick ? NICK_GLIST_HEADER_X : NICK_GLIST_HEADER,
+ na->nc->display);
+ for (i = 0; i < na->nc->aliases.count; i++) {
+ na2 = na->nc->aliases.list[i];
+ if (na2->nc == na->nc)
+ notice_user(s_NickServ, u, " %c%s",
+ ((na2->status & NS_NO_EXPIRE) ? '!' : ' '),
+ na2->nick);
+ }
+ notice_lang(s_NickServ, u, NICK_GLIST_FOOTER,
+ na->nc->aliases.count);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+/**
+ * List the channels that the given nickname has access on
+ *
+ * /ns ALIST [level]
+ * /ns ALIST [nickname] [level]
+ *
+ * -jester
+ */
+static int do_alist(User * u)
+{
+ char *lev = strtok(NULL, " ");
+
+ NickAlias *na;
+
+ int min_level = 0;
+ int is_servadmin = is_services_admin(u);
+
+ /* Services admins can request ALIST on nicks.
+ * Check if 'lev' (first token) is actually a nick
+ * and, if so, reassign pointers.
+ */
+ if (is_servadmin && lev && (na = findnick(lev))) {
+ lev = strtok(NULL, " ");
+ } else {
+ na = u->na;
+ }
+
+ /* if a level was given, make sure it's an int for later */
+ if (lev) {
+ if (stricmp(lev, "FOUNDER") == 0) {
+ min_level = ACCESS_FOUNDER;
+ } else if (stricmp(lev, "SOP") == 0) {
+ min_level = ACCESS_SOP;
+ } else if (stricmp(lev, "AOP") == 0) {
+ min_level = ACCESS_AOP;
+#ifdef HAS_HALFOP
+ } else if (stricmp(lev, "HOP") == 0) {
+ min_level = ACCESS_HOP;
+#endif
+ } else if (stricmp(lev, "VOP") == 0) {
+ min_level = ACCESS_VOP;
+ } else {
+ min_level = atoi(lev);
+ }
+ }
+
+ if (!nick_identified(u)) {
+ notice_lang(s_NickServ, u, ACCESS_DENIED);
+ } else if (lev && !na) {
+ notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, na->nick);
+ } else if (na->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ } else {
+ int i, level;
+ int chan_count = 0;
+ int match_count = 0;
+ ChannelInfo *ci;
+
+ notice_lang(s_NickServ, u, (is_servadmin ? NICK_ALIST_HEADER_X :
+ NICK_ALIST_HEADER), na->nick);
+
+ for (i = 0; i < 256; i++) {
+ for ((ci = chanlists[i]); ci; (ci = ci->next)) {
+
+ if ((level = get_access_level(ci, na))) {
+ chan_count++;
+
+ if (min_level > level) {
+ continue;
+ }
+
+ match_count++;
+
+ if ((ci->flags & CI_XOP) || (level == ACCESS_FOUNDER)) {
+ char *xop;
+
+ xop = get_xop_level(level);
+
+ notice_lang(s_NickServ, u, NICK_ALIST_XOP_FORMAT,
+ match_count,
+ ((ci->
+ flags & CI_NO_EXPIRE) ? '!' : ' '),
+ ci->name, xop,
+ (ci->desc ? ci->desc : ""));
+ } else {
+ notice_lang(s_NickServ, u,
+ NICK_ALIST_ACCESS_FORMAT, match_count,
+ ((ci->
+ flags & CI_NO_EXPIRE) ? '!' : ' '),
+ ci->name, level,
+ (ci->desc ? ci->desc : ""));
+
+ }
+ }
+ }
+ }
+
+ notice_lang(s_NickServ, u, NICK_ALIST_FOOTER, match_count,
+ chan_count);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_recover(User * u)
+{
+ char *nick = strtok(NULL, " ");
+ char *pass = strtok(NULL, " ");
+ NickAlias *na;
+ User *u2;
+
+ if (!nick) {
+ syntax_error(s_NickServ, u, "RECOVER", NICK_RECOVER_SYNTAX);
+ } else if (!(u2 = finduser(nick))) {
+ notice_lang(s_NickServ, u, NICK_X_NOT_IN_USE, nick);
+ } else if (!(na = u2->na)) {
+ notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ } else if (na->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ } else if (stricmp(nick, u->nick) == 0) {
+ notice_lang(s_NickServ, u, NICK_NO_RECOVER_SELF);
+ } else if (pass) {
+ int res = check_password(pass, na->nc->pass);
+
+ if (res == 1) {
+ notice_lang(s_NickServ, u2, FORCENICKCHANGE_NOW);
+ collide(na, 0);
+ notice_lang(s_NickServ, u, NICK_RECOVERED, s_NickServ, nick);
+ } else {
+ notice_lang(s_NickServ, u, ACCESS_DENIED);
+ if (res == 0) {
+ alog("%s: RECOVER: invalid password for %s by %s!%s@%s",
+ s_NickServ, nick, u->nick, u->username, GetHost(u));
+ bad_password(u);
+ }
+ }
+ } else {
+ if (group_identified(u, na->nc)
+ || (!(na->nc->flags & NI_SECURE) && is_on_access(u, na->nc))) {
+ notice_lang(s_NickServ, u2, FORCENICKCHANGE_NOW);
+ collide(na, 0);
+ notice_lang(s_NickServ, u, NICK_RECOVERED, s_NickServ, nick);
+ } else {
+ notice_lang(s_NickServ, u, ACCESS_DENIED);
+ }
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_release(User * u)
+{
+ char *nick = strtok(NULL, " ");
+ char *pass = strtok(NULL, " ");
+ NickAlias *na;
+
+ if (!nick) {
+ syntax_error(s_NickServ, u, "RELEASE", NICK_RELEASE_SYNTAX);
+ } else if (!(na = findnick(nick))) {
+ notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ } else if (na->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ } else if (!(na->status & NS_KILL_HELD)) {
+ notice_lang(s_NickServ, u, NICK_RELEASE_NOT_HELD, nick);
+ } else if (pass) {
+ int res = check_password(pass, na->nc->pass);
+ if (res == 1) {
+ release(na, 0);
+ notice_lang(s_NickServ, u, NICK_RELEASED);
+ } else {
+ notice_lang(s_NickServ, u, ACCESS_DENIED);
+ if (res == 0) {
+ alog("%s: RELEASE: invalid password for %s by %s!%s@%s",
+ s_NickServ, nick, u->nick, u->username, GetHost(u));
+ bad_password(u);
+ }
+ }
+ } else {
+ if (group_identified(u, na->nc)
+ || (!(na->nc->flags & NI_SECURE) && is_on_access(u, na->nc))) {
+ release(na, 0);
+ notice_lang(s_NickServ, u, NICK_RELEASED);
+ } else {
+ notice_lang(s_NickServ, u, ACCESS_DENIED);
+ }
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_ghost(User * u)
+{
+ char *nick = strtok(NULL, " ");
+ char *pass = strtok(NULL, " ");
+ NickAlias *na;
+ User *u2;
+
+ if (!nick) {
+ syntax_error(s_NickServ, u, "GHOST", NICK_GHOST_SYNTAX);
+ } else if (!(u2 = finduser(nick))) {
+ notice_lang(s_NickServ, u, NICK_X_NOT_IN_USE, nick);
+ } else if (!(na = u2->na)) {
+ notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ } else if (na->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ } else if (stricmp(nick, u->nick) == 0) {
+ notice_lang(s_NickServ, u, NICK_NO_GHOST_SELF);
+ } else if (pass) {
+ int res = check_password(pass, na->nc->pass);
+ if (res == 1) {
+ char buf[NICKMAX + 32];
+ snprintf(buf, sizeof(buf), "GHOST command used by %s",
+ u->nick);
+#ifdef IRC_BAHAMUT
+ send_cmd(NULL, "SVSKILL %s :%s", nick, buf);
+#else
+ kill_user(s_NickServ, nick, buf);
+#endif
+ notice_lang(s_NickServ, u, NICK_GHOST_KILLED, nick);
+ } else {
+ notice_lang(s_NickServ, u, ACCESS_DENIED);
+ if (res == 0) {
+ alog("%s: GHOST: invalid password for %s by %s!%s@%s",
+ s_NickServ, nick, u->nick, u->username, GetHost(u));
+ bad_password(u);
+ }
+ }
+ } else {
+ if (group_identified(u, na->nc)
+ || (!(na->nc->flags & NI_SECURE) && is_on_access(u, na->nc))) {
+ char buf[NICKMAX + 32];
+ snprintf(buf, sizeof(buf), "GHOST command used by %s",
+ u->nick);
+#ifdef IRC_BAHAMUT
+ send_cmd(NULL, "SVSKILL %s :%s", nick, buf);
+#else
+ kill_user(s_NickServ, nick, buf);
+#endif
+ notice_lang(s_NickServ, u, NICK_GHOST_KILLED, nick);
+ } else {
+ notice_lang(s_NickServ, u, ACCESS_DENIED);
+ }
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_status(User * u)
+{
+ char *nick;
+ User *u2;
+ int i = 0;
+
+ while ((nick = strtok(NULL, " ")) && (i++ < 16)) {
+ if (!(u2 = finduser(nick)))
+ notice_user(s_NickServ, u, "STATUS %s 0", nick);
+ else if (nick_identified(u2))
+ notice_user(s_NickServ, u, "STATUS %s 3", nick);
+ else if (nick_recognized(u2))
+ notice_user(s_NickServ, u, "STATUS %s 2", nick);
+ else
+ notice_user(s_NickServ, u, "STATUS %s 1", nick);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+/* A simple call to check for all emails that a user may have registered */
+/* with. It returns the nicks that match the email you provide. Wild */
+/* Cards are not excepted. Must use user@email-host. */
+/*************************************************************************/
+static int do_getemail(User * u)
+{
+ char *email = strtok(NULL, " ");
+ int i, j = 0;
+ NickCore *nc;
+
+ if (!email) {
+ syntax_error(s_NickServ, u, "GETMAIL", NICK_GETEMAIL_SYNTAX);
+ return MOD_CONT;
+ }
+ alog("%s: %s!%s@%s used GETEMAIL on %s", s_NickServ, u->nick,
+ u->username, GetHost(u), email);
+ for (i = 0; i < 1024; i++) {
+ for (nc = nclists[i]; nc; nc = nc->next) {
+ if (nc->email) {
+ if (stricmp(nc->email, email) == 0) {
+ j++;
+ notice_lang(s_NickServ, u, NICK_GETEMAIL_EMAILS_ARE,
+ nc->display, email);
+ }
+ }
+ }
+ }
+ if (j <= 0) {
+ notice_lang(s_NickServ, u, NICK_GETEMAIL_NOT_USED, email);
+ return MOD_CONT;
+ }
+ return MOD_CONT;
+}
+
+/**************************************************************************/
+
+static int do_getpass(User * u)
+{
+#ifndef USE_ENCRYPTION
+ char *nick = strtok(NULL, " ");
+ NickAlias *na;
+ NickRequest *nr = NULL;
+#endif
+
+ /* Assumes that permission checking has already been done. */
+#ifdef USE_ENCRYPTION
+ notice_lang(s_NickServ, u, NICK_GETPASS_UNAVAILABLE);
+#else
+ if (!nick) {
+ syntax_error(s_NickServ, u, "GETPASS", NICK_GETPASS_SYNTAX);
+ } else if (!(na = findnick(nick))) {
+ if ((nr = findrequestnick(nick))) {
+ alog("%s: %s!%s@%s used GETPASS on %s", s_NickServ, u->nick,
+ u->username, GetHost(u), nick);
+ if (WallGetpass)
+ wallops(s_NickServ, "\2%s\2 used GETPASS on \2%s\2",
+ u->nick, nick);
+ notice_lang(s_NickServ, u, NICK_GETPASS_PASSCODE_IS, nick,
+ nr->passcode);
+ } else {
+ notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ }
+ } else if (na->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ } else if (NSSecureAdmins && nick_is_services_admin(na->nc)
+ && !is_services_root(u)) {
+ notice_lang(s_NickServ, u, PERMISSION_DENIED);
+ } else if (NSRestrictGetPass && !is_services_root(u)) {
+ notice_lang(s_NickServ, u, PERMISSION_DENIED);
+ } else {
+ alog("%s: %s!%s@%s used GETPASS on %s", s_NickServ, u->nick,
+ u->username, GetHost(u), nick);
+ if (WallGetpass)
+ wallops(s_NickServ, "\2%s\2 used GETPASS on \2%s\2", u->nick,
+ nick);
+ notice_lang(s_NickServ, u, NICK_GETPASS_PASSWORD_IS, nick,
+ na->nc->pass);
+ }
+#endif
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_sendpass(User * u)
+{
+#ifndef USE_ENCRYPTION
+ char *nick = strtok(NULL, " ");
+ NickAlias *na;
+#endif
+
+#ifdef USE_ENCRYPTION
+ notice_lang(s_NickServ, u, NICK_SENDPASS_UNAVAILABLE);
+#else
+ if (!nick) {
+ syntax_error(s_NickServ, u, "SENDPASS", NICK_SENDPASS_SYNTAX);
+ } else if (RestrictMail && !is_oper(u)) {
+ notice_lang(s_NickServ, u, PERMISSION_DENIED);
+ } else if (!(na = findnick(nick))) {
+ notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ } else if (na->status & NS_VERBOTEN) {
+ notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ } else {
+ char buf[BUFSIZE];
+ MailInfo *mail;
+
+ snprintf(buf, sizeof(buf), getstring(na, NICK_SENDPASS_SUBJECT),
+ na->nick);
+ mail = MailBegin(u, na->nc, buf, s_NickServ);
+ if (!mail)
+ return MOD_CONT;
+
+ fprintf(mail->pipe, getstring(na, NICK_SENDPASS_HEAD));
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring(na, NICK_SENDPASS_LINE_1), na->nick);
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring(na, NICK_SENDPASS_LINE_2),
+ na->nc->pass);
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring(na, NICK_SENDPASS_LINE_3));
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring(na, NICK_SENDPASS_LINE_4));
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring(na, NICK_SENDPASS_LINE_5),
+ NetworkName);
+ fprintf(mail->pipe, "\n.\n");
+
+ MailEnd(mail);
+
+ alog("%s: %s!%s@%s used SENDPASS on %s", s_NickServ, u->nick,
+ u->username, GetHost(u), nick);
+ notice_lang(s_NickServ, u, NICK_SENDPASS_OK, nick);
+ }
+#endif
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_forbid(User * u)
+{
+ NickAlias *na;
+ char *nick = strtok(NULL, " ");
+ char *reason = strtok(NULL, "");
+
+ /* Assumes that permission checking has already been done. */
+ if (!nick || (ForceForbidReason && !reason)) {
+ syntax_error(s_NickServ, u, "FORBID",
+ (ForceForbidReason ? NICK_FORBID_SYNTAX_REASON :
+ NICK_FORBID_SYNTAX));
+ return MOD_CONT;
+ }
+
+ if (readonly)
+ notice_lang(s_NickServ, u, READ_ONLY_MODE);
+ if ((na = findnick(nick)) != NULL) {
+ if (NSSecureAdmins && nick_is_services_admin(na->nc)
+ && !is_services_root(u)) {
+ notice_lang(s_NickServ, u, PERMISSION_DENIED);
+ return MOD_CONT;
+ }
+ delnick(na);
+ }
+ na = makenick(nick);
+ if (na) {
+ na->status |= NS_VERBOTEN;
+ na->last_usermask = sstrdup(u->nick);
+ if (reason)
+ na->last_realname = sstrdup(reason);
+
+ na->u = finduser(na->nick);
+ if (na->u)
+ na->u->na = na;
+
+ if (na->u) {
+ notice_lang(s_NickServ, na->u, FORCENICKCHANGE_NOW);
+ collide(na, 0);
+ }
+
+ if (WallForbid)
+ wallops(s_NickServ, "\2%s\2 used FORBID on \2%s\2", u->nick,
+ nick);
+
+ alog("%s: %s set FORBID for nick %s", s_NickServ, u->nick, nick);
+ notice_lang(s_NickServ, u, NICK_FORBID_SUCCEEDED, nick);
+ } else {
+ alog("%s: Valid FORBID for %s by %s failed", s_NickServ, nick,
+ u->nick);
+ notice_lang(s_NickServ, u, NICK_FORBID_FAILED, nick);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+int ns_do_register(User * u)
+{
+ return do_register(u);
+}