/* Nickname access list module. * * IRC Services is copyright (c) 1996-2007 Andrew Church. * E-mail: * Parts written by Andrew Kempe and others. * This program is free but copyrighted software; see the file COPYING for * details. */ #include "services.h" #include "modules.h" #include "conffile.h" #include "language.h" #include "commands.h" #include "modules/operserv/operserv.h" #include "nickserv.h" #include "ns-local.h" /*************************************************************************/ static Module *module; static Module *module_nickserv; static int32 NSAccessMax; static int NSFirstAccessEnable; static int NSFirstAccessWild; /*************************************************************************/ static void do_access(User *u); static Command cmds[] = { {"ACCESS", do_access, NULL, NICK_HELP_ACCESS, -1, NICK_OPER_HELP_ACCESS}, { NULL } }; /*************************************************************************/ /**************************** Local routines *****************************/ /*************************************************************************/ /* Handle the ACCESS command. */ static void do_access(User *u) { char *cmd = strtok(NULL, " "); char *mask = strtok(NULL, " "); NickInfo *ni; NickGroupInfo *ngi; int i; if (cmd && stricmp(cmd, "LIST") == 0 && mask && is_services_admin(u)) { ni = get_nickinfo(mask); if (!ni) { notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, mask); } else if (ni->status & NS_VERBOTEN) { notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, mask); } else if (!(ngi = get_ngi(ni))) { notice_lang(s_NickServ, u, INTERNAL_ERROR); } else if (ngi->access_count == 0) { notice_lang(s_NickServ, u, NICK_ACCESS_LIST_X_EMPTY, mask); } else { notice_lang(s_NickServ, u, NICK_ACCESS_LIST_X, mask); ARRAY_FOREACH (i, ngi->access) notice(s_NickServ, u->nick, " %s", ngi->access[i]); } } else if (!cmd || ((stricmp(cmd,"LIST")==0) ? mask!=NULL : mask==NULL)) { 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 (ngi = u->ngi, !(ni = u->ni)) { notice_lang(s_NickServ, u, NICK_NOT_REGISTERED); } else if (!user_identified(u)) { notice_lang(s_NickServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ); } else if (stricmp(cmd, "ADD") == 0) { if (readonly) { notice_lang(s_NickServ, u, NICK_ACCESS_DISABLED); return; } if (ngi->access_count >= NSAccessMax) { notice_lang(s_NickServ, u, NICK_ACCESS_REACHED_LIMIT, NSAccessMax); return; } ARRAY_FOREACH (i, ngi->access) { if (stricmp(ngi->access[i], mask) == 0) { notice_lang(s_NickServ, u, NICK_ACCESS_ALREADY_PRESENT, mask); return; } } if (strchr(mask, '!')) notice_lang(s_NickServ, u, NICK_ACCESS_NO_NICKS); ARRAY_EXTEND(ngi->access); ngi->access[ngi->access_count-1] = sstrdup(mask); put_nickgroupinfo(ngi); notice_lang(s_NickServ, u, NICK_ACCESS_ADDED, mask); } else if (stricmp(cmd, "DEL") == 0) { if (readonly) { notice_lang(s_NickServ, u, NICK_ACCESS_DISABLED); return; } /* First try for an exact match; then, a case-insensitive one. */ ARRAY_SEARCH_PLAIN(ngi->access, mask, strcmp, i); if (i == ngi->access_count) ARRAY_SEARCH_PLAIN(ngi->access, mask, stricmp, i); if (i == ngi->access_count) { notice_lang(s_NickServ, u, NICK_ACCESS_NOT_FOUND, mask); return; } notice_lang(s_NickServ, u, NICK_ACCESS_DELETED, ngi->access[i]); free(ngi->access[i]); ARRAY_REMOVE(ngi->access, i); put_nickgroupinfo(ngi); } else if (stricmp(cmd, "LIST") == 0) { if (ngi->access_count == 0) { notice_lang(s_NickServ, u, NICK_ACCESS_LIST_EMPTY); } else { notice_lang(s_NickServ, u, NICK_ACCESS_LIST); ARRAY_FOREACH (i, ngi->access) notice(s_NickServ, u->nick, " %s", ngi->access[i]); } } else { syntax_error(s_NickServ, u, "ACCESS", NICK_ACCESS_SYNTAX); } } /*************************************************************************/ /*************************** Callback routines ***************************/ /*************************************************************************/ /* Nick-registration callback (initializes access list). */ static int do_registered(User *u, NickInfo *ni, NickGroupInfo *ngi, int *replied) { if (NSFirstAccessEnable) { ngi->access_count = 1; ngi->access = smalloc(sizeof(char *)); if (NSFirstAccessWild) { ngi->access[0] = create_mask(u, 0); } else { ngi->access[0] = smalloc(strlen(u->username)+strlen(u->host)+2); sprintf(ngi->access[0], "%s@%s", u->username, u->host); } } return 0; } /*************************************************************************/ /* Check whether a user is on the access list of the nick they're using. * Return 1 if on the access list, 0 if not. */ static int check_on_access(User *u) { int i; char buf[BUFSIZE]; if (!u->ni || !u->ngi) { module_log("check_on_access() BUG: ni or ngi is NULL!"); return 0; } if (u->ngi->access_count == 0) return 0; i = strlen(u->username); snprintf(buf, sizeof(buf), "%s@%s", u->username, u->host); ARRAY_FOREACH (i, u->ngi->access) { if (match_wild_nocase(u->ngi->access[i], buf)) return 1; } return 0; } /*************************************************************************/ /***************************** Module stuff ******************************/ /*************************************************************************/ const int32 module_version = MODULE_VERSION_CODE; ConfigDirective module_config[] = { { "NSAccessMax", { { CD_POSINT, CF_DIRREQ, &NSAccessMax } } }, { "NSFirstAccessEnable",{{CD_SET, 0, &NSFirstAccessEnable } } }, { "NSFirstAccessWild",{ { CD_SET, 0, &NSFirstAccessWild } } }, { NULL } }; /*************************************************************************/ int init_module(Module *module_) { module = module_; if (NSAccessMax > MAX_NICK_ACCESS) { module_log("NSAccessMax upper-bounded at MAX_NICK_ACCESS (%d)", MAX_NICK_ACCESS); NSAccessMax = MAX_NICK_ACCESS; } module_nickserv = find_module("nickserv/main"); if (!module_nickserv) { module_log("Main NickServ module not loaded"); return 0; } use_module(module_nickserv); if (!register_commands(module_nickserv, cmds)) { module_log("Unable to register commands"); exit_module(0); return 0; } if (!add_callback(module_nickserv, "check recognized", check_on_access) || !add_callback(module_nickserv, "registered", do_registered) ) { exit_module(0); return 0; } return 1; } /*************************************************************************/ int exit_module(int shutdown_unused) { #ifdef CLEAN_COMPILE shutdown_unused = shutdown_unused; #endif if (module_nickserv) { remove_callback(module_nickserv, "registered", do_registered); remove_callback(module_nickserv, "check recognized", check_on_access); unregister_commands(module_nickserv, cmds); unuse_module(module_nickserv); module_nickserv = NULL; } return 1; } /*************************************************************************/