Move watch code from core to module

This commit is contained in:
k4be 2021-07-19 18:16:28 +02:00
parent 15ac841aea
commit bcbcc10b9d
No known key found for this signature in database
GPG key ID: CF966708422300DC
9 changed files with 463 additions and 346 deletions

View file

@ -334,7 +334,6 @@ extern void del_queries(char *);
/* Hash stuff */
#define NICK_HASH_TABLE_SIZE 32768
#define CHAN_HASH_TABLE_SIZE 32768
#define WATCH_HASH_TABLE_SIZE 32768
#define WHOWAS_HASH_TABLE_SIZE 32768
#define THROTTLING_HASH_TABLE_SIZE 8192
#define hash_find_channel find_channel
@ -350,12 +349,6 @@ extern int add_to_id_hash_table(char *, Client *);
extern int del_from_id_hash_table(char *, Client *);
extern int add_to_channel_hash_table(char *, Channel *);
extern void del_from_channel_hash_table(char *, Channel *);
extern int add_to_watch_hash_table(char *, Client *, int);
extern int del_from_watch_hash_table(char *, Client *);
extern int hash_check_watch(Client *, int);
extern int hash_del_watch_list(Client *);
extern void count_watch_memory(int *, u_long *);
extern Watch *hash_get_watch(char *);
extern Channel *hash_get_chan_bucket(uint64_t);
extern Client *hash_find_client(const char *, Client *);
extern Client *hash_find_id(const char *, Client *);

View file

@ -1165,6 +1165,10 @@ extern void SavePersistentLongX(ModuleInfo *modinfo, char *varshortname, long va
#define HOOKTYPE_CONNECT_EXTINFO 104
/** See hooktype_is_invited() */
#define HOOKTYPE_IS_INVITED 105
/** See hooktype_post_local_nickchange() */
#define HOOKTYPE_POST_LOCAL_NICKCHANGE 106
/** See hooktype_post_remote_nickchange() */
#define HOOKTYPE_POST_REMOTE_NICKCHANGE 107
/* Adding a new hook here?
* 1) Add the #define HOOKTYPE_.... with a new number
* 2) Add a hook prototype (see below)
@ -1523,9 +1527,10 @@ int hooktype_modechar_add(Channel *channel, int modechar);
* @param client The client
* @param mtags Message tags associated with the event
* @param reason The away reason, or NULL if away is unset.
* @param already_as_away Set to 1 if the user only changed their away reason.
* @return The return value is ignored (use return 0)
*/
int hooktype_away(Client *client, MessageTag *mtags, char *reason);
int hooktype_away(Client *client, MessageTag *mtags, char *reason, int already_as_away);
/** Called when a user wants to invite another user to a channel (function prototype for HOOKTYPE_PRE_INVITE).
* @param client The client
@ -2115,6 +2120,20 @@ int hooktype_connect_extinfo(Client *client, NameValuePrioList **list);
*/
int hooktype_is_invited(Client *client, Channel *channel, int *invited);
/** Called after a local user has changed the nick name (function prototype for HOOKTYPE_POST_LOCAL_NICKCHANGE).
* @param client The client
* @param mtags Message tags associated with the event
* @return The return value is ignored (use return 0)
*/
int hooktype_post_local_nickchange(Client *client, MessageTag *mtags);
/** Called after a remote user has changed the nick name (function prototype for HOOKTYPE_POST_REMOTE_NICKCHANGE).
* @param client The client
* @param mtags Message tags associated with the event
* @return The return value is ignored (use return 0)
*/
int hooktype_post_remote_nickchange(Client *client, MessageTag *mtags);
/** @} */
#ifdef GCC_TYPECHECKING
@ -2225,7 +2244,9 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum
((hooktype == HOOKTYPE_ACCOUNT_LOGIN) && !ValidateHook(hooktype_account_login, func)) || \
((hooktype == HOOKTYPE_CLOSE_CONNECTION) && !ValidateHook(hooktype_close_connection, func)) || \
((hooktype == HOOKTYPE_CONNECT_EXTINFO) && !ValidateHook(hooktype_connect_extinfo, func)) || \
((hooktype == HOOKTYPE_IS_INVITED) && !ValidateHook(hooktype_is_invited, func)) ) \
((hooktype == HOOKTYPE_IS_INVITED) && !ValidateHook(hooktype_is_invited, func)) || \
((hooktype == HOOKTYPE_POST_LOCAL_NICKCHANGE) && !ValidateHook(hooktype_post_local_nickchange, func)) || \
((hooktype == HOOKTYPE_POST_REMOTE_NICKCHANGE) && !ValidateHook(hooktype_post_remote_nickchange, func)) ) \
_hook_error_incompatible();
#endif /* GCC_TYPECHECKING */

