mirror of git://git.acid.vegas/unrealircd.git
393 lines
9.5 KiB
C
393 lines
9.5 KiB
C
/*
|
|
* IRC - Internet Relay Chat, src/modules/watch-backend.c
|
|
* (C) 2021 The UnrealIRCd Team
|
|
*
|
|
* See file AUTHORS in IRC package for additional names of
|
|
* the programmers.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 1, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "unrealircd.h"
|
|
|
|
#define WATCH_HASH_TABLE_SIZE 32768
|
|
|
|
#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 = NULL;
|
|
static char *siphashkey_watch = NULL;
|
|
|
|
void dummy_free(ModData *md);
|
|
void watch_free(ModData *md);
|
|
|
|
int watch_backend_user_quit(Client *client, MessageTag *mtags, const char *comment);
|
|
int _watch_add(char *nick, Client *client, int flags);
|
|
int _watch_check(Client *client, int event, int (*watch_notify)(Client *client, Watch *watch, Link *lp, int event));
|
|
Watch *_watch_get(char *nick);
|
|
int _watch_del(char *nick, Client *client, int flags);
|
|
int _watch_del_list(Client *client, int flags);
|
|
uint64_t hash_watch_nick_name(const char *name);
|
|
|
|
ModuleHeader MOD_HEADER
|
|
= {
|
|
"watch-backend",
|
|
"6.0.3",
|
|
"backend for /WATCH",
|
|
"UnrealIRCd Team",
|
|
"unrealircd-6",
|
|
};
|
|
|
|
MOD_TEST()
|
|
{
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
|
|
EfunctionAdd(modinfo->handle, EFUNC_WATCH_ADD, _watch_add);
|
|
EfunctionAdd(modinfo->handle, EFUNC_WATCH_DEL, _watch_del);
|
|
EfunctionAdd(modinfo->handle, EFUNC_WATCH_DEL_LIST, _watch_del_list);
|
|
EfunctionAddPVoid(modinfo->handle, EFUNC_WATCH_GET, TO_PVOIDFUNC(_watch_get));
|
|
EfunctionAdd(modinfo->handle, EFUNC_WATCH_CHECK, _watch_check);
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
void watch_generic_free(ModData *m)
|
|
{
|
|
safe_free(m->ptr);
|
|
}
|
|
|
|
MOD_INIT()
|
|
{
|
|
ModDataInfo mreq;
|
|
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
ModuleSetOptions(modinfo->handle, MOD_OPT_PERM_RELOADABLE, 1); /* or do a complex memory freeing algorithm instead */
|
|
|
|
LoadPersistentPointer(modinfo, siphashkey_watch, watch_generic_free);
|
|
if (siphashkey_watch == NULL)
|
|
{
|
|
siphashkey_watch = safe_alloc(SIPHASH_KEY_LENGTH);
|
|
siphash_generate_key(siphashkey_watch);
|
|
}
|
|
LoadPersistentPointer(modinfo, watchTable, watch_generic_free);
|
|
if (watchTable == NULL)
|
|
watchTable = safe_alloc(sizeof(Watch *) * WATCH_HASH_TABLE_SIZE);
|
|
|
|
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;
|
|
}
|
|
|
|
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, watch_backend_user_quit);
|
|
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_LOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_UNLOAD()
|
|
{
|
|
SavePersistentPointer(modinfo, siphashkey_watch);
|
|
SavePersistentPointer(modinfo, watchTable);
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
void dummy_free(ModData *md)
|
|
{
|
|
}
|
|
|
|
void watch_free(ModData *md)
|
|
{
|
|
/* it should have been never requested to free as the module is PERM */
|
|
if (md)
|
|
{
|
|
unreal_log(ULOG_ERROR, "watch-backend", "BUG_WATCH_FREE_MEMORY_LEAK", NULL,
|
|
"[BUG] watchList moddata was not freed -- memory leak!");
|
|
}
|
|
}
|
|
|
|
int watch_backend_user_quit(Client *client, MessageTag *mtags, const char *comment)
|
|
{
|
|
/* Clean out list and watch structures -Donwulff */
|
|
watch_del_list(client, 0);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* _watch_add
|
|
*/
|
|
int _watch_add(char *nick, Client *client, int flags)
|
|
{
|
|
unsigned int hashv;
|
|
Watch *watch;
|
|
Link *lp;
|
|
|
|
|
|
/* Get the right bucket... */
|
|
hashv = hash_watch_nick_name(nick);
|
|
|
|
/* Find the right nick (header) in the bucket, or NULL... */
|
|
if ((watch = watchTable[hashv]))
|
|
while (watch && mycmp(watch->nick, nick))
|
|
watch = watch->hnext;
|
|
|
|
/* If found NULL (no header for this nick), make one... */
|
|
if (!watch) {
|
|
watch = safe_alloc(sizeof(Watch)+strlen(nick));
|
|
watch->lasttime = timeofday;
|
|
strcpy(watch->nick, nick);
|
|
|
|
watch->watch = NULL;
|
|
|
|
watch->hnext = watchTable[hashv];
|
|
watchTable[hashv] = watch;
|
|
}
|
|
/* Is this client already on the watch-list? */
|
|
if ((lp = watch->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 = watch->watch;
|
|
watch->watch = make_link();
|
|
watch->watch->value.client = client;
|
|
watch->watch->flags = flags;
|
|
watch->watch->next = lp;
|
|
|
|
lp = make_link();
|
|
lp->next = WATCH(client);
|
|
lp->value.wptr = watch;
|
|
lp->flags = flags;
|
|
WATCH(client) = lp;
|
|
WATCHES(client)++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* _watch_check
|
|
*/
|
|
int _watch_check(Client *client, int event, int (*watch_notify)(Client *client, Watch *watch, Link *lp, int event))
|
|
{
|
|
unsigned int hashv;
|
|
Watch *watch;
|
|
Link *lp;
|
|
|
|
/* Get us the right bucket */
|
|
hashv = hash_watch_nick_name(client->name);
|
|
|
|
/* Find the right header in this bucket */
|
|
if ((watch = watchTable[hashv]))
|
|
while (watch && mycmp(watch->nick, client->name))
|
|
watch = watch->hnext;
|
|
if (!watch)
|
|
return 0; /* This nick isn't on watch */
|
|
|
|
/* Update the time of last change to item */
|
|
watch->lasttime = TStime();
|
|
|
|
/* Send notifies out to everybody on the list in header */
|
|
for (lp = watch->watch; lp; lp = lp->next)
|
|
{
|
|
watch_notify(client, watch, lp, event);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* _watch_get
|
|
*/
|
|
Watch *_watch_get(char *nick)
|
|
{
|
|
unsigned int hashv;
|
|
Watch *watch;
|
|
|
|
hashv = hash_watch_nick_name(nick);
|
|
|
|
if ((watch = watchTable[hashv]))
|
|
while (watch && mycmp(watch->nick, nick))
|
|
watch = watch->hnext;
|
|
|
|
return watch;
|
|
}
|
|
|
|
/*
|
|
* _watch_del
|
|
*/
|
|
int _watch_del(char *nick, Client *client, int flags)
|
|
{
|
|
unsigned int hashv;
|
|
Watch **watch, *wprev;
|
|
Link **lp, *prev;
|
|
|
|
/* Get the bucket for this nick... */
|
|
hashv = hash_watch_nick_name(nick);
|
|
|
|
/* Find the right header, maintaining last-link pointer... */
|
|
watch = (Watch **)&watchTable[hashv];
|
|
while (*watch && mycmp((*watch)->nick, nick))
|
|
watch = &(*watch)->hnext;
|
|
if (!*watch)
|
|
return 0; /* No such watch */
|
|
|
|
/* Find this client from the list of notifies... with last-ptr. */
|
|
lp = &(*watch)->watch;
|
|
while (*lp)
|
|
{
|
|
if ((*lp)->value.client == client && ((*lp)->flags & flags) == flags)
|
|
break;
|
|
lp = &(*lp)->next;
|
|
}
|
|
if (!*lp)
|
|
return 0; /* No such client to watch */
|
|
|
|
/* Fix the linked list under header, then remove the watch entry */
|
|
prev = *lp;
|
|
*lp = prev->next;
|
|
free_link(prev);
|
|
|
|
/* Do the same regarding the links in client-record... */
|
|
lp = (Link **)&WATCH(client);
|
|
while (*lp && ((*lp)->value.wptr != *watch))
|
|
lp = &(*lp)->next;
|
|
|
|
/*
|
|
* Give error on the odd case... probobly not even neccessary
|
|
* No error checking in ircd is unneccessary ;) -Cabal95
|
|
*/
|
|
if (!*lp)
|
|
{
|
|
unreal_log(ULOG_WARNING, "watch", "BUG_WATCH_DEL", client,
|
|
"[BUG] watch_del found a watch entry with no client counterpoint, "
|
|
"while processing nick $nick on client $client.details",
|
|
log_data_string("nick", nick));
|
|
} else {
|
|
prev = *lp;
|
|
*lp = prev->next;
|
|
free_link(prev);
|
|
}
|
|
/* In case this header is now empty of notices, remove it */
|
|
if (!(*watch)->watch) {
|
|
wprev = *watch;
|
|
*watch = wprev->hnext;
|
|
safe_free(wprev);
|
|
}
|
|
|
|
/* Update count of notifies on nick */
|
|
WATCHES(client)--;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* _watch_del_list
|
|
*/
|
|
int _watch_del_list(Client *client, int flags)
|
|
{
|
|
unsigned int hashv;
|
|
Watch *watch;
|
|
Link **np, **lp, *prev;
|
|
|
|
np = (Link **)&WATCH(client);
|
|
|
|
while (*np) {
|
|
if (((*np)->flags & flags) != flags)
|
|
{
|
|
/* this entry is not fitting requested flags */
|
|
np = &(*np)->next;
|
|
continue;
|
|
}
|
|
|
|
WATCHES(client)--;
|
|
|
|
/* Find the watch-record from hash-table... */
|
|
watch = (*np)->value.wptr;
|
|
lp = &(watch->watch);
|
|
while (*lp && ((*lp)->value.client != client))
|
|
lp = &(*lp)->next;
|
|
|
|
/* Not found, another "worst case" debug error */
|
|
if (!*lp)
|
|
{
|
|
unreal_log(ULOG_WARNING, "watch", "BUG_WATCH_DEL_LIST", client,
|
|
"[BUG] watch_del_list found a watch entry with no table counterpoint, "
|
|
"while processing client $client.details");
|
|
} else {
|
|
/* Fix the watch-list and remove entry */
|
|
Link *prev = *lp;
|
|
*lp = prev->next;
|
|
free_link(prev);
|
|
|
|
/*
|
|
* If this leaves a header without notifies,
|
|
* remove it. Need to find the last-pointer!
|
|
*/
|
|
if (!watch->watch) {
|
|
Watch **np2, *wprev;
|
|
|
|
hashv = hash_watch_nick_name(watch->nick);
|
|
|
|
np2 = &watchTable[hashv];
|
|
while (*np2 && *np2 != watch)
|
|
np2 = &(*np2)->hnext;
|
|
|
|
*np2 = watch->hnext;
|
|
|
|
safe_free(watch);
|
|
}
|
|
}
|
|
|
|
prev = *np; /* Save last pointer processed */
|
|
*np = prev->next; /* Jump to the next pointer */
|
|
free_link(prev); /* Free the previous */
|
|
}
|
|
|
|
if (!flags)
|
|
WATCHES(client) = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint64_t hash_watch_nick_name(const char *name)
|
|
{
|
|
return siphash_nocase(name, siphashkey_watch) % WATCH_HASH_TABLE_SIZE;
|
|
}
|
|
|