/* Routines related to nickname colliding. * * 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 "language.h" #include "timeout.h" #include "nickserv.h" #include "ns-local.h" /*************************************************************************/ static Module *module; static int cb_collide = -1; /*************************************************************************/ /*************************************************************************/ /* Introduce an enforcer for a given nick. */ void introduce_enforcer(NickInfo *ni) { char realname[NICKMAX+16]; /*Long enough for s_NickServ + " Enforcement"*/ snprintf(realname, sizeof(realname), "%s Enforcement", s_NickServ); send_nick(ni->nick, NSEnforcerUser, NSEnforcerHost, ServerName, realname, enforcer_modes); ni->status |= NS_KILL_HELD; add_ns_timeout(ni, TO_RELEASE, NSReleaseTimeout); } /*************************************************************************/ /* Collide a nick. */ void collide(NickInfo *ni, int from_timeout) { if (!ni->user) return; if (!from_timeout) rem_ns_timeout(ni, TO_COLLIDE, 1); if (call_callback_1(module, cb_collide, ni->user) > 0) return; if (NSForceNickChange) { char *guestnick = make_guest_nick(); notice_lang(s_NickServ, ni->user, FORCENICKCHANGE_NOW, guestnick); send_nickchange_remote(ni->nick, guestnick); ni->status |= NS_GUESTED; return; } else { notice_lang(s_NickServ, ni->user, DISCONNECT_NOW); kill_user(s_NickServ, ni->nick, "Nick kill enforced"); introduce_enforcer(ni); } } /*************************************************************************/ /* Release hold on a nick. */ void release(NickInfo *ni, int from_timeout) { if (!from_timeout) rem_ns_timeout(ni, TO_RELEASE, 1); send_cmd(ni->nick, "QUIT"); ni->status &= ~NS_KILL_HELD; } /*************************************************************************/ /*************************************************************************/ static struct my_timeout { struct my_timeout *next, *prev; NickInfo *ni; Timeout *to; int type; } *my_timeouts; /*************************************************************************/ /* Collide a nick on timeout. */ static void timeout_collide(Timeout *t) { NickInfo *ni = t->data; NickGroupInfo *ngi = NULL; if (ni) { if (ni->nickgroup != 0) ngi = get_ngi(ni); } else { log("BUG: NULL NickInfo in timeout_collide"); return; } rem_ns_timeout(ni, TO_COLLIDE, 0); /* If they identified or don't exist anymore, don't kill them. */ if ((ngi && (nick_identified(ni) || nick_ident_nomail(ni))) || !ni->user || ni->user->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(ni, 1); } /*************************************************************************/ /* Release a nick on timeout. */ static void timeout_release(Timeout *t) { NickInfo *ni = t->data; if (!ni) { log("BUG: NULL NickInfo in timeout_release"); return; } rem_ns_timeout(ni, TO_RELEASE, 0); release(ni, 1); } /*************************************************************************/ /* Send a 433 (nick in use) numeric to the given user. */ static void timeout_send_433(Timeout *t) { NickInfo *ni = t->data; User *u; if (!ni) { log("BUG: NULL NickInfo in timeout_send_433"); return; } rem_ns_timeout(ni, TO_SEND_433, 0); /* If they identified or don't exist anymore, don't send the 433. */ if ((nick_identified(ni) || nick_ident_nomail(ni)) || !(u = get_user(ni->nick)) || u->my_signon > t->settime) return; if (ni->status & NS_VERBOTEN) { send_cmd(ServerName, "433 %s %s :Nickname may not be used", ni->nick, ni->nick); } else { send_cmd(ServerName, "433 %s %s :Nickname is registered to someone" " else", ni->nick, ni->nick); } } /*************************************************************************/ /* Add a collide/release/433 timeout. */ void add_ns_timeout(NickInfo *ni, int type, time_t delay) { Timeout *to; struct my_timeout *t; void (*timeout_routine)(Timeout *); if (!ni) { log("BUG: NULL NickInfo in add_ns_timeout (type=%d delay=%ld)", type, (long)delay); return; } if (type == TO_COLLIDE) timeout_routine = timeout_collide; else if (type == TO_RELEASE) timeout_routine = timeout_release; else if (type == TO_SEND_433) timeout_routine = timeout_send_433; else { module_log("BUG: unknown timeout type %d! ni=%p (%s), delay=%ld", type, ni, ni->nick, (long)delay); return; } to = add_timeout(delay, timeout_routine, 0); to->data = ni; t = smalloc(sizeof(*t)); LIST_INSERT(t, my_timeouts); t->ni = ni; t->to = to; t->type = type; } /*************************************************************************/ /* Remove a collide/release timeout from our private list. If del_to is * nonzero, also delete the associated timeout. If type == -1, delete * timeouts of all types. If ni == NULL, delete all timeouts of the given * type(s). */ void rem_ns_timeout(NickInfo *ni, int type, int del_to) { struct my_timeout *t, *t2; LIST_FOREACH_SAFE (t, my_timeouts, t2) { if ((!ni || t->ni == ni) && (type < 0 || t->type == type)) { LIST_REMOVE(t, my_timeouts); if (del_to) del_timeout(t->to); free(t); } } } /*************************************************************************/ /*************************************************************************/ int init_collide(Module *my_module) { module = my_module; cb_collide = register_callback(module, "collide"); if (cb_collide < 0) { module_log("collide: Unable to register callbacks"); exit_collide(); return 0; } return 1; } /*************************************************************************/ void exit_collide() { rem_ns_timeout(NULL, -1, 1); unregister_callback(module, cb_collide); } /*************************************************************************/