View file

@ -1323,8 +1323,6 @@ struct LocalClient {
u_short sendB; /**< Statistics: counters to count upto 1-k lots of bytes */
u_short receiveB; /**< Statistics: sent and received (???) */
short lastsq; /**< # of 2k blocks when sendqueued called last */
Link *watch; /**< Watch notification list (WATCH) for this user */
u_short watches; /**< Number of entries in the watch list */
ModData moddata[MODDATA_MAX_LOCAL_CLIENT]; /**< LocalClient attached module data, used by the ModData system */
#ifdef DEBUGMODE
time_t cputime; /**< Something with debugging (why is this a time_t? TODO) */

View file

@ -260,11 +260,9 @@ void siphash_generate_key(char *k)
static struct list_head clientTable[NICK_HASH_TABLE_SIZE];
static struct list_head idTable[NICK_HASH_TABLE_SIZE];
static Channel *channelTable[CHAN_HASH_TABLE_SIZE];
static Watch *watchTable[WATCH_HASH_TABLE_SIZE];
static char siphashkey_nick[SIPHASH_KEY_LENGTH];
static char siphashkey_chan[SIPHASH_KEY_LENGTH];
static char siphashkey_watch[SIPHASH_KEY_LENGTH];
static char siphashkey_whowas[SIPHASH_KEY_LENGTH];
static char siphashkey_throttling[SIPHASH_KEY_LENGTH];
@ -277,7 +275,6 @@ void init_hash(void)
siphash_generate_key(siphashkey_nick);
siphash_generate_key(siphashkey_chan);
siphash_generate_key(siphashkey_watch);
siphash_generate_key(siphashkey_whowas);
siphash_generate_key(siphashkey_throttling);
@ -288,7 +285,6 @@ void init_hash(void)
INIT_LIST_HEAD(&idTable[i]);
memset(channelTable, 0, sizeof(channelTable));
memset(watchTable, 0, sizeof(watchTable));
memset(ThrottlingHash, 0, sizeof(ThrottlingHash));
/* do not call init_throttling() here, as
@ -310,11 +306,6 @@ uint64_t hash_channel_name(const char *name)
return siphash_nocase(name, siphashkey_chan) % CHAN_HASH_TABLE_SIZE;
}
uint64_t hash_watch_nick_name(const char *name)
{
return siphash_nocase(name, siphashkey_watch) % WATCH_HASH_TABLE_SIZE;
}
uint64_t hash_whowas_name(const char *name)
{
return siphash_nocase(name, siphashkey_whowas) % WHOWAS_HASH_TABLE_SIZE;
@ -599,303 +590,6 @@ Channel *hash_get_chan_bucket(uint64_t hashv)
return channelTable[hashv];
}
void count_watch_memory(int *count, u_long *memory)
{
int i = WATCH_HASH_TABLE_SIZE;
Watch *anptr;
while (i--)
{
anptr = watchTable[i];
while (anptr)
{
(*count)++;
(*memory) += sizeof(Watch)+strlen(anptr->nick);
anptr = anptr->hnext;
}
}
}
/*
* add_to_watch_hash_table
*/
int add_to_watch_hash_table(char *nick, Client *client, int awaynotify)
{
unsigned int hashv;
Watch *anptr;
Link *lp;
/* Get the right bucket... */
hashv = hash_watch_nick_name(nick);
/* Find the right nick (header) in the bucket, or NULL... */
if ((anptr = (Watch *)watchTable[hashv]))
while (anptr && mycmp(anptr->nick, nick))
anptr = anptr->hnext;
/* If found NULL (no header for this nick), make one... */
if (!anptr) {
anptr = (Watch *)safe_alloc(sizeof(Watch)+strlen(nick));
anptr->lasttime = timeofday;
strcpy(anptr->nick, nick);
anptr->watch = NULL;
anptr->hnext = watchTable[hashv];
watchTable[hashv] = anptr;
}
/* Is this client already on the watch-list? */
if ((lp = anptr->watch))
while (lp && (lp->value.client != client))
lp = lp->next;
/* No it isn't, so add it in the bucket and client addint it */
if (!lp) {
lp = anptr->watch;
anptr->watch = make_link();
anptr->watch->value.client = client;
anptr->watch->flags = awaynotify;
anptr->watch->next = lp;
lp = make_link();
lp->next = client->local->watch;
lp->value.wptr = anptr;
lp->flags = awaynotify;
client->local->watch = lp;
client->local->watches++;
}
return 0;
}
/*
* hash_check_watch
*/
int hash_check_watch(Client *client, int reply)
{
unsigned int hashv;
Watch *anptr;
Link *lp;
int awaynotify = 0;
if ((reply == RPL_GONEAWAY) || (reply == RPL_NOTAWAY) || (reply == RPL_REAWAY))
awaynotify = 1;
/* Get us the right bucket */
hashv = hash_watch_nick_name(client->name);
/* Find the right header in this bucket */
if ((anptr = (Watch *)watchTable[hashv]))
while (anptr && mycmp(anptr->nick, client->name))
anptr = anptr->hnext;
if (!anptr)
return 0; /* This nick isn't on watch */
/* Update the time of last change to item */
anptr->lasttime = TStime();
/* Send notifies out to everybody on the list in header */
for (lp = anptr->watch; lp; lp = lp->next)
{
if (!awaynotify)
{
sendnumeric(lp->value.client, reply,
client->name,
(IsUser(client) ? client->user->username : "<N/A>"),
(IsUser(client) ?
(IsHidden(client) ? client->user->virthost : client->
user->realhost) : "<N/A>"), anptr->lasttime, client->info);
}
else
{
/* AWAY or UNAWAY */
if (!lp->flags)
continue; /* skip away/unaway notification for users not interested in them */
if (reply == RPL_NOTAWAY)
sendnumeric(lp->value.client, reply,
client->name,
(IsUser(client) ? client->user->username : "<N/A>"),
(IsUser(client) ?
(IsHidden(client) ? client->user->virthost : client->
user->realhost) : "<N/A>"), client->user->lastaway);
else /* RPL_GONEAWAY / RPL_REAWAY */
sendnumeric(lp->value.client, reply,
client->name,
(IsUser(client) ? client->user->username : "<N/A>"),
(IsUser(client) ?
(IsHidden(client) ? client->user->virthost : client->
user->realhost) : "<N/A>"), client->user->lastaway, client->user->away);
}
}
return 0;
}
/*
* hash_get_watch
*/
Watch *hash_get_watch(char *nick)
{
unsigned int hashv;
Watch *anptr;
hashv = hash_watch_nick_name(nick);
if ((anptr = (Watch *)watchTable[hashv]))
while (anptr && mycmp(anptr->nick, nick))
anptr = anptr->hnext;
return anptr;
}
/*
* del_from_watch_hash_table
*/
int del_from_watch_hash_table(char *nick, Client *client)
{
unsigned int hashv;
Watch *anptr, *nlast = NULL;
Link *lp, *last = NULL;
/* Get the bucket for this nick... */
hashv = hash_watch_nick_name(nick);
/* Find the right header, maintaining last-link pointer... */
if ((anptr = (Watch *)watchTable[hashv]))
while (anptr && mycmp(anptr->nick, nick)) {
nlast = anptr;
anptr = anptr->hnext;
}
if (!anptr)
return 0; /* No such watch */
/* Find this client from the list of notifies... with last-ptr. */
if ((lp = anptr->watch))
while (lp && (lp->value.client != client)) {
last = lp;
lp = lp->next;
}
if (!lp)
return 0; /* No such client to watch */
/* Fix the linked list under header, then remove the watch entry */
if (!last)
anptr->watch = lp->next;
else
last->next = lp->next;
free_link(lp);
/* Do the same regarding the links in client-record... */
last = NULL;
if ((lp = client->local->watch))
while (lp && (lp->value.wptr != anptr)) {
last = lp;
lp = lp->next;
}
/*
* Give error on the odd case... probobly not even neccessary
* No error checking in ircd is unneccessary ;) -Cabal95
*/
if (!lp)
sendto_ops("WATCH debug error: del_from_watch_hash_table "
"found a watch entry with no client "
"counterpoint processing nick %s on client %p!",
nick, client->user);
else {
if (!last) /* First one matched */
client->local->watch = lp->next;
else
last->next = lp->next;
free_link(lp);
}
/* In case this header is now empty of notices, remove it */
if (!anptr->watch) {
if (!nlast)
watchTable[hashv] = anptr->hnext;
else
nlast->hnext = anptr->hnext;
safe_free(anptr);
}
/* Update count of notifies on nick */
client->local->watches--;
return 0;
}
/*
* hash_del_watch_list
*/
int hash_del_watch_list(Client *client)
{
unsigned int hashv;
Watch *anptr;
Link *np, *lp, *last;
if (!(np = client->local->watch))
return 0; /* Nothing to do */
client->local->watch = NULL; /* Break the watch-list for client */
while (np) {
/* Find the watch-record from hash-table... */
anptr = np->value.wptr;
last = NULL;
for (lp = anptr->watch; lp && (lp->value.client != client);
lp = lp->next)
last = lp;
/* Not found, another "worst case" debug error */
if (!lp)
sendto_ops("WATCH Debug error: hash_del_watch_list "
"found a WATCH entry with no table "
"counterpoint processing client %s!",
client->name);
else {
/* Fix the watch-list and remove entry */
if (!last)
anptr->watch = lp->next;
else
last->next = lp->next;
free_link(lp);
/*
* If this leaves a header without notifies,
* remove it. Need to find the last-pointer!
*/
if (!anptr->watch) {
Watch *np2, *nl;
hashv = hash_watch_nick_name(anptr->nick);
nl = NULL;
np2 = watchTable[hashv];
while (np2 != anptr) {
nl = np2;
np2 = np2->hnext;
}
if (nl)
nl->hnext = anptr->hnext;
else
watchTable[hashv] = anptr->hnext;
safe_free(anptr);
}
}
lp = np; /* Save last pointer processed */
np = np->next; /* Jump to the next pointer */
free_link(lp); /* Free the previous */
}
client->local->watches = 0;
return 0;
}
/* Throttling - originally by Stskeeps */
/* Note that we call this set::anti-flood::connect-flood nowadays */

View file

@ -544,8 +544,6 @@ static void exit_one_client(Client *client, MessageTag *mtags_i, const char *com
}
if (*client->name)
del_from_client_hash_table(client->name, client);
if (IsUser(client))
hash_check_watch(client, RPL_LOGOFF);
if (remote_rehash_client == client)
remote_rehash_client = NULL; /* client did a /REHASH and QUIT before rehash was complete */
remove_client_from_list(client);
@ -630,8 +628,6 @@ void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, char
{
RunHook3(HOOKTYPE_LOCAL_QUIT, client, recv_mtags, comment);
sendto_connectnotice(client, 1, comment);
/* Clean out list and watch structures -Donwulff */
hash_del_watch_list(client);
on_for = TStime() - client->local->firsttime;
if (IsHidden(client))
ircd_log(LOG_CLIENT, "Disconnect - (%lld:%lld:%lld) %s!%s@%s [%s] [vhost: %s] (%s)",

View file

@ -73,10 +73,9 @@ CMD_FUNC(cmd_away)
new_message(client, recv_mtags, &mtags);
sendto_server(client, 0, 0, mtags, ":%s AWAY", client->name);
hash_check_watch(client, RPL_NOTAWAY);
sendto_local_common_channels(client, client, ClientCapabilityBit("away-notify"), mtags,
":%s AWAY", client->name);
RunHook3(HOOKTYPE_AWAY, client, mtags, NULL);
RunHook4(HOOKTYPE_AWAY, client, mtags, NULL, 0);
free_message_tags(mtags);
}
@ -125,13 +124,11 @@ CMD_FUNC(cmd_away)
if (MyConnect(client))
sendnumeric(client, RPL_NOWAWAY);
hash_check_watch(client, already_as_away ? RPL_REAWAY : RPL_GONEAWAY);
sendto_local_common_channels(client, client,
ClientCapabilityBit("away-notify"), mtags,
":%s AWAY :%s", client->name, client->user->away);
RunHook3(HOOKTYPE_AWAY, client, mtags, client->user->away);
RunHook4(HOOKTYPE_AWAY, client, mtags, client->user->away, already_as_away);
free_message_tags(mtags);

View file

@ -227,12 +227,11 @@ CMD_FUNC(cmd_nick_remote)
/* Finally set new nick name. */
del_from_client_hash_table(client->name, client);
hash_check_watch(client, RPL_LOGOFF);
strcpy(client->name, nick);
add_to_client_hash_table(nick, client);
hash_check_watch(client, RPL_LOGON);
RunHook2(HOOKTYPE_POST_REMOTE_NICKCHANGE, client, mtags);
}
CMD_FUNC(cmd_nick_local)
@ -243,7 +242,8 @@ CMD_FUNC(cmd_nick_local)
char nick[NICKLEN + 2], descbuf[BUFSIZE];
Membership *mp;
long lastnick = 0l;
int differ = 1, update_watch = 1;
int differ = 1;
int newuser = 0;
unsigned char removemoder = (client->umodes & UMODE_REGNICK) ? 1 : 0;
Hook *h;
int i = 0;
@ -353,6 +353,8 @@ CMD_FUNC(cmd_nick_local)
/* New local client? */
if (!client->name[0])
{
newuser = 1;
if (iConf.ping_cookie)
{
/*
@ -389,7 +391,6 @@ CMD_FUNC(cmd_nick_local)
*/
} else {
/* New user! */
update_watch = 0; /* already done in register_user() */
strlcpy(nick, client->name, sizeof(nick)); /* don't ask, but I need this. do not remove! -- Syzop */
}
}
@ -456,8 +457,6 @@ CMD_FUNC(cmd_nick_local)
}
del_from_client_hash_table(client->name, client);
if (update_watch && IsUser(client))
hash_check_watch(client, RPL_LOGOFF);
strlcpy(client->name, nick, sizeof(client->name));
add_to_client_hash_table(nick, client);
@ -466,11 +465,11 @@ CMD_FUNC(cmd_nick_local)
snprintf(descbuf, sizeof(descbuf), "Client: %s", nick);
fd_desc(client->local->fd, descbuf);
if (update_watch && IsUser(client))
hash_check_watch(client, RPL_LOGON);
if (removemoder && MyUser(client))
sendto_one(client, NULL, ":%s MODE %s :-r", me.name, client->name);
if (MyUser(client) && !newuser)
RunHook2(HOOKTYPE_POST_LOCAL_NICKCHANGE, client, recv_mtags);
}
/*
@ -1106,7 +1105,6 @@ int _register_user(Client *client, char *nick, char *username, char *umode, char
safe_strdup(client->user->virthost, virthost);
}
hash_check_watch(client, RPL_LOGON); /* Uglier hack */
build_umode_string(client, 0, SEND_UMODES|UMODE_SERVNOTICE, buf);
sendto_serv_butone_nickcmd(client->direction, client, (*buf == '\0' ? "+" : buf));

