1 /************
2  *  hash.c  *
3  ************
4  *
5  * My hash routines for hashing NickLists, and eventually ChannelList's
6  * and WhowasList's
7  *
8  * These are not very robust, as the add/remove functions will have
9  * to be written differently for each type of struct
10  * (To DO: use C++, and create a hash "class" so I don't need to
11  * have the functions different.)
12  *
13  *
14  * Written by Scott H Kilau
15  *
16  * Copyright(c) 1997
17  *
18  * Modified by Colten Edwards for use in BitchX.
19  * Added Whowas buffer hashing.
20  *
21  * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
22  */
23 
24 #include "irc.h"
25 static char cvsrevision[] = "$Id: hash.c 52 2008-06-14 06:45:05Z keaston $";
CVS_REVISION(hash_c)26 CVS_REVISION(hash_c)
27 #include "struct.h"
28 #include "ircaux.h"
29 #include "hook.h"
30 #include "vars.h"
31 #include "output.h"
32 #include "misc.h"
33 #include "server.h"
34 #include "list.h"
35 #include "window.h"
36 
37 #include "hash.h"
38 #include "hash2.h"
39 #define MAIN_SOURCE
40 #include "modval.h"
41 
42 /*
43  * hash_nickname: for now, does a simple hash of the
44  * nick by counting up the ascii values of the lower case, and
45  * then %'ing it by NICKLIST_HASHSIZE (always a prime!)
46  */
47 unsigned long hash_nickname(char *nick, unsigned int size)
48 {
49         register u_char  *p = (u_char *) nick;
50 	unsigned long hash = 0, g;
51 	if (!nick) return -1;
52 	while (*p)
53 	{
54 		hash = (hash << 4) + ((*p >= 'A' && *p <= 'Z') ? (*p+32) : *p);
55 		if ((g = hash & 0xF0000000))
56 			hash ^= g >> 24;
57 		hash &= ~g;
58 		p++;
59 	}
60 	return (hash %= size);
61 }
62 
63 /*
64  * move_link_to_top: used by find routine, brings link
65  * to the top of the list in the specific array location
66  */
move_link_to_top(NickList * tmp,NickList * prev,HashEntry * location)67 static inline void move_link_to_top(NickList *tmp, NickList *prev, HashEntry *location)
68 {
69 	if (prev)
70 	{
71 		NickList *old_list;
72 		old_list = (NickList *) location->list;
73 		location->list = (void *) tmp;
74 		prev->next = tmp->next;
75 		tmp->next = old_list;
76 	}
77 }
78 
79 /*
80  * remove_link_from_list: used by find routine, removes link
81  * from our chain of hashed entries.
82  */
remove_link_from_list(NickList * tmp,NickList * prev,HashEntry * location)83 static inline void remove_link_from_list(NickList *tmp, NickList *prev, HashEntry *location)
84 {
85 	if (prev)
86 	{
87 		/* remove the link from the middle of the list */
88 		prev->next = tmp->next;
89 	}
90 	else {
91 		/* unlink the first link, and connect next one up */
92 		location->list = (void *) tmp->next;
93 	}
94 	/* set tmp's next to NULL, as its unlinked now */
95 	tmp->next = NULL;
96 }
97 
BX_add_name_to_genericlist(char * name,HashEntry * list,unsigned int size)98 void BX_add_name_to_genericlist(char *name, HashEntry *list, unsigned int size)
99 {
100 	List *nptr;
101 	unsigned long hvalue = hash_nickname(name, size);
102 
103 	nptr = (List *) new_malloc(sizeof(List));
104 	nptr->next = (List *) list[hvalue].list;
105 	nptr->name = m_strdup(name);
106 
107 	/* assign our new linked list into array spot */
108 	list[hvalue].list = (void *) nptr;
109 	/* quick tally of nicks in chain in this array spot */
110 	list[hvalue].links++;
111 	/* keep stats on hits to this array spot */
112 	list[hvalue].hits++;
113 }
114 
115 /*
116  * move_link_to_top: used by find routine, brings link
117  * to the top of the list in the specific array location
118  */
move_gen_link_to_top(List * tmp,List * prev,HashEntry * location)119 static inline void move_gen_link_to_top(List *tmp, List *prev, HashEntry *location)
120 {
121 	if (prev)
122 	{
123 		List *old_list;
124 		old_list = (List *) location->list;
125 		location->list = (void *) tmp;
126 		prev->next = tmp->next;
127 		tmp->next = old_list;
128 	}
129 }
130 
131 /*
132  * remove_link_from_list: used by find routine, removes link
133  * from our chain of hashed entries.
134  */
remove_gen_link_from_list(List * tmp,List * prev,HashEntry * location)135 static inline void remove_gen_link_from_list(List *tmp, List *prev, HashEntry *location)
136 {
137 	if (prev)
138 	{
139 		/* remove the link from the middle of the list */
140 		prev->next = tmp->next;
141 	}
142 	else {
143 		/* unlink the first link, and connect next one up */
144 		location->list = (void *) tmp->next;
145 	}
146 	/* set tmp's next to NULL, as its unlinked now */
147 	tmp->next = NULL;
148 }
149 
BX_find_name_in_genericlist(char * name,HashEntry * list,unsigned int size,int remove)150 List *BX_find_name_in_genericlist(char *name, HashEntry *list, unsigned int size, int remove)
151 {
152 	HashEntry *location;
153 	register List *tmp, *prev = NULL;
154 	unsigned long hvalue = hash_nickname(name, size);
155 
156 	location = &(list[hvalue]);
157 
158 	/* at this point, we found the array spot, now search
159 	 * as regular linked list, or as ircd likes to say...
160 	 * "We found the bucket, now search the chain"
161 	 */
162 	for (tmp = (List *) location->list; tmp; prev = tmp, tmp = tmp->next)
163 	{
164 		if (!my_stricmp(name, tmp->name))
165 		{
166 			if (remove != REMOVE_FROM_LIST)
167 				move_gen_link_to_top(tmp, prev, location);
168 			else
169 			{
170 				location->links--;
171 				remove_gen_link_from_list(tmp, prev, location);
172 			}
173 			return tmp;
174 		}
175 	}
176 	return NULL;
177 }
178 
179 /*
180  * add_nicklist_to_channellist: This function will add the nicklist
181  * into the channellist, ensuring that we hash the nicklist, and
182  * insert the struct correctly into the channelist's Nicklist hash
183  * array
184  */
BX_add_nicklist_to_channellist(NickList * nptr,ChannelList * cptr)185 void BX_add_nicklist_to_channellist(NickList *nptr, ChannelList *cptr)
186 {
187 	unsigned long hvalue = hash_nickname(nptr->nick, NICKLIST_HASHSIZE);
188 
189 	/* take this nicklist, and attach it as the HEAD pointer
190 	 * in our chain at the hashed location in our array...
191 	 * Note, by doing this, this ensures that the "most active"
192 	 * users always remain at the top of the chain... ie, faster
193 	 * lookups for active users, (and as a side note, makes
194 	 * doing the add quite simple!)
195 	 */
196 	nptr->next = (NickList *) cptr->NickListTable[hvalue].list;
197 
198 	/* assign our new linked list into array spot */
199 	cptr->NickListTable[hvalue].list = (void *) nptr;
200 	/* quick tally of nicks in chain in this array spot */
201 	cptr->NickListTable[hvalue].links++;
202 	/* keep stats on hits to this array spot */
203 	cptr->NickListTable[hvalue].hits++;
204 }
205 
BX_find_nicklist_in_channellist(char * nick,ChannelList * cptr,int remove)206 NickList *BX_find_nicklist_in_channellist(char *nick, ChannelList *cptr, int remove)
207 {
208 	HashEntry *location;
209 	register NickList *tmp, *prev = NULL;
210 	unsigned long hvalue = hash_nickname(nick, NICKLIST_HASHSIZE);
211 
212 	if (!cptr)
213 		return NULL;
214 	location = &(cptr->NickListTable[hvalue]);
215 
216 	/* at this point, we found the array spot, now search
217 	 * as regular linked list, or as ircd likes to say...
218 	 * "We found the bucket, now search the chain"
219 	 */
220 	for (tmp = (NickList *) location->list; tmp; prev = tmp, tmp = tmp->next)
221 	{
222 		if (!my_stricmp(nick, tmp->nick))
223 		{
224 			if (remove != REMOVE_FROM_LIST)
225 				move_link_to_top(tmp, prev, location);
226 			else
227 			{
228 				location->links--;
229 				remove_link_from_list(tmp, prev, location);
230 			}
231 			return tmp;
232 		}
233 	}
234 	return NULL;
235 }
236 
237 /*
238  * Basically this makes the hash table "look" like a straight linked list
239  * This should be used for things that require you to cycle through the
240  * full list, ex. for finding ALL matching stuff.
241  * : usage should be like :
242  *
243  *	for (nptr = next_nicklist(cptr, NULL); nptr; nptr =
244  *	     next_nicklist(cptr, nptr))
245  *		YourCodeOnTheNickListStruct
246  */
BX_next_nicklist(ChannelList * cptr,NickList * nptr)247 NickList *BX_next_nicklist(ChannelList *cptr, NickList *nptr)
248 {
249 	unsigned long hvalue = 0;
250 	if (!cptr)
251 		/* No channel! */
252 		return NULL;
253 	else if (!nptr)
254 	{
255 		/* wants to start the walk! */
256 		while ((NickList *) cptr->NickListTable[hvalue].list == NULL)
257 		{
258 			hvalue++;
259 			if (hvalue >= NICKLIST_HASHSIZE)
260 				return NULL;
261 		}
262 		return (NickList *) cptr->NickListTable[hvalue].list;
263 	}
264 	else if (nptr->next)
265 	{
266 		/* still returning a chain! */
267 		return nptr->next;
268 	}
269 	else if (!nptr->next)
270 	{
271 		int hvalue;
272 		/* hit end of chain, go to next bucket */
273 		hvalue = hash_nickname(nptr->nick, NICKLIST_HASHSIZE) + 1;
274 		if (hvalue >= NICKLIST_HASHSIZE)
275 		{
276 			/* end of list */
277 			return NULL;
278 		}
279 		else
280 		{
281 			while ((NickList *) cptr->NickListTable[hvalue].list == NULL)
282 			{
283 				hvalue++;
284 				if (hvalue >= NICKLIST_HASHSIZE)
285 					return NULL;
286 			}
287 			/* return head of next filled bucket */
288                         return (NickList *) cptr->NickListTable[hvalue].list;
289 		}
290 	}
291 	else
292 		/* shouldn't ever be here */
293 		say ("HASH_ERROR: next_nicklist");
294 	return NULL;
295 }
296 
BX_next_namelist(HashEntry * cptr,List * nptr,unsigned int size)297 List *BX_next_namelist(HashEntry *cptr, List *nptr, unsigned int size)
298 {
299 	unsigned long hvalue = 0;
300 	if (!cptr)
301 		/* No channel! */
302 		return NULL;
303 	else if (!nptr)
304 	{
305 		/* wants to start the walk! */
306 		while ((List *) cptr[hvalue].list == NULL)
307 		{
308 			hvalue++;
309 			if (hvalue >= size)
310 				return NULL;
311 		}
312 		return (List *) cptr[hvalue].list;
313 	}
314 	else if (nptr->next)
315 	{
316 		/* still returning a chain! */
317 		return nptr->next;
318 	}
319 	else if (!nptr->next)
320 	{
321 		int hvalue;
322 		/* hit end of chain, go to next bucket */
323 		hvalue = hash_nickname(nptr->name, size) + 1;
324 		if (hvalue >= size)
325 		{
326 			/* end of list */
327 			return NULL;
328 		}
329 		else
330 		{
331 			while ((List *) cptr[hvalue].list == NULL)
332 			{
333 				hvalue++;
334 				if (hvalue >= size)
335 					return NULL;
336 			}
337 			/* return head of next filled bucket */
338                         return (List *) cptr[hvalue].list;
339 		}
340 	}
341 	else
342 		/* shouldn't ever be here */
343 		say ("HASH_ERROR: next_namelist");
344 	return NULL;
345 }
346 
clear_nicklist_hashtable(ChannelList * cptr)347 void clear_nicklist_hashtable(ChannelList *cptr)
348 {
349 	if (cptr)
350 	{
351 		memset((char *) cptr->NickListTable, 0,
352 		   sizeof(HashEntry) * NICKLIST_HASHSIZE);
353 	}
354 }
355 
356 
show_nicklist_hashtable(ChannelList * cptr)357 void show_nicklist_hashtable(ChannelList *cptr)
358 {
359         int count, count2;
360         NickList *ptr;
361 
362         for (count = 0; count < NICKLIST_HASHSIZE; count++)
363         {
364 		if (cptr->NickListTable[count].links == 0)
365 			continue;
366                 say("HASH DEBUG: %d   links %d   hits %d",
367                     count,
368                     cptr->NickListTable[count].links,
369                     cptr->NickListTable[count].hits);
370 
371                 for (ptr = (NickList *) cptr->NickListTable[count].list,
372                     count2 = 0; ptr; count2++, ptr = ptr->next)
373                 {
374                         say("HASH_DEBUG: %d:%d  %s!%s", count, count2,
375                             ptr->nick, ptr->host);
376                 }
377         }
378 }
379 
show_whowas_debug_hashtable(WhowasWrapList * cptr)380 void show_whowas_debug_hashtable(WhowasWrapList *cptr)
381 {
382         int count, count2;
383         WhowasList *ptr;
384 
385         for (count = 0; count < WHOWASLIST_HASHSIZE; count++)
386         {
387 		if (cptr->NickListTable[count].links == 0)
388 			continue;
389                 say("HASH DEBUG: %d   links %d   hits %d",
390                     count,
391                     cptr->NickListTable[count].links,
392                     cptr->NickListTable[count].hits);
393 
394                 for (ptr = (WhowasList *) cptr->NickListTable[count].list,
395                     count2 = 0; ptr; count2++, ptr = ptr->next)
396                 {
397                         say("HASH_DEBUG: %d:%d  %10s %s!%s", count, count2,
398                             ptr->channel, ptr->nicklist->nick, ptr->nicklist->host);
399                 }
400         }
401 }
402 
BUILT_IN_COMMAND(show_hash)403 BUILT_IN_COMMAND(show_hash)
404 {
405 char *c;
406 ChannelList *chan = NULL, *chan2;
407 extern int from_server;
408 extern WhowasWrapList whowas_userlist_list;
409 extern WhowasWrapList whowas_reg_list;
410 extern WhowasWrapList whowas_splitin_list;
411 	if (args && *args)
412 		c = next_arg(args, &args);
413 	else
414 		c = get_current_channel_by_refnum(0);
415 	if (c && from_server > -1)
416 	{
417 		chan2 = get_server_channels(from_server);
418 		chan = (ChannelList *)find_in_list((List **)&chan2, c, 0);
419 	}
420 	if (chan)
421 		show_nicklist_hashtable(chan);
422 	show_whowas_debug_hashtable(&whowas_userlist_list);
423 	show_whowas_debug_hashtable(&whowas_reg_list);
424 	show_whowas_debug_hashtable(&whowas_splitin_list);
425 }
426 
427 /*
428  * the following routines are written by Colten Edwards (panasync)
429  * to hash the whowas lists that the client keeps.
430  */
431 
hash_userhost_channel(char * userhost,char * channel,unsigned int size)432 static unsigned long hash_userhost_channel(char *userhost, char *channel, unsigned int size)
433 {
434 	register const unsigned char *p = (const unsigned char *)userhost;
435 	unsigned long g, hash = 0;
436 	if (!userhost) return -1;
437 	while (*p)
438 	{
439 		hash = (hash << 4) + ((*p >= 'A' && *p <= 'Z') ? (*p+32) : *p);
440 		if ((g = hash & 0xF0000000))
441 			hash ^= g >> 24;
442 		hash &= ~g;
443 		p++;
444 	}
445 	p = (const unsigned char *)channel;
446 	if (p)
447 	{
448 		while (*p)
449 		{
450 			if (*p == ',')
451 				return -1;
452 			hash = (hash << 4) + ((*p >= 'A' && *p <= 'Z') ? (*p+32) : *p);
453 			if ((g = hash & 0xF0000000))
454 				hash ^= g >> 24;
455 			hash &= ~g;
456 			p++;
457 		}
458 	}
459 	return (hash % size);
460 }
461 
462 /*
463  * move_link_to_top: used by find routine, brings link
464  * to the top of the list in the specific array location
465  */
move_link_to_top_whowas(WhowasList * tmp,WhowasList * prev,HashEntry * location)466 static inline void move_link_to_top_whowas(WhowasList *tmp, WhowasList *prev, HashEntry *location)
467 {
468 	if (prev)
469 	{
470 		WhowasList *old_list;
471 		old_list = (WhowasList *) location->list;
472 		location->list = (void *) tmp;
473 		prev->next = tmp->next;
474 		tmp->next = old_list;
475 	}
476 }
477 
478 /*
479  * remove_link_from_list: used by find routine, removes link
480  * from our chain of hashed entries.
481  */
remove_link_from_whowaslist(WhowasList * tmp,WhowasList * prev,HashEntry * location)482 static inline void remove_link_from_whowaslist(WhowasList *tmp, WhowasList *prev, HashEntry *location)
483 {
484 	if (prev)
485 	{
486 		/* remove the link from the middle of the list */
487 		prev->next = tmp->next;
488 	}
489 	else {
490 		/* unlink the first link, and connect next one up */
491 		location->list = (void *) tmp->next;
492 	}
493 	/* set tmp's next to NULL, as its unlinked now */
494 	tmp->next = NULL;
495 }
496 
497 /*
498  * add_nicklist_to_channellist: This function will add the nicklist
499  * into the channellist, ensuring that we hash the nicklist, and
500  * insert the struct correctly into the channelist's Nicklist hash
501  * array
502  */
BX_add_whowas_userhost_channel(WhowasList * wptr,WhowasWrapList * list)503 void BX_add_whowas_userhost_channel(WhowasList *wptr, WhowasWrapList *list)
504 {
505 	unsigned long hvalue = hash_userhost_channel(wptr->nicklist->host, wptr->channel, WHOWASLIST_HASHSIZE);
506 
507 	/* take this nicklist, and attach it as the HEAD pointer
508 	 * in our chain at the hashed location in our array...
509 	 * Note, by doing this, this ensures that the "most active"
510 	 * users always remain at the top of the chain... ie, faster
511 	 * lookups for active users, (and as a side note, makes
512 	 * doing the add quite simple!)
513 	 */
514 	wptr->next = (WhowasList *) list->NickListTable[hvalue].list;
515 
516 	/* assign our new linked list into array spot */
517 	list->NickListTable[hvalue].list = (void *) wptr;
518 	/* quick tally of nicks in chain in this array spot */
519 	list->NickListTable[hvalue].links++;
520 	/* keep stats on hits to this array spot */
521 	list->NickListTable[hvalue].hits++;
522 	list->total_links++;
523 }
524 
BX_find_userhost_channel(char * host,char * channel,int remove,WhowasWrapList * wptr)525 WhowasList *BX_find_userhost_channel(char *host, char *channel, int remove, WhowasWrapList *wptr)
526 {
527 	HashEntry *location;
528 	register WhowasList *tmp, *prev = NULL;
529 	unsigned long hvalue;
530 
531 	hvalue = hash_userhost_channel(host, channel, WHOWASLIST_HASHSIZE);
532 	location = &(wptr->NickListTable[hvalue]);
533 
534 	/* at this point, we found the array spot, now search
535 	 * as regular linked list, or as ircd likes to say...
536 	 * "We found the bucket, now search the chain"
537 	 */
538 	for (tmp = (WhowasList *) (&(wptr->NickListTable[hvalue]))->list; tmp; prev = tmp, tmp = tmp->next)
539 	{
540 		if (!tmp->nicklist->host || !tmp->channel || !host || !channel)
541 			continue;
542 		if (!my_stricmp(host, tmp->nicklist->host) && !my_stricmp(channel, tmp->channel))
543 		{
544 			if (remove != REMOVE_FROM_LIST)
545 				move_link_to_top_whowas(tmp, prev, location);
546 			else
547 			{
548 				location->links--;
549 				remove_link_from_whowaslist(tmp, prev, location);
550 				wptr->total_unlinks++;
551 			}
552 			wptr->total_hits++;
553 			return tmp;
554 		}
555 	}
556 	return NULL;
557 }
558 
559 /*
560  * Basically this makes the hash table "look" like a straight linked list
561  * This should be used for things that require you to cycle through the
562  * full list, ex. for finding ALL matching stuff.
563  * : usage should be like :
564  *
565  *	for (nptr = next_userhost(cptr, NULL); nptr; nptr =
566  *	     next_userhost(cptr, nptr))
567  *		YourCodeOnTheWhowasListStruct
568  */
BX_next_userhost(WhowasWrapList * cptr,WhowasList * nptr)569 WhowasList *BX_next_userhost(WhowasWrapList *cptr, WhowasList *nptr)
570 {
571 	unsigned long hvalue = 0;
572 	if (!cptr)
573 		/* No channel! */
574 		return NULL;
575 	else if (!nptr)
576 	{
577 		/* wants to start the walk! */
578 		while ((WhowasList *) cptr->NickListTable[hvalue].list == NULL)
579 		{
580 			hvalue++;
581 			if (hvalue >= WHOWASLIST_HASHSIZE)
582 				return NULL;
583 		}
584 		return (WhowasList *) cptr->NickListTable[hvalue].list;
585 	}
586 	else if (nptr->next)
587 	{
588 		/* still returning a chain! */
589 		return nptr->next;
590 	}
591 	else if (!nptr->next)
592 	{
593 		int hvalue;
594 		/* hit end of chain, go to next bucket */
595 		hvalue = hash_userhost_channel(nptr->nicklist->host, nptr->channel, WHOWASLIST_HASHSIZE) + 1;
596 		if (hvalue >= WHOWASLIST_HASHSIZE)
597 		{
598 			/* end of list */
599 			return NULL;
600 		}
601 		else
602 		{
603 			while ((WhowasList *) cptr->NickListTable[hvalue].list == NULL)
604 			{
605 				hvalue++;
606 				if (hvalue >= WHOWASLIST_HASHSIZE)
607 					return NULL;
608 			}
609 			/* return head of next filled bucket */
610                         return (WhowasList *) cptr->NickListTable[hvalue].list;
611 		}
612 	}
613 	else
614 		/* shouldn't ever be here */
615 		say ("WHOWAS_HASH_ERROR: next_userhost");
616 	return NULL;
617 }
618 
show_whowas_hashtable(WhowasWrapList * cptr,char * list)619 void show_whowas_hashtable(WhowasWrapList *cptr, char *list)
620 {
621         int count, count2 = 1;
622         WhowasList *ptr;
623 
624 	say("WhoWas %s Cache Stats: %lu hits  %lu  links  %lu unlinks", list, cptr->total_hits, cptr->total_links, cptr->total_unlinks);
625         for (count = 0; count < WHOWASLIST_HASHSIZE; count++)
626         {
627 
628 		if (cptr->NickListTable[count].links == 0)
629 			continue;
630                 for (ptr = (WhowasList *) cptr->NickListTable[count].list; ptr; count2++, ptr = ptr->next)
631 			put_it("%s", convert_output_format("%K[%W$[3]0%K] %Y$[10]1 %W$2%G!%c$3", "%d %s %s %s", count2, ptr->channel, ptr->nicklist->nick, ptr->nicklist->host));
632         }
633 }
634 
show_wholeft_hashtable(WhowasWrapList * cptr,time_t ltime,int * total,int * hook,char * list)635 int show_wholeft_hashtable(WhowasWrapList *cptr, time_t ltime, int *total, int *hook, char *list)
636 {
637         int count, count2;
638         WhowasList *ptr;
639 
640         for (count = 0; count < WHOWASLIST_HASHSIZE; count++)
641         {
642 
643 		if (cptr->NickListTable[count].links == 0)
644 			continue;
645                 for (ptr = (WhowasList *) cptr->NickListTable[count].list, count2 = 1; ptr; count2++, ptr = ptr->next)
646 		{
647 			if (ptr->server1/* && ptr->server2*/)
648 			{
649 				if (!(*total)++ && (*hook = do_hook(WHOLEFT_HEADER_LIST, "%s %s %s %s %s %s", "Nick", "Host", "Channel", "Time", "Server", "Server")))
650 					put_it("%s", convert_output_format(fget_string_var(FORMAT_WHOLEFT_HEADER_FSET), NULL));
651 				if (do_hook(WHOLEFT_LIST, "%s %s %s %ld %s %s", ptr->nicklist->nick, ptr->nicklist->host, ptr->channel, ltime-ptr->time, ptr->server1?ptr->server1:"Unknown", ptr->server2?ptr->server2:"Unknown"))
652 					put_it("%s", convert_output_format(fget_string_var(FORMAT_WHOLEFT_USER_FSET), "%s %s %s %l %s", ptr->nicklist->nick, ptr->nicklist->host, ptr->channel, (long)ltime-ptr->time, ptr->server1?ptr->server1:empty_string));
653 			}
654 		}
655         }
656 	if (*total)
657 		do_hook(WHOLEFT_FOOTER_LIST, "%s", "End of WhoLeft");
658 	return *hook;
659 }
660 
BX_remove_oldest_whowas_hashlist(WhowasWrapList * list,time_t timet,int count)661 int BX_remove_oldest_whowas_hashlist(WhowasWrapList *list, time_t timet, int count)
662 {
663 WhowasList *ptr;
664 int total = 0;
665 register unsigned long x;
666 	if (!count)
667 	{
668 		for (x = 0; x < WHOWASLIST_HASHSIZE; x++)
669 		{
670 			ptr = (WhowasList *) (&(list->NickListTable[x]))->list;
671 			if (!ptr || !ptr->nicklist)
672 				continue;
673 			while (ptr)
674 			{
675 				if ((ptr->time + timet) <= now)
676 				{
677 					if (!(ptr = find_userhost_channel(ptr->nicklist->host, ptr->channel, 1, list)))
678 						break;
679 					new_free(&(ptr->nicklist->ip));
680 					new_free(&(ptr->nicklist->nick));
681 					new_free(&(ptr->nicklist->host));
682 					new_free(&(ptr->nicklist->server));
683 					new_free((char **)&(ptr->nicklist));
684 					new_free(&(ptr->channel));
685 					new_free(&(ptr->server1));
686 					new_free(&(ptr->server2));
687 					new_free((char **)&ptr);
688 					total++;
689 					ptr = (WhowasList *) (&(list->NickListTable[x]))->list;
690 				} else ptr = ptr->next;
691 			}
692 		}
693 	}
694 	else
695 	{
696 		while((ptr = next_userhost(list, NULL)) && count)
697 		{
698 			x = hash_userhost_channel(ptr->nicklist->host, ptr->channel, WHOWASLIST_HASHSIZE);
699 			if (!(ptr = find_userhost_channel(ptr->nicklist->host, ptr->channel, 1, list)))
700 				break;
701 			if (ptr->nicklist)
702 			{
703 				new_free(&(ptr->nicklist->ip));
704 				new_free(&(ptr->nicklist->nick));
705 				new_free(&(ptr->nicklist->host));
706 				new_free(&(ptr->nicklist->server));
707 				new_free((char **)&(ptr->nicklist));
708 			}
709 			new_free(&(ptr->channel));
710 			new_free(&(ptr->server1));
711 			new_free(&(ptr->server2));
712 			new_free((char **)&ptr);
713 			total++; count--;
714 		}
715 	}
716 	return total;
717 }
718 
cmp_host(List * a,List * b)719 int cmp_host (List *a, List *b)
720 {
721 NickList *a1 = (NickList *)a, *b1 = (NickList *)b;
722 	return strcmp(a1->host, b1->host);
723 }
724 
cmp_time(List * a,List * b)725 int cmp_time (List *a, List *b)
726 {
727 NickList *a1 = (NickList *)a, *b1 = (NickList *)b;
728 	if (a1->idle_time > b1->idle_time)
729 		return -1;
730 	if (a1->idle_time < b1->idle_time)
731 		return 1;
732 	return strcmp(a1->nick, b1->nick);
733 }
734 
735 
cmp_ip(List * a,List * b)736 int cmp_ip (List *a, List *b)
737 {
738 NickList *a1 = (NickList *)a, *b1 = (NickList *)b;
739 unsigned long at, bt;
740 	if (!a1->ip && !b1->ip)
741 		return -1;
742 /*		return strcmp(a1->nick, b1->nick);*/
743 	if (!a1->ip)
744 		return -1;
745 	if (!b1->ip)
746 		return 1;
747 	at = inet_addr(a1->ip); bt = inet_addr(b1->ip);
748 	if (at < bt)
749 		return 1;
750 	if (at > bt)
751 		return -1;
752 	return strcmp(a1->nick, b1->nick);
753 }
754 
755 /* Compare two Nicks by channel status, chanop > halfop > voice */
cmp_stat(List * a,List * b)756 int cmp_stat (List *a, List *b)
757 {
758 	NickList *a1 = (NickList *)a, *b1 = (NickList *)b;
759 	int a_status =
760 		nick_isop(a1) ? 0 : nick_ishalfop(a1) ? 1 : nick_isvoice(a1) ? 2 : 3;
761 	int b_status =
762 		nick_isop(b1) ? 0 : nick_ishalfop(b1) ? 1 : nick_isvoice(b1) ? 2 : 3;
763 	int cmp;
764 
765 	cmp = a_status - b_status;
766 
767 	/* Equal status */
768 	if (cmp == 0)
769 		cmp = strcmp(a1->nick, b1->nick);
770 
771 	return cmp;
772 }
773 
774 /* Determines if the Nick matches the nick!user@host mask given. */
nick_match(NickList * nick,char * mask)775 int nick_match(NickList *nick, char *mask)
776 {
777     int match = 0;
778     char *nuh = m_3dup(nick->nick, "!", nick->host);
779 
780     match = wild_match(mask, nuh);
781     new_free(&nuh);
782 
783     return match;
784 }
785 
BX_sorted_nicklist(ChannelList * chan,int sort)786 NickList *BX_sorted_nicklist(ChannelList *chan, int sort)
787 {
788 	NickList *tmp, *l = NULL, *list = NULL, *last = NULL;
789 	for (tmp = next_nicklist(chan, NULL); tmp; tmp = next_nicklist(chan, tmp))
790 	{
791 		l = (NickList *)new_malloc(sizeof(NickList));
792 		memcpy(l, tmp, sizeof(NickList));
793 		l->next = NULL;
794 		switch(sort)
795 		{
796 			case NICKSORT_HOST:
797 				add_to_list_ext((List **)&list, (List *)l, cmp_host);
798 				break;
799 			case NICKSORT_STAT:
800 				add_to_list_ext((List **)&list, (List *)l, cmp_stat);
801 				break;
802 			case NICKSORT_TIME:
803 				add_to_list_ext((List **)&list, (List *)l, cmp_time);
804 				break;
805 			case NICKSORT_IP:
806 				add_to_list_ext((List **)&list, (List *)l, cmp_ip);
807 				break;
808 			case NICKSORT_NONE:
809 				if (last)
810 					last->next = l;
811 				else
812 					list = l;
813 				break;
814 			default:
815 			case NICKSORT_NICK:
816 			case NICKSORT_NORMAL:
817 				add_to_list((List **)&list, (List *)l);
818 				break;
819 		}
820 		last = l;
821 	}
822 	return list;
823 }
824 
BX_clear_sorted_nicklist(NickList ** list)825 void BX_clear_sorted_nicklist(NickList **list)
826 {
827 	register NickList *t;
828 	while(*list)
829 	{
830 		t = (*list)->next;
831 		new_free((char **)&(*list));
832 		*list = t;
833 	}
834 }
835 
BX_add_name_to_floodlist(char * name,char * host,char * channel,HashEntry * list,unsigned int size)836 Flooding *BX_add_name_to_floodlist(char *name, char *host, char *channel, HashEntry *list, unsigned int size)
837 {
838 	Flooding *nptr;
839 	unsigned long hvalue = hash_nickname(name, size);
840 	nptr = (Flooding *)new_malloc(sizeof(Flooding));
841 	nptr->next = (Flooding *) list[hvalue].list;
842 	nptr->name = m_strdup(name);
843 	nptr->host = m_strdup(host);
844 	list[hvalue].list = (void *) nptr;
845 	/* quick tally of nicks in chain in this array spot */
846 	list[hvalue].links++;
847 	/* keep stats on hits to this array spot */
848 	list[hvalue].hits++;
849 	return nptr;
850 }
851 
BX_find_name_in_floodlist(char * name,char * host,HashEntry * list,unsigned int size,int remove)852 Flooding *BX_find_name_in_floodlist(char *name, char *host, HashEntry *list, unsigned int size, int remove)
853 {
854 	HashEntry *location;
855 	register Flooding *tmp, *prev = NULL;
856 	unsigned long hvalue = hash_nickname(name, size);
857 
858 	location = &(list[hvalue]);
859 
860 	/* at this point, we found the array spot, now search
861 	 * as regular linked list, or as ircd likes to say...
862 	 * "We found the bucket, now search the chain"
863 	 */
864 	for (tmp = (Flooding *) location->list; tmp; prev = tmp, tmp = tmp->next)
865 	{
866 		if (!my_stricmp(name, tmp->name))
867 		{
868 			if (remove != REMOVE_FROM_LIST)
869 				move_gen_link_to_top((List *)tmp, (List *)prev, location);
870 			else
871 			{
872 				location->links--;
873 				remove_gen_link_from_list((List *)tmp, (List *)prev, location);
874 			}
875 			return tmp;
876 		}
877 	}
878 	return NULL;
879 }
880 
881