unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

watch-backend.c (9771B)

      1 /*
      2  *   IRC - Internet Relay Chat, src/modules/watch-backend.c
      3  *   (C) 2021 The UnrealIRCd Team
      4  *
      5  *   See file AUTHORS in IRC package for additional names of
      6  *   the programmers.
      7  *
      8  *   This program is free software; you can redistribute it and/or modify
      9  *   it under the terms of the GNU General Public License as published by
     10  *   the Free Software Foundation; either version 1, or (at your option)
     11  *   any later version.
     12  *
     13  *   This program is distributed in the hope that it will be useful,
     14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  *   GNU General Public License for more details.
     17  *
     18  *   You should have received a copy of the GNU General Public License
     19  *   along with this program; if not, write to the Free Software
     20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     21  */
     22 
     23 #include "unrealircd.h"
     24 
     25 #define WATCH_HASH_TABLE_SIZE 32768
     26 
     27 #define WATCHES(client) (moddata_local_client(client, watchCounterMD).i)
     28 #define WATCH(client) (moddata_local_client(client, watchListMD).ptr)
     29 
     30 ModDataInfo *watchCounterMD;
     31 ModDataInfo *watchListMD;
     32 
     33 static Watch **watchTable = NULL;
     34 static char *siphashkey_watch = NULL;
     35 
     36 void dummy_free(ModData *md);
     37 void watch_free(ModData *md);
     38 
     39 int watch_backend_user_quit(Client *client, MessageTag *mtags, const char *comment);
     40 int _watch_add(char *nick, Client *client, int flags);
     41 int _watch_check(Client *client, int event, int (*watch_notify)(Client *client, Watch *watch, Link *lp, int event));
     42 Watch *_watch_get(char *nick);
     43 int _watch_del(char *nick, Client *client, int flags);
     44 int _watch_del_list(Client *client, int flags);
     45 uint64_t hash_watch_nick_name(const char *name);
     46 
     47 ModuleHeader MOD_HEADER
     48 = {
     49 	"watch-backend",
     50 	"6.0.3",
     51 	"backend for /WATCH",
     52 	"UnrealIRCd Team",
     53 	"unrealircd-6",
     54 };
     55 
     56 MOD_TEST()
     57 {
     58 	MARK_AS_OFFICIAL_MODULE(modinfo);
     59 
     60 	EfunctionAdd(modinfo->handle, EFUNC_WATCH_ADD, _watch_add);
     61 	EfunctionAdd(modinfo->handle, EFUNC_WATCH_DEL, _watch_del);
     62 	EfunctionAdd(modinfo->handle, EFUNC_WATCH_DEL_LIST, _watch_del_list);
     63 	EfunctionAddPVoid(modinfo->handle, EFUNC_WATCH_GET, TO_PVOIDFUNC(_watch_get));
     64 	EfunctionAdd(modinfo->handle, EFUNC_WATCH_CHECK, _watch_check);
     65 	return MOD_SUCCESS;
     66 }
     67 
     68 void watch_generic_free(ModData *m)
     69 {
     70 	safe_free(m->ptr);
     71 }
     72 
     73 MOD_INIT()
     74 {	
     75 	ModDataInfo mreq;
     76 
     77 	MARK_AS_OFFICIAL_MODULE(modinfo);
     78 	ModuleSetOptions(modinfo->handle, MOD_OPT_PERM_RELOADABLE, 1); /* or do a complex memory freeing algorithm instead */
     79 
     80 	LoadPersistentPointer(modinfo, siphashkey_watch, watch_generic_free);
     81 	if (siphashkey_watch == NULL)
     82 	{
     83 		siphashkey_watch = safe_alloc(SIPHASH_KEY_LENGTH);
     84 		siphash_generate_key(siphashkey_watch);
     85 	}
     86 	LoadPersistentPointer(modinfo, watchTable, watch_generic_free);
     87 	if (watchTable == NULL)
     88 		watchTable = safe_alloc(sizeof(Watch *) * WATCH_HASH_TABLE_SIZE);
     89 
     90 	memset(&mreq, 0 , sizeof(mreq));
     91 	mreq.type = MODDATATYPE_LOCAL_CLIENT;
     92 	mreq.name = "watchCount",
     93 	mreq.free = dummy_free;
     94 	watchCounterMD = ModDataAdd(modinfo->handle, mreq);
     95 	if (!watchCounterMD)
     96 	{
     97 		config_error("[%s] Failed to request user watchCount moddata: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle));
     98 		return MOD_FAILED;
     99 	}
    100 
    101 	memset(&mreq, 0 , sizeof(mreq));
    102 	mreq.type = MODDATATYPE_LOCAL_CLIENT;
    103 	mreq.name = "watchList",
    104 	mreq.free = watch_free;
    105 	watchListMD = ModDataAdd(modinfo->handle, mreq);
    106 	if (!watchListMD)
    107 	{
    108 		config_error("[%s] Failed to request user watchList moddata: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle));
    109 		return MOD_FAILED;
    110 	}
    111 
    112 	HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, watch_backend_user_quit);
    113 
    114 	return MOD_SUCCESS;
    115 }
    116 
    117 MOD_LOAD()
    118 {
    119 	return MOD_SUCCESS;
    120 }
    121 
    122 MOD_UNLOAD()
    123 {
    124 	SavePersistentPointer(modinfo, siphashkey_watch);
    125 	SavePersistentPointer(modinfo, watchTable);
    126 	return MOD_SUCCESS;
    127 }
    128 
    129 void dummy_free(ModData *md)
    130 {
    131 }
    132 
    133 void watch_free(ModData *md)
    134 {
    135 	/* it should have been never requested to free as the module is PERM */
    136 	if (md)
    137 	{
    138 		unreal_log(ULOG_ERROR, "watch-backend", "BUG_WATCH_FREE_MEMORY_LEAK", NULL,
    139 		           "[BUG] watchList moddata was not freed -- memory leak!");
    140 	}
    141 }
    142 
    143 int watch_backend_user_quit(Client *client, MessageTag *mtags, const char *comment)
    144 {
    145 	/* Clean out list and watch structures -Donwulff */
    146 	watch_del_list(client, 0);
    147 	return 0;
    148 }
    149 
    150 /*
    151  * _watch_add
    152  */
    153 int _watch_add(char *nick, Client *client, int flags)
    154 {
    155 	unsigned int hashv;
    156 	Watch *watch;
    157 	Link *lp;
    158 	
    159 	
    160 	/* Get the right bucket... */
    161 	hashv = hash_watch_nick_name(nick);
    162 	
    163 	/* Find the right nick (header) in the bucket, or NULL... */
    164 	if ((watch = watchTable[hashv]))
    165 		while (watch && mycmp(watch->nick, nick))
    166 		 watch = watch->hnext;
    167 	
    168 	/* If found NULL (no header for this nick), make one... */
    169 	if (!watch) {
    170 		watch = safe_alloc(sizeof(Watch)+strlen(nick));
    171 		watch->lasttime = timeofday;
    172 		strcpy(watch->nick, nick);
    173 		
    174 		watch->watch = NULL;
    175 		
    176 		watch->hnext = watchTable[hashv];
    177 		watchTable[hashv] = watch;
    178 	}
    179 	/* Is this client already on the watch-list? */
    180 	if ((lp = watch->watch))
    181 		while (lp && (lp->value.client != client))
    182 		 lp = lp->next;
    183 	
    184 	/* No it isn't, so add it in the bucket and client addint it */
    185 	if (!lp) {
    186 		lp = watch->watch;
    187 		watch->watch = make_link();
    188 		watch->watch->value.client = client;
    189 		watch->watch->flags = flags;
    190 		watch->watch->next = lp;
    191 		
    192 		lp = make_link();
    193 		lp->next = WATCH(client);
    194 		lp->value.wptr = watch;
    195 		lp->flags = flags;
    196 		WATCH(client) = lp;
    197 		WATCHES(client)++;
    198 	}
    199 	
    200 	return 0;
    201 }
    202 
    203 /*
    204  *	_watch_check
    205  */
    206 int _watch_check(Client *client, int event, int (*watch_notify)(Client *client, Watch *watch, Link *lp, int event))
    207 {
    208 	unsigned int hashv;
    209 	Watch *watch;
    210 	Link *lp;
    211 
    212 	/* Get us the right bucket */
    213 	hashv = hash_watch_nick_name(client->name);
    214 	
    215 	/* Find the right header in this bucket */
    216 	if ((watch = watchTable[hashv]))
    217 		while (watch && mycmp(watch->nick, client->name))
    218 		 watch = watch->hnext;
    219 	if (!watch)
    220 		return 0;	 /* This nick isn't on watch */
    221 	
    222 	/* Update the time of last change to item */
    223 	watch->lasttime = TStime();
    224 	
    225 	/* Send notifies out to everybody on the list in header */
    226 	for (lp = watch->watch; lp; lp = lp->next)
    227 	{
    228 		watch_notify(client, watch, lp, event);
    229 	}
    230 	
    231 	return 0;
    232 }
    233 
    234 /*
    235  * _watch_get
    236  */
    237 Watch *_watch_get(char *nick)
    238 {
    239 	unsigned int hashv;
    240 	Watch *watch;
    241 	
    242 	hashv = hash_watch_nick_name(nick);
    243 	
    244 	if ((watch = watchTable[hashv]))
    245 		while (watch && mycmp(watch->nick, nick))
    246 		 watch = watch->hnext;
    247 	
    248 	return watch;
    249 }
    250 
    251 /*
    252  * _watch_del
    253  */
    254 int _watch_del(char *nick, Client *client, int flags)
    255 {
    256 	unsigned int hashv;
    257 	Watch **watch, *wprev;
    258 	Link **lp, *prev;
    259 
    260 	/* Get the bucket for this nick... */
    261 	hashv = hash_watch_nick_name(nick);
    262 	
    263 	/* Find the right header, maintaining last-link pointer... */
    264 	watch = (Watch **)&watchTable[hashv];
    265 	while (*watch && mycmp((*watch)->nick, nick))
    266 		watch = &(*watch)->hnext;
    267 	if (!*watch)
    268 		return 0;	 /* No such watch */
    269 	
    270 	/* Find this client from the list of notifies... with last-ptr. */
    271 	lp = &(*watch)->watch;
    272 	while (*lp)
    273 	{
    274 		if ((*lp)->value.client == client && ((*lp)->flags & flags) == flags)
    275 			break;
    276 		lp = &(*lp)->next;
    277 	}
    278 	if (!*lp)
    279 		return 0;	 /* No such client to watch */
    280 	
    281 	/* Fix the linked list under header, then remove the watch entry */
    282 	prev = *lp;
    283 	*lp = prev->next;
    284 	free_link(prev);
    285 	
    286 	/* Do the same regarding the links in client-record... */
    287 	lp = (Link **)&WATCH(client);
    288 	while (*lp && ((*lp)->value.wptr != *watch))
    289 		lp = &(*lp)->next;
    290 	
    291 	/*
    292 	 * Give error on the odd case... probobly not even neccessary
    293 	 * No error checking in ircd is unneccessary ;) -Cabal95
    294 	 */
    295 	if (!*lp)
    296 	{
    297 		unreal_log(ULOG_WARNING, "watch", "BUG_WATCH_DEL", client,
    298 		           "[BUG] watch_del found a watch entry with no client counterpoint, "
    299 		           "while processing nick $nick on client $client.details",
    300 		           log_data_string("nick", nick));
    301 	} else {
    302 		prev = *lp;
    303 		*lp = prev->next;
    304 		free_link(prev);
    305 	}
    306 	/* In case this header is now empty of notices, remove it */
    307 	if (!(*watch)->watch) {
    308 		wprev = *watch;
    309 		*watch = wprev->hnext;
    310 		safe_free(wprev);
    311 	}
    312 	
    313 	/* Update count of notifies on nick */
    314 	WATCHES(client)--;
    315 	
    316 	return 0;
    317 }
    318 
    319 /*
    320  * _watch_del_list
    321  */
    322 int _watch_del_list(Client *client, int flags)
    323 {
    324 	unsigned int hashv;
    325 	Watch *watch;
    326 	Link **np, **lp, *prev;
    327 	
    328 	np = (Link **)&WATCH(client);
    329 	
    330 	while (*np) {
    331 		if (((*np)->flags & flags) != flags)
    332 		{
    333 			/* this entry is not fitting requested flags */
    334 			np = &(*np)->next;
    335 			continue;
    336 		}
    337 		
    338 		WATCHES(client)--;
    339 		
    340 		/* Find the watch-record from hash-table... */
    341 		watch = (*np)->value.wptr;
    342 		lp = &(watch->watch);
    343 		while (*lp && ((*lp)->value.client != client))
    344 			lp = &(*lp)->next;
    345 
    346 		/* Not found, another "worst case" debug error */
    347 		if (!*lp)
    348 		{
    349 			unreal_log(ULOG_WARNING, "watch", "BUG_WATCH_DEL_LIST", client,
    350 				   "[BUG] watch_del_list found a watch entry with no table counterpoint, "
    351 				   "while processing client $client.details");
    352 		} else {
    353 			/* Fix the watch-list and remove entry */
    354 			Link *prev = *lp;
    355 			*lp = prev->next;
    356 			free_link(prev);
    357 			
    358 			/*
    359 			 * If this leaves a header without notifies,
    360 			 * remove it. Need to find the last-pointer!
    361 			 */
    362 			if (!watch->watch) {
    363 				Watch **np2, *wprev;
    364 				
    365 				hashv = hash_watch_nick_name(watch->nick);
    366 				
    367 				np2 = &watchTable[hashv];
    368 				while (*np2 && *np2 != watch)
    369 					np2 = &(*np2)->hnext;
    370 
    371 				*np2 = watch->hnext;
    372 
    373 				safe_free(watch);
    374 			}
    375 		}
    376 		
    377 		prev = *np; /* Save last pointer processed */
    378 		*np = prev->next; /* Jump to the next pointer */
    379 		free_link(prev); /* Free the previous */
    380 	}
    381 	
    382 	if (!flags)
    383 		WATCHES(client) = 0;
    384 	
    385 	return 0;
    386 }
    387 
    388 uint64_t hash_watch_nick_name(const char *name)
    389 {
    390 	return siphash_nocase(name, siphashkey_watch) % WATCH_HASH_TABLE_SIZE;
    391 }
    392