View file

@ -103,7 +103,6 @@ CMD_FUNC(cmd_svsnick)
add_history(acptr, 1);
del_from_client_hash_table(acptr->name, acptr);
hash_check_watch(acptr, RPL_LOGOFF);
sendto_snomask(SNO_NICKCHANGE,
"*** %s (%s@%s) has been forced to change their nickname to %s",
@ -111,5 +110,5 @@ CMD_FUNC(cmd_svsnick)
strlcpy(acptr->name, parv[2], sizeof acptr->name);
add_to_client_hash_table(parv[2], acptr);
hash_check_watch(acptr, RPL_LOGON);
RunHook2(HOOKTYPE_POST_LOCAL_NICKCHANGE, acptr, mtags);
}

View file

@ -22,9 +22,34 @@
#include "unrealircd.h"
CMD_FUNC(cmd_watch);
#define MSG_WATCH "WATCH"
#define WATCH_HASH_TABLE_SIZE 32768
#define MSG_WATCH "WATCH"
#define WATCHES(client) (moddata_local_client(client, watchCounterMD).i)
#define WATCH(client) (moddata_local_client(client, watchListMD).ptr)
ModDataInfo *watchCounterMD;
ModDataInfo *watchListMD;
static Watch *watchTable[WATCH_HASH_TABLE_SIZE];
static int watch_initialized = 0;
static char siphashkey_watch[SIPHASH_KEY_LENGTH];
CMD_FUNC(cmd_watch);
void dummy_free(ModData *md);
void watch_free(ModData *md);
int watch_user_quit(Client *client, MessageTag *mtags, char *comment);
int watch_away(Client *client, MessageTag *mtags, char *reason, int already_as_away);
int watch_nickchange(Client *client, MessageTag *mtags, char *newnick);
int watch_post_nickchange(Client *client, MessageTag *mtags);
int watch_user_connect(Client *client);
int add_to_watch_hash_table(char *nick, Client *client, int awaynotify);
int hash_check_watch(Client *client, int reply);
Watch *hash_get_watch(char *nick);
int del_from_watch_hash_table(char *nick, Client *client);
int hash_del_watch_list(Client *client);
uint64_t hash_watch_nick_name(const char *name);
void count_watch_memory(int *count, u_long *memory);
ModuleHeader MOD_HEADER
= {
@ -36,9 +61,51 @@ ModuleHeader MOD_HEADER
};
MOD_INIT()
{
CommandAdd(modinfo->handle, MSG_WATCH, cmd_watch, 1, CMD_USER);
{
ModDataInfo mreq;
MARK_AS_OFFICIAL_MODULE(modinfo);
if (!watch_initialized)
{
memset(watchTable, 0, sizeof(watchTable));
siphash_generate_key(siphashkey_watch);
watch_initialized = 1;
}
CommandAdd(modinfo->handle, MSG_WATCH, cmd_watch, 1, CMD_USER);
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, watch_user_quit);
HookAdd(modinfo->handle, HOOKTYPE_REMOTE_QUIT, 0, watch_user_quit);
HookAdd(modinfo->handle, HOOKTYPE_AWAY, 0, watch_away);
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_NICKCHANGE, 0, watch_nickchange);
HookAdd(modinfo->handle, HOOKTYPE_REMOTE_NICKCHANGE, 0, watch_nickchange);
HookAdd(modinfo->handle, HOOKTYPE_POST_LOCAL_NICKCHANGE, 0, watch_post_nickchange);
HookAdd(modinfo->handle, HOOKTYPE_POST_REMOTE_NICKCHANGE, 0, watch_post_nickchange);
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, watch_user_connect);
HookAdd(modinfo->handle, HOOKTYPE_REMOTE_CONNECT, 0, watch_user_connect);
memset(&mreq, 0 , sizeof(mreq));
mreq.type = MODDATATYPE_LOCAL_CLIENT;
mreq.name = "watchCount",
mreq.free = dummy_free;
watchCounterMD = ModDataAdd(modinfo->handle, mreq);
if (!watchCounterMD)
{
config_error("[%s] Failed to request user watchCount moddata: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle));
return MOD_FAILED;
}
memset(&mreq, 0 , sizeof(mreq));
mreq.type = MODDATATYPE_LOCAL_CLIENT;
mreq.name = "watchList",
mreq.free = watch_free;
watchListMD = ModDataAdd(modinfo->handle, mreq);
if (!watchListMD)
{
config_error("[%s] Failed to request user watchList moddata: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle));
return MOD_FAILED;
}
return MOD_SUCCESS;
}
@ -52,6 +119,14 @@ MOD_UNLOAD()
return MOD_SUCCESS;
}
void dummy_free(ModData *md)
{
}
void watch_free(ModData *md)
{
}
/*
* RPL_NOWON - Online at the moment (Successfully added to WATCH-list)
* RPL_NOWOFF - Offline at the moement (Successfully added to WATCH-list)
@ -127,7 +202,7 @@ CMD_FUNC(cmd_watch)
continue;
if (do_nick_name(s + 1))
{
if (client->local->watches >= MAXWATCH)
if (WATCHES(client) >= MAXWATCH)
{
sendnumeric(client, ERR_TOOMANYWATCH, s + 1);
continue;
@ -187,13 +262,13 @@ CMD_FUNC(cmd_watch)
for (lp = anptr->watch, count = 1;
(lp = lp->next); count++)
;
sendnumeric(client, RPL_WATCHSTAT, client->local->watches, count);
sendnumeric(client, RPL_WATCHSTAT, WATCHES(client), count);
/*
* Send a list of everybody in their WATCH list. Be careful
* not to buffer overflow.
*/
if ((lp = client->local->watch) == NULL)
if ((lp = WATCH(client)) == NULL)
{
sendnumeric(client, RPL_ENDOFWATCHLIST, *s);
continue;
@ -229,7 +304,7 @@ CMD_FUNC(cmd_watch)
*/
if ((*s == 'L' || *s == 'l') && !did_l)
{
Link *lp = client->local->watch;
Link *lp = WATCH(client);
did_l = 1;
@ -264,3 +339,349 @@ CMD_FUNC(cmd_watch)
*/
}
}
int watch_user_quit(Client *client, MessageTag *mtags, char *comment)
{
if (IsUser(client))
hash_check_watch(client, RPL_LOGOFF);
if (MyConnect(client))
/* Clean out list and watch structures -Donwulff */
hash_del_watch_list(client);
return 0;
}
int watch_away(Client *client, MessageTag *mtags, char *reason, int already_as_away)
{
if (reason)
hash_check_watch(client, already_as_away ? RPL_REAWAY : RPL_GONEAWAY);
else
hash_check_watch(client, RPL_NOTAWAY);
return 0;
}
int watch_nickchange(Client *client, MessageTag *mtags, char *newnick)
{
hash_check_watch(client, RPL_LOGOFF);
return 0;
}
int watch_post_nickchange(Client *client, MessageTag *mtags)
{
hash_check_watch(client, RPL_LOGON);
return 0;
}
int watch_user_connect(Client *client)
{
hash_check_watch(client, RPL_LOGON);
return 0;
}
/*
* add_to_watch_hash_table
*/
int add_to_watch_hash_table(char *nick, Client *client, int awaynotify)
{
unsigned int hashv;
Watch *anptr;
Link *lp;
/* Get the right bucket... */
hashv = hash_watch_nick_name(nick);
/* Find the right nick (header) in the bucket, or NULL... */
if ((anptr = (Watch *)watchTable[hashv]))
while (anptr && mycmp(anptr->nick, nick))
anptr = anptr->hnext;
/* If found NULL (no header for this nick), make one... */
if (!anptr) {
anptr = (Watch *)safe_alloc(sizeof(Watch)+strlen(nick));
anptr->lasttime = timeofday;
strcpy(anptr->nick, nick);
anptr->watch = NULL;
anptr->hnext = watchTable[hashv];
watchTable[hashv] = anptr;
}
/* Is this client already on the watch-list? */
if ((lp = anptr->watch))
while (lp && (lp->value.client != client))
lp = lp->next;
/* No it isn't, so add it in the bucket and client addint it */
if (!lp) {
lp = anptr->watch;
anptr->watch = make_link();
anptr->watch->value.client = client;
anptr->watch->flags = awaynotify;
anptr->watch->next = lp;
lp = make_link();
lp->next = WATCH(client);
lp->value.wptr = anptr;
lp->flags = awaynotify;
WATCH(client) = lp;
WATCHES(client)++;
}
return 0;
}
/*
* hash_check_watch
*/
int hash_check_watch(Client *client, int reply)
{
unsigned int hashv;
Watch *anptr;
Link *lp;
int awaynotify = 0;
if ((reply == RPL_GONEAWAY) || (reply == RPL_NOTAWAY) || (reply == RPL_REAWAY))
awaynotify = 1;
/* Get us the right bucket */
hashv = hash_watch_nick_name(client->name);
/* Find the right header in this bucket */
if ((anptr = (Watch *)watchTable[hashv]))
while (anptr && mycmp(anptr->nick, client->name))
anptr = anptr->hnext;
if (!anptr)
return 0; /* This nick isn't on watch */
/* Update the time of last change to item */
anptr->lasttime = TStime();
/* Send notifies out to everybody on the list in header */
for (lp = anptr->watch; lp; lp = lp->next)
{
if (!awaynotify)
{
sendnumeric(lp->value.client, reply,
client->name,
(IsUser(client) ? client->user->username : "<N/A>"),
(IsUser(client) ?
(IsHidden(client) ? client->user->virthost : client->
user->realhost) : "<N/A>"), anptr->lasttime, client->info);
}
else
{
/* AWAY or UNAWAY */
if (!lp->flags)
continue; /* skip away/unaway notification for users not interested in them */
if (reply == RPL_NOTAWAY)
sendnumeric(lp->value.client, reply,
client->name,
(IsUser(client) ? client->user->username : "<N/A>"),
(IsUser(client) ?
(IsHidden(client) ? client->user->virthost : client->
user->realhost) : "<N/A>"), client->user->lastaway);
else /* RPL_GONEAWAY / RPL_REAWAY */
sendnumeric(lp->value.client, reply,
client->name,
(IsUser(client) ? client->user->username : "<N/A>"),
(IsUser(client) ?
(IsHidden(client) ? client->user->virthost : client->
user->realhost) : "<N/A>"), client->user->lastaway, client->user->away);
}
}
return 0;
}
/*
* hash_get_watch
*/
Watch *hash_get_watch(char *nick)
{
unsigned int hashv;
Watch *anptr;
hashv = hash_watch_nick_name(nick);
if ((anptr = (Watch *)watchTable[hashv]))
while (anptr && mycmp(anptr->nick, nick))
anptr = anptr->hnext;
return anptr;
}
/*
* del_from_watch_hash_table
*/
int del_from_watch_hash_table(char *nick, Client *client)
{
unsigned int hashv;
Watch *anptr, *nlast = NULL;
Link *lp, *last = NULL;
/* Get the bucket for this nick... */
hashv = hash_watch_nick_name(nick);
/* Find the right header, maintaining last-link pointer... */
if ((anptr = (Watch *)watchTable[hashv]))
while (anptr && mycmp(anptr->nick, nick)) {
nlast = anptr;
anptr = anptr->hnext;
}
if (!anptr)
return 0; /* No such watch */
/* Find this client from the list of notifies... with last-ptr. */
if ((lp = anptr->watch))
while (lp && (lp->value.client != client)) {
last = lp;
lp = lp->next;
}
if (!lp)
return 0; /* No such client to watch */
/* Fix the linked list under header, then remove the watch entry */
if (!last)
anptr->watch = lp->next;
else
last->next = lp->next;
free_link(lp);
/* Do the same regarding the links in client-record... */
last = NULL;
if ((lp = WATCH(client)))
while (lp && (lp->value.wptr != anptr)) {
last = lp;
lp = lp->next;
}
/*
* Give error on the odd case... probobly not even neccessary
* No error checking in ircd is unneccessary ;) -Cabal95
*/
if (!lp)
sendto_ops("WATCH debug error: del_from_watch_hash_table "
"found a watch entry with no client "
"counterpoint processing nick %s on client %p!",
nick, client->user);
else {
if (!last) /* First one matched */
WATCH(client) = lp->next;
else
last->next = lp->next;
free_link(lp);
}
/* In case this header is now empty of notices, remove it */
if (!anptr->watch) {
if (!nlast)
watchTable[hashv] = anptr->hnext;
else
nlast->hnext = anptr->hnext;
safe_free(anptr);
}
/* Update count of notifies on nick */
WATCHES(client)--;
return 0;
}
/*
* hash_del_watch_list
*/
int hash_del_watch_list(Client *client)
{
unsigned int hashv;
Watch *anptr;
Link *np, *lp, *last;
if (!(np = WATCH(client)))
return 0; /* Nothing to do */
WATCH(client) = NULL; /* Break the watch-list for client */
while (np) {
/* Find the watch-record from hash-table... */
anptr = np->value.wptr;
last = NULL;
for (lp = anptr->watch; lp && (lp->value.client != client);
lp = lp->next)
last = lp;
/* Not found, another "worst case" debug error */
if (!lp)
sendto_ops("WATCH Debug error: hash_del_watch_list "
"found a WATCH entry with no table "
"counterpoint processing client %s!",
client->name);
else {
/* Fix the watch-list and remove entry */
if (!last)
anptr->watch = lp->next;
else
last->next = lp->next;
free_link(lp);
/*
* If this leaves a header without notifies,
* remove it. Need to find the last-pointer!
*/
if (!anptr->watch) {
Watch *np2, *nl;
hashv = hash_watch_nick_name(anptr->nick);
nl = NULL;
np2 = watchTable[hashv];
while (np2 != anptr) {
nl = np2;
np2 = np2->hnext;
}
if (nl)
nl->hnext = anptr->hnext;
else
watchTable[hashv] = anptr->hnext;
safe_free(anptr);
}
}
lp = np; /* Save last pointer processed */
np = np->next; /* Jump to the next pointer */
free_link(lp); /* Free the previous */
}
WATCHES(client) = 0;
return 0;
}
uint64_t hash_watch_nick_name(const char *name)
{
return siphash_nocase(name, siphashkey_watch) % WATCH_HASH_TABLE_SIZE;
}
void count_watch_memory(int *count, u_long *memory)
{
int i = WATCH_HASH_TABLE_SIZE;
Watch *anptr;
while (i--)
{
anptr = watchTable[i];
while (anptr)
{
(*count)++;
(*memory) += sizeof(Watch)+strlen(anptr->nick);
anptr = anptr->hnext;
}
}
}