/* Routines related to nickname colliding.
*
* IRC Services is copyright (c) 1996-2007 Andrew Church.
* E-mail: <achurch@achurch.org>
* 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);
}
/*************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1