diff options
Diffstat (limited to 'nickserv.c')
-rw-r--r-- | nickserv.c | 4010 |
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); +